Files
PolyLoop/README.md
T
iomgaa c4e5732587 docs: 回写全仓库对契约套件的指向,以及 README、架构与 CHANGELOG
套件从 tests/contract/ 搬进 polyloop.testing 之后,全仓库 28 处引用要重新指过。修了 12 处,
其余在 design/(只增不改)与 scratch/(由人清理)里。

**CLAUDE.md 改了四处事实**:§0 权威表里行为契约的权威、§0 那句依赖规则的条数、§5 目录树与
模块数、§1.8 那句「谁断言公共 Protocol 的签名」。§1 的其余硬约束与 §2 的人类门一条没动。

**architecture.md**:分层图第 4 层加一格,装配层从三个变四个;代码地图加一行;第十节按代码
逐项重写——那笔「工具段渲染样式」的欠账**没有被数字对上盖掉**,加了 fingerprints 之后请求
的字段数恰好还是十一,而组成已经换过,所以那一节正面写着它仍然欠着;新增第十条依赖规则
(pytest 只在 testing 那个 extra 里,别处 import 它会让下游的生产环境一 import 本库就
ModuleNotFoundError),带静态与运行时两半;删掉「src/ 下一行代码都没有」那段过期状态说明;
决策索引补齐 0008 到 0016,其中四行原描述说的不是那份文档真正定的东西。

**migrations/dissect.md** 那笔「内存实现不存在」的欠账还掉了。

**压测那边**三条测试守的是一条已经撤销的公共契约,改名并写清它们现在守的是场景自己的选择。
AppWorld 那处刻意的偏离(不补三个反引号)留着不恢复——那条路径要模型输出被 stop 序列截断才
触发,而压测不配 stop 序列,恢复的收益不抵重跑一次压测的成本。但注释的理由改对了:它现在是
一笔有出处的欠账,不是一个决定。

CHANGELOG 攒在「未发布」段,版本号不提前写(§1.10)。
2026-08-27 03:59:39 -04:00

126 lines
8.4 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.
# PolyLoop
实验室共用的 Agent 执行内核。治理单位是**一次运行**:围绕一个目标的有界多轮
「模型决策 → 动作 → 观察」循环,含预算、停止语义、取消、逐步轨迹与 Skill 注入。
它和 PolyGateway 是叠起来的两层。PolyGateway 治理**一次模型调用**(多源、限流、重试、熔断、
缓存、遥测),PolyLoop 治理**一次运行**,并用 PolyGateway 的顶层公共 API 拿模型。
任务编排、批量调度、评分、检索、Skill 的生成与进化都留在下游项目。
---
## 安装
发布在实验室自建的 Gitea PyPI registry 上,公网 PyPI 查它是 404,所以装它必须自己带索引地址:
```bash
pip install --extra-index-url https://gitea.iomgaa.online/api/packages/iomgaa/pypi/simple/ \
"polyloop==1.0.*"
```
模型调用要经 PolyGateway,那部分是一个单独的 extra——不用它的人不该被迫装上网关:
```bash
pip install --extra-index-url https://gitea.iomgaa.online/api/packages/iomgaa/pypi/simple/ \
"polyloop[gateway]==1.0.*"
```
契约套件随包发布,它要的 pytest 也是一个单独的 extra
```bash
pip install --extra-index-url https://gitea.iomgaa.online/api/packages/iomgaa/pypi/simple/ \
"polyloop[testing]==1.0.*"
```
**只有自己实现了某个接缝、要拿库这边的契约套件验它的时候才装这个。** 存储、模型调用、决策
解释、动作执行、事件出口五处允许换实现,谁换了谁就得证明自己那份还满足接缝的行为契约,而
证明的方式就是继承 `polyloop.testing` 里对应的基类跑一遍。只调 `run` / `resume`、五个接缝
全用库自带或别人写好的实现的项目,不需要它。
写进 `requirements.txt` 的话,那一行 `--extra-index-url` 必须排在 `polyloop` 之前。
**这台开发机上的注意事项**:它设了 `http_proxy` 指向一个到不了外面的本地代理,走代理会失败,
装的时候加 `NO_PROXY=gitea.iomgaa.online`
## 现状
十一个模块全部落地,四层测试都在跑。**还没有任何下游项目真的用过它**——这是它现在最大的未验证项,
下面的阶段清单是唯一的进度权威。
## 消费者与验收标准
| 项目 | 现状 | 本库对它的验收标准 |
|---|---|---|
| dissect | 已有跑着的 `harness/agent/`loop / context / memory / parser | **能把那套循环搬到本库上,dissect 原有测试全绿。** 一手需求证据最强 |
| GovDoc-SaaS | 仓库 2026-08-03 起整体重建,实现全部清空,自己的清单停在「构建文档框架」 | **不做迁移验收,做设计级验收**:它真实需要的东西逐条能不能被承载,见 `research-wiki/migrations/govdoc-saas.md`。它将来写 agent 层时是直接长在本库上,不是迁过来 |
| CHSAnalyzer | 还没写到 agent 那一步,只有设计方案 | **远期可以兼容使用。** 它的 agent 需求要么本库能满足,要么明确写进「不属于本库」清单并说明为什么 |
三者的证据强度不同,能进本库的语义也就分档:dissect 和 GovDoc-SaaS 的真实代码是一手证据,
两边都需要的机制才有资格做成稳定内核;CHSAnalyzer 只用来检验边界画得对不对,不能凭它的
设计方案单独长出一个组件——一个没有真实调用方的抽象,等到有调用方那天多半是错的。
## 阶段清单
**这份清单是「当前处在哪个阶段」的唯一权威。** `CLAUDE.md` 不重复这里的内容,只有一条常青规则
(§1.11)要求动手前先看这里——这样阶段推进时不需要改 `CLAUDE.md`,不会在别处留下过期条文。
- [x] ① 协作规范 —— 见 [CLAUDE.md](CLAUDE.md) 与 [research-wiki/README.md](research-wiki/README.md)(文档体系)
- [x] ② 需求对齐 —— 从 dissect 的 `harness/agent/` 与 GovDoc-SaaS 的 `packages/docagent-core/`
提取真实需求,产出 `research-wiki/migrations/` 下两份迁移文档(删除清单 + 组件映射 + 验收口径),
并检查 CHSAnalyzer 的 agent 方案落在边界内还是边界外。**这一阶段的产物决定库的边界**,
所以它排在架构前面:边界画错,后面每一份架构文档都要重写
- [x] ③ 架构 —— `research-wiki/explanation/architecture.md``pyproject.toml` 的 import-linter 契约。
架构文档先于代码存在,此期间它是一份规格而不是描述,文档开头须写明这一点
- [x] ④ 测试框架 —— 四层都在跑(划分判据是「依赖什么」,见 [CLAUDE.md](CLAUDE.md) §1.9)。
e2e 打真实模型网关、会产生真实费用,所以默认不跑:要 `POLYLOOP_E2E=1` 加显式
`pytest -m e2e`,配置见 [.env.example](.env.example)。那套公共行为一致性用例住在
`polyloop.testing` 里随包发出去,五个接缝在本仓库都接上了实现跑起来,接点是
`tests/contract/``tests/integration/` 下那几个继承契约基类的子类
- [x] ⑤ 实现 —— 十一个模块全部落地,五个接缝都有调用点。`stores` 有两种形态:逐行追加进
本地文件的那个,和只留在进程内存里、进程一退就没了的那个。关系数据库那种仍然由下游
自己实现,契约套件是它的准入标准,而套件随包发布在 `polyloop.testing`
- [x] ⑥ 验收 —— 两件事都做完了。**一是自己造负载压**:照三个消费者将来的用法造负载,用真实
数据真的打模型跑完,看这个内核在这个量级上扛不扛得住。**这一步之所以必须自己做,是因为
三个消费者一个都还没到能用它的时候**,而「从没被任何人用过」是它当时最大的未验证项,
等下游是等不来的。**二是把 dissect 的迁移方案交出去**:不在 dissect 仓库里写代码,
出一份方案提到它的 issue 上,由那边自己排期。GovDoc-SaaS 那半是设计级验收(理由见上面
那张表),口径在 `research-wiki/migrations/govdoc-saas.md`
压测这套东西住在 `tools/soak/`,它守什么、怎么重跑见
[research-wiki/explanation/soak-harness.md](research-wiki/explanation/soak-harness.md)。
2026-08-11 那一跑:193 次运行、1743 次真实模型调用,两个场景各自的十一条不变量全部通过;
九类故障注入 58 条判据零击穿。**库本身没有被压出 bug**,压出来的四个问题全在压测这一侧
(判据写错、场景缺完成通路、崩溃时机抢不到、环境客户端漏了「连不上」那一档),各自的
commit 里写了是怎么发现的。AppWorld 那一路的步数分布与 dissect 已有的 937 条真实轨迹
基本重合,这是「同一个 benchmark 换个内核驱动、轨迹形状没变」的证据
## 本地检查
```
make check # ruff format --check + ruff check + lint-imports
make test # pyteste2e 默认不跑,它打真实网关要花钱)
make ci # 上面两条
```
还没有 CI workflow`make ci` 就是当前的全部机器闸,和 PolyGateway 一样。发布怎么做见
[research-wiki/guides/releasing.md](research-wiki/guides/releasing.md)。
文档质量不走机器检查,走 [CLAUDE.md](CLAUDE.md) §3 的「硕士生阅读」评审。
## 参考资料
`reference/` 下的六个仓库与 `agent-core.md` **都只是参考,不是本项目的设计,也不是任何事实的权威**
(理由见 [CLAUDE.md](CLAUDE.md) §0)。它们只读、不改、不入库。
| 位置 | 是什么 |
|---|---|
| `reference/agent-core.md` | 别人为本项目写的一份架构提案。其中任何一条在被我们自己的 design doc 采纳前都不作数 |
| `reference/dissect/` | 消费者,已有 ReAct 循环实现 |
| `reference/GovDoc-SaaS/``background` 分支) | 消费者的**旧代码**,第一次抽库尝试 `packages/docagent-core/`。它那边的活仓库已经把这份降级成「只作研究输入,不是当前事实来源」 |
| `reference/GovDoc-Editor/` | GovDoc-SaaS 重构之前的那一版,今天跑在生产上。需求来源,不是迁移对象 |
| `reference/CHSAnalyzer/` | 远期消费者;同时是本仓库协作规范的蓝本 |
| `reference/PolyGateway/` | 本库的依赖,也是「实验室共用库该怎么做」的蓝本 |
| `reference/pi/` | 外部参考实现 |
其余约定见 [CLAUDE.md](CLAUDE.md),那里是协作规则的唯一来源。