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

12 KiB
Raw Permalink Blame History

任务书 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。