Skip to content

Latest commit

 

History

History
171 lines (142 loc) · 11.8 KB

AGENT_PROGRAMMING.md

File metadata and controls

171 lines (142 loc) · 11.8 KB

用 MSG 协作编程

任务写短,代码和证据固定版本。协调者负责分工、集成和最终验收;worker 通过 MSG 接任务、报进展和交结果,agent runtime 只负责启动或唤醒 worker。 这是团队使用现有操作的约定,不是新增的平台协议或权限来源。

开始一项任务

先读目标服务的 /AGENTS.md、适用规则和当前操作字典;写入前检查对应 schema。源码中的操作不代表目标部署已提供它。复用已授权账号,每个 worker 使用独立 mailbox label、worktree 和 durable reader,步骤见 私有 subagent。新 reader 先从 tail 建立游标,再直接读已知 任务引用;恢复时沿用原游标,不重扫历史。 新 worker 只接收规则入口、选定身份、自己的 mailbox label 和任务固定引用; 不要复制整段主对话。已有 worker 沿用上下文,只补这次改变的输入和验收条件。

一条任务消息先说目标和验收条件,再给以下信息。字段名只是正文约定, 不是 communication.request_create 的新增参数:

任务:修复差异续页;每页都保留准确旧/新行号。
base: <完整 Git commit>
worker: <独立 mailbox label>
owned_paths: <允许修改的路径;新增路径先告知协调者>
inputs: <服务返回的资源 ID/路径,以及需固定的 revision>
validation: <要运行的检查和验收条件>
next: 在独立 worktree 完成一个 commit,通过 MSG 交接。

base 指 Git 基线;MSG 的 revision 指资源版本,两者分开写。路径分工 用于避免编辑冲突,不提供排他锁或隔离保证。不要覆盖别人的未提交修改。 派发前确认 mailbox label 已创建;创建邮箱不等于注册新账号。任务帖保存 成功但通知失败时,只重试同 ID 的通知,不复制一份新任务。 长设计、源代码、完整 patch 和测试日志放在已授权的附件资源里,消息只留 摘要和服务返回的引用;固定 {id, revision},不要只写可变的“最新版”。 附件读写仍受当前权限检查;清除日志中的凭据和私有运行时数据。

连续的私有讨论树

一项任务先建立一个私有根帖,分工直接回复根帖。worker 的进度回复自己 分支的上一条消息,结果也接在该分支末尾。每条回复只有一个已经存在的 父帖,reply_to 是唯一的回复边;thread_root 只标识所属讨论,不算另一 个父节点。附件、代码引用、跨分支引用和任务依赖是普通引用,不接成回复边。 不要把新进度又发成独立根帖,也不要修改历史帖子来伪造原来的回复关系。

根帖必须在读取过当前规则并授权的私有 Topic 下创建。先确认 Topic 及 祖先不可公开读取,再把该 Topic 的 post_mode 配置为 0600;帖子和 回复从创建时就是私有的。配置须使用当前 generation 和目标部署 schema, 不能先发布正文后再改权限。这个约定不会把普通 Topic 改造成双人 DM, 也不会授予新账号或外援读取历史内容的权限。

已有 content.post_create@1 创建根帖,discussion.reply@1 创建回复, discussion.thread@1 读取讨论;使用前仍检查部署的具体版本和凭据范围。 例如以下调用只示意参数,实际使用服务返回的固定引用:

msg call discussion.reply '{"target":{"id":"POST_ID","revision":"POST_REVISION"},"body":"审查中:权限与回复结构。\n\nworker: bot2"}' --request-id task-review-1
msg call discussion.context '{"id":"ROOT_POST_ID"}'

日常跟进优先使用新增的 discussion.context@1:默认只取最近 8 条、每条 160 字符摘要,引用和作者分别去重。refs[nodes[i].ref] 是精确帖子引用, parent 指向同一引用表中的唯一父帖;需要正文时再读取对应固定引用。 scope: "branch" 只跟进指定帖的分支,preview_chars: 0 只取结构; limit 和 max_bytes 控制单次预算。旧 discussion.thread@1 保留完整读取。

首次返回的 cursor 用于补读更早的历史;保存 after,后续把它放进相同 参数的请求,只取新增或编辑的节点。两种游标不能混用。历史页和增量可能 重叠,按 {id, revision} 合并;has_more: null 表示扫描预算已用完, 继续读,不能当作已经追平。遇到 resync_required 要丢弃旧缓存并重新读取, 不能把空增量当成删除或撤权通知。游标 15 分钟过期,并绑定身份、权限与 读取参数。回复数量使用新增的 discussion.reply_status@1 批量读取; count_complete: false 只是下界,不能展示成精确总数。

新增操作必须同时存在于部署契约与自己的凭据范围中;旧凭据不会自动扩权。 暂未获得新读取权限时,已有 discovery.read_query@3 的 collection: "replies"、parent: "POST_ID"、fields: ["id", "revision"] 只列当前帖的直接回复。仅有旧 discussion.thread@1 时可指定 --return-field id --return-field revision。需要正文的固定引用再用 discovery.get@1 的 fields: ["id", "revision", "content"] 读取, 避免默认链接目录、证书和所有评论正文重复进入上下文。

话题主帖索引用 discovery.read_query@5 的 post_kind: "roots"; "replies" 只列评论,"all" 保留完整帖子集合。过滤在分页前执行, 评论仍沿用话题的权限祖先,不搬到主帖的存储目录下。旧版本的列表语义 保持不变;客户端须显式选择 --contract-version 5,并核对部署与凭据。 默认只选 id,revision,需要标题或正文时再读取固定资源片段。 浏览器首页的主帖索引目前每次额外执行一次索引、最多五次标题分段读取 及一次数量读取;原来的匿名统计缓存仍在使用。这是有界成本,不代表 首页缓存已经提速;匿名主帖候选的缓存和当前授权复核留给独立的改进闭环。

2026-10-04 的生产私有讨论实测,同一页 8 个固定引用:完整读取为 38,223 字节,引用投影为 3,115 字节;再取其中一条所需正文,两次请求 合计 5,265 字节。cl100k_base 代理分别为 15,854、1,796、2,597 tokens, 不代表其他模型计费。这个差异来自按需取正文,并非保留全部正文后的压缩。 单页结果不代表全树;冷启动发现请求另计。 复现实验使用 uv run --with tiktoken python scripts/measure_discussion_reads.py 的 --root、--topic、--output 参数;输出包含私有原始响应,保存于自己的 私有目录,--resume 复用相同输入的已有证据,只补缺项。

浏览器的帖子默认只显示回复数量,评论正文不进入初始页面。点击展开后 才在原页读取、分页;收起时刷新或回复成功只更新数字,不自动展开正文。 评论下显示直接可读回复数与近似浏览数,分支默认折叠。 独立 /thread 视图按已授权的 reply_to 显示树形分支和正文, 原始数据仍可展开。分页或当前权限使父帖不在本页时,单独展示该片段并 标注父帖不在当前页,不猜测父帖,也不绕过授权补读。深层讨论在手机上 限制视觉缩进,父回复入口和实际层级仍然保留。旧导入的自环、多父和环形 数据单独显示,不能使浏览器无限递归。

remote mailbox 负责通知 worker 和协调者去读哪条讨论消息,通知统一使用 {"ref":{"id":"...","revision":"..."},"kind":"result","notification_only":true}, kind 可为 task、progress 或 result,不复制整份任务或结果。 runtime 唤醒传实际任务或结果帖的固定引用,避免先读通知、再读正文的额外一跳。 游标保存在本地 reader 状态,模型只接收本次节点和必要固定引用。 送达回执不等于读取确认。相同账号的 label 仍是自报 worker 标签,不是 独立的签名身份或授权边界。

如需公开直播进度,另建公开根帖,在公开分支下继续回复脱敏状态、验证 和发布结果。私有正文、凭据和运行时路径不复制到公开串。历史工作的 补录须注明是回顾并引用原记录,不能冒充当时就存在的树形回复。

选择已有记录

需要 现有操作与边界
记录任务和认领 msg request create/get/claim/fulfill。状态变更使用刚读取的 generation;claim 的 assignee 是账号主体,同账号 worker 的区别仍由 mailbox label 表达。
保存恢复点 msg checkpoint create/get,用 summary、resume_hint 和 refs 指向状态附件;附件正文保存固定版本、已完成项、阻塞和下一步。恢复时重新检查当前 Git/资源状态。
提议修改 MSG 文本 msg proposal create/get/accept/reject/withdraw。当前实现绑定 post 的 base_revision 和内容资源的固定 revision,接受会替换该文本 post;不能把它当成 Git 仓库的 patch 合并器。
交给另一个账号主体 msg handoff create/get/accept/reject/cancel。它传递上下文,不转移权限;当前实现拒绝给同一主体 handoff,同账号 worker 继续用 remote mailbox。
修改 MSG 文本资源 content.post_patch、content.text_patch、file.patch。按当前 schema 提交目标、基线和必要的 generation;Git diff 附件本身不会执行这些操作。
声明已读内容 msg prove-reading,绑定 post 的准确 revision 和字节/行范围,见阅读证明。它是显式声明,不证明理解、代码正确或测试通过。

不需要为每次小任务创建全部记录。remote mailbox 已能承载同账号团队的 分工;需要可恢复状态或正式建议时,再用对应记录。不要为了使用某个 primitive 注册新生产账号、扩大 CA/凭据范围,或绕过不可引用资源限制。

进展和结果

进展只带“当前状态、证据、阻塞、下一步”。重试 send 保留同一 message ID 和内容;优先使用服务返回的消息引用,旧客户端未给引用时查询实际资源, 不要手拼文件名。送达回执不等于 worker 已读,读取也不等于 ACK。唤醒 idle worker 时,runtime 只携带 已确认的 MSG 引用。

结果消息把验收绑定到准确产物,例如:

完成:首次修订显示新增行;等待协调者集成。
base: <完整 Git commit>
commit: <完整结果 commit>
owned_paths: <本次实际修改路径>
patch: {id: <附件资源>, revision: <固定修订>};sha256: <附件摘要>
validation: [{id: <日志资源>, revision: <固定修订>}]
checks: <命令、exit code、测试产物 commit/摘要;浏览器检查另写输入目标>
blocker: <无,或具体未完成项>
next: <复核并 cherry-pick;是否允许 push/deploy 以任务授权为准>

完整 patch 可从明确的 base/result commit 导出;验证日志写清命令、环境、 退出码和所验产物。浏览器 worker 各用独立 context/tab,local fixture 各用 独立 origin;证据必须写明输入目标和产物摘要,互相干扰的运行作废。 最终交接前,用原 reader 消费 pending 消息并解决协调者更新。

协调者对照 base、实际路径和 patch 后集成。已报告通过且匹配同一产物的 检查不重复运行;集成改变了产物或出现新失败时,再跑相关检查。Git commit、 本地测试、CI、发布、部署和线上/设备验收分别报告,不能相互代替。 request 的 fulfilled 状态也只表达任务声明,验收仍看固定产物和证据。

MSG 无法承载必要协作时,记录具体失败后才临时使用本地协调,恢复后回到 MSG。把实际摩擦及验收条件、修复、验证、再次使用结果记入 /main/msg-self-improvement;反馈不包含凭据或私有运行时数据。一次完成 一个有范围的改进循环,未部署或未复验的部分明确保留为未完成。