2024 AI Agent入门必备:从零实现LangChain智能体

2026-06-05阅读 0热度 0
智能体

什么是智能体(Agent)?

从被动响应的聊天机器人到主动决策的智能体

传统聊天机器人遵循“一问一答”的机械流程——用户输入,模型直接输出,对话随即终结。它就像一位只会照本宣科的客服代表,绝不可能主动发起任何操作。

AI Agent 实战教程:基于 LangChain 从零搭建基础智能体

而Agent(智能体)彻底颠覆了这一范式——它被设计为“行动导向”的实体,而非单纯的“应答装置”。两者本质区别可用一段代码直观对比:

// 聊天机器人:单次执行const response = await chat(userInput);console.log(response);// 智能体:循环执行,直到任务完成while (!taskCompleted) {const action = await agent.think(); // 思考:下一步做什么?const result = await action.execute(); // 行动:调用工具agent.observe(result); // 观察:检查结果}

ReAct 模式:智能体核心工作流

ReAct(Reasoning + Acting)是Agent最关键的运作机制,通过“推理→行动→观察”的闭环实现自主决策。简单来说,就是让大模型自己动脑分析、动手执行、再审视结果。

┌─────────────────────────────────────────────────────────┐│ ReAct 循环 ││ ││ 用户提问 ──→ ? 推理 (Reason) ──→ ⚡ 行动 (Act) ││↑ ↓││└─── ?️ 观察 (Observe) ←─┘││ (工具执行结果反馈) │└─────────────────────────────────────────────────────────┘

举例说明:用户问“北京今天天气怎么样,适合穿什么衣服?”

步骤阶段内容
1? 推理需要先获取天气数据,决定调用天气查询工具
2⚡ 行动执行 get_weather("北京")
3?️ 观察获得结果:“晴天,25°C,湿度40%”
4? 推理根据温度判定,25°C 建议穿短袖
5⚡ 行动生成最终回答

智能体的三大核心组件

一个完整的Agent系统由三个基础模块构成:

组件作用类比
LLM(决策引擎)理解用户意图,决定调用哪个工具大脑
Tools(工具集)Agent 可调用的外部能力手脚
Memory(记忆)保存对话历史和中间状态笔记

Agent 类型与适用场景

LangChain 提供了多种 Agent 实现,选对类型可以显著提升任务成功率:

Agent 类型适用场景特点
Zero-Shot ReAct工具少(<5个)、任务简单(1-2步)最常用,通过提示工程让模型自主决策
Structured ReAct中等复杂度,需要多步规划使用预定义模板,调用更稳定
Conversational ReAct多轮对话场景内置记忆机制,保持上下文

实现基础 Agent

最简单的 Agent

先跑通一个最基础的示例,彻底理解核心概念:

// basic-agent.tsimport { ChatOpenAI } from "@langchain/openai";import { tool } from "@langchain/core/tools";import { z } from "zod";import { createAgent } from "langchain";import dotenv from "dotenv";dotenv.config();async function basicAgent() {// 1. 初始化模型const model = new ChatOpenAI({openai_api_key: process.env.BAILIAN_API_KEY,configuration: { baseURL: process.env.BAILIAN_BASE_URL },model: "qwen-plus",temperature: 0.3,});// 2. 定义一个简单的计算器工具const calculator = tool(async ({ expression }) => {try {const result = Function('"use strict";return (' + expression + ')')();return `计算结果:${result}`;} catch {return `计算错误:表达式 "${expression}" 无效`;}},{name: "calculator",description: "执行数学计算。当用户需要计算数学表达式时使用此工具。",schema: z.object({expression: z.string().describe("数学表达式,如 '2+3*4' 或 '(10-5)*2'"),}),});// 3. 创建 Agent(自动处理 ReAct 循环)const agent = createAgent({model,tools: [calculator],systemPrompt: "你是一个数学计算助手。当用户需要计算时,使用 calculator 工具。",});// 4. 执行任务const result = await agent.invoke({messages: [{ role: "user", content: "帮我计算 25 * 4 + 10 等于多少" }],});console.log("最终回复:", result.messages[result.messages.length - 1].content);}basicAgent();

预期输出:

最终回复:25 * 4 + 10 等于 110。

完整 Agent 循环可视化

create_agent 内部自动封装了 ReAct 循环,但为了精确理解每一步,我们手动实现循环逻辑:

// agent-loop.tsimport { ChatOpenAI } from "@langchain/openai";import { tool } from "@langchain/core/tools";import { z } from "zod";import { HumanMessage, AIMessage, ToolMessage } from "@langchain/core/messages";import dotenv from "dotenv";dotenv.config();async function visualAgentLoop() {const model = new ChatOpenAI({apiKey: process.env.DASHSCOPE_API_KEY,configuration: {baseURL: process.env.DASHSCOPE_API_URL,},model: "qwen-plus",temperature: 0.3,});// 定义两个工具const calculator = tool(async ({ expression }) => {const result = Function('"use strict";return (' + expression + ')')();return result.toString();},{name: "calculator",description: "数学计算",schema: z.object({ expression: z.string() }),});const weather = tool(async ({ city }) => {const weathers: Record<string, string> = {北京: "晴天,25°C",上海: "多云,28°C",深圳: "阵雨,30°C",};return weathers[city] || `${city}:晴,22°C`;},{name: "get_weather",description: "查询天气",schema: z.object({ city: z.string() }),});const tools = [calculator, weather];const toolsByName = Object.fromEntries(tools.map(t => [t.name, t]));// 绑定工具到模型const modelWithTools = model.bindTools(tools);// 用户输入const userInput = "北京今天天气怎么样?另外帮我算一下 (100-25)*3+50";console.log(`? 用户:${userInput}n`);console.log("? 开始 Agent 循环...n");console.log("=".repeat(50));// 初始化消息列表const messages = [new HumanMessage(userInput)];let iteration = 0;const maxIterations = 5;while (iteration < maxIterations) {iteration++;console.log(`n? 第 ${iteration} 轮迭代`);// 1. 推理阶段:调用 LLMconsole.log(" ? 推理中...");const response = await modelWithTools.invoke(messages);messages.push(response);// 2. 检查是否需要调用工具const toolCalls = (response as any).tool_calls;if (!toolCalls || toolCalls.length === 0) {console.log(" ✅ 任务完成,无更多工具调用");break;}// 3. 行动阶段:执行工具console.log(` ⚡ 决定调用 ${toolCalls.length} 个工具:`);for (const toolCall of toolCalls) {console.log(` - ${toolCall.name}(${JSON.stringify(toolCall.args)})`);const tool = toolsByName[toolCall.name];if (tool) {const result = await tool.invoke(toolCall.args);console.log(` ✅ 返回:${result}`);// 将工具结果添加到消息中messages.push(new ToolMessage({content: result,tool_call_id: toolCall.id,}));}}console.log(" ?️ 观察结果已记录,继续下一轮...");}console.log("n" + "=".repeat(50));console.log("n? 最终回答:");const lastMessage = messages[messages.length - 1];console.log(lastMessage.content);}visualAgentLoop();

预期输出:

? 用户:北京今天天气怎么样?另外帮我算一下 (100-25)*3+50? 开始 Agent 循环...==================================================? 第 1 轮迭代 ? 推理中... ⚡ 决定调用 2 个工具: - get_weather({"city": "北京"}) ✅ 返回:晴天,25°C - calculator({"expression": "(100-25)*3+50"}) ✅ 返回:275 ?️ 观察结果已记录,继续下一轮...? 第 2 轮迭代 ? 推理中... ✅ 任务完成,无更多工具调用==================================================? 最终回答:北京今天天气晴朗,气温为25°C。另外,计算结果为:(100 - 25) × 3 + 50 = 275。

带记忆的 Agent

利用 LangGraph 的 StateGraph 实现持久化的 Agent 记忆:

// agent-with-memory.tsimport { ChatOpenAI } from "@langchain/openai";import { tool } from "@langchain/core/tools";import { z } from "zod";import { StateGraph, MessagesAnnotation, START, END } from "@langchain/langgraph";import { HumanMessage, AIMessage } from "@langchain/core/messages";import dotenv from "dotenv";dotenv.config();async function agentWithMemory() {const model = new ChatOpenAI({openai_api_key: process.env.BAILIAN_API_KEY,configuration: { baseURL: process.env.BAILIAN_BASE_URL },model: "qwen-plus",temperature: 0.3,});// 定义工具const calculator = tool(async ({ expression }) => {const result = Function('"use strict";return (' + expression + ')')();return result.toString();},{name: "calculator",description: "数学计算",schema: z.object({ expression: z.string() }),});const modelWithTools = model.bindTools([calculator]);// 定义 Agent 节点async function callModel(state: typeof MessagesAnnotation.State) {const response = await modelWithTools.invoke(state.messages);return { messages: [response] };}// 构建 StateGraphconst workflow = new StateGraph(MessagesAnnotation).addNode("agent", callModel).addEdge(START, "agent").addEdge("agent", END);const app = workflow.compile();// 模拟多轮对话console.log("?️ 带记忆的多轮对话n");console.log("=".repeat(50));let state = { messages: [] };const conversations = ["我叫张三,是一名前端开发","我刚才说我叫什么名字?","计算 15 * 8 等于多少?","我的职业是什么?"];for (const input of conversations) {console.log(`n? 用户:${input}`);state = { messages: [...state.messages, new HumanMessage(input)] };const result = await app.invoke(state);const aiResponse = result.messages[result.messages.length - 1];console.log(`? AI:${aiResponse.content}`);state = result;}}agentWithMemory();

预期输出:

?️ 带记忆的多轮对话==================================================? 用户:我叫张三,是一名前端开发? AI:很高兴认识你,张三!作为一名前端开发,你主要使用哪些技术栈呢?比如 HTML、CSS、Ja vaScript,还是有在用 React、Vue 或其他框架?如果有任何前端相关的问题,或者需要帮助实现某个功能,随时告诉我! ?? 用户:我刚才说我叫什么名字?? AI:你刚才说你叫**张三**!?? 用户:计算 15 * 8 等于多少?? AI:15 × 8 = 120。? 用户:我的职业是什么?? AI:你的职业是**前端开发**。

Agent 执行流程详解

核心循环的 5 个阶段

Agent 的执行过程可以拆分为5个阶段:

阶段英文说明示例
1. 感知Perceive接收用户输入和环境状态"北京今天天气怎么样?"
2. 推理ReasonLLM 分析需要什么信息"需要调用天气查询工具"
3. 规划Plan确定调用顺序和参数先查天气,再给出建议
4. 行动Act执行工具调用get_weather("北京")
5. 观察Observe分析工具结果,决定是否继续得到天气数据,可以回答了

如何调试 Agent

方法一:启用详细日志

// 在 createAgent 中设置 verboseconst agent = createAgent({model,tools,systemPrompt: "...",// 新版 API 使用 callbacks 进行监控});

方法二:手动打印中间状态

每一步都打印当前状态:

// 关键调试信息console.log(`[推理] 决定调用工具:${toolName}`);console.log(`[行动] 参数:${JSON.stringify(args)}`);console.log(`[观察] 结果:${result}`);

方法三:限制迭代次数防止无限循环

const maxIterations = 5;let iteration = 0;while (iteration < maxIterations && !isComplete) {iteration++;// Agent 逻辑...}if (iteration >= maxIterations) {console.log("⚠️ 达到最大迭代次数,强制终止");}

什么场景适合使用 Agent?

✅ 适合使用❌ 不适合使用
任务步骤数无法预先确定流程固定、步骤可预测
需要根据中间结果调整策略单次 LLM 调用 + 单次工具就能解决
多步推理(如:先查数据,再分析,最后建议)对延迟要求极高(每次迭代都有 LLM 调用)

前端适配 Agent 开发的技巧

工具设计的原子性原则

每个工具应该只负责一项职责,描述必须精准,避免模糊。来看正反示例:

// ❌ 不好的设计:一个工具做太多事情const badTool = tool(async ({ city, addClothingAdvice }) => { ... },{name: "weather_and_clothing",description: "查询天气并推荐穿搭",// 职责不单一});// ✅ 好的设计:拆分为多个原子工具const weatherTool = tool(async ({ city }) => getWeather(city),{name: "get_weather",description: "查询指定城市的实时天气。当用户询问天气时使用此工具。",});const clothingTool = tool(async ({ temperature }) => suggestClothing(temperature),{name: "suggest_clothing",description: "根据温度推荐穿搭。需要先获取天气数据后才能使用。",});

工具描述编写技巧

工具描述是AI理解工具用途的关键,必须包含:

  • 功能说明:这个工具做什么
  • 触发条件:什么场景下使用
  • 输入格式:需要什么参数
  • 输出说明:返回什么格式
const fileReader = tool(async ({ path }) => { ... },{name: "read_local_file",description: `读取本地文件内容。使用场景:用户需要查看代码文件、阅读文档、分析配置文件时。输入要求:文件路径(支持相对路径,如 ./src/index.ts)输出格式:纯文本内容,如文件过大则自动截断。限制:只能读取项目目录内的文件,最大 1MB。`,schema: z.object({path: z.string().describe("文件路径,相对于项目根目录"),}),});

错误处理最佳实践

工具调用可能失败,需要优雅处理:

const robustTool = tool(async (args) => {try {// 1. 参数校验if (!args.required) {return JSON.stringify({ success: false, error: "缺少必填参数" });}// 2. 超时控制const result = await Promise.race([doSomething(args),timeout(30000),]);// 3. 返回统一格式return JSON.stringify({ success: true, data: result });} catch (error) {// 4. 分类错误返回if (error.message.includes("timeout")) {return JSON.stringify({ success: false, error: "操作超时,请重试" });}return JSON.stringify({ success: false, error: error.message });}},{ name: "robust_tool", description: "...", schema: ... });

基础 Agent 常见问题

问题 1:Agent 不调用工具

现象:Agent 直接回复,不使用你定义的 tools

解决方案:

  • 检查工具 description 是否清晰说明了使用场景
  • 在 systemPrompt 中明确提示可以使用哪些工具
  • 确认模型支持 Function Call(qwen-turbo/plus/max 都支持)
// 优化 systemPromptsystemPrompt: `你是一个智能助手,可以使用以下工具:- get_weather: 查询天气(当用户询问天气时使用)- calculator: 数学计算(当用户需要计算时使用)请根据用户需求主动调用合适的工具。`,

问题 2:工具重复调用或陷入循环

现象:同一个工具被反复调用,或者 Agent 永远不结束

解决方案:

  • 设置最大迭代次数限制
  • 在工具返回中包含"任务完成"信号
  • 优化提示词,添加终止条件
// 设置最大迭代次数const agent = createAgent({model,tools,maxIterations: 5,// 限制最多 5 轮});

问题 3:工具参数传递错误

现象:工具被调用了,但参数格式不对

解决方案:

  • 使用 Zod 严格定义参数 schema
  • 为每个参数添加 .describe() 说明
  • 在工具内部增加参数校验和友好错误提示

问题 4:记忆丢失

现象:Agent 在多轮对话中忘记之前的信息

解决方案:

  • 使用 LangGraph 的 StateGraph + InMemorySa ver 管理状态
  • 确保每次调用都传入相同的 thread_id
import { InMemorySa ver } from "@langchain/langgraph";const checkpointer = new InMemorySa ver();const graph = workflow.compile({ checkpointer });const config = { configurable: { thread_id: "user-session-123" } };const result = await graph.invoke(state, config);

结语

本文带你从零实现了一个基于 LangChain 的基础 Agent,并深入剖析了 ReAct 循环的运作细节。文中如有疏漏,或你有更优的实践思路,欢迎留言探讨。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策