Files
2026-10-03 00:17:54 -04:00

171 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 任务书 07:agents(角色会话)
写 agents 模块:把一个任务交给 Pi 里的一个模型去做,等它交上来。它是程序里唯一直接跟 Pi 打交道的模块。
本任务书已由用户和监督者在 2026-10-03 一起确定。下面说的"监督者",是在参考项目目录里和用户讨论设计的另一个会话。
## 开工前先读
- `CLAUDE.md`:全部,尤其是"罕见情况怎么办"(什么算边界)、"不设默认值"、"测试"、"Pi 与三个角色"。
- `docs/结构.md`:
- 第二部分:agents 一行,"给模型看什么只归 materials""收不收一份提交,只在 submissions 一处判"两条边界;
- 第三部分:账本记录第 3 种(提交、私人笔记)和第 10 种(用量);
- 第六部分:只有 agents 和 plugin 能用 Pi;
- 第八部分:vocab 的 `ROLES`、`SUBMISSION_KINDS`、`SUBMISSION_SCHEMAS`、`ModelUsage`。
- `docs/方法.md`:
- 第三部分:角色与工具、私人笔记;
- 第五部分开头:替换自己刚交的提交;
- 第七部分:用量只记录,拿不到记"未知"、不记 0。
- `WORK_LOG.md`。
- 本机 Pi 0.85.1 的包和类型定义:`/home/iomgaa/.npm-global/lib/node_modules/@earendil-works/pi-coding-agent/`。只照这个版本写,不照网上别的版本的示例。
- 参考项目(只读,不要改):
- `/home/iomgaa/Projects/RivalBits/src/agents/session.ts`:开会话、核对加载了什么、收用量;
- `/home/iomgaa/Projects/RivalBits/src/agents/tools.ts`:各角色的工具、提交工具的两处特殊处理、shell 环境变量;
- `/home/iomgaa/Projects/RivalBits/src/agents/contract.ts`。
## 一、这个模块做什么
用实验员实现候选 c1 举例:
1. flow 交给 agents:
- 用哪个模型(provider、模型名、思考档位);
- 角色提示词、第一条消息(都是拼好的文字,由 materials 写);
- 工作目录、会话文件存放的目录;
- 这次要交哪种提交;
- 几个回调:模型调某个工具时,要找谁办。
2. agents 在 Pi 里开一个子会话,按角色只发该给的工具,发出第一条消息。
3. 模型干活:
- 调"查询",agents 转给回调,materials 回答;
- 调"提交",agents 转给回调,flow 经 submissions 判定、写账本,把"收下 + 编号"或"退回 + 原因"交回,agents 再原样回给模型;
- 模型每回复一次,agents 把这次的 token 用量交给回调。
4. 会话结束,agents 交回结局和这次会话的墙钟时间。
agents 不碰账本,不决定给模型看什么,也不判断交上来的东西好不好。
## 二、对外提供什么
经 `src/agents/index.ts` 导出,函数名由你定。
**跑一次会话**:一个函数,输入下面这些,全部必填,可以没有的写 `null`。
- 角色、模型(provider、模型名、思考档位)。思考档位必须写明,不接受 `null`(WORK_LOG"给 agents")。
- 角色提示词、第一条消息。
- 工作目录、会话文件目录。
- 这次要交的提交种类(vocab `SubmissionKind`)。提交工具的参数形状直接用 vocab 的 `SUBMISSION_SCHEMAS`,不另写一份。
- 交上一份被收下以后,是否就结束会话:
- 实验员、核验员是"是";
- 研究者是"否":一个研究者可能要交几份提案,或者看了回执发现被截断,要替换。
- 回调:
- 查询:给一个编号,交回文字,或者"看不到";
- 提交:给内容和"替换哪一份"(可以没有),交回"收下 + 编号"或"退回 + 原因";
- 用量:每次回复交一份 vocab 的 `ModelUsage`;
- 研究者另有"写私人笔记":笔记是账本记录,由回调去写;
- 实验员另有"轻量检查":由 flow 用 tasks 和 runner 接好,交回结果文字。
- 研究者和核验员另给"能读的目录"清单。
- 一个"停下"信号:用户中途停下研究时用。
用联合类型按角色区分:研究者才有写笔记和能读的目录,实验员才有轻量检查。写不出"实验员带笔记"这种组合,就不用写检查。
**交回的结局**:
- 交了:至少有一份被收下;
- 没交:附原因,例如最后一条回复被输出上限截断;
- 出错:模型服务报错、子会话核对不通过等,附原因;
- 被停下。
每种结局都带这次会话的墙钟时间和会话文件路径。账本记录本身不带时间,flow 把墙钟时间写进"模型调用结束"那条记录(结构.md 第三部分第 10 种)。
## 三、各角色的工具
| 角色 | 工具 |
|---|---|
| 研究者 | 查询、读代码、提交、写私人笔记 |
| 核验员 | 查询、读代码、提交 |
| 实验员 | 查询、提交、轻量检查,加上 Pi 自带的 read、write、edit、bash、grep、find、ls |
工具名、说明文字照参考项目 `tools.ts`,按下面改:
- **提交**:
- 去掉参考项目的 `slot` 参数:一个会话只处理一个对象,交给谁由 flow 定,模型不用再写一遍。
- `replaces` 只给研究者的提交种类;实现、修复、核验、消融实现、消融核验不能替换(方法.md 第五部分),这几种的工具参数里就没有这个字段。
- 同一次回复里调两次提交时,按顺序执行,照参考项目声明 `executionMode: "sequential"`。
- **读代码**:只能读给定目录里的文件、列目录,读之前按真实路径判断在不在目录里。模型正常探索时就会去 `ls` 上一层目录,看到别人还没公开的东西。单次读多少照参考项目的上限。
- **实验员的 bash**:
- 环境变量只放 PATH、HOME、LANG、LC_ALL、TMPDIR。插件进程的环境里可能有模型密钥,不能漏进实验员的 shell。参考项目还额外设了 `OMP_NUM_THREADS` 这一类变量,那是为宿主上跑轻量检查准备的,现在轻量检查在容器里跑,不用设了。
- 不把 Pi 的会话信息放进环境变量(`exposeSessionEnvironment: false`)。
## 四、要保留的几处特殊处理(都有真实证据)
1. **提交参数不是合法 JSON 时退回**:Pi 0.85.1 遇到写坏的工具参数,会悄悄只取前半截。参考项目 15-real-1 里,一份收尾就这样被截成 214 个字收下了。照参考项目的做法:监听流式输出,拼出参数原文,严格解析;解析失败就退回,告诉模型最常见的原因是正文里有没转义的半角双引号。参考项目提交 bcc422e。
2. **必填文字字段交 null 时退回**:Pi 校验之前会把 null 变成字符串 `"null"`。参考项目 14-real-1 里有两条挑刺回应就这样被收下了。照参考项目的 `nullFieldPaths`,在 Pi 校验之前查出来,报出字段位置。参考项目提交 fd73b78。
3. **形状不合格也要让 flow 知道**:Pi 在调提交工具之前就会把形状错误回给模型,提交回调收不到,flow 就记不下"退回"。所以 1、2 和 Pi 自己的形状校验发现的问题,都交给提交回调记一条退回,再照常回给模型。参考项目用的是 `rejectShape`;新项目里怎么接你定,但只用提交这一个回调,不另加一个。
4. **核对子会话实际加载了什么**(CLAUDE.md"Pi 与三个角色"最后一条):
- 会话用的是不是指定的模型,Pi 有没有悄悄换模型;
- 有没有加载扩展、技能、提示模板、`AGENTS.md` 这类上下文文件;
- 系统提示词是不是我们给的那一份,有没有被追加内容;
- 工具表是不是正好这几个,工具实现是不是我们给的。
本机 `~/.pi/agent/` 下有 extensions 和 skills,这件事没人操作也会发生。核对不通过,一个请求都不发,交回"出错"。
另外几点:
- **用量**:模型服务没报用量(输入、缓存读、缓存写都是 0)时,这次记"测不到"并附原因,不记 0。价格记"测不到",原因照参考项目:"Pi 的价格表不是账单"。
- **截断**:最后一条回复是因为输出上限停下的、而且没交,结局"没交"的原因里写明。
- **单次回复的输出上限**:不设,用 Pi 模型配置里的值。
- **重试**:模型服务出错时用 Pi 自己的重试。用的是哪些设置,在代码里写明,不另写一层重试。
## 五、不搬的东西
| 处理 | 内容 |
|---|---|
| 删除 | `shell-review.ts` 整个文件:Jev 审实验员的命令,方法里已删除 |
| 不归 agents | `prompts.ts`:读提示词文件、拼第一条消息、收尾段,归 materials;agents 只收拼好的文字 |
| 删除 | `context-window.ts`:估算材料会不会撑爆上下文;以后真出问题再在 materials 里加 |
| 删除 | `check.ts`:轻量检查由 flow 接好作为回调传进来 |
| 删除 | `redact.ts`:没查到报错信息里真带出过密钥 |
| 删除 | `notesSection`:笔记怎么放进第一条消息归 materials |
| 删除 | 配置里的"单次回复输出上限"和"服务没照上限截断"的检查 |
| 删除 | 提交工具的 `slot` 参数和对它的检查 |
| 删除 | `deps.runtime ?? ModelRuntime.create()` 这类可选参数:不设默认值,也不为测试给产品代码加参数 |
实验员会话里的 shell 命令不测 CPU,用户 2026-10-03 定(方法.md 第七部分)。
## 六、测试
**不花钱的测试**,放进 `npm test`:
- 第四部分第 1 条:用一段半角双引号没转义的参数原文喂给监听逻辑,结果是退回,原因写明。参考项目的回归测试可以借,有 slot 在前和在后两种。
- 第四部分第 2 条:必填文字字段交 null,退回并报出字段位置。
- 读代码:目录外的路径读不到,包括用 `..` 和符号链接绕出去的。
- 实验员 bash 的环境变量:插件进程里设一个假的密钥变量,shell 里看不到。
- 工具表:三种角色各自拿到的工具名正好是第三部分那几个。
**真实模型的测试**:CLAUDE.md 说真实模型链路要用真实运行来证明。另起一个命令,例如 `npm run test:pi`,不放进 `npm test`,因为要花钱、要联网。
用配置里的 glm-5.3,至少跑这几个:
- 一个研究者会话交一份简单的提交,用你选的最简单的种类,第一次故意让回调退回,模型改了再交,收下;
- 一个实验员会话在工作目录里写一个文件、跑一条 shell 命令,再交;
- 每次回复都交出了用量,会话交回了墙钟时间;
- 核对子会话加载了什么:在本机真实的 `~/.pi/agent/` 下跑一次,确认核对通过,没加载扩展和技能。
报告里贴出这几次的实际结局和用量。
**审阅**:agents 不碰账本,按原型标准,不另派子 Agent 审阅。
## 七、规模与文档
- 预期 400–600 行,不含测试。超过 600 行就停下来检查。
- 完成后更新:
- `docs/结构.md` 第八部分:补 agents 对外的函数和类型,每项一句话;
- `docs/结构.md` 第七部分:agents 的预期行数改成 400–600;
- `WORK_LOG.md`:在"已完成"补一条,删掉"给 agents:模型的思考档位必须写明"那条。
- 不新建 md 文件。
## 八、验收与报告
做完后本地提交,然后在回复里交一份报告,监督者会对着实际文件逐项核对:
1. 提交号,新增和修改的文件列表,每个文件的行数,agents 的总行数。
2. `npm run check`、`npm test`、`npm run test:pi` 的完整输出。
3. 真实模型那几次会话的结局、每次回复的用量、墙钟时间。
4. Pi 自己的重试用了什么设置。
5. 你和这份任务书、结构.md、方法.md 不一致的地方,以及为什么。
6. **规则不清楚的地方**:读 CLAUDE.md、结构.md、方法.md 时,哪里让你犹豫、哪里两份文件说法不一致、哪里你是靠猜的。没有就写"没有"。没有真实证据的可能情况,在这里提一句就够,不写进 WORK_LOG。