Hello 大家好呀,我是计算机魔术师。凌晨两点的服务器告警,通常比前任的深夜短信更让人心惊肉跳。
当你的 Agent 开始以每秒 5 次的频率,试图用一个不存在的 SQL 字段“骚扰”生产数据库,并在 10 分钟内烧掉你半个月的 Token 额度时,你才会意识到:没有反思能力的 AI,只是一个带了信用卡的复读机。

Token 烧没了,心也在滴血
这种“骚扰式”报错并非偶然。在 Agent 从 Demo 走向生产的路上,逻辑死循环(Looping)是所有工程师绕不开的噩梦。
这件事之所以和你有关,是因为随着 LLM 成本的下降,我们正以前所未有的速度把决策权交给 Agent,但如果你的系统里没有一套“自省”机制,你其实是在裸奔。
今天我们不聊虚的,直接拆解如何用 Reflection Pattern(反思模式)给你的 Agent 装上大脑。
为什么 Agent 会在同一个坑里摔倒 500 次?
想象一下,你雇了一个极其勤奋但脑子不太转弯的实习生。你让他去查一下去年的销售数据,他写了一个 SQL,报错了:Unknown column 'sales_2023'。
正常的实习生会想:“哦,字段名可能错了,我去查查表结构。”但一个没有反思模式的 Agent 会想:“报错了?那我再试一次。
”于是,它又发了一遍同样的 SQL。再报错,再发。直到你的 API Key 欠费或者数据库连接池爆炸。

这一问下去,缓存和队列都得一起背
概率的囚徒:LLM 的路径依赖
大语言模型本质上是一个极其复杂的“概率预测器”。当它生成一段代码或一个决策时,它是在预测下一个 Token 是什么。如果它陷入了一个高概率的错误路径,它很难靠自己跳出来。
这就像是一个在迷宫里只盯着脚下的人,他能看到下一步怎么走,但看不到自己已经在同一个转角绕了三圈。
这种“路径依赖”在处理结构化任务(如 SQL 生成、API 调用)时尤为致命,因为这些任务的容错率几乎为零。
逻辑陷阱:当“下一步”变成了“上一步”
在复杂的 Agent 链条中,逻辑死循环往往源于上下文的污染。
Agent 会把之前的错误尝试也塞进 Prompt 里,如果 Prompt 引导不够清晰,它会认为之前的错误是某种“既定事实”,从而在错误的基础上继续堆砌错误。
这时候,Agent 表现得就像是一个在泥潭里挣扎的人,越用力,陷得越深。它不是在解决问题,而是在通过不断的尝试来缓解“必须输出点什么”的焦虑。

逻辑死循环:我在哪?我要干嘛?
Reflection Pattern:从“概率复读”到“逻辑自愈”
要打破这种死循环,我们需要引入一个更高维度的观察者。这就是 Reflection Pattern 的核心:不要让执行者自己去检查作业,而是引入一个专门的“批改老师”。
架构设计:Actor 与 Critic 的博弈
Reflection Pattern 通常由两个核心角色组成:Actor(执行者)和 Critic(评论者)。
Actor:负责根据用户需求生成初始输出。它的目标是“快”,尽量给出它认为最可能的答案。
Critic:负责审视 Actor 的输出。它不直接干活,但它手里拿着“红笔”,专门找茬。它会检查逻辑漏洞、语法错误、甚至是不符合预期的格式。
这种设计精妙的地方在于,它把一个极其困难的“一次性生成正确答案”的任务,拆解成了多个简单的“生成-反馈-修正”的小步快跑。

这一把,终于像样了
Critic 的反馈会作为新的上下文喂给 Actor,告诉它:“你刚才那招不行,因为字段名错了,建议你先执行 DESCRIBE TABLE。”
状态机思维:把 Prompt 变成可控的流转
在工程实现上,我们不能简单地写一个 while 循环。那不叫 Reflection,那叫“自杀式请求”。我们需要用状态机的思维来管理这个过程。
每一个状态(State)都清晰地定义了:当前在做什么?下一步可能去哪?什么时候必须停下来?
通过将 Agent 的行为抽象为状态机的节点,我们可以精确地控制反思的深度。比如,我们可以设定“最大反思次数为 3”,或者“如果连续两次反馈相同,则判定为逻辑死锁,直接报错”。
这就像是给 Agent 装上了一个“逻辑保险丝”,防止它在错误的道路上狂奔到天黑。
实战:构建一个能“自省”的 SQL 助手
让我们来看一个具体的例子。假设我们要构建一个能够根据自然语言查询数据库的 Agent。如果它生成的 SQL 报错了,它应该如何自愈?
核心代码实现:Actor 节点与 Critic 节点
在 Python 中,我们可以利用类似 LangGraph 的框架来组织这种逻辑。核心在于定义好节点的输入输出流转。
# 伪代码示例:定义反思逻辑的核心流转
def actor_node(state):
"""执行者:生成 SQL 查询"""
prompt = f"根据需求 {state['task']} 生成 SQL。之前的反馈:{state['feedback']}"
sql = llm.invoke(prompt)
return {"sql": sql}
def critic_node(state):
"""评论者:检查 SQL 合法性"""
try:
db.execute(state['sql'])
return {"status": "success"}
except Exception as e:
# 关键点:将报错细节作为反馈返回
feedback = f"SQL 执行失败,错误信息:{str(e)}。请检查字段名或表关联。"
return {"status": "retry", "feedback": feedback}
# 状态流转逻辑
if state['status'] == "retry" and state['retry_count'] < 3:
goto actor_node
else:
goto end_node
这段代码虽然简单,但它建立了一个基本的“闭环”。Actor 不再是盲目猜测,而是根据 Critic 提供的具体报错信息进行有针对性的修正。
如果数据库说“没有这个字段”,Actor 下一次生成的 Prompt 里就会包含“不要使用该字段”的强约束。

代码写得好,下班回得早
⚠️ 踩坑提醒:小心“套娃式”反思
这里有一个非常隐蔽的坑:如果你的 Critic 节点也是由 LLM 驱动的,那么 Critic 可能会产生“幻觉”。
它可能会给出一个完全错误的修正建议,导致 Actor 在错误的道路上越走越远。这种“套娃式”的错误会让你的 Token 消耗呈指数级增长。
解决办法是:尽量让 Critic 节点使用确定性的工具(如编译器、数据库执行器、正则检查器),只有在逻辑层面的评估才求助于 LLM。
生产级防线:除了反思,你还需要熔断器
在真实的生产环境中,光有 Reflection 是不够的。你还需要一套强硬的工程手段来兜底,防止 Agent 变成一个“高智商骚扰者”。
语义去重:识别那些“换汤不换药”的复读
有时候,Agent 虽然在反思,但它生成的修正方案其实和上一次大同小异。比如只是换了一个空格,或者改了一个无关紧要的别名。我们需要在工程层面对 Actor 的输出进行“语义去重”。
如果连续两次输出的语义相似度超过 95%,且依然报错,那么反思就失去了意义。这时候,系统应该果断介入,停止循环。
动态预算:给 Agent 的“骚扰”设个上限
每一个 Agent 任务都应该有一个“Token 预算”和“时间预算”。这就像是给实习生发一张限额信用卡。
如果这个任务在 5 分钟内还没跑通,或者已经消耗了超过 10 万个 Token,系统必须强制熔断。
不要指望 Agent 能在第 101 次尝试中突然开悟,通常情况下,如果前 3 次反思没解决问题,后面的尝试大概率也是在浪费钱。

这锅我不背,熔断器已经生效了
写在最后
Reflection Pattern 并不是万能药,它本质上是用“算力换逻辑”。在追求 Agent 智能的道路上,我们往往容易迷恋更强大的模型,却忽略了最基础的工程约束。
一个好的 Agent,不仅要学会如何完成任务,更要学会何时承认自己“搞不定”。
在我的经验里,最优雅的 Agent 设计往往不是最复杂的,而是最“克制”的。它知道在什么时候该反思,更知道在什么时候该停下来向人类求助。
毕竟,AI 的终点不应该是替代人类,而是让人类能睡个好觉,而不是在凌晨两点被一个复读机吵醒。
你遇到过最离谱的 Agent 死循环是什么样的?是为了一个标点符号改了半小时,还是在死循环里把你的 API 余额直接抹零?欢迎在评论区分享你的“烧钱”经历,让我们一起在坑里长点记性。

锅还没落地,人已经被点名了
参考文献
LangChain Blog: Reflection and Self-Correction in Agents
Paper: Self-Reflection: Language Agents with Internal Feedback
OpenClaw Documentation: Core Interaction Patterns
如果你想继续追更,欢迎在公众号 计算机魔术师 找到我。后续的新稿、精选合集和阶段性复盘,会优先在那里做串联。