Agent工坊

【Agent工坊】Bernstein 实战:确定性调度器编排 40+ Agent

当你开始让多个编程 Agent 并行干活,三个问题会立刻找上门。第一,结果不可复现——同一个目标昨天能跑通,今天换个模型、换个温度,就产出另一套东西,你想回滚都回不到「昨天那份」。第二,合并地狱——几个 Agent 改同一份代码,互相踩踏,主分支被搞得一团糟,你根本分不清哪行是谁写的。第三,无法审计——上线出了事故,你翻遍日志也说不清到底是哪一步、哪个 Agent 引入的。

Bernstein 就是冲着这三个问题来的。它是一个确定性编排器,把 Claude Code、Codex、Gemini CLI 等 40 多个 CLI 编程 Agent 并行跑起来,而整个协调过程里没有第二个 LLM 参与调度。项目 8 月 15 日刚提交了新版本,GitHub 上已积累 894 颗星,采用 Apache-2.0 协议,Python 3.12+ 实现,还贴心地提供了简体中文 README 和离线部署的 wheelhouse。这篇文章带你从安装到审计验证完整走一遍,每个命令附真实用法,最后给出一份踩坑清单。

图1▲ 图1

一、先搞清楚定位:它不是什么

很多人第一眼会把 Bernstein 当成又一个 Agent 框架,或者又一个省 token 工具。都不是。它是一层「编排层」,负责四件事:管调度、管隔离、管验收、管审计。你自己的编程 Agent 该怎么用还怎么用,Bernstein 只是站在它们上面,决定谁跑哪个任务、跑在哪个目录里、产出算不算合格、事后能不能查。

官方把它的差异化概括成四点。第一,「协调环里没有 LLM」,调度是纯 Python 写的,所以一次运行可以端到端复现——重放昨天的计划,就能拿到昨天的任务图。第二,「事后可查」,每次运行都写 replay 日志,始终开启的 lineage 脊骨记录每个带血缘的步骤,可选开启的 HMAC 审计链则给你一张可以离线验证的收据。第三,「天然隔离」,每个编码任务拿到独立的 git worktree,Agent 之间默认不共享任何可变工作区,唯一共享的是原子抢占的任务队列。第四,「广且本地」,40 多个 CLI Agent 适配器外加一个通用 prompt 包装器,基于文件的状态,没有 SaaS 中转,没有第三方数据面。

换句话说,它不试图让 Agent 更聪明,而是让「一群 Agent 一起干活」这件事变得可控、可复现、可追责。这恰恰是大多数多 Agent 实践里最缺的一块——大家忙着堆 Agent 数量,却没人管「这群 Agent 到底在做什么、能不能重来一遍」。

二、30 秒装好,先跑一个

安装走 uv 或 pipx,一条命令。pipx、pip、brew、dnf、npm 和 Docker 的装法官方安装指南里都有,离线场景有单独的 air-gap wheelhouse。

uv tool install bernstein

# 或者

pipx install bernstein

装完进入你的项目目录,初始化工作区,然后让 doctor 检查一遍环境:

cd your-project

bernstein init

bernstein doctor

bernstein -g "fix the failing test in tests/test_foo.py"

bernstein init 会创建 .sdd/ 工作区、bernstein.yaml 配置和 templates/ 目录。bernstein doctor 这一步别省,它会检查你机器上有没有装好 CLI Agent、是否已登录认证——很多人第一次跑失败就是卡在 Agent 没登录这一步。

-g 是 goal 的缩写,也就是「给一个目标,剩下的交给它」。敲下这条命令,Agent 们就会并行开跑,最后各自验收、合并、退出。整个过程你可以在两个观察面板里实时看:终端面板 bernstein live(左边是 Agent 的实时日志,右边是任务看板,底部还有一条成本线),或者浏览器面板 bernstein gui serve(能列出六十多个任务、十一个在跑,点开任意一个直接看它的 worktree diff)。两个面板读的是同一份任务 API,谁也不滞后。

顺带说一句「40+ 适配器」具体指什么:主流的 CLI 编程 Agent 基本都覆盖了,你手上没在列表里的冷门 Agent 也还有个通用 --prompt 包装器兜底,不至于被锁死在特定生态里。

三、核心机制:四阶段流水线

每个目标都走同一条流水线,一共四步。

第一步,Decompose(拆解)。管理器把你的目标拆成一堆任务,每个任务带角色、拥有的文件、完成的信号。这一步只消耗一次 LLM 调用,之后全是纯 Python 调度——这正是确定性的来源。

第二步,Spawn(派发)。Agent 在隔离的 git worktree 里启动,每个编码任务一个 worktree,主分支始终干净。非编码交付物(报告、数据集、操作日志)走 artifact 模式,拿到 .sdd/workspaces/ 下的独立工作目录,而不是硬塞进 git。

第三步,Verify(验收)。janitor 检查具体信号:测试通过没有、文件存在没有、lint 干净没有、类型正确没有。这一步是硬门槛,信号不过就进不了下一步。

第四步,Merge(合并)。验证通过的工作合入主分支;失败的任务要么重试,要么路由到另一个模型。

这套流程的价值在第四步特别明显:因为每个 Agent 在独立 worktree 里干活,失败的重试不会污染别人的成果,合并时也不会有「谁又覆盖了谁的修改」这种烂账。更关键的是,验收信号是白纸黑字的(测试、文件、lint、类型),而不是某个 Agent 自说自话的「我做完了」。

图2▲ 图2

四、一次真实运行长什么样

为了让你对这条流水线有个直观感受,下面是一次典型的单目标运行的流程还原(示例目标:给三个模块补上缺失的单元测试)。

先是拆解阶段,管理器把目标拆成六个任务:三个模块各写一份测试,两个任务修共享的测试 fixture,还有一个任务补 CI 配置。拆解只调一次 LLM,之后调度器接管。

然后是派发阶段。六个编码任务各自在独立 worktree 里启动,主分支纹丝不动。你可以理解成:每个 Agent 都拿到了一份干净的代码副本,爱怎么改怎么改,改坏了也不影响别人。

接着是验收阶段。janitor 逐个跑信号:六个任务里四个一次通过,一个因为 lint 不过被打回重试,还有一个类型错误被路由到另一个模型重新做。这个过程里没有任何一个 Agent 能「蒙混过关」——测试、lint、类型检查是硬指标。

最后是合并阶段。五个通过的任务合入 main,剩下一个最终失败,被明确标记出来,而不是悄悄消失在日志里。整条运行结束,你手上多了一份可重放的 journal 和一张签名收据。

对比一下没有编排器的场景:六个 Agent 塞进同一个目录,互相覆盖,你盯着一堆混杂的 diff 手忙脚乱。这就是 Bernstein 最直观的价值。

五、确定性从哪来:调度器是纯 Python

这是 Bernstein 最核心的设计取舍。绝大多数编排工具会让一个 LLM 来决定「下一步该派谁、该做什么」,这看似智能,代价是每一步都有随机性,同一份输入可能走出不同的路径。Bernstein 反其道而行:拆解目标用一次 LLM 调用,之后调度器是纯 Python,没有任何随机性。

确定性的好处不是抽象的。它的 replay 日志记录每一次运行,始终开启的 lineage 脊骨记录每个带血缘的步骤。如果某个 Agent 出了非确定性行为——比如同样的 prompt 产出不同的 diff——它不会表现为「某次重跑偶然好了」,而是精确地在某个步骤暴露一个 hash 不匹配。你一眼就能定位是哪一步、哪个 Agent 引入了不确定性,而不是对着一个飘忽不定的失败反复重试。

这背后是一个更深的判断:多 Agent 系统最怕的不是「跑得慢」,而是「不知道为什么这次挂、为什么上次好」。确定性把这种玄学变成了可定位的工程问题。官方有一篇专门讲「为什么确定性」的文档,论证这个取舍换来了什么、牺牲了什么,值得一读。

六、怎么证明一次运行没被篡改

确定性在这里不是让你「相信」,而是让你「检查」。开启审计跑一次,然后逐步验证记录下来的东西:

BERNSTEIN_AUDIT=1 bernstein -g "fix the failing test in tests/test_foo.py"

bernstein replay list # 磁盘上记录过的 run id

bernstein replay latest --verify # 重算 journal 头,点名第一个分叉的步骤

bernstein lineage verify <run_id> # 重算始终开启的 lineage 脊骨

bernstein audit verify # 校验 HMAC 链 + Merkle 封印

一张运行收据把 journal 头、lineage 脊骨头、以及可选的审计链区间,绑定在一个 Ed25519 签名的主体下,公钥内嵌其中。审核者拿到这个文件加上你的公钥,就能确认「记录的动作就是实际执行的动作」,不需要 HMAC 密钥,不需要活的 .sdd/ 目录,被篡改时会以退出码 2 点名第一个分叉步骤。

有一个细节要记住:如果你验证收据时不指定 --public-key 引脚,那这次校验只是「完整性校验」——它证明收据内部自洽,但不证明是谁签的,判决结果也会明确这么写。要证明「签名者」,必须带上公钥。

bernstein verify receipt .sdd/runs/<run_id>/run-receipt.json --public-key key.pem

官方仓库里还放了一次真实的 demo 运行,连同它的录屏、签名收据和公钥一起提交在仓库里,CI 每次 push 都会重新验证那张收据。你可以先跑 bernstein verify receipt 验证那段演示,确认工具链路是通的,再跑自己的。

图3▲ 图3

七、多阶段计划与可靠性基准

如果目标复杂,你可以跳过 LLM 规划,直接执行一份多阶段计划:

bernstein run plan.yaml

bernstein stop # 优雅停机,带 drain

plan.yaml 里定义好阶段和任务,调度器按你的剧本走,而不是让模型即兴发挥。这对需要固定 SOP 的团队尤其有用——同一个计划,今天跑和明天跑,产出的任务图一模一样。bernstein stop 是优雅停机,会把在跑的任务 drain 完再退出,不会半路硬切。

评估数字也一样可以查。bernstein bench run --reliability k 让每个任务在固定协调下跑 k 次,然后同时报告 pass^k 下限(k 次全部通过才算过)和 pass@1 上限。这个结果被封进签名收据,bernstein bench reliability-verify 可以离线重算——也就是说,一个伪造的可靠性下限是过不了验证的。对要拿评估数字去对外的团队来说,这一点很实在。

除了上面这些日常命令,它还带了一套更完整的「操作者界面」:PR 自动化、定时调度、聊天桥、以及一个能自动修小的回归问题的 autofix 守护进程。文档里把它们统称 operator commands。此外它还支持集群模式和 air-gap(离线)部署,意味着这套确定性编排不依赖联网,可以在内网或隔离环境里照常跑。对数据不出内网的团队来说,这是决定性的加分项——很多编排工具的云端数据面直接把你挡在门外。

八、踩坑清单(重点看这里)

下面这些坑,有一半是官方文档里明说的,另一半是新手容易忽略的:

第一,它是 beta,而且是一个人维护的。官方明确写了「版本号计的是发布次数,不是成熟度」,小版本号之间可能改接口。你如果把它接进任何依赖里,务必 pin 住版本号;出了问题报 issue,修复速度倒是快。

第二,--audit 这个标志属于 bernstein run,不挂在 -g 那个简写形式上。上面第六节的例子,-g 形式要开审计得用环境变量 BERNSTEIN_AUDIT=1,别把 flag 位置搞混了。

第三,短运行可能得到一个合法的空 lineage 脊骨。脊骨是「始终开启」的,但只有带血缘的步骤才会往里写条目,所以一个很短的运行结束时,脊骨是空的是正常现象,不是 bug。

第四,先跑 bernstein doctor。Agent 没装、没登录认证,是最常见的首跑失败原因。别省这一步。

第五,worktree 隔离是默认开着的,关掉它就等于所有任务跑在共享 checkout 里,隔离性归零。除非你有明确理由,别动这个默认值。

第六,别把它当省 token 工具用。它的价值是确定性和可审计性,不是压缩上下文。如果你的诉求只是省钱,单人小项目用 subagent 跑便宜模型往往更划算,Bernstein 属于「多 Agent 并行 + 需要复现和审计」的那批人。

第七,artifact 模式的任务不落在 git 里,完成信号是一张带血缘的签名收据而不是 git commit。如果你习惯了「看 commit 判断做完没有」,遇到报告类、数据集类任务会一时找不到「完成」在哪,要先接受这个心智切换。

第八,确定性不等于结果一定正确。它能保证「同样的输入产出同样的任务图、同样的合并结果」,但拆解目标那一次 LLM 调用本身的判断错了,后续再怎么确定性地重放,也是确定性地重复同一个错误。别把「可复现」误解成「更聪明」。

九、适合谁,不适合谁

适合的人很明确:需要并行跑多个编程 Agent、且要求结果可复现、事后可审计的团队;需要 air-gap(离线)部署的场景;以及要拿可靠性数字或运行收据对外交代的场景。它的本地优先、无 SaaS 数据面,对数据敏感的团队是加分项。

不适合的人也很明确:只跑单 Agent、项目很小、对审计没硬性要求的一人公司,上 Bernstein 属于杀鸡用牛刀。这时候,你手里的 Agent 框架内置的 subagent 能力、或者简单的并行脚本,成本更低、心智负担更小。两者的区别在于:subagent 帮你省 token、省心,但它不承诺「这次跑和上次跑一样」,也不给你一张能离线验证的收据。Bernstein 卖的是后者。

判断标准其实就一句话:当你开始为「这个 Agent 到底改了什么、为什么这次跑挂了」而抓狂,就是该认真看看 Bernstein 的时候了。它把一个长期以来靠感觉、靠人肉盯盘的事,变成了可重放、可验签、可定位到具体步骤的工程问题,而工程问题是能解决、能复现、能写进交接文档的。

十、给一人公司的落地建议

如果你现在规模不大,但已经在心里盘算「要不要上多 Agent 编排」,我的建议是分三步走。第一步,先把手里单 Agent 的工作流跑顺,把任务拆解、验收信号这些习惯建立起来——这些是 Bernstein 要复用的东西,你现在不练,上了编排器也排不好。第二步,当并行需求真的出现(比如同时改多个模块、批量处理多个仓库),先用便宜的 subagent 或简单脚本试水,摸清自己的真实痛点到底是「调度乱」还是「不可复现」。第三步,如果痛点确实落在不可复现和不可审计上,再上 Bernstein,并且从 bernstein -g 单目标跑起,跑通一条签名收据,再逐步上 plan.yaml 和 bench。

不要一上来就为了「看起来专业」而引入编排层。编排层的价值,只在并行规模和对可审计性的要求同时到位之后才显现。

工具本身还在快速迭代,beta 阶段的接口说变就变。装它、跑通、验证一条收据,半小时足够。值不值,你跑一次就知道。

如果要给这次上手一个可执行的验证清单,我建议按这个顺序:先用 bernstein doctor 确认你的 CLI Agent 能跑;再用 -g 跑一个很小的真实目标(比如修一个失败的单测),观察它拆解、派发、验收、合并的全过程;然后 bernstein replay latest --verify 看它能不能重算 journal;最后开 BERNSTEIN_AUDIT=1 再跑一次,用 bernstein audit verify 和 verify receipt 把整条审计链走通。四步都过了,你对「确定性编排」这四个字才算是有了第一手体感,而不是停留在文章里的概念。

(本文事实信息来自 Bernstein 官方 GitHub 仓库 sipyourdrink-ltd/bernstein 及其 README、文档,数据截至 2026-08-16。)

本文由AI辅助创作,经人工审核编辑发布

更多一人公司案例与工具,微信搜索「AI创业内参」关注我们