
我写过不少 prompt。去年有一段时间我特别沉迷prompt 工程这件事觉得只要 system prompt 写得够好、few-shot 给得够全模型就能稳定输出我想要的东西。有一回我给一个复杂任务写了将近 200 行的 system prompt——包含完整的工作流描述、输出格式模板、边界条件处理、甚至还有一套自纠错指令。结果换了个模型版本直接崩了。不是效果变差是崩——输出格式不对、拒绝执行中间步骤、在同一个循环里反复打转。200 行 prompt一个版本更新就全废了。这事让我开始想一个问题我们把太多逻辑塞进 prompt 里是不是搞错了方向后来我接触到了 Agent Loop 的模式花了大概一个月把一套生产级工具从单次 prompt 调用改成了循环式 Agent。这篇文章不打算讲理论就想说说我踩的坑和真实的感受。从 200 行 prompt 到 0 行传统 prompt 工程的思路是你替模型想好每一步——告诉他你先做 A然后根据 A 的结果做 B如果 B 出错就做 C……。这个思路在简单任务上没问题但任务复杂度一上来你就会发现你根本不可能穷举所有路径。用户的输入千奇百怪工具返回的数据格式五花八门一次 API 超时可能让整条链子断了。Agent Loop 的思路刚好反过来不给模型指路给它工具和终点让它自己绕。每次模型调用就是一步决策——看当前状态、选一个工具调用、看返回结果、再决策。这个转变刚开始挺不适应的。毕竟你习惯了对模型的每一步输出都掌控现在要放手让它自己走第一反应是不放心。但跑了几个测试用例之后我发现效果居然比硬编码的 prompt 流程好很多尤其是在处理意外情况的时候。我写的最简 Agent Loop贴一个我最早跑通的版本去掉业务细节只保留核心结构import json import openai class AgentLoop: def __init__(self, tools, system_prompt, max_iters10): self.tools tools # [{name, description, parameters, fn}] self.system_prompt system_prompt self.max_iters max_iters self.messages [{role: system, content: system_prompt}] def run(self, user_input): self.messages.append({role: user, content: user_input}) for i in range(self.max_iters): # 这里踩了个坑最开始没加 max_iters结果有一次模型反复调用同一个工具 # 每次返回一样的错误但它就是不终止——token 白白烧了 15 分钟 resp openai.chat.completions.create( modelgpt-4, messagesself.messages, tools[{ type: function, function: { name: t[name], description: t[description], parameters: t[parameters] } } for t in self.tools] ) msg resp.choices[0].message self.messages.append(msg) if not msg.tool_calls: # 模型决定不调用工具 → 返回最终答案 return msg.content for tc in msg.tool_calls: tool next(t for t in self.tools if t[name] tc.function.name) args json.loads(tc.function.arguments) # 另一个坑工具返回值一定要统一格式不然模型解析会出幻觉 result tool[fn](**args) self.messages.append({ role: tool, tool_call_id: tc.id, content: json.dumps({status: ok, data: result}, ensure_asciiFalse) }) return {error: max_iterations_reached, messages: self.messages}这段代码大概 40 行但已经是一个完整的 Agent Loop 骨架了。核心思路就是给模型工具 → 让模型决定用哪个 → 把结果喂回去 → 直到模型自己说好了。我一开始用的是递归实现觉得优雅。结果线上跑了一个星期发现调用栈炸了——递归深度超过 Python 默认限制。后来改成迭代就稳了。这个教训告诉我Agent Loop 的迭代控制不在算法层面而在生产层面。生产环境踩的 5 个坑把 Agent Loop 从 demo 搬到生产我踩了不少坑。下面这 5 个是最痛的1. 无限循环不是段子是真的会发生我第一次把 Agent Loop 部署上线跑了大概三个小时就出事——同一个工具被反复调用了 40 多次每次返回相同的错误。模型就是不停重试同一个已失败的步骤。我赶紧加了 max_iters但后来发现单纯的次数限制不够有些场景需要上下文感知的终止条件比如连续 3 次相同错误就熔断。现在我的每个 Agent 都有两种终止条件硬性次数上限 智能熔断策略。2. 工具返回值格式必须统一不然模型会疯这点我吃过大亏。最开始不同的工具返回的格式不一样——有的返回纯文本有的返回 JSON有的什么都不返回。结果模型在循环中越来越困惑开始出现幻觉它会把一个工具的文本输出当成另一个工具的结果来解析产生完全不相关的后续步骤。后来我强制所有工具返回统一的 JSON 格式{status, data, error}这个问题就没了。其实说白了就是你给模型的数据必须可预测。3. Token 消耗比想象的大得多一个 Agent Loop 每次迭代都在把历史对话塞进上下文。跑了 5 轮之后上下文里就已经有了 5 次 tool call 的完整输入输出。我测了一个典型场景单次 prompt 调用消耗约 2000 tokens改成 Agent Loop 后一次完整任务平均消耗 8000–12000 tokens——3 到 6 倍。这还是顺利的情况如果模型反复调用同一个工具token 能到 5 万。我的应对策略是上下文压缩定期把历史中的 tool call 详情摘要成一句话只保留关键信息。4. 调试比普通 prompt 难一个数量级普通 prompt 的调试你已经熟悉了prompt 进去了输出出来了不对就改 prompt。Agent Loop 的调试是prompt → 模型决定调工具 A → 工具 A 返回 → 模型决定调工具 B → 工具 B 失败 → 模型决定重试工具 B → … 每一步都可能出错。我最初的做法是在每步打印日志但一个任务跑下来日志上万行根本看不出问题在哪。后来我给每个 Agent 加了一个思维链可视化——把每一步的模型的想法即它的推理过程和工具调用结果并列展示调试效率一下子就上来了。5. 成本失控比你想象的快上面说的 token 消耗问题直接反映在账单上。我第一次跑了一周 Agent Loop 测试看了一眼 API 消耗——是我的做法大概是我平时 prompt 做法的 10 倍。不是 10% 是 10 倍。后来我加了预算控制每个 Agent 实例初始化时设定一个 max_cost每次迭代前估算当前消耗达到阈值就直接终止并返回 partial result。class BudgetAgent(AgentLoop): def __init__(self, tools, prompt, max_cost0.01): super().__init__(tools, prompt) self.max_cost max_cost self.spent 0 def estimate_cost(self, messages): # 粗略估算input $3/M tokens, output $15/M tokens total_chars sum(len(m[content] or ) for m in messages) return total_chars * 1.5 / 1_000_000 * 3 def step(self): cost self.estimate_cost(self.messages) if self.spent cost self.max_cost: return {error: budget_exceeded, spent: self.spent} self.spent cost return super().step()这比跑完了告诉你对不起超预算了要体面得多。还得老实说prompt 工程没那么容易死说了这么多 Agent Loop 的好处我也得老实承认prompt 工程没死也不需要死。有些场景用 prompt 就是比 Loop 合适——简单的一问一答、格式转换、分类任务——你用 loop 反而是杀鸡用牛刀。Agent Loop 引入的复杂度调试困难、token 消耗大、行为不确定性在简单场景下完全划不来。我现在的工作方式是简单任务用 prompt复杂任务用 loop。而且即使是 loop 的 Agent我也会在 system prompt 里写清楚它的行为边界——这不是prompt 工程的回归吗某种意义上是的。只不过这个 prompt 不是告诉模型每一步怎么走而是告诉它你有这些工具、这些约束、这个目标你自己想办法。所以与其说prompt 已死不如说是 prompt 的职责变了——从详细指令集变成了约束条件和目标定义。我觉得这才是 2026 年写 AI 应用的正确打开方式。