Agent 智能体:从概念到实践 —— 如何打造一个真正能干活的 AI 助手

51 阅读1分钟

引言:大模型的"天花板"与 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 内部会经历一套精密的协作流程:

  1. 规划与推理(Planning / Reasoning):LLM 首先理解用户意图,将复杂任务拆解为可执行的子步骤。比如"帮我分析一下这个项目代码的质量",Agent 会规划出:先列出文件结构 → 读取关键文件 → 分析代码模式 → 给出报告。

  2. 记忆检索(Memory):Agent 检查是否需要加载历史记忆。如果用户之前讨论过这个项目的背景,Agent 会从记忆模块中提取相关上下文,避免用户重复解释。

  3. 工具调用(Tool Use):Agent 判断需要调用哪些工具,按步骤执行。读取文件、搜索代码、运行测试——每个工具调用都有明确的输入参数和返回结果,Agent 根据上一步的结果决定下一步的行动。

  4. 知识增强(RAG):当任务涉及内部私有知识时,Agent 会从向量数据库中检索相关文档片段,通过 Prompt Template 将检索结果注入到上下文中,让模型获得"超出训练数据"的知识。

  5. 响应与反馈: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 接口规范,只需修改 baseURLmodelName,就能无缝切换到 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 方法,将工具注册到模型上。当用户提出"读取文件并解释代码"的请求时,模型会:

  1. 识别出需要调用 read_file 工具
  2. 生成结构化的 tool_calls,包含工具 ID、名称和参数
  3. 暂停自身文本生成,等待工具返回结果
  4. 将工具返回结果关联到对应的 tool_call_id,拼接到历史会话中
  5. 基于完整的上下文(用户问题 + 工具返回内容)生成最终回答

这个过程非常精妙: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 规划为三步:

  1. 用 Vite 脚手架创建项目 → 调用 CLI 工具
  2. 编写 TodoList 组件代码 → 调用文件写入工具
  3. 启动开发服务器验证 → 再次调用 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 产品——这条路,现在才刚刚开始。