# 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/` 落地。