Implement semantic context compaction checkpoints - #35
Conversation
better-er
left a comment
There was a problem hiding this comment.
PR #35 审阅:Implement semantic context compaction checkpoints
- 仓库:Ev3rGan/coding-agent-kernel
- PR:#35
- head:
43bf5c80b70aab4a91ac64e6f230d88cbc9ba500 - base:
origin/main=c26db32,单 commit,17 文件,+3048 / -249 - 审阅环境:Windows,CPython 3.14.0,pytest 9.1.1,mypy 2.3.1,ruff 0.16.6
- 结论:机制实现与设计文档一致,质量门禁可复现,测试覆盖扎实。发现 1 个会让合法压缩被硬失败的功能缺陷、1 个会终止整个 Run 的过严判定,以及若干口径与文档问题。
一、事实核对
1. 测试与质量门禁
| 门禁 | PR 声明 | 本次复核 |
|---|---|---|
| pytest | 355 passed,2 skipped | 355 passed,2 skipped |
| strict mypy | PASS,34 source files | PASS,34 source files |
| ruff check / format | PASS | PASS,52 files formatted |
| git diff --check | PASS | PASS |
| wheel 构建 | PASS | PASS,coding_agent_kernel-0.1.0-py3-none-any.whl |
本机 pytest 原始输出是 5 failed, 350 passed, 2 skipped。这 5 个失败在 origin/main 上以完全相同的方式失败,与本 PR 无关,全部是 Windows 环境限制:
tests/test_swebench.py::test_docker_workspace_normalizes_trusted_mode_only_image_head_and_host_noisetests/test_swebench.py::test_docker_workspace_rejects_tracked_symlink_that_escapes_workspacetests/test_swebench.py::test_docker_workspace_rejects_symlinked_git_control_file_before_resettests/test_tool_loop.py::test_agent_run_executes_tool_batch_and_sends_ordered_results_to_next_turntests/test_tool_loop.py::test_bash_progress_is_observable_before_process_completion
前三个失败于 symlink_to 需要开发者模式或管理员权限,后两个失败于测试内 python/bash 子进程在该环境不可用。两处 skip 与 PR 声明一致:未安装 swebench optional 依赖、未开启官方 dataset network test。
PR 声明的 wheel SHA-256 未复核。wheel 默认包含构建时间戳,本机重建的哈希与 PR 不同属于正常现象,不作为问题。
2. CLI 与 demo 声明
逐条复现 PR 正文的验收声明,全部一致:
python -m coding_agent swebench run --context-max-characters存在;传0时返回 2,输出configuration/prediction_invalid记录。python -m coding_agent demo context-compaction --case success:exit 0,checkpoint_count=1,bounded=true,message_roles=["summary","user"]。python -m coding_agent demo context-compaction --case summary-error:exit 1,compaction_summary_failed,provider_calls=0,checkpoint_count=0,session_resumable=true。- PR 正文使用
Relates to #33,并明确合并不关闭 Issue #33,与设计文档 §11.2 一致。
3. 设计条款核对
逐条对照 docs/design/context-compaction-v2.md,均有实现与聚焦测试:
- v2 checkpoint 独立对象、lineage、first-kept、typed evidence、strategy、metrics:
compaction.py与test_context_compaction_v2.py。 - 增量压缩只输入 previous checkpoint 与新增 span,不再产生
summary:summary::test_repeated_compaction_passes_previous_checkpoint_separately_and_projects_only_latest。 - 近期完整 turn 与 ToolCall/ToolResult 安全切点、类型感知 shedding、未知 Extension tool 输出不 shed:
test_safe_cut_never_splits_tool_transaction_and_sheds_only_regenerable_output、test_non_regenerable_extension_tool_output_is_not_shed_from_semantic_input。 - 权威资源每次重新投影、Skill 不进入 conversation summary:
test_extension_skill_resource_is_reprojected_and_never_enters_compaction_span。 - summary Provider 无 Tool、stream 校验与关闭、失败原子性、失败早于 Coding Turn 请求:
test_summary_provider_failure_is_observable_atomic_and_precedes_coding_turn等。 - v1 可读并升级为 v2、非法 v2 replay 拒绝:
test_v1_checkpoint_replays_and_upgrades_to_v2_without_rewriting_history、test_replay_rejects_invalid_v2_checkpoint_contract。 COMPACTION_STARTED/SUCCEEDED/FAILED可观察。
4. 随机化探查
用 200 组会话、每组最多 12 步随机历史,混合普通 turn、工具调用 turn 与 assistant 追加消息,预算取 600/900/1200/2000/4000,每步构建并在产生 plan 时写入 Session,最后 close 后 Session.resume 重新加载并重建投影。未发现 checkpoint 持久化失败、resume 校验失败、投影出现多个 summary、或重建上下文超预算。核心不变量在这组随机输入下成立。
二、修改要求
1. 中等:保留窗口的预算估计与真实摘要编码不一致,会硬失败掉本来可成立的压缩
ContextPipeline._select_retained_turns 用 BranchSummaryMessage(text="x" * summary_budget) 估计候选请求大小,但 estimate_provider_request_characters 对 message 文本做 canonical JSON 编码。真实摘要必须包含 8 个 ## 标题,必然带换行,换行在 JSON 里变成两字节,引号同理。因此占位符估计系统性偏小,最多偏小到 summary_budget 量级。
实测:构造 400 字符、含 215 个换行的合法摘要,JSON 编码长度 615;会话为 1 个旧 turn 加 3 个近期 turn。
retained 0 placeholder_est 614 real_est 827
retained 1 placeholder_est 1024 real_est 1237
max_characters 取 1030 到 1230 时,build 选中 retained 1 后抛 context_budget_exceeded,而 retained 0 的真实估计只有 827,完全在预算内。也就是说,一个合法且 bounded 的压缩方案存在,实现却直接失败。这会在紧预算下把 Agent Run 变成 context_budget_exceeded,而不是退化成更小的保留窗口。
建议:占位符改用转义后的上界,或按 summary_budget 预留 JSON 转义开销;或者在最终 characters_after > max_characters 时回退到更小的保留窗口再试一次,而不是立即失败。
2. 中等:thrashing guard 会在压缩结果已经满足预算时终止整个 Run
build 先判定 characters_after > max_characters 通过,随后又用 reduction < max(32, characters_before // 20) 抛 compaction_thrashing。此时上下文已经被验证为 bounded,可以继续执行,却被判成 Run 失败。设计文档 §7 的原话是「报告 compaction thrashing 并停止自动重试」,并没有要求把已经 bounded 的上下文判为失败。判定还发生在一次真实 summary Provider 调用之后,失败无法回收这次调用。
建议:当 characters_after <= max_characters 时记录 thrashing_detected 并继续,仅在压缩后仍超预算且无新 span 可压时失败;或者在 PR 与设计中明确「压缩收益不足即失败」这一更强的语义。
3. 低:metrics.characters_before 与 characters_after 口径不同
characters_before 由 pipeline 在 Extension transform 之前计算,characters_after 在 AgentKernel._build_context 里被替换成最终请求的估计值,而最终请求已经包含 Extension 通过 context_resource 与 ContextSupplement 注入的资源。两个字段不是同一口径,checkpoint 里记录的前后对比会失真。测试 test_persisted_characters_after_matches_final_supplemented_provider_request 只断言了 characters_after,掩盖了这一点。
建议:要么两个字段都在 Extension transform 之后计算,要么在 metrics 里区分 pipeline 估计与最终请求估计。
4. 低:可观察性字段目前永远是默认值
CompactionMetrics.trigger只会是automatic,仓库没有 manual compaction 入口。CompactionMetrics.thrashing_detected永远是False,因为触发 thrashing 时直接抛错、不落 checkpoint,而decode_v2_compaction_checkpoint又强制该字段必须为False。CompactionInput.retained_recent_entries被两个 engine 完整忽略,生产摘要请求里没有任何近期保留窗口的信息。
设计 §10 要求 trigger 与 thrashing guard 可检查。建议要么补齐 manual 触发与落盘路径,要么在设计与 PR 说明里承认这些字段当前是占位。
5. 低:兼容性与文档未覆盖
ContextPipeline.build从同步改为 async,是公开 API 的调用约定变更。PR 正文写「现有 Context、Session、Extension、DeepSeek 与 SWE-bench 路径继续使用同一 Kernel seam」,没有提到这一点,也没有迁移说明。config.json新增context_max_characters,但version仍为 1。CONTEXT.md、docs/specs/coding-agent-kernel.md未补充CONTEXT_RESOURCEHook、ContextResource与 v2 checkpoint 字段。
6. 低:校验里的两处不对称与静默处理
ExtensionRuntime.transform_context与transform_provider_request在 handler 把resources清空时静默恢复 canonical 值,但改动其中一个就报错。这种不对称容易掩盖 handler 的 bug,建议统一为拒绝。_validate_v2_compaction_payload对 previous coverage 做entry_id in set(message_ids)过滤,非法 id 被静默丢弃而非拒绝。前置 checkpoint 的自身校验通常能挡住,但这里应显式报错。
三、结论
机制方向正确,测试与门禁证据基本可复现,未发现数据损坏、checkpoint 原子性或 replay 校验被绕过的问题。合并前建议至少处理第 1、2 条:第 1 条会在紧预算下把可用的压缩变成 context_budget_exceeded,第 2 条会在结果已满足预算时终止 Run。其余为口径、文档与可观察性改进。
|
感谢在 review 5143237599 中提供的可复现审阅。该报告已按不可信 bug report 独立复现和检查;对应修复已作为普通 follow-up commit 逐项处置:
有意保持不变:
同一最终树验证:150 targeted passed;362 full-suite passed、2 documented skips;strict mypy、Ruff lint/format、diff-check、global Standards 与 repair-scoped Standards/Spec 均通过;wheel SHA-256 English summary: commit |
better-er
left a comment
There was a problem hiding this comment.
机制方向正确,测试与门禁可复现,未发现数据损坏与 checkpoint 原子性问题。合并前请优先处理评审第 1、2 条:紧预算下的收益判定,以及已满足预算时的 thrashing 失败语义。
关联
Relates to #33
本 PR 实现 Context Compaction v2。详细语义与边界见 Context Compaction v2 设计文档。本 PR 合并不关闭 Issue #33;Issue 必须保持开放,等待 post-merge 真实验收。
问题范围
当前确定性前缀摘要会丢失修改文件、验证结果、blocker、关键决策与 next step;重复压缩还可能把旧 checkpoint 当作普通消息,形成递归 summary,并让 Agent 在压缩后重复已经完成的探索。
核心机制
ModelProvider、无 Tool 调用完成,采用固定八段语义 schema,并验证 usage、stream、关闭和失败路径。CONTEXT_RESOURCEHook 注入。ContextPipeline重新投影和验证,summary、coverage、first-kept、metrics、ProviderRequest 与持久化 payload 保持一致。COMPACTION_STARTED、COMPACTION_SUCCEEDED、COMPACTION_FAILED保持 Host 可观察,并保证每个 started compaction 都有 succeeded/failed 终止事件。SWEbenchRunConfig同时驱动 Kernel、config.json和manifest.json。兼容性与原子性
现有 Context、Session、Extension、DeepSeek 与 SWE-bench 路径继续使用同一 Kernel seam。
ContextPipeline.build的 async 调用迁移已写入设计说明。摘要 Provider 失败、取消、空输出、超预算或 schema 非法时,不持久化部分 checkpoint,也不启动正常 Coding Turn Provider 请求,Session 保持可恢复。Coding 与 Compaction Provider 路径共用取消隔离,Provider-owned cancellation 会形成结构化compaction_provider_failed。协作者审查处置
针对 review 5143237599,本次 follow-up commit
4bea3e17ca5616a6e1fa618a8a4f8de287db7587完成以下修复:CompactionPlan经唯一ContextPipeline重新投影,确保 coverage、first-kept、request、metrics 与持久化 payload 一致。ContextPipeline.build迁移、ContextResource 责任边界、automatic/manual 边界、retained raw 行为与 resource compatibility,并精确归属 README 中的 ContextSettings、Tool schema 与 ContextResource。经独立检查后保持不变的兼容决策:
manual仅保留为 wire-compatible trigger value。config.jsonversion 保持 1;该设置为 additive,当前不存在 strict field-set parser。验证证据
git diff --check:PASS。coding_agent_kernel-0.1.0-py3-none-any.whl。f8494bcd4da9ec5c9f55bc98dc3c9dcf9963b9cd790f805c0fda9c404cd5ddad。--help:PASS。demo context-compaction:PASS,事件顺序为compaction_started→compaction_succeeded,持久化 v2 checkpoint,最终请求为 649/650 characters。当前完整 PR:20 paths,3,622 additions / 367 deletions;head 为
4bea3e17ca5616a6e1fa618a8a4f8de287db7587。Issue #33 关闭门禁
本 PR 合并后,仍需至少两个彼此不同、且不同于原诊断样例的额外 SWE-bench Verified 实例完成最终验收。每个实例必须:
FAIL_TO_PASS: PASS与PASS_TO_PASS: PASS;在这些门禁全部完成前,Issue #33 保持 OPEN。
English appendix
This PR implements Context Compaction v2 through an independent append-only checkpoint, recent complete-turn retention, incremental coverage and lineage validation, a tool-free semantic summary through the existing provider-neutral ModelProvider, authoritative resource re-projection, and validate-before-persist atomicity.
The review follow-up at
4bea3e17ca5616a6e1fa618a8a4f8de287db7587corrects JSON-escaped budget estimation, treats bounded low-reduction compaction as observable thrashing instead of a Run failure, aligns metrics, preserves one canonical checkpoint projection, shares Provider cancellation isolation, terminates every started lifecycle, and re-projects final Hook-transformed plans through the single ContextPipeline. Compatibility decisions for the manual wire value, retained raw entries, additive config versioning, empty-resource restoration, and authoritative checkpoint validation remain intentional.The final frozen tree passed 150 targeted tests, 362 full-suite tests with 2 documented optional/network skips, strict mypy, Ruff, diff checks, review gates, wheel installation, and installed CLI acceptance. Merging this PR does not close Issue #33; closure still requires two additional credentialed DeepSeek SWE-bench Verified runs with actual v2 compaction and official Harness PASS results for both FAIL_TO_PASS and PASS_TO_PASS.