docs(design): 落成边界、续跑、公共 API 形状与停止语义四份决策
第 ② 阶段需求对齐与第 ③ 阶段架构的产出,代码尚未开始。 design/0001 定边界判据:三道测试(时机 / 信息 / 性质)全过才在界内, 外加「只认接缝、不认接缝后面是什么」与不夺走下游实验因子的排除条款。 design/0002 定步级续跑:不承诺原子性,承诺绝不静默丢失与不替工具猜幂等性; 先写意图再执行、结果 ID 预分配、重放策略由工具声明且默认绝不重放。 design/0003 定公共 API 形状:单一入口两个动词、五个接缝、三个伪接缝的排除理由、 分层与九条依赖规则。design/0004 定停止判定顺序、十个停止原因取值与步记录字段表。 0003 与 0004 需过 CLAUDE.md §2 人类门,已由项目负责人确认,状态转为已接受。 explanation/scope.md 与 explanation/architecture.md 是这四份决策的常青回写, 分层与模块边界的权威在 architecture.md,将来由 import-linter 契约机器断言。 migrations/ 下 dissect 是唯一的硬迁移验收,govdoc-saas 只做设计级对齐。 三道闸都过了:14 agent 对抗辩论定骨架,两轮硕士生阅读报的 30 余条已修完, Codex 对抗审查抓出的两条致命问题(提交型完成被误判成环境故障、 崩溃恢复漏一个状态)已修,修完的形状还没送 Codex 复审。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
# 迁移:dissect
|
||||
|
||||
> **更新触发点:第 2 档(同提交同改)。** 公共类型或接缝语义变更时,在同一个提交里核对
|
||||
> 本文的组件映射与需求条目;dissect 侧真正搬走某一块代码时,在同一个提交里勾掉删除清单
|
||||
> 对应行。
|
||||
>
|
||||
> **当前状态:一行代码都还没搬。** 组件映射一栏填的是接缝名而不是类名,因为 `src/` 还不
|
||||
> 存在;等第 ③ 阶段架构定了再补具体类型名。
|
||||
|
||||
dissect 是 PolyLoop 唯一做硬迁移的消费者,也是唯一有跑着的代码、有测试、有真实实验在依赖
|
||||
的消费者。它的循环在 `reference/dissect/harness/agent/`,五个模块加起来 1158 行,配套测试
|
||||
(`reference/dissect/tests/agent/`)1278 行。
|
||||
|
||||
**验收口径:把那套循环搬到 PolyLoop 上之后,dissect 现有测试全绿,且真实实验跑出来的轨迹
|
||||
与迁移前逐字段可比。** 这是 PolyLoop 唯一的硬验收标准,另外两个消费者走设计级验收
|
||||
(见 `govdoc-saas.md`)。
|
||||
|
||||
## dissect 的项目术语
|
||||
|
||||
这些词是 dissect 的,不是 PolyLoop 的——库不认识它们,本文和 dissect 侧的代码注释里会用到。
|
||||
PolyLoop 自己的词表在 `../explanation/architecture.md` 第二节。
|
||||
|
||||
**rollout**——被试 agent 在一道题上跑完的一次完整过程。它正好对应 PolyLoop 的「一次运行」,
|
||||
所以迁移之后 dissect 的一个 rollout 就是一次 `run`。
|
||||
|
||||
**三本账**——dissect 把模型调用按用途分成三类分别记账:做题的(生成)、跑验证集的(评估)、
|
||||
让模型反思改进的(反思)。分开记是它的实验纪律要求的——比较两个配置时,双方的**总花费必须
|
||||
对等**,而混在一本账里就算不清谁在哪一类上多花了。每次调用记哪一本由调用方指定,循环本身
|
||||
不选。
|
||||
|
||||
**best-of-N**——同一道题独立重做 N 次、取最好的那次。它是一个「同预算基线」:如果一个花哨
|
||||
的方法效果好,得先证明它比「同样的钱拿去重做 N 次」更好。
|
||||
|
||||
**因子**——dissect 要测的设计变量,十来个,每个能独立扫几档取值。它整个项目就是在测每个因子
|
||||
的效应量,所以任何被库写死、又会影响成绩的取值,都可能污染某个因子的测量。
|
||||
|
||||
**AppWorld**——一个交互式 benchmark(给 agent 一批模拟的 app 和一个任务,看它能不能用代码
|
||||
操作那些 app 完成)。它是 dissect 的第一个任务,dissect 的提示词格式和多代码块处理策略都跟
|
||||
它的官方实现对齐,为的是成绩可比。
|
||||
|
||||
**缓存成本校正的锚点**——供应商对「命中缓存的前缀」按折扣价计费。要事后算清一次运行真实
|
||||
花了多少,得知道哪些前缀被缓存了;而一次上下文截断会把前缀打断,之后每一步重新全价,
|
||||
原来那个推算的参照就没了。
|
||||
|
||||
## dissect 是什么,为什么它的约束特别硬
|
||||
|
||||
dissect 是一个论文项目,研究「agent 的文本自我进化为什么有效」。它把自我进化拆成十来个可以
|
||||
独立操纵的设计因子,用受控实验测每个因子的效应量。**被试 agent 的每一次运行都是一个实验
|
||||
数据点**,轨迹文件是论文的原始数据。
|
||||
|
||||
这带来一批别的项目没有的约束。轨迹里少记一个字段,事后补不回来——那次运行已经发生过了。
|
||||
一个本该被记录的失败被静默吞掉,会以「效应」的形式进入统计。所以下面「需求条目」那一节里
|
||||
的每一条,都是从这个性质推出来的,不是偏好。
|
||||
|
||||
## 删除清单
|
||||
|
||||
「继任」指这块代码的职责由 PolyLoop 承担,dissect 侧删掉。「留下」指它是 dissect 的项目
|
||||
资产,PolyLoop 只提供它要实现的接缝。
|
||||
|
||||
| dissect 侧 | 行数 | 去向 |
|
||||
|---|---|---|
|
||||
| `harness/agent/loop.py` 的 `ReActAgent._loop` 与 `_take_step` | ~130 | 继任 |
|
||||
| `harness/agent/loop.py` 的 `_make_step` | ~35 | 继任 |
|
||||
| `harness/agent/loop.py` 的 `_observe` 与 `_is_done` | ~40 | 继任(成为动作执行接缝的语义) |
|
||||
| `harness/agent/loop.py` 的 `ReActAgent.run` 装配部分 | ~75 | 留下(组装 Rollout 头、传项目 metadata) |
|
||||
| `harness/agent/memory.py` 的 `StopReason` | ~25 | 继任 |
|
||||
| `harness/agent/memory.py` 的 `Step` | ~65 | 继任(字段是库的下界,见需求条目) |
|
||||
| `harness/agent/memory.py` 的 `Rollout` | ~90 | 留下 |
|
||||
| `harness/agent/memory.py` 的 `render_messages` | ~40 | 继任 |
|
||||
| `harness/agent/context.py` 的 `ArtifactView` | ~20 | 继任(成为 Skill 注入的输入形态) |
|
||||
| `harness/agent/context.py` 的 `build_prefix` 段顺序约束 | ~15 | 继任(约束本身,不含渲染格式) |
|
||||
| `harness/agent/context.py` 的 `PromptTemplate` 与 `_split_by_role` | ~95 | 留下 |
|
||||
| `harness/agent/parser.py` 全部 | 182 | 留下 |
|
||||
| `harness/agent/config.py` 的预算字段 | ~10 | 继任 |
|
||||
| `harness/agent/config.py` 的 YAML 加载与校验 | ~140 | 留下 |
|
||||
| `harness/envs/protocol.py` 的 `Episode` | ~60 | 继任(成为动作执行接缝) |
|
||||
| `harness/envs/protocol.py` 的 `ScoredEpisode` / `TaskEnv` / `TaskItem` / `ItemScore` | ~120 | 留下 |
|
||||
|
||||
标「继任」的十行相加,净删除约 440 行,其中循环本体和轨迹结构占大头。
|
||||
|
||||
### 三个「留下」值得解释
|
||||
|
||||
**`Rollout` 留下**,因为它的二十个头字段绝大多数是项目 metadata——运行标识、进化轮次、
|
||||
题目标识、场景标识、数据划分、用途、随机种子、尝试序号这些,PolyLoop 一个都不认识。
|
||||
库只返回它自己知道的事实,不反向吸收这些字段——吸收了就等于替另外两个下游做了它们不需要
|
||||
的假设。
|
||||
|
||||
**`parser.py` 留下**,因为它是 dissect 的动作语言。它把模型输出里的 Python 代码围栏抽出来,
|
||||
处理未闭合围栏、空围栏、多围栏这些情况,并且刻意把第一个围栏之后的文字截掉(模型常在
|
||||
代码块后编造「执行结果」)。这套规则是 dissect 和 AppWorld 官方对齐的口径,不是通用的。
|
||||
PolyLoop 提供的是「把模型响应解释成动作、最终回答或无效决策」这条接缝,`parser.py` 是它的
|
||||
一个实现。
|
||||
|
||||
**`config.py` 的加载与校验留下**,因为 dissect 有一条硬纪律:所有实验参数从 YAML 读入、
|
||||
没有任何默认值、多余的键也要报错。这套「配置双模式」是它自己的规矩。PolyLoop 只接受
|
||||
已经组装好的预算值。
|
||||
|
||||
## 需求条目
|
||||
|
||||
这些是 dissect 的代码和它的实验有效性对 PolyLoop 施加的硬约束。每一条都能指出一个具体的
|
||||
失败后果。
|
||||
|
||||
**一、停止原因必须是枚举,而且要能区分「恰好在最后一步做完」与「预算耗尽」。**
|
||||
从轨迹长度反推不出来,两种情况的长度一模一样。dissect 的崩坏判据要按停机原因分层,这一档
|
||||
分不开,整批数据的分层就塌了。
|
||||
|
||||
**二、上下文超限必须显式终止,不许静默截断。** 截断是一次前缀破坏操作,会让后续每一步
|
||||
重新按全价计费,并把缓存成本校正的锚点搞丢。而且被截断的运行会表现成一批低分,看起来像
|
||||
模型能力不足。
|
||||
|
||||
**三、三类没碰环境的步也必须留痕:解析失败、模型调用失败、环境故障。** 前者消耗了一次
|
||||
模型调用,不计的话预算对等不成立。后两者的情形是钱已经花了、账已经记了,丢掉那一步会让
|
||||
账目与轨迹对不上,而且会丢掉模型在出故障那一步说了什么——排查「是环境坏了还是模型写了
|
||||
危险代码」最需要的就是这段原文。
|
||||
|
||||
**四、模型调用标识是轨迹与账目之间唯一的连接键,它可以是「没有」,但不能是空串。**
|
||||
空串是个看起来合法的键,连表时静默匹配不上;显式的「没有」至少能被筛出来。它为空的合法
|
||||
含义只有一个:调用在记账之前就失败了。
|
||||
|
||||
**五、消息段必须按变化频率从低到高装配。** 稳定前缀在前、逐题变化的在后。排错了不会报错,
|
||||
只会让供应商的 prompt cache 静默失效,而多付的幅度随注入内容的规模变化——于是缓存伪影会
|
||||
精确地伪装成因子效应。
|
||||
|
||||
**六、历史只能追加,不能改写或重排。** 理由同上:第 n 步的提示词正好是第 n-1 步加一段尾巴,
|
||||
任何中途截断、摘要、重排都会让后续每一步重新全价计费。
|
||||
|
||||
**七、模型原文与真正进入历史的文本必须分开记,环境观察同理。**
|
||||
dissect 的解析器会把第一个代码围栏之后的文字丢掉,所以「模型说了什么」和「下一轮模型看见
|
||||
什么」是两个不同的量。混用会让统计口径出错。
|
||||
|
||||
**八、可见回复与推理段的长度按字符记,不依赖上游上报。**
|
||||
实测中转网关会用本地分词器补算并整体替换用量对象,把明细一起吃掉——某次标定里 24 次调用
|
||||
的推理 token 全部没上报。字符数直接数,不受上报与否影响。
|
||||
|
||||
**九、一个 agent 定义可以被并发驱动跑不同的题,任何实例级可变状态都不允许。**
|
||||
dissect 有一档实验要在同一时刻用不同的注入内容跑同一批题,实例级状态会让并发的候选互相污染。
|
||||
|
||||
**十、循环拿不到分数。** 让被试拿得到真值等于开后门,实验当场作废。评分必须在会话关闭前由
|
||||
外层用宽接口完成,PolyLoop 从始至终只见窄接口。
|
||||
|
||||
## 组件映射
|
||||
|
||||
接缝的完整清单与各自职责在 `../explanation/architecture.md` 第九节,本表只做映射。
|
||||
|
||||
| dissect 侧 | PolyLoop 侧 | 备注 |
|
||||
|---|---|---|
|
||||
| `LedgerClient.chat` | 模型调用接缝 | dissect 的三本账记账继续作为绑定上下文的实现装配进来 |
|
||||
| `parse_code_action` | 决策解释接缝 | 三分支:动作 / 最终回答 / 无效决策 |
|
||||
| `Episode.execute` | 动作执行接缝 | dissect 侧只有「返回文本」和「抛异常」两种结果,状态恒为「已执行」 |
|
||||
| `Episode.is_done` | 动作执行接缝返回值上的完成信号字段 | **不是独立接缝**,见 architecture.md 第九节 |
|
||||
| `ArtifactView`(通道、条目标识、内容三字段) | Skill 注入的输入形态 | |
|
||||
| `build_prefix` 的段顺序约束 | **不是接缝**,是库内 `_assembly` 的纯函数 | 渲染格式留在 dissect,理由是排除条款(见 `../explanation/scope.md`) |
|
||||
| `AgentConfig` 的四个预算字段 | 请求上的预算 | |
|
||||
| `StopReason` | 停止原因 | dissect 现有六个取值是库的下界 |
|
||||
| `Step` | 逐步轨迹的一步 | dissect 现有十三个字段是库的下界 |
|
||||
|
||||
具体类型名与签名按 `../../CLAUDE.md` §2 要过人类门,还没定;定了之后本表补上英文名。
|
||||
|
||||
## 缺口登记
|
||||
|
||||
**轨迹文件的格式是反思模型的唯一输入界面,迁移后要由 dissect 自己重组。**
|
||||
`Rollout.to_jsonl` 现在把项目 metadata 头和逐步轨迹写在同一个文件里,第一行是头、后面每行
|
||||
一步。PolyLoop 只返回运行标识和步序列,所以 dissect 要自己把两者拼回那个格式,而且格式
|
||||
不能变——现有的分析代码和不变量检查器都在读它。这是一笔真实的迁移成本,不是设计缺陷。
|
||||
|
||||
**模型是正常收尾还是被输出长度上限砍断,现在拿不到。** dissect 的 `Step` 里没有这个字段,
|
||||
原因是 PolyGateway 的响应类型不透出它。这个信息有实际价值——输出被砍断时代码只写了一半,
|
||||
表现成解析失败或语法错误,会与「模型不会做」混淆。迁移后仍然拿不到,要补得给 PolyGateway
|
||||
提 PR。登记在这里是为了迁移时不要误以为是 PolyLoop 弄丢的。
|
||||
|
||||
**动作被拒绝这个概念 dissect 侧没有。** 它的环境执行只有两种结果:返回文本(代码报错也算
|
||||
正常观察)或者抛异常(环境故障)。PolyLoop 的动作执行接缝要区分「已执行」和「未执行」,
|
||||
是因为另一个消费者需要。dissect 侧的实现状态恒为「已执行」,这一点要在迁移时确认不会改变
|
||||
它的步数统计口径。
|
||||
|
||||
### 由 `../design/0003-public-api-shape.md` 新产生的四项成本
|
||||
|
||||
**每个 episode 要写一个包装类。** dissect 现有的 `Episode` 协议与 PolyLoop 的动作执行接缝
|
||||
**不构成结构化子类型**:那边是 `execute(action: str) -> str` 与 `is_done() -> bool`,
|
||||
而库这边要的是一个返回结构体(状态、观察、是否合成、完成信号、截断字符数)的方法。
|
||||
所以不能直接把 `Episode` 传进去,每个 benchmark 的适配器要多一层薄包装。
|
||||
|
||||
**续跑路径要改。** dissect 的 runner 现在把「没有结果行的尝试」重新排进待办,用完全相同
|
||||
的六维主键第二次调用循环。迁移后这条路要改成调「接着跑」而不是「再跑一次」——按
|
||||
`../design/0003` 的前置条件,用同一个运行标识第二次调「跑一次」会直接报错。
|
||||
|
||||
**要选一个存储实现并给它一个目录。** 存储接缝是必填的,不传就装配不起来。dissect 如果暂时
|
||||
不要恢复能力,要显式装配那个明确命名的、不提供恢复的内存实现——「我不要恢复」是一次
|
||||
看得见的选择,不是一个可以忘记传的参数。
|
||||
|
||||
**模型绑定要从关键字参数还原。** dissect 现在给每次调用传五个关键字参数(账本、轮次、
|
||||
阶段、题目、尝试序号)。库这边接的是一个字符串映射,所以适配器要做一次还原(把账本那个
|
||||
字符串转回枚举、把轮次和尝试序号转回整数)。这是适配器该干的活,收益是这五维能原样进
|
||||
参数快照。
|
||||
@@ -0,0 +1,152 @@
|
||||
# 设计对齐:GovDoc-SaaS
|
||||
|
||||
> **更新触发点:第 2 档(同提交同改)。** 公共类型或接缝语义变更时,在同一个提交里核对
|
||||
> 本文的需求条目还能不能被承载;GovDoc 侧的 agent 方案定下来时,在同一个提交里更新需求
|
||||
> 来源与缺口清单。
|
||||
>
|
||||
> **当前状态:这不是一份迁移清单,是一份需求清单。** GovDoc-SaaS 的 agent 部分还没有可迁移
|
||||
> 的东西,本文的作用是防止 PolyLoop 只按 dissect 一家的形状长。
|
||||
|
||||
PolyLoop 只有 dissect 一个硬消费者(见 `dissect.md`)。只对着一个消费者做,做出来的库会长成
|
||||
那个消费者的形状,而这件事在完成之前看不出来。GovDoc 这边不做迁移验收,改做**设计级验收**:
|
||||
把它真实需要的东西列成条目,逐条问 PolyLoop 能不能承载,答不上来的登记成缺口。
|
||||
|
||||
## GovDoc 的项目术语
|
||||
|
||||
这些词是 GovDoc 的,不是 PolyLoop 的。PolyLoop 自己的词表在
|
||||
`../explanation/architecture.md` 第二节。
|
||||
|
||||
**PES**——Plan-Execute-Summarize,「计划—执行—总结」三阶段。GovDoc 的一个任务被切成这三段,
|
||||
每段跑一次 agent,段与段之间靠工作区里的文件交换状态:计划阶段产出一份计划文件,执行阶段
|
||||
读它并产出一批发现,总结阶段读那批发现产出最终结果。
|
||||
|
||||
**工作区**——一次 agent 运行独占的沙箱目录。里面有这次运行能看的数据、能写的输出、以及
|
||||
运行结束后要留下的产物。它由 GovDoc 侧建立、快照和清理,PolyLoop 不认识它。
|
||||
|
||||
**必须产出的文件**——每个阶段声明几个文件路径,跑完之后这些文件必须存在,否则这个阶段算
|
||||
失败。这是 GovDoc 判断「阶段目标达成没有」的方式,按 `../explanation/scope.md` 它在界外。
|
||||
|
||||
**提交型完成**——agent 靠调用一个特定的工具来宣布自己做完了,环境状态不发生任何变化。它跟
|
||||
另一种完成方式(环境自己报告「这道题结束了」)是两回事,两者都要能表达。
|
||||
|
||||
## 为什么这里没有迁移清单
|
||||
|
||||
GovDoc 有两套 agent,形态完全不同,而两套都不能直接当迁移对象。
|
||||
|
||||
**GovDoc-Editor 是今天跑在生产上的系统。** 它的 agent 循环归 Claude Agent SDK,业务层明令
|
||||
禁止 import 那个 SDK,中间隔着一层叫 Scrivai 的编排框架。Scrivai 做的是「计划—执行—总结」
|
||||
三阶段编排:每个阶段跑一次 agent,阶段之间通过工作区里的文件交换状态,每个阶段有自己的
|
||||
步数上限、可用工具集和必须产出的文件清单。
|
||||
|
||||
按 `../explanation/scope.md` 的判据,**阶段编排在 PolyLoop 界外**。所以 GovDoc-Editor 不是
|
||||
迁移对象,它是需求来源——它告诉我们「阶段编排要建在执行内核之上」时,对执行内核提了哪些
|
||||
要求。
|
||||
|
||||
**GovDoc-SaaS 是 GovDoc-Editor 的重构版**,它的 `packages/docagent-core/` 正在把上面那套东西
|
||||
换成自建:`workflow/phase.py` 的注释写着「三阶段泛化为 N 阶段」,`PhasedWorkflow` 自己接
|
||||
`AgentLoop`,Claude Agent SDK 不见了。
|
||||
|
||||
但那份 `AgentLoop` 不能当迁移基准。文件头第一句写着它「完整保留」某个项目的循环逻辑,而
|
||||
那个项目已经废弃;更要紧的是,`grep "from docagent_core" src/` 在业务层零匹配——它有测试,
|
||||
但从来没被任何业务代码调用过。**拿一份没有调用方、且血统来自废弃项目的代码当验收标准,
|
||||
验的是那个废弃项目的行为。**
|
||||
|
||||
所以需求来源是两处:GovDoc-Editor 的生产实践,以及 `docagent-core` 作为一次已有的抽象尝试
|
||||
所暴露的形状。
|
||||
|
||||
## 需求条目
|
||||
|
||||
**一、一份 agent 定义要能按次运行配不同的预算。**
|
||||
生产上那份审核 agent 的三个阶段分别是 50、50、16 步。如果预算只能挂在定义上,就得为三个
|
||||
阶段建三份定义,而它们除了预算之外完全一样。
|
||||
|
||||
**二、要能按次运行收窄工具集。**
|
||||
三个阶段可见的工具不同:计划阶段有读、写、搜索、执行;执行阶段多一个技能调用;总结阶段
|
||||
只有读、写、通配查找。这条和「模型可见的 schema、存在性校验、分发三者同源」不冲突——
|
||||
每次运行构造一个窄的注册表就行。
|
||||
|
||||
**三、工具调用协议是 JSON,而且要容错。**
|
||||
模型输出的是一段 JSON,含反思、计划、动作三部分,动作里有工具名和参数。实际遇到的两种
|
||||
偏差都得处理:整段 JSON 被包在代码围栏里,以及参数被平铺在动作层而没有嵌套。后者的收拢
|
||||
要保守——只有当除工具名之外确实存在平铺参数时才收拢,否则会把「缺参数」这个错误静默升级
|
||||
成「参数为空但合法」。
|
||||
|
||||
**四、无效的工具调用不计有效步,但要计入总迭代上限。**
|
||||
不计有效步是因为它没真的做事;计入总迭代是因为模型可能一直调用不存在的工具,没有这个上界
|
||||
循环不会停。这两个计数必须分开,混成一个就防不住无限循环。
|
||||
|
||||
**五、提交型完成:某个特定工具被调用即视为完成。**
|
||||
生产上那份 agent 靠调用一个提交工具来结束,环境状态不发生变化。这和另一种完成方式——环境
|
||||
自己报告「这道题结束了」——是两回事,两者都要能表达。
|
||||
|
||||
**六、审计事件必须覆盖模型步与工具调用,且事件发送失败不能中断循环。**
|
||||
业务侧有一条硬纪律:agent 的原始输出、修复后的输出、恢复来源全程留痕,禁止静默修复。所以
|
||||
「模型原文」和「解析/修复之后的结果」要分别可见。
|
||||
|
||||
**七、取消要能穿透。** 长任务的所有权以数据库租约为准,取消信号从外部进来之后必须能打断
|
||||
正在进行的模型调用与工具执行,并释放在途资源。
|
||||
|
||||
**八、工作区是一个有边界的端口。**
|
||||
生产实现把它抽象成六个操作:按行读、原子写、grep、列文件、判断存在、算校验和;所有路径都
|
||||
是工作区内的相对路径,越界要抛异常。按 `../explanation/scope.md`,工作区的建立、快照与清理
|
||||
在界外,但**提示词的构造要能读工作区**(阶段的提示词是从上一阶段的产物生成的),这一点见
|
||||
下面的缺口。
|
||||
|
||||
**九、阶段级续跑要能判断上一次运行是不是真的跑完了。**
|
||||
阶段级续跑本身在界外,但它依赖一个界内的事实:运行结果必须是可持久化、可读回、可判定的
|
||||
结构,而且「这次运行结束了」要由库自己写下来。这条已经落进 `../design/0002-step-level-resume.md`。
|
||||
|
||||
## 已经有答案的(原缺口登记,`0003` 回答)
|
||||
|
||||
这三条曾经登记为「PolyLoop 答不上来」,`../design/0003-public-api-shape.md` 已经定了。
|
||||
留在这里是因为它们是 GovDoc 侧真实的接入约束,迁移时要照着改代码。
|
||||
|
||||
**预算挂在请求上,不是定义上。** 一份定义可以被三个阶段共用,各传一份预算,不会长出三份
|
||||
除预算外完全相同的定义。**不设「定义给默认值、请求可覆盖」**——两处取值意味着「这次到底
|
||||
跑的什么设置」要对照两个地方才答得出来。
|
||||
|
||||
**提示词的内容由项目读好了传进来,库不接触工作区。** 上下文装配不是接缝,所以「让库接受
|
||||
一个项目对象再转交」这条路不存在。每个阶段的提示词构造器仍然在 GovDoc 侧,它照常读工作区,
|
||||
只是把读出来的结果作为上下文的一段交给库。库只管段的顺序和注入槽的位置。
|
||||
|
||||
**两层重试都不归本库,预算口径按库能观测的量算。** 一次模型调用之内的重试与换源归
|
||||
PolyGateway,整次运行的重试归 GovDoc 自己的编排层;中间那一层(库在模型调用失败后自己再
|
||||
调一次)举不出两个消费者,不做。**PolyGateway 内部换源重试不消耗任何预算**,因为库数的是
|
||||
一次模型调用接缝的调用——口径必须是库自己能观测的量,否则换个后端口径就变了。
|
||||
|
||||
**提交型完成靠工具注册表上的完成标记。** 注册表知道哪个工具一旦被成功执行就代表目标达成,
|
||||
库在动作结算之后查它。GovDoc 的动作执行接缝完成信号恒为「未完成」——它的环境状态不因为
|
||||
提交而改变,所以环境这条通路对它永远不成立,收尾走的是完成标记这条。
|
||||
|
||||
这一条曾经差点出大事:`0003`/`0004` 的初稿把完成信号定成「布尔或空、空表示取不到」,并且
|
||||
规定「取不到即环境故障」。照那个写法,GovDoc 的**每一次运行都会在第一步撞环境故障终止**。
|
||||
Codex 对抗审查抓出来了,修法见 `../design/0004-stopping-and-step-record.md` 决策三 G 档。
|
||||
|
||||
这一条对 GovDoc 是实质变化:`docagent-core` 现有的那层步级重试(超时与网络异常,退避
|
||||
20 秒和 40 秒)迁移后没有对应位置。它当初存在的理由是「治理层的某些异常类型如果没在装配点
|
||||
显式注入,重试对它们就静默失效」——那是 PolyGateway 装配的问题,要在装配点解决,不是在
|
||||
Agent 层补一层。
|
||||
|
||||
## 缺口登记
|
||||
|
||||
这些是 PolyLoop 现在答不上来的问题。答不上来不等于设计错了,但每一条都得有明确结论——
|
||||
哪怕结论是「不支持,理由是什么」。
|
||||
|
||||
**审计出口与事件流的关系。** 生产实现的审计出口是一个「发一条带类型和载荷的事件」的接口,
|
||||
而 PolyLoop 的方向是「观察走事件流、干预走具名回调」。事件流能不能覆盖审计的需求,取决于
|
||||
事件里带不带原始响应——审计纪律要求原始输出和修复后的输出都留痕。事件集与回调清单要独立
|
||||
成一份 design doc,这条在那时候定。
|
||||
|
||||
**动作被拒绝之后的重复行为。** 生产实现对无效工具调用不设单独的重试上限,靠总迭代上界收敛。
|
||||
PolyLoop 目前的想法一致,但要确认这在长阶段(50 步)上够不够——模型反复调用同一个不存在的
|
||||
工具,会把整个步数预算烧光而不产出任何东西,而这在轨迹上表现成「预算耗尽」,与真的做不完
|
||||
混在一起。
|
||||
|
||||
## CHSAnalyzer 为什么没有对应的文档
|
||||
|
||||
CHSAnalyzer 是第三个已知消费者,定位是远期兼容。它的架构文档第十四节明确写了评估层和诊断层
|
||||
现在是空的,而且「不为它们预留任何结构」,理由是「留了位置整个系统会立刻复杂一个量级,
|
||||
而现在还不知道它们真正需要什么形状——猜出来的接缝比没有接缝更难拆」。
|
||||
|
||||
**没有需求可以登记,所以不建那份文档。** 等它的方案定下来再建。这一段写在这里,是为了让
|
||||
读者知道这是一个决定而不是遗漏。
|
||||
Reference in New Issue
Block a user