第 4 章 Agent 编排与状态机¶
本章学习目标 - 深入理解 LangGraph 的 StateGraph、条件边、Checkpointer、Store 等核心机制 - 掌握 TTD 的 20+ 节点图拓扑、7 条路由、AgenticBIState 状态机设计 - 深度对比 ReAct、Plan-and-Execute、Reflexion 三种 Agent 范式及其在 TTD 中的融合 - 理解修复循环、条件路由、SQL Cache 快速路径的实现原理 - 认清当前编排设计的工程权衡与改进方向
前置知识:第 2 章 整体架构、第 3 章 ADR 关联代码:
backend/app/agents/builder.py、backend/app/agents/state.py、backend/app/agents/sub_agents/router.py
4.1 背景与动机¶
4.1.1 为什么需要编排¶
一个 NL2SQL 查询远不止"调用一次 LLM 生成 SQL"那么简单。它涉及意图理解、检索、规划、生成、校验、执行、可视化、洞察——每一步都可能失败、需要重试或降级。如果把这些全塞进一个 prompt,LLM 既无法可靠执行,也无法审计。
Agent 编排要解决的核心问题是:如何把一个复杂任务拆解为可控的多步流程,让每步可观测、可纠错、可组合。
4.1.2 编排范式的光谱¶
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#e8f0fe','primaryTextColor':'#202124','primaryBorderColor':'#1a73e8','lineColor':'#5f6368','secondaryColor':'#e0f2f1','tertiaryColor':'#f8f9fa'}}}%%
flowchart LR
A["单次 LLM 调用<br/>最简但不可控"] --> B["ReAct 自主循环<br/>灵活但不可预测"]
B --> C["Plan-and-Execute<br/>先规划再执行"]
C --> D["确定性 DAG + 条件路由<br/>TTD 当前选择<br/>可控但不够灵活"]
D --> E["自适应编排<br/>动态子图 + 中断点<br/>演进目标"]
classDef bpProcess fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px,color:#202124
classDef bpExternal fill:#f8f9fa,stroke:#dadce0,stroke-width:2px,color:#5f6368
classDef bpInfo fill:#f3e8fd,stroke:#9334e6,stroke-width:2px,color:#202124
classDef bpSuccess fill:#e6f4ea,stroke:#1e8e3e,stroke-width:2px,color:#202124
class A,B,C bpExternal
class D bpInfo
class E bpSuccess
TTD 选择了 "确定性 DAG + 条件路由"(上图 D),这是工程上最可控的范式。代价是灵活性——无法处理预料之外的查询模式。演进目标是 自适应编排(上图 E),在保持可控性的前提下增加动态性。
4.2 设计原理¶
4.2.1 LangGraph 核心机制¶
AI Agent 理论关联:LangGraph 的编排模型
LangGraph 是 LangChain 团队推出的 Agent 编排框架,其核心抽象是 StateGraph——一个以状态为中心的有向图。理解 LangGraph 需要掌握几个关键概念:
- State(状态):一个 TypedDict 或 dataclass,贯穿全图,所有节点共享。每个节点接收状态、返回状态的增量更新。LangGraph 通过 reducer(如
add_messages)定义字段如何合并(覆盖 vs 追加)。 - Node(节点):一个函数
async def node(state) -> dict,接收当前状态,返回要更新的字段。节点内部可调用 LLM、工具、数据库。 - Edge(边):节点间的转移。固定边(
add_edge)是无条件跳转;条件边(add_conditional_edges)根据状态动态决定下一个节点。 - Checkpointer(检查点):将每一步的状态快照持久化(如
AsyncPostgresSaver),实现会话恢复、时间旅行、断点续跑。这是多轮对话与容错的基础。 - Store(长期存储):跨会话的 KV 存储(如
AsyncPostgresStore),支持按 namespace 隔离与语义搜索,用于长期记忆。 - Command(goto=...):节点可通过返回
Command对象显式指定跳转目标,比条件边更灵活。 - Interrupt(中断):在指定节点暂停执行,等待外部输入(如人工确认)后恢复——这是 human-in-the-loop 的基础。
LangGraph 与纯 ReAct 的本质区别:ReAct 让 LLM 自主决定下一步,LangGraph 让开发者用图结构预定义下一步。前者灵活不可控,后者可控不灵活。生产系统倾向后者。
4.2.2 图拓扑总览¶
TTD 用 LangGraph 的 StateGraph 编排了 20+ 节点的工作流。入口固定为 START → supervisor → query_understanding_agent → router,router 之后根据意图分流到 7 条预定义路由计划。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#e8f0fe','primaryTextColor':'#202124','primaryBorderColor':'#1a73e8','lineColor':'#5f6368','secondaryColor':'#e0f2f1','tertiaryColor':'#f8f9fa'}}}%%
graph TD
START([START]) --> supervisor
supervisor --> query_understanding_agent
query_understanding_agent --> router
router -->|"kpi_lookup<br/>nl2sql_query"| data_retrieval_agent
router -->|"deep_analysis_workflow"| planner
router -->|"graph_reasoning"| graph_rag_agent
router -->|"business_knowledge_qa"| data_retrieval_agent_kb
router -->|"clarification_required"| data_retrieval_agent_cl
subgraph SqlPipeline["SQL Pipeline"]
data_retrieval_agent --> reranker
reranker --> semantic_planner
semantic_planner --> sql_generator
sql_generator --> guardrails_agent
guardrails_agent -->|"execute"| sql_executor
guardrails_agent -->|"corrective_repair"| corrective_retrieval
guardrails_agent -->|"reject"| END_REJECT([END])
corrective_retrieval --> sql_generator
sql_executor --> result_processor
result_processor --> sql_explainer
end
sql_explainer --> visualization_agent
visualization_agent --> insights_agent
insights_agent --> suggest_followup_agent
suggest_followup_agent --> END_OK([END])
planner --> data_retrieval_agent
graph_rag_agent --> END_GRAPH([END])
data_retrieval_agent_kb --> knowledge_qa_agent
knowledge_qa_agent --> END_KB([END])
data_retrieval_agent_cl --> follow_up_agent
follow_up_agent --> END_CL([END])
classDef bpProcess fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px,color:#202124
classDef bpData fill:#e0f2f1,stroke:#0d7377,stroke-width:2px,color:#202124
classDef bpDecision fill:#fef7e0,stroke:#f9ab00,stroke-width:2px,color:#202124
classDef bpSuccess fill:#e6f4ea,stroke:#1e8e3e,stroke-width:2px,color:#202124
classDef bpError fill:#fce8e6,stroke:#d93025,stroke-width:2px,color:#202124
classDef bpInfo fill:#f3e8fd,stroke:#9334e6,stroke-width:2px,color:#202124
class supervisor,query_understanding_agent,router bpProcess
class data_retrieval_agent,data_retrieval_agent_kb,data_retrieval_agent_cl,reranker bpData
class semantic_planner,sql_generator,result_processor,sql_explainer bpProcess
class guardrails_agent bpDecision
class sql_executor bpProcess
class corrective_retrieval bpInfo
class visualization_agent,insights_agent,suggest_followup_agent bpProcess
class planner,knowledge_qa_agent,follow_up_agent bpProcess
class graph_rag_agent bpInfo
class END_OK,END_GRAPH,END_KB,END_CL bpSuccess
class END_REJECT bpError
图的编译在 main.py 启动时完成一次,注入 Checkpointer(AsyncPostgresSaver,会话持久化)和 Store(AsyncPostgresStore,长期记忆)。编译后的图是只读的,所有状态变化通过 Checkpointer 落盘。
4.2.3 七条路由¶
Router 根据 Query Understanding 的输出,用 _INTENT_TO_ROUTE 映射表(非 ML)决定路径:
| Route | 触发意图 | 执行路径 |
|---|---|---|
kpi_lookup |
KPI 直查 | retrieval → reranker → sql_gen → guardrails → executor → explainer |
nl2sql_query |
趋势/对比/排名 | 完整管道 + viz + insights + followup |
deep_analysis_workflow |
根因分析/报告 | planner + 全链路 + 三级评估器 |
analytical_workflow |
归因/预测/分群 | + analytical_agent (ML toolkit) |
graph_reasoning |
关系探索 | graph_rag_agent (NL2Cypher),失败回退 SQL |
business_knowledge_qa |
政策/SOP 问答 | retrieval → knowledge_qa_agent |
clarification_required |
模糊意图 | retrieval → follow_up_agent |
AI Agent 理论关联:ReAct / Plan-and-Execute / Reflexion 三范式融合
TTD 的编排混合了三种 Agent 范式,理解它们的差异是理解 TTD 设计的关键:
ReAct(Reasoning + Acting,2022) - 核心思想:LLM 交替进行"推理(Thought)"与"行动(Action)",在每一步根据观察结果(Observation)决定下一步。 - 循环:Thought → Action → Observation → Thought → ... 直到完成。 - 优势:灵活,能处理开放性任务。 - 劣势:不可预测(LLM 可能走偏)、难审计(需追踪完整 thought 链)、延迟不可控(循环次数不定)。 - 在 TTD 中:每个节点内部是 ReAct 的单步——LLM 推理后产出结构化结果,但"下一步做什么"由图决定而非 LLM。
Plan-and-Execute(2023)
- 核心思想:先让 LLM 制定完整计划(Plan),再逐步执行(Execute),执行中可动态调整计划。
- 优势:有全局视野,避免 ReAct 的短视;适合多步复杂任务。
- 劣势:计划可能基于不完整信息;计划与执行的解耦增加复杂度。
- 在 TTD 中:deep_analysis_workflow 路由含独立的 planner 节点,先 LLM 生成多步执行计划,再逐步执行。这是典型的 Plan-and-Execute(arxiv.org/abs/2305.04091)。
Reflexion(2023)
- 核心思想:在行动后引入"自我反思"——评估执行结果,基于反馈修正后续行为。
- 优势:能从失败中学习,自我纠错。
- 劣势:反思本身消耗 LLM 调用;反思质量依赖评估准确性。
- 在 TTD 中:deep_analysis_workflow 的三级评估器(retrieval/sql/answer evaluator)对结果打分,低分触发反思重试。这是 Reflexion(arxiv.org/abs/2303.11366)的简化版——评估但不基于反思重写计划。
TTD 的取舍:用确定性图保证安全可审计(不同于纯 ReAct),用节点内的 LLM 保证局部智能,用 Plan-and-Execute 处理复杂分析,用 Reflexion 做质量兜底。这是"范式融合"的工程实践。
4.2.4 全局状态 AgenticBIState¶
AgenticBIState(backend/app/agents/state.py)是贯穿全图的 TypedDict(total=False),所有字段可选,节点只返回需要更新的字段(状态增量模式——LangGraph 的 reducer 会合并这些增量)。
| 类别 | 关键字段 | 说明 |
|---|---|---|
| 输入 | question, language, user_context, session_id, options |
用户请求 |
| 编排 | routing_plan, current_agent, route_decision, execution_plan, plan_step_index |
路由与计划 |
| 记忆 | short_term_memory, memory_context |
短期 + 长期记忆 |
| 生成 | query_understanding, retrieval_results, graph_context, generated_sql, execution_result, visualization, insights |
各阶段产出 |
| 评估 | retrieval_quality, sql_quality, answer_quality |
评估器打分 |
| 治理 | tenant_id, governance_overlays, region |
治理上下文 |
| 控制 | status, errors, sql_cache_hit, repair_attempt, clarification_message |
流程控制 |
4.3 实现详解¶
4.3.1 图的构建¶
图的编译在 main.py 启动时完成一次,注入 checkpointer(会话持久化)和 store(长期记忆):
# backend/app/agents/builder.py(简化)
def build_supervisor_graph(checkpointer, store, registry, pool):
graph = StateGraph(AgenticBIState)
# 注册 20+ 节点
graph.add_node("supervisor", make_supervisor_node(registry))
graph.add_node("query_understanding_agent", make_query_understanding_node(registry))
graph.add_node("router", make_router_node(sql_cache))
graph.add_node("data_retrieval_agent", make_data_retrieval_node(retriever))
# ... 其余节点
# 固定入口边
graph.add_edge(START, "supervisor")
graph.add_edge("supervisor", "query_understanding_agent")
graph.add_edge("query_understanding_agent", "router")
# 条件边:router → 7 路由
graph.add_conditional_edges("router", _route_after_router, {...})
# 条件边:guardrails → execute/repair/reject
graph.add_conditional_edges("guardrails_agent", _route_after_validation, {...})
return graph.compile(checkpointer=checkpointer, store=store)
thread_id=session_id 是 Checkpointer 的会话标识——同一会话的状态快照按 thread_id 隔离,实现多轮对话的上下文持久化。
4.3.2 条件路由函数¶
条件边是 TTD 编排的"控制流大脑"。每个条件函数根据当前状态决定下一个节点:
| 函数 | 位置 | 可能返回值 | 决策依据 |
|---|---|---|---|
_route_after_router |
router 后 | 7 路由分支 | QU 的 intent_type 映射 |
_route_after_retrieval |
data_retrieval 后 | rerank, knowledge_qa, no_context, clarify |
检索结果质量 |
_route_after_validation |
guardrails 后 | execute, corrective_repair, repair, reject |
校验失败类型 |
_route_after_execution |
executor 后 | proceed, repair, fail |
执行结果/超时 |
_route_after_result |
explainer 后 | visualize, evaluate, insight, done |
路由计划 |
_route_after_graph_rag |
graph_rag 后 | done, fallback_sql |
Cypher 是否成功 |
4.3.3 修复循环¶
这是 TTD 编排的核心鲁棒性机制。Guardrails 校验失败时,不是直接拒绝,而是尝试修复——这是 Reflexion 范式的工程实现:
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#e8f0fe','primaryTextColor':'#202124','primaryBorderColor':'#1a73e8','lineColor':'#5f6368','secondaryColor':'#e0f2f1','tertiaryColor':'#f8f9fa'}}}%%
flowchart LR
GR["Guardrails<br/>五层校验"] -->|"通过"| EX[执行]
GR -->|"列错误/术语违规<br/>可修复(最多2次)"| CR["Corrective Retrieval<br/>补偿检索"]
CR --> SG["SQL Generator<br/>重试生成"]
SG --> GR
GR -->|"性能问题<br/>EXPLAIN cost 超阈值"| SG2["SQL Generator<br/>性能优化重试<br/>(最多1次)"]
SG2 --> GR
GR -->|"不可修复<br/>语法错误/策略违规"| REJ["拒绝执行<br/>返回用户友好错误"]
classDef bpProcess fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px,color:#202124
classDef bpDecision fill:#fef7e0,stroke:#f9ab00,stroke-width:2px,color:#202124
classDef bpSuccess fill:#e6f4ea,stroke:#1e8e3e,stroke-width:2px,color:#202124
classDef bpError fill:#fce8e6,stroke:#d93025,stroke-width:2px,color:#202124
classDef bpInfo fill:#f3e8fd,stroke:#9334e6,stroke-width:2px,color:#202124
class GR bpDecision
class EX bpSuccess
class CR bpInfo
class SG,SG2 bpProcess
class REJ bpError
linkStyle 0 stroke:#1e8e3e,stroke-width:2.5px
linkStyle 1,2,4 stroke:#f9ab00,stroke-width:2px
linkStyle 3 stroke:#1a73e8,stroke-width:2px
linkStyle 5 stroke:#d93025,stroke-width:2px
- 列引用错误:
sql_column_validator检测到不存在的列 → corrective_retrieval 拉取正确列 → sql_generator 重试(最多 2 次) - 术语绑定违规:术语"价格"应绑定到
fact_transaction.price,但 SQL 写了dim_article.price→ corrective_retrieval 拉取正确表 → 重试 - 性能问题:EXPLAIN 估算行数/cost 超阈值 → sql_generator 做性能优化重试(最多 1 次)
AI Agent 理论关联:Reflexion 与自愈
修复循环是 Reflexion 范式的工程实现:Agent 执行后获得反馈(校验失败信息),基于反馈调整行为(重新检索 + 重新生成)。
区别于学术 Reflexion 的是,TTD 的"反思"是确定性的(基于明确的错误类型触发对应的修复策略),而非让 LLM 自主反思。这保证了修复的可预测性,但限制了处理"未知错误"的能力。
与 Corrective RAG(CRAG)的关系:修复循环中的 corrective_retrieval 正是 CRAG 思想的体现——检索质量不够时不硬生成,而是补充检索后再试。详见 第 5 章。
4.3.4 SQL Cache 快速路径¶
Router 在路由时检查 SQL 语义缓存:若问题 embedding 与缓存 SQL 的 embedding 相似度 ≥ 0.92,直接跳转 guardrails_agent,跳过检索与生成。这是用空间换时间的优化,详见 第 7 章。
4.3.5 Graph RAG 降级¶
graph_reasoning 路由的 graph_rag_agent 若失败(如 Cypher 生成错误),自动回退到 SQL 流水线(_route_after_graph_rag → fallback_sql)。这是优雅降级的典型实现——任何子路径失败都有 fallback,不阻塞用户。
4.3.6 模型分配¶
每个节点通过 registry.get_model(task) 获取模型,任务→模型映射见 ADR-7。模型分配由 ModelRegistry 统一管理,支持 Admin DB 在线覆盖与 Fallback 链降级。
4.4 方案选型对比¶
4.4.1 Agent 编排框架对比¶
| 维度 | LangGraph | AutoGen | CrewAI | 自建状态机 |
|---|---|---|---|---|
| 编排模型 | 显式状态图 | 对话式多 Agent | 角色流水线 | 完全自定义 |
| 可控性 | 高 | 中 | 中 | 最高 |
| 状态持久化 | 内置 Checkpointer | 需自建 | 弱 | 需自建 |
| HITL 中断 | 原生 interrupt | 需自建 | 弱 | 需自建 |
| 生态 | LangChain 生态 | 微软生态 | 新兴 | 无 |
| 学习曲线 | 中 | 中 | 低 | 高 |
| TTD 选择理由 | 显式图 + 持久化 + LangChain 集成最契合 NL2SQL 的可审计需求 |
4.4.2 DAG 编排 vs 自主 ReAct¶
| 维度 | 确定性 DAG(TTD) | 自主 ReAct |
|---|---|---|
| 可预测性 | 高(路径固定) | 低(LLM 自主决定) |
| 可审计性 | 高(每步显式) | 低(需追踪 thought 链) |
| 灵活性 | 低(路径预设) | 高(可处理意外) |
| 安全性 | 高(可控执行) | 低(可能跑偏) |
| 延迟 | 可预估 | 不可预估 |
| 适用场景 | 安全敏感、流程明确的任务 | 探索性、开放任务 |
性能与质量指标(设计目标)
以下为编排管道的设计目标值,TTD 尚未进行系统化实测:
| 指标 | 设计目标 | 说明 |
|---|---|---|
| Supervisor 延迟 | < 100ms | 嵌入计算 + 治理叠加层解析(无 LLM) |
| Query Understanding 延迟 | 0.5-1.5s | 单次 LLM 调用(deepseek-v4-flash) |
| Router 延迟 | < 5ms | 纯确定性映射(_INTENT_TO_ROUTE) |
| 主路径端到端延迟(nl2sql_query) | < 3s | 不含 SQL 执行时间 |
| deep_analysis 端到端延迟 | < 10s | 含三级评估器 + 多步分析 |
| SQL Cache 命中跳过延迟 | < 200ms | 跳过检索与生成,仅护栏+执行 |
当前实现可改进之处
- 路由完全静态缺乏自学习:7 条路由是硬编码映射,当用户意图不在预设类别时只能 fallback。无法从历史查询中自动发现新模式。改进:增加路由效果反馈环,记录失败率,提示维护者扩展路由(详见 第 16 章)。
- 状态字段过多(~50 个)是"上帝对象"反模式:AgenticBIState 字段间有隐含依赖(如
visualization依赖execution_result),难以追踪。改进:用子状态分组(input/retrieval/generation/control)或引入 dataclass 收敛,强制声明字段依赖。 - 无 human-in-the-loop 中断点:LangGraph 原生支持 interrupt,但 TTD 只在可视化有 HITL,SQL 执行前无"高危查询人工确认"。改进:对 EXPLAIN 成本超阈值的查询加 interrupt,让用户确认后再执行。
改进方向
核心演进方向是自适应编排:保留确定性 DAG 的可审计骨架,在关键决策点引入动态子图与中断点,让系统在"可控"与"灵活"间取得更好平衡。这与多 Agent 辩论(multi-agent debate)、Self-Discover 等前沿方向呼应——让 Agent 在不确定时能"停下来想一想"或"找人确认",而非硬走预设路径。
4.5 小结与延伸阅读¶
本章要点:
- TTD 用 LangGraph 编排 20+ 节点的确定性 DAG,7 条路由覆盖从 KPI 直查到深度分析的全场景。
- LangGraph 核心机制:StateGraph(状态中心图)、条件边(动态路由)、Checkpointer(状态持久化)、Store(长期记忆)、Interrupt(HITL)。
- AgenticBIState 是贯穿全图的状态机,节点采用状态增量模式。
- 修复循环(corrective_retrieval + 重试)是核心鲁棒性机制,基于 Reflexion 范式。
- 编排融合了 ReAct(节点内)、Plan-and-Execute(planner)、Reflexion(评估器)三种范式。
延伸阅读:
- ReAct: Synergizing Reasoning and Acting — arxiv.org/abs/2210.03629
- Plan-and-Execute — arxiv.org/abs/2305.04091
- Reflexion — arxiv.org/abs/2303.11366
- LangGraph 官方文档 — langchain-ai.github.io/langgraph
下一章:第 5 章 检索增强:R/V/G/D 四引擎 RAG——详解四引擎如何协作检索、Reranker 如何重排、补偿检索如何修复。