2026-07-07 06:00:00

本文永久链接 – https://tonybai.com/2026/07/07/how-to-create-loops-with-claude-code
大家好,我是Tony Bai。
在上两篇中,我们先后探讨了 Andrej Karpathy 关于长程运行的 Agent 的核心理念《LOOPS.md》,以及 20 个极具启发性的“LOOP 设计模式”。
这些前沿理论无一不在昭示着一个事实:AI 开发的下半场,是属于“循环(Loops)”的。
然而,理论再性感,无法落地也只是空中楼阁。基于 Anthropic 推出命令行工具 Claude Code,以及各大厂商纷纷跟进 Agentic 开发者工具,我们有了可以直接上手构建“Loop”的利器。

今天翻译的这篇实战指南,同样来自技术专家 Rahul (@sairahul1)。他将手把手教你如何利用极简的“4文件系统”和权限控制阶梯,在 Claude Code(或任何主流 LLM 开发环境)中无缝搭建一套“睡后运行”的自动化循环系统。
无论你是想做每日代码库自动审计、CI/CD 错误自动排查,还是想让 AI 自动跟踪你的项目进度,这套保姆级的实战模板都能让你直接复制粘贴、即刻上线。
以下为译文全文:

目前,绝大多数人使用 Claude 的工作流,依然是“一锤子买卖(One-shot)”。
你写一段 Prompt -> Claude 给出回复 -> 会话结束。
明天,一切又得从零开始。
对于一次性的简单任务,这种方式完全没问题。
但对于高频重复的工作——比如每日项目审查、CI/CD 故障排查、项目进度监控——这就称不上是一个“工作流”了。这充其量只是在传统的体力劳动上,撒了一点 AI 调味料。
更好的抽象设计,应该是构建一个“循环(Loop)”。
建议收藏本文,你绝对用得上。
循环绝对不等于“重复地去问 Claude 相同的问题”。
循环,是围绕大模型构建的一套可复用的工作流结构。
它明确定义了:
模型依然负责底层的推理(Reasoning),但循环为它提供了运转的框架结构(Operating Structure)。

这种转变至关重要。因为 Claude 的 Session(会话)是临时性的。没有循环,每一次运行都是从零开始;而有了循环,Claude 能够读取上一次发生的事,并在上一次停下的地方完美接棒。
一个缺乏结构的循环,不过是一个被运行了两次的普通 Prompt 而已。
要让一个循环真正跑起来,必须具备以下 6 个核心拼图:
PROGRESS.md、记录当前的 Blockers(阻碍项)、为下一次运行留下指南。在整个闭环中,每一步都生死攸关:

你根本不需要配置复杂的数据库和后台系统,只需要在你的项目目录下新建 4 个 Markdown 文件:
my-loop/
├── TASK.md
├── LOOP_INSTRUCTIONS.md
├── PROGRESS.md
└── outputs/
└── daily-review.md
就是这么简单。
Claude 在每次运行开始前,会读取这些文件;在结束运行前,会更新这些文件。下一次运行启动时,它便能完美实现断点续传。状态存在于聊天窗口之外,这就是循环与普通 Prompt 调优的本质区别。

这个文件告诉 Claude 这个循环是干嘛的。
保持高层次(High-level)的宏观描述,不要把具体的操作细节写在这里,那是 LOOP_INSTRUCTIONS.md 的工作。
你可以直接复制这个模板:
# 每日项目审查循环
## 目标(Goal)
审查当前项目文件夹,总结变更内容,识别当前的阻碍项(Blockers),并输出一份简短的每日审查报告。
## 预期输出(Expected Output)
每次运行应当产出或更新:
- `outputs/daily-review.md`
- `PROGRESS.md`
## 权限与范围(Scope)
Claude 允许检查当前工作区的文件并写入:
- 禁止删除或重命名文件
- 禁止直接发送消息或开 Ticket
请特别注意最后一节。任何循环的第一版,都必须有明确的“禁止行为(Do Not)”规则。这不是因为不信任 Claude,而是合格工程师的职业素养。
这是整个系统中最灵魂的文件。
没有它,每一次运行都是盲目的。有了它,Claude 才能拥有跨越会话的记忆。
# Loop 运行进度表
## 当前状态(Current State)
- 状态:运行中(Active)
- 核心任务:每日项目审查
- 当前聚焦:[写下目前最重要的工作]
- 上次更新时间:[日期]
## 上次运行记录(Last Run)
- 日期:
- 摘要:
- 审查过的文件:
- 产出的结果:
## 未解决事项(Open Items)
-
## 阻碍项(Blockers)
-
## 需人工介入(Needs Human Review)
-
## 下次运行聚焦(Next Run Should)
-
## 已做出的决策(Decisions Made)
-
两条保持 PROGRESS.md 高效运转的铁律:
PROGRESS.md 中。outputs/history/ 文件夹中。永远膨胀的状态文件等同于无用。请把 PROGRESS.md 当作控制面板(Control Panel)来维护,而不是当作无休止的历史档案馆。

这个文件精细控制着 Claude 的行为:该读什么、该写什么、什么东西绝对不能碰,以及在停止前如何进行质量自我验证。
实测表现最稳定的一套模板:
你当前正在执行【每日项目审查循环】。
## 启动准备(Before You Start)
1. 读取 `TASK.md`
2. 读取 `PROGRESS.md`
3. 检查项目当前的主文件夹
4. 识别出发生了哪些变更、哪些部分还没写完、哪些地方需要人工介入
## 标准操作流程(What You Should Do)
撰写一份简短的每日审查报告,并写入 `outputs/daily-review.md`,内容包含:
- 当前整体状态总结
- 审查过的文件列表
- 产生的实质性变更
- 当前遇到的阻碍项或未解决的疑问
- 建议的下一步动作
写完报告后,立即更新 `PROGRESS.md` 中的以下项:
- 本次运行日期
- 本次工作摘要
- 检查过的文件
- 下次运行的建议焦点
- 是否需要人工介入审查
## 安全与红线规则(Safety Rules)
- 严禁删除任何文件
- 严禁重命名或移动任何文件
- 严禁擅自修改任何源代码文件
在结束本次运行前,必须确认以下事项:
- `outputs/daily-review.md` 已生成,且包含所有必备章节
- `PROGRESS.md` 已被成功更新
- 没有任何安全红线以外的文件被擅自修改过
## 异常失败策略(Failure Policy)
如果自我验证(Verification)失败:
1. 报告中缺失部分章节 -> 尝试修复一次。
2. PROGRESS.md 未更新 -> 尝试更新一次。
3. 发现有红线外的源码文件被修改 -> 立即无条件终止运行(Stop Immediately)。
4. 相同的校验连续失败两次 -> 停止运行,将状态标记为“需要人工审查”。
注意底部的异常失败策略(Failure Policy)。大多数人设计的 Agent 没有任何失败退路,一旦出错,AI 只能当场胡乱发挥。优秀的循环,必须明确定义好失败后的退出机制。
在把循环放进定时任务之前,先手动跑通。
在工作区目录打开 Claude Code(或 Claude 桌面客户端),输入以下手动提示词:
请运行当前工作区的每日项目审查循环。
严格遵循 `LOOP_INSTRUCTIONS.md` 的要求。
在采取任何行动前,请先完整阅读:
- `TASK.md`
- `PROGRESS.md`
- `LOOP_INSTRUCTIONS.md`
接着检查当前工作区,将每日审查结果写入 `outputs/daily-review.md`,并在停止运行前更新 `PROGRESS.md`。最后执行自我校验清单,并向我汇报发生了哪些变更。
除了 `outputs/daily-review.md` 和 `PROGRESS.md`,严禁修改任何其他文件。
检查产出的两个文件是否完美。如果格式和内容都正确,说明循环逻辑走通了。
接下来,手动模拟 3-5 次项目变更:
在目录里增加一个临时笔记、在 PROGRESS.md 里手动写一个阻碍项、改动一点代码。然后重新运行上面的指令,观察 Claude 能否完美读取上一次留下的状态,做到“断点续传”。

这是整套 Agent 运行机制中,几乎所有人都在悄悄隐瞒的失败模式:
Claude 跑完了循环,自信满满地向你汇报它已经全部搞定了,接着更新了状态文件,然后利落地停止了运行。
你打开目录,一切看起来似乎都毫无破绽。
然而事实却是:最终生成的报告里,其实漏掉了几个必不可少的硬性章节;或者 PROGRESS.md 只是被敷衍地涂改了两笔,根本没有写入实质性进度;甚至更糟糕的是,Claude 在你看不见的地方,悄悄修改了一个根本不该碰的源码文件。
而这些致命的失误,你往往只有在系统彻底跑崩、或者项目上线报错的那一刻才会痛苦地发现。
这就是为什么验证(Verification)必须作为独立的一环存在。
一个优秀的循环之所以会停下来,绝对不是因为模型“觉得写完了”,而是因为某一个具体的、硬性的约束条件通过了校验。
独立运行一个专门的“验证步骤”:来审查最近一次(latest)循环的执行结果。
对照标准进行体检:严格根据 LOOP_INSTRUCTIONS.md 中写明的“验证清单(Verification Checklist)”来核对输出结果。
生成一份详尽的体检报告,内容必须包含以下 5 个硬性指标:
在执行此验证步骤期间,验证器严禁修改工作区内的任何文件。
干活的与监工的,必须彻底分离。
先让“工人”(Worker)干活。干完之后,再让“监工”(Verifier)入场。两者必须分属不同的会话或不同的提示词逻辑。
因为监工需要一套绝对黑白分明的“通过/不通过”的标准。
“帮我看看这个结果,告诉我它看起来怎么样,行不行?”
“严格对照这 7 条硬性指标进行体检,只有在 7 条指标全部亮绿灯(PASS)的情况下,才允许将这次运行标记为‘接受(Accepted)’。”

当你在手动模式下反复验证,系统表现极度稳定之后,就可以将它托付给自动化了。
在 Claude Code 中,直接使用自带的 /loop 命令行工具:
/loop 24h 请为当前工作区运行每日项目审查循环。
严格遵循 `LOOP_INSTRUCTIONS.md`。
先阅读 `TASK.md` 与 `PROGRESS.md`。
将审查报告写入 `outputs/daily-review.md`,并在退出前更新 `PROGRESS.md`。
执行验证 checklist。
若无 meaningful 变更,保持报告简短。
若需要人工介入,在 `PROGRESS.md` 中进行显式标记。
除了`outputs/daily-review.md` 和 `PROGRESS.md`,严禁修改任何其他文件。
其中可选的时间间隔包括:
/loop 15m:测试节奏(在观看过程中使用)/loop 1h:每小时监控/loop 24h:每日定时运行/loop 7d:每周例行整理敲黑板:
/loop自身不是魔法。它仅仅是一个帮你自动按时发送 Prompt 的定时器。一个循环系统的真正生命力,完全来自于你在 Prompt 之外所设计的 4 文件结构、SOP 指南、强验证机制和状态持久化。
此外,你还需要了解另一个强大的内置命令 /goal:
/loop:代表基于时间触发(因为时间到期了,所以再跑一次,适合做日常监控、日报生成)。/goal:代表基于状态/目标触发(不达目标不停下,比如:直到通过所有测试用例前,不断尝试修复 Bug)。/goal outputs/daily-review.md exists, PROGRESS.md is updated,
verification checklist passes, no forbidden files modified.

很多人在构建 Loop 时,一上来就给 AI 开放最高权限:让它自由地往 Slack 发消息、直接合并代码到 Master 分支、自动关闭工单。
一旦发生幻觉,后果就是一场毁灭性的车祸。
你应该使用“权限控制阶梯”,逐步放权:
第 1 级 - 只读(Read Only):AI 只能读取文件、工单、日志,无法做任何修改。
第 2 级 - 输出草稿(Draft Outputs):AI 只能在指定的 `outputs/` 目录下生成草稿报告,严禁与外界发生任何交互。
第 3 级 - 沙盒修改(Sandbox Edits):AI 被允许在完全隔离的 Docker 容器或 Git 临时分支或Worktree上尝试修改源码。
第 4 级 - 外部动作草稿(Draft external Actions):AI 准备好 PR 描述、草拟好 Slack 消息,但绝不发送,等待人类最后点击确认。
第 5 级 - 人类授权动作(Human-approved actions):只有在人类显式授权后,才能实施修改。
第 6 级 - 授权低风险自动化(Automated Low-risk):在有严格日志、范围限制和回滚(Rollback)方案的前提下,允许 AI 自动执行窄领域的低风险任务。
你的第一个循环系统,必须老老实实呆在第 1 级或第 2 级。
这绝不意味着系统无能,恰恰相反,这是极度成熟的工程化体现。即使是前两级的循环,仍然具有巨大的价值。
Claude 可以检查你的 GitHub 问题,查看持续集成中的失败情况,审核 Pull 请求,并汇总所有相关信息——而无需直接接触那些重要的文件或数据。
首先,请证明该循环在 2 级时能够正常工作。
然后判断它是否值得获得三级评级。

这是大多数人最容易踩坑的地方。
一个反面教材(糟糕的初始循环设计):
“每天持续优化产品战略文档,直到觉得它变得更强大。”
这个设计之所以糟糕,是因为:
这样一个失控的循环,要么什么有用武之地的事情都做不成,要么会在后台把你根本不想改动的文件改得面目全非。
一个优秀的循环:
outputs/strategy-review.md。这个设计之所以优秀,是因为它具备了:
这其间的差距,根本不在于模型的“智商(Intelligence)”,而在于你的“系统设计(Design)”。
请在执行你的第一条 /loop 命令前,务必排查并解决这五个问题:
一旦本地的单机循环运行稳定后,你才可以考虑逐步为它扩展外部连接:GitHub、Slack、Linear、Jira、CI 日志等。
但是,每增加一个新工具,都会相应地扩大系统的破坏半径。
请遵循完全相同的工程原则:先只读,再草拟,只有在经历过反复、多次成功的运行后,才放开写入权限。
你需要在 LOOP_INSTRUCTIONS.md 中,为特定的工具配置极其详尽的规则:
## 工具权限管理策略(Tool Permissions Policy)
### GitHub
允许的行为(Allowed):
- 读取公开/未解决的 Issues
- 读取 Pull Request 的状态
- 读取 CI 的构建状态
- 在 `outputs/github-review.md` 中草拟摘要总结
禁止的行为(NOT allowed):
- 在未经人类审核前,直接在公开讨论区发表评论
### Slack
允许的行为(Allowed):
- 在 `outputs/slack-update.md` 中草拟消息内容
禁止的行为(NOT allowed):
- 直接发送 Slack 消息
- 擅自 @ 提及用户
- 擅自往公共频道发帖
### Linear / Jira
允许的行为(Allowed):
- 读取分配给当前用户的任务工单(Tickets)
- 在指定目录下草拟建议的更新内容
禁止的行为(NOT allowed):
- 擅自修改工单状态
- 擅自关闭工单
- 在未经人类审核确认前,创建新工单
如果遇到任何未在上述策略中列出的工具,立即停止运行,并向人类寻求审核。
这项安全机制,能提供一道坚实的防火墙,保护你的系统不会在后台做出你意料之外的事情。

以下是你可以从零开始直接复制的完整配置:
首先,建立 LOOP_INSTRUCTIONS.md 并在其中声明基础工具策略:
## 工具权限管理策略(Tool Permissions Policy)
### GitHub
允许(Allowed):
- 读取 open 状态的 issues
- 读取 pull request 状态
- 读取 CI 状态
- 关闭 issues
- 在未经批准前,允许在公开区域进行非评论类操作
... (其余工具策略同上文)
随后,根据前文提供的模板,填满 TASK.md、PROGRESS.md 和 LOOP_INSTRUCTIONS.md。
运行以下提示词来手动触发任务:
在采取行动前,请先完整阅读 TASK.md、PROGRESS.md 以及 LOOP_INSTRUCTIONS.md。
将每日审查报告写入 `outputs/daily-review.md`。
在停止运行前,务必更新 `PROGRESS.md`。
执行自我验证核对清单。
除了 `outputs/daily-review.md` 和 `PROGRESS.md`,严禁修改任何其他文件。
运行以下提示词来进行独立验证:
请执行一次验证过磅(Verification Pass)。
严格对照 `LOOP_INSTRUCTIONS.md` 中的“验证清单”进行逐项检查。
向我汇报:哪些检查通过了、哪些失败了、有哪些文件被改动过,以及是否需要人类介入审查。
在此期间,严禁修改任何文件。
在手动模拟微小的项目变更、并反复运行上述流程 3 到 5 次之后。
如果你确认它的表现已经无懈可击,即可配置定时任务:
[使用与上述手动运行完全相同的提示词,但在其最前面加上 /loop 24h 前缀]
在自动运行的第一周,请务必对每一次输出结果进行人工复审,确认无误后再完全信任它。
这就是一套完整的、生产级循环系统的生命周期。

TASK.md 一次性向系统声明了终极目标。PROGRESS.md 跨越不同的会话,完美传递运行状态和记忆。LOOP_INSTRUCTIONS.md 如同法律般死死控死 AI 的行为。至此,系统的瓶颈已经彻底从“如何生成代码/文案”,转移到了“如何更好地去审查和验证代码/文案”。
你真正地设计出了一套无需你肉身盯防、自己就能持续运转的自动化系统。
PROGRESS.md,自动为你润色好今天的站会发言稿。请记住,上面列出的每一个自动化工作流,全都是从那套极其简单的 “4文件系统” 开始起步的。
唯一需要修改的就是TASK.md 和 LOOP_INSTRUCTIONS.md。
如果说调整 Prompt 是在教 AI “怎么说一句话”,那么构建 Loop 就是在教 AI “怎么打一份工”。
这套以 TASK.md 定目标、INSTRUCTIONS.md 定规矩、PROGRESS.md 存记忆、outputs 装成果的极简“4 文件系统”,完美击中了当下大模型落地的最痛点——状态丢失与行为失控。它不依赖任何昂贵的企业级架构,任何一个掌握了命令行的开发者,今晚就能在自己的本地文件夹里跑通。
请记住文章中的那句忠告:“永远膨胀的状态文件等同于无用。把 PROGRESS.md 当作控制面板,而不是历史档案馆。” 收起你的权限,从只读(Level 1)开始,让你的循环在严苛的强验证(Strong Verification)下经历 5 次手动测试,再放心地交给自动调度。掌握了这一套微型系统工程学,你便跨越了普通使用者的门槛,真正成为了 AI 时代的系统架构师。
所有这些,都只需要你复制、粘贴本文的模板,填写你的具体要求,即可开始在 Claude Code 中狂奔。
去构建你的第一个循环,把重复的体力劳动,交还给守纪律的 AI吧。
原文链接:https://x.com/sairahul1/status/2074063593759227938
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-07-06 06:00:00

本文永久链接 – https://tonybai.com/2026/07/06/get-started-with-gvu-in-5-minutes
大家好,我是Tony Bai。
上一篇《五年,三篇文章,一个我一直没真正解决的问题》里,我记录了这五年折腾私有 Go module 拉取方案的几段经历,也提到今年白盒交付给客户时踩的新坑。文章结尾我说会把这个工具单独写篇上手文章——今天这篇就是它。
gvu(GomodVanityUrls 的缩写)现在正式开放公测,官网在 gomodvanityurls.com。这篇文章不谈太多背景,直接上手,5 分钟带你走完从安装到拉取用上第一个私有 module 的完整流程。

一句话概括:gvu 是一个命令行工具,用来把 Go module 的 vanity import path(比如 mycompany.com/go/common)映射到你真实的代码仓库地址(GitHub / GitLab / Bitbucket / Codeberg / 自建仓库都支持),全程一行命令搞定,不需要你再手写 govanityurls 的 YAML 配置,也不需要自己维护 Nginx。
如果你看过我五年前那几篇搭建私有 Go module 拉取方案的文章,就知道这背后原本要做的事:起一个 govanityurls 服务、写映射配置、配 Nginx 反代、(如果要自定义域名)自己签 TLS 证书。gvu 把这一整套操作压缩成了一行 gvu add。
curl -fsSL gomodvanityurls.com/install.sh | bash
如果你用 Git Bash / MSYS2 / Cygwin/ WSL/ WSL2,直接执行和上面一样的命令即可,脚本会自动识别 Windows 环境并下载对应的 .exe。
如果你在Windows上使用 Scoop 的话,可以在Powershell命令行里执行下面:
scoop bucket add gomodvanityurls https://github.com/gomodvanityurls/scoop-bucket
scoop install gvu
装完之后,跑一下 gvu --version 确认安装成功。
最简单的方式是直接用 gvu 提供的默认域名 gomod.io,不需要注册、不需要配 Domain解析,一行命令(以你的私有仓库在github.com上为例):
$ gvu add gomod.io/private-module https://github.com/yourorg/private-module.git
✔ Route created successfully!
═══════════════════════════════════════════════════════════
┃ Route ID: m_abc123
┃ Vanity Path: gomod.io/private-module
┃ Target Repo: https://github.com/yourorg/private-module.git
┃ Quota: 1 / 5 used
═══════════════════════════════════════════════════════════
这条命令执行时,gvu 会自动帮你创建一个匿名账号(免签名、免邮箱),并且自动识别出这是个 GitHub 仓库。GitLab、Bitbucket、Codeberg 或者你自己内网的 GitLab 也都是同样的用法,把仓库地址换掉即可,gvu 会自动识别托管平台。
如果你的仓库需要用 SSH 方式拉取,也可以直接传 SSH 地址:
gvu add gomod.io/private-module ssh://[email protected]:yourorg/private-module.git
export GOPRIVATE=gomod.io
建议把这行加到你的 ~/.bashrc 或 ~/.zshrc 里,一劳永逸。
在你的 go.mod 里,module path 要直接用 vanity path,而不是原始的 Git 托管地址:
// go.mod —— 正确
module gomod.io/private-module
// go.mod —— 错误,会直接去github上拉去私有仓库
module github.com/yourorg/private-module
如果你的模块之前用的是 github.com/... 这样的 module path,迁移时记得把它改成 vanity path 再发布。
改好之后,同组织内有github.com私有仓库访问权限的人 import "gomod.io/private-module" 就能正常拉取了。
Go 工具链会自动把这个 vanity path 解析到你背后真实的仓库地址——以后即便你把代码从 GitHub 挪到自建 GitLab,只要在 gvu 里改一下映射,所有使用方的 import path 完全不用变。这也是五年前我们折腾 govanityurls 时最看重的那个特性,gvu 把它继承了下来,而且不需要你自己运维那套基础设施。
# 查看所有已注册的路由
gvu list
# 删除一个路由
gvu remove m_abc123
# 批量删除
gvu remove m_abc123 m_def456 -y
Monorepo 场景:如果一个仓库里有多个 Go module(比如 go/module-a/go.mod),用 --subdir 指定子目录(需要 Go 1.25+):
gvu add gomod.io/module-a https://github.com/yourorg/monorepo.git --subdir go/module-a
自定义域名场景:如果你不想用 gomod.io 这个默认域名,想用自己或公司的域名(比如 go.mycompany.com)做 vanity path 前缀,需要先升级到 Verified 账号(免费,只需绑定邮箱)并配置好 DNS CNAME。这部分内容稍微多一点,我会放在下一篇有关gvu使用的文章中详细讲,因为这正好对应我们当年真实的迁移场景。
匿名账号(也就是你什么都不注册、直接 gvu add 或gvu auth signup时自动创建的那种)免费可以用 5 个路由,够个人项目和小范围试用。用 gvu auth bind [email protected] 免费升级成 Verified 账号可以拿到 10 个,并解锁自定义域名。如果你需要更大的配额或者短路径这类进阶能力,可以看一眼 pricing 页面,这篇就不展开了。
大家随时可以用下面这条命令看自己账号的状态:
gvu auth whoami
到这里,你应该已经跑通了从安装到用上第一个私有 module 的完整流程——中间没有 Nginx,没有 YAML,也没有证书要自己签。
这就是 gvu 现在的公测版本。如果你在使用中遇到问题,或者有功能上的想法,欢迎去 Community 页面 ,或使用gvu issue 反馈,我会认真看每一条。
下一篇我会把五年前那套"手动 govanityurls + Nginx"的方案,原样迁移到 gvu 上,一步步对比迁移前后到底省了多少事——这也是很多老读者最关心的问题:如果我已经有一套手搭的方案,值不值得换。
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-07-06 05:00:00

本文永久链接 – https://tonybai.com/2026/07/06/is-go-language-conservative-evolution-still-viable-next-5-years
大家好,我是Tony Bai。
如果观察近几年主流编程语言的演进路线,我们会发现一个明显的趋势:语言的特性正在变得越来越丰富。
C# 保持着稳健的步伐引入新颖的语法糖;Rust 在宏体系和类型系统的边界上不断突破;TypeScript 则致力于将各种现代编程范式融为一炉;C++已经正式发布C++ 26,又增加四大史诗级新特性。
现代语言似乎都在努力取悦开发者,让他们在编写代码时感到更加舒适与高效。
在这样的大环境下,Go 语言(Golang) 的演进策略显得格外引人瞩目。
刚刚功能冻结,并发布了首个RC1版本的 Go 1.27 ,依然没有带来让人眼前一亮的语法层面的革新(唯一让人欣慰的是“泛型方法”的落地)。它的更新列表,再次“枯燥地”集中在了微小内存分配优化、垃圾回收(Green Tea GC)的升级、以及对协程泄露监控的底层支持上。
这引发了海外技术社区 Reddit 的 r/golang 板块一场思辨。一位开发者发出了拷问:
“Go 在语言演进上这种极其保守的态度,在未来 5 年还是正确的吗?它到底是不是在阻碍自身的发展?”
令人深思的是,在这场辩论中,那些有着十几年经验的工程师、基础架构老兵们,出奇一致地为 Go 的这种“保守”投下了赞成票。
在软件工程的复杂性管理中,Go 的这种“保守”究竟意味着什么?

许多开发者在审视一门语言时,习惯于做“特性对比清单”。但他们往往忽略了软件工程中的一个隐性成本:当一门语言不断叠加新特性时,维护这些特性的认知边界成本是呈指数级上升的。
在社区的讨论中,开发者们将这种无限制接纳特性的反模式称为“厨房水槽(Kitchen Sink)”困境——即把所有看起来不错的主意全塞进语言里。
以 C++ 为例,它作为系统级编程的奠基者之一,其实一直坚持着向后兼容。但正因为背负了数十年的特性积累,一位网友精准地总结道:“C++ 确实从不破坏现有代码,但它却为你提供了 37 种实现同一个功能的方法。”
当一门语言中解决同一个问题有太多流派时,团队协作的摩擦成本就会激增。代码评审(Code Review)不再是纯粹的业务逻辑探讨,而容易沦为对不同语言特性(比如究竟该用哪种智能指针或模板元编程)的审美辩论。
前端领域的 TypeScript/JavaScript 生态则展示了另一种挑战。为了兼容历史并拥抱未来,语言特性快速迭代。这导致开发者经常面临生态断裂:去年流行的写法,今年可能就不再是最佳实践。开发者被迫陷入持续的“追逐新语法-重构老项目”的循环中。
相比之下,Go 的演进极其克制。即使是社区呼声最高的“泛型(Generics)”,Go 团队也是经历了长达十年的拉锯、无数次的提案否决,直到确认其不会破坏现有语言的简洁性哲学后,才审慎引入。
正如讨论区的一位开发者所言:“新特性的引入不应该仅仅是因为它是个‘好主意’,而是它必须能极其自然地、且以唯一正确的方式融入现有的生态。”
在这场关于“是否应该加速演进”的辩论中,最精彩的洞察来自于对编程语言设计目标的本质追问。一位网友提出了一个极具启发的二元分类框架:
代表语言:Rust, Kotlin, 现代 C#。
这类语言的核心驱动力是优化开发者的编写体验。它们提供极具表达力的语法、强大的类型推导和安全的编译期检查。
其背后的 Trade-off(权衡)是极高的认知负载(Cognitive Load)。
为了享受这些便利,开发者需要跨越陡峭的学习曲线;复杂的特性也往往导致编译速度较慢,以及在大型团队中统一代码风格的困难。
代表语言:Go。
Go 是一种典型的“愿意牺牲编写代码时的部分表达力,来换取极致的工程运维体验”的语言。
对于 CNCF(云原生计算基金会)生态和基础架构团队来说,“代码被阅读和维护的次数,远大于被编写的次数。” Go 语言的设计初衷,就是为了“在庞大的组织中易于协作(easy to cooperate in)”。
如果 Go 不在语言特性上大步快跑,那么它的工程价值沉淀在哪里?
答案是:对稳定性的死磕,以及向系统底层的深耕。
一位开发者留言道:“我十年前写的 Go 代码,现在依然能用最新的编译器毫无障碍地编译运行。”
这种变态级的稳定性,带来了一个巨大的生态红利:外部依赖库不会无故腐烂。 在 Go 的世界里,很多几年没有更新的开源库,并不是因为它们被“遗弃”了,而是因为它们已经“完备(Finished)”了。它们不需要为了适配语言的新语法而被迫发版。
Go 的设计者深知,给开发者过多的选择,往往会增加协作的沟通成本。Go 强迫你用最直接、最基础的控制流来表达逻辑。这种“同质化(Homogeneous)”的代码风格,在大型团队或开源社区中,是消除隔阂的最有效武器。
当其他语言在讨论语法糖时,Go 团队在优化 GMP 协程调度、降低垃圾回收的停顿时间(STW)、优化编译器内联策略。这些看不见的工作,才是真正在高并发场景下,帮企业节省海量算力成本的核心基石。
回到最初的问题:在未来 5 年,Go 的这种保守态度是否依然正确?
如果我们观察未来的技术趋势,无论是微服务架构的进一步深化,还是大模型(AI)辅助编程的普及,代码的生成速度都在呈指数级增长。
在这样一个代码泛滥的时代,系统的复杂性将成为最大的挑战。此时,我们需要的是更多令人眼花缭乱的新语法吗?
或许,我们更需要的是一个坚如磐石、行为可预测、任何 AI 生成的代码都能被初级工程师迅速读懂和审查的基础设施语言。
编程语言并不都是为了“美丽”而存在的,它们的存在是为了解决实际任务。在这个世界上,我们需要有用于探索编程范式前沿的极客语言(比如C++、Rust等),但也同样需要像 Go 这样,甘愿做一枚安静、无聊但坚不可摧的螺丝钉。
坚持“断舍离”,抵御住盲目添加特性的诱惑,这不仅仅是一种语言设计上的自信,更是在喧嚣的技术洪流中,坚守长期主义的最高级自我修养。
资料资料:https://www.reddit.com/r/golang/comments/1uhxr4c/is_gos_conservative_approach_to_language/
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!
我们致力于打造一个高品质的 Go 语言深度学习 与 AI 应用探索 平台。在这里,你将获得:
衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-07-05 07:00:00

本文永久链接 – https://tonybai.com/2026/07/05/go-private-modules-lessons-learned
大家好,我是Tony Bai。
五年前,我写过两篇文章,讲的是怎么在公司内部搭一套私有 Go module 拉取方案。三年前,我又补了第三篇,说这套东西“跑了两年,基本没什么大问题”。
三篇文章,五年时间,我一直以为这件事已经被我解决了。
直到上个月,我们把一个 Go 私有库以白盒方式交付给一家客户。第二天一早,对方工程师就甩来一段报错:
$ go mod tidy
go: finding module for package mycompany.com/go/common
go: mycompany.com/go/common: unrecognized import path "mycompany.com/go/common":
https fetch: Get "https://mycompany.com/go/common?go-get=1":
dial tcp: lookup mycompany.com: no such host
我盯着这段报错愣了几秒,随即哑然失笑——这不就是五年前我们内部第一次遇到的那个问题吗?只不过这一次,“内部”变成了别人的“内部”。
五年里,我一直在同一个边界内部不断打磨这套方案,却从没想过,如果有一天代码要跨出这个边界呢?
客户的开发机、CI 机器,统统访问不到我们组织内部的私有 GOPROXY 服务器,也访问不到我们内部的 VCS。代码本身没有任何问题,问题出在一个我们几乎从未认真思考过的地方:这个 Go module 的 import path 本身,是一个只在我们自己的网络边界里才成立的“承诺”。

这不是我第一次跟这类问题交手。
2021 年,我写过一篇《小厂内部私有Go module拉取方案》。当时的背景很简单:随着公司里 Go 项目变多,“代码重复"问题冒了出来,我们把公共代码抽成了内部私有仓库,于是就有了"如何让 go get 拉到私有Go代码库"这个需求。
大厂的答案很简单——因为大厂内部的 VCS 天然就挂在一个内部域名下(比如 git.company.com),import path 直接指向真实仓库地址即可,go get 该怎么工作就怎么工作。
但小厂或个人开发者通常没有这个条件:我们希望私有 module 的 import path 是干净的、带有自己域名语义的(比如 mycompany.com/go/common),但这个域名背后并没有真的托管代码,代码实际存放在内部的 Git 服务器上。这中间,需要一层"翻译”。
我们当时选择的方案是 govanityurls + Nginx + 内部 GOPROXY 的组合:govanityurls 负责把 mycompany.com/go/common 这样的 vanity import path 翻译成真实的仓库地址,内部 GOPROXY 负责统一代理公共 module 和私有 module 的拉取。开发者只需要把 GOPROXY 指向内部代理,剩下的事情对他们透明。
这套方案上线之后,确实解决了当时的问题。
2022 年,业务线决定把代码托管从 Gerrit 迁移到 GitLab,我又写了篇《小厂内部私有Go module拉取方案(续)》,记录了适配过程中踩的坑——SSH 密钥交换算法不匹配、vanity.yaml 里仓库地址的调整,等等。本质上,这次只是把"翻译层"背后指向的仓库换了个地方,原理没有变。
2023 年,我又写了第三篇《小厂内部私有Go module拉取方案3》。那时候这套基础设施已经稳定运行了两年,“总体感觉不错,没什么大问题”。文章里我提到了一个一直没有优雅解决的小麻烦:每次新增一个用作私有依赖的仓库,vanity.yml 都得手动改一次,开发者对这种运维琐事天然是抗拒的。同一篇文章里,我还顺手研究了一下 go mod replace 能不能绕开 vanity 层直接指向内部仓库——最后发现 Go 官方早就支持了这种用法,只是没多少人知道。
回头看这三篇文章,我当时关注的都是"怎么让方案更稳、更省心",但有一个前提我从来没有认真质疑过:
所有这些方案,都默认"拉代码的人"和"存代码的服务器"处在同一个网络边界、同一个信任边界里。
五年里,我们不断优化这个边界内部的体验,却从来没想过,如果有一天代码需要跨出这个边界呢?
今年我们开始以白盒方式向客户交付 Go 私有库的源码。这是一种新的交付模式:不再是"我们运营一个服务,客户调用接口",而是代码本身要跑到客户自己组织的机房里,由客户自己的工程师编译、构建和维护。
这一下子把五年来被小心维护的假设全部打破了:
mycompany.com/go/common 这样的 import path,在客户的网络里就是个死链接,govanityurls 部署在我们这边,客户那边根本连不到。于是我们不得不在客户组织内部,重新搭建一套简化版的私有 module 拉取方案——申请域名或者用内网 DNS 顶一下、配一遍 vanity 映射、协调 GOPROXY 或 GOPRIVATE、把仓库权限单独开一份给客户。整个过程和五年前第一次搭建时几乎一样繁琐,唯一的区别是,这次要在一个我们不熟悉、不受控的环境里,重新做一遍。
那一刻我意识到,这从来都不是一个"公司内部基础设施"问题,而是一个更普遍的问题:
只要代码需要跨越一个信任边界——从团队到团队,从公司到客户,从内网到公网——import path 和它背后真实仓库地址之间的这层"翻译",就会重新变成一个需要人工搭建的脆弱环节。
这个问题不是 Go 独有的,几乎任何一种需要"私有代码分发"的语言生态都会遇到类似的处境。只是 Go 因为 go get 直接依赖 import path 做域名解析这个设计,把这个问题暴露得格外明显。
过去这类"边界重新搭建"的问题,标准答案往往是造一个管理后台:一个 Web UI,让你去配置域名映射、TLS 证书、路由规则——本质上是把运维动作图形化。
但对开发者来说,这从来都不是最自然的心智模型。真正高频的运维动作,最后总会被收敛成一行命令:gh repo create、wrangler deploy、supabase db push——这几年你会发现,越来越多原本需要打开一个 Web 控制台才能完成的事情,正在被重新压缩进终端里的一行命令。命令行不再只是"极客的偏好",它正在重新成为很多产品事实上的控制面,AI 时代尤甚。
这和我这五年反复折腾的事情,其实是同一个道理:vanity.yml 手动改一次、Nginx 配一次、GOPROXY 调一次——这些散落在不同工具、不同配置文件里的动作,本质上都应该是一个命令的事。不管这个"边界"是公司内部,还是客户组织内部。
所以这次白盒交付踩完坑之后,我索性把这五年反复重复的这套操作——vanity import path 的映射、私有 module 的拉取配置——抽象成了一个通用的命令行工具,取名 gvu(gomodvanityurls 的缩写)。它现在正在做公测,官网在 gomodvanityurls.com。
它解决的问题很朴素:不管你是要在公司内部搭一套私有 module 拉取方案,还是像我们这次一样要在客户组织内部临时重建一套,都不用再手写 vanity.yml、配 Nginx、协调 GOPROXY 这些琐事了,一行命令搞定映射和分发。
这篇不是产品说明书,就不展开细节了,后面我会单独写几篇上手向的文章。感兴趣的话可以先去gvu官网看看,花五分钟体验一下。
go build 一次成功方案在客户组织内部重新搭好之后,我又收到了那位工程师的消息,这次贴的是:
$ go mod tidy
go: downloading mycompany.com/go/common v1.2.3
$ go build ./...
没有报错,干干净净地结束。
五年前那篇文章的开头,我写过"Go module 拉取公共依赖不再是痛点"。五年后我想补一句:私有依赖的拉取,只要代码不跨越边界,也早就不是痛点了。真正的痛点,永远出现在边界被打破的那一刻。
这五年,我们的方案从“能用”到“稳定”,看起来是在不断进步,但本质上一直是在同一个边界内部打磨细节。
今年白盒交付撞到的这堵墙,才让我第一次看清问题的全貌:Go 私有 module 的拉取困境,本质上是一个"代码如何优雅地跨越信任边界"的问题,而不是一个"公司内部该怎么配置 GOPROXY"的问题。
五年三篇文章,记录的都是同一个问题在不同场景下的重复出现。这一次,我把重复出现的部分做成了一个工具。
接下来,我会写几篇 gvu 的上手教程和一些进阶用法,感兴趣的朋友可以留意后续文章。
如果你也被"私有 Go module 怎么优雅分发和拉去"这个问题困扰过,欢迎在评论区聊聊你的方案,说不定又是一次有意思的讨论。
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!
我们致力于打造一个高品质的 Go 语言深度学习 与 AI 应用探索 平台。在这里,你将获得:
衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-07-04 04:00:00

本文永久链接 – https://tonybai.com/2026/07/04/build-gin-simple-over-easy
大家好,我是Tony Bai。
在 2014 年那个 Go 语言(Golang)还未彻底统治云原生世界的年份,一个刚刚从旧金山搬回西班牙的年轻人 Manu Martínez-Almeida,正面临着人生的十字路口。
他打算开发一个名为 Fyve 的社交网络后端。因为喜欢 Go 语言那种“平淡无奇(plain)”的特质,他选择了 Go,并顺手写了一个 Web 框架作为该项目的底座。
戏剧性的是,社交网络 Fyve 早已在历史的尘埃中灰飞烟灭,但那个顺手写出的副产物—— Gin 框架,却在经历了十多年的岁月洗礼后,斩获了超过 88,000 个 GitHub Stars,成为了将近 30 万个 Go 语言项目的基石。

Gin 为什么能活这么久、这么火?在这个充斥着各种花哨特性的开源世界里,它的制胜密码是什么?
近日,Manu 撰文回顾了 Gin 的诞生历程。答案其实就藏在他深受 Go 语言先驱 Rob Pike 启发的一句话里:“Simple Over Easy(简单胜于容易)。”
今天,我们就来深度扒一扒,这八个字是如何塑造了 Gin 坚不可摧的架构底座的。

时间回到 2014 年。如果你在当时向 Go 开发者询问推荐哪个 Web 框架,几乎所有人都会指向一个名字:Martini。
当你打开 Martini 的 README 时,你会立刻被它吸引。它的代码看起来极度“Easy(容易)”,寥寥几行就能让一个路由跑起来,中间件模型看起来优雅极了。
然而,这种表面上的“容易”,却包裹着致命的毒药:过度依赖基于反射(Reflection)的依赖注入。
Manu 很快发现了 Martini 的灾难性缺陷。为了让开发者在写代码时感到“轻松”,Martini 在底层使用了大量的反射来强行绑定处理器(Handlers)。
这引发了 Manu 对 Rob Pike 著名演讲《Simplicity is Complicated》的深刻共鸣。
他也悟出了一个颠覆性的道理:看似容易(Easy)的软件,往往是因为它在底层替你隐藏了太多的魔法和运动部件(Moving parts);而真正的简单(Simple),是指系统底层的概念清晰透明,哪怕这意味着开发者在编码时需要稍微多写几行显式的代码。
Gin 就是在寻找这条“中庸之道(The middle ground)”。
当时原生的 net/http 太过简陋,没有路由参数解析,没有验证辅助,所有重复的管道代码(Plumbing)都要自己写,噪声极大。
Gin 填补了这个空白,但它绝对禁止在请求路径上使用反射。
它引入了一个天才的设计:gin.Context。
r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id") // 显式获取路径参数,没有反射魔法
c.JSON(200, gin.H{"id": id}) // 一次调用完成响应渲染
})
r.Run(":8080")
*gin.Context 携带了请求、响应写入器、路径参数以及渲染方法。它是请求在整个生命周期中传递的唯一对象。
没有依赖注入,没有隐藏逻辑。普通操作只需一个方法调用,而且底层全是非常直白的 Go 代码,一旦生产环境出现诡异现象,你可以直接通过 IDE 步入(Step into)源码进行 Debug。
(有趣的是,gin.Context 在 2014 年发布时,Go 标准库里的 context.Context 甚至还没诞生。两年后标准库补齐了这一概念,Gin 顺势兼容了它。)
让 Gin 一战成名的,是它在当时秒杀众生的极速路由性能。而这同样源自“Simple”的约束。
当时的许多框架(如 Martini)为了提供极致的路由灵活性,允许开发者使用正则表达式(Regexes)来定义路由。这确实很“Easy”,你可以让一个路由只匹配数字,或者隐藏极其复杂的匹配模式。
但代价是,框架在内部维护了一个正则列表。每来一个请求,都要去遍历询问这个列表:“是你吗?是你吗?”。这相当于在你的 Web 框架里,寄生了一门名为“正则表达式”的第二语言。
Gin 选择了极度的克制。它的路由语言非常小:只允许静态段、命名参数和全匹配通配符。
这种克制,使得 Gin 能够采用一种当时由 httprouter 带火的高效数据结构:Radix Tree(基数树/压缩前缀树)。
在 Radix 树中,共享相同前缀的路由会被折叠在一起:
/search/support/blog/:slug/blog/:slug/comments当请求 /blog/42/comments 进来时,路由查找不需要遍历一个包含一万条正则的列表,它只需要顺着树干,依次走过 /blog/ -> 42 (:slug) -> /comments 节点。

这就是架构设计上的降维打击:
极致的零分配艺术
性能不仅来自于算法,还来自于对内存的苛刻控制。
在 Gin 的设计中,当请求在树中游走到达最终叶子节点时,需要提取的路径参数(如上面的 42)会被直接放入一个预先分配好的切片(Slice)中。
那个贯穿始终的 Context 对象,也是从 sync.Pool 对象池中拿取并在请求结束后重置的。
这意味着什么?意味着在热路径(Hot Path)上,垃圾回收器(GC)几乎没有废料需要清理。系统延迟不再会因为 GC 的剧烈介入而产生波动。
正如 Manu 所言:“我之所以相信这种性能,是因为它的速度来源于‘做得更少’。而系统做得更少,也就意味着阅读源码的人需要理解的东西更少。”
如果说性能让 Gin 一炮而红,那么真正让它统治生态十年的,是它在开源治理上的“死板”。
Go 语言在 1.0 发布时,向世界做出了著名的“Go1 向后兼容承诺(Compatibility Promise)”。Manu 决定,Gin 也要向它的用户提供一模一样的契约。
这个约束彻底改变了框架设计的心理模型。
在合入任何一个公开 API 到主分支之前,作者都必须经受直击灵魂的拷问:“我愿意在接下来的十年里,一直忍受并维护这个 API 吗?”
这种约束执行起来是极其痛苦的。对于开源作者来说,频繁发布带着炫酷新特性的破坏性版本(Breaking Changes)能带来极大的成就感,而维护向后兼容往往意味着要咽下很多历史遗留的苦水。
但这带来了无可估量的信任红利。十多年前用 Gin 早期版本写的第一批服务,有些代码到今天甚至一行都不用改,依然能在最新的编译器下完美编译运行。这就是 Gin 后来能在 Hacker News 上爆火,并被各类企业核心业务大规模采用的终极原因。
如今的 Gin,早已经不需要 Manu 亲自去写每一行代码了。它“毕业”了,被移交给了更加专业和热情的社区维护者,每天服务着数十亿计的网络请求。
这可能是所有开源项目最完美的归宿。
在文章的最后,Manu 给所有试图构建基础库的工程师留下了一段极具分量的忠告:
“如果你要设计一个库,把目标定得高一点。设计一个你能想象维护十年的 API,并让其底层极其简单透明(Simple underneath)。即使这会让你在初期实现时付出更加高昂的代价。因为只要你运气够好,这个东西最终会超越你个人的力量,长成参天大树。”
在追逐 AI 代码生成、“一秒钟写出一个微服务”的狂热时代里,Gin 的故事像一面镜子,提醒着我们:
永远别把“容易”当成了“简单”。真正伟大的系统,都是在隐忍与克制中,被一行行透明的代码雕刻出来的。
资料链接:https://manualmeida.dev/articles/gin-simple-over-easy/
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!
我们致力于打造一个高品质的 Go 语言深度学习 与 AI 应用探索 平台。在这里,你将获得:
衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-07-03 06:00:00

本文永久链接 – https://tonybai.com/2026/07/03/20-loop-design-patterns-every-ai-engineer-should-know
大家好,我是Tony Bai。
在前一篇中,我们分享了 OpenAI 联合创始人、现Anthropic独立研究员 Andrej Karpathy 的一线手记《LOOPS.md》。他一针见血地指出:“Prompt 是你写完一次就忘的东西,而 Loop(循环)才是你睡觉时仍在为你工作的系统。” 提示词的杠杆效应已经见顶,现在的竞争维度,是“循环工程(Loop Engineering)”。
但是,具体到工程实践中,我们该如何设计这些循环?
AI 圈知名技术专家 Rahul (@sairahul1) 梳理总结了当前工业级 AI 生产系统中最核心的 20 个“循环设计模式”(Loop Design Patterns)。
正如传统的软件工程有《设计模式》(Design Patterns)作为圣经,AI 时代的软件工程也正在迎来它自己的范式。正如Rahui所说“Agent 只是干活的工人,而 Loop 才是让工人不断自我进化的机制”。普通工程师和年薪百万的资深 AI 架构师之间,差的正是这套循环设计的功力。
从代码纠错、多角色博弈、动态规划,到让系统自我重构的终极优化循环。这 20 个模式,将彻底帮你告别单次调用的“拼运气”时代,跨入工业级 Agent 系统的大门。
以下为译文全文:

大多数 AI 工程师只知道如何构建一个 Agent(智能体)。
但极少数人知道如何构建一个在第一次尝试失败后,能够自主变得更好的系统。
而这,正是年薪百万与普通工程师之间的鸿沟。
两者的本质区别在于:
今天,在生产环境(Production)中运行的最强大的 AI 系统,绝不是靠单次模型调用(Single Model Call)支撑起来的。
它们全部都是“循环(Loops)”。
生成(Generate) -> 评估(Evaluate) -> 学习(Learn) -> 改进(Improve)
周而复始。
直到输出结果真正达到优秀标准。
以下是频繁出现在工业级 AI 系统中的 20 个循环设计模式。
建议收藏,你迟早会用到它们。
前者就像一个工厂临时工,干完一次就走人。
后者则像一个卓越的正式工:研究每一次失误,重新改写操作手册,让自己的工作在每一个班次都提升 3%。
目前正在交付生产级 AI 的顶尖团队,早就不再死磕怎么写出更好的提示词(Prompt)了。
他们正在构建更好的循环(Loops)。

(核心目的:在输出结果离开系统前,使其质量达到极限)
这是 AI 工程中最核心、最重要的循环。
生成输出 -> 批判者(Critic)进行审查 -> 生成者(Generator)根据反馈重写。不断重复,直到达到质量阈值。
这不是用一个模型搞定一切,而是两个角色,一条流水线。
[生成者 Generator] → 产出初稿
[批判者 Critic] → "第 3 段很模糊。缺乏论据支撑。语气不够专业。"
[生成者 Generator] → 根据批判意见进行重写
[批判者 Critic] → "有进步,但结论部分仍然偏弱。"
[生成者 Generator] → 完成最终重写

生成 -> 打分。如果低于阈值,则重试。
极度简单,极度强大,却在实践中被严重低估。
score = evaluate(output)
while score < threshold:
output = generate(prompt)
score = evaluate(output)
attempts += 1
if attempts > max_retries:
return best_so_far # 达到最大重试次数,返回目前最好的结果
单个批判者难免会有盲点。
那就引入四个。
每个批判者独立进行评估。
最终的输出必须同时通过这四个维度的审核,才能获准出库。

在这个模式中,批判者的唯一任务就是摧毁(Break)生成者的答案。
不帮忙优化,只负责挑刺和否定。
对抗式批判者会提出以下灵魂拷问:
生成者随后必须进行自我辩护或重写。
只有经受住这一轮轮猛烈攻击后存活下来的答案,才是最好的答案。
单个裁判的打分往往伴随着噪声(Noise)。
让五个裁判联合打分,就能抹平这种噪音。
将同一个输出结果送入多个独立的评估器(Evaluators)中,汇总并计算平均分。只有在获得高度共识(High Consensus)的情况下,系统才会继续推进。

(核心目的:从历史经验中学习,让下一次运行变得更聪明)
这是目前存在的最重要的自我进化(Self-Improvement)设计模式。
Agent 执行失败 -> 分析失败原因 -> 将教训存入记忆库 -> 带着这段教训(写入 Context)重新尝试。
每一次迭代,都比上一次更聪明。
尝试 1: 失败了
反思: "我假设了 X 成立,但实际上 X 是错的。下次一定要先验证 X。"
尝试 2: 注入教训 → 获得部分成功
反思: "变好了。但我漏掉了步骤 Y。需要增加对 Y 的检查。"
尝试 3: 成功
这就是“会失败一次的系统”与“只会在同一个地方摔倒一次的系统”之间的本质区别。

在每次任务结束后,雷打不动地记录并固化三件事:
未来的每一次运行,都会自动继承这些知识库。
系统运行到第 6 个月时的表现,和第 1 个月时相比会有天壤之别,因为它已经阅读了自己长达 6 个月的成长史。
把每一次失败都存进“错题本”。
不管是错误的回答、糟糕的输出、失败的执行,还是遇到的极端 Edge Case。
在执行任何新任务之前,系统会首先检索错题本:如果存在类似的失败记录 -> 在任务开始前,直接将已知的修正方案写入执行策略。
确保系统绝不在同一个地方跌倒两次。这是目前生产环境中最被低估的模式。
大多数工程师只记得记录失败。
但你也需要记录成功。
当一个任务完成得很漂亮时:保存它的执行路径 -> 保存当时的 Context -> 保存促成成功的关键因素。
在面对类似任务时,检索并复用这些成功模式。不仅要从错误中吸取教训,更要从胜利中复制经验。
记忆会无休止地膨胀。
而无限的、杂乱的原始记忆,等同于无法使用的垃圾。
当积累了 N 条记忆项后,系统自动启动压缩机制:将大量具体的细碎记忆 -> 升华为更高级别的抽象规律(Abstractions)。
【压缩前】:
"任务 A 失败了,因为存在 X 问题"
"任务 B 失败了,因为存在 X 问题"
"任务 C 失败了,因为存在 X 问题"
【压缩后】:
"底层规律:X 会直接导致失败。执行任何任务前,务必优先检查 X。"
保持上下文窗口(Context)永远干净清爽,让关键规律随时可读,确保系统高速运行。

(核心目的:当现实发生变化时,能够动态调整路线)
AI Agent 设计中最常见的错误,就是把最初的规划当成一成不变的圣旨。
再完美的规划,在碰撞现实的瞬间也会碎落一地。
该模式的核心路径是:
制定规划 -> 执行当前步骤 -> 观察实际结果 -> 更新规划 -> 继续前行
它不是一条线性的瀑布流(Waterfall),而是一个不断收敛的螺旋(Spiral)。每一圈迭代,都在拉近与目标的距离。

绝大多数 AI 流水线(Pipelines)都是死板固定的:步骤 1 -> 步骤 2 -> 步骤 3,永远如此。
而动态工作流会根据中间结果在运行时决定自己的形状:
如果步骤 1 的输出是 A -> 走分支 X;
如果输出是 B -> 走分支 Y;
如果输出是 C -> 直接跳过步骤 2,执行步骤 5。
适用场景:多文档深度调研、多渠道客服自动路由、个性化内容自适应生成。
当一个极其庞大、模糊的目标进入系统时:
大目标: "写一份详尽的竞品分析报告"
|
├─ 子目标 1: "定位排名前 5 的直接竞品"
├─ 子目标 2: "分析每个竞品的核心功能点"
├─ 子目标 3: "对比彼此的价格模型"
└─ 子目标 4: "找出市场空白与破局点"
|
每个子目标 → 拆解为任务 → 转化为底层的单次 API 调用
在系统有能力开始干活之前,这个拆分循环绝不停下。
每执行 N 个步骤,系统都需要强行停下来问自己:“我们当前采取的动作,真的正在拉近我们与终极目标的距离吗?”
如果是:继续执行当前策略。
如果否:果断切换策略、寻找新工具,或者重修路线图。
Agent 绝不应该只会盲目地执行命令,它必须具备进度监控(Self-monitoring)的能力。
不达目的,誓不罢休。在所有硬性约束被完全满足之前,循环永远在后台运转。
while not all_constraints_satisfied(output):
output = improve(output, unsatisfied_constraints)
# 必须通过的约束硬性指标:
constraints = [
budget_under_limit, # 预算未超支
quality_above_threshold, # 质量达标
latency_under_200ms, # 延迟低于200毫秒
tone_matches_brand, # 语气符合品牌调性
no_hallucinations # 无幻觉成分
]
这在真实的商业化生产系统中非常普遍:只要有一条业务规则(Business Rule)没有通过,这个输出在系统内部就绝对不算完工。

(核心目的:通过尝试多条路径,榨出最优解)
不要把赌注押在单一条路上。
同时向多个方向展开探索。
paths = [
generate(approach="conservative"), # 保守方案
generate(approach="aggressive"), # 激进方案
generate(approach="creative") # 创意方案
]
# 对所有方案进行打分评估,挑出最优解
scores = [evaluate(p) for p in paths]
best = paths[scores.index(max(scores))]
横向对比所有尝试的产出,选择表现最好的那条分支,无情丢弃其余分支。

“分支探索循环”仅仅是在广度上展开了一层。
而树搜索(Tree Search)则可以根据需要向纵深无限延伸。
展开最具潜力的节点,剪掉最弱的分支。不惜代价持续探索,直到在树的深处挖出正确答案。
根节点 → 展开分支 [A, B, C]
├─ 节点 A → 展开 [A1, A2] (系统评估 A 很有前景,继续深挖)
├─ 节点 B → 剪枝剪掉 (系统评估 B 表现太差,在此止步)
└─ 节点 A1 → 展开 [A1a, A1b]
└─ A1a → 找到最优解! ✓
准备两个 Agent。针对同一个议题,站在完全相反的立场上。
Agent A 负责正方立论,Agent B 负责反方驳斥。
在每一轮辩论中,双方都在无情地挑战对方的假设、要求对方出示证据、戳破对方的逻辑漏洞。
最终的正确答案,不是在妥协和共识中产生的,而是在激烈的冲突和对抗中被逼出来的。这种对抗性的张力,能把单 Agent 盲目自信下隐藏的所有死角全部逼成显形。

(核心目的:让循环本身具备自我优化的元能力)
大多数普通工程师写好一个 Prompt 之后就再也不动它了。
而自动优化循环打破了这一现状。在这套系统里:
整个 Prompt 的进化过程完全自动化,不需要任何人类插手。
current_prompt = "请帮我总结这份文件。"
for iteration in range(max_iterations):
outputs = [run(current_prompt, doc) for doc in test_set]
scores = [evaluate(o) for o in outputs]
avg_score = mean(scores)
if avg_score >= target:
break # 达到目标分数,退出优化
# 筛选出低于阈值的失败案例
failures = [o for o, s in zip(outputs, scores) if s < threshold]
# 根据失败反馈,让模型自我重写升级 Prompt
current_prompt = improve_prompt(current_prompt, failures)
今天,在最顶尖的 AI 生产系统中运行的最强 Prompt,早就不是由人类手工写出来的了。
它们,是被系统自己“繁衍和进化”出来的。

这是整套框架最迷人、最硬核的部分。
它实现了“用循环去优化循环本身”(The loop improves the loop)。
系统在运行过程中,会不断、精确地测量自己的各项体征:
接着,它开始动刀修改自己的工作流。
嫌系统运行太慢?它会自动将两个可以并行的串行步骤改为并行(Parallelize)。
嫌运行成本太贵?它会在质量不降级的前提下,自动把某个昂贵的 GPT-4 节点替换为小尺寸模型(Cheaper Model)。
发现某段输出质量下滑?它会在这个节点输出前,自动安插一个批判者(Critic)角色进行强行拦截。
metrics = measure_workflow(outputs, latency, cost)
if metrics.latency > target_latency:
# 延迟超标,自动改写工作流,将慢步骤改为并行执行
workflow = parallelize(slow_steps)
if metrics.cost > budget:
# 预算超支,自动在非核心步骤替换为更低廉的模型
workflow = replace_with_cheaper_model(high_cost_steps)
if metrics.quality < threshold:
# 质量不达标,自动在关键出口前强行塞入一个 Critic 拦截节点
workflow = add_critic_before(final_output_step)
至此,真正具备“自我进化”能力的终极系统诞生了。
它不仅在改进它输出的数据,它在重写它自己的生命结构。

尽管这 20 个设计模式形态各异,但如果你把它们的骨架抽离出来,你会发现它们都共享着同一个底层公式:
$$\text{行动 (Act)} \rightarrow \text{观察 (Observe)} \rightarrow \text{评估 (Evaluate)} \rightarrow \text{调整 (Adjust)}$$行动 -> 观察 -> 评估 -> 调整
这就是全部的秘诀。
不要指望第一枪就能打中靶心。第一枪的产出,永远只是一个粗糙的起点。
而循环(Loop),才是将一个简陋的起点,淬炼成工业级可用产品的唯一熔炉。

大多数普通人依然坚信“Agent”就是大模型的未来。
但真相是:Agent 只是打工的工人。Loop,才是让工人每天自我进化的机制。
当前 AI 行业最底层的范式大转移,根本不是在追求更好的基础模型,而是研发思路的彻底倒转:
那些真正掌握了 “循环设计(Loop Design)” 精髓的团队,从来不会浪费时间去祈祷模型给个好提示词。
他们构建的系统,在部署上线后的每一天,都在人类看不见的后台默默地自我进化。
去构建循环吧,让它在你睡觉时,替你改变世界。
如果说过去两年是“提示词工程(Prompt Engineering)”的启蒙期,那么这篇关于 20 种“循环模式”的总结,则正式为我们拉开了“AI 软件系统设计学”的帷幕。
从最基础的“生成-批判”流水线,到令人震撼的“Prompt 与工作流自我重构”,这 20 个模式无一不在向我们传递一个冰冷的工程真相:即使大模型再聪明,单点突破的运气也永远敌不过系统级闭环的确定性。
不再寄希望于模型一次性吐出完美的答案,而是构建一个能在失败中反思、在记忆中学习、在约束中收敛的熔炉。当你的架构图里充满了自我诊断、自我修复和自我优化的飞轮时,你就真正掌握了 AI 时代系统工程的顶级心法。
原文链接:https://x.com/sairahul1/status/2072258045460226373
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!
我们致力于打造一个高品质的 Go 语言深度学习 与 AI 应用探索 平台。在这里,你将获得:
衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。
