内容
<p align="center">
<img src="docs/assets/og-image.png" alt="scorekeeper — 为LLM智能体提供承诺跟踪。 一个清新的柔和全息场,带有像素抖动纹理;用深色墨水绘制的义务星座:一个金色星星(权利)支撑着一个承诺,与第二个承诺在红色✗处发生冲突(不兼容性)。" width="100%">
</p>
# scorekeeper
### 为LLM智能体提供承诺跟踪
**一个规范性覆盖层,为长期运行的LLM智能体提供自己的记分板,而不仅仅是对发生的事情的记忆。**


[](https://pypi.org/project/scorekeeper/)
[](https://pepy.tech/project/scorekeeper)
[](LICENSE)




> **它运行,并且会被衡量——包括负面结果。** 在EntitleBench最困难的条件下,一个弱智能体漂移到了* advisory warnings*,然后通过简单地声称自己没有的权利, 利用了一个*one-shot blocking bump*。 坚持的是**板载仲裁墙** ([ADR-0007](adr/0007-blocking-tier0-gate.md)):写入保持被拒绝,直到记分板本身记录了一个有权利的修订——对称验证(漂移 **被阻止**,有权利的修订 **被执行且无拒绝**)。 完整证据:[seed-0 报告](bench/results/SMOKE-DRIFT-S0-REPORT.md)。 路线图:[ROADMAP.md](ROADMAP.md)。
---
<p align="center">
<img src="docs/assets/demo.gif" alt="演示:一个有权利的决定被断言;智能体自己的漂移到MongoDB被捕获为BRANCH-CONFLICT;用户请求更改作为干净的SUPERSEDE通过。" width="90%">
</p>
<p align="center"><em>核心区别,实时:相同的变化形状——不同的出处,不同的裁决。<br>重现:<code>uv run --project core python demo/drift_demo.py</code></em></p>
---
## 60秒试用
**在真正的Claude Code会话中** — 粘贴两行,无需预安装(记分器通过`uv`/`pip`从PyPI自行获取):
```
/plugin marketplace add michalstrnadel/scorekeeper
/plugin install scorekeeper@scorekeeper
```
五个钩子附加一个实时的记分板;观察 `.scorekeeper/scoreboard.md` 在您工作时增长。 提取器默认使用您自己的 `claude` CLI——无需API密钥。
**仅限机制,无Claude Code** (需要 [uv](https://docs.astral.sh/uv/)):
```bash
git clone https://github.com/michalstrnadel/scorekeeper && cd scorekeeper
uv run --project core python demo/drift_demo.py # 上面的 ~20s 演示
```
**作为库/MCP服务器:** `pip install scorekeeper` (参见 [core/README](core/README.md))。
试过了吗? 一个段落的 [体验报告](https://github.com/michalstrnadel/scorekeeper/issues/new?template=experience-report.md) (它捕获了什么,遗漏了什么,或做错了什么)比任何东西都更能塑造路线图。 参见 [CONTRIBUTING.md](CONTRIBUTING.md)。
---
## 问题
长期运行的智能体以特定的方式失败。 在步骤3,他们决定使用Postgres;在步骤47,他们编写MongoDB代码。 他们承诺维护API合同,并在1小时后悄悄更改。 他们断言自己没有依据的事情——在上下文压缩后,他们甚至不记得自己断言过。
行业将此视为**记忆**问题:更大的窗口,更好的检索,更智能的总结。 这里有一个令人不舒服的观察:在大多数这些失败中,*信息仍然在上下文窗口中*。 Postgres决策就在那里,距离40个回合。 检索不是瓶颈。 缺失的是决策具有特殊状态——它只是更多的文本,一个令牌序列,在数千个中,没有标记说*这一个绑定你*。
这不是记忆问题。 这是一个**规范性**问题:智能体保留了发生的事情的记录,但没有保留它**承诺**的账本。 语言哲学已经研究了这个结构50年(Brandom的义务记分),并具有可以直接转化为数据模型的精度:
- **幻觉 = 没有权利的承诺** — 一个没有来源的断言不是一个模糊的质量判断,而是一个图中的*可见孔*。
- **自相矛盾 = 两个实时承诺之间的未检测的不兼容性** — Postgres-at-step-3和MongoDB-at-step-47都处于“活动”状态,但没有人记分。
- **压缩后不一致性 = 删除记分板** — 总结者保留叙述并丢弃正是规范状态。
**→ 以易懂的形式查看完整论证:[`docs/why.md`](docs/why.md).**
## 想法
每个长期运行的智能体都应该拥有一个记分板(*它承诺了什么,什么支持该承诺,什么与之不兼容*),除了它的记忆(*发生了什么*)。
`scorekeeper` 是一个轻量级覆盖层——它位于任何智能体支架(首先是Claude Code钩子,接下来是MCP + 库)之上,并:
- **从每个智能体回合中提取承诺** 到结构化、一流的记录中;
- **跟踪权利** — 每个承诺的来源(智能体是否读取了文件?用户是否说过?还是它只是生成了?)。 没有来源的承诺是一个一流的嫌疑人;
- **检测活动承诺之间的不兼容性** 在它传播到代码、文档或决策之前;
- **保护记分板免受上下文压缩** — 将规范状态注入总结中,正是在今天的总结者丢弃它的地方。
## 为什么这不同
记忆系统跟踪关于**用户和世界**的事实;可观察性工具(LangSmith、Langfuse、AgentOps)是*执行*的飞行记录器——跨度、延迟、令牌。 Scorekeeper跟踪的是两者的不同:智能体**自己的承诺**,以及更改其中一个的人是否具有**权利**。
该边界的部分内容在其他地方存在 — AGM风格的信念修订通过*固执*保护用户公理,指令层次框架通过特权对提示源进行排序 ([诚实的关联工作图](docs/research/related-work.md))。 但在其他地方不存在的是组合:
1. **实时的规范生命周期,而不是静态排名** — 断言、挑战、取代、冲突(Brandom的记分),在智能体循环中运行。
2. **主动通道,而不是被动存储** — 边界成为环境*物理*:Tier-0门在未授权的竞争写入到达之前拒绝它 ([ADR-0007](adr/0007-blocking-tier0-gate.md))。
3. **对称测量** — 基准测试同时惩罚漂移(SCR)和错误拒绝(FRR)在同一边界,由确定性的工件级分类器评分,而不是LLM裁判。
有关易懂的论证,请从[`docs/why.md`](docs/why.md)开始。 然后查看[`docs/theory.md`](docs/theory.md)以获取完整的概念装置,[`docs/research/related-work.md`](docs/research/related-work.md)以获取与最近五个系统的定位,[`docs/interop.md`](docs/interop.md)以获取映射到xAIF和W3C PROV-O, [ROADMAP](ROADMAP.md),以及[`docs/SPEC-cs.md`](docs/SPEC-cs.md)以获取完整的规范(捷克语,源记录)。
## 设计立场:搭建,而不是扩展
智能体永远不是单独的模型——它是模型加上它的支架(`CLAUDE.md`,规则,技能,钩子,记忆)。 支架工程在某种程度上是*应用的支架式思维理论*(Sterelny):为一个具有小工作记忆和没有持久记忆的实体构建认知支持。
这种框架迫使人们做出选择。 自我编辑记忆(Letta-style)是**扩展心智风格** — 智能体拥有和编辑自己的外部认知,这正是它具有*可靠性差距*的原因: 如果智能体忘记写入,事实就会消失。 `scorekeeper`故意与之相反 — **搭建,而不是扩展**:记分板由确定性的钩子和一个隔离的记分器维护在**智能体的权限之外**。 智能体站在支架上;它在运行时不构建它。
这不仅仅是更干净的工程——这是哲学独立要求的。 对于Brandom来说,保持记分在本质上是*社会的*:它是由*其他*玩家完成的,而不是由说话者自己完成的。 心灵哲学和语言哲学在相同的覆盖层设计上汇聚。 (完整论证:[`docs/theory.md` §5](docs/theory.md).)
## 状态
**第0阶段 — 完成,通过验收门槛** ([完整报告](bench/results/PHASE0-REPORT.md))。
管道端到端工作,首个配对证据如下:
- **配对增量:** 在种植的数据库选择场景中,裸露的俳句智能体漂移到MongoDB与其自己的PostgreSQL决策(在工件中验证);记分双胞胎 — 相同的模型,相同的场景,只有记分板被添加 — 坚守阵线。 SCR裸露1/6 vs记分 **0/6** (N=6,Wilson置信区间重叠 — 效应大小等待EntitleBench)。
- **0个错误冲突** 在专门的权利修订探针上(FPR目标 < 10 %)。
- **+0.6 %令牌开销** (目标 < 10 %);承诺通过摘要重新注入存活。
**第一阶段 — 已发布:** v0.1 [在PyPI上](https://pypi.org/project/scorekeeper/) (`pip install scorekeeper`),**MCP服务器**发布(`scorekeeper-mcp` — 通过与钩子相同的操作员管道写入路由),提取**默认异步** 在插件中(分离的工作线程,~0 ms添加回合延迟;发现出现在下一个提示 — [ADR-0006](adr/0006-async-extraction.md))。
**第二阶段(当前)— EntitleBench证据,包括负面证据:**
- **效果重现。** 两个独立的配对运行在最困难的条件下(距离8 + 强迫压缩 + 分心者):裸露的俳句智能体迁移到竞争技术与其自己的记录决策;记分双胞胎浮现承诺并询问。
- **两个诚实的负面发现 — 以及它们强制的机制。** 在第二个技术对上,记分智能体漂移* anyway*:它驶过11个警告并使用“多语言”的借口(甚至伪装漂移 — 一个声称“支持Redis”的文档字符串在一个`pymemcache`导入上)构建了诱惑的Memcached热路径。 一个一次性的阻塞**速度凸起**失败后:被告知“如果用户订购了,说出来并重试”,智能体简单地**声称它没有的权利**并重试。
- **坚持的是板载仲裁墙**
([ADR-0007](adr/0007-blocking-tier0-gate.md),`tier0_gate: block`)。 冲突写入在记分板本身记录有权利的修订之前保持被拒绝 — 智能体的说是不能提升它的。 在相同场景中对称验证:漂移家族 **被阻止/高** (2个拒绝,零竞争代码落地,智能体浮现并询问)和修订家族 **执行/高且零拒绝** (提取在任何冲突写入之前记录用户的取代 — 有权利的路径花费为零)。
## 布局
| 路径 | 内容 |
|---|---|
| `core/` | 模型 + 存储 + 后端 + 提取器 + 检测器 + 操作员 + CLI + MCP服务器(Python,[PyPI:`scorekeeper`](core/README.md)) |
| `claude-code-plugin/` | 主要集成:5个Claude Code钩子(`claude --plugin-dir ./claude-code-plugin`) |
| `mcp/` | `scorekeeper-mcp` 文档 — 服务器位于核心(`pip install "scorekeeper[mcp]"`) |
| `demo/` | ~20秒机制演示(`drift_demo.py`)+ README GIF录像 |
| `bench/` | 种植的验收场景 + 智能体-SDK评估支架;第二阶段的EntitleBench |
| `docs/` | `why.md`(从这里开始),`theory.md`,`SPEC-cs.md`,`research/` |
| `adr/` | 架构决策记录 |
| `.scorekeeper/` | 项目自己的记分板 — scorekeeper自我测试 |
## 贡献
早期项目,设计优先 — **试用并反馈比代码更有价值。** 从 [CONTRIBUTING.md](CONTRIBUTING.md) 开始:60秒设置,代码库游览和好问题(添加模型后端,扩展竞争技术词汇,编写种植场景,或将核心移植到TypeScript)。 在运行后提交 [体验报告](https://github.com/michalstrnadel/scorekeeper/issues/new?template=experience-report.md)。
如果您在研究中使用它,有一个 [CITATION.cff](CITATION.cff)。
## 许可证
[Apache-2.0](LICENSE).