1. 项目概述:用LangChain.js构建智能助手
作为一名长期从事AI应用开发的技术人员,我见证了从简单聊天机器人到具备自主决策能力的智能代理(Agent)的技术演进。今天要分享的是如何利用LangChain.js框架,快速构建一个能自主调用工具的智能助手。这个项目特别适合刚接触大模型开发的程序员,因为它完美展现了AI从"被动应答"到"主动思考"的跨越。
传统的大模型调用就像使用计算器——你输入问题,它直接输出答案。而Agent则像雇佣了一位专业助理:你只需要说明目标(比如"比较北京和上海的温差"),它会自主决定是否需要查天气、做计算,并整合最终结果给你。这种能力在复杂任务处理中展现出巨大价值,比如客户服务、数据分析等场景。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心概念解析:理解Agent工作机制
2.1 Agent与普通LLM的本质区别
普通LLM调用是典型的请求-响应模式,其交互流程如下:
- 用户输入问题
- LLM基于训练数据生成回答
- 返回结果
这种模式存在明显局限:
- 无法获取实时信息(如最新天气、股价)
- 无法执行具体操作(如发送邮件、查询数据库)
- 复杂问题需要人工拆解步骤
而Tool-Calling Agent的工作机制则完全不同:
mermaid复制graph TD
A[用户输入目标] --> B(Agent思考是否需要工具)
B -->|是| C[选择合适工具并调用]
C --> D[获取工具返回结果]
D --> B
B -->|否| E[整合信息生成最终回答]
2.2 ReAct模式详解
Agent的核心是ReAct(Reasoning+Acting)模式,这是Yao等人于2022年提出的框架。其典型思考过程如下:
-
思考阶段:分析当前问题和可用工具
- "用户需要知道北京和上海的温差"
- "我需要先获取两地的当前温度"
-
行动阶段:调用适当工具
- 调用天气查询工具,参数
- 调用天气查询工具,参数
-
观察阶段:分析工具返回
- 北京:25°C
- 上海:28°C
-
再思考:决定下一步行动
- "需要计算28-25的差值"
-
最终响应:整合信息输出结果
- "上海比北京高3°C"
这种循环过程使Agent能处理需要多步推理的复杂任务,而无需开发者预先编写具体步骤。
3. 技术选型:为什么选择LangChain.js
3.1 框架对比分析
目前主流的Agent开发框架包括:
| 框架 | 语言 | 核心优势 | 适用场景 |
|---|---|---|---|
| LangChain.js | TypeScript | 类型安全、工具生态完善 | 复杂Agent开发 |
| LangChain | Python | 功能全面、社区资源丰富 | 研究型项目 |
| Vercel AI SDK | JavaScript | 轻量级、UI集成友好 | 简单聊天应用 |
| Semantic Kernel | .NET | 企业级支持、微软生态 | Windows平台应用 |
3.2 LangChain.js的核心优势
- 类型安全:原生TypeScript支持,开发时即可捕获类型错误
- 模型无关:同一套代码可切换OpenAI、Anthropic等不同模型
- 标准化工具定义:使用Zod定义工具参数schema,与前端开发习惯一致
- 完整Agent生态:从基础工具调用到复杂工作流编排的全套解决方案
- 活跃社区:GitHub 13k+ Stars,持续更新的文档和示例
特别对于前端开发者,LangChain.js能无缝集成到现有技术栈中,避免了Python生态的学习成本。
4. 实战开发:构建天气查询Agent
4.1 环境准备
首先初始化项目并安装依赖:
bash复制# 创建项目目录
mkdir weather-agent && cd weather-agent
# 初始化npm项目
npm init -y
# 安装核心依赖
npm install langchain @langchain/openai @langchain/core zod
如需使用其他模型,可安装对应包:
bash复制# 使用Anthropic Claude
npm install @langchain/anthropic
# 使用本地Ollama
npm install @langchain/ollama
4.2 工具定义规范
每个工具需要明确三个要素:
- name:工具的唯一标识符
- description:说明工具用途和使用场景
- schema:定义输入参数的格式和类型
天气查询工具实现
typescript复制import { tool } from "@langchain/core/tools";
import { z } from "zod";
const weatherTool = tool(
async ({ city }) => {
// 模拟数据 - 实际项目应接入真实API
const mockWeather = {
"北京": "晴,25°C,湿度40%",
"上海": "多云,28°C,湿度65%",
"深圳": "阵雨,30°C,湿度80%"
};
return mockWeather[city] || `暂不支持查询${city}的天气`;
},
{
name: "get_weather",
description: "查询指定城市的当前天气状况,包括温度、湿度和天气现象。当用户询问某地天气或需要天气信息进行决策时使用。",
schema: z.object({
city: z.string().describe("要查询的城市名称,如'北京'、'New York'")
})
}
);
计算器工具实现
typescript复制const calculatorTool = tool(
async ({ expression }) => {
try {
// 注意:生产环境应使用更安全的计算库
const result = Function(`"use strict"; return (${expression})`)();
return `${expression} = ${result}`;
} catch {
return `无法计算:${expression}`;
}
},
{
name: "calculator",
description: "执行数学运算,包括加减乘除、指数运算等。当用户需要进行数值计算或比较时使用。",
schema: z.object({
expression: z.string().describe("数学表达式,如'(2+3)*4'、'100*0.08'")
})
}
);
4.3 Agent组装与配置
LLM初始化
typescript复制import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
modelName: "gpt-4o", // 推荐使用最新模型
temperature: 0, // 降低随机性,提高工具调用准确性
maxTokens: 1000 // 确保有足够token处理复杂任务
});
Prompt模板设计
typescript复制import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
const prompt = ChatPromptTemplate.fromMessages([
["system", "你是一个智能助手,可以查询天气和执行计算。回答应简洁专业,使用中文。"],
new MessagesPlaceholder("chat_history"), // 对话历史上下文
["human", "{input}"], // 用户当前输入
new MessagesPlaceholder("agent_scratchpad") // Agent思考过程记录
]);
Agent创建与执行
typescript复制import { createToolCallingAgent } from "langchain/agents";
import { AgentExecutor } from "langchain/agents";
const tools = [weatherTool, calculatorTool];
const agent = createToolCallingAgent({ llm, tools, prompt });
const executor = new AgentExecutor({
agent,
tools,
verbose: true, // 开发时开启,查看详细思考过程
maxIterations: 5 // 防止无限循环
});
4.4 实现对话记忆
typescript复制import { HumanMessage, AIMessage } from "@langchain/core/messages";
const chatHistory: (HumanMessage | AIMessage)[] = [];
async function chat(userInput: string) {
const result = await executor.invoke({
input: userInput,
chat_history: chatHistory
});
// 更新对话历史
chatHistory.push(new HumanMessage(userInput));
chatHistory.push(new AIMessage(result.output));
return result.output;
}
// 示例对话
await chat("北京和上海哪更热?");
await chat("温差具体是多少?"); // Agent会记住之前的对话
5. 生产环境优化策略
5.1 工具设计最佳实践
-
description编写规范
- 明确说明工具功能和适用场景
- 包含典型用例示例
- 避免技术术语,用LLM能理解的自然语言
typescript复制// 好的description示例 description: "查询股票实时价格和涨跌幅。当用户询问某支股票当前行情、今日表现或需要比较多支股票时使用。支持通过股票代码或公司名称查询。" -
schema设计要点
- 每个参数添加详细描述
- 设置合理的默认值
- 使用enum限制可选值
typescript复制schema: z.object({ symbol: z.string().describe("股票代码或公司名称,如'AAPL'、'苹果'"), type: z.enum(['price', 'change', 'all']).default('all') })
5.2 性能与可靠性优化
-
超时处理
typescript复制const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 8000); try { const res = await fetch(url, { signal: controller.signal }); // 处理响应 } catch (error) { if (error.name === 'AbortError') { return "请求超时,请稍后再试"; } throw error; } finally { clearTimeout(timeout); } -
错误处理策略
- 工具内部捕获所有异常
- 返回LLM能理解的错误信息
- 记录详细日志供调试
typescript复制try { // 工具逻辑 } catch (error) { logger.error(`工具调用失败: ${error}`); return `服务暂时不可用: ${error.message}`; } -
限流与重试
typescript复制import pRetry from 'p-retry'; const response = await pRetry( () => fetchWithTimeout(url), { retries: 3, onFailedAttempt: (error) => { console.log(`第${error.attemptNumber}次尝试失败`); } } );
6. 进阶应用场景
6.1 接入真实API服务
替换模拟天气数据为真实API调用:
typescript复制const realWeatherTool = tool(
async ({ city }) => {
const API_KEY = process.env.WEATHER_API_KEY;
const url = `https://api.weatherapi.com/v1/current.json?key=${API_KEY}&q=${city}&lang=zh`;
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`API响应错误: ${res.status}`);
const data = await res.json();
return `${city}天气: ${data.current.condition.text},
温度: ${data.current.temp_c}°C,
湿度: ${data.current.humidity}%,
风速: ${data.current.wind_kph}km/h`;
} catch (error) {
return `查询失败: ${error.message}`;
}
},
// ...其他配置不变
);
6.2 多工具组合应用
添加新闻搜索工具,构建更强大的Agent:
typescript复制const newsTool = tool(
async ({ keyword }) => {
const API_KEY = process.env.NEWS_API_KEY;
const url = `https://newsapi.org/v2/everything?q=${keyword}&apiKey=${API_KEY}`;
const res = await fetch(url);
const data = await res.json();
return data.articles
.slice(0, 3)
.map(article => `${article.title} (${article.url})`)
.join("\n\n");
},
{
name: "search_news",
description: "搜索最新新闻资讯。当用户询问时事、行业动态或需要了解某主题最新进展时使用。",
schema: z.object({
keyword: z.string().describe("搜索关键词,如'AI技术'、'股市行情'")
})
}
);
// 更新工具列表
const tools = [weatherTool, calculatorTool, newsTool];
7. 常见问题排查指南
7.1 工具调用问题
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Agent不调用工具 | description不够明确 | 重写description,包含典型用例 |
| 参数传递错误 | schema定义不清晰 | 为每个参数添加详细描述 |
| 工具返回被忽略 | 返回格式不符合预期 | 确保返回字符串类型,避免复杂对象 |
7.2 性能优化技巧
-
缓存常用结果
typescript复制const cache = new Map(); async function cachedWeather(city) { if (cache.has(city)) { return cache.get(city); } const result = await fetchWeather(city); cache.set(city, result); return result; } -
精简prompt长度
- 限制chat_history长度
- 对历史消息进行摘要
- 移除不必要的中文描述
-
模型参数调优
typescript复制const llm = new ChatOpenAI({ modelName: "gpt-4-turbo-preview", temperature: 0.2, maxTokens: 500, frequencyPenalty: 0.5 // 减少重复内容 });
8. 项目扩展方向
8.1 集成知识库检索
结合RAG(Retrieval-Augmented Generation)技术,使Agent能回答专业问题:
typescript复制import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
// 创建向量存储
const vectorStore = new MemoryVectorStore(new OpenAIEmbeddings());
// 添加文档
await vectorStore.addDocuments([
{ pageContent: "LangChain使用指南", metadata: { source: "doc1" } },
// 更多文档...
]);
// 创建检索工具
const retriever = vectorStore.asRetriever();
const retrievalTool = createRetrieverTool(retriever, {
name: "knowledge_search",
description: "搜索公司知识库获取专业信息"
});
8.2 多Agent协作系统
使用LangGraph实现多个Agent的协同工作:
typescript复制import { StateGraph } from "@langchain/langgraph";
const workflow = new StateGraph({
channels: {
input: { value: null }, // 用户输入
agent1_output: { value: null }, // Agent1输出
final_output: { value: null } // 最终结果
}
});
// 定义节点
workflow.addNode("agent1", async (state) => {
// Agent1处理逻辑
});
workflow.addNode("agent2", async (state) => {
// Agent2处理逻辑
});
// 定义边
workflow.addEdge("agent1", "agent2");
workflow.addEdge("agent2", "final_output");
// 设置入口点
workflow.setEntryPoint("agent1");
const app = workflow.compile();
9. 避坑经验分享
9.1 开发调试技巧
-
verbose模式活用
typescript复制const executor = new AgentExecutor({ agent, tools, verbose: process.env.NODE_ENV !== 'production' }); -
工具调用监控
typescript复制tools.forEach(tool => { const originalFunc = tool.invoke; tool.invoke = async (input) => { console.log(`调用工具: ${tool.name}`, input); const result = await originalFunc.call(tool, input); console.log(`工具返回:`, result); return result; }; });
9.2 生产环境注意事项
-
安全防护措施
- 验证用户输入
- 限制工具调用频率
- 敏感操作需二次确认
typescript复制const safeCalculator = tool( async ({ expression }) => { // 禁止危险操作 if (/process|require|import/.test(expression)) { return "禁止执行该操作"; } // 正常计算逻辑... } ); -
性能监控指标
- 记录每次调用的响应时间
- 监控工具调用成功率
- 跟踪Token使用情况
typescript复制const start = Date.now(); const result = await executor.invoke(input); const duration = Date.now() - start; metrics.track('agent_invoke', { duration, toolCalls: result.intermediateSteps.length, success: !result.output.includes('错误') });
10. 完整项目结构参考
code复制/my-agent
├── src/
│ ├── tools/
│ │ ├── weather.ts # 天气查询工具
│ │ ├── calculator.ts # 计算器工具
│ │ └── news.ts # 新闻搜索工具
│ ├── agents/
│ │ └── main.ts # Agent配置
│ ├── utils/
│ │ ├── logger.ts # 日志工具
│ │ └── safety.ts # 安全检查
│ └── index.ts # 主入口
├── .env # 环境变量
├── package.json
└── tsconfig.json
关键实现代码:
typescript复制// src/index.ts
import 'dotenv/config';
import { initAgent } from './agents/main';
import { chat } from './utils/chat';
async function main() {
const executor = await initAgent();
// 命令行交互
process.stdin.on('data', async (input) => {
const text = input.toString().trim();
if (text === 'exit') process.exit();
const response = await chat(executor, text);
console.log('助手:', response);
});
}
main().catch(console.error);
这个项目结构具有良好的扩展性,可以方便地添加新工具或修改Agent行为。实际开发中,建议结合具体业务需求进行优化调整。
