手把手构建AI Agent:基于LangChain实现股价查询智能体

手把手构建AI Agent:基于LangChain实现股价查询智能体 1. 项目概述从概念到实践的AI Agent之旅最近和不少同行交流发现大家一提到AI Agent要么觉得是遥不可及的黑科技要么就停留在“不就是个聊天机器人吗”的浅层认知。其实AI Agent的核心魅力在于它让大语言模型LLM从一个“博学的健谈者”变成了一个“能动手的执行者”。今天我就想用一个最接地气的例子——手把手实现一个能查询实时股价的智能体来彻底拆解AI Agent的三大核心支柱LLM、Tools工具和记忆体Memory。无论你是前端、后端还是对AI感兴趣的开发者只要会用Node.js写个简单的HTTP服务就能跟着我一起从零到一搭建一个属于你自己的“数字股票小秘书”。这个项目听起来简单但它麻雀虽小五脏俱全。它要求LLM能理解我们“帮我看看腾讯的股价”这样的自然语言指令然后调用一个外部的股票查询工具Tool去获取真实数据最后还能结合对话历史记忆体进行更连贯的交流比如你接着问“那它今天涨了还是跌了”。整个过程就是AI Agent最经典的“感知-规划-执行”循环的微型演练。我们将完全基于Node.js环境使用目前最主流、最轻量的开源框架来构建避开那些庞大复杂的商业平台专注于理解底层原理。读完本文你不仅能搞懂这些概念更能获得一个可以直接运行、甚至扩展功能的代码仓库。2. AI Agent核心概念深度拆解在开始敲代码之前我们必须把地基打牢。很多人把AI Agent想象得过于复杂其实它的核心架构可以概括为一个“大脑”和它的“四肢”与“记事本”。2.1 LLM智能体的“大脑”与决策核心LLM即大语言模型是AI Agent的“大脑”。它的核心职责是理解和规划。当我们对Agent说“查一下苹果公司的股价”LLM需要做以下几件事意图识别理解用户的指令是“查询股价”而不是闲聊或执行其他任务。信息提取从指令中提取关键实体Entity这里是“苹果公司”。这里有一个经典陷阱LLM需要区分用户指的是苹果Apple Inc.这家科技公司还是水果“苹果”。这通常需要结合上下文记忆体或通过工具调用进一步澄清。规划与决策判断完成这个意图需要调用哪个工具Tool。LLM内部有一份它“知道如何操作”的工具清单它会根据意图匹配最合适的工具并生成调用这个工具所需的精确参数。注意LLM本身并不“知道”实时股价。它就像一个极其博学但足不出户的顾问知道世界上存在“股票查询API”这种东西也知道调用它需要“股票代码”这个参数但它自己无法直接获取数据。这就是为什么需要Tools。目前我们通常通过API调用云端LLM服务如OpenAI的GPT系列、Anthropic的Claude、或国内的各种大模型平台。在开发阶段为了节省成本并快速迭代我们可以使用轻量级的本地模型或小参数模型进行逻辑测试待流程跑通后再接入更强大的模型。2.2 Tools智能体的“四肢”与能力扩展如果说LLM是大脑那么Tools就是Agent的“手和脚”是其与真实世界交互、执行具体任务的能力载体。一个Tool本质上是一个函数它有着明确的输入、输出和功能描述。以我们的股价查询Agent为例它需要一个getStockPrice工具。这个工具的描述可能如下名称get_stock_price描述根据公司名称或股票代码查询该股票的实时最新价格。参数symbol(字符串类型表示公司名或股票代码如 “AAPL” 或 “腾讯”)返回一个包含股价、公司名、涨跌幅等信息的JSON对象。LLM在决定调用此工具后会生成一个符合该函数签名的调用请求例如get_stock_price(“AAPL”)。然后Agent的“执行引擎”会实际运行这个函数即调用一个真实的股票API并将返回的结果如{“price”: 172.34, “change”: 1.2%}交回给LLM。实操心得设计Tool时描述description至关重要。清晰、无歧义的描述能极大提高LLM调用工具的准确率。例如将参数描述为“股票代码或公司全称”就比只写“股票名称”要好得多。此外Tools的能力决定了Agent的上限。你可以为Agent装备邮件发送、数据库查询、文件操作等任何你能用代码实现的工具让它真正“无所不能”。2.3 记忆体Memory智能体的“记事本”与上下文管理器记忆体是让对话变得连贯、智能的关键。没有记忆体的Agent每次对话都是独立的你无法进行多轮复杂的交互。记忆体主要分为两类短期记忆/对话记忆保存当前会话中的历史消息Human和AI的对话轮次。这直接决定了LLM能够看到的上下文长度。当你说“它今天涨了还是跌了”LLM需要从短期记忆中知道“它”指代的是上一轮查询的股票。长期记忆保存跨越多个会话的、结构化的关键信息。例如用户可能说过“我主要关注科技股”Agent可以将此信息存入长期记忆在未来推荐股票或新闻时优先考虑科技板块。实现长期记忆通常需要向量数据库来存储和检索嵌入Embedding。在我们的股价查询Demo中为了简化我们会先实现短期记忆。这意味着我们的Agent能记住当前对话中查询过的股票从而支持上文提到的指代性追问。3. 技术选型与项目环境搭建理解了核心概念后我们就要选择趁手的“兵器”。我们的目标是轻量、快速上手、概念清晰因此选型如下3.1 框架选择为什么是LangChain虽然市面上有LangChain、LlamaIndex、Semantic Kernel等多种框架但对于快速构建一个功能完整的AgentLangChain仍然是生态最丰富、社区最活跃、文档最齐全的选择。它提供了构建Agent所需的所有标准化组件LLM、Tools、Memory、Chains让我们能像搭积木一样快速组装。更重要的是它的设计哲学非常贴近我们刚讨论的核心概念学习成本相对较低。替代方案考量你也可以使用更底层的OpenAI Assistant API直接构建但它将你锁定在单一供应商且对核心组件的控制力较弱。对于学习和深度定制从LangChain开始是更好的选择。3.2 开发环境与依赖安装首先确保你的系统已安装Node.js版本18或以上。接着我们创建一个新的项目并安装核心依赖。# 1. 创建项目目录并初始化 mkdir stock-price-agent cd stock-price-agent npm init -y # 2. 安装LangChain核心包和OpenAI SDK我们将使用GPT作为示例LLM npm install langchain langchain/openai # 3. 安装用于构建HTTP Agent服务器和调用外部API的辅助包 npm install express axios dotenvlangchain: LangChain的核心库。langchain/openai: LangChain官方维护的OpenAI集成包比通用包更稳定。express: 我们将构建一个简单的Web服务器来提供Agent服务。axios: 用于调用外部股票API。dotenv: 用于管理环境变量如API密钥。3.3 获取并配置API密钥我们需要两个关键的API密钥OpenAI API Key用于调用GPT模型作为我们Agent的“大脑”。你需要在OpenAI平台注册并获取。股票数据API Key我们将使用一个免费的金融数据API如Alpha Vantage或Twelvedata。这里以Twelvedata为例去其官网注册一个免费账户即可获得API Key。在项目根目录创建.env文件用于安全地存储密钥# .env 文件 OPENAI_API_KEYsk-your-openai-api-key-here STOCK_API_KEYyour-twelve-data-api-key-here重要安全提示务必把.env文件添加到.gitignore中切勿将包含真实密钥的文件提交到Git等版本控制系统。4. 核心模块实现手把手构建股价查询智能体现在我们开始编写核心代码。项目结构将分为几个清晰的模块。4.1 实现股票查询工具Tool在tools/stockTool.js中我们创建第一个核心工具。// tools/stockTool.js import axios from ‘axios’; import { Tool } from “langchain/core/tools”; import ‘dotenv/config’; // 加载环境变量 export class StockPriceTool extends Tool { name “get_stock_price”; description “根据公司名称或股票代码获取该股票的实时最新价格和涨跌幅。输入应为公司名如’Apple’或股票代码如’AAPL’。对于中文公司请使用股票代码如’00700’代表腾讯以确保准确性。”; constructor() { super(...arguments); } /** _call方法是Tool的核心执行实际任务 */ async _call(input) { try { // 1. 处理输入这里可以添加逻辑将公司名映射为股票代码。 // 为简化我们假设输入已经是代码或能被API直接识别的名称。 const symbol input.trim(); // 2. 调用真实股票API (以Twelvedata为例) const apiKey process.env.STOCK_API_KEY; const url https://api.twelvedata.com/price?symbol${symbol}apikey${apiKey}; const response await axios.get(url); const data response.data; // 3. 处理API响应 if (data.price) { // 为了获取更多信息如涨跌幅可以调用quote接口 const quoteUrl https://api.twelvedata.com/quote?symbol${symbol}apikey${apiKey}; const quoteResponse await axios.get(quoteUrl); const quoteData quoteResponse.data; return 股票 ${symbol} (${quoteData.name || ‘N/A’}) 的当前价格为 $${data.price}。较前一日收盘价变动为 ${quoteData.percent_change || ‘N/A’}%。; } else { return 未能找到股票代码或公司名为 “${symbol}” 的信息。请检查输入是否正确。; } } catch (error) { console.error(“股票查询工具出错:”, error); return 查询股价时出现错误${error.message}。请稍后再试或检查网络。; } } }代码解析与注意事项我们继承了LangChain的Tool基类这保证了我们的工具能无缝集成到Agent框架中。description属性写得非常详细这是“教导”LLM何时以及如何使用该工具的关键。好的描述能减少幻觉和错误调用。在_call方法中我们不仅获取价格还尝试获取了公司名称和涨跌幅使返回信息更友好。同时加入了完整的错误处理确保Agent在工具失败时也能给出用户友好的反馈而不是直接崩溃。4.2 构建智能体Agent并集成记忆体在agent/stockAgent.js中我们将LLM、Tool和Memory组装起来。// agent/stockAgent.js import { OpenAI } from “langchain/openai”; import { initializeAgentExecutorWithOptions } from “langchain/agents”; import { StockPriceTool } from “../tools/stockTool.js”; import { BufferMemory } from “langchain/memory”; import { ChatPromptTemplate, MessagesPlaceholder } from “langchain/core/prompts”; export async function createStockAgent() { // 1. 初始化LLM大脑 const llm new OpenAI({ openAIApiKey: process.env.OPENAI_API_KEY, temperature: 0, // 设置为0使输出更确定、更专注于工具调用 modelName: “gpt-3.5-turbo”, // 使用性价比高的模型可替换为gpt-4等 }); // 2. 准备工具四肢 const tools [new StockPriceTool()]; // 3. 创建记忆体记事本 const memory new BufferMemory({ memoryKey: “chat_history”, // 存储在记忆中的键名 returnMessages: true, // 以消息格式返回便于LLM处理 inputKey: “input”, // 输入键 outputKey: “output”, // 输出键 }); // 4. 创建提示模板为Agent设定角色和上下文 const prompt ChatPromptTemplate.fromMessages([ [“system”, “你是一个专业的股票查询助手。你可以根据用户提供的公司名称或股票代码查询实时股价。请清晰、准确地回答用户的问题。如果用户的问题与股票无关请礼貌地告知你的能力范围。”], new MessagesPlaceholder(“chat_history”), // 此处将注入对话历史 [“human”, “{input}”], // 此处将注入用户当前输入 new MessagesPlaceholder(“agent_scratchpad”), // 此处是Agent思考工具调用的暂存区 ]); // 5. 初始化Agent执行器 const executor await initializeAgentExecutorWithOptions({ tools, llm, memory, prompt, agentType: “openai-functions”, // 使用OpenAI函数调用格式这是目前最稳定高效的方式 verbose: true, // 开发时开启可以看到Agent的思考过程 }); return executor; }关键点解析BufferMemory这是我们选择的短期记忆实现。它会自动保存最近的对话轮次到内存中。returnMessages: true确保数据格式与ChatModel兼容。提示模板Prompt这是指挥Agent行为的“剧本”。System Message定义了Agent的角色和能力范围。MessagesPlaceholder是LangChain的占位符运行时会被实际的对话历史chat_history和Agent的思考过程agent_scratchpad动态填充。agentType: “openai-functions”这是关键。它告诉LangChain使用OpenAI的“函数调用”Function Calling功能。在这种模式下LLM会输出一个结构化的函数调用请求而不是自然语言这使得工具调用极其精准和可靠。这是目前构建生产级Agent的首选方式。verbose: true在开发阶段务必开启。它会在控制台打印出Agent的完整思考链ReAct模式包括“Thought”思考、“Action”选择工具、“Observation”工具返回结果这对于调试和理解Agent行为至关重要。4.3 创建Web服务与交互接口最后我们在index.js中创建一个简单的Express服务器提供HTTP API来与我们的Agent交互。// index.js import express from ‘express’; import { createStockAgent } from ‘./agent/stockAgent.js’; import ‘dotenv/config’; const app express(); const port 3000; // 中间件解析JSON格式的请求体 app.use(express.json()); // 全局变量保存Agent实例生产环境需考虑更佳的管理方式 let agentExecutor; // 启动时初始化Agent (async () { try { agentExecutor await createStockAgent(); console.log(“✅ 股票查询智能体初始化成功”); } catch (error) { console.error(“❌ 智能体初始化失败:”, error); process.exit(1); } })(); // 定义查询端点 app.post(‘/api/query’, async (req, res) { const { message } req.body; if (!message || typeof message ! ‘string’) { return res.status(400).json({ error: “请输入有效的查询消息。” }); } if (!agentExecutor) { return res.status(503).json({ error: “智能体正在初始化请稍候。” }); } try { console.log(用户查询: “${message}”); // 调用Agent执行器传入用户输入 const result await agentExecutor.invoke({ input: message }); console.log(智能体回复: “${result.output}”); res.json({ response: result.output, // 可以返回更多调试信息如使用的工具等 }); } catch (error) { console.error(“处理查询时出错:”, error); res.status(500).json({ error: “智能体处理您的请求时出现了问题。” }); } }); // 健康检查端点 app.get(‘/health’, (req, res) { res.json({ status: ‘ok’, agentInitialized: !!agentExecutor }); }); app.listen(port, () { console.log( 智能体服务已启动监听端口 http://localhost:${port}); console.log( 请使用 POST 请求访问 /api/query 进行查询); });5. 运行测试与效果演示现在让我们启动这个智能体看看它的实际表现。5.1 启动服务与基础查询在终端运行node index.js看到“✅ 股票查询智能体初始化成功”和服务器启动信息即表示成功。使用curl或 Postman 等工具进行测试curl -X POST http://localhost:3000/api/query \ -H “Content-Type: application/json” \ -d ‘{“message”: “苹果公司现在的股价是多少”}’预期输出Agent会调用工具并返回类似“股票 AAPL (Apple Inc.) 的当前价格为 $172.34。较前一日收盘价变动为 1.2%。”的信息。5.2 测试记忆体功能这是体现Agent智能的关键。我们进行连续对话测试第一轮查询curl … -d ‘{“message”: “腾讯的股价呢”}’返回腾讯股价信息。第二轮查询依赖记忆curl … -d ‘{“message”: “它今天涨了吗”}’预期效果由于BufferMemory保存了上一轮对话LLM能从上下文中理解“它”指代的是“腾讯”从而再次调用get_stock_price工具查询腾讯股票并对比价格告诉你涨跌情况。如果关闭记忆体Agent将无法理解“它”指的是什么。5.3 查看思考过程Debug利器由于我们在初始化Agent时设置了verbose: true在服务端控制台你会看到类似以下的详细日志用户查询: “苹果公司现在的股价是多少” Thought: 用户想查询苹果公司的股价。我需要使用 get_stock_price 工具。 Action: { “name”: “get_stock_price”, “args”: {“input”: “AAPL”} } Observation: 股票 AAPL (Apple Inc.) 的当前价格为 $172.34。较前一日收盘价变动为 1.2%。 Thought: 我已经获取到了股价信息可以回答用户了。 Final Answer: 苹果公司 (Apple Inc.) 的当前股价为 172.34美元今日上涨了1.2%。这个日志清晰地展示了Agent的“思考-行动-观察”循环是排查Agent是否错误理解意图、是否选错工具、工具是否返回异常结果的终极武器。6. 常见问题、优化与扩展方向在实际开发和测试中你肯定会遇到各种问题。这里我总结了一些典型坑点和优化思路。6.1 常见问题排查速查表问题现象可能原因解决方案Agent回复“我不知道如何回答”或直接拒绝。1. System Prompt定义的角色或能力范围太窄。2. Tool的描述不够清晰LLM无法匹配。1. 优化System Prompt明确告知Agent“你是一个股票查询助手请使用工具回答问题”。2. 细化Tool的description包含更具体的关键词和用例。Agent没有调用工具而是尝试自己编造股价。1. LLM的temperature参数过高导致创造性过强。2. 提示模板中未正确引导Agent使用工具。1. 将temperature设为0或接近0的值。2. 在System Prompt中强调“你必须使用提供的工具来获取实时数据”。工具调用失败返回网络或API错误。1. API密钥未正确设置或已过期。2. 股票代码格式不正确如中文名直接传递。3. 免费API有调用频率限制。1. 检查.env文件和环境变量加载。2. 在Tool的_call方法中添加公司名到股票代码的映射逻辑。3. 添加请求延迟、错误重试机制或升级API套餐。记忆体不工作Agent无法理解“它”。1.BufferMemory配置错误memoryKey等未与提示模板中的占位符对应。2. 在调用executor.invoke()时未正确传递或管理会话ID。1. 检查prompt中的MessagesPlaceholder(“chat_history”)与memory的memoryKey是否一致。2. 对于多用户场景需要为每个会话创建独立的BufferMemory实例并通过会话ID管理。6.2 性能与体验优化工具调用优化当前的工具是同步调用网络API可能会阻塞主线程。对于耗时工具可以考虑将其封装为异步任务或利用LangChain的Tool基类对并发和超时进行更好管理。成本控制LLM的Token消耗是主要成本。可以通过以下方式优化精简上下文定期清理BufferMemory中的过旧消息或使用ConversationSummaryMemory来压缩历史。缓存结果对相同的股票查询请求可以在短时间内缓存结果避免重复调用LLM和股票API。准确性提升当用户输入“苹果”时可能指水果、公司或电影。可以在Tool调用前增加一个“澄清”步骤或使用一个专门的“实体解析”工具/微服务将模糊的公司名精确映射到股票代码。6.3 项目扩展方向这个Demo只是一个起点你可以在此基础上将它扩展成一个真正有用的个人助手增加更多金融工具get_stock_news: 获取公司相关新闻。calculate_portfolio_value: 计算模拟投资组合的价值。set_price_alert: 设置股价提醒需要持久化存储和后台任务。集成长期记忆引入向量数据库如Chroma、Pinecone将用户表达的投资偏好“我喜欢新能源股票”存入长期记忆实现个性化服务。构建多模态Agent接入文生图模型让Agent可以根据股票表现生成数据图表接入语音模型实现语音交互。部署与集成将Express服务部署到云服务器如Railway、Fly.io并为其开发一个简单的聊天前端网页或移动端或者集成到Slack、Discord、微信等通讯平台中。通过这个从零到一的项目你应该能清晰地感受到AI Agent不再是空中楼阁。它是由LLM提供认知由Tools赋予能力由Memory保持连续性的一个可构建、可调试、可扩展的系统。掌握这三大核心你就拿到了开启下一代人机交互应用的钥匙。