1. 项目概述:构建你的第一个Tool-Calling Agent
作为一名长期奋战在前端开发一线的工程师,我最近一直在探索如何将AI能力深度整合到实际项目中。今天要分享的是如何用LangChain.js构建一个真正能"自主思考"的Tool-Calling Agent。这不同于简单的聊天机器人,而是一个能主动判断何时使用工具、如何使用工具的智能体。
想象一下这样的场景:用户问"北京和上海今天温差多少度?",传统聊天机器人可能只会根据训练数据编造答案。但一个真正的Agent会自主完成以下操作:
- 调用天气API获取北京实时气温
- 调用天气API获取上海实时气温
- 计算两地温差
- 生成自然语言回复
这种"思考→行动→观察→再思考"的能力,正是现代AI应用最令人兴奋的部分。而LangChain.js让这一切变得异常简单——你只需要几十行TypeScript代码就能实现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心概念解析
2.1 Agent与普通LLM调用的本质区别
在AI领域,"Agent"这个概念经常被滥用,导致很多开发者产生误解。让我们用前端开发熟悉的类比来理解:
普通LLM调用就像jQuery的AJAX请求:
javascript复制// 你明确知道要请求什么、何时请求
$.get('/api/data', function(response) {
// 处理响应
});
Agent则更像React的智能组件:
jsx复制<SmartComponent
goal="解决用户问题"
tools={[api1, api2]}
onComplete={(result) => ...}
/>
关键区别在于:
- 普通调用:开发者完全控制流程,LLM只负责生成文本
- Agent:开发者定义能力和目标,LLM自主决定如何达成
2.2 ReAct模式:Agent的思考框架
ReAct(Reasoning + Acting)是Agent的核心工作模式,类似于前端中的"状态管理→渲染→副作用"循环:
- Reasoning:分析当前状态和用户需求
- Acting:选择并执行最合适的工具
- Observing:接收工具返回结果
- Loop:基于新状态继续思考
这种模式让Agent能够处理需要多步操作的任务,比如:
- 查询多个数据源后综合分析
- 先检索知识库再生成回答
- 分步骤解决复杂数学问题
3. 环境准备与项目搭建
3.1 初始化项目
我们从一个干净的TypeScript项目开始:
bash复制mkdir my-agent && cd my-agent
npm init -y
npm install typescript ts-node @types/node --save-dev
npx tsc --init
安装LangChain核心依赖:
bash复制npm install langchain @langchain/openai @langchain/core zod
3.2 配置TypeScript
确保tsconfig.json包含以下关键配置:
json复制{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
3.3 设置环境变量
创建.env文件存储API密钥:
ini复制OPENAI_API_KEY=your_key_here
WEATHER_API_KEY=your_key_here
建议使用dotenv加载配置:
typescript复制import 'dotenv/config';
if (!process.env.OPENAI_API_KEY) {
throw new Error('缺少OPENAI_API_KEY环境变量');
}
4. 工具定义与实现
4.1 天气查询工具
一个完整的工具需要三个核心部分:
typescript复制import { tool } from "@langchain/core/tools";
import { z } from "zod";
const weatherTool = tool(
// 工具实现函数
async ({ city }) => {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}&units=metric&lang=zh_cn`;
try {
const res = awa
