设计具备弹性的AI智能体图结构:重试机制、备用方案与GraphRAG的应用。
学习如何利用明确的故障分支、重试逻辑、LangGraph来构建容错型AI智能体工作流,以及了解在何种情况下GraphRAG比普通RAG或智能体循环更具优势。
当故障发生时:规划备用路径
承接之前的讨论,假设此时金融研究工具已经变得更为先进。
回想一下投资组合经理可能会提出的示例问题,比如某只股票在发布财报后为何下跌,以及未来哪些风险需要关注。该系统能够拆解这类问题,从多个来源获取数据,对结果进行分析,起草回复内容,进行验证,并在必要时将其转交给人工审核员——本质上是一种人机协同的模式。
不过还有一点我们尚未讨论:如果该流程中的某个步骤出现故障会怎样?
想象一下系统到达负责处理市场数据的分支节点的场景。
它会发送请求以获取价格变动和交易量数据,但数据提供方未能响应。
在这种情况下,不能假装什么都没发生就直接进入Analyze步骤。
同时,彻底终止整个流程通常也过于极端,因为生产级系统需要某种备用机制。
这正是为什么故障应该被视为图结构中的独立分支,而非事后才考虑的问题。
最初的简单流程如下:
Search Market Data
↓
Analyze
我们可以将其改成类似这样的结构:
Search Market Data
/ \
↓ ↓
Success Failure
↓ ↓
Analyze Retry (how to handle this!?)
通过这种结构,图结构中明确了在请求失败时该采取的行动。
重试逻辑的管理
如果重试成功,工作流程就会像正常情况一样继续进行。
但如果再次失败,与其不断重复发送相同的请求,不如换用其他数据源或提交给人工审核会更合理。
以下是该分支结构的更详细版本:
Search Market Data
/ \
↓ ↓
Success Failure
↓ ↓
Analyze Retry
↓
┌──────┴──────┐
↓ ↓
Success Failure
↓ ↓
Analyze Alternative
Source
这里重要的不是允许重试的次数——而是失败处理从一开始就内嵌在图表的设计中。如图所示,不同类型的故障会引导系统走向不同的处理路径。
假设市场数据 API 只是出现了超时情况,由于问题很可能是暂时的,因此重试是合理的。
但假如请求虽能成功发送,却仅返回与查询时间段无关的无用数据,那么重复发送同样的请求也不会有任何效果。
在这种情况下,更好的做法是查询其他经过验证的备用数据源。
如果这样仍然没有结果,那么这种情况很可能需要提交给人工审核员处理。正是这类细节决定了工作流程是脆弱不可靠的,还是稳定可靠的。
驱动此过程的路由功能可能如下所示:
def route_after_market_search(state):
if state["market_data"]:
return "analyze"
if state["retry_count"] < 2:
return "retry"
return "human_review"
这些条件为流程图设定了明确的边界——允许重试,但不能无限次重复。
同样的原则也适用于流程图中的其他部分,因为过程中可能会出现很多问题:
- 数据检索步骤可能会失败
- 对大语言模型的调用可能会失败
- 某些数据源可能暂时无法访问
- 生成的响应可能无法通过验证检查
无需用一个庞大的错误处理机制来覆盖整个应用程序,而是可以针对每个节点,在出现问题时决定应采取何种措施。
这正是将工作流建模为图结构如此吸引人的原因之一。当出现故障时,你不必陷入“为什么代理会失败?”这类模糊的疑问中。
相反,你可以追踪任务执行的具体路径,精准定位出问题所在:
Understand
↓
Search Filings ✓
↓
Search Earnings ✓
↓
Search Market Data ✗
↓
Retry ✓
↓
Analyze
↓
Write
↓
Check
这样你就有具体的内容可以分析,而非面对一个黑箱。
当然,随着图结构的复杂化,手动管理所有这些内容会变得极为费力——这确实是个值得关注的问题。
此时你需要处理执行逻辑、状态管理、条件路由、重试机制、数据持久化,以及某种用于查看特定执行过程中实际发生情况的方法。
正是在这种情况下,框架能帮你避免在每个项目中重复构建相同的调度机制。
这正是LangGraph旨在填补的空白。
LangGraph
有了这样的框架,你的工作流程就可以拆分为多个独立的研究任务,之后再将这些结果合并,重新尝试失败的步骤,暂停以便人工介入决策,并在流程从一个阶段过渡到另一个阶段时持续跟踪状态。
当然,定义该图结构并实际运行它仍然需要付出努力。
你可以自行实现所有的流程调度,但这样意味着你得维护大量围绕核心人工智能逻辑存在的基础设施。
LangGraph正是为处理这一层而设计的。
LangGraph是一个用于构建具有状态功能的、基于图的智能体工作流程的框架。与其将智能体视为一个不断向语言模型询问下一步该做什么的循环结构,不如明确定义节点、节点间的转换关系、共享状态以及整体的执行流程。
回到之前提到的 NVIDIA 研究代理,其结构可以这样描述:
Question
↓
Understand
↓
┌──────────┼──────────┐
↓ ↓ ↓
Filings Earnings Market
↓ ↓ ↓
└──────────┼──────────┘
↓
Analyze
↓
Write
↓
Check
↙ ↓ ↘
Search Rewrite Human
↓ ↓ Review
└───────┴───────┘
↓
Send
值得注意的是,图的结构与实际的应用逻辑可以共存于同一个代码库中。
同一工作流程的简化版 LangGraph 实现可能如下所示:
from langgraph.graph import StateGraph, END
builder = StateGraph(ResearchState)
builder.add_node("understand", understand)
builder.add_node("search_filings", search_filings)
builder.add_node("search_earnings", search_earnings)
builder.add_node("search_market", search_market)
builder.add_node("analyze", analyze)
builder.add_node("write", write)
builder.add_node("check", check)
builder.set_entry_point("understand")
builder.add_edge("understand", "search_filings")
builder.add_edge("understand", "search_earnings")
builder.add_edge("understand", "search_market")
builder.add_edge("analyze", "write")
builder.add_edge("write", "check")
上述代码片段刻意保持极简,以便您能专注于核心思维模型——即将本系列前文介绍的节点、边和状态概念联系起来。
- 每个节点负责处理一项工作
- 图定义了这些任务单元之间的连接方式
- 状态用于在各个节点之间传递信息
其实就这些内容。这种思维模型比试图将所有内容塞进一个庞大的代理提示语中要容易理解得多。
在深入研究每个节点的具体实现方式之前,你可以通过查看图表本身来了解工作流应有的功能。
假设你想在最终结果输出之前加入人工审批步骤——你需要直接修改工作流的结构:
Check
↓
Human Review
↓
Send
没必要把这类决策藏在某个提示语中。这样的结构本身就能清晰地展现出来,便于团队理解。
为何选择 LangGraph 而非普通的智能体循环?
传统的智能体循环通常采用以下结构:
Think
↓
Choose Tool
↓
Use Tool
↓
Think Again
↓
Choose Tool
↓
...
在这种架构下,模型在决定下一步操作时拥有很大的自由度。当任务本身具有开放性时,这种灵活性确实非常有价值。
但像这里描述的那种金融研究工作流包含多个决策点,你很可能希望对其实施更严格的控制。这一区别正是问题的关键所在。
并非所有工作流都需要代理循环。
举几个例子:
- 你可能要求系统在生成答案之前先收集特定证据。
- 你可能希望市场数据与相关文件能够独立获取。
- 在将任何内容转交给人类审核员之前,你可能需要进行验证步骤。
- 你可能希望在该流程经过指定次数的重试后自动停止。
这些都不是那种可以完全交给模型在每次运行时自行决定的事项。在循环结构中,无法保证模型每次都会执行相同的操作。
图表能让您将这些约束直接嵌入工作流的架构中。模型仍可在每个独立节点内进行推理,但整个系统会决定它下一步可以前往何处。
这就是需要牢记的关键区别。目标并非消除智能体的推理能力——而是有意识地决定推理应放在何处,以及何时采用确定性调度更为合适。
随着您继续构建处理结构化金融数据的工作流,一个新的问题出现了。
目前,我们的智能体从各种来源获取文档和数据。但当底层信息之间存在真正重要的关联时又会怎样呢?
- 企业会雇佣高管
- 这些高管通常还会担任公司董事会成员
- 一家企业可能会收购另一家企业
到目前为止,我们一直将每条数据视为独立的个体。然而,如果您的知识库仅仅是一堆独立的文档,上述关联就很难体现出来。
这就引出了另一个核心概念:GraphRAG。
GraphRAG
迄今为止,我们主要利用图结构来控制智能体的行为方式。但图结构还有第二个用途:
对知识本身进行组织
在我们之前的示例中,传统的RAG架构可能会检索提及NVIDIA盈利情况、未来预期、营收数据、利润率以及市场反应的文档。
当答案主要存在于少数几份相关文档中时,这种方法效果不错。但金融类问题并不总是如此局限。
一个有意义的答案往往取决于将分散在多份独立文档中的信息关联起来。
现在想象一下,我们不仅想了解 NVIDIA 公布了什么,还想知道该报告中的各个部分之间是如何相互关联的(这正是我们当前面临的情况)。
而这正是知识图谱发挥作用的地方。
我们无需仅以文本块的形式存储知识,而是可以构建重要的实体及其相互关系模型:
NVIDIA ──→ produces ──→ GPUs
│
├──→ reports ──→ Earnings
│
├──→ depends on ──→ Suppliers
│
├──→ competes with ──→ AMD
│
└──→ operates in ──→ AI Infrastructure
这样的图谱能够捕捉到那些单独阅读任何一份文档时都可能无法发现的关联。
RAG 与 GraphRAG 的对比
标准的RAG通常通过获取相关片段并将其作为上下文输入给模型来工作。以下是该流程的简化示意图:
Question
↓
Retrieve relevant chunks
↓
LLM
↓
Answer
GraphRAG增加了额外的一层处理机制。该系统可以利用实体之间的关系来判断哪些信息才是真正重要的:
Question
↓
Identify entities
↓
Traverse relationships
↓
Retrieve relevant information
↓
LLM
↓
Answer
对于我们的金融研究智能体而言,每当有需求需要同时关联多个实体或文档时,这一机制就会发挥重要作用。比如,假设投资组合经理询问:如果对AI基础设施的需求下降,NVIDIA会面临哪些风险?
要妥善回答这个问题,就需要将NVIDIA与其产品、客户、供应商、竞争对手、更广泛的市场环境,以及其在过往文件中已提及的风险联系起来。
图结构为检索系统提供了有序的路径,便于探索所有这些关联关系。
NVIDIA
/ | \
/ | \
Products Suppliers Competitors
| | |
↓ ↓ ↓
AI GPUs Supply Risk AMD
|
↓
AI Infrastructure
|
↓
Demand
由此,模型可以基于从这些关联实体中获取的所有信息进行推理。
这并不意味着 GraphRAG 总能优于普通的 RAG。只有当信息之间的关联关系是所提问题的核心时,额外的结构优势才会显现。
如果答案完整地存在于某份财报中,传统的检索流程可能就足够了。
如果回答问题需要整合分散在众多实体和来源中的信息,基于图的架构就会显得更有优势。
这里还有另一个需要区分的概念:你的工作流图与知识图谱是完全不同的两回事。
工作流图用于回答智能体下一步应该做什么的问题。知识图谱则用于回答在所处理的数据中哪些元素彼此之间存在关联。
在我们的系统中,这两种结构可以并行存在:
WORKFLOW GRAPH:
Question
↓
Retrieve
↓
Analyze
↓
Write
↓
Check
而底层则由检索层负责处理:
KNOWLEDGE GRAPH:
NVIDIA
├── Products
├── Suppliers
├── Competitors
├── Executives
├── Filings
└── Industry
注意职责的分工:一种结构负责控制执行流程,另一种则负责整理知识及其内部关系。
一旦这两种理念开始相互支撑,金融研究智能体的能力就会大幅提升。
- 工作流决定了研究的具体执行方式。
- 知识图谱则决定了哪些信息彼此关联。
当然,这引出了一个后续问题:既然图结构能带来如此大的控制力,是否所有的工作流都应该用图结构来构建?
并非必然。
在很多情况下,问题实际上只需要一个简单的智能体循环即可解决。
因此,与其默认将每个人工智能工作流都转换为图结构,不如直接比较这两种方法,弄清楚何时图结构带来的额外复杂性才真正有价值。
需要记住的是,图结构虽然能赋予你更多控制权,但这种控制力也意味着额外的工作量:你现在必须亲自设计并维护该结构。
如果底层任务本身就很简单,将其封装在图结构中反而可能让系统更难以理解,且不会带来太多好处。
再以金融研究智能体为例。假设用户提出类似这样的请求:总结英伟达最新的财报。
这是一个范围明确、独立的请求。
你可以通过获取相关报告并交由模型来生成总结来解决它:
Question
↓
Retrieve Report
↓
Summarize
↓
Answer
这里没有复杂的分支结构,也不需要处理复杂的逻辑。普通的RAG流程或基础的智能体循环就能很好地完成这项任务。
现在将其与更复杂的请求相比,用户希望了解是什么导致了财报发布后股价的下跌,希望将实际结果与分析师的预测进行对比,希望获得一份未来需要关注的风险清单,还希望在这些分析结果被纳入投资组合报告之前能够对其进行确认。
那完全是另一种类型的问题了。
此版本涉及多种类型的研究,需要从不同来源获取数据,必须经过验证,可能需要多次尝试,并且还包括人工审核步骤。
当前的工作流程更接近于以下形式:
Question
↓
Understand
↓
┌──────────┼──────────┐
↓ ↓ ↓
Filings Earnings Market
↓ ↓ ↓
└──────────┼──────────┘
↓
Analyze
↓
Write
↓
Check
↙ ↓ ↘
Search Rewrite Human
↓ ↓ Review
└───────┴──────┘
↓
Send
在如此复杂的场景下,明确阐述工作流程确实能带来好处。
那么,何时使用智能体循环就足够了呢?
当模型能够在选择下一步使用哪种工具时拥有某种自由度时,智能体循环往往是合适的选择。
例如:
Question
↓
LLM decides what to do
↓
Tool
↓
LLM decides what to do next
↓
Tool
↓
Answer
当任务具有探索性,且无法提前预测步骤数量时,这种灵活性就显得极为重要。
该代理可能会决定需要查看收益记录,然后提取特定指标,再寻找更多背景信息。你不必提前规划好所有可能的路径。
何时图表会更有意义?
只有当工作流拥有真正重要的规则时,图表才能发挥作用。
对于金融研究系统而言,这些规则可能表现为:
Retrieve evidence
↓
Run analysis
↓
Generate draft
↓
Verify claims
↓
Human review
↓
Publish
这些都是工作流层面的约束,而图表能让您直接将它们编码进去,无需依赖模型自行推断。
权衡
图表虽然能赋予您控制力,但同时也意味着需要处理更复杂的架构问题。
现在您必须考虑:
- 状态的结构方式
- 路由逻辑
- 出现故障时的处理方式
- 重试机制
- 数据持久性
这些本身并无坏处——只是意味着该图结构需要证明其带来的额外开销是合理的。
对于 NVIDIA 的研究代理而言,仅仅因为图结构有趣就提前构建完整的图结构是毫无意义的。
相反,只有在工作流确实需要时才应引入额外的结构:
- 从简单开始。
- 只有在真正需要分支时才添加分支结构。
- 只有当任务能够真正独立运行时才添加并行执行功能。
- 只有在工作流需要暂停或从中断中恢复时才添加检查点。
- 只有在额外成本值得承担时才加入人工审核步骤。
- 只有当知识内部的关系开始变得重要时才引入 GraphRAG。
这样,图的存在是因为问题需要它,而非因为它随手可得。
总结
这或许是完成这项设计后所得到的核心启示。
图的存在并非为了让人工智能系统显得更复杂,而是为了让工作流程更易于理解和控制。
通过这次讨论构建的金融研究智能体现在能够进行分支处理、重试、暂停、引入人工审核环节,甚至在有必要时利用不同知识点之间的关联。
不过,这并不意味着每个系统都需要图结构。有时简单的RAG流程就已足够;有时智能体循环就能满足问题所需的一切功能。
真正的技巧在于能够识别问题复杂到何种程度时,使用图表来分析才是值得的。