0
异步视界/agentic-coding-classics/armin-agents-are-hard-fulltext
· AGENTIC-CODING-CLASSICS · 2026.05.06 · 13 MIN ·

Armin Ronacher《Agent Design Is Still Hard》逐段全译

lucumr.pocoo.org 2025-11-21 发布。CC BY-NC 4.0 授权。逐段中文全译,所有 🟢 是译注。配套精读见 09。 · by fancyoung
AI · HERO seed:5420260506 lucumr.pocoo.org 2025-11-21 发布。CC BY-NC 4.0 授权。逐段中文全译,所有 🟢 是译注。配套精读见 09。
FIG.00 — cover · ai-generated · placeholder

原文:Agent Design Is Still Hard 作者:Armin Ronacher(Flask、Jinja2、Click 作者;Earendil 联合创始人) 原发布:2025-11-21 本译版定位:完整逐段翻译(配少量译注)。对应精读版 05-armin-ronacher-agents-are-hard.md。如要快速了解请读精读版,本文给希望读完整原文中文版的读者。

译者前言

这是一篇坦率到罕见的”半年实战总结”。Armin 在 Earendil 做 agent 平台,这篇是他写给行业的第二份大型笔记(第一份是 2025 年 6 月那篇《Agentic Coding Recommendations》)。值得反复读的核心不在结论,而在他公开承认自己半年前观点错了这一点上

下面是逐段翻译。所有 🟢 译注: 是我加的,其余是原文。


Agent 设计依然困难

Agent Design Is Still Hard - social card

写于 2025 年 11 月 21 日

我觉得现在是个好时机,写写我最近学到的一些新东西。这篇内容大部分会关于造 agent,稍带一点关于用 agentic 编程工具的事。

TL;DR:造 agent 还是一团乱。SDK 抽象一碰到真实工具调用就会断裂。缓存如果你自己管会更好用,但跨模型差别很大。强化(reinforcement)最后干的活比预期多得多,失败需要严格隔离才能不让循环跑偏。通过类似文件系统的层来共享状态,是一个重要的构件。输出工具出乎意料地难处理,模型选择仍然取决于具体任务。


选哪个 Agent SDK?

当你自建 agent 时,你可以选择以一个底层 SDK 为目标 —— 比如 OpenAI SDK 或 Anthropic SDK —— 也可以选高层抽象,比如 Vercel AI SDK 或 Pydantic。我们当时的选择是采用 Vercel AI SDK,但只用它的 provider 抽象部分,基本上 自己驱动 agent loop到现在这个时点,我们 不会再做这个选择了。Vercel AI SDK 没什么问题,但当你试着造一个 agent 时,有两件我们最初没料到的事会发生:

第一件是模型之间的差异已经大到你必须自建 agent 抽象。我们没找到任何一个 SDK 给出了一个对的 agent 抽象。我觉得这部分是因为,虽然基础 agent 设计只是一个循环,但根据你提供的工具不同,会有微妙差别。这些差别影响你找到合适抽象的难易度(缓存控制、强化的不同要求、工具 prompt、provider 端工具等)。因为合适的抽象还不清晰,使用专门平台的原生 SDK 让你完全保持控制。如果用某些高层 SDK,你必须建在它们已有的抽象之上 —— 而那可能不是你最终想要的抽象。

我们也发现 Vercel SDK 在处理 provider 端工具时非常痛苦。它试图统一消息格式,但行不通。比如 Anthropic 的 web search 工具,经常会用 Vercel SDK 把消息历史搞坏,我们至今没完全搞清楚原因。另外,在 Anthropic 的情况下,直接对接它的 SDK 比对接 Vercel 那一层做缓存管理容易得多。出错时它的报错信息也清晰得多。

这种情况以后可能改变,但眼下我们造 agent 大概率不会用任何抽象层,至少在事情稳定下来之前。收益还不超过代价,对我们而言。

可能别人已经搞清楚了。如果你读到这里觉得我说错了,请发邮件给我。我想学

🟢 译注:这一节的核心信息和 Dex Horthy 12-Factor “Own your control flow” 完全同向。两个独立观察者得出同一结论 —— 这一刻框架抽象暂时是负价值的。


缓存课

不同平台对缓存的处理差异很大。这件事已经被说了很多,但 Anthropic 让你为缓存付费。它让你显式管理缓存断点,这从 agent 工程层面彻底改变了你与之交互的方式。我一开始觉得手动管理挺蠢。为什么平台不自己做这件事?但我已经彻底转变立场,现在远远更偏好显式缓存管理。它让成本和缓存利用率可预测得多。

显式缓存让你能做一些其它方式很难做的事。比如,你可以把一段对话分叉,让它在两个不同方向上同时跑。你也有机会做 context editing(上下文编辑)。最优策略目前还不清楚,但你显然能拿到更多控制权,而我真的喜欢这种控制感。它也让你理解底层 agent 成本变得容易得多。你能对缓存利用率做更多假设,而我们发现别的平台的缓存命中率非常飘忽。

我们在 Anthropic 这边的 agent 缓存做法相当直接:一个缓存断点放在 system prompt 之后两个缓存断点放在对话开头,最后一个跟着对话尾部往前移。然后路上还有些可以做的优化。

因为 system prompt 和 tool 选择现在必须基本静态,我们把动态消息(比如当前时间)放到后面注入。否则那会击穿缓存。我们也在循环中更多地利用强化(reinforcement)

🟢 译注:Armin 这种”我半年前错了”的公开记录,是这篇的灵魂。这篇的真正价值不是”现在的最佳实践”,而是”已被时间筛过、半年内没变的最佳实践”


循环中的强化(Reinforcement In The Agent Loop)

每次 agent 跑工具时,你都有机会不仅仅返回工具产生的数据,还能把更多信息喂回循环。比如:你可以提醒 agent 整体目标和各个任务的状态。你可以在工具失败时给出该工具调用如何成功的提示。强化的另一个用途是告诉系统后台发生了状态变化。如果你的 agent 用了并行处理,你可以在每次工具调用之后注入信息 —— 当那个状态发生变化、且这变化对完成任务相关时。

有时候让 agent 自己强化自己就够了。比如在 Claude Code 里,todo 写入工具就是一个自我强化工具。它做的事就是从 agent 那里拿到一份它认为自己应该做的任务清单,然后把传入的内容回显出来。它本质就是个 echo 工具,真的没做别的事。但仅此就足以比”在 context 开头给出任务和子任务,然后任由后续大量事情发生”的方式更好地推动 agent 前进

我们也用强化告诉系统:执行过程中环境发生了对 agent 有问题的变化。比如,如果我们的 agent 失败,从某一步开始重试,但恢复操作基于的是已损坏的数据,我们会注入一条消息,告知它可能想退回几步、重做更早的某一步

🟢 译注:这一节是这篇文章对实战派最大的礼物。如果你的 agent 跑着跑着开始忘记目标、原地打转、产生幻觉 —— 90% 的概率你需要的是循环中的强化机制,而不是更长的系统提示。


隔离失败

如果你预计代码执行中会有大量失败,你有机会把这些失败从 context 中藏起来。这有两种做法。一种是把可能需要迭代的任务单独跑。你会在子 agent 里跑它直到成功,只把成功本身报告回主流,可能再加一段简短的”哪些办法没成”的总结。让 agent 知道某个子任务里什么没成,是有用的 —— 它可以把这信息喂给下一个任务,以期避开同样的坑。

第二个选项不是所有 agent 或基础模型都有,但 Anthropic 这边你可以做 context editing。到目前为止,我们用 context editing 没什么大成功,但我们觉得它是个有意思的事,我们很想多探索。我们也想知道有没有人成功使用了它。context editing 有趣的地方是,它应该能为后续迭代循环节省 token。你可以把那些没推动循环成功完成、只在执行中负面影响过某些尝试的失败,从 context 里拿出来。但跟我刚才说的同理:让 agent 理解什么没成有用,但也许不需要所有失败的完整状态和完整输出

不幸的是,context editing 会自动让缓存失效。绕不开。所以做这件事的权衡什么时候能补偿”击穿缓存”的额外成本,是不清楚的


子 Agent / 子推理(Sub Agents / Sub Inference)

我在博客上提过几次,我们的大部分 agent 是基于代码执行和代码生成的。这真的需要一个 agent 共同存数据的地方。我们的选择是文件系统 —— 我们这里是虚拟文件系统 —— 但这要求不同工具能访问它。这件事在你有子 agent 或子推理时尤其重要

你应该尽量造一个没有死胡同的 agent所谓死胡同,就是一个任务只能在你建的子工具里继续往下执行。比如,你可能造了一个生成图像的工具,但它只能把那张图喂给另一个工具。这是个问题 —— 因为你后来可能想用代码执行工具把那些图打包成 zip。所以必须有一个系统,让图像生成工具把图写到代码执行工具能读到的同一个地方。本质上,这就是一个文件系统

显然反过来也得行。你可能想用代码执行工具解压一个 zip,然后回到推理去描述所有图像,以便下一步回到代码执行,如此循环。文件系统是我们用的机制。但这要求工具被造成”能接收虚拟文件系统的文件路径”的样子。

简单说,一个 ExecuteCode 工具应该能访问与 RunInference 工具相同的文件系统,后者能接收一个 path 参数指向那个虚拟文件系统上的文件。

🟢 译注:这是给造 agent 平台的人最重要的一条架构指南。工具孤岛是 agent 的头号失败模式,共享文件系统是简单且强力的解药。


输出工具的使用

我们 agent 结构里一个有趣的事是:它不代表一个 chat session。它最终会跟用户或外部世界沟通,但它中间发的所有消息通常不被暴露出来。问题是:它怎么造出最终那条消息?我们有一个 “输出工具”。agent 显式调用它来跟人沟通。我们用 prompt 指导它什么时候用这个工具。我们这里,输出工具就是发邮件。

但这又带来几个挑战。一是出乎意料地难调输出工具的措辞和语气,相比之下,直接用主 agent loop 的文本输出作为对用户讲话的机制反而容易。我说不清原因,但我猜可能跟这些模型怎么训出来的有关。

一个失败的尝试:让输出工具内部跑另一个快 LLM(比如 Gemini 2.5 Flash)来调整语气。但这增加延迟,实际反而降低输出质量。一部分原因我觉得是模型措辞不对,而子工具又没足够上下文。把主 agent 上下文的更多片段灌给子工具,会让它变贵,还没完全解决问题。它有时还会把我们不想露在最终输出里的信息(比如得到结果的步骤)暴露出来

输出工具的另一个问题是它有时就是不调那个工具。我们逼它调的方法之一是记下输出工具是否被调过。如果循环结束了输出工具没被调过,我们就注入一条强化消息鼓励它调用


模型选择

整体上我们的模型选择没大变。我觉得 Haiku 和 Sonnet 还是目前最好的工具调用者,所以它们仍是 agent loop 里的极佳选择。它们对 RL 是怎么样的也有一定透明度。另一个明显的选择是 Gemini 系列。我们到目前还没在主循环里用 GPT 系列模型成功

对于个别子工具(它们也可能要做推理),我们目前的选择是 Gemini 2.5,如果你需要总结大文档或处理 PDF 之类。它从图片提取信息也挺好,特别是因为 Sonnet 系列模型容易撞到安全过滤,这有时挺烦

还有一个相当明显的认知:单纯的 token 成本不能定义一个 agent 有多贵更好的工具调用者用更少的 token 把活干完。今天有些模型比 Sonnet 便宜,但它们在循环里不一定真的更便宜

但综合来看,过去几周没变多少。


测试与评估(Evals)

测试和 eval 是我们这里最难的问题。这并不奇怪,但 agent 的本质让它更难。和 prompt 不一样,你不能在某个外部系统里就把 eval 做了 —— 你要喂进去的东西太多。这意味着你想基于可观测性数据,或者给真实测试运行加 instrument 来做 eval。我们目前试过的方案,没有一个让我们觉得它找到了对的路子。很遗憾,我得报告:目前我们没找到让我们满意的东西。我希望我们能找到方案 —— 因为这正在变成造 agent 越来越令我们沮丧的方面。

🟢 译注:这一节是埋在文章里的定时炸弹。当前所有”agent eval 平台”创业公司都在攻这个山头。谁能解决,谁就能拿到下一轮独角兽估值。但截至 Armin 写作时,没人解决了。


编程 Agent 的近况

至于我用编程 agent 的体验,真没什么大变化。主要的新发展是我在多试一下 Amp。如果你好奇为什么:不是说它在客观上比我现在用的更好,而是我相当喜欢他们对 agent 的思考方式(从他们发的内容看)。不同子 agent(比如 Oracle)与主循环的交互做得很漂亮,今天没几个 harness 能这样做。这也是个不错的方式让我验证不同的 agent 设计如何工作。Amp,跟 Claude Code 一样,真的感觉是被那些自己也用这个工具的人造出来的。我感觉行业里很多其他 agent 没做到这点。

🟢 译注:“做自己产品用户”是 Armin 反复强调的工程美德。


我读到觉得值得分享的零碎东西

只是一些我觉得也可能值得分享的随机东西:


This entry was tagged ai

© Copyright 2026 by Armin Ronacher. Content licensed under CC BY-NC 4.0.


译者总评

如果你之前没读过精读版,这里再贴一遍Armin 这篇对前面四位的隐含回应:

  • 对 Karpathy:你的 autonomy slider 思路对,但实现 slider 的 SDK 都还没就绪
  • 对 Anthropic:5 种 workflow pattern 抽象对,但具体到 caching / sub-agent 共享状态 / 强化机制,Anthropic 自己的 cookbook 也没标准答案
  • 对 12-Factor:你的 “own your control flow” / “own your context window” 是对的,而且你提到的痛苦,我在每一行代码里都遇到了
  • 对 Ralph:你的暴力循环有效,但只在你能容忍 “$297 替代 $50k 但 90% 完成度” 这个交易时有效;对真生产你需要的不止这些

一句最深的底色:2025 年的”最佳实践”,2026 年还会再变一次。这个领域要再 2-3 年才会收敛到下一个稳定点。在那之前,你要做的不是押注,是做能让你跟上的事。

🔗 调研来源(可校验)

05-armin-ronacher-agents-are-hard.md 末尾的”调研来源(可校验)“段落,完整 URL 列表共享。本文与 05 互为对照。