[Project] 3. Harness-Everything — AI 自主代码改进 Harness

全局视图
LLM 是大脑,Harness 是手脚,项目代码是被改的对象。LLM 从来没有直接碰过文件系统——它只说"我想做什么",你的代码去执行。
本质:三句话说清楚
- LLM 是发动机:把项目代码扔给大语言模型,让它分析、提改进、写代码。核心就是一个 while 循环不停问 LLM"还能怎么改"。
- 工具是手脚:LLM 不能直接读写文件,所以通过 Anthropic 的 tool_use 协议告诉你的代码"我想读这个文件"/“我想改这行代码”,你的代码去执行,再把结果喂回来。只留一个
bash工具也能跑。 - 重启是关键:Python 模块加载后就固化在内存里。LLM 改了自己的
.py文件,运行中的进程还是用旧代码。必须重启进程,改进才生效。所以有了 push → tag → CI 部署 → 重启 的闭环。
从最简单到完整系统:每一层解决一个问题
最简版本(概念上)
| |
这就能跑。但会遇到一堆问题。下面每一层都是为了解决上一层的问题:
第 1 层:代码太多,上下文放不下
问题:项目一大,所有文件塞不进 LLM 的上下文窗口。
解法:不再一次性塞所有代码,而是给 LLM 工具让它自己选择看什么。
| |
这就是 tool_use 机制 —— LLM 输出一段 JSON 说"我想调什么工具、传什么参数",你的 Python 代码去执行,把结果喂回来。LLM 从来没有直接碰过文件系统。
核心代码(harness/core/llm.py 的 call_with_tools())只有 60 行:
| |
就这些。这是整个项目最核心的 60 行。
第 2 层:LLM 会改出 bug
问题:LLM 改的代码可能有语法错误,甚至会搞坏项目。
解法:每次改完代码,自动跑 python -m py_compile 检查语法。如果出错,把错误信息告诉 LLM,让它修。
| |
这就是 harness/pipeline/hooks.py 里的 SyntaxCheckHook。
第 3 层:不知道改得好不好
问题:LLM 改了一轮,改得好吗?怎么判断?
解法:用另一次 LLM 调用来评判。而且用两个独立的评判者(互相看不到对方的评分),从不同角度打分:
- Basic evaluator:找最关键的缺陷(安全漏洞、逻辑错误、代码质量)
- Diffusion evaluator:分析二阶效应(会不会破坏其他模块?会不会让上下文膨胀?)
| |
这就是 harness/evaluation/dual_evaluator.py。本质上是让 LLM 自己评自己,但通过隔离两个评估视角来减少自我吹嘘。
第 4 层:一次改一点,多轮迭代
问题:一次性让 LLM “把所有问题都修了” 效果很差。大改容易翻车。
解法:拆成多个阶段(phase),每个阶段专注一件事,一轮一轮迭代:
| |
每个阶段内部还有内层轮次(inner rounds):生成多个方案,评估器选最好的,然后合成。
这就是 harness/pipeline/pipeline_loop.py(外层)和 harness/pipeline/phase_runner.py(阶段执行)。
第 5 层:改了不生效
问题:LLM 在 Round 3 改了 harness/core/llm.py,但运行中的进程还在用 Round 1 加载的旧代码。改了等于没改。
解法:你设计的重启闭环。
| |
这是你做的最核心的架构决策。 没有这个循环,自我优化就是假的——LLM 以为自己改了代码,但改动永远不会执行。
第 6 层:循环别断了
问题:各种情况会让循环意外停止。
| 问题 | 解法 |
|---|---|
| 代码太多上下文爆了 | 给 LLM 工具,让它按需读 |
| LLM 改出 bug | 语法检查 hook 自动验证 |
| 不知道改得好不好 | 双重评估器打分,选最高的 |
| 改了不生效 | push → tag → CI → 重启进程 |
| 连续几轮没进步 → 早停 → 没打 tag | auto_tag_at_end 强制每次退出都打 tag |
| 崩溃 3 次 systemd 放弃 | 心跳 cron 每 30 分钟检查并重启 |
| 用户推 commit 导致 push 冲突 | git pull --rebase 自动合并 |
| LLM 把部署脚本改坏了 | prompt 里的 PROTECTION 黑名单 |
| 部署了坏代码 | CI 烟测 + 回滚到 harness-last-good |
| 磁盘满 | 清理 cron 每天删旧数据 |
工具系统:不是核心,是优化
如果只有一个 bash 工具,LLM 会这么干:
| |
完全能跑。那为什么还搞 30 个专用工具?
| 用 bash | 用专用工具 | 为什么换 |
|---|---|---|
cat /etc/passwd | read_file 会拒绝 | 安全:路径检查限制在 workspace 内 |
grep 输出 10 万行 | grep_search 自动截断 | 省 token:不会撑爆上下文 |
sed 改错了没法回退 | edit_file 精确替换 | 可控:替换失败会报错,不会静默改坏 |
| LLM 编造参数 | registry 参数校验 | 容错:未知参数直接拦截 |
工具是给 LLM 戴的安全手套,不是给它的超能力。
30 个工具分类
| |
工具安全边界(_check_path)
每个文件操作工具在执行前都要过安全检查:
| |
LLM 说"读 /etc/passwd" → 被拦截。说"读 ../../etc/passwd" → realpath 解析后还是被拦截。
工具循环的成本模型
Anthropic API 是无状态的。每次调用都要重发完整对话历史。所以工具循环的每一轮都比上一轮贵:
| |
后半程每多一轮,花的钱比前半程多得多。 这就是为什么 max_tool_turns 从 30 砍到 20 能省 40% —— 砍掉的是最贵的那几轮。
缓解措施
| 机制 | 文件 | 原理 |
|---|---|---|
| 对话剪枝 | llm.py | 总字符超 150K 时,截断旧的工具结果 |
| 主动压缩 | llm.py | turn >= 6 后,旧工具结果替换为一行摘要 |
| 文件读取缓存 | llm.py | 同一循环内 read_file 结果缓存,写操作使缓存失效 |
| 代码注入预算 | phase_runner.py | 只注入最相关的 30K 字符源码,不是全部 |
DeepSeek 成本估算
| |
Pipeline 架构详解
整体结构
| |
每一层做什么
外层轮次 (pipeline_loop.py):
- 编排所有阶段的执行顺序
- 跟踪分数趋势(3 连降警告)
- 决定是否早停(patience)
- 决定是否推送(auto_push)
- 决定是否打 tag(auto_tag)
阶段执行 (phase_runner.py):
- 按关键词 + 修改时间 + 文件大小排序,注入最相关的源码
- debate 模式:LLM 只分析不改,并行跑多个方案,快
- implement 模式:LLM 用工具改文件,顺序跑(因为会改文件,不能并行)
- 评估:两个独立 LLM 调用并行打分
- 合成:从多个方案中提取最佳元素
- 验证:语法检查 + git commit
工具循环 (llm.py):
- LLM 说"我要调这个工具" → 你的代码执行 → 结果喂回来 → 循环
- 每轮都重发完整对话(API 无状态)
- 自动剪枝防止上下文爆炸
- 最多 20 轮(成本控制)
关键数据流
| |
自改进循环(服务器部署)
架构图
为什么需要重启
| |
操作手册
| 想做什么 | 怎么做 |
|---|---|
| 看实时日志 | ssh server "tail -f ~/harness-everything/logs/harness.log" |
| 看 commit 进度 | git log --oneline -20 |
| 推一个修复(不停服务) | 直接 git push,harness 会 pull --rebase 自动合并 |
| 改配置 | 改 config/pipeline_example_self_improve_server.json,push,下次部署自动生效 |
| 跑完这轮就停 | ssh server "touch ~/.config/harness/STOP_AFTER_CHUNK" |
| 恢复循环 | ssh server "systemctl --user start harness.service" |
| 立刻停 | ssh server "systemctl --user stop harness.service" |
| 彻底关闭 | stop + disable + 清 cron |
核心数据结构
| |
关键文件索引
| 文件 | 核心职责 | 一句话 |
|---|---|---|
main.py | 入口 | 解析参数,启动循环 |
harness/core/llm.py | 最核心 | 工具循环:LLM 说 → 你执行 → 反馈 → 重复 |
harness/core/config.py | 配置 | JSON → 配置对象,路径安全验证 |
harness/pipeline/pipeline_loop.py | 外层循环 | 轮次编排、push、tag、早停、关闭 |
harness/pipeline/phase_runner.py | 阶段执行 | 代码注入、内层轮次、评估、合成、hooks |
harness/evaluation/dual_evaluator.py | 质量把关 | 两个 LLM 并行打分,选最好的方案 |
harness/tools/registry.py | 工具分发 | 工具注册、参数校验、异常封装 |
harness/tools/base.py | 工具安全 | _check_path 路径边界检查 |
harness/pipeline/hooks.py | 验证 | 语法检查 + git commit(富信息) |
deploy/harness.service | 部署 | systemd 服务定义 |
.github/workflows/deploy.yml | CI/CD | tag 触发 → 烟测 → 部署 → 重启/回滚 |
deploy/heartbeat.sh | 保活 | 崩溃后自动重启 |
完整数据流:从 JSON 配置到代码提交
一段话总结
把项目代码扔给 LLM,让它分析、提改进方案、用工具改代码。用另一个 LLM 调用评判改得好不好,选最好的方案 commit。多轮迭代,每轮都比上一轮基于更好的代码。因为 Python 模块加载后就固化在内存里,所以每 10 轮重启一次进程让改进生效。重启通过 git tag 触发 GitHub Actions 自动部署实现,形成无人值守的自改进循环。工具系统(30 个文件/搜索/执行工具)本质上只是给 LLM 戴的安全手套——只留一个 bash 也能跑,但更危险、更费 token。