LangChain + LangGraph 实战:构建生产级多模态 WorkflowAgent 的完整指南

在多模态 AI 应用开发中,开发者常面临三大核心痛点:工具调用顺序混乱步骤结果无法传递架构集成复杂 。传统的基于大语言模型的 BaseAgent 因其随机决策的特性,难以保障复杂任务的可靠执行。本文将深入探讨如何结合 LangChain(能力组件库)与 LangGraph(流程操作系统),设计一个可闭环集成到现有架构的 WorkflowAgent,以解决上述痛点,并通过实战案例展示其从设计到落地的全过程。

一、框架核心定位:从"能力组件"到"流程操作系统"

LangChainLangGraph 形成了互补的解决方案:

  • 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) :解析意图与所需工具。

    javascript 复制代码
    async 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) :拆分步骤与依赖。

    javascript 复制代码
    async 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) :调用工具与传递结果。

    javascript 复制代码
    async 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. 核心代码适配

  • 感知节点扩展:识别文档与语音工具。

  • 执行节点扩展

    javascript 复制代码
    switch (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 性能优化:高并发场景下的效率提升并行执行、状态缓存、资源复用 三方面优化。

  • 无依赖步骤并行

    javascript 复制代码
    async 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, ...);
      }
    }
  • 重复任务状态缓存

    javascript 复制代码
    async 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 };
    }

五、学习路径:从入门到实战

  1. 入门阶段 :掌握核心组件。
    • 环境搭建 :安装 langchain, langgraph, redis,配置LLM。
    • 基础练习:用LangChain封装一个自定义工具;用LangGraph实现"单步骤文本生成"流程。
  2. 进阶阶段 :攻克复杂流程。
    • 核心能力:实现"错误重试+降级";开发"条件分支+循环"(以文档转语音为案例)。
    • 流式处理 :基于 agent.stream() 实现前端实时进度展示,调试SSE连接。
  3. 实战阶段 :构建完整系统。
    • 综合项目:开发"智能内容生产系统":文档→解析→摘要→配图→合成语音。
    • 架构闭环:复用现有用户、日志、工具服务;进行压测优化,通过缓存和并行降低响应时间。

六、总结:核心价值与落地建议

  • 框架协同价值
    • LangChain 解决 "工具复用""能力扩展"
    • LangGraph 解决 "流程控制""状态管理"
    • 二者结合实现从"工具调用"到"智能系统"的跨越。
  • 架构闭环关键
    • 组件复用:所有工具基于现有基类封装。
    • 状态透明:全局状态通道便于排查与数据分析。
    • 场景适配 :明确 BaseAgentWorkflowAgent 分工,避免过度设计。
  • 落地注意事项
    • 错误处理分级:关键步骤中断,非关键步骤降级。
    • 性能优先:大文件任务拆分并行,高频任务加缓存。
    • 可观测性:记录工具耗时与错误率,利用LangSmith等工具优化流程。

通过本文的案例与设计,开发者可将多模态AI应用从"Demo级"可靠地升级为"生产级",有效解决流程失控的痛点。

相关推荐
Java中文社群1 小时前
重磅!N8N新版2.0发布!不再支持MySQL?
人工智能
梯度下降不了班1 小时前
【mmodel/xDit】Cross-Attention 深度解析:文生图/文生视频的核心桥梁
人工智能·深度学习·ai作画·stable diffusion·音视频·transformer
大模型服务器厂商1 小时前
人形机器人的技术概况与算力支撑背景
大数据·人工智能
老蒋新思维1 小时前
创客匠人洞察:AI 时代 IP 变现的认知重构,从流量焦虑到价值深耕的破局之道
网络·人工智能·tcp/ip·重构·知识付费·创始人ip·创客匠人
商汤万象开发者2 小时前
UniParse:让多模态模型真正“读懂”文档的解析引擎
人工智能·多模态模型·ai应用·文档解析·版面分析·内容提取
第二只羽毛2 小时前
主题爬虫采集主题新闻信息
大数据·爬虫·python·网络爬虫
plmm烟酒僧2 小时前
TensorRT 推理 YOLO Demo 分享 (Python)
开发语言·python·yolo·tensorrt·runtime·推理
rit84324992 小时前
压缩感知信号恢复算法:OMP与CoSaMP对比分析
数据库·人工智能·算法
天才测试猿2 小时前
Postman中变量的使用详解
自动化测试·软件测试·python·测试工具·职场和发展·接口测试·postman