文章目录一、不是直接调用大模型接口——LLM 的五大短板二、Agent 的工作流程三、Agent 开发框架 LangChain3.1 为什么需要框架3.2 LangChain 核心抽象1LLM 统一接口2Tool 工具定义3Tool 调用的底层机制——LLM 的自知之明四、LLM Tool 调用性能优化4.1 Promise 基础回顾4.2 实战并行执行多个 Tool五、LangChain 框架总结六、全文总结核心结论核心知识点复盘常见问题 / 避坑指南最值钱的 Agent 开发技能从概念、原理到 LangChain 实战一次性讲透。一、不是直接调用大模型接口——LLM 的五大短板很多人以为 AI 应用就是调一下大模型 API把 prompt 丢进去拿结果。实际生产环境中裸调 LLM 会遇到五个绕不开的问题短板问题描述解决方案无记忆上周聊过的内容LLM 完全不记得StatelessMemory 模块数据库 / Redis / 前端存储不会动手让它访问网页、操作文件它只能给你思路Tool Use 模块不懂内部知识公司内部文档、私有数据LLM 训练时没见过RAG检索增强生成信息滞后最新的新闻、实时数据不在预训练语料中MCP / Function Call做不了复杂任务做 PPT、分析股市自动买卖等长链路操作Skills技能编排 / 蒸馏核心认知Agent 就是给 LLM 装上大脑Memory 手脚Tool 知识库RAG 技能包Skills。Claude Code、Cursor、Manus、小龙虾这些明星产品本质都是LLM 能力扩展的 Agent。二、Agent 的工作流程一个完整的 Agent 处理任务的过程如下User Prompt用户提出复杂任务 ↓ ┌─────────────────────────┐ │ LLM Planning/Reasoning │ ← 规划 推理拆解任务步骤 └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ 需要加载 Memory 吗 │ ← 回忆历史上下文 └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ 需要调用 Tool 吗 │ ← 分步骤调用多个工具读文件/写文件/执行命令 └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ RAG 检索 Prompt 模板 │ ← 从知识库查询相关内容拼入 Prompt └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ Response → 返回给用户 │ ← 任务完成 └─────────────────────────┘整个流程的核心驱动力是LLM 自身的推理能力Reasoning它自主判断每一步需要什么、调用什么、何时结束。我们开发者要做的就是把 Memory、Tool、RAG 这些能力挂载上去。三、Agent 开发框架 LangChain3.1 为什么需要框架直接调 OpenAI 接口也能做 Tool Call但问题是各家 LLM 接口不统一OpenAI 的 tool_calls 格式和 DeepSeek、文心一言都不一样工具管理繁琐参数校验、结果回传、异常处理都要手写多 Agent 协作困难多个 Agent 之间的消息路由、状态同步是地狱级复杂度LangChain 就是来解决这些问题的。技术栈推荐Node.js (NestJS) LangChain (单 Agent) LangGraph (多 Agent)3.2 LangChain 核心抽象1LLM 统一接口langchain/openai的ChatOpenAI类兼容所有 OpenAI-API 兼容的模型DeepSeek、通义千问等按需切换// llm-demo.mjs — LangChain 统一 LLM 调用importdotenv/config;import{ChatOpenAI}fromlangchain/openai;constmodelnewChatOpenAI({modelName:deepseek-v4-flash,// 模型名称apiKey:process.env.DEEPSEEK_API_KEY,// API Keytemperature:0,// 温度0确定性输出适合工具调用configuration:{baseURL:https://api.deepseek.com/v1// 兼容 OpenAI 接口的 Base URL}});// 一行 invoke等价于 openai.chat.completions.createconstresponseawaitmodel.invoke(用一句话介绍 Agent 是什么);console.log(response.content);关键设计ChatOpenAI封装了底层的 HTTP 调用细节换模型只需改baseURL和modelName业务代码零改动。2Tool 工具定义Tool 是 Agent 的手脚让 LLM 从想变成做。LangChain 使用langchain/core/tools的tool()把js变成LLM的tool 函数定义工具配合zod做参数校验。一个 Tool 由两部分组成组成部分说明处理函数异步真正的执行逻辑如fs.readFile()描述对象name工具名、description告诉 LLM 什么时候用、schemazod 参数约束// tool.mjs — 定义文件读取工具importdotenv/config;import{ChatOpenAI}fromlangchain/openai;import{tool}fromlangchain/core/tools;import{HumanMessage,// role: userSystemMessage,// role: systemToolMessage,// role: toolAIMessage// role: assistant}fromlangchain/core/messages;importfsfromnode:fs/promises;import{z}fromzod;// zod 提供运行时类型约束constmodelnewChatOpenAI({modelName:deepseek-v4-flash,apiKey:process.env.DEEPSEEK_API_KEY,temperature:0,configuration:{baseURL:https://api.deepseek.com/v1}});// 定义 Tool constreadFileTooltool(// 第一部分异步处理函数 —— 真正干活的代码async({filePath}){constcontentawaitfs.readFile(filePath,utf-8);// 每次工具调用都应输出反馈Agent 任务可能很耗时console.log([工具调用] read_file(${filePath}) 成功读取${content.length}字节);returncontent;// 返回值会作为 ToolMessage 喂回 LLM},// 第二部分描述对象 —— LLM 据此判断何时调用、传什么参数{name:read_file,// 工具名称LLM 通过它引用工具description:用此工具来读取文件内容。当用户要求读取文件、查看代码、 分析文件内容时调用此工具。输入文件路径可以是相对路径或绝对路径。,schema:z.object({filePath:z.string().describe(要读取的文件路径),// zod 参数约束})});// 绑定工具到模型 consttools[readFileTool];constmodelWithToolsmodel.bindTools(tools);// LangChain 提供的抽象// 构建对话 constmessages[newSystemMessage(你是一个代码助手可以使用工具读取文件并解释代码。 工作流程 1. 用户要求读取文件时立即调用 read_file 工具。 2. 等待工具返回文件内容。 3. 基于文件内容进行分析和解释。),newHumanMessage(请读取 tool.mjs 文件内容并解释代码),];// 第一轮LLM 决定调用工具 letresponseawaitmodelWithTools.invoke(messages);messages.push(response);// 把 AIMessage含 tool_calls加入历史// 此时 response.tool_calls 包含// [{ id: call_xxx, name: read_file, arguments: { filePath: tool.mjs } }]3Tool 调用的底层机制——LLM 的自知之明这是最容易踩坑的核心原理必须彻底理解LLM 知道自己不会读文件。当它判断需要调用工具时不会硬编一个答案而是停下来返回tool_calls数组。一个tool_calls条目的结构// response.tool_calls[0] 的结构{id:call_abc123,// 唯一 ID关联后续的 ToolMessagename:read_file,// 要调用的工具名args:{filePath:tool.mjs// 参数 —— 完全由 schema 约束}}id是关键后续执行工具后返回的ToolMessage必须带上相同的tool_call_idLLM 才能把工具结果和之前的调用请求对应起来。// 第二轮执行工具 回传结果 // 遍历 LLM 返回的 tool_calls逐一执行for(consttoolCallofresponse.tool_calls){if(toolCall.nameread_file){constresultawaitreadFileTool.invoke(toolCall.args);// 构造 ToolMessageid 必须匹配这样才能组成完整上下文messages.push(newToolMessage({content:result,tool_call_id:toolCall.id// ← 关键关联 LLM 的调用请求}));}}// 第三轮LLM 基于工具结果生成最终回复 constfinalResponseawaitmodelWithTools.invoke(messages);console.log(finalResponse.content);// 输出对文件内容的解释和分析...一句话总结tool_calls.id是 LLM 和 Tool 之间的快递单号丢了就关联不上。四、LLM Tool 调用性能优化复杂任务中LLM 可能调用多个不同的 Tool或对同一 Tool多次调用。如果串行执行总耗时 每次调用耗时之和。4.1 Promise 基础回顾概念说明PromiseES6 异步语法三种状态Pending→Fulfilled/Rejected且不可逆awaitES8 语法糖让异步代码看起来像同步最优雅的写法Promise.all()并行执行多个 Promise全部完成才返回结果顺序与输入一致4.2 实战并行执行多个 Tool// 并行执行多个工具调用 // LLM 返回了多个 tool_calls如同时读3个文件// ❌ 错误写法串行 await慢for(consttoolCallofresponse.tool_calls){constresultawaitexecuteTool(toolCall);// 一个接一个等messages.push(newToolMessage({content:result,tool_call_id:toolCall.id}));}// 总耗时 t1 t2 t3// ✅ 正确写法Promise.all 并行执行consttoolResultsawaitPromise.all(response.tool_calls.map(async(toolCall){constresultawaitexecuteTool(toolCall);// 每个独立运行return{result,tool_call_id:toolCall.id};}));// 一次性 push 所有结果for(const{result,tool_call_id}oftoolResults){messages.push(newToolMessage({content:result,tool_call_id}));}// 总耗时 ≈ max(t1, t2, t3) —— 接近最慢的那个适用场景多个工具调用之间没有依赖关系时才可并行。如果 Tool B 的输入依赖 Tool A 的输出必须串行。五、LangChain 框架总结LangChain 比 OpenAI 的 Transformer 架构还早诞生它解决的核心问题是能力LangChain 模块说明统一 LLM 调用langchain/openai的ChatOpenAI兼容所有 OpenAI-API 格式的模型工具定义 校验langchain/core/tools的tool()zod声明式定义工具自动生成 JSON Schema消息管理HumanMessage/SystemMessage/ToolMessage/AIMessage类型化的消息对象避免手动拼 JSONTool 绑定model.bindTools(tools)自动将 Tool Schema 注入请求LLM 按需返回tool_calls一句话理解 LangChain它把调 LLM从手写 fetch 拼 JSON升级为类型安全的工程化开发让 Agent 开发从 demo 级别走向生产级别。六、全文总结核心结论Agent LLM Memory Tool RAG Skills。裸调 LLM 只是聊天机器人加上这些扩展才是能自动干活的智能体。Agent 工作流是一个闭环用户提需求 → LLM 规划 → 调用工具 → 获取结果 → 继续推理 → 输出答案。LangChain 是 Agent 开发的事实标准框架核心就三件事统一 LLM 接口、管理 Tool 生命周期、编排消息流转。Tool 机制的本质是 LLM 的自知之明——它知道自己不会读文件所以停下来返回tool_calls而不是瞎编。核心知识点复盘知识点一句话总结Agent 定义LLM 能力扩展Memory/Tool/RAG/SkillsAgent 工作流Planning → Memory → Tool → RAG → ResponseLangChain 价值统一 LLM 接口 Tool 管理 消息编排Tool 两部分处理函数干活 描述对象告诉 LLM 怎么用tool_calls.id“快递单号”关联工具请求和结果Promise.all无依赖的多个 Tool 可以并行执行常见问题 / 避坑指南Q1LLM 调用了不存在的工具怎么办在toolMap中查不到时返回明确的错误信息给 LLM如未知工具: xxxLLM 会自行调整策略。Q2tool_calls 的 id 可以随便写吗不可以。必须用 LLM 返回的原始id原封不动填到ToolMessage.tool_call_id。写错了 LLM 无法关联会导致上下文错乱。Q3什么时候用Promise.all什么时候串行多个 Tool 调用互不依赖时用Promise.all并行加速如果 Tool B 需要 Tool A 的输出作为输入必须串行await。Q4temperature 为什么要设为 0temperature 0让 LLM 输出确定性的tool_calls避免同样的 prompt 每次调用不同工具。工具调用场景强烈建议设为 0。本文基于 LangChain.js DeepSeek 实战编写代码完整可运行适合作为 Agent 开发的入门到进阶参考。
从零到一搞懂 AI Agent:原理、工作流程、LangChain Tool Use 与实战
发布时间:2026/7/7 1:24:16
文章目录一、不是直接调用大模型接口——LLM 的五大短板二、Agent 的工作流程三、Agent 开发框架 LangChain3.1 为什么需要框架3.2 LangChain 核心抽象1LLM 统一接口2Tool 工具定义3Tool 调用的底层机制——LLM 的自知之明四、LLM Tool 调用性能优化4.1 Promise 基础回顾4.2 实战并行执行多个 Tool五、LangChain 框架总结六、全文总结核心结论核心知识点复盘常见问题 / 避坑指南最值钱的 Agent 开发技能从概念、原理到 LangChain 实战一次性讲透。一、不是直接调用大模型接口——LLM 的五大短板很多人以为 AI 应用就是调一下大模型 API把 prompt 丢进去拿结果。实际生产环境中裸调 LLM 会遇到五个绕不开的问题短板问题描述解决方案无记忆上周聊过的内容LLM 完全不记得StatelessMemory 模块数据库 / Redis / 前端存储不会动手让它访问网页、操作文件它只能给你思路Tool Use 模块不懂内部知识公司内部文档、私有数据LLM 训练时没见过RAG检索增强生成信息滞后最新的新闻、实时数据不在预训练语料中MCP / Function Call做不了复杂任务做 PPT、分析股市自动买卖等长链路操作Skills技能编排 / 蒸馏核心认知Agent 就是给 LLM 装上大脑Memory 手脚Tool 知识库RAG 技能包Skills。Claude Code、Cursor、Manus、小龙虾这些明星产品本质都是LLM 能力扩展的 Agent。二、Agent 的工作流程一个完整的 Agent 处理任务的过程如下User Prompt用户提出复杂任务 ↓ ┌─────────────────────────┐ │ LLM Planning/Reasoning │ ← 规划 推理拆解任务步骤 └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ 需要加载 Memory 吗 │ ← 回忆历史上下文 └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ 需要调用 Tool 吗 │ ← 分步骤调用多个工具读文件/写文件/执行命令 └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ RAG 检索 Prompt 模板 │ ← 从知识库查询相关内容拼入 Prompt └──────────┬──────────────┘ ↓ ┌─────────────────────────┐ │ Response → 返回给用户 │ ← 任务完成 └─────────────────────────┘整个流程的核心驱动力是LLM 自身的推理能力Reasoning它自主判断每一步需要什么、调用什么、何时结束。我们开发者要做的就是把 Memory、Tool、RAG 这些能力挂载上去。三、Agent 开发框架 LangChain3.1 为什么需要框架直接调 OpenAI 接口也能做 Tool Call但问题是各家 LLM 接口不统一OpenAI 的 tool_calls 格式和 DeepSeek、文心一言都不一样工具管理繁琐参数校验、结果回传、异常处理都要手写多 Agent 协作困难多个 Agent 之间的消息路由、状态同步是地狱级复杂度LangChain 就是来解决这些问题的。技术栈推荐Node.js (NestJS) LangChain (单 Agent) LangGraph (多 Agent)3.2 LangChain 核心抽象1LLM 统一接口langchain/openai的ChatOpenAI类兼容所有 OpenAI-API 兼容的模型DeepSeek、通义千问等按需切换// llm-demo.mjs — LangChain 统一 LLM 调用importdotenv/config;import{ChatOpenAI}fromlangchain/openai;constmodelnewChatOpenAI({modelName:deepseek-v4-flash,// 模型名称apiKey:process.env.DEEPSEEK_API_KEY,// API Keytemperature:0,// 温度0确定性输出适合工具调用configuration:{baseURL:https://api.deepseek.com/v1// 兼容 OpenAI 接口的 Base URL}});// 一行 invoke等价于 openai.chat.completions.createconstresponseawaitmodel.invoke(用一句话介绍 Agent 是什么);console.log(response.content);关键设计ChatOpenAI封装了底层的 HTTP 调用细节换模型只需改baseURL和modelName业务代码零改动。2Tool 工具定义Tool 是 Agent 的手脚让 LLM 从想变成做。LangChain 使用langchain/core/tools的tool()把js变成LLM的tool 函数定义工具配合zod做参数校验。一个 Tool 由两部分组成组成部分说明处理函数异步真正的执行逻辑如fs.readFile()描述对象name工具名、description告诉 LLM 什么时候用、schemazod 参数约束// tool.mjs — 定义文件读取工具importdotenv/config;import{ChatOpenAI}fromlangchain/openai;import{tool}fromlangchain/core/tools;import{HumanMessage,// role: userSystemMessage,// role: systemToolMessage,// role: toolAIMessage// role: assistant}fromlangchain/core/messages;importfsfromnode:fs/promises;import{z}fromzod;// zod 提供运行时类型约束constmodelnewChatOpenAI({modelName:deepseek-v4-flash,apiKey:process.env.DEEPSEEK_API_KEY,temperature:0,configuration:{baseURL:https://api.deepseek.com/v1}});// 定义 Tool constreadFileTooltool(// 第一部分异步处理函数 —— 真正干活的代码async({filePath}){constcontentawaitfs.readFile(filePath,utf-8);// 每次工具调用都应输出反馈Agent 任务可能很耗时console.log([工具调用] read_file(${filePath}) 成功读取${content.length}字节);returncontent;// 返回值会作为 ToolMessage 喂回 LLM},// 第二部分描述对象 —— LLM 据此判断何时调用、传什么参数{name:read_file,// 工具名称LLM 通过它引用工具description:用此工具来读取文件内容。当用户要求读取文件、查看代码、 分析文件内容时调用此工具。输入文件路径可以是相对路径或绝对路径。,schema:z.object({filePath:z.string().describe(要读取的文件路径),// zod 参数约束})});// 绑定工具到模型 consttools[readFileTool];constmodelWithToolsmodel.bindTools(tools);// LangChain 提供的抽象// 构建对话 constmessages[newSystemMessage(你是一个代码助手可以使用工具读取文件并解释代码。 工作流程 1. 用户要求读取文件时立即调用 read_file 工具。 2. 等待工具返回文件内容。 3. 基于文件内容进行分析和解释。),newHumanMessage(请读取 tool.mjs 文件内容并解释代码),];// 第一轮LLM 决定调用工具 letresponseawaitmodelWithTools.invoke(messages);messages.push(response);// 把 AIMessage含 tool_calls加入历史// 此时 response.tool_calls 包含// [{ id: call_xxx, name: read_file, arguments: { filePath: tool.mjs } }]3Tool 调用的底层机制——LLM 的自知之明这是最容易踩坑的核心原理必须彻底理解LLM 知道自己不会读文件。当它判断需要调用工具时不会硬编一个答案而是停下来返回tool_calls数组。一个tool_calls条目的结构// response.tool_calls[0] 的结构{id:call_abc123,// 唯一 ID关联后续的 ToolMessagename:read_file,// 要调用的工具名args:{filePath:tool.mjs// 参数 —— 完全由 schema 约束}}id是关键后续执行工具后返回的ToolMessage必须带上相同的tool_call_idLLM 才能把工具结果和之前的调用请求对应起来。// 第二轮执行工具 回传结果 // 遍历 LLM 返回的 tool_calls逐一执行for(consttoolCallofresponse.tool_calls){if(toolCall.nameread_file){constresultawaitreadFileTool.invoke(toolCall.args);// 构造 ToolMessageid 必须匹配这样才能组成完整上下文messages.push(newToolMessage({content:result,tool_call_id:toolCall.id// ← 关键关联 LLM 的调用请求}));}}// 第三轮LLM 基于工具结果生成最终回复 constfinalResponseawaitmodelWithTools.invoke(messages);console.log(finalResponse.content);// 输出对文件内容的解释和分析...一句话总结tool_calls.id是 LLM 和 Tool 之间的快递单号丢了就关联不上。四、LLM Tool 调用性能优化复杂任务中LLM 可能调用多个不同的 Tool或对同一 Tool多次调用。如果串行执行总耗时 每次调用耗时之和。4.1 Promise 基础回顾概念说明PromiseES6 异步语法三种状态Pending→Fulfilled/Rejected且不可逆awaitES8 语法糖让异步代码看起来像同步最优雅的写法Promise.all()并行执行多个 Promise全部完成才返回结果顺序与输入一致4.2 实战并行执行多个 Tool// 并行执行多个工具调用 // LLM 返回了多个 tool_calls如同时读3个文件// ❌ 错误写法串行 await慢for(consttoolCallofresponse.tool_calls){constresultawaitexecuteTool(toolCall);// 一个接一个等messages.push(newToolMessage({content:result,tool_call_id:toolCall.id}));}// 总耗时 t1 t2 t3// ✅ 正确写法Promise.all 并行执行consttoolResultsawaitPromise.all(response.tool_calls.map(async(toolCall){constresultawaitexecuteTool(toolCall);// 每个独立运行return{result,tool_call_id:toolCall.id};}));// 一次性 push 所有结果for(const{result,tool_call_id}oftoolResults){messages.push(newToolMessage({content:result,tool_call_id}));}// 总耗时 ≈ max(t1, t2, t3) —— 接近最慢的那个适用场景多个工具调用之间没有依赖关系时才可并行。如果 Tool B 的输入依赖 Tool A 的输出必须串行。五、LangChain 框架总结LangChain 比 OpenAI 的 Transformer 架构还早诞生它解决的核心问题是能力LangChain 模块说明统一 LLM 调用langchain/openai的ChatOpenAI兼容所有 OpenAI-API 格式的模型工具定义 校验langchain/core/tools的tool()zod声明式定义工具自动生成 JSON Schema消息管理HumanMessage/SystemMessage/ToolMessage/AIMessage类型化的消息对象避免手动拼 JSONTool 绑定model.bindTools(tools)自动将 Tool Schema 注入请求LLM 按需返回tool_calls一句话理解 LangChain它把调 LLM从手写 fetch 拼 JSON升级为类型安全的工程化开发让 Agent 开发从 demo 级别走向生产级别。六、全文总结核心结论Agent LLM Memory Tool RAG Skills。裸调 LLM 只是聊天机器人加上这些扩展才是能自动干活的智能体。Agent 工作流是一个闭环用户提需求 → LLM 规划 → 调用工具 → 获取结果 → 继续推理 → 输出答案。LangChain 是 Agent 开发的事实标准框架核心就三件事统一 LLM 接口、管理 Tool 生命周期、编排消息流转。Tool 机制的本质是 LLM 的自知之明——它知道自己不会读文件所以停下来返回tool_calls而不是瞎编。核心知识点复盘知识点一句话总结Agent 定义LLM 能力扩展Memory/Tool/RAG/SkillsAgent 工作流Planning → Memory → Tool → RAG → ResponseLangChain 价值统一 LLM 接口 Tool 管理 消息编排Tool 两部分处理函数干活 描述对象告诉 LLM 怎么用tool_calls.id“快递单号”关联工具请求和结果Promise.all无依赖的多个 Tool 可以并行执行常见问题 / 避坑指南Q1LLM 调用了不存在的工具怎么办在toolMap中查不到时返回明确的错误信息给 LLM如未知工具: xxxLLM 会自行调整策略。Q2tool_calls 的 id 可以随便写吗不可以。必须用 LLM 返回的原始id原封不动填到ToolMessage.tool_call_id。写错了 LLM 无法关联会导致上下文错乱。Q3什么时候用Promise.all什么时候串行多个 Tool 调用互不依赖时用Promise.all并行加速如果 Tool B 需要 Tool A 的输出作为输入必须串行await。Q4temperature 为什么要设为 0temperature 0让 LLM 输出确定性的tool_calls避免同样的 prompt 每次调用不同工具。工具调用场景强烈建议设为 0。本文基于 LangChain.js DeepSeek 实战编写代码完整可运行适合作为 Agent 开发的入门到进阶参考。