Files
PolyLoop/research-wiki/explanation/scope.md
T
iomgaa 4f8812fa82 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>
2026-08-09 10:48:33 -04:00

9.5 KiB
Raw Blame History

PolyLoop 管什么、不管什么

更新触发点:第 2 档(同提交同改)。 三种事情发生时,本文在同一个提交里改:新消费者 接入并带来了新的裁决;某项因为凑齐了两个真实消费者而从界外移进界内;某项被实现时发现 判据用错了。判据本身的变更走人类门,并新写一份 design doc 标 supersedes。

当前状态:本文描述的是已经定下来的边界,src/ 还不存在。 界内的东西大部分还没有代码。 这段说明在库本体落地后删掉。

一个新项目来问「我要的这个东西,PolyLoop 管不管」,答案在这里。

这个问题不好答,因为「agent 框架」这个词罩着两种不同的东西。一种是执行内核:持有 「模型做决策 → 执行动作 → 写回观察 → 再决策」这个循环,管预算、停止判定、逐步轨迹、取消。 另一种是阶段编排:把一个大任务切成几个固定阶段,每个阶段跑一次执行内核,阶段之间交换 状态,并检查每个阶段的产物合不合格。

PolyLoop 是前者。 阶段编排留在项目侧。这个划分和它的理由记在 ../design/0001-scope-boundary.md

判据

一句话是:管「一个目标之内」的事,不管「多个目标之间」的事。 一次 run 等于一个目标。

这句话自己判不了案,配三条判据,三条全中才在界内

  1. 时机 —— 它发生在一次运行开始之后、返回之前。
  2. 信息 —— 它不需要知道这次运行之外的任何事:上一次运行的结果、下一次运行是什么、 此刻还有几次运行在跑、这个目标是谁派下来的。
  3. 性质 —— 它回答「怎么跑」,不回答「跑什么」「为什么跑」「跑得好不好」。

再配一条补充规则:只认接缝,不认接缝后面是什么。 检索、沙箱、容器、工作区目录,库一概 不知道——它只知道有一个环境接口,交给它一个动作、拿回一段观察。没有这条,第一条判据会被 滥用成「检索发生在一次运行之内,所以检索该进库」。

补充规则有个更好用的等价说法:能表达成「环境提供的一个动作」的,就在界外。 检索能, 沙箱执行能,写文件能,调外部 API 能。而「记这一步花了多少预算」不能——没有任何环境会提供 这么个动作,那是库自己的算法。

排除条款:不夺走下游要调的那个旋钮

三条判据都过、补充规则也不适用,仍然可能不该进库——如果它是下游要扫的实验因子或要调的 业务刺激。

Skill 的渲染形态是这一条唯一的现役例子。把注入内容用什么格式贴进提示词,按三条判据全过: 它发生在一次运行之内、不需要知道运行之外的事、回答的是「怎么跑」。但 dissect 要扫这个因子 来测它的效应量。库一旦把格式写死,那段文本就成了库定的实验刺激,而且它逃出了项目的 参数快照——扫不动了。

判据是:这个取值变一下,下游的实验结论或业务结果会不会跟着变? 会,就留在项目侧, 哪怕它长得再像库该管的事。

当前裁决

「已落地」一栏区分两件不同的事:某项归本库管(在界内),和本库现在真的做了它。凡是标 「未落地」的,第一版不实现、不设接缝、不预留字段——归属不变,只是还没到做的时候。

界内

事项 已落地 说明
主循环:决策、执行、观察、再决策 待落地 结构已定,代码未写
预算与停止判定 待落地 判定顺序与预算计数语义还没定,见第十四节缺口
停止原因的分类与取值 待落地 取值还没定
逐步轨迹 待落地 字段清单还没定
取消传播 待落地
决策解释接缝 待落地 把模型回复解释成动作 / 最终回答 / 无效决策
动作执行接缝 待落地 含完成信号查询
工具的注册、模型可见 schema 生成、存在性与参数校验、分发 待落地 四者必须同源
通用工具的现成实现 待落地 可选装配;读写文件、grep、跑 shell 这类
Skill 的注入与注入顺序快照 待落地 只注入和记录
崩溃后「接着跑」 待落地 ../design/0002-step-level-resume.md
意图日志与恢复状态判定 待落地 同上
运行结果的持久化存储端口 待落地 同上
最终输出对项目所给 schema 的校验,以及校验失败后的有界重问 不做 凑不齐两个真实消费者
上下文压缩 不做 同上。做的时候必须是显式策略,基线关闭

后两项标「不做」而不是「待落地」,区别在于前面那些只是排期问题,这两项是准入问题—— 它们要等第二个真实消费者出现,见下一节。

界外

事项 栽在哪一条
评分、业务正确性判定 性质
阶段编排 信息、性质
阶段产物是否齐全、合不合格 性质
崩溃后「判断该不该续、历史存哪、怎么读回来」 时机、信息
阶段级的跳过与续跑 时机、信息
并行调度、批量运行、结果汇总 时机、信息
跨运行的长期记忆 信息
Skill 的生成、评测、进化、审批、激活 时机、信息、性质
Skill 的选择规则(这次注入哪几条) 信息
Skill 的渲染形态 排除条款
上下文的渲染格式 排除条款
检索、文本切分、向量库、重排 接缝后面
工作区目录的建立、快照与清理 接缝后面
沙箱与容器的生命周期 接缝后面
业务工具的实现 接缝后面
任务队列、租约、心跳、多租户规则 时机、信息
模型调用的重试、换源、限流、熔断、缓存、遥测 归 PolyGateway
一次模型调用失败之后要不要再来一次 见下

最后一条要说清楚,因为它容易被读成「库要做一层重试」。 重试分两个尺度,两个都不归 本库:一次模型调用之内的重试与换源归 PolyGateway整次运行失败之后要不要重跑, 归下游的 runner。中间那一层——「这一步的模型调用失败了,库自己再调一次」——举不出两个 消费者,所以不做。预算口径一并定死:PolyGateway 内部换源重试不消耗任何预算,因为库数的 是一次模型调用接缝的调用,口径必须是库自己能观测的量。

三个容易判错的地方

「校验」这个词底下压着三件不同的事,两件在界内。 模型吐的文本能不能解析成一个动作—— 那是决策解释接缝,界内。动作的参数合不合法、这个工具存不存在——那是动作执行接缝裁决、 库定语义,界内。一次运行结束后文件系统上的产物齐不齐、合不合格——那是「跑得好不好」,界外。

接缝的完整清单、各自的职责、以及为什么恰好是这几个,在 architecture.md 第九节。本文只 判归属,不定形状。

工具调用在界内,某个具体工具的实现不一定。 模型说要调 search,库解析它、校验它、分发 它、把结果变成观察,这一整套在界内。search 本身怎么实现在界外,由项目注册进来。库自带 的通用工具是个例外,判据是不含业务概念且有两个以上消费者需要——读写文件过得了,检索过不了。

恢复要拆成两件事。 拿着一段已有历史从中间接着跑,界内。判断该不该续、历史存在哪、怎么 读回来,界外。

界外的东西怎么进来

先有两个真实消费者,并且它们的语义稳定一致。「真实消费者」指有跑着的代码或已冻结的 设计,不包括「将来可能需要」。

在满足这个条件之前,界外的需求只登记不实现,也不为它预留结构。不预留是刻意的:预留一个 接缝要占公共类型的位置、受兼容性约束、还要有人维护它的测试,而它守护的那个形状是猜出来的, 等真需求到了多半不合用,那时候拆比重写贵。

登记的地方是 ../migrations/ 下对应那份。

边界之外,库仍然要为它们留出余地

不做某件事,不等于可以挡住别人做。三条已知的、由界外功能反向施加给界内设计的约束:

阶段编排要给每次运行配不同的预算。 一份 agent 定义在三个阶段里可能要用三个不同的步数 上限,所以预算不能只挂在定义上。

阶段编排要按阶段收窄工具集。 每个阶段可见的工具不同,所以工具集要能按次运行装配,而 不是固定在定义里。这条和「四者同源」不冲突:每次运行构造一个窄的注册表就行。

阶段级续跑要能判断上一次运行是不是真的跑完了。 所以运行结果必须是可持久化、可读回、 可判定的结构,而且「结束了」这个事实要由库自己写下来,不能跨两个存储。这条已经落进 ../design/0002-step-level-resume.md

已知没有机器兜底的部分

pyproject.toml 的 import-linter 契约将来能守住其中一部分——不反向 import 下游、业务概念 不进内核。但「这个机制该不该进库」这类判断守不住,只能靠 ../../CLAUDE.md §3 的评审。 那些契约现在还没写,要等 src/ 落地。