> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 我的 ReAct 智能体像一台老虎机
- URL: https://www.hubwiz.com/blog/my-react-agent-was-a-slot-machine/
- Published: 2026-05-21T12:06:06.000Z
- Updated: 2026-10-13T16:07:04.000Z
- Description: 我如何将一个需要30次LLM调用的ReAct智能体重设计为只需要2次LLM调用的确定性图，成本降低了一个数量级，并弄清了AI工具的局限性所在
- Author: admin
- Tags: AGENT, #Import 2026-10-14 14:41

一个星期六上午9点，我坐下来处理一个小型自主工作流的规格说明。从数据库读取记录，应用一堆业务规则，查阅一些非结构化的政策文档，然后把决策写回去。

我在六小时内让它端到端运行起来。大部分代码是Claude Code生成的。架构设计没有。

这个区别就是整篇文章的核心。

## 1、第一个版本代价高昂

我的第一直觉是最明显的那种：给LLM一组工具，写一个解释工作流的系统提示词，让它自己推理完成。经典的ReAct模式。这是每个教程都会采用的模式，Claude Code大约十分钟就能搭建一个。

它也确实能运行。差不多吧。

它读取了记录，调用了聚合工具，调用了供应商查询工具。它到了运行结束，返回了一个结果，但结果以难以预测的方式是*错误的*。有时它跳过了一条记录。有时它把两个数字相加得到一个既不是和也不是差的第三个数字。有时在相同输入的不同运行之间，工具调用顺序颠倒了。

我数了LLM调用次数。每次运行二十到三十次。每一次都是到前沿模型的往返，每一次都是一个小小的骰子投掷——模型是否会调用正确的工具——每一次都计费。

这是教程中不会出现的部分。*一个在你已经理解的工作流上的纯ReAct智能体是一台你在付费运营的老虎机。*

对于一个步骤已知、顺序已知、数学必须精确的问题来说，这是错误的工具。

## 2、从$$$到$的重设计

我删除了ReAct循环。我把整个东西重写为具有八个节点的固定图。六个是普通Python函数。两个调用LLM。状态对象是一个贯穿每个节点的类型化字典。

```python
class ClaimsState(TypedDict):
    db: DatabaseClient
    current_date: str
    policy_context: str
    raw_claims: list
    aggregated_exposures: list
    eligible_payers: dict
    governance_filtered_payers: dict
    decisions: list
    alerts: list

```

这就是一个工业级理赔分流工作流的整个共享状态。节点接收它，只返回它们更新的键，然后传递下去。没有魔法。没有决定下一步做什么的推理循环。

![架构图](https://www.hubwiz.com/blog/content/images/2026/05/1-iFVbxdBqep2cCw6VgxlNHQ.png)

LLM只承担两项工作：

1. 读取非结构化的政策PDF并根据它们对候选者进行排名。
2. 在最后综合出人类可读的警报摘要。

其他一切——算术部分、基于规则的部分、数据库读取——都变成了确定性的Python。LLM调用次数从每次运行大约三十次降到了两次。在我使用的模型上，这就是每次运行几美元和每次运行几美分的区别。是一个数量级的差异，不是四舍五入的误差。

这是我希望每个读者都能带走的核心观点：**找到那一两个LLM确实无法完成的步骤，把LLM放在那里。其他所有地方，写代码。**

政策解释就是其中之一。读取一个写着"当患者的计划是PPO时优先选择网络内提供商，除非最近的网络更新备忘录覆盖了它"的PDF，并用通俗英语根据它对列表进行排名——这确实是LLM擅长的事情。两个整数相加不是。

## 3、LLM仍然试图做算术并出错的地方

有趣的bug出现在重设计开始工作之后。

一个索赔有截止日期。一个付款方有周转时间。检查很简单：`today + turnaround <= deadline`。我把这个检查放在了LLM治理提示词中，因为LLM已经在筛选候选者了，把这条规则也加进去感觉很自然。

它大约有10%的时间是错误的。特别是边界情况。14天的周转时间对比12天的截止日期会被漏过去。LLM会写一句自信的话解释为什么这个付款方是合适的并批准它们，然后下游节点会愉快地做出一个已经不可行的决策。

修复方法是一个六行的确定性守卫，在LLM看到候选列表之前运行：

```python
delivery_dt = datetime.strptime(current_date, "%Y-%m-%d") + timedelta(days=payer.turnaround_days)
deadline_dt = datetime.strptime(claim["deadline"], "%Y-%m-%d")
if delivery_dt > deadline_dt:
    logger.info("payer_excluded_lead_time", payer_id=payer.id)
    continue

```

LLM再也没有机会在这方面犯错了。它现在唯一的工作是在可行选项之间进行排名，这才是它真正擅长的事情。

> 每次我捕获到LLM不可靠时，根本原因都是我让它做了确定性检查可以完成的事情。把检查移到上游，不可靠性就消失了。

这是一小段无聊的代码。但它也是系统从"演示质量"变成"我真的会信任这个"的时刻。

## 4、Claude Code写了大部分代码

我想精确说明Claude Code做了什么和没做什么，因为答案不是"它很棒"或"它被过度炒作了"。答案比那更有用。

**它擅长的：**

- 一旦我告诉它节点名称，就能搭建LangGraph DAG和类型化状态。
- 用适当的上下文管理器和Pydantic模型编写SQLite客户端。
- 从单个提示生成跨Azure OpenAI、OpenAI和Anthropic的LLM提供商工厂。
- 在三十秒内调试"为什么这个Pydantic验证失败"。这以前需要我眯着眼睛看错误消息看十分钟。
- 生成那种机械的、仔细的、有点无聊的代码——我可以写但不喜欢写的代码。
- 用不同场景测试智能体。这是我最喜欢Claude Code能做的事情之一。

**它不擅长的，以及我必须自己做的：**

- **选择架构。** 当我问它"这应该是一个ReAct智能体还是一个固定图"时，它给了我两个选项和平衡的总结。它不了解我的问题。它不知道工作流是一个已知的DAG。它不知道算术正确性是一个硬性要求。我必须做出这个决定。
- **知道LLM是错误的工具。** 上面的截止日期守卫修复。Claude Code第一次时愉快地写了我包含截止日期检查的LLM提示词，因为那是我要求的。它不会看着系统说"等等，你正在用一个概率系统做确定性检查，这就是bug。"
- **范围纪律。** 放任自流的话，它会在每一行周围添加try/except包装器，生成三层抽象"为了未来的灵活性"，并产出一个看起来像Java企业应用的项目结构。它产出的每个文件都需要一轮"删除这个、这个和这个"。
- **把整个系统装在脑子里。** 它在一个文件内非常出色。跨八个文件，它就失去主线了。我必须自己知道 `nodes.py` 中状态形状的变更意味着 `procurement_agent_deterministic.py` 中的相应变更，即使我没有同时粘贴两个文件。

到那天结束时对我有效的思维模型是：**Claude Code是一个快速的、不知疲倦的初级工程师，具有很好的局部品味，但对整个系统没有判断力。** 对于你面前的四行代码问题，它是你合作过的最好的初级工程师。但它不会拯救你在决定项目是老虎机还是计算器的架构决策上。

如果你想在七小时内交付一个严肃的东西，这正是你想要的交易。你负责思考，它负责打字。

## 5、一个收获

如果你在2026年构建智能体工作流，一个深思熟虑的设计和默认的ReAct循环之间的成本差异大约是一个数量级。便宜的版本也是更可靠的版本。这不是你在软件中通常得到的权衡，值得放慢速度来抓住它。

---

原文链接: [My ReAct agent was a slot machine. The deterministic version cost 90% less](https://medium.com/gitconnected/my-react-agent-was-a-slot-machine-the-deterministic-version-cost-90-less-9cbbb235f45a?ref=hubwiz.com)汇智网翻译整理，转载请标明出处

> **这是「Agent / MCP」专题的一部分。**完整地图见：[Agent / MCP / 自动化完全指南](https://www.hubwiz.com/blog/agent-stack/)