LangGraph 工作流模式

📑目录


  在构建基于大语言模型(LLM)的智能体应用时,工作流模式是预先定义好的执行路径。就像工厂的流水线一样,每个步骤都有明确的输入输出和顺序。根据不同的需求场景,LangGraph 提供了多种工作流构建范式。

一、提示链模式(Prompt Chaining)

1.1 概念

  提示链就像流水线一样,前一个步骤的输出作为下一个步骤的输入 。这就跟进行内容创作时一样,需要 "大纲" →"初稿"→"润色"→"最终稿",且每个步骤的输出需要传输给下一个步骤,才能确保内容质量逐步提升。

为什么需要提示链?

  在复杂的 LLM 应用中,如果将庞大且复杂的任务一次性交给大模型,往往会导致上下文超载、遗漏关键细节或产生严重的"幻觉"。提示链的核心思想是"分而治之",它具有以下优势:

  • 降低单次 Prompt 复杂度:让模型每次只专注单一的子任务(例如:本节点只负责生成大纲)。

  • 提升结果准确性与稳定性:通过多个步骤的渐进式打磨,显著提升最终输出的质量。

  • 便于中间干预与校验:在步骤之间,可以方便地插入代码校验逻辑(如检查大纲格式是否正确),或接入人工审核(Human-in-the-loop),确保整体方向不偏离。

1.2 模式实践

  创建一个模拟上述内容创作的工作流。

第一步:定义输入输出的方式

python 复制代码
class InputState(TypedDict):
	topic: str  # 用户输入的主题

class OutputState(TypedDict):
	final_content: str  # 最终的内容

class OverallState(InputState,OutputState):
	outline: str 	# 第一步:生成的大纲
	draft: str   	# 第二步:生成的初稿
	polished_draft: str  # 第三步:润色后的稿件

第二步:生成大纲

python 复制代码
PROMPT_1 = (
	"根据主题生成文章大纲。\n"
	"主题:{topic}\n"
	"要求:"
	"1.只需两个最核心标题"
	"2.不用进行说明,只返回最终大纲"
)
def generate_outline(state:OverallState):
	"""根据主题生成内容大纲"""
	print("*"*50)
	print("大纲生成中...\n")
	prompt=PROMPT_1.format(topic=state['topic'])
	outline=model.invoke([HumanMessage(content=prompt)]).content
	print(f"大纲已生成:\n{outline}\n")
	return {
		"outline":outline, 
	}

第三步:基于大纲生成初稿

python 复制代码
PROMPT_2 = (
	"根据以下内容生成文章完整初稿。\n"
	"主题:{topic}\n"
	"大纲:"
	"{outline}\n"
	"要求:"
	"1.每个标题下,最多使用三句话的内容即可"
	"2.不用进行说明,只返回最终结果"
)
def generate_draft(state:OverallState):
	"""根据大纲生成完整初稿"""
	print("*"*50)
	print(f"生成初稿中...\n")
	prompt=PROMPT_2.format(topic=state['topic'],outline=state['outline'])
	draft=model.invoke([HumanMessage(content=prompt)]).content
	print(f"初稿已生成:\n{draft}\n")
	return {"draft":draft}

第四步:润色稿件

python 复制代码
PROMPT_3 = (
	"根据文章初稿进行润色。\n"
	"主题:{topic}\n"
	"初稿:"
	"{draft}\n"
	"要求:"
	"1.润色后,文章不能太长"
)
def polish_content(state:OverallState):
	"""对初稿进行润色优化"""
	print("*"*50)
	print(f"文章润色中...\n")
	prompt=PROMPT_3.format(topic=state['topic'],draft=state['draft'])
	polished=model.invoke([HumanMessage(content=prompt)]).content
	print(f"润色完成,内容如下:\n{polished}\n")
	return {"polished_draft":polished}

第五步:生成最终稿

python 复制代码
PROMPT_4 = (
	"根据润色版文章,生成文章终稿。\n"
	"主题:{topic}\n"
	"大纲:"
	"{outline}\n"
	"润色版文章:"
	"{polished_draft}\n"
)
def finalize_content(state:OverallState):
	"""生成最终版本的内容"""
	prompt=PROMPT_4.format(topic=state['topic'],outline=state['outline'],polished_draft=state['polished_draft'])
	final_content=model.invoke([HumanMessage(content=prompt)]).content
	return {"final_content":final_content}

第六步:构建图

python 复制代码
#构建工作流
builder = StateGraph(
	OverallState,
	input_schema=InputState,
	output_schema=OutputState
)
#添加节点
builder.add_node(generate_outline)		# 节点1:生成大纲
builder.add_node(generate_draft)			# 节点2:生成初稿
builder.add_node (polish_content)		# 节点3:润色稿件
builder.add_node(finalize_content)		# 节点4:生成最终稿

#连接节点(直线流程)
builder.add_edge(START, "generate_outline")
builder.add_edge("generate_outline","generate_draft")
builder.add_edge("generate_draft","polish_content")
builder.add_edge("polish_content","finalize_content")
builder.add_edge("finalize_content", END)

#编译工作流
chain = builder.compile()

result = chain.invoke({"topic":"人工智能的未来发展"})
print("=" * 50)
print("最终创作结果:")
print("=" * 50)
print(result["final_content"])
print("=" * 50)

运行输出

text 复制代码
**************
内容大纲生成中...

大纲已生成:
#人工智能的未来发展

##1.技术趋势与创新方向
###1.1自然语言处理的进步
###1.2机器学习与深度学习的演变
###1.3强人工智能的可能性

##2.应用领域与社会影响
###2.1医疗行业的变革
###2.2自动化与就业市场的关系
###2.3伦理与监管的挑战

**************
生成初稿中...

初稿已生成:
(内容较多,已省略)

**************
文章润色中...

润色完成,内容如下:
(内容较多,已省略)

=================
最终创作结果:
(内容较多,已省略)
=================

二、并行化模式 (Parallelization)

2.1 概念

  并行化是指将一个大任务拆解为多个互不依赖的子任务,让它们同时进行,最后再统一汇总结果。在多角度、多维度的复杂问题分析中,这种模式非常高效。

业务场景举例:

  假设要研发一款主打城市通勤的"智能电动自行车",在立项前需要进行多维度调研:

  • 市场分析:用户关注续航里程、车身重量、防盗能力,并对"骑行社交"(组队、分享路线)有浓厚兴趣。

  • 竞品分析:传统品牌车型智能化不足;互联网品牌车型续航和线下售后服务是其短板。

  • 技术分析:评估更轻量化的电池材料与车身设计以提升续航和便携性,并开发基于GPS和移动网络的智能防盗系统与社交功能App集成。

这三个分析任务彼此独立、没有先后依赖关系。如果串行处理,总耗时是三者之和;而采用并行处理,总耗时取决于最慢的那个任务,不仅省时,汇总后的综合报告也更能提升决策质量。

2.2 模式实践

  以下代码实现了一个工作流,并行执行三个维度的分析,最后整合成全面的产品研发建议。

第一步:定义状态 (State)

python 复制代码
from typing import TypedDict
from langgraph.constants import START, END
from langgraph.graph import StateGraph

#1. 定义输入模式(仅外部输入)
class InputState(TypedDict):
    concept: str  # 概念(如:城市通勤智能电动自行车)

# 2. 定义输出模式(仅最终输出)
class OutputState(TypedDict):
    report: str   # 汇总报告

# 3. 定义内部完整状态(包含输入、输出和中间并行结果)
class AnalysisState(InputState, OutputState):
    market: str      # 市场分析结果
    competitor: str  # 竞品分析结果
    tech: str        # 技术分析结果

第二步:定义并行执行的节点

  三个节点接收完整的 AnalysisState,各自处理后,返回自己负责的那个字段。由于这三个字段互不相同,LangGraph 底层的状态更新机制(Reducer)会自动将它们合并,不会产生覆盖冲突。

python 复制代码
# 三个并行分析任务(实际场景中这里可以调用大模型或外部API)
def market_task(state: AnalysisState):
    """市场分析"""
    return {"market": "用户关注续航、重量、防盗,对骑行社交有兴趣..."}

def competitor_task(state: AnalysisState):
    """竞品分析"""
    return {"competitor": "传统品牌智能化不足,互联网品牌续航和售后差..."}

def tech_task(state: AnalysisState):
    """技术分析"""
    return {"tech": "轻量化电池车身、GPS防盗、社交App集成..."}

第三步:定义汇总节点

  汇总节点也必须声明为接收完整的 AnalysisState,这样它才能读取到并行任务返回的三个中间状态。

python 复制代码
# 汇总结果
def combine_results(state: AnalysisState):
    """生成最终报告"""
    report = "产品分析报告\n\n"
    report += f"市场分析:\n{state['market']}\n\n"
    report += f"竞品分析:\n{state['competitor']}\n\n"
    report += f"技术分析:\n{state['tech']}\n\n"
    report += "建议:聚焦续航、防盗、社交功能的平衡发展"
    return {"report": report}

第四步:构建工作流图

  这是并行化的核心:从 START 节点分出三条边指向三个子任务;等这三个子任务结束后,再统一指向 combine 节点。

python 复制代码
# 构建工作流
builder = StateGraph(
    AnalysisState,
    input_schema=InputState,   # 限制输入
    output_schema=OutputState  # 过滤输出
)

# 添加节点
builder.add_node("market", market_task)
builder.add_node("competitor", competitor_task)
builder.add_node("tech", tech_task)
builder.add_node("combine", combine_results)

# 1. 并行分发:START 同时指向三个分析节点
builder.add_edge(START, "market")
builder.add_edge(START, "competitor")
builder.add_edge(START, "tech")

# 2. 结果汇聚:三个分析节点完成后,都指向汇总节点
builder.add_edge("market", "combine")
builder.add_edge("competitor", "combine")
builder.add_edge("tech", "combine")

# 3. 结束
builder.add_edge("combine", END)

workflow = builder.compile()

# 使用
result = workflow.invoke({"concept": "城市通勤智能电动自行车"})
print(result["report"])

三、路由模式 (Routing)

3.1 概念

  路由模式(Routing)在系统架构中也称为"智能分流"。其核心逻辑是:根据输入内容的特征,动态决定执行哪一个下游分支。在构建 LLM 应用时,面对用户的多样化输入,系统通常需要先进行意图识别,再将请求分发给专用的处理模块。以智能客服系统为例:

路由模式不仅避免了单一 Prompt 处理所有任务导致的上下文混乱,还能让不同的分支按需调用对应的工具或知识库,提升响应质量。

3.2 模式实践

  以下代码实现了一个智能客服路由系统。其核心设计在于条件路由机制:

  • 动态路径选择:借助大模型的结构化输出(Structured Output)能力,将用户意图强制分类为特定枚举值,避免解析错误。

  • 分支隔离:不同类型的问题由专用处理器独立处理。

第一步:定义状态模式

python 复制代码
from langchain.chat_models import init_chat_model
from langgraph.constants import START, END
from langgraph.graph import StateGraph
from typing_extensions import Literal, TypedDict
from pydantic import BaseModel, Field

class State(TypedDict):
    input: str      # 用户输入
    decision: str   # 路由决策(中间状态)
    output: str     # 最终输出

第二步:定义路由决策的数据结构(利用 Pydantic 和 Literal 强制约束输出)

python 复制代码
class Route(BaseModel):
    step: Literal["pre_sale", "after_sale", "technical"] = Field(
        description="根据用户问题类型决定路由到售前、售后还是技术处理"
    )

第三步:路由决策节点(调用大模型进行分类)

python 复制代码
def model_call_router(state: State):
    """分析用户输入,决定问题类型"""
    model = init_chat_model("gpt-4o-mini")
    # 使用 with_structured_output 确保模型返回符合 Route 结构的数据
    decision = model.with_structured_output(Route).invoke(state["input"])
    return {"decision": decision.step}

第四步:定义三个独立的处理节点

python 复制代码
def pre_sale_handler(state: State):
    """处理售前咨询"""
    return {"output": "售前咨询已处理,处理内容....."}

def after_sale_handler(state: State):
    """处理售后问题"""
    return {"output": "售后问题已处理,处理内容....."}

def technical_handler(state: State):
    """处理技术问题"""
    return {"output": "技术问题已处理,处理内容....."}

第五步:路由函数:根据决策返回下一个要执行的节点名称

python 复制代码
def route_decision(state: State):
    if state["decision"] == "pre_sale":
        return "pre_sale_handler"
    elif state["decision"] == "after_sale":
        return "after_sale_handler"
    elif state["decision"] == "technical":
        return "technical_handler"

第六步:构建路由工作流

python 复制代码
router_builder = StateGraph(State)

# 添加所有节点
router_builder.add_node(pre_sale_handler)
router_builder.add_node(after_sale_handler)
router_builder.add_node(technical_handler)
router_builder.add_node(model_call_router)

# 先经过路由决策节点
router_builder.add_edge(START, "model_call_router")

# 添加条件边:根据 route_decision 的返回值动态选择分支
router_builder.add_conditional_edges(
    "model_call_router",
    route_decision,
    ["pre_sale_handler", "after_sale_handler", "technical_handler"]
)

# 所有分支最终都指向结束节点
router_builder.add_edge("pre_sale_handler", END)
router_builder.add_edge("after_sale_handler", END)
router_builder.add_edge("technical_handler", END)

router_workflow = router_builder.compile()

第七步:测试运行

python 复制代码
test_cases = [
    "我想了解一下你们产品的价格和功能",               # 售前咨询
    "我购买的产品有质量问题,需要退货",                 # 售后问题
    "这个软件安装后无法正常运行,报错代码0x80070005", # 技术问题
    "请问你们的售后服务政策是什么",                     # 售前咨询
    "我的订单已经发货但还没收到",                       # 售后问题
    "如何配置数据库连接参数"                            # 技术问题
]

for test_case in test_cases:
    print("*" * 50)
    result = router_workflow.invoke({"input": test_case})
    print(f"用户问题: {test_case}\n{result['output']}")

运行结果:

text 复制代码
**************************************************
用户问题: 我想了解一下你们产品的价格和功能
售前咨询已处理,处理内容.....
**************************************************
用户问题: 我购买的产品有质量问题,需要退货
售前咨询已处理,处理内容.....
**************************************************
用户问题: 这个软件安装后无法正常运行,报错代码0x80070005
技术问题已处理,处理内容.....
**************************************************
用户问题: 请问你们的售后服务政策是什么
售后问题已处理,处理内容.....
**************************************************
用户问题: 我的订单已经发货但还没收到
售前咨询已处理,处理内容.....
**************************************************
用户问题: 如何配置数据库连接参数
技术问题已处理,处理内容.....

(注:在运行结果中,部分问题时被模型误判的,例如"产品有质量问题需要退货"被分到了售前。这反映了 LLM 分类的实际不确定性。在实际工程中,若需高准确率,可通过优化 Prompt 中的分类定义,或加入规则引擎兜底。)


四、协调者-工作者模式 (Orchestrator-Workers)

4.1 概念

  协调者-工作者模式(Orchestrator-Workers)是处理复杂、未知结构任务的典型架构。其核心逻辑是:由一个中心节点(协调者)对任务进行动态拆解与规划,再将子任务派发给多个执行节点(工作者)并发处理,最后由汇总节点(合成器)将结果整合输出。

与并行化模式(Parallelization)的核心区别:

  • 并行化:任务的分支数量在设计时(静态)就已确定。例如,固定并发执行"市场、竞品、技术"三项分析。

  • 协调者-工作者:任务的分支数量与具体内容在运行时(动态)由协调者决定。例如,在处理一份长文档时,协调者需要先阅读文档结构,发现需要拆分为 5 个章节,随后动态分配 5 个 worker 并发执行;如果拆分为 10 个章节,则分配 10 个 worker。

4.2 核心机制:动态路由与 Send 对象

  在传统的图结构(如之前的章节)中,我们通过 add_edge 定义静态的连接。但在协调者-工作者模式中,由于 worker 的数量未知,我们需要在运行时动态生成边。LangGraph 通过条件边(Conditional Edges)返回 Send 对象来实现这一机制。

📌 Send 两个参数:

  • 目标节点的名称(例如 "llm_call")。
  • 要传递给该节点的局部状态(Payload)。

当条件边返回一个 Send 对象列表时,LangGraph 会根据列表的长度,动态创建对应数量的并行分支,并将各自的 payload 独立传递给 worker 节点。

4.3 模式实践

  以下代码实现了一个"长报告生成系统"。协调者根据 topic 生成包含 3 个章节的大纲,随后动态分配 3 个 Worker 并发生成各章节内容,最后汇总为完整报告。

python 复制代码
from langchain.chat_models import init_chat_model
from langgraph.constants import START, END
from langgraph.graph import StateGraph
from langgraph.types import Send
from typing import Annotated, TypedDict, List
import operator
from pydantic import BaseModel

# 1. 定义全局状态
class State(TypedDict):
    topic: str
    sections: List[Section]  # 顺便明确为 Pydantic 对象列表
    # 【关键修改 2】将元素类型从 str 改为 SectionResult 字典
    completed_sections: Annotated[List[SectionResult], operator.add]
    final_report: str

class Section(BaseModel):
    name: str
    description: str
    
class Sections(BaseModel):
    sections: List[Section]

class SectionResult(TypedDict):
    index: int  # 用于最后排序的依据
    content: str  # 大模型生成的文本

# 2. 初始化模型
model = init_chat_model("gpt-4o-mini")
planner = model.with_structured_output(Sections)

# 3. 协调者节点:生成计划
def orchestrator(state: State):
    """根据主题生成大纲,并分配给 worker"""
    report_sections = planner.invoke(
        f"为主题 '{state['topic']}' 制定报告大纲,包含3个章节"
    )
    return {"sections": report_sections.sections}

# 4. 任务分配函数:动态创建 Worker
def assign_workers(state: State):
    """任务分配:使用 enumerate 为每个章节分配一个 index"""
    worker_tasks = []
    for index, section in enumerate(state["sections"]):
        # 将 section 和 index 一起发给 Worker,后续按index排序合并,保证合并后不乱序
        worker_tasks.append(
            Send("llm_call", {"section": section, "index": index})
        )
    return worker_tasks

# 5. 工作者节点:执行局部任务
def llm_call(state: dict):
    """Worker:执行局部任务,并在返回结果中带上 index"""
    # 这里的 state 是 Send 传递过来的局部字典
    section = state["section"]
    index = state["index"]

    result = model.invoke(
        f"编写报告章节:{section.name},内容要求:{section.description}"
    )
    return {"completed_sections": [{"index": index, "content": result.content}]}

# 6. 合成器节点:汇总结果
def synthesizer(state: State):
    """合成器:按 index 排序并汇总结果"""
    completed_sections = state["completed_sections"]

    # 按 index 从小到大对并发返回的结果进行排序
    sorted_sections = sorted(completed_sections, key=lambda x: x["index"])

    # 提取排好序的 content
    ordered_contents = [item["content"] for item in sorted_sections]

    # 拼接为最终文本
    final_report = "\n\n---\n\n".join(ordered_contents)
    return {"final_report": final_report}
    
# 7. 构建图
builder = StateGraph(State)

# 添加节点
builder.add_node("orchestrator", orchestrator)
builder.add_node("llm_call", llm_call)
builder.add_node("synthesizer", synthesizer)

# 构建边
builder.add_edge(START, "orchestrator")

# 关键:从协调者到工作者的条件边
builder.add_conditional_edges(
    "orchestrator",
    assign_workers,
    ["llm_call"]  # 动态创建的所有分支都指向 llm_call 节点
)

# 所有 worker 完成后,统一指向合成器
builder.add_edge("llm_call", "synthesizer")
builder.add_edge("synthesizer", END)

worker_workflow = builder.compile()

# 8. 运行测试
response = worker_workflow.invoke({"topic": "中国近代史"})
print(response)

运行结果

text 复制代码
json
{
  "topic": "中国近代史",
  "sections": [
    {
      "name": "第一章:近代史概述",
      "description": "介绍中国近代史的背景与重要性,涵盖社会、经济、文化等多个方面的变化。"
    },
    {
      "name": "第二章:鸦片战争与列强侵华",
      "description": "探讨鸦片战争对中国的影响,以及随后的列强侵华与民族觉醒进程。"
    },
    {
      "name": "第三章:辛亥革命与新文化运动",
      "description": "分析辛亥革命及其后果,以及新文化运动对中国现代化的推动作用。"
    }
  ],
  "completed_sections": [
    "## 第一章:近代史概述\n\n中国近代史...",
    "## 第二章:鸦片战争与列强侵华\n\n鸦片战争是...",
    "## 第三章:辛亥革命与新文化运动\n\n辛亥革命... "
  ],
  "final_report": "## 第一章:近代史概述\n\n中国近代史...\n\n---\n\n## 第二章:鸦片战争与列强侵华\n\n鸦片战争是...\n\n---\n\n## 第三章:辛亥革命与新文化运动\n\n辛亥革命..."
}

五、评估器-优化器模式 (Evaluator-Optimizer)

5.1 概念

  评估器-优化器模式通过引入闭环反馈回路(Feedback Loop)来提升大模型的输出质量。其核心流转逻辑为:

  • 生成(Generate):由生成节点(Generator)根据输入完成任务初稿。

  • 评估(Evaluate):由评估节点(Evaluator)对初稿的质量进行审查或打分。

  • 反馈与迭代(Optimize):若评估未达标,系统将带着评估反馈(Feedback)重新进入生成节点进行优化;若评估通过,则结束流程。

  该模式适用于输出质量难以一次性保证、且存在明确评估标准的场景,例如:高质量文本生成、复杂代码编写与自动审查、多轮数据提取等。相较于单次 Prompt 提示,它能有效降低大模型的幻觉与逻辑漏洞。

5.2 核心机制

  在 LangGraph 中,该模式依赖条件边(Conditional Edges)构建循环结构(Cycle)。

条件路由:评估节点执行后,通过条件边决定下一步走向。通常需要定义一个路由函数,解析评估结果并返回目标节点(如返回 "generate" 或 END)。

终止条件:为防止模型在"生成-评估"之间陷入无限死循环,必须在状态(State)中记录迭代次数,并在路由函数中设定最大重试阈值(Max Iterations)。

5.3 模式实践

  以下代码实现了一个"代码生成与自动审查"的工作流。生成器负责编写 Python 代码,评估器负责检查代码的语法与安全性,若不通过则打回重做。

python 复制代码
from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Literal
from pydantic import BaseModel, Field

# 初始化模型
model = init_chat_model("gpt-4o-mini")

# 1. 定义状态
class State(TypedDict):
    task: str               # 待完成的任务
    draft: str              # 当前的生成内容(草稿)
    feedback: str           # 评估节点的反馈意见
    is_approved: bool       # 评估是否通过
    iterations: int         # 当前迭代次数(用于防死循环)

# 定义评估结果的结构化输出
class Evaluation(BaseModel):
    is_approved: bool = Field(description="代码是否满足需求且无严重错误")
    feedback: str = Field(description="如果不通过,请提供具体的修改建议;如果通过,填入'PASS'")

evaluator = model.with_structured_output(Evaluation)

# 2. 生成器节点
def generate_node(state: State):
    task = state["task"]
    feedback = state.get("feedback", "")
    iterations = state.get("iterations", 0) + 1
    
    prompt = f"请完成以下任务:{task}\n"
    if feedback and feedback != "PASS":
        prompt += f"上一版代码存在以下问题,请修正:\n{feedback}\n"
        
    result = model.invoke(prompt).content
    return {
        "draft": result,
        "iterations": iterations,
        # 每次重新生成后,重置评估状态
        "is_approved": False,
        "feedback": "" 
    }

# 3. 评估器节点
def evaluate_node(state: State):
    draft = state["draft"]
    
    prompt = f"请作为资深Code Reviewer,审查以下代码:\n{draft}\n"
    prompt += "评估标准:1. 语法正确;2. 逻辑严密;3. 无安全漏洞。"
    
    evaluation = evaluator.invoke(prompt)
    return {
        "is_approved": evaluation.is_approved,
        "feedback": evaluation.feedback
    }

# 4. 路由函数(控制循环与退出)
def route_evaluation(state: State):
    # 达到最大迭代次数(如3次),强制退出以防死循环
    if state["iterations"] >= 3:
        print(">>> 达到最大重试次数,强制退出。")
        return END
        
    if state["is_approved"]:
        print(">>> 评估通过,流程结束。")
        return END
    else:
        print(f">>> 评估未通过,打回重写。反馈:{state['feedback']}")
        return "generate"

# 5. 构建图
builder = StateGraph(State)

# 添加节点
builder.add_node("generate", generate_node)
builder.add_node("evaluate", evaluate_node)

# 构建边
builder.add_edge(START, "generate")
builder.add_edge("generate", "evaluate")

# 核心:从评估器到(生成器 / END)的条件边
builder.add_conditional_edges(
    "evaluate",
    route_evaluation,
    ["generate",END]
)

workflow = builder.compile()

# 6. 运行测试
task = "写一个Python函数,使用正则表达式从URL中提取所有的查询参数,并返回一个字典。"
result = workflow.invoke({"task": task, "iterations": 0})

print("\n--- 最终代码 ---\n")
print(result["draft"])

📌 架构扩展:引入人机交互 (Human-in-the-loop)

  评估器并不一定必须由大模型(LLM)充当。在 LangGraph 中,可以非常容易地将评估节点替换为人工审核(例如,在发布重要新闻稿、或执行高危运维脚本前)。实现方式通常是利用 LangGraph 的 interrupt 机制或外部状态检查。


六、工作流模式的选型矩阵与工程考量

  在前面的五个小节中,我们详细拆解了 LangGraph 的五种基础工作流模式。然而,在真实的工程落地中,业务需求往往不是单一的。作为架构师或开发者,我们不仅要会"写"这些模式,更要懂"选"模式。

6.1 核心维度对比

  在 LLM 应用开发中,响应延迟(Latency)、生成质量(Quality) 和 Token 成本(Cost) 往往构成一个不可兼得的"不可能三角"。我们对五种模式的工程表现进行横向对比:

工作流模式 响应延迟 Token 成本 输出质量上限 适用场景特征
提示链 (Prompt Chaining) 中等 (串行叠加) 中等 (按步累加) 较高 任务必须按步骤完成,如:写大纲 -> 填内容 -> 润色。
并行化 (Parallelization) 低 (受限于最慢分支) 高 (同时并发) 中等 多维度数据收集、独立评估。对总时长敏感的业务。
路由模式 (Routing) 低 (仅走一条分支) 低 (按需调用) 中等 意图识别、智能客服分流。避免无关上下文干扰。
协调者-工作者 较高 (动态规划+并发) 极高 (根据任务拆分) 较高 处理复杂长文档、代码库分析等无法预设步骤的任务。
评估器-优化器 极高 (多轮循环重试) 极高 (反复调用) 最高 代码生成、核心文案撰写。对错误零容忍的高精尖场景。

📌 工程建议:如果前端要求接口在 3 秒内返回,请绝对不要使用"评估器-优化器"或"协调者-工作者",这两种模式天然适合放入异步任务队列(如 Celery)中后台执行。

6.2 业务拓扑决策树

面对一个具体的业务需求,你可以按照以下决策树(Decision Tree)快速定位所需的工作流模式:
#mermaid-svg-zxYa46Z7dgvuz8GV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zxYa46Z7dgvuz8GV .error-icon{fill:#552222;}#mermaid-svg-zxYa46Z7dgvuz8GV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zxYa46Z7dgvuz8GV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zxYa46Z7dgvuz8GV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zxYa46Z7dgvuz8GV .marker.cross{stroke:#333333;}#mermaid-svg-zxYa46Z7dgvuz8GV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zxYa46Z7dgvuz8GV p{margin:0;}#mermaid-svg-zxYa46Z7dgvuz8GV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zxYa46Z7dgvuz8GV .cluster-label text{fill:#333;}#mermaid-svg-zxYa46Z7dgvuz8GV .cluster-label span{color:#333;}#mermaid-svg-zxYa46Z7dgvuz8GV .cluster-label span p{background-color:transparent;}#mermaid-svg-zxYa46Z7dgvuz8GV .label text,#mermaid-svg-zxYa46Z7dgvuz8GV span{fill:#333;color:#333;}#mermaid-svg-zxYa46Z7dgvuz8GV .node rect,#mermaid-svg-zxYa46Z7dgvuz8GV .node circle,#mermaid-svg-zxYa46Z7dgvuz8GV .node ellipse,#mermaid-svg-zxYa46Z7dgvuz8GV .node polygon,#mermaid-svg-zxYa46Z7dgvuz8GV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zxYa46Z7dgvuz8GV .rough-node .label text,#mermaid-svg-zxYa46Z7dgvuz8GV .node .label text,#mermaid-svg-zxYa46Z7dgvuz8GV .image-shape .label,#mermaid-svg-zxYa46Z7dgvuz8GV .icon-shape .label{text-anchor:middle;}#mermaid-svg-zxYa46Z7dgvuz8GV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zxYa46Z7dgvuz8GV .rough-node .label,#mermaid-svg-zxYa46Z7dgvuz8GV .node .label,#mermaid-svg-zxYa46Z7dgvuz8GV .image-shape .label,#mermaid-svg-zxYa46Z7dgvuz8GV .icon-shape .label{text-align:center;}#mermaid-svg-zxYa46Z7dgvuz8GV .node.clickable{cursor:pointer;}#mermaid-svg-zxYa46Z7dgvuz8GV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zxYa46Z7dgvuz8GV .arrowheadPath{fill:#333333;}#mermaid-svg-zxYa46Z7dgvuz8GV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zxYa46Z7dgvuz8GV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zxYa46Z7dgvuz8GV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zxYa46Z7dgvuz8GV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zxYa46Z7dgvuz8GV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zxYa46Z7dgvuz8GV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zxYa46Z7dgvuz8GV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zxYa46Z7dgvuz8GV .cluster text{fill:#333;}#mermaid-svg-zxYa46Z7dgvuz8GV .cluster span{color:#333;}#mermaid-svg-zxYa46Z7dgvuz8GV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zxYa46Z7dgvuz8GV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zxYa46Z7dgvuz8GV rect.text{fill:none;stroke-width:0;}#mermaid-svg-zxYa46Z7dgvuz8GV .icon-shape,#mermaid-svg-zxYa46Z7dgvuz8GV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zxYa46Z7dgvuz8GV .icon-shape p,#mermaid-svg-zxYa46Z7dgvuz8GV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zxYa46Z7dgvuz8GV .icon-shape .label rect,#mermaid-svg-zxYa46Z7dgvuz8GV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zxYa46Z7dgvuz8GV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zxYa46Z7dgvuz8GV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zxYa46Z7dgvuz8GV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
是
否
是
否
是
否
开始: 分析业务需求
任务能拆解为

固定步骤吗?
步骤之间有严格的

先后依赖关系吗?
需要从多个独立维度

收集信息吗?
提示链模式
并行化模式
拆分的子任务数量

在设计时是固定的吗?
输入的类型复杂多变吗?
并行化模式
协调者-工作者模式
路由模式
对最终结果的正确性

要求极高吗?
评估器-优化器模式


七、综合大实战:智能研报生成系统

本节将把前序章节中的路由、并行、提示链、评估器四种模式进行组合,构建一个企业级的"智能研报系统"。

业务需求:

  1. 接收用户问题。
  2. 判断是"宏观分析"还是"个股研报"。
  3. 如果是个股,并行调用工具获取财报数据、新闻舆情。
  4. 汇总数据,生成研报初稿。
  5. 引入合规与质量评估器,如果不合格,带上修改意见打回重写。
  6. 最终输出报告。

7.1 定义全局状态与数据结构

python 复制代码
from typing import TypedDict, Literal, Annotated, List
from langgraph.graph import StateGraph, START, END
from langgraph.types import Send
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage, SystemMessage
from pydantic import BaseModel, Field
import operator

model = init_chat_model("gpt-4o-mini")

class State(TypedDict):
    user_query: str                 # 用户原始输入
    report_type: str                # 路由结果:macro / stock
    financial_data: str             # 财报数据
    news_data: str                  # 新闻舆情
    report_draft: str               # 研报初稿
    feedback: str                   # 评估反馈
    is_approved: bool               # 是否通过
    iterations: int                 # 迭代次数
    final_output: str               # 最终输出

class RouteDecision(BaseModel):
    type: Literal["macro", "stock"] = Field(description="识别输入的类型:宏观分析还是个股研报")

7.2 节点定义(路由、并行、生成、评估)

python 复制代码
# 1. 路由节点
def router_node(state: State):
    structured_llm = model.with_structured_output(RouteDecision)
    decision = structured_llm.invoke(f"判断以下问题属于宏观分析还是个股研报:{state['user_query']}")
    return {"report_type": decision.type}

# 2. 并行数据抓取节点(模拟)
def fetch_financial(state: State):
    return {"financial_data": "公司Q3营收同比增长20%,净利润超预期..."}

def fetch_news(state: State):
    return {"news_data": "近期行业政策利好,机构密集调研,市场情绪积极..."}

# 3. 初稿生成节点
def generate_report(state: State):
    feedback = state.get("feedback", "")
    prompt = f"根据以下数据撰写研报初稿。\n财报:{state.get('financial_data', '无')}\n舆情:{state.get('news_data', '无')}\n"
    if feedback:
        prompt += f"\n上次修改意见:{feedback},请重点修正。"
    
    result = model.invoke(prompt).content
    return {
        "report_draft": result,
        "iterations": state.get("iterations", 0) + 1,
        "is_approved": False,
        "feedback": ""
    }

# 4. 质量评估节点
class EvalResult(BaseModel):
    is_approved: bool = Field(description="研报质量是否合格")
    feedback: str = Field(description="修改建议,若合格填PASS")

def evaluate_report(state: State):
    structured_llm = model.with_structured_output(EvalResult)
    eval_res = structured_llm.invoke(f"请作为首席分析师,审查以下研报质量:\n{state['report_draft']}")
    return {
        "is_approved": eval_res.is_approved,
        "feedback": eval_res.feedback
    }

# 5. 汇总导出节点
def export_final(state: State):
    return {"final_output": f"【最终研报】\n{state['report_draft']}"}

7.3 构建复合图拓扑

python 复制代码
builder = StateGraph(State)

# 添加节点
builder.add_node("router", router_node)
builder.add_node("fetch_financial", fetch_financial)
builder.add_node("fetch_news", fetch_news)
builder.add_node("generate", generate_report)
builder.add_node("evaluate", evaluate_report)
builder.add_node("export", export_final)

# 1. 起点路由
builder.add_edge(START, "router")

# 2. 动态路由分发 (Routing)
def route_decision(state: State):
    if state["report_type"] == "stock":
        return ["fetch_financial", "fetch_news"] # 返回列表,触发并行
    return "generate" # 宏观分析直接生成

builder.add_conditional_edges(
    "router",
    route_decision,
    ["fetch_financial", "fetch_news", "generate"]
)

# 3. 并行汇聚 -> 生成初稿 (Fan-in)
builder.add_edge("fetch_financial", "generate")
builder.add_edge("fetch_news", "generate")

# 4. 生成 -> 评估 (Prompt Chaining)
builder.add_edge("generate", "evaluate")

# 5. 评估路由 (Evaluator-Optimizer)
def route_evaluation(state: State):
    if state["iterations"] >= 3:
        print(">>> 达到最大重试次数,强制通过。")
        return "export"
    if state["is_approved"]:
        return "export"
    print(f">>> 评估未通过,打回重写。意见:{state['feedback']}")
    return "generate"

builder.add_conditional_edges(
    "evaluate",
    route_evaluation,
    {"generate": "generate", "export": "export"}
)

builder.add_edge("export", END)

research_workflow = builder.compile()

# 运行测试
print("=== 测试个股研报 ===")
res = research_workflow.invoke({"user_query": "帮我分析一下宁德时代最近的财报和舆情,写一份研报"})
print(res["final_output"])

📌 总结:在实际的 AI 工程落地中,极少存在单一的纯粹模式。真正的企业级应用,往往是这五种模式的深度嵌套与组合。

相关推荐
泯泷11 分钟前
AI Agent 工具设计:接了上百个工具,为什么反而更难干活?
llm·agent
岁月宁静35 分钟前
DeepSeek Harness 实战与架构解析:让 Agent 从“会聊”到“能交付”
人工智能·python·agent
LiaoWL12342 分钟前
NumPy 入门实战(二):向量化与余弦相似度——从 ndarray 到 Embedding
python·numpy·embedding
forest_091 小时前
多 Agent 协作第二课:交叉验证≠独立验证
人工智能·python·算法·机器学习·prompt·agi
吴可可1231 小时前
WScript Shell操作注册表详解
开发语言
dozenyaoyida2 小时前
AI与大模型新闻日报 | 2026-11-08
人工智能·搜索引擎·ai·大模型·新闻
纪念 2292 小时前
C++ 算法基础 (差分)
开发语言·c++·算法
泯泷2 小时前
AI Agent 工具设计:读完一百页 PDF,不等于真的看懂了
人工智能·llm·agent
泯泷4 小时前
AI 怎样"查资料"?——RAG 检索增强生成入门(三)
人工智能·llm