工具调用与 Agent

构建真正的智能体:让 LLM 学会使用工具(Tool Calling)

📚 学习目标

学完这篇文章后,你将能够:

  • 使用 @langchain/core/tools 定义自定义工具
  • 使用 bindTools 将工具能力赋予 LLM
  • 使用预构建的 ToolNode 执行工具调用
  • 复现经典的 ReAct(Reasoning + Acting)Agent 模式

前置知识

在开始学习之前,建议先阅读:


1️⃣ 什么是 Agent?

简单来说,Agent = LLM + Tools + Loop。

  1. LLM 负责思考:理解用户意图,决定是否需要使用工具,以及使用哪个工具。
  2. Tools 负责行动:执行具体任务(查天气、算数学、搜网页)。
  3. Loop 负责循环:思考 → 行动 → 再思考 → 再行动,直到任务完成。

2️⃣ 定义工具 (Define Tools)

我们使用 tool 函数和 zod schema 来定义工具。Schema 非常重要,因为它告诉 LLM 该如何正确调用这个工具。

import { tool } from "@langchain/core/tools";
import z from "zod";

// 定义天气查询工具
const getWeather = tool(
    async (input) => {
        // 工具实现的实际逻辑(例如调用 API)
        return `查询结果:${input.city} 今天晴,25°C`;
    },
    {
        name: 'getWeather',
        description: '获取指定城市的天气',
        // 参数 Schema
        schema: z.object({
            city: z.string().describe('城市名称,如北京'),
        })
    }
);

const tools = [getWeather];

3️⃣ 赋予 LLM 工具能力

LangChain 提供了 bindTools 方法,将工具定义转换为 LLM 能理解的 JSON Schema 格式。

// 1. 实例化 LLM
const llm = new ChatOpenAI({ model: 'qwen3-max' });

// 2. 绑定工具
const llmWithTools = llm.bindTools(tools);

// 3. 节点逻辑
const agentNode = async (state) => {
    // LLM 会智能决定是返回普通文本,还是返回工具调用请求
    const result = await llmWithTools.invoke(state.messages);
    return { messages: [result] };
}

4️⃣ 执行工具调用 (ToolNode)

LangGraph 提供了一个预构建的节点 ToolNode,它能自动解析 LLM 的工具调用请求,执行对应的工具,并将结果返回。

import { ToolNode } from '@langchain/langgraph/prebuilt';

const toolNode = new ToolNode(tools);

5️⃣ 构建 ReAct 循环

我们需要一个条件路由逻辑:

  • 如果 LLM 想要调用工具 (tool_calls) → 去 tools 节点。
  • 如果 LLM 只是普通回复 → 结束。
// 路由函数
const shouldContinue = (state) => {
    const lastMessage = state.messages[state.messages.length - 1];
    
    // 检查是否有工具调用请求
    if (lastMessage.tool_calls?.length) {
        return 'tools';
    }
    return END;
}

// 构建图
const graph = new StateGraph(StateAnnotation)
    .addNode('agent', agentNode)
    .addNode('tools', toolNode)
    
    .addEdge(START, 'agent')
    
    // 条件边
    .addConditionalEdges('agent', shouldContinue, {
        tools: 'tools',
        [END]: END
    })
    
    // 工具执行完后,必须回到 agent 节点,让 LLM 看到结果并继续思考
    .addEdge('tools', 'agent') 
    
    .compile();

下面的 Mermaid 图展示了这个经典的循环:

💡 练习题

  1. 实操题:向你的 Agent 添加一个 calculate 工具(可以使用 JavaScript 的 evalmathjs),试着问它:"北京的天气怎么样?然后计算 123 * 456 是多少?"。观察它是否能连续调用两个工具。
  2. 思考ToolNode 执行完具后,为什么要连回 agent 节点?如果连向 END 会发生什么?(参考答案:连回 agent 是为了让 LLM 能够基于工具的返回结果生成最终的自然语言回复,或者决定是否需要进行下一步操作。直接连向 END 会导致用户只看到工具的原始输出,而不是 LLM 的解读。)

📚 参考资源

官方文档

项目代码


✅ 总结

核心要点

  • Schema 定义了一切:工具的描述和参数定义决定了 LLM 能否正确使用它。
  • bindTools 是链接 LLM 和代码实现的桥梁。
  • ReAct 循环(Agent → Tools → Agent)是现代 AI Agent 的基石。

下一步:在下一篇文章《记忆与持久化》中,我们将学习如何让 Agent 记住上下文,实现多轮连续对话。

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page