LangChain v1.0 的 Agents:让 AI 真正"动起来"

最近在重构项目时升级到了 LangChain v1.0.2,发现新版本的 createAgent() API 确实好用很多。之前写 Agent 需要自己手动处理工具调用的循环,现在一个函数调用就搞定了。今天就来聊聊 Agents 这个核心概念,以及 v1.0 到底改进了什么。

什么是 Agent?

简单来说,Agent 就是让大语言模型"动起来"的东西。它不仅仅是生成文本,还能调用工具、做决策、迭代解决问题。

用公式来表达就是:

ini 复制代码
Agent = Language Model(语言模型) + Tools(工具) + Reasoning(推理能力)

想象一下,你问 ChatGPT:"北京的天气怎么样?"传统的模型只能告诉你它训练数据里的信息,或者告诉你它不知道。但有了 Agent,它就可以:

  1. 理解你的问题
  2. 决定调用天气查询工具
  3. 执行工具获取实时数据
  4. 基于结果生成回答

整个过程是自动的,你不需要手动去调用天气 API,也不需要把结果传回去。Agent 自己会处理这个循环。

为什么需要 Agent?

这个问题其实很简单。语言模型再聪明,它也只是"知道"训练数据里的东西。但现实世界的数据是动态的、实时的。你需要:

  • 查询数据库获取最新订单状态
  • 调用 API 获取实时天气
  • 搜索代码库找到相关代码
  • 执行代码验证结果

这些操作都需要"工具",而 Agent 就是那个能智能选择和使用工具的"大脑"。

v1.0 的改进:createAgent()

之前用 LangChain 0.3.x 的时候,创建 Agent 需要好几步:

typescript 复制代码
import { AgentExecutor, createOpenAIFunctionsAgent } from 'langchain/agents';
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from '@langchain/core/prompts';

// 先定义 prompt
const prompt = ChatPromptTemplate.fromMessages([
  ['system', '你是一个有用的助手'],
  ['human', '{input}'],
  new MessagesPlaceholder('agent_scratchpad'),
]);

// 创建 agent
const agent = await createOpenAIFunctionsAgent({
  llm,
  tools,
  prompt,
});

// 再创建 executor
const executor = new AgentExecutor({
  agent,
  tools,
  maxIterations: 15,
});

// 最后才能用
const result = await executor.invoke({ input: '问题' });

说实话,这套流程对新手来说有点复杂。而且每次都要写模板代码,容易出错。

现在 v1.0 的 createAgent() 就简单多了:

typescript 复制代码
import { createAgent } from 'langchain';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from 'langchain';
import { z } from 'zod';

// 定义工具
const weatherTool = tool(
  async ({ city }: { city: string }) => {
    return `天气:${city} 晴天,25°C`;
  },
  {
    name: 'get_weather',
    description: '获取指定城市的天气信息',
    schema: z.object({
      city: z.string().describe('城市名称'),
    }),
  }
);

// 创建模型
const llm = new ChatOpenAI({
  modelName: 'gpt-4',
  temperature: 0,
});

// 创建 Agent - 就这么简单!
const agent = createAgent({
  llm,
  tools: [weatherTool],
  maxIterations: 15,
  systemMessage: '你是一个有用的助手',
});

// 使用
const result = await agent.invoke({
  input: '北京的天气如何?',
});

console.log(result.output);

看,代码量少了一半,而且更直观。这就是 v1.0 "opinionated" 设计理念的体现------提供最佳实践,减少配置负担。

Agent 是怎么工作的?

Agent 的核心是一个循环。理解这个循环机制很重要,它能帮你更好地使用 Agent。

Agent 执行循环详解

让我用图来展示 Agent 的工作流程:

yaml 复制代码
┌─────────────────────────────────┐
│  1. 接收用户输入                │
└────────────┬────────────────────┘
             ↓
┌─────────────────────────────────┐
│  2. LLM 分析任务并决定行动      │
│     - 是否需要调用工具?        │
│     - 调用哪个工具?            │
│     - 使用什么参数?            │
└────────────┬────────────────────┘
             ↓
         ┌───┴───┐
         │需要工具│
         └───┬───┘
             ↓
┌─────────────────────────────────┐
│  3. 执行选定的工具              │
│     - 调用工具函数              │
│     - 获取工具结果              │
└────────────┬────────────────────┘
             ↓
┌─────────────────────────────────┐
│  4. 将工具结果返回给 LLM        │
│     - 创建 ToolMessage          │
│     - 添加到消息历史            │
└────────────┬────────────────────┘
             ↓
┌─────────────────────────────────┐
│  5. LLM 基于结果继续推理         │
│     - 分析工具结果              │
│     - 决定下一步行动            │
└────────────┬────────────────────┘
             ↓
    ┌────────┴────────┐
    │  是否有最终答案? │
    └────────┬────────┘
         ┌───┴───┐
    No   │   Yes │
    └───┬─┘   └─┬───┘
        │       │
        ↓       ↓
  返回步骤2   返回最终答案

这个循环会自动重复,直到满足停止条件。整个过程你不需要写任何循环代码,Agent 自己会处理。

停止条件详解

Agent 会在以下情况下停止:

  1. 模型给出最终答案

    • LLM 认为不需要更多工具了
    • 直接返回最终答案
    • 这是最理想的停止方式
  2. 达到迭代限制

    typescript 复制代码
    const agent = createAgent({
      llm,
      tools,
      maxIterations: 15, // 最多执行 15 次迭代
    });
    • 防止无限循环
    • 保护机制,防止资源耗尽
    • 如果达到限制还没完成,会返回当前状态
  3. 错误发生

    • 工具执行失败
    • 或其他错误导致停止
    • Agent 会尝试错误恢复,但严重错误会停止

实际执行示例

让我用一个具体例子说明 Agent 的执行过程:

typescript 复制代码
// 用户输入
const input = '查询北京的天气,如果温度高于25度,就搜索附近的游泳馆';

// Agent 执行过程(自动)

// 迭代 1:
// LLM 决策:需要调用 get_weather 工具
// 执行:get_weather({ city: '北京' })
// 结果:{ temperature: 28, condition: '晴天' }

// 迭代 2:
// LLM 分析:温度是28度,高于25度
// LLM 决策:需要调用 search_nearby 工具
// 执行:search_nearby({ query: '游泳馆', location: '北京' })
// 结果:[{ name: '...', distance: '...' }]

// 迭代 3:
// LLM 分析:已有足够信息
// LLM 决策:生成最终答案,不再调用工具
// 输出:'今天北京天气晴朗,温度28度,适合游泳。附近有以下游泳馆...'

// 停止:模型发出最终答案 ✅

看,Agent 会自动处理这整个流程,你只需要调用一次 invoke() 就行了。

深入理解:Agent 的核心组件

在写实际例子之前,我想先聊聊 Agent 的三个核心组件,这样你能更好地理解它是怎么工作的。

1. Language Model(语言模型)

这是 Agent 的"大脑",负责:

  • 理解用户意图
  • 决定使用哪个工具
  • 分析工具结果
  • 生成最终答案
typescript 复制代码
const llm = new ChatOpenAI({
  modelName: 'gpt-4',
  temperature: 0, // 对于需要确定性的任务,设为 0
});

2. Tools(工具)

这是 Agent 的"双手",能让 Agent 执行实际操作:

typescript 复制代码
// 工具的结构
const tool = tool(
  async (input) => {
    // 实际执行的代码
    return result;
  },
  {
    name: 'tool_name',           // 工具名称
    description: '工具描述',      // 这很重要!
    schema: z.object({...}),      // 参数定义
  }
);

3. Reasoning(推理能力)

这是 Agent 的"思考过程",体现在:

  • 分析任务复杂度
  • 规划执行步骤
  • 决定工具调用顺序
  • 整合多个工具的结果

这三个组件组合在一起,就形成了 Agent 的完整能力。用公式来表达就是:

ini 复制代码
Agent = Language Model(大脑) + Tools(双手) + Reasoning(思考过程)

Agent vs 传统 LLM:什么时候用哪个?

理解 Agent 和传统 LLM 的区别,能帮你更好地判断什么时候用 Agent。

核心区别对比

特性 传统 LLM Agent
能力范围 只能生成文本 能调用工具、执行操作
数据来源 训练数据(静态) 训练数据 + 实时工具数据
解决问题 一次性回答 迭代式解决
工具使用 不支持 自动选择和执行
适用场景 简单问答、文本生成 复杂任务、多步骤操作

什么时候用 Agent?

适合用 Agent 的场景:

  • ✅ 需要查询实时数据(天气、订单、数据库)
  • ✅ 需要执行多个步骤(查询 → 分析 → 生成报告)
  • ✅ 需要调用外部工具(搜索、计算、API)
  • ✅ 任务需要迭代优化(不断尝试直到找到答案)

不适合用 Agent 的场景:

  • ❌ 简单的文本生成(翻译、摘要)
  • ❌ 不需要实时数据的问答
  • ❌ 成本敏感的场景(Agent 会多次调用 LLM)
  • ❌ 确定性要求极高的场景(Agent 有一定不确定性)

举个例子:

typescript 复制代码
// ❌ 不需要 Agent:简单的翻译
const llm = new ChatOpenAI({ modelName: 'gpt-4' });
const result = await llm.invoke('把 "Hello" 翻译成中文');
// 直接用 LLM 就够了

// ✅ 需要 Agent:查询实时数据并分析
const agent = createAgent({
  llm,
  tools: [weatherTool, calculatorTool],
});
const result = await agent.invoke(
  '北京的天气如何?如果温度高于20度,计算一下换算成华氏度是多少'
);
// Agent 会自动调用工具、分析结果、生成答案

一个实际例子

让我写个完整的例子,展示 Agent 如何解决一个多步骤的问题:

typescript 复制代码
import { createAgent } from 'langchain';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from 'langchain';
import { z } from 'zod';

// 工具1:查询订单
const queryOrderTool = tool(
  async ({ orderId }: { orderId: string }) => {
    // 假设这是你的数据库查询
    const order = await db.query('SELECT * FROM orders WHERE id = ?', [
      orderId,
    ]);
    return JSON.stringify(order);
  },
  {
    name: 'query_order',
    description: '查询订单详细信息,包括状态、金额、商品等',
    schema: z.object({
      orderId: z.string().describe('订单号'),
    }),
  }
);

// 工具2:查询物流
const trackShippingTool = tool(
  async ({ orderId }: { orderId: string }) => {
    const shipping = await logisticsAPI.track(orderId);
    return JSON.stringify(shipping);
  },
  {
    name: 'track_shipping',
    description: '查询订单的物流信息和配送进度',
    schema: z.object({
      orderId: z.string().describe('订单号'),
    }),
  }
);

// 工具3:搜索FAQ
const searchFAQTool = tool(
  async ({ question }: { question: string }) => {
    const answer = await faqDatabase.search(question);
    return answer;
  },
  {
    name: 'search_faq',
    description: '在常见问题库中搜索答案',
    schema: z.object({
      question: z.string().describe('用户的问题'),
    }),
  }
);

// 创建客服 Agent
const customerServiceAgent = createAgent({
  llm: new ChatOpenAI({
    modelName: 'gpt-4',
    temperature: 0, // 客服需要确定性
  }),
  tools: [queryOrderTool, trackShippingTool, searchFAQTool],
  maxIterations: 10,
  systemMessage: `你是一个专业的客服助手。
  
工作原则:
1. 友好、专业、耐心
2. 使用工具获取准确信息
3. 如果无法解决问题,引导用户联系人工客服`,
});

// 使用
const result = await customerServiceAgent.invoke({
  input: '你好,我想查询订单 #12345 的信息,还有什么时候能到?',
});

console.log(result.output);

当你运行这段代码时,Agent 会自动:

  1. 理解用户想查询订单和物流信息
  2. 调用 queryOrderTool 获取订单详情
  3. 调用 trackShippingTool 获取物流信息
  4. 整合信息生成友好的回答

整个过程你不需要写任何循环代码,Agent 自己会处理。

工具描述的重要性

这里有个很重要的点:工具的描述一定要清晰。Agent 是通过描述来理解工具的用途的,描述越清晰,Agent 选择工具就越准确。

好的描述:

typescript 复制代码
{
  name: 'get_weather',
  description: '获取指定城市的天气信息,包括温度、湿度和天气状况',
  // ...
}

不好的描述:

typescript 复制代码
{
  name: 'tool1',
  description: '获取信息', // 太模糊了
  // ...
}

如果描述不清楚,Agent 可能会选错工具,或者不知道该用哪个。这就像给一个员工分配任务,如果你不说清楚,他可能做错事。

迭代限制和错误处理

Agent 会自动处理错误,这很贴心。如果工具执行失败,Agent 会捕获错误信息,然后尝试其他方案。但你需要设置合理的迭代限制,防止无限循环。

typescript 复制代码
const agent = createAgent({
  llm,
  tools,
  maxIterations: 15, // 最多15次迭代
  // ...
});

一般来说,10-15 次迭代足够解决大多数问题了。如果任务特别复杂,可以适当增加,但也要注意成本。

实际应用场景

我在项目中用 Agent 做了几个场景:

客服机器人:用户问订单状态,Agent 自动查询数据库和物流 API,然后生成回答。这比写一堆 if-else 判断要优雅多了。

数据分析助手:用户用自然语言问数据问题,Agent 自动生成 SQL、执行查询、可视化结果。不需要用户懂 SQL。

代码助手:搜索代码库、读取文件、执行测试、生成代码。Agent 可以自动完成多个步骤。

每个场景都大大简化了代码,而且用户体验更好。

和手动工具调用的对比

之前提到过,v0.3.x 需要手动处理工具调用循环。让我详细对比一下这两种方式的区别。

手动方式(v0.3.x)

typescript 复制代码
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, ToolMessage } from '@langchain/core/messages';

const llm = new ChatOpenAI({ modelName: 'gpt-4' });
const llmWithTools = llm.bindTools([weatherTool, databaseTool]);

const messages = [new HumanMessage('问题')];
let response = await llmWithTools.invoke(messages);

while (response.tool_calls?.length > 0) {
  // 手动执行每个工具
  const toolResults = await Promise.all(
    response.tool_calls.map(tc =>
      tools.find(t => t.name === tc.name).invoke(tc.args)
    )
  );

  // 手动创建 ToolMessage
  messages.push(response);
  toolResults.forEach((result, i) => {
    messages.push(
      new ToolMessage({
        content: result,
        tool_call_id: response.tool_calls[i].id,
      })
    );
  });

  // 再次调用
  response = await llmWithTools.invoke(messages);
}

手动方式的问题:

  • ❌ 代码复杂,需要写循环逻辑
  • ❌ 容易出错,消息历史管理麻烦
  • ❌ 需要手动处理错误
  • ❌ 没有迭代限制保护
  • ❌ 每次都要写类似的模板代码

Agent 方式(v1.0)

typescript 复制代码
import { createAgent } from 'langchain';

const agent = createAgent({
  llm,
  tools: [weatherTool, databaseTool],
  maxIterations: 15,
});

const result = await agent.invoke({ input: '问题' });

Agent 方式的优势:

  • ✅ 代码简洁,一行搞定
  • ✅ 自动处理循环和消息历史
  • ✅ 内置错误处理
  • ✅ 自动迭代限制保护
  • ✅ 生产就绪的实现

对比总结

维度 手动方式 Agent 方式
代码量 20-30 行 5-10 行
复杂度
错误处理 手动实现 自动处理
迭代限制 手动管理 自动保护
维护性 难维护 易维护
适用场景 特殊需求 大多数场景

很明显,Agent 方式更简单、更可靠。除非你有特殊需求,否则建议直接用 createAgent()

一些注意事项

使用 Agent 的时候,有几个点需要注意:

  1. 工具描述要清晰:这决定了 Agent 能否正确选择工具
  2. 设置合理的迭代限制:防止无限循环,也控制成本
  3. 错误处理:虽然 Agent 会自动处理,但工具内部最好也有错误处理
  4. 成本考虑:每次迭代都会调用 LLM,复杂的任务成本会比较高

总结

LangChain v1.0 的 createAgent() 确实是个很大的改进。代码更简洁,API 更直观,而且内置了最佳实践。对于需要多步骤交互的场景,Agent 是个很好的选择。

不过它也不是万能的。简单的任务可能直接用 LLM 就够了,不需要 Agent。而且 Agent 需要调用 LLM 多次,成本会比较高。所以要根据实际需求来选择。

总的来说,Agent 让 AI 应用开发变得更简单了。你可以专注于业务逻辑,而不用操心工具调用的循环处理。这就是框架的价值------让复杂的事情变简单。

相关推荐
稳稳C96 小时前
02|Langgraph | 从入门到实战 | workflow与Agent
人工智能·langchain·agent·langgraph
FreeCode7 小时前
LangChain1.0智能体开发:消息组件(Messages)
人工智能·langchain·agent
工藤学编程10 小时前
零基础学AI大模型之LangChain WebBaseLoader与Docx2txtLoader实战
人工智能·langchain
serve the people11 小时前
Partial Prompt Templates in LangChain
服务器·langchain·prompt
大模型真好玩1 天前
LangChain1.0速通指南(二)——LangChain1.0 create_agent api 基础知识
人工智能·langchain·mcp
FreeCode1 天前
LangChain1.0智能体开发:模型使用
人工智能·langchain·agent
chenchihwen1 天前
AI代码开发宝库系列:LangChain 工具链:从LCEL到实际应用
人工智能·python·langchain·rag
扯蛋4382 天前
LangChain的学习之路( 一 )
前端·langchain·mcp
serve the people2 天前
Formatting Outputs for ChatPrompt Templates(one)
langchain·prompt