引言:大模型的"天花板"与 Agent 的"破局"
2026 年,大语言模型(LLM)的能力已经毋庸置疑。你可以问它任何问题,它都能给出令人惊叹的回答。但如果你尝试让它"记住上周聊过的事情"、"帮我读一下这个网页然后填个表"、"查一下公司内部最新的销售数据"——你会发现,裸的 LLM 瞬间变得力不从心。
这不是模型不够聪明,而是大模型天生存在几个结构性短板:
第一,LLM 是无状态的。 每一次对话对模型来说都是"初次见面"。你上周和它聊了两个小时的项目背景,今天再问,它已经忘得一干二净。要让模型记住上下文,你需要一个记忆模块(Memory)——把对话历史存到数据库、Redis,或者前端本地存储中,每次请求时把相关的历史记录一起发给模型。
第二,LLM 只能"说",不能"做"。 你让它帮你访问一个网页、写一个文件、调一个 API——它只能告诉你思路,然后等着你自己动手。要让模型真正执行操作,你需要给它装上工具(Tool Use)——让模型能够调用外部函数,读写文件、发送网络请求、执行命令行指令。
第三,LLM 的知识有截止日期。 模型训练完成后,它的知识就凝固在了训练数据的时间点上。最新的世界杯新闻、今天的股价、公司内部的私有文档——这些它统统不知道。你需要**RAG(检索增强生成)来接入外部知识库,需要MCP(Model Context Protocol)**来标准化第三方工具的接入方式。
第四,LLM 缺少领域深度。 让它做 PPT、分析股市并自动买卖、执行复杂的多步骤工作流——这些需要将通用能力"蒸馏"成特定领域的专业技能(Skills)。
而 Agent(智能体),正是围绕以上所有问题,给 LLM 加上 Memory 记忆模块、Tool 工具调用能力、RAG 知识检索、MCP 协议支持和 Skills 技能体系之后的产物。
Agent = LLM + Memory + Tool + RAG + MCP + Skills
这不是一个简单的加法,而是一次质的飞跃——LLM 从"能聊天的模型"变成了"能干活的智能体"。
Agent 的工作原理:从接收到完成任务的全流程
当一个用户以 Prompt 的形式向 Agent 提出一个复杂任务时,Agent 内部会经历一套精密的协作流程:
-
规划与推理(Planning / Reasoning):LLM 首先理解用户意图,将复杂任务拆解为可执行的子步骤。比如"帮我分析一下这个项目代码的质量",Agent 会规划出:先列出文件结构 → 读取关键文件 → 分析代码模式 → 给出报告。
-
记忆检索(Memory):Agent 检查是否需要加载历史记忆。如果用户之前讨论过这个项目的背景,Agent 会从记忆模块中提取相关上下文,避免用户重复解释。
-
工具调用(Tool Use):Agent 判断需要调用哪些工具,按步骤执行。读取文件、搜索代码、运行测试——每个工具调用都有明确的输入参数和返回结果,Agent 根据上一步的结果决定下一步的行动。
-
知识增强(RAG):当任务涉及内部私有知识时,Agent 会从向量数据库中检索相关文档片段,通过 Prompt Template 将检索结果注入到上下文中,让模型获得"超出训练数据"的知识。
-
响应与反馈:Agent 汇总所有工具执行结果和推理过程,生成最终回答,完成用户任务。
整个流程的核心思想是:LLM 是大脑,工具是双手,记忆是经验,RAG 是外部知识库——缺一不可。
LangChain:Agent 开发的"万能胶水"
在 Agent 开发领域,LangChain 是最成熟的开源框架之一。它比 OpenAI 的很多技术还要早诞生,核心价值在于统一和兼容——市面上有几十家大模型厂商,每家都有自己的 API 格式。LangChain 提供了一层抽象,让你用同一套代码调用 DeepSeek、OpenAI、Anthropic 等不同模型。
在我们的实践中,技术栈选型为:
- 后端框架:NestJS(Node.js 企业级框架)
- Agent 框架:LangChain.js(单智能体)+ LangGraph(多智能体协作)
- 工具生态:MCP / RAG / Skills
下面通过一个真实的代码示例来看 LangChain 如何让 LLM 获得工具调用能力。
第一步:绑定模型
import { ChatOpenAI } from '@langchain/openai';
const model = new ChatOpenAI({
modelName: 'deepseek-v4-flash',
apiKey: process.env.DEEPSEEK_API_KEY,
temperature: 0,
configuration: {
baseURL: 'https://api.deepseek.com/v1',
},
});
LangChain 的 ChatOpenAI 兼容 OpenAI 接口规范,只需修改 baseURL 和 modelName,就能无缝切换到 DeepSeek 等国产模型。这种按需加载、即插即用的设计,让团队可以灵活选择性价比最高的模型,而不被某个厂商锁定。
第二步:定义工具
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
const readFileTool = tool(
async ({ filePath }) => {
const content = await fs.readFile(filePath, 'utf-8');
console.log(`[工具调用] read_file(${filePath}) 成功读取 ${content.length} 字节`);
return content;
},
{
name: 'read_file',
description: '用此工具来读取文件内容,当用户要求读取文件、查看代码、分析文件内容时,调用此工具。',
schema: z.object({
filePath: z.string().describe('要读取的文件路径'),
}),
}
);
一个 Tool 由两个核心部分组成:
- 功能函数(异步处理函数):实际执行操作的逻辑。这里是读取文件内容并返回。
- 描述对象:包含
name(工具名称)、description(详细功能说明,覆盖使用场景和参数需求)和schema(参数约束)。schema使用 Zod 定义,确保 LLM 调用工具时传入的参数类型和格式完全正确。
这里有一个容易被忽视但至关重要的设计细节:工具执行中要时刻反馈执行状态。Agent 的任务可能很复杂、很耗时,如果用户长时间看不到任何反馈,会以为程序卡死了而退出。每执行一步就打印日志,是 Agent 开发的基本素养。
第三步:注册工具并创建 Agent
const tools = [readFileTool];
const modelWithTools = model.bindTools(tools);
const messages = [
new SystemMessage('你是一个代码助手,可以使用工具读取文件并解释代码。'),
new HumanMessage('请读取 tool.mjs 文件内容并解释代码'),
];
let response = await modelWithTools.invoke(messages);
messages.push(response);
LangChain 提供了 bindTools 方法,将工具注册到模型上。当用户提出"读取文件并解释代码"的请求时,模型会:
- 识别出需要调用
read_file工具 - 生成结构化的
tool_calls,包含工具 ID、名称和参数 - 暂停自身文本生成,等待工具返回结果
- 将工具返回结果关联到对应的
tool_call_id,拼接到历史会话中 - 基于完整的上下文(用户问题 + 工具返回内容)生成最终回答
这个过程非常精妙:LLM 有"自知之明"——当它判断需要调用工具时,它不会强行编造内容,而是生成一个 tool_calls 数组告知系统"我需要调用这些工具"。工具异步执行完成后,结果通过 id 关联回对应的调用,整个任务上下文被完整地保存在历史会话列表中。LLM 基于自然语言理解这一切,却能做到精确的函数调用编排。
性能优化:Promise.all 并行执行
当 Agent 需要调用多个工具时,如果串行执行——读完文件 A 再读文件 B,再读文件 C——总耗时等于所有操作时间之和。这在复杂任务中可能导致用户等待数十秒甚至数分钟。
解决方案是利用 JavaScript 的 Promise 机制实现并行执行:
const [weatherData, tweetsData] = await Promise.all([
getWeather(), // 耗时 2000ms
getTweets(), // 耗时 500ms
]);
// 总耗时 ≈ 2000ms(取最长的那个),而不是 2500ms
Promise 是 ES6 引入的异步编程核心,它有三种状态:
- Pending(等待中):异步操作尚未完成
- Fulfilled(已成功):操作成功完成,状态从 Pending 变为 Fulfilled
- Rejected(已拒绝):操作失败,状态从 Pending 变为 Rejected
关键特性是:状态只能从 Pending 变到 Fulfilled 或 Rejected 之一,且一旦改变就不可逆转。这保证了异步操作的确定性。
Promise.all() 接收一个 Promise 数组,并行启动所有异步任务,等待所有任务完成后返回结果数组,结果顺序与输入数组顺序严格一致。配合 async/await(ES8 语法),异步代码读起来像同步代码一样清晰。
在 Agent 场景中,当 LLM 判断出多个工具调用之间没有依赖关系时——比如同时读取三个不相关的配置文件——就应该使用 Promise.all 并行执行,将总耗时从"三个操作之和"降到"最慢的那个操作"。这是打造高性能 Agent 的关键技巧。
从 Demo 到产品:Agent 开发的完整图景
回到一个具体的例子:让 Agent "创建一个 React + Vite 的 TodoList 应用"。这个任务会被 LLM 规划为三步:
- 用 Vite 脚手架创建项目 → 调用 CLI 工具
- 编写 TodoList 组件代码 → 调用文件写入工具
- 启动开发服务器验证 → 再次调用 CLI 工具
每一步都是工具调用,每一步的结果影响下一步的决策。这就是手写一个简化版 Claude Code 的核心原理——LLM + 文件系统工具 + 命令行工具。
而 LangGraph 则更进一步,支持多智能体协作:你可以有一个"架构师 Agent"负责规划设计,一个"编码 Agent"负责实现,一个"审查 Agent"负责检查代码质量——它们之间通过消息传递协作,各司其职。
结语:Agent 其实不复杂
回到最开始的问题:Agent 复杂吗?
拆开来看,每一块都不复杂。LLM 本身就能思考和规划,给它装上 Tool 就能干活,配上 Memory 就能记住你要它记住的东西,接上 RAG 就能查询内部知识库。把这些拼在一起——一个知道内部知识、能思考规划、能动手做事的扩展后的大模型——就是一个 Agent。
真正的挑战不在于理解这些概念,而在于工程落地(Harness Engineering)。如何设计稳定的工具接口?如何处理工具调用的异常?如何在并发和串行之间做最优调度?如何让 Agent 的行为可预测、可调试?
这正是 AI 全栈 Agent 产品开发的使命:让 AI 技术通过扎实的工程实践落地,实现 AI 技术的商业价值(FDE——Frontier Development Engineering)。
从 hello-langchain 的第一行代码开始,到支撑千万用户的 Agent 产品——这条路,现在才刚刚开始。