Skip to content

02-tool-calling.ts

单轮工具调用示例,展示 agent 底层的 tool calling 机制(不是完整 agent)。

功能介绍

这个示例演示了单轮 tool calling 的完整闭环:模型决定调用工具 -> 执行工具拿到 ToolMessage -> 把工具结果喂回模型 -> 拿到最终答案。这是理解 agent 的前置知识--agent 内部就是把这个循环多跑几轮。

注意: 这是单轮演示,真正的 agent(多轮工具调用循环)见 03-react-agent

使用场景

  • 理解 tool calling 的底层机制
  • 学习 tool_callsToolMessage 的传递
  • 不需要 agent 循环的简单工具调用场景
  • 调试工具定义和绑定

学习要点

  1. 使用 .bindTools() 将工具绑定到模型上
  2. 检查 response.tool_calls 判断模型是否想要调用工具
  3. tool.invoke(toolCall) 执行工具,返回 ToolMessage
  4. [HumanMessage, AIMessage(tool_calls), ToolMessage] 喂回模型拿最终答案
  5. 这是单轮演示,agent 会多轮循环这个过程

源码

typescript
import "dotenv/config";
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { HumanMessage } from "@langchain/core/messages";
import { z } from "zod";

if (!process.env.ZHIPUAI_API_KEY) {
  throw new Error("ZHIPUAI_API_KEY is not set in environment variables");
}

// 复用 01-tools 的天气工具定义
const weatherTool = tool(
  async ({ city, unit }) => {
    const weatherData: Record<string, { temp: number; condition: string }> = {
      "北京": { temp: 25, condition: "晴天" },
      "上海": { temp: 22, condition: "多云" },
      "广州": { temp: 28, condition: "小雨" },
    };
    const data = weatherData[city] || { temp: 20, condition: "未知" };
    const temp = unit === "fahrenheit" ? Math.round(data.temp * 9/5 + 32) : data.temp;
    return `${city} 的天气: ${data.condition},${temp}°${unit === "celsius" ? "C" : "F"}`;
  },
  {
    name: "get_current_weather",
    description: "获取指定城市的当前天气",
    schema: z.object({
      city: z.string().describe("城市名称"),
      unit: z.enum(["celsius", "fahrenheit"]).default("celsius").describe("温度单位"),
    }),
  }
);

async function main() {
  try {
    console.log("=== Tool Calling 示例(单轮)===\n");
    console.log("注意:这是单轮工具调用演示,展示 agent 底层机制。");
    console.log("真正的 agent(多轮工具调用循环)见 03-react-agent.ts\n");

    const model = new ChatOpenAI({
      model: "deepseek-v3-1",
      temperature: 0,
      configuration: {
        baseURL: "https://ark.cn-beijing.volces.com/api/coding/v3/",
        apiKey: process.env.ZHIPUAI_API_KEY,
      },
    }).bindTools([weatherTool]);

    const question = "北京今天天气怎么样?用华氏度告诉我温度。";
    console.log("问题:", question);
    console.log("\n---\n");

    // 第一步:调用模型,看它是否要调工具
    const response1 = await model.invoke([new HumanMessage(question)]);
    console.log("模型响应(含 tool_calls):", response1.content);
    console.log("tool_calls:", response1.tool_calls);
    console.log("\n---\n");

    if (response1.tool_calls && response1.tool_calls.length > 0) {
      const toolCall = response1.tool_calls[0];
      console.log(`调用工具: ${toolCall.name}`);
      console.log(`参数:`, toolCall.args);
      console.log("\n---\n");

      // 第二步:执行工具,拿到 ToolMessage
      const toolResult = await weatherTool.invoke(toolCall);
      console.log("工具结果:", toolResult);
      console.log("\n---\n");

      // 第三步:把工具结果喂回模型,拿最终答案
      const response2 = await model.invoke([
        new HumanMessage(question),
        response1,  // AIMessage with tool_calls
        toolResult, // ToolMessage
      ]);
      console.log("最终回答:", response2.content);
    } else {
      console.log("模型未发起工具调用,直接回答:", response1.content);
    }
  } catch (error) {
    console.error("Error during tool calling example:", error);
    process.exit(1);
  }
}

main().catch(console.error);

运行方式

bash
npm run dev src/05-agents/02-tool-calling.ts