解构 LangChain Tool Calling:从 Schema 定义到 Agent 执行循环的深度解析

8 阅读4分钟

摘要: 在 2026 年的今天,仅仅会调用 model.invoke 已经无法满足复杂的业务需求。本文将带你从零开始,通过对比“基础大模型调用”与“工具增强型智能体”的代码实现,深入剖析 AI Agent 的核心架构(LLM + Tools + Loop)。我们将手把手教你如何让 AI 拥抱“手脚”,实现真正的自主行动。


🧠 引言:Prompt Engineering 已死,Agentic Engineering 当立

如果你还在通过简单的 console.log(await model.invoke("你好")) 来使用 AI,那么你可能正在浪费大模型的潜力。

回顾我们对 AI 应用的认知,已经发生了翻天覆地的变化:

  • 过去(LLM Era): 我们认为 AI 是一个超级大脑,通过精心设计的 Prompt(提示词)来获取答案。
  • 现在(Agent Era): 我们意识到 AI 应该是一个智能体(Agent) 。它不仅有大脑(LLM),还有记忆(Memory)、知识库(RAG)和手脚(Tools)。

正如我们在核心理念中所总结的:

AI Agent = LLM(思考) + Memory(记忆) + Tools(行动) + RAG(知识)

今天,我们将重点攻克 Tools(工具)Loop(循环) ,利用 Node.js 和 LangChain,打造一个能读取文件、分析代码的“Cursor”最小雏形。


🆚第一部分:冰与火之歌——基础模型 vs 智能体

为了让你直观感受两者的区别,我们准备了两段核心代码进行对比。

1. 基础版:单向的“传声筒”

这是最原始的 LLM 调用方式,对应我们代码库中的基础调用逻辑。

import { ChatOpenAI } from '@langchain/openai';
import dotenv from 'dotenv';
dotenv.config();

// 1. 初始化模型
const model = new ChatOpenAI({
    modelName: process.env.MODEL_NAME,
    apiKey: process.env.OPENAI_API_KEY,
    configuration: {
        baseURL: process.env.OPENAI_API_BASE_URL
    }
});

// 2. 单次对话
const response = await model.invoke("介绍下高德地图");
console.log(response.content);

痛点分析:
这段代码只能进行单次、无状态的交互。它像一个被蒙上眼睛的天才,虽然知识渊博,但无法感知外部世界,也无法执行任何操作。如果让它写代码,它只能凭空想象,无法读取现有文件。

2. 进阶版:拥有“手脚”的智能体

这是我们今天的主角,一个具备文件读取能力的 Agent。

// ... 导入必要的模块 (LangChain, fs, zod) ...

// 1. 定义工具 (Tools) - 给 AI 装上手脚
const readFileTool = tool(
  async ({ path }) => {
    const content = await fs.readFile(path, 'utf-8');
    return content; // 返回文件内容
  },
  {
    name: 'read_file',
    description: '当用户需要读取文件内容时调用此工具',
    schema: z.object({ path: z.string().describe('文件路径') })
  }
);

⚙️ 第二部分:核心架构解密——Agent 的“心跳”

让 AI 变得智能的关键,不在于工具本身,而在于 Agent Loop(智能体循环) 。这是 AI 思考、行动、观察的闭环。

1. 绑定工具

首先,我们将工具交给模型,让它知道“我能做什么”。

const modelWithTools = model.bindTools([readFileTool]);

2. 构建记忆(Message History)

Agent 需要记住刚才发生了什么,所以我们维护了一个 messages 数组。

const messages = [
  new SystemMessage("你是一个代码助手..."), // 角色设定
  new HumanMessage('请读取当前文件并解释代码') // 用户指令
];

3. 核心循环 (The Loop)

这是最精彩的部分,也是 while 循环存在的意义。

let response = await modelWithTools.invoke(messages);

// 【关键】Agent Loop:只要 AI 想调用工具,就一直循环
while (response.tool_calls && response.tool_calls.length > 0) {
  console.log(`[检测到工具调用]`);
  
  // Step 1: 并发执行工具 (Promise.all 提升效率)
  const toolResults = await Promise.all(
    response.tool_calls.map(async (toolCall) => {
      const tool = findToolByName(toolCall.name);
      return await tool.invoke(toolCall.args); // 执行工具
    })
  );

  // Step 2: 将工具执行结果(Observation)塞回给 AI
  response.tool_calls.forEach((toolCall, index) => {
    messages.push(
      new ToolMessage({
        content: toolResults[index], // 工具返回的数据
        tool_call_id: toolCall.id // 关联 ID
      })
    );
  });

  // Step 3: 让 AI 基于新信息进行下一轮思考
  response = await modelWithTools.invoke(messages);
}

// 循环结束,输出最终结果
console.log(response.content);

💡 第三部分:深度思考——为什么这样设计?

这种架构设计解决了传统 AI 开发的三大痛点:

1. 为什么用 while 循环?

大模型本质上是“短视”的,一次只能做一个决定。

  • 第一轮: 你让它解释代码,它想:“我得先读文件” -> 调用 read_file
  • 第二轮: 你把文件内容给它,它想:“哦,原来是这样” -> 生成解释。
    如果没有 while 循环,AI 就会在第一步卡住,无法完成任务。

2. 为什么用 Promise.all()

如果 AI 同时需要读取 10 个文件,你是让它一个一个读(串行),还是同时读(并行)?
Promise.all() 让 Agent 能够并发执行多个工具,这是实现高性能 Agent 的关键。

3. 从 Cursor 到 Manus

  • Cursor 的本质: 就是 LLM + File System Tools。它通过工具读写文件,通过循环不断修正代码。
  • Manus 的本质:LLM + Web Browsing Tools + Planning。它能拆解复杂任务,通过多步工具调用完成上网、搜索、下单等操作。

🚀 结语:拥抱 Agentic Engineering

2026 年,AI 开发的范式已经从 Prompt Engineering(提示词工程) 转向了 Agentic Engineering(智能体工程)

作为全栈开发者,我们不再仅仅是调用 API,而是构建 AI 的大脑(LLM)记忆(Memory)手脚(Tools)

下一步建议:

  1. 扩展工具箱: 尝试加入 write_file(写文件)和 execute_command(执行命令)工具。
  2. 引入记忆: 使用 ConversationBufferMemory 让 AI 记住上下文。
  3. 接入 RAG: 让 AI 能基于你的私有文档回答问题。

代码即智能,工具即力量。 现在,你已经拥有了构建属于自己的 AI 助手的钥匙。