LangGraph与LangSmith实战:构建可观测的JS/TS智能体工作流

发布时间:2026/8/12 12:09:49

LangGraph与LangSmith实战:构建可观测的JS/TS智能体工作流
1. 项目概述为什么是 LangGraph 与 LangSmith如果你正在用 JavaScript 或 TypeScript 捣鼓大语言模型应用大概率已经体验过 LangChain 带来的便利。它能帮你快速串联起提示词、模型和工具。但当你开始构建更复杂的应用比如需要多步骤决策、循环执行或者状态管理的智能体时你可能会发现单纯用链式调用变得有些力不从心代码结构也容易变得混乱。这正是 LangGraph 和 LangSmith 这对“黄金搭档”要解决的问题。LangGraph 不是一个新框架而是 LangChain 生态中一个专门用于构建有状态、多参与者工作流的库。你可以把它想象成一个功能强大的流程图绘制工具只不过你画的每个节点是代码函数而连接线定义了数据流转和决策逻辑。它特别擅长处理那些需要“思考-行动-观察”循环的智能体应用或者任何需要根据中间结果动态决定下一步流程的场景。而 LangSmith则是这个生态的“观察哨”和“调试器”。当你把应用部署到 LangGraph 上运行LangSmith 能帮你追踪每一次调用的输入输出、记录每一步的状态变化、可视化整个执行图谱甚至进行版本比对和性能分析。没有它调试一个复杂的图工作流就像在迷宫里摸黑前进。这个教程的目标很直接带你从零开始用 JS/TS 上手这两个工具。我不会只讲 API 怎么调用而是会结合一个具体的智能体案例拆解每一步的设计思路、常见陷阱以及如何利用 LangSmith 快速定位问题。无论你是想构建一个能联网搜索并总结的助手还是一个能自动处理工单的客服机器人这里面的核心模式都是相通的。2. 核心概念与工具选型解析在动手写代码之前我们需要先统一一下认知理解几个核心概念这能帮你更好地设计自己的图。2.1 LangGraph 的核心状态、节点与边LangGraph 的核心理念围绕“状态”展开。整个图共享一个状态对象这个对象会在各个节点间传递和修改。你可以把它理解为一个共享的“记忆白板”。状态State通常是一个 TypeScript 接口或普通对象。关键设计原则是它应该包含工作流运行所需的所有信息。例如对于一个聊天智能体状态里可能有messages对话历史、next指示下一步该执行哪个节点等字段。LangGraph 推荐使用Annotation来定义状态它能提供更严格的类型检查和序列化支持。节点Nodes节点是图的基本执行单元本质上是一个接收当前状态、返回部分新状态的函数。一个节点只做一件事并且要做得纯粹。比如一个“调用模型”的节点它只负责接收消息列表调用 LLM然后把模型回复追加到状态中。边Edges边定义了控制流即“接下来执行哪个节点”。有两种主要类型条件边Conditional Edges根据当前状态的某些属性动态决定下一个节点。这是实现分支和循环的关键。普通边Regular Edges无条件地指向下一个节点。入口点Entry Point指定图开始运行的第一个节点。一个经典的“智能体”模式就是由三个核心节点构成的循环一个“代理”节点决定做什么、一个“工具执行”节点执行具体动作、一个“结果处理”节点。边则根据代理节点的输出决定是继续循环还是结束。2.2 LangSmith 的角色可观测性的基石LangSmith 是一个 SaaS 平台也提供自托管方案它为 LLM 应用开发提供全链路可观测性。对于 LangGraph 应用它的价值尤为突出追踪与可视化自动记录每一次 LLM 调用、工具执行和节点运行。你可以在 LangSmith UI 上清晰地看到整个图的执行路径每个节点的输入输出耗时多少。调试与复现当应用输出不符合预期时你可以直接找到对应的追踪记录查看每一步的中间状态无需添加繁琐的console.log。你甚至可以基于某个追踪记录重新运行“播放”整个流程方便复现问题。测试与评估你可以将不同的提示词版本、模型或图结构在相同的数据集上运行通过 LangSmith 对比它们的输出结果、成本和延迟从而做出数据驱动的优化决策。监控与告警在生产环境中你可以监控关键指标如延迟、错误率和 token 消耗并设置告警。注意LangSmith 需要注册账号并获取 API 密钥。在开发初期就集成它能为你节省大量后期的调试时间。它的免费套餐对于个人开发和小型项目通常足够。2.3 为什么选择 JS/TS 版本Python 版的 LangChain 生态固然成熟但 Node.js 运行时在构建 Web 后端、Serverless 函数或桌面应用时有其独特优势。JS/TS 版的 LangGraph 和 LangSmith SDK 已经非常完善能提供与 Python 版对等的功能。对于全栈开发者或专注于 Node.js 生态的团队来说直接在熟悉的语言环境中构建 AI 应用能减少上下文切换提升开发效率。本教程将全程使用 TypeScript以获得更好的类型安全和开发体验。3. 环境准备与基础配置让我们从搭建一个最基础的环境开始。这里假设你已经有 Node.js (18) 和 npm/yarn/pnpm 的环境。3.1 初始化项目与安装依赖首先创建一个新目录并初始化项目mkdir langgraph-js-tutorial cd langgraph-js-tutorial npm init -y接下来安装核心依赖。我们将使用 OpenAI 的模型作为 LLM 驱动。npm install langchain/langgraph langchain/core langchain/openai npm install --save-dev typescript ts-node types/nodelangchain/langgraph: LangGraph 核心库。langchain/core: LangChain 的核心基础类型和工具。langchain/openai: OpenAI 模型的 LangChain 集成。typescript,ts-node: 用于 TypeScript 开发。然后初始化 TypeScript 配置npx tsc --init在生成的tsconfig.json中确保target至少是ES2020并且module是commonjs或NodeNext。3.2 配置 LangSmith 追踪要启用 LangSmith你需要先在其官网注册并创建一个项目。之后获取你的 API 密钥。在项目根目录创建.env文件添加你的密钥# OpenAI API Key (用于调用模型) OPENAI_API_KEYsk-your-openai-key-here # LangSmith API Key (用于追踪) LANGCHAIN_API_KEYlsv2-your-langsmith-key-here # 可选设置追踪项目名称默认是 “default” LANGCHAIN_PROJECTmy-langgraph-tutorial实操心得建议在项目一开始就设置好环境变量。LangSmith 的 SDK 会自动检测这些变量。如果你在多个项目间切换通过LANGCHAIN_PROJECT区分它们非常有用这样在 LangSmith UI 中就能清晰地归类追踪记录。现在在你的入口文件例如index.ts的最顶部添加以下代码来配置 LangSmith。这通常只需要做一次import { traceable } from langsmith/traceable; // LangSmith 会自动读取 process.env.LANGCHAIN_API_KEY // 无需显式初始化客户端SDK 会自动处理。实际上对于大多数 LangChain/LangGraph 组件只要你设置了环境变量它们发出的调用就会被自动追踪。但了解traceable这个包装器很有用它可以让你手动包装任何自定义函数使其调用也出现在 LangSmith 的追踪记录中。3.3 创建第一个简单的图验证环境让我们构建一个最简单的图来验证一切是否正常工作。这个图只有一个节点它接收一个名字然后让 LLM 生成一句问候语。首先定义我们的状态。我们将使用Annotation来获得更好的类型支持。// graph/state.ts import { Annotation } from langchain/langgraph; // 使用 Annotation 定义状态模式 export const AgentState Annotation.Root({ // 输入的用户消息 input: Annotationstring, // 模型生成的回复 response: Annotationstring, });接下来创建图本身// graph/simple-graph.ts import { StateGraph, START, END } from langchain/langgraph; import { ChatOpenAI } from langchain/openai; import { AgentState } from ./state.js; // 1. 定义节点函数 async function modelNode(state: typeof AgentState.State): PromisePartialtypeof AgentState.State { const llm new ChatOpenAI({ modelName: gpt-3.5-turbo, temperature: 0, }); // 从状态中获取输入 const userInput state.input; // 构造提示词 const prompt 请用中文友好地打招呼。对方的名字是${userInput}; // 调用模型 const response await llm.invoke(prompt); // 返回要更新到状态中的部分 return { response: response.content as string, }; } // 2. 构建图 export function createSimpleGraph() { const workflow new StateGraph({ // 指定状态模式 schema: AgentState, }); // 添加节点命名为 “call_model” workflow.addNode(call_model, modelNode); // 设置边从 START 到 “call_model” 节点然后到 END workflow.addEdge(START, call_model); workflow.addEdge(call_model, END); // 编译图得到一个可执行对象 const app workflow.compile(); return app; }最后编写入口文件来运行它// index.ts import { createSimpleGraph } from ./graph/simple-graph.js; async function main() { const app createSimpleGraph(); // 初始化状态 const initialState { input: 小明, }; console.log(开始运行简单图...); const stream await app.stream(initialState, { streamMode: values }); for await (const chunk of stream) { // 流式输出每个步骤后的状态快照 console.log(当前状态:, chunk); console.log(---); } // 或者使用非流式调用 // const finalState await app.invoke(initialState); // console.log(最终回复:, finalState.response); } main().catch(console.error);运行npx ts-node index.ts。如果一切配置正确你应该能看到 LLM 生成的问候语并且在 LangSmith 的网页控制台里找到这次运行的完整追踪记录。点开记录你能看到invoke的输入提示词和输出回复以及整个图的执行流程。这个简单的例子验证了你的环境、API 密钥和 LangSmith 连接都是正常的。接下来我们将构建一个更有趣的、具备循环能力的智能体。4. 构建一个具备工具调用能力的循环智能体现在我们来构建一个更真实的智能体一个能根据用户问题决定是否要调用外部工具比如搜索网络来获取信息的助手。这是 LangGraph 最典型的应用场景。4.1 设计智能体状态与工具首先扩展我们的状态定义。一个循环智能体通常需要维护对话历史、中间思考过程以及工具调用结果。// graph/agent-state.ts import { Annotation } from langchain/langgraph; import { BaseMessage } from langchain/core/messages; export const AgentState Annotation.Root({ // 完整的消息历史用于给模型提供上下文 messages: AnnotationBaseMessage[], // 模型最新一次“思考”的输出包含是否调用工具、调用哪个工具等信息 next: Annotationstring, // 可选存储工具执行的结果 toolResult: Annotationstring | null, });接下来我们定义一个模拟的“网络搜索”工具。在实际项目中你可以替换成真实的 Serper、Tavily 等搜索 API。// graph/tools.ts import { tool } from langchain/core/tools; // 使用 tool 装饰器定义一个工具 export const searchTool tool( async ({ query }: { query: string }) { // 模拟网络搜索的延迟和结果 console.log([工具调用] 正在搜索: ${query}); await new Promise(resolve setTimeout(resolve, 500)); // 模拟延迟 // 返回模拟的搜索结果 const mockResults 关于“${query}”的模拟搜索结果 1. 这是一个模拟的搜索结果条目一。 2. 这是模拟的搜索结果条目二包含一些相关信息。 3. 根据模拟数据相关结论是XXX。; return mockResults; }, { name: web_search, description: 当用户的问题需要最新或外部知识时使用此工具进行网络搜索。输入应为搜索查询词。, schema: { type: object, properties: { query: { type: string, description: 要搜索的查询词, }, }, required: [query], }, } );4.2 实现核心节点代理、工具执行与结果处理一个标准的 ReActReasoning Acting智能体循环包含三个核心节点。1. 代理节点Agent Node它的职责是分析当前对话历史和状态决定下一步该做什么直接回复还是调用工具。// graph/nodes/agent-node.ts import { ChatOpenAI } from langchain/openai; import { HumanMessage, AIMessage, ToolMessage } from langchain/core/messages; import { AgentState } from ../agent-state.js; import { searchTool } from ../tools.js; export async function agentNode(state: typeof AgentState.State): PromisePartialtypeof AgentState.State { const llm new ChatOpenAI({ modelName: gpt-3.5-turbo, temperature: 0, // 关键将工具绑定到模型模型才能知道可以调用什么 modelKwargs: { tools: [searchTool], }, }); // 准备给模型的消息历史 const messages state.messages; // 调用模型并开启工具调用功能 const response await llm.withStructuredOutput({ schema: { type: object, properties: { reasoning: { type: string, description: 模型的思考过程 }, action: { type: string, enum: [respond, use_tool], description: 下一步动作直接回复或使用工具 }, tool_input: { type: object, properties: { query: { type: string } }, description: 如果需要调用工具这里是输入参数 }, }, required: [reasoning, action], }, }).invoke(messages); console.log([代理思考] ${response.reasoning}); console.log([代理决策] 动作: ${response.action}); // 将模型的“思考”消息也存入历史方便后续查看可选 const thinkingMessage new AIMessage({ content: response.reasoning, }); // 根据决策设置下一步该去哪个节点 let nextStep: string; if (response.action use_tool response.tool_input) { nextStep call_tool; // 去工具执行节点 // 将工具调用请求也作为消息存储这是一种常见模式 const toolCallMessage new AIMessage({ content: , tool_calls: [{ name: searchTool.name, args: response.tool_input, id: tool_call_${Date.now()}, }], }); return { messages: [...state.messages, thinkingMessage, toolCallMessage], next: nextStep, // 传递工具调用参数方式可以灵活设计。这里我们将其暂存于一个字段。 toolResult: JSON.stringify({ tool: searchTool.name, input: response.tool_input }), }; } else { nextStep end; // 去结束节点或直接生成回复的节点 // 如果直接回复我们需要在后续节点生成回复内容。这里先传递决策。 return { messages: [...state.messages, thinkingMessage], next: nextStep, }; } }2. 工具执行节点Tool Node执行代理节点指定的工具并将结果存回状态。// graph/nodes/tool-node.ts import { AgentState } from ../agent-state.js; import { searchTool } from ../tools.js; import { ToolMessage } from langchain/core/messages; export async function toolNode(state: typeof AgentState.State): PromisePartialtypeof AgentState.State { // 从状态中解析出要调用哪个工具以及参数 // 注意这是一个简化的解析逻辑。在实际中你可能需要更健壮的状态设计。 let toolInput: { query: string } | null null; try { const pendingToolCall state.toolResult ? JSON.parse(state.toolResult) : null; if (pendingToolCall?.tool searchTool.name) { toolInput pendingToolCall.input; } } catch (e) { console.error(解析工具调用参数失败:, e); } if (!toolInput) { // 如果没有有效的工具调用则返回错误信息 const errorMessage new ToolMessage({ content: 工具调用参数无效。, tool_call_id: error_${Date.now()}, }); return { messages: [...state.messages, errorMessage], next: end, // 出错则结束 }; } // 执行工具 const result await searchTool.invoke(toolInput); // 将工具执行结果作为 ToolMessage 存入历史 const toolResultMessage new ToolMessage({ content: result, tool_call_id: tool_call_${Date.now()}, name: searchTool.name, }); console.log([工具执行] 结果长度: ${result.length} 字符); // 更新状态添加结果消息并指示下一步回到代理节点进行下一步思考 return { messages: [...state.messages, toolResultMessage], next: agent, // 循环回去让代理基于新信息再次思考 toolResult: null, // 清空待处理的任务 }; }3. 结果处理/结束节点End Node当代理决定直接回复时由这个节点生成最终的回答。// graph/nodes/end-node.ts import { ChatOpenAI } from langchain/openai; import { AIMessage } from langchain/core/messages; import { AgentState } from ../agent-state.js; export async function endNode(state: typeof AgentState.State): PromisePartialtypeof AgentState.State { const llm new ChatOpenAI({ modelName: gpt-3.5-turbo, temperature: 0.7, // 最终回复可以更有创造性一点 }); // 基于完整的对话历史生成最终回复 const finalResponse await llm.invoke(state.messages); const finalAIMessage new AIMessage({ content: finalResponse.content as string, }); console.log([生成最终回复]); // 返回最终消息并将 next 标记为真正的结束可以用一个特殊值如 “__end__” return { messages: [...state.messages, finalAIMessage], next: __end__, response: finalResponse.content as string, // 也可以单独存一份最终回复 }; }4.3 组装图并设置条件边现在我们将所有节点组装起来并定义它们之间的流转逻辑。// graph/agent-graph.ts import { StateGraph, START, END } from langchain/langgraph; import { AgentState } from ./agent-state.js; import { agentNode } from ./nodes/agent-node.js; import { toolNode } from ./nodes/tool-node.js; import { endNode } from ./nodes/end-node.js; export function createAgentGraph() { const workflow new StateGraph({ schema: AgentState, }); // 添加三个节点 workflow.addNode(agent, agentNode); workflow.addNode(call_tool, toolNode); workflow.addNode(end, endNode); // 设置入口点 workflow.addEdge(START, agent); // 设置条件边代理节点之后根据 state.next 的值决定去哪 workflow.addConditionalEdges( agent, // 路由函数根据状态的 next 字段决定下一个节点 (state: typeof AgentState.State) { return state.next; }, // 映射state.next 的值 - 节点名称 { call_tool: call_tool, end: end, // 默认情况如果都不匹配可以去一个错误处理节点或直接结束 } ); // 从工具节点执行完后无条件回到代理节点进行下一步思考 workflow.addEdge(call_tool, agent); // 结束节点指向真正的 END workflow.addEdge(end, END); const app workflow.compile(); return app; }4.4 运行与调试智能体创建一个新的入口文件来运行这个智能体// run-agent.ts import { createAgentGraph } from ./graph/agent-graph.js; import { HumanMessage } from langchain/core/messages; async function main() { const app createAgentGraph(); // 初始化状态包含用户的第一个问题 const initialState { messages: [new HumanMessage(2023年世界杯的冠军是哪支球队)], next: , // 初始为空由第一个代理节点填充 toolResult: null, }; console.log( 开始运行智能体 ); console.log(用户问题: ${initialState.messages[0].content}); console.log(---); // 使用流式调用可以观察每一步的状态变化 const stream await app.stream(initialState, { streamMode: values }); let stepCount 0; for await (const chunk of stream) { stepCount; console.log(步骤 ${stepCount} 完成:); console.log( - 最新消息数: ${chunk.messages.length}); console.log( - 下一步指示 (next): ${chunk.next}); if (chunk.toolResult) { console.log( - 待处理工具任务: ${chunk.toolResult.substring(0, 100)}...); } console.log(---); } // 获取最终状态 const finalState await app.invoke(initialState); const finalMessage finalState.messages[finalState.messages.length - 1]; console.log( 智能体运行结束 ); console.log(最终回复:); console.log(finalMessage.content); } main().catch(console.error);运行npx ts-node run-agent.ts。你会看到控制台输出智能体的思考过程、工具调用决策以及最终回复。同时打开 LangSmith 的追踪页面你应该能看到一个清晰的图执行轨迹包含了多次agent节点和call_tool节点的调用记录。点击任何一个节点都能看到其详细的输入输出。注意事项这个示例是一个简化的 ReAct 实现。在实际的 LangGraph 高级模式中你可能更倾向于使用内置的createReactAgent或createToolCallingAgent等辅助函数它们封装了消息处理、工具调用解析等更复杂的逻辑让代码更简洁。但通过手动构建你能更深刻地理解其底层机制这对于调试和定制高级功能至关重要。5. 利用 LangSmith 进行深度调试与性能分析当你的图变得复杂或者输出不符合预期时LangSmith 就从“观察哨”变成了“诊断仪”。下面分享几个我常用的调试和分析技巧。5.1 追踪可视化与步骤检查在 LangSmith 项目页面找到你刚刚运行的追踪记录并点击进入。你会看到一个类似流程图的视图清晰地展示了agent-call_tool-agent-end的路径。检查输入输出点击每个节点在 LangSmith 中称为 “Steps”在右侧面板查看其详细的输入和输出。对于agent节点输入是完整的消息历史输出是模型的原始响应包含思考过程和工具调用请求。确保模型接收到的上下文是正确的。查看原始提示词展开 LLM 调用的步骤你可以看到实际发送给模型的系统提示词和用户消息。这是排查“模型不按指令行事”问题的第一现场。有时 LangChain 的默认提示词模板可能不符合你的需求。检查工具调用在call_tool节点查看工具接收的参数是否正确。在我们的例子里就是搜索查询词query是否被正确地从模型输出中解析出来。5.2 比较不同运行结果假设你对提示词做了修改或者换用了gpt-4模型想知道效果如何。在 LangSmith 中可以为一个追踪记录添加“标签”或“元数据”比如prompt_v2或model_gpt4。运行几次不同配置的智能体。在 LangSmith 的“数据集与测试”模块你可以手动或通过 API 创建数据集一组输入问题。为你的图配置不同的“设置”如不同的模型、温度参数然后在同一个数据集上运行评估。LangSmith 会自动并排展示不同配置下对同一问题的回答方便你直观对比质量和风格差异。5.3 监控耗时与成本对于生产应用性能和成本至关重要。延迟分析在追踪列表或单个追踪详情中LangSmith 会清晰列出每个步骤的耗时。重点关注 LLM 调用和工具调用的耗时。如果agent节点耗时过长可能是提示词太复杂或模型本身慢。Token 消耗与成本估算对于 OpenAI 模型LangSmith 会估算每次调用的输入/输出 Token 数量并据此计算成本。你可以通过筛选和聚合找出消耗最高的提示词或最频繁的工具调用进行针对性优化。设置告警在 LangSmith 的项目设置中可以配置基于延迟、错误率或成本的告警。例如当平均响应时间超过 5 秒或单次调用成本异常高时你会收到通知。5.4 一个真实的调试案例工具调用解析失败在我早期的一个项目中智能体有时会莫名其妙地结束对话而不是去调用工具。通过 LangSmith我很快定位了问题。现象在 LangSmith 中查看出错的追踪发现agent节点的输出里模型的action字段是”use_tool”但tool_input字段却是null。排查检查发送给模型的提示词发现我对工具调用输出格式的描述不够精确导致模型有时会生成不符合schema的 JSON。解决我做了两件事一是优化了提示词更明确地要求模型必须提供tool_input二是在agentNode函数中增加了更健壮的容错逻辑如果解析失败则让模型重新思考或给出默认回复而不是直接进入错误状态。验证修改后重新运行相同的问题在 LangSmith 中对比新旧两次追踪确认tool_input被正确填充并且流程按预期走到了call_tool节点。实操心得养成“遇事不决看 LangSmith”的习惯。把每次调试都看作一次“现场取证”追踪记录里的输入、输出、耗时就是你的证据。这比在代码里漫无目的地添加console.log要高效和系统得多。6. 高级模式与生产环境考量当你掌握了基础循环智能体的构建后可以探索 LangGraph 更强大的功能来构建更稳健、高效的应用。6.1 预构建模式createReactAgentLangGraph 提供了一些高级预构建模式。例如createReactAgent函数可以快速创建一个标准的 ReAct 智能体它帮你处理了工具调用的绑定、消息格式的转换等繁琐细节。import { createReactAgent } from langchain/langgraph/prebuilt; import { ChatOpenAI } from langchain/openai; import { searchTool } from ./tools.js; const llm new ChatOpenAI({ modelName: gpt-4-turbo }); const tools [searchTool]; const agent createReactAgent({ llm, tools, // 你可以完全自定义提示词 // prompt: customPrompt, }); const config { configurable: { thread_id: user-session-123 } }; const stream await agent.stream({ messages: [{ role: user, content: 今天北京的天气怎么样 }], }, config); for await (const chunk of stream) { // chunk 已经是处理好的消息流 if (agent in chunk) { console.log(chunk.agent.messages[0].content); } if (tools in chunk) { console.log(调用工具: ${chunk.tools.messages[0].content}); } }使用预构建模式可以极大提升开发速度但牺牲了一些灵活性。建议在理解底层原理后再使用以便在需要时能进行深度定制。6.2 持久化状态与多轮对话在我们的例子中状态是临时的。对于聊天机器人我们需要在多次交互间保持状态记忆。LangGraph 通过Checkpointer接口支持状态持久化。内存检查点适用于开发测试重启后状态丢失。数据库检查点可以集成 SQLite、PostgreSQL 等将对话状态持久化到数据库。你需要实现或使用社区提供的Checkpointer适配器。使用方式在调用app.invoke()或app.stream()时传入一个config对象其中包含configurable字段如thread_id。LangGraph 会根据这个 ID 来加载和保存对应的检查点状态。// 伪代码示例 import { MemorySaver } from langchain/langgraph; const memory new MemorySaver(); // 一个内存型的检查点实现 const app workflow.compile({ checkpointer: memory }); const config { configurable: { thread_id: unique-chat-session-id } }; // 第一次调用状态会被保存 const result1 await app.invoke({ messages: [userMessage1] }, config); // 第二次调用会从 memory 中恢复之前的状态实现多轮对话 const result2 await app.invoke({ messages: [userMessage2] }, config);6.3 错误处理与边界情况一个健壮的生产级图必须考虑错误处理。节点内的错误捕获在每个节点函数内部使用try...catch包裹核心逻辑。发生错误时可以向状态中添加错误信息并将next指向一个专门的“错误处理节点”。错误处理节点这个节点可以记录日志、通知用户、尝试恢复或优雅地结束会话。设置超时对于 LLM 调用或外部工具调用务必设置超时防止单个步骤卡死整个工作流。可以在初始化模型或工具时配置timeout选项。验证输入在状态流转到关键节点如工具调用前验证输入数据的完整性和有效性。6.4 性能优化技巧减少不必要的状态传递状态对象应该尽可能精简。只传递节点真正需要的数据。过大的状态会影响序列化/反序列化的性能。并行执行如果图中有多个可以独立执行的节点LangGraph 支持通过addNode和addEdge定义并行分支。但需要注意并行节点不能修改状态的同一部分否则会产生竞争条件。缓存对于耗时的、结果确定的计算如某些复杂的提示词渲染可以考虑使用缓存。LangChain 的RunnableLambda或RunnableWithFallbacks可以与缓存中间件结合。流式输出对于用户体验至关重要的场景使用app.stream()并逐步输出模型生成的内容而不是等待整个流程结束。这需要模型和你的前端/客户端支持流式响应。构建复杂的 LangGraph 应用是一个迭代过程。从一个小而简单的图开始逐步添加功能和复杂性并始终利用 LangSmith 作为你的指南针和调试器。随着你对状态管理、控制流和工具集成的理解加深你将能够设计出强大而优雅的 AI 应用工作流。

相关新闻

CUDA加速HOG与LBP特征提取实战优化

CUDA加速HOG与LBP特征提取实战优化

2026/8/12 12:09:49

1. 项目概述在计算机视觉领域,图像特征提取是许多高级任务的基础环节。HOG(方向梯度直方图)和LBP(局部二值模式)作为两种经典的特征描述子,在目标检测、人脸识别等应用中表现出色。然而,随着图像…

终极Steam游戏独立运行指南:如何3分钟实现免Steam启动

终极Steam游戏独立运行指南:如何3分钟实现免Steam启动

2026/8/12 12:09:49

终极Steam游戏独立运行指南:如何3分钟实现免Steam启动 【免费下载链接】Steam-auto-crack Steam Game Automatic Cracker 项目地址: https://gitcode.com/gh_mirrors/st/Steam-auto-crack 你是否厌倦了每次玩游戏都要启动Steam客户端?Steam-auto-…

企业AI Agent技能开发与实战应用指南

企业AI Agent技能开发与实战应用指南

2026/8/12 12:09:49

1. 企业AI Agent的核心价值与挑战在数字化转型浪潮中,企业运营效率的提升往往受制于重复性人工操作的瓶颈。以某电商企业为例,其客服部门每天需要处理3000订单状态查询请求,而财务团队每周要手动发送500余封报表邮件。这类操作虽然规则明确&a…

【2026年】女性读MBA,择校有什么特别建议?

【2026年】女性读MBA,择校有什么特别建议?

2026/8/12 18:00:05

越来越多的女性职场人把MBA列入成长规划,但真正择校时,很多人发现网上针对"女性MBA"的讨论要么泛泛而谈,要么充斥刻板印象。这篇文章从女性在职读MBA的真实痛点出发,给出可落地的择校参考:选什么样的学校&am…

程序流程图实战指南:从核心符号到工具选型与避坑技巧

程序流程图实战指南:从核心符号到工具选型与避坑技巧

2026/8/12 18:00:05

1. 程序流程图:从“是什么”到“怎么用”的实战拆解 如果你刚入行,或者需要向非技术背景的同事解释一个复杂流程,大概率会听到“画个流程图看看”这句话。程序流程图,这个听起来有点“古早”的工具,至今仍然是软件开发…

LangGraph实战:构建具备条件路由与循环执行能力的智能Agent

LangGraph实战:构建具备条件路由与循环执行能力的智能Agent

2026/8/12 18:00:05

1. 项目概述:从单步执行到循环思考的跃迁 如果你已经跟着上一篇内容,用 LangGraph 搭出了一个能自动生成图文内容的 Agent 雏形,那么恭喜你,已经迈出了从零到一的关键一步。但那个 Agent 更像一个听话的“流水线工人”&#xff0c…

Qt Creator编码设置全解析:告别乱码,实现跨平台开发一致性

Qt Creator编码设置全解析:告别乱码,实现跨平台开发一致性

2026/8/12 18:00:05

1. 项目概述:为什么Qt Creator的编码设置如此关键? 如果你在Qt Creator里写过带中文的代码,大概率遇到过那个经典的“烫烫烫”乱码,或者编译时蹦出一堆你看不懂的编码错误。这问题看似简单,背后却牵扯到编辑器、编译器…

Go 底层心智模型:并发、内存与闭包

Go 底层心智模型:并发、内存与闭包

2026/8/12 18:00:05

写给已经能写 Go 但想知道"为什么"的人。这篇文章不讲语法,只聊机制。一、并发不是并行 很多人把这两个词当同义词用。Rob Pike 那句 “Concurrency is not parallelism” 被引用了无数遍,但真正能在白板上画清楚的人不多。 并发是程序的结构—…

G7手套抗磁干扰测试方案:从原理到实践的EMC工程指南

G7手套抗磁干扰测试方案:从原理到实践的EMC工程指南

2026/8/12 17:50:04

在实际工业自动化、医疗设备、精密仪器和穿戴式传感器项目中,传感器数据的准确性是系统可靠性的基石。其中,电磁干扰(EMI)是导致传感器读数漂移、信号失真甚至功能失效的常见环境因素。对于集成在手套这类柔性、可穿戴设备中的传感…

比较好的亚太EMBA,问了6位校友师资差别真的挺大

比较好的亚太EMBA,问了6位校友师资差别真的挺大

2026/8/12 7:11:29

比较好的亚太EMBA核心差异先看什么?对于希望兼顾工作与系统管理能力提升的亚太区高管而言,筛选匹配度高的EMBA项目时,师资配置是决定学习体验与实际收获的核心要素之一。我们结合3-4个公开信息透明、办学历史较长的亚太区主流EMBA项目特点&am…

备考3个月对比6份资料 海外游学的亚洲EMBA面试注意点

备考3个月对比6份资料 海外游学的亚洲EMBA面试注意点

2026/8/11 8:44:43

备考海外游学的亚洲EMBA面试,核心要围绕项目国际化设计逻辑、个人跨文化管理经验匹配度两个维度准备,避免把游学模块等同于普通旅游参访的认知偏差。不少备考者花3个月对比6份资料,却容易忽略面试官对“国际视野落地能力”的考察——比如香港…

比较好的国内EMBA,问了二十位校友聊透人脉价值

比较好的国内EMBA,问了二十位校友聊透人脉价值

2026/8/11 15:57:54

比较好的国内EMBA核心差异体现在哪些方面?比较好的国内EMBA的核心长期价值,很大程度上依托于校友网络的连接质量与资源生态的活跃度,这也是不少高管在择校时优先考量的因素。我们结合3-4个市场关注度较高的项目公开信息,从课程、师…

告别模组冲突!5步掌握《神界:原罪2》模组管理的终极秘诀

告别模组冲突!5步掌握《神界:原罪2》模组管理的终极秘诀

2026/8/12 9:39:37

告别模组冲突!5步掌握《神界:原罪2》模组管理的终极秘诀 【免费下载链接】DivinityModManager A mod manager for Divinity: Original Sin - Definitive Edition. 项目地址: https://gitcode.com/gh_mirrors/di/DivinityModManager 你是否曾经为《…

如何用Charge Limiter延长MacBook电池寿命:终极保护指南

如何用Charge Limiter延长MacBook电池寿命:终极保护指南

2026/8/12 9:39:37

如何用Charge Limiter延长MacBook电池寿命:终极保护指南 【免费下载链接】charge-limiter macOS app to set battery charge limit for Intel MacBooks 项目地址: https://gitcode.com/gh_mirrors/ch/charge-limiter 还在为MacBook电池健康度下降而烦恼吗&am…

推三返一模式5.0版本系统开发

推三返一模式5.0版本系统开发

2026/8/12 9:39:37

推三返一模式5.0版本系统开发要点编辑:araolin(私域邦网络土土哥)模式核心逻辑 推三返一是一种促销或分销机制,用户推荐三人完成特定行为(如购买、注册),推荐人可获得返利或奖励。5.0版本通常在…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/8 5:07:31

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/9 13:42:46

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/8 2:30:15

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…