跳到主要内容
把模型关进笼子,这层代码长什么样

把模型关进笼子,这层代码长什么样

陈渊
陈渊

· 阅读约 3 分钟

模型写完一个 diff,到它能安全合进主分支,中间那层东西是什么?Tomas Grasl 写了篇 dev.to 帖子,说他管着一个 paywall、订阅、OAuth 都沾钱的后端团队,现在不写函数了,时间全花在定规则、配权限、设测试门槛上。第一次听到这话会想翻白眼——写不动代码了,就说自己在做更高级的事。但这几个词在代码里有具体的形状,而且可以搭出来。这篇我们动手写一个最小的 agent harness,跑一遍你就知道那层"更高级的工作"到底是什么。

先从最外面那层搭。agent 给了你一个 diff,第一件事永远是:它碰了哪些文件?这些文件它有没有资格碰?这个判断不需要智能,十行:

def permissions_check(diff, allowed_paths):
    for f in diff.files:
        if not any(f.startswith(p) for p in allowed_paths):
            return False, f"touched forbidden file: {f}"
    return True, "ok"

死板的字符串匹配,却是整个 harness 的基石。它做的是从模型的动作空间里删掉一大片动作——billing 目录、数据库连接、支付脚本,这些路径从一开始就不存在于 agent 能碰的集合里。这里要停下来把 context engineering 和 harness 的分界说清。把"不许碰 billing"写进上下文,只是让模型下一次采样时不太可能碰 billing;permissions_check 是它碰了就撞墙。前者是概率,后者是确定性。模型读了规则,采样的条件概率会变,但"碰 billing"这个动作还在动作空间里,只是不太会被采到。硬约束把它从空间里直接删掉,连"可能"都不剩。这两层是不同物种——Grasl 说 context 拦不住模型干别的,就是这个意思。

第二层是测试门槛。过了权限的 diff 进沙盒跑测试:

def gate_test(diff, repo):
    sandbox = Sandbox(repo, diff)
    result = sandbox.run_tests()
    return result.passed, result.logs

到这里 harness 还是线性流水线:权限、测试、完。但这么搞每跑一次都要人站起来看结果,agent 没有被教训的机会。真正让它转起来的是把 rejection 回填给模型,让它再采样一次。这就是 loop:

def run_loop(agent, repo, task, max_rounds=3):
    for _ in range(max_rounds):
        diff = agent.propose(repo, task)
        ok, msg = permissions_check(diff, repo.allowed_paths)
        if not ok:
            agent.feedback(msg)
            continue
        passed, logs = gate_test(diff, repo)
        if not passed:
            agent.feedback(logs)
            continue
        return diff
    return None

追一次执行。第一轮 agent 改了 paywall 的逻辑,顺带碰了 billing 文件。permissions_check 拦下来,回填一句 "touched forbidden file: billing/charge.py"。第二轮 diff 只碰 paywall,但测试挂了,失败日志塞回上下文。第三轮测试过了,loop 退出,diff 交给人审。跑一遍就看见了:所谓"规则、权限、测试门槛",在代码里就是三个串行 gate,每次 rejection 都是把模型下一次采样的空间裁小一圈。

有个流传的数字说同一个模型光换更好的 harness,编码任务从 52% 涨到 66%。乍一听像营销,但搭完上面这个最小版本之后,方向我信——模型没动,架子变密了,出来的东西就是好一截。机制上一句能解释:模型不是在推理,是在上下文里做条件采样。rejection 回填得越密,下一次采样的条件分布就越窄,命中率自然上去。不是模型变聪明了,是你替它把错路一条条堵死。Grasl 说过去一年质量提升主要来自外面那层架子而不是模型本身,跟这个观察对得上。

再往下挖一层,线性流水线是简化版。真实的 harness 是图。为什么是图?因为"测试挂了回填再 propose"本身是一条往回走的边,一旦加入人审节点,路径开始分叉,你就需要一个能回答"从 propose 到 merge 有没有一条不经过人审的路"这种问题的数据结构。Google 把 ADK 从 agent runner 重建成图引擎,点就在这。我一开始也以为 graph engineering 就是画个流程图,翻到重建 ADK

陈渊
陈渊

据守底层,挑「会用却说不出为什么」的 CS 机制从第一性原理挖到底。

查看主页 →