关于 ducklab
ducklab 是面向软件项目的可自托管开发流程框架,使用多个 LLM 分工完成需求、规格、计划、构建和评审。测试门槛由命令退出码判定,模型调用和决策均有记录,并在提交前等待人工确认。
主要功能
- 01多阶段开发循环
- 02多个 LLM 固定角色协作
- 03以命令退出码判定测试门槛
- 04记录模型调用与决策
- 05接受变更前重跑门槛验证
- 06CLI、桌面应用与 MCP 服务
适用场景
- 01用本地模型推进项目开发
- 02在测试门槛后审查并接受变更
- 03让其他 Agent 操作开发流程
- 04核验已接受变更的验证凭据
MCP 相关
ducklab 提供 MCP 服务,其他模型可通过它操作开发流程;操作和决策会被记录并注明来源。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Ducklab
一个可自托管的开发流程工具,可让多个 LLM 扮演固定角色,运行项目的完整开发周期,并由你签署测试关卡结果。
概览: 可自托管的开发流程工具(Go 引擎 + CLI + 桌面端,优先支持 Linux)· 简述 → 需求 → 规范 → 计划 → 构建 → 审查 → 发布 · 判定结果是退出代码,而非模型意见 · 优先使用本地模型(llama.cpp、vLLM),也可搭配任意兼容 OpenAI 或 Anthropic 的端点 · 人类或其他 Agent 均可通过 MCP 操作,所有决策均有记录并注明归属 · Apache-2.0 · 自我开发(.ducklab/ 中的运行记录就是凭证)。Agent:从 AGENTS.md 和 llms.txt 开始。
你提供一份简述。它会编写需求、规范和计划;使用一个或多个模型(它们可以相互辩论)构建任务;运行项目的真实测试关卡;并在提交任何内容前停下来等待你。每次模型调用都会记录。模型永远不会决定判定结果。
一次实时记录并加速播放的评审团需求接收过程:架构师流式输出草稿,另一个模型进行审查,预算以美分为单位逐步增加——然后运行在你的关卡处暂停。刚才所见内容的总费用:$0.07。
它从一开始就是为本地模型打造的。参与构建它的大部分工作的两个席位,分别由局域网上的一台 vLLM 机器和 localhost 上的 llama.cpp 服务担任,两者费用均为零;托管模型也可加入同一阵容,并依据同样的证据进行评估。
它为何存在
大多数 Agentic 编码工具都假设有一个强大的模型,并信任它。Ducklab 假设有多个廉价模型,且一个也不信任:
- 由门禁来判定,绝不由模型决定。 判定结果就是命令的退出代码。 先测试的运行会在编写任何测试前测量绿色的基线,之后测量新测试 带来的红色结果;每次接受都要从已提交 sha 的干净检出中重新运行门禁—— 未经复现的改动绝不会合入,而复现失败的接受会撤回其自身提交。
- 处处去相关。 由不同的模型评审;评审者永远不会知道代码是谁写的 (作者信息不在载荷中,而非仅在界面上隐藏);锦标赛评委盲选; 委员会批评者只读草稿,不看彼此的意见。
- 工作是一份契约。 任务的交付项就是实现者按编号列出的清单;它会
按编号逐项报告,评审者会对照差异检查每一项;若有交付项未完成,就会召来
橡皮鸭——只有在通过可测量的困境信号(刹车拒绝、连续失败、红色门禁)
触发时才会唤醒的顾问席位,并给出
none、一条让实现者直接回去继续工作的 备注,或stop。 - 根据证据选择席位。 每只小鸭都有一张评分卡——来自你自己的运行的 席位内通过率、每次运行成本、编码指数——席位名单板据此推荐席位,排名标准 可由你重新排序。推荐既少又有理有据:通过率按 Wilson 下界排序,至少运行 三次,本地模型不会因价格为 $0 而胜出。
- 一切都有上限。 轮次、令牌、成本、运行时间、工具输出、Shell 命令—— 每个上限都清晰可见,并可在运行期间调整,且留有记录。
- 文档不受模型上下文窗口限制。 为某个阶段附加 Wiki,大型席位会完整阅读;
小型席位则会将每份文档消化到适合其上下文的长度,完整文本只需调用一次
ref_read即可获取,而门禁会指出哪些文档无人打开。32k 的本地模型也能 获得 25 万字符的参考资料简报——运行框架负责承载工作记忆。
记录绝不凑整:每次运行都有其判定结果、成本,以及接受项是否从干净检出中复现为绿色。
Ducklab 在 Ducklab 内部开发。计划、缺陷、发布和已接受的任务都经过 它自己的循环,由它测量的同一批本地和托管模型驱动;近期功能(每次运行的 工作树、合并证明式接受、验收回执、治理写入保护)由这只鸭子构建,并由 人把关。你可以亲自验证这一说法:
git clone https://github.com/jrullan/ducklab && cd ducklab
go build -o ducklab-cli ./cmd/ducklab
for r in .ducklab/runs/*/receipt.json; do ./ducklab-cli proof verify "$r"; done
自 v0.7.0 起,每次接受都会附带回执:已提交的 sha、门禁命令、其退出代码, 以及干净检出复现判定——这些都是第三方可以重新推导的事实,而非评估意见。
状态
v0.7.0 及现已合并到 main 的第三阶段工作:每次构建和测试运行都在独立的 git 工作树中执行(绝不会触碰你的检出目录),验收时会变基运行分支,在变基后的提交上重新运行门禁,并且只执行快进合并;如果某次运行的工作已在引擎之外进入 main,操作者可以将已完成的运行重新标记为 landed。除此之外还有:七个阶段、五种模式、带实测评分卡的人员名单看板、自动消化参考文档、从桌面端管理技能、可入席的顾问聊天(发送图像前会先验证视觉能力)、附带截图证据的错误报告、带确定性覆盖检查的采用情况调查、会说明运行等待原因的提供方感知排队、当某个席位的能力达到可测量上限时给出的升级建议、验收回执(ducklab proof verify)、发布、自动驾驶模式、CLI、桌面应用和 MCP 服务——已进入官方 MCP 注册表,条目为 io.github.jrullan/ducklab——让另一个模型也能通过记录并注明归属的决策来操作整个循环。
docs/status.md 跟踪所有验收标准,不会虚报进度。如果代码与规范不一致,差异会记录在 docs/decisions/ 中。
安装
需要 Go 1.25+、用于桌面应用的 Node 22+,以及 git。
Linux
CLI 和引擎使用纯 Go 编写。桌面应用是 Wails v3 应用,需要 GTK/WebKit 开发包:
sudo apt install libgtk-3-dev libwebkit2gtk-4.1-dev # Debian/Ubuntu names
make desktop && make install
在 Ubuntu 24.04+ 上,桌面应用还需要 AppArmor 配置文件——请参阅决策 0003和 packaging/apparmor/。
macOS
xcode-select --install # the desktop build links against WebKit
brew install go node
make desktop && make install
说明:ducklab 每天都在 Linux 上开发和运行测试。每次执行 make cross 时,CLI 和引擎都会针对 darwin/arm64 进行编译检查,但目前还没有在 Mac 上验证过桌面应用的构建——第一个尝试的人就是测试者;无论如何,make install 都会为你安装 CLI 和引擎。欢迎报告遇到的任何问题。
两者
make install 会安装到 ~/.local/bin——请确保该目录位于你的 PATH 中。如果桌面二进制文件早于 frontend/src,它会发出警告,因为它仍会照常安装过时的文件。
不使用桌面应用进行前端开发
要在浏览器中运行前端,请在不同的终端中分别启动引擎和 Vite,然后使用连接详情打开浏览器。模拟引擎是最快的选择;同样的流程也可搭配启用了可选 CORS 标志的真实引擎使用:
# Fast, scripted data (recommended for UI work)
go run ./cmd/fake-engine --port 8787 --token fake-token
npm run dev --prefix frontend
# open http://localhost:5173/?engine=http://127.0.0.1:8787&token=fake-token
# Or use real engine data (development only; keep the origin explicit)
go run ./cmd/ducklab-engine --allow-origin http://localhost:5173
npm run dev --prefix frontend
# open http://localhost:5173/?engine=http://127.0.0.1:<engine-port>&token=<engine-token>
真实引擎默认仍限制为同源访问。--allow-origin 只允许一个浏览器源,适用于本地前端开发和视觉审查;它不会更改身份验证或环回地址绑定。如果不使用此标志,浏览器的跨源错误可能看起来像是会话失效。
engine 和 token 查询参数仅适用于 Vite 开发构建。也可以通过 VITE_DUCKLAB_ENGINE 和 VITE_DUCKLAB_TOKEN 环境变量提供这些参数。桌面应用外壳仍使用注入的 window.ducklab 连接。
三个二进制文件
| 说明 | |
|---|---|
ducklab-engine | 守护进程。拥有所有运行。仅绑定到 127.0.0.1,每次启动时都会轮换 bearer token。 |
ducklab | CLI 客户端。不保存状态,只向引擎发送请求。 |
ducklab-desktop | 桌面应用。也是客户端,也不保存状态。会自行启动(或接管)引擎。 |
提供方密钥在调用时从引擎的环境中读取——请在引擎启动前导出密钥,或通过能够从密钥环加载密钥的封装程序启动桌面应用。当它接管的引擎缺少本应用已有的密钥时,应用会告知你,重启按钮就在提示文字旁边。
从头到尾走完一个周期
从桌面应用开始:首次启动时,选择 Now → Create your first project;之后选择 Settings → Projects → New project,然后依次选择 Work → Documents → Intent → Add intention → Draft it。从 终端开始:
cd ~/dev/myproject
git init # ducklab needs a git repo
ducklab project init --name MyProject # auto-starts the engine if none is running
ducklab intake --from brief.txt # brief → requirements
ducklab intent # your briefs, verbatim, and what each one changed
ducklab spec # requirements → spec
ducklab plan # spec → milestones and tasks
ducklab run T-001 # build it
ducklab run accept r-20260729-... # commit it
ducklab review T-001 # read the commit
ducklab release plan --bump minor # what shipped
你的文字会成为记录的一部分:每份简述都会在任何模型读取之前,原封不动地保存为 INT-nnn 条目;它添加或更改的需求也会指向该条目。因此,需求始终可以回答“谁提出了这项要求,原话是什么”。
每个阶段都会先写入一个 .proposed 文件,然后等待你操作。accept 会将其正式纳入;reject 会精确还原本次运行所写入的内容,不多不少;“request changes”会将任何草稿(规格、计划、发行说明)连同你的备注一起退回。未经你同意(或未经你明确授予的自主权限),任何内容都不会提交。
参考文档可用于任何阶段:使用 --ref ~/wiki/product/(或桌面应用中的附件入口)加载文件或整个目录,作为架构师的背景资料。提示词会明确说明两条原则,以确保结果有据可依:已批准的需求决定范围;如果参考资料与代码不一致,以代码为准。当语料超出当前席位的上下文容量时,每份文档只会消化一次(按内容哈希缓存),完整文本仍可通过 ref_read 工具访问,而提案卡片会列出所有未被任何席位打开过的文档。
采用现有代码库的流程也相同:导入过程会读取代码并编写现状需求,规格会将相应章节标记为 as-built,计划则有意保持为空。之后的新工作通过 Bug 报告和计划修订进入,这也是 ducklab 自身的开发方式。
项目会在 .ducklab/project.toml 中声明自身的事实来源:验证门([verify]——包含干净检出所需的 link_deps 和 setup)、应用的启动方式(带有预检的 [run]),以及如何重新构建项目自身的二进制文件([install]),这样整个循环无需离开 ducklab 即可运行。技术栈能力会根据构建元数据提出 run.command 选项(例如 Meson 可执行文件、Cargo 二进制目标、Go 主包或 Node 脚本),但必须由人来采纳。配置完成后,每次原本会通过的构建门都会在声明了 run.smoke 时执行它,否则回退到 run.command。这样,交互式应用可以声明无头冒烟测试,而无需在每次验证时打开真实 UI。run.smoke_expect = "exit" | "live" 定义成功条件:exit 要求进程在 run.smoke_timeout_s 内以状态码 0 退出;live 要求进程在整个观察窗口内持续运行。如果未指定,显式配置的 run.smoke 默认采用 exit;run.command 回退项或带 URL/健康检查端点的应用默认采用 live。验证门和 UI 始终会显示实际生效的预期,因此,一个运行 20 ms 后干净退出的交互式程序不会被误认为已成功启动的产品。主动停止时,会向整个应用进程组发送温和的终止信号,等待 run.stop_grace_s 秒(默认 3 秒),之后才强制终止;当 live 产品冒烟测试结束观察窗口时,也会使用相同的优雅终止流程。
验证门和 Shell 进程树始终会收到 DUCKLAB_RUN_ID 和 DUCKLAB_PROJECT_ID。例如,excercise-tracker 可以在 [verify].tests 中使用 DATABASE_URL=test_db_${DUCKLAB_RUN_ID},而 compose 预检可以使用 ${DUCKLAB_PROJECT_ID} 作为每次运行的项目名称。Ducklab 仅保证提供标识;资源配置和清理仍由项目负责。
添加模型
ducklab provider set openrouter --url https://openrouter.ai/api/v1 \
--key-env OPENROUTER_API_KEY
ducklab duckling set pato-sonnet --provider openrouter \
--model anthropic/claude-sonnet-4.5 \
--roles reviewer,judge --context 200000 \
--cost-in 3.0 --cost-out 15.0
ducklab duckling test pato-sonnet --prompt "say OK"
--key-env 是环境变量的名称,绝不是密钥。密钥不会写入配置、通过 API 发送或保留在 shell 历史记录中。
席位建议附有依据:来自你自己的运行记录的通过率、每次运行的成本、编码指数。由你决定。
桌面应用中的 阵容 视图用于分配席位:将小鸭从群组拖到某个模式的席位上,可进行全局分配,也可按项目分配;卡片上会显示每只小鸭的依据,席位旁则会显示引擎建议。若小鸭使用 OpenRouter,编码、智能和 Agent 能力指数取自 OpenRouter 的基准测试端点;其余指标则来自你自己的运行记录。
构建此仓库的模型阵容
这不是推荐列表,而是此仓库自身的运行记录(截至 2026-08-24,共记录 454 次运行、约 2,300 次席位分配),你可以看到各个席位实际由谁担任。任何兼容 OpenAI 的端点都可以用同样的方式安排。
| 小鸭 | 模型 | 服务提供方 | 担任席位数 | 记录所示情况 |
|---|---|---|---|---|
beelink-local | Qwen3.6-35B-A3B (Q4 GGUF) | Ryzen AI Max 395 上的 llama.cpp (Vulkan),位于桌面 | 465(此仓库中担任席位最多的小鸭) | 评审、记录员、审查者。免费。 |
luna | gpt-5.6-luna | OpenRouter | 455 | 实施主力:实测通过率为 77%,每次运行约 $0.02。 |
atom-local | Qwen3.8-27B | 局域网内 DGX Spark 上的 vLLM | 352 | 担任架构师和记录员;发布说明由它撰写。免费。 |
k3 | Kimi K3 | OpenRouter | 348 | 分类处理、架构草案、问题顾问。 |
terra | gpt-5.6-terra | OpenRouter | 303 | 能力更强的实施者,每次运行约 $0.28。 |
glm52 | GLM-5.2 | OpenRouter | 160 | 审查者席位:261 次审查中的实测通过率为 81%。 |
qwen38-max | Qwen3.8-Max | OpenRouter | 140 | 顾问(橡皮鸭)。实测通过率为 88%。 |
pato-sonnet | Claude Sonnet 4.5 | OpenRouter | 7 | 成本较高的席位,仅在更便宜的模型被实测证明达到能力上限时使用。 |
为准确起见,补充两点。第一,此处的“使用本地模型构建”是指本地席位承担判断和文档工作(评审、审查、记录、架构),而便宜的托管模型完成了大部分文字编写;大约三分之一的席位分配由本房间内的硬件完成。第二,上述通过率是根据我的运行记录测得的(ducklab duckling scorecard,Wilson 下限)。你的结果会有所不同,而这正是重点:阵容依据你自己的记录运行,而不是排行榜。
同一套机制应用于实际工作:一个委员会正在修订 ducklab 自身的规格文档,已处理 4.5M 个 token,曾因预算问题暂停一次,并请求提高预算。
五种模式
ducklab run T-001 --mode <mode>
| 模式 | 功能 |
|---|---|
solo | 一只小鸭。其他所有模式都以它为基准。 |
pair | 实施者和审查者,两者彼此去相关。它们之间还有顾问——橡皮鸭。 |
tournament | 各参赛者在隔离的工作树中完成同一任务;评审者以盲审方式选出胜者。 |
split | 架构师拆分任务;子任务并行运行;通过复制文件进行集成,不涉及模型。 |
council | 多个模型共同处理一份文档,用于需求收集、规格编写、计划制定和审查。一个模型起草,其他模型盲审并提出批评,然后由第一个模型修订。 |
不会做什么
通过代码强制执行的不变量:
- 模型永远不会决定判定结果。门禁由命令的退出代码决定。
- 通过检查的候选内容会逐字节应用;通过后不会重新生成任何内容。
- 审查者永远不会知道代码是谁写的。
- 只有能从正在合并的提交的干净检出中重现的内容才能合入。
- 拒绝会撤销本次运行写入的内容,不会影响其他人的工作。
- 每项预算(令牌数、成本、轮次、运行时长)都有明确可见的上限。
- 密钥永远不会接触项目状态。
- 引擎仅限环回访问。没有远程模式。
技能
技能是一个包含 SKILL.md 的目录——可以位于单个项目的 .ducklab/skills/ 下,也可以位于全机器通用的技能目录中,为所有项目提供服务(名称冲突时,项目级技能优先于全局技能)。仅含文档的形式没有脚本,也是默认形式:模型会读取并遵循其中的操作指南。架构师会在采纳前阅读调查指南(其提示词中包含 skill_list),顾问会在聊天中阅读,而只有实现者可以通过 skill_run 执行可运行的技能。
技能通过桌面应用管理(齿轮图标 → 技能):按作用范围徽标和验证问题列出、阅读、编辑整个 SKILL.md、附带参数运行,以及删除。小鸭在运行期间编写的技能会在桌面应用中灰显为 待接受,直到该次运行被接受——提议技能与提议代码一样,都要经过相同的门禁。
ducklab skill new house-style
ducklab skill run changelog-entry --arg summary="..."
顾问
每个项目都配备一名顾问(在名册面板上占据一个普通席位):它是“就此聊聊”入口和指南侧栏自由聊天背后的模型。它会读取代码、运行记录、面板和技能——但绝不写入——也能处理图像:粘贴故障视图的截图并提问。视觉能力经过验证,而非想当然:声明支持视觉的席位会实际收到一次图像请求进行探测,而仅支持文本的席位会用文字拒绝粘贴,而不是幻觉式地给出答案。
已入席的顾问正在回答有关其所读取仓库的问题。
通过其他模型操作 ducklab
ducklab mcp serve 会通过标准输入/输出将整个工作循环作为 MCP 服务公开:外部模型读取每项结果、决定门禁(必须提供并记录理由——决策会记为 approved_by: mcp:<client>,绝不会记作“human”)、回答问题、提交 Bug、修改计划并启动工作。引擎的 next 列表就是规则:操作人员不能执行个人无法执行的操作。
贡献
请参阅 CONTRIBUTING.md——其中说明了如何构建、测试如何守护架构、工作如何流经 ducklab 自身的循环,以及从何处开始。简而言之:
make # vet, test, build the frontend
go test ./... # 39 packages
cd frontend && npx vitest run
许可证: Apache-2.0。贡献按相同条款接受(许可证第 5 条——无需签署 CLA)。Ducklab 名称和鸭子形象归维护者所有(第 6 条)。
规范
代码实现了本仓库中的书面规范:
docs/spec/(00-VISION 至 08-DESKTOP-UI)是规范性层——涵盖愿景、不变量、协议约定和验收标准。系统如今的实际状态则记录在 .ducklab/docs/ 中——这是循环自身维护的已构建需求、规范和计划,每个版本都在人工门禁处签署。若两者有意存在差异,该差异会记录在 docs/decisions/ 中;它们之间的差异就是路线图,且对齐阶段会计算该差异。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。