在多模态 AI 应用开发中,开发者常面临三大核心痛点:工具调用顺序混乱 、步骤结果无法传递 、架构集成复杂 。传统的基于大语言模型的 BaseAgent 因其随机决策的特性,难以保障复杂任务的可靠执行。本文将深入探讨如何结合 LangChain(能力组件库)与 LangGraph(流程操作系统),设计一个可闭环集成到现有架构的 WorkflowAgent,以解决上述痛点,并通过实战案例展示其从设计到落地的全过程。
一、框架核心定位:从"能力组件"到"流程操作系统"
LangChain 与 LangGraph 形成了互补的解决方案:
LangChain:提供工具封装(文本/图像/文档/语音)、LLM适配、记忆组件,是**"能力组件库"**。LangGraph:通过状态图(StateGraph)实现多步骤流程控制、状态持久化、分支与循环,是**"流程操作系统"**。
二者协同,从根本上解决了 BaseAgent 的天然缺陷:
| 对比维度 | BaseAgent (纯LLM驱动) | WorkflowAgent (LangChain+LangGraph) |
|---|---|---|
| 工具调用顺序 | LLM随机决策,无法保证依赖(如"写诗配图"可能先调图) | 严格按"感知→规划→执行"定义顺序,支持步骤依赖 |
| 状态管理 | 无共享状态,步骤结果无法传递(如诗无法给配图工具用) | 全局状态通道存储结果,节点间可直接读取 |
| 架构集成 | 需手动对接工具与存储,无统一规范 | 复用现有工具封装与数据库逻辑,形成完整闭环 |
二、多模态 WorkflowAgent 实战案例
案例1:文本生成 + 图像生成(写诗配图) 需求 :用户输入"写一首关于春天的诗,然后为这首诗配一张图",需确保顺序(先文后图)与结果传递。 架构范式:Perceive(意图识别)→ Plan(步骤拆分)→ Execute(工具调用)。
1. 核心代码实现
(1) 类定义与状态初始化
javascript
class WorkflowAgent {
constructor(options = {}) {
this.userId = options.userId; // 对接现有用户体系
this.sessionId = options.sessionId;
this.llm = new DoubaoLLM({ streaming: true }); // 复用LLM适配器
// 全局状态通道:节点间共享数据
this.state = {
input: null, // 用户原始输入
perceived: null, // 感知后的意图
plan: null, // 执行计划
result: null, // 最终结果
tools: [], // 已调用工具列表(日志)
stepResults: {} // 步骤结果(依赖传递)
};
this.graph = this.createGraph(); // 初始化状态图
}
}
(2) LangGraph 状态图创建(核心流程控制)
javascript
createGraph() {
const workflow = new StateGraph({
channels: {
input: { reducer: (x, y) => y || x },
perceived: { reducer: (x, y) => y || x },
plan: { reducer: (x, y) => y || x },
result: { reducer: (x, y) => y || x },
tools: { reducer: (x, y) => [...(x || []), ...(y || [])] },
stepResults: { reducer: (x, y) => ({ ...x, ...y }) }
}
});
// 添加核心节点
workflow.addNode('perceive', this.perceiveNode.bind(this));
workflow.addNode('plan', this.planNode.bind(this));
workflow.addNode('execute', this.executeNode.bind(this));
// 定义线性流转路径
workflow.setEntryPoint('perceive');
workflow.addEdge('perceive', 'plan');
workflow.addEdge('plan', 'execute');
workflow.addEdge('execute', END);
return workflow.compile();
}
(3) 三大核心节点实现
-
感知节点 (Perceive) :解析意图与所需工具。
javascriptasync perceiveNode(state) { const prompt = `分析用户输入,返回JSON格式意图: 用户输入:${state.input} 输出字段:intent、task_type、required_tools`; const response = await this.llm.invoke(prompt); const perceived = JSON.parse(response.content.match(/\{[\s\S]*\}/)[0]); return { perceived }; // 更新状态 } // 结果示例: { intent: "创作诗歌并配图", task_type: "multi", required_tools: ["text_generator", "image_generator"] } -
规划节点 (Plan) :拆分步骤与依赖。
javascriptasync planNode(state) { const { perceived } = state; const prompt = `基于意图制定步骤计划(JSON数组): 意图:${perceived.intent},工具:${perceived.required_tools.join(',')} 要求:标记depends_on,参数含use_previous_result`; const response = await this.llm.invoke(prompt); let plan = JSON.parse(response.content.match(/\[([\s\S]*)\]/)[0]); return { plan }; } // 结果示例: [ // { step: 1, action: "生成春天主题诗歌", tool: "text_generator", params: { topic: "春天" }, depends_on: null }, // { step: 2, action: "基于诗歌生成配图", tool: "image_generator", params: { use_previous_result: true }, depends_on: 1 } // ] -
执行节点 (Execute) :调用工具与传递结果。
javascriptasync executeNode(state) { const { plan } = state; const results = []; const toolsUsed = []; const stepResults = {}; for (const step of plan) { try { // 处理步骤依赖 if (step.depends_on) step.params.previous_result = state.stepResults[step.depends_on]; // 调用工具(复用LangChain封装) switch (step.tool) { case "text_generator": const textTool = new TextGeneratorTool(); stepResult = await textTool._call({ prompt: step.params.topic, userId: this.userId, sessionId: this.sessionId }); break; case "image_generator": const imageTool = new ImageGeneratorTool(); stepResult = await imageTool._call({ prompt: `基于文本生成图像:${step.params.previous_result}`, size: "1024x1024", userId: this.userId, sessionId: this.sessionId }); break; } stepResults[step.step] = stepResult; results.push(`步骤${step.step}:${stepResult}`); toolsUsed.push(step.tool); } catch (error) { results.push(`步骤${step.step}失败:${error.message}`); } } return { result: results.join('\n'), tools: toolsUsed, stepResults }; }
2. 执行流程与结果
javascript
await agent.run("写一首关于春天的诗,然后为这首诗配一张图")
状态流转 :初始状态 → 感知节点 → 规划节点 → 执行节点 → 结束 最终结果:
json
{
"input": "写一首关于春天的诗...",
"perceived": { "intent": "创作诗歌并配图", ... },
"plan": [步骤1, 步骤2],
"result": "步骤1:春风吹绿了大地...\n步骤2:图像URL: /uploads/xxx.jpg",
"tools": ["text_generator", "image_generator"],
"stepResults": { "1": "春风吹绿了大地...", "2": "/uploads/xxx.jpg" }
}
案例2:文档解析 + 语音合成(文档转语音) 需求 :用户上传PDF,先解析文本,再合成语音,需支持流式反馈。 架构适配 :在原有框架上扩展工具 (document_parser, audio_synthesizer) 并实现流式接口。
1. 核心代码适配
-
感知节点扩展:识别文档与语音工具。
-
执行节点扩展 :
javascriptswitch (step.tool) { case "document_parser": const docTool = new DocumentParserTool(); stepResult = await docTool._call({ fileUrl: step.params.fileUrl, userId: this.userId, sessionId: this.sessionId }); break; case "audio_synthesizer": const audioTool = new AudioSynthesizerTool(); stepResult = await audioTool._call({ text: step.params.previous_result, voiceType: "female", userId: this.userId, sessionId: this.sessionId }); break; }
2. 流式执行与前端反馈
javascript
// 后端流式接口
async *stream(input) {
const stream = await this.graph.stream({ input });
for await (const chunk of stream) {
yield chunk; // 逐节点返回状态
}
}
// 前端调用 (SSE)
async function callDocToAudioAgent(message, fileUrl) {
const response = await fetch('/api/agent/workflow', { method: 'POST', body: JSON.stringify({ message, fileUrl }) });
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = JSON.parse(new TextDecoder().decode(value));
// 实时更新UI: if (chunk.perceive) showProgress("已识别需求...");
}
}
三、与现有架构的完整闭环体系
1. 工具封装闭环:复用 LangChain 工具层 所有工具基于现有Tool基类封装,调用规范统一。
javascript
class ImageGeneratorTool extends Tool {
async _call(input) {
// 对接现有图像服务
return await generateSeedreamImages({
userId: input.userId,
sessionId: input.sessionId,
prompt: input.prompt
});
}
}
// WorkflowAgent中无感知调用
const tool = new ImageGeneratorTool();
const result = await tool._call({...});
闭环链路 :WorkflowAgent → LangChain 工具 → 现有服务层 → 数据库
2. 控制器与路由集成(对接现有接口) 创建控制器,复用用户认证与日志存储。
javascript
// workflowAgentController.js
module.exports = {
chat: async (req, res) => {
const { message, session_id } = req.body;
const userId = req.user.user_id; // 现有认证体系
res.writeHead(200, { 'Content-Type': 'text/event-stream' });
const agent = new WorkflowAgent({ userId, sessionId: session_id });
for await (const chunk of agent.stream({ message })) {
res.write(`data: ${JSON.stringify(chunk)}\n\n`); // 流式推送
}
// 复用现有日志存储
await saveChatRecord({ user_id: userId, session_id, request_text: message, model: 'workflow-agent' });
res.end();
}
};
// 集成到现有路由
router.post('/workflow', workflowAgentController.chat);
3. 与 BaseAgent 的场景分工
| 任务类型 | 推荐 Agent | 工具调用数 | 流程控制需求 |
|---|---|---|---|
| 单模态简单任务(如"生成日落图") | BaseAgent | 1 | 无需顺序控制 |
| 多模态多步骤任务(如写诗配图) | WorkflowAgent | ≥2 | 需步骤依赖与顺序控制 |
| 企业级合规任务(如带审核的内容生成) | WorkflowAgent(加审核节点) | ≥2 | 需分支与人工干预 |
四、生产级扩展与最佳实践
4.1 错误处理增强:生产级可靠性保障 通过 重试策略、错误降级、日志上报 三重机制,将任务成功率提升至95%以上。
javascript
async executeNode(state) {
for (const step of plan) {
let retryCount = 0, maxRetry = 3, stepSuccess = false;
while (retryCount < maxRetry && !stepSuccess) {
try {
// 1. 依赖校验
if (step.depends_on && !stepResults[step.depends_on]) throw new Error(`依赖步骤未成功`);
// 2. 参数校验
if (step.tool === "image_generator" && !step.params.previous_result) throw new Error("参数不完整");
// 3. 工具调用(带超时)
stepResult = await tool._call({ ..., timeout: 10000 });
stepSuccess = true; // 成功
} catch (error) {
retryCount++;
// 4. 指数退避重试
if (retryCount < maxRetry) await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
}
}
// 5. 多次重试失败:执行降级
if (!stepSuccess) {
if (step.tool === "text_generator") stepResult = "默认春天主题诗歌...";
else if (step.tool === "image_generator") stepResult = "/static/fallback/spring.jpg";
}
// 6. 错误日志上报(对接ELK等监控)
if (errorLogs.length > 0) await reportErrorLogs({ userId: this.userId, logs: errorLogs });
}
}
4.2 支持条件分支与循环(企业级合规需求) 应对"人工审核"、"多轮优化"等动态流程,以"写诗配图+人工审核"为例。
javascript
createGraph() {
// ... 原有节点 ...
workflow.addNode('review', this.reviewNode.bind(this)); // 新增审核节点
workflow.addEdge('execute', 'review');
// 条件边:根据审核结果决定后续流程
workflow.addConditionalEdges(
'review',
(state) => {
const status = state.reviewStatus;
if (status === "approved") return "end";
else if (status === "rejected") return "replan"; // 驳回则重新规划
else return "review"; // 待审核则循环等待
},
{ 'end': END, 'replan': 'plan', 'review': 'review' }
);
}
// 审核驳回后,规划节点融入审核意见重新生成计划
async planNode(state) {
const { perceived, reviewStatus, reviewComment } = state;
const reviewInfo = reviewStatus === "rejected" ? `(审核驳回原因:${reviewComment})` : "";
const prompt = `基于意图制定步骤计划...意图:${perceived.intent} ${reviewInfo}`;
// ... 生成新计划
}
4.3 性能优化:高并发场景下的效率提升 从 并行执行、状态缓存、资源复用 三方面优化。
-
无依赖步骤并行 :
javascriptasync executeNode(state) { const independentSteps = plan.filter(step => step.depends_on === null); const dependentSteps = plan.filter(step => step.depends_on !== null); // 并行执行无依赖步骤 if (independentSteps.length > 0) { const parallelPromises = independentSteps.map(step => this.executeSingleStep(step, ...)); const parallelResults = await Promise.all(parallelPromises); // ...处理结果 } // 串行执行依赖步骤 for (const step of dependentSteps.sort((a, b) => a.step - b.step)) { await this.executeSingleStep(step, ...); } } -
重复任务状态缓存 :
javascriptasync executeNode(state) { const cacheKey = `user:${this.userId}:task:${md5(input)}`; const cachedResult = await this.cache.get(cacheKey); // Redis缓存 if (cachedResult) return { ...parsedCache, fromCache: true }; // 命中缓存 // ... 执行任务 await this.cache.set(cacheKey, JSON.stringify(result), "EX", 3600); // 写入缓存 return { ...result, fromCache: false }; }
五、学习路径:从入门到实战
- 入门阶段 :掌握核心组件。
- 环境搭建 :安装
langchain,langgraph,redis,配置LLM。 - 基础练习:用LangChain封装一个自定义工具;用LangGraph实现"单步骤文本生成"流程。
- 环境搭建 :安装
- 进阶阶段 :攻克复杂流程。
- 核心能力:实现"错误重试+降级";开发"条件分支+循环"(以文档转语音为案例)。
- 流式处理 :基于
agent.stream()实现前端实时进度展示,调试SSE连接。
- 实战阶段 :构建完整系统。
- 综合项目:开发"智能内容生产系统":文档→解析→摘要→配图→合成语音。
- 架构闭环:复用现有用户、日志、工具服务;进行压测优化,通过缓存和并行降低响应时间。
六、总结:核心价值与落地建议
- 框架协同价值 :
LangChain解决 "工具复用" 和 "能力扩展"。LangGraph解决 "流程控制" 和 "状态管理"。- 二者结合实现从"工具调用"到"智能系统"的跨越。
- 架构闭环关键 :
- 组件复用:所有工具基于现有基类封装。
- 状态透明:全局状态通道便于排查与数据分析。
- 场景适配 :明确
BaseAgent与WorkflowAgent分工,避免过度设计。
- 落地注意事项 :
- 错误处理分级:关键步骤中断,非关键步骤降级。
- 性能优先:大文件任务拆分并行,高频任务加缓存。
- 可观测性:记录工具耗时与错误率,利用LangSmith等工具优化流程。
通过本文的案例与设计,开发者可将多模态AI应用从"Demo级"可靠地升级为"生产级",有效解决流程失控的痛点。