跳到正文
Tessl Blog· Ian Johnson·· 5 天前AI 评分50

如何只用自己拥有的文件约束编码智能体

How to constrain a coding agent using only files you own

AI 导读

用一份 ticket 测试编码智能体时,无约束运行只通过 8 项检查中的 1 项,而配置了 charter 的运行通过 8/8。charter 由四个杠杆组成:CLAUDE.md 指令、.claude/rules/ 路径规则、.claude/commands/ 等能力配置,以及唯一能拒绝写入的 .claude/hooks/。作者指出,绿色评分不等于正确变更,语义权限检查仍需人工审查。

正文 · AI 翻译

我给一个编码代理一张工单,没给它任何章程。这个仓库像真实仓库那样积了三代。一个 2026 年的领域层把一切都做对了,而一个 2023 年的上帝文件加上一个谁都不许碰的冻结的 2021 年模块,数量是它的十倍。

代理从未打开正确的代码。它去了上帝文件,调用 random.randint 掷骰子,并在没有种子的情况下序列化总数。它通过一个存在且可用的辅助函数重写了一条日志条目,还两次伸手进了那个冻结模块。

得分:八项检查中过了一项。那个仓库里没有任何东西是坏的,代理照抄了它眼前的代码。所有本可以拦住它的东西,都是我还没写出来的文件。

你真正拥有的东西

我从事 harness 与章程工程:运行代理的引擎,以及它最先读取的规则。这就是《Harness Engineering》这本书的主题,也是我的开源章程框架工具 Keystone 的主题。十一月我将在 AIDevCon 主持一场九十分钟的工作坊,让一屋子工程师写这些文件,并衡量每一个文件带来了什么。

写一个 CLAUDE.md 的建议如今到处都是,这是好建议,但那个文件只是四个杠杆之一。另外三个就在同一个仓库里、就在它旁边,位于你已经拥有的路径下,而许多团队并没有把它们交付到位。

当代理不同意章程时,章程的哪一部分能站得住,每一部分又买到了什么?这两个问题都能在一台笔记本上回答,下面就是我在同一张工单上回答三次所得到的结果。

你的章程是四个杠杆,不是一个文件

常规版本:把你的标准写进 CLAUDE.md。

每个人都会写那个杠杆,而另外三个就在它旁边。四个都是文件,四个都是你的,四个都位于代理在写下一行之前会读取的路径下:

  1. 指令是 CLAUDE.md。这个仓库现在是什么,用现在时。
  2. 约束是 .claude/rules/,按路径限定范围,每条规则都带着导致它的那次事故的日期。
  3. 能力是 .claude/commands/、.claude/skills/、.claude/agents/、.claude/settings.json 和 .mcp.json。代理能够伸手去拿什么,以及它被允许运行什么。
  4. 检查是 .claude/hooks/。什么会拒绝它。
The four charter mechanisms

左边的三个杠杆改变代理读取什么、伸手去拿什么。右边那个是唯一能拒绝的。

能力杠杆是通常没有被设计的那一半。它上面有四级:一条命令、一项技能、一个子代理、一个 MCP 服务器。家族测试把它们区分开。把那个东西拿掉,如果代理能做的事变少了,它就是一项能力。如果代理做同样的工作、只是做得更差,那你写了一条规则,并把它打扮成了技能。

授权列表也属于那个杠杆,而且它是对你本意的有损翻译。一个指向 make 目标的允许列表,和一个指向命令字符串的允许列表,说的不是一回事。

这对你意味着什么:在你自己的仓库上运行 ls -R .claude/。如果答案是一个设置文件、别的什么都没有,那你就拥有四个杠杆,却只交付了一个。

两个杠杆说服。一个拒绝。

指令会被读取。按路径限定的规则会被读取。任何被读取的东西都可以被争辩,而代理会说服自己放弃一条被周围代码所否定的规则。能力杠杆两边都不争辩,因为它改变的是代理能够伸手拿到什么,而不是它相信什么。

钩子不会被读取。它是一个在回合内运行并返回拒绝的谓词。你没法跟它谈判。

四十行 Python 挂在那道差异的两端。一端是一个 PreToolUse 钩子,用来阻止写入;另一端是 make gate,也就是 CI 运行的东西。一个谓词,两个挂载点,没有重复逻辑。这种形态正是它能传播开来的原因。

那些能说服人的杠杆仍然能买到很多东西,但有一个条件:规则必须与某些东西相冲突。你的代码库已经遵循的规则不约束任何东西。智能体本来就会模仿着得出同样的答案,所以你无法分辨是哪一个让你走到了那里。

所以要把规则写在规则与代码相冲突的地方。在工作坊仓库里,一个模块从会话中推导请求权限,另一个则直接从请求体中读取它。两种模式都活着,而且都能用,所以章程必须做出选择。它的职责就是说明该复制这两种真实做法中的哪一种,而大多数改造都是如此。

同一张工单跑了三次。裸跑改了 3 个文件,加了 49 行,8 项检查过了 1 项。带章程的跑改了 3 个文件,加了 102 行,8 项检查全过。更大的差异反而是更好的那个,这一点值得在有人开始为改动行数做优化之前大声说出来。

这对你意味着什么:你最不愿看到被破坏的那条规则,应该放在那个会拒绝的杠杆上。

你的所有权止于何处

一个程序可以解决九项检查中的八项。差异是否留在范围内?每个随机种子是否都已设定?日志是追加而不是重写?另外五项问的是同一类问题,脚本对每一个都能不带意见地作答。

第九项是 authority.semantic,它问的是访问检查对这个功能是否正确。没有谓词能解决这个问题,所以评分器打印一个问号然后等待。

Automated checks and reviewer judgment

两种判定读的是同一份差异。只有钩子能在写入发生之前阻止它。只有审查者能解决第九项检查。

在八项全过的那次运行中,一个审查者子智能体读了差异并填上了第九列:

  • 处理程序以 session.is_dungeon_master() 为门控,所以它会拒绝一个请求保存自己角色的玩家。验收标准 4 允许这样做。
  • 谁魅惑了一个战斗单位,谁就在持续时间内控制它。处理程序没有考虑这一点,工单对此也保持沉默,所以智能体本应提出这个问题,而不是选一个答案。
  • 测试通过一个模块全局变量来设置它的请求,所以它只走了拒绝路径。

八项绿色检查。三个真实缺陷,其中一个是权限漏洞。

绿色的记分卡不等于正确的改动。它只是一个没有机械可检测问题的改动,而这段距离正是你的审查注意力该放的地方。

这在软件生命周期中的位置

章程是约束中你完全拥有的那部分。它住在仓库里,受版本控制,随克隆一起到达。四个文件就能在每一台检出代码的机器上改变智能体的行为。

我见过的许多测试框架和工厂图里都有一个标着质量门的方框,而那个方框掩盖了这个问题:你的标准中哪些能成为门?在一个仓库的一张工单上,我得到的是八和一,但我选的规则就是为了让效果可见,所以请把那个比例当作演示。你的会不一样。

方向才是我会围绕它做规划的东西。随着智能体编写更多代码,通过那道门的量会攀升,而不可判定那一列并不会随之缩小。所以当一条规则超出了章程的范围,就说出它上面那一级然后停下,因为一个 markdown 文件装不下智能体能够到并删除的东西。

规则不是静态的。它们有生命周期,会从部落知识迁移到章程,再到约束框架,最后到工厂。具体在哪取决于关注点。

简而言之

拿一张工单。在完全没有章程的情况下,针对你的代码库运行它,并用你事先写好的检查项对结果打分。那次运行就是事故,之后你写的每一条规则都应该指向它。然后一次添加一个杠杆,重新运行同一张工单,并指出哪个杠杆改变了哪个检查项。

你会发现两件事。你的少数规则没有约束任何东西,因为代码已经与它们一致了。而且至少有一条你在意的规则没有任何机器能检查,这使它成为值得人类花时间的那一条。

要点:约束编码代理的每一条限制都是你拥有的一个文件。唯一的问题是,其中哪些你费心写了下来。

来源:Tessl Blog · tessl.io