fischer-agentkit/docs/plans/2026-06-07-013-feat-agentki...

345 lines
16 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.

---
title: "feat: AgentKit Phase 7 — Headroom 上下文压缩集成"
status: completed
created: 2026-06-07
plan_type: feat
depth: standard
origin: Phase 6 完成后 Headroom 集成评估 + GEO Pipeline token 成本优化需求
branch: feat/agentkit-phase7-headroom
---
# AgentKit Phase 7 — Headroom 上下文压缩集成
## Summary
在 ReAct 引擎中集成 Headroom 作为上下文压缩层,在工具输出拼装到对话历史前进行智能压缩,减少 60-90% token 消耗。采用 Library 模式集成,作为可选依赖默认关闭,通过 YAML 配置开关启用。定义 CompressionStrategy Protocol 使现有 ContextCompressor 和新 HeadroomCompressor 可互换,扩展 ReAct 循环内压缩点实现增量压缩。
## Problem Frame
Phase 6 完成后AgentKit 的工具生态WebCrawl、BaiduSearch、Schema 工具)产生大量工具输出,这些输出是 GEO Pipeline token 消耗的主要来源。当前 ContextCompressor 仅在初始消息构建时做一次 LLM 摘要式压缩ReAct 循环内工具结果累积后不再压缩,导致长对话 token 膨胀严重。
Headroom 提供 6 种压缩算法SmartCrusher/CodeCompressor/Kompress/CacheAligner/IntelligentContext/ImageCompressor按内容类型智能路由CCR 可逆压缩保证原始数据不丢失。集成后可在不改变 Agent 行为的前提下大幅降低 API 成本。
## Requirements
- R1: Headroom 集成后ReAct 循环内工具输出在拼装到对话历史前被压缩
- R2: 压缩是可选的,默认关闭,通过 YAML 配置启用
- R3: Headroom 未安装时系统正常工作,自动降级到现有 ContextCompressor
- R4: CCR 可逆压缩LLM 可通过 headroom_retrieve 工具取回原始数据
- R5: 压缩策略可配置:全局开关、内容类型路由、压缩强度
- R6: 不引入 PyTorch 等重型依赖headroom-ai[code] 为最大可选安装范围
- R7: 增量压缩ReAct 循环内每步工具结果独立压缩,而非仅初始一次
## Key Technical Decisions
### KTD-1: CompressionStrategy Protocol 替代继承
**决策**: 定义 `CompressionStrategy` Protocol`async def compress(messages) -> list[dict]`),而非让 HeadroomCompressor 继承 ContextCompressor。
**理由**: ContextCompressor 是具体类,内部硬编码了 LLM 摘要逻辑不适合作为基类。Protocol 允许两种压缩策略独立演化ReActEngine 只依赖 Protocol 接口。
**替代方案**: 让 HeadroomCompressor 继承 ContextCompressor 并 override compress() — 耦合度高ContextCompressor 内部状态llm_gateway, max_tokens对子类无意义。
### KTD-2: Library 模式集成,不用 Proxy/MCP Server
**决策**: 使用 `from headroom import compress` Library 模式在进程内调用。
**理由**: AgentKit 是框架不是终端工具,需要在 ReAct 循环内精确控制压缩时机工具结果构建后、LLM 调用前。Proxy 模式无法区分哪些消息需要压缩MCP Server 模式增加了网络开销和额外进程管理。
### KTD-3: 不引入 Kompress-base 模型
**决策**: 仅使用 SmartCrusherJSON和 CodeCompressor代码不使用 Kompress-base文本压缩模型
**理由**: Kompress-base 依赖 HuggingFace Transformers + PyTorch安装体积约 2GB。AgentKit 的文本压缩需求(对话历史摘要)由现有 ContextCompressor 的 LLM 摘要模式覆盖。Headroom 的 SmartCrusher 对 JSON 工具输出效果最佳92% 压缩率)。
### KTD-4: 工具结果压缩 + 对话历史压缩双层架构
**决策**: 新增 `compress_tool_result()` 方法处理单个工具输出SmartCrusher/CodeCompressor保留 `compress()` 处理整段对话历史(现有 ContextCompressor 逻辑)。
**理由**: 工具输出和对话历史的压缩策略不同 — 工具输出是结构化数据JSON/代码),适合 Headroom 的统计压缩;对话历史是混合内容,适合 LLM 摘要。双层架构让两种策略各司其职。
### KTD-5: CCR 检索工具自动注册
**决策**: 当 HeadroomCompressor 启用时,自动注册 `headroom_retrieve` 工具到 ToolRegistryLLM 可通过 Function Calling 取回原始数据。
**理由**: CCR 的核心价值是可逆性 — 压缩后 LLM 仍可按需取回原始数据。将 retrieve 暴露为工具是最自然的集成方式LLM 在需要详细信息时会自动调用。
---
## Implementation Units
### U1. CompressionStrategy Protocol 与工厂函数
**Goal**: 定义压缩策略 Protocol 接口,实现工厂函数根据配置创建压缩器实例。
**Dependencies**: 无
**Files**:
- `src/agentkit/core/compressor.py` — 修改:新增 CompressionStrategy Protocol新增 create_compressor() 工厂函数
- `tests/unit/test_compression_strategy.py` — 新增Protocol 合规性测试 + 工厂函数测试
**Approach**:
1. 在 compressor.py 中定义 `CompressionStrategy` Protocol
- `async def compress(self, messages: list[dict]) -> list[dict]`
- `async def compress_tool_result(self, tool_name: str, result: Any) -> str`
- `def is_available(self) -> bool`
2. 让现有 `ContextCompressor` 实现该 Protocol添加 `compress_tool_result` 方法,默认返回 `str(result)`
3. 新增 `create_compressor(config: dict | None = None) -> CompressionStrategy | None` 工厂函数:
- config 为 None 或空 → 返回 None不压缩
- config.provider == "headroom" 且 headroom-ai 已安装 → 返回 HeadroomCompressor
- config.provider == "headroom" 但未安装 → 警告并降级到 ContextCompressor
- config.provider == "summary" 或默认 → 返回 ContextCompressor
**Patterns to follow**: `src/agentkit/telemetry/setup.py` 的 setup_telemetry() 模式 — 配置驱动 + ImportError 降级
**Test scenarios**:
- ContextCompressor 满足 CompressionStrategy Protocolisinstance 检查)
- create_compressor(None) 返回 None
- create_compressor({"provider": "summary"}) 返回 ContextCompressor 实例
- create_compressor({"provider": "headroom"}) 在 headroom-ai 未安装时降级到 ContextCompressor 并记录警告
- create_compressor({"provider": "headroom"}) 在 headroom-ai 已安装时返回 HeadroomCompressor 实例
- ContextCompressor.compress_tool_result() 默认返回 str(result)
**Verification**: 所有测试通过Protocol 接口可被 mypy 检查
---
### U2. HeadroomCompressor 实现
**Goal**: 实现 HeadroomCompressor 类,封装 headroom-ai Library 模式 API支持工具输出压缩和 CCR 检索。
**Dependencies**: U1
**Files**:
- `src/agentkit/core/headroom_compressor.py` — 新增HeadroomCompressor 类
- `src/agentkit/core/__init__.py` — 修改:导出 CompressionStrategy, create_compressor, HeadroomCompressor
- `tests/unit/test_headroom_compressor.py` — 新增HeadroomCompressor 完整测试
**Approach**:
1. 模块级 `_HEADROOM_AVAILABLE` 标志(参照 Crawl4AI 模式)
2. `HeadroomCompressor` 类实现 CompressionStrategy Protocol
- `__init__(config: dict)` — 接收压缩配置compressors 列表、ccr_ttl、model 等)
- `compress(messages)` — 对 messages 中 role=tool 的消息调用 headroom.compress(),其他消息原样保留
- `compress_tool_result(tool_name, result)` — 根据内容类型路由到 SmartCrusher/CodeCompressor返回压缩文本 + CCR 哈希
- `is_available()``_HEADROOM_AVAILABLE`
- `retrieve(ccr_hash: str, query: str)` → 从 CCR 缓存取回原始数据
3. 内容类型路由逻辑:
- 检测 result 是否为 JSONtry json.loads→ SmartCrusher
- 检测是否为代码(常见代码模式匹配)→ CodeCompressor
- 其他 → 不压缩,原样返回
4. CCR 哈希附加格式:`[compressed content]\n<!-- CCR:hash=abc123 -->`
5. 配置项:
- `enabled: bool` — 开关
- `provider: "headroom"` — 标识
- `compressors: ["smart_crusher", "code_compressor"]` — 启用的压缩器
- `ccr_ttl: int` — CCR 缓存 TTL默认 300
- `min_length: int` — 最小压缩长度(字符),短于此不压缩,默认 500
- `model: str` — 传给 headroom 的模型名,用于 token 估算
**Patterns to follow**: `src/agentkit/tools/web_crawl.py` 的 _CRAWL4AI_AVAILABLE 降级模式
**Test scenarios**:
- HeadroomCompressor 未安装 headroom-ai 时 is_available() 返回 False
- compress() 对 role=tool 消息压缩,其他消息原样保留
- compress_tool_result() 对 JSON 内容使用 SmartCrusher
- compress_tool_result() 对代码内容使用 CodeCompressor
- compress_tool_result() 对短内容(< min_length不压缩
- compress_tool_result() 返回的压缩文本包含 CCR 哈希
- retrieve() 可通过 CCR 哈希取回原始数据
- compress() headroom-ai 未安装时静默返回原消息不抛异常
- 配置项正确传递给 headroom API
**Verification**: 所有测试通过headroom-ai 未安装时测试也能通过mock 或跳过
---
### U3. ReAct 引擎压缩点扩展
**Goal**: ReAct 循环内新增工具结果压缩和增量压缩调用点
**Dependencies**: U1
**Files**:
- `src/agentkit/core/react.py` 修改扩展 compressor 使用点
- `tests/unit/test_react_compression.py` 新增ReAct 循环内压缩测试
**Approach**:
1. `_build_tool_result_message` 方法增加 compressor 参数
- compressor 时调用 `compressor.compress_tool_result(tool_name, result)` 获取压缩内容
- compressor 时保持原逻辑 `str(result)`
2. `_execute_loop` `execute_stream` 中传递 compressor `_build_tool_result_message`
3. while 循环内每步 LLM 调用前检查 conversation 是否超过 token 预算超过则调用 `compressor.compress(conversation)` 增量压缩
4. 新增 `_should_compress(conversation, compressor)` 辅助方法估算当前 conversation token 超过阈值时返回 True
**Patterns to follow**: 现有 `compressor.compress(conversation)` 调用模式L218-222
**Test scenarios**:
- _build_tool_result_message compressor 时行为不变
- _build_tool_result_message compressor 时调用 compress_tool_result
- ReAct 循环内工具结果被压缩后拼入 conversation
- 长对话触发增量压缩conversation 超过 token 预算时
- 短对话不触发增量压缩
- execute_stream 模式下压缩正常工作
- compressor.compress() 异常时不影响 ReAct 循环try/except 保护
**Verification**: ReAct 循环内压缩测试通过现有 ReAct 测试不受影响
---
### U4. 配置集成与 Agent 注入
**Goal**: ServerConfig 中新增 compression 配置 ConfigDrivenAgent 中实例化并注入 compressor
**Dependencies**: U1, U2, U3
**Files**:
- `src/agentkit/server/config.py` 修改ServerConfig 新增 compression 字段
- `src/agentkit/server/app.py` 修改create_app 中创建 compressor 并注入
- `src/agentkit/core/config_driven.py` 修改ConfigDrivenAgent 传递 compressor ReActEngine
- `configs/agentkit.example.yaml` 修改新增 compression 配置示例
- `tests/unit/test_compression_config.py` 新增配置集成测试
**Approach**:
1. ServerConfig.__init__ 新增 `compression: dict[str, Any] | None = None`
2. from_dict 中提取 `data.get("compression", {})`
3. _try_reload_config 中同步更新 compression 字段
4. create_app
- 调用 `create_compressor(server_config.compression)` 创建压缩器
- 存入 `app.state.compressor`
- 传递给 AgentPool
5. ConfigDrivenAgent.__init__ 接收 compressor 参数
6. ConfigDrivenAgent._handle_react 传递 compressor ReActEngine.execute()
**YAML 配置示例**:
```yaml
compression:
enabled: true
provider: headroom # "headroom" | "summary" | None
compressors:
- smart_crusher
- code_compressor
ccr_ttl: 300
min_length: 500
model: default
```
**Patterns to follow**: `src/agentkit/server/config.py` telemetry 配置模式
**Test scenarios**:
- ServerConfig 解析 compression 配置
- compression 为空时 create_compressor 返回 None
- compression.provider=headroom 且已安装时创建 HeadroomCompressor
- compression.provider=headroom 且未安装时降级到 ContextCompressor
- create_app 正确注入 compressor app.state
- ConfigDrivenAgent 传递 compressor ReActEngine
- 配置热重载时 compression 字段同步更新
- agentkit.yaml 中无 compression 段时系统正常工作
**Verification**: 端到端配置测试通过 compression 配置时向后兼容
---
### U5. CCR 检索工具注册
**Goal**: HeadroomCompressor 启用时自动注册 headroom_retrieve 工具到 ToolRegistry
**Dependencies**: U2, U4
**Files**:
- `src/agentkit/tools/headroom_retrieve.py` 新增HeadroomRetrieveTool
- `src/agentkit/tools/__init__.py` 修改条件导出
- `src/agentkit/server/app.py` 修改条件注册 headroom_retrieve 工具
- `tests/unit/test_headroom_retrieve_tool.py` 新增检索工具测试
**Approach**:
1. 新增 `HeadroomRetrieveTool(Tool)`
- name: "headroom_retrieve"
- description: "Retrieve original uncompressed data from CCR cache by hash or query"
- input_schema: `{ccr_hash: str, query: str}`至少一个
- execute: 调用 `compressor.retrieve(ccr_hash, query)` 返回原始数据
2. create_app compressor HeadroomCompressor 实例时创建并注册 HeadroomRetrieveTool
3. HeadroomRetrieveTool 持有 compressor 引用execute 时调用 compressor.retrieve()
4. headroom-ai 未安装时不注册此工具
**Patterns to follow**: `src/agentkit/tools/baidu_search.py` Tool 实现模式
**Test scenarios**:
- HeadroomRetrieveTool 构造和属性
- execute 传入 ccr_hash 返回原始数据
- execute 传入 query 返回匹配数据
- execute 传入无效 hash 返回错误信息
- headroom-ai 未安装时工具不注册
- HeadroomCompressor 时工具不注册
- 工具 schema 正确name, description, input_schema
**Verification**: 工具注册和检索功能测试通过
---
### U6. GEO Pipeline 压缩验证与文档
**Goal**: 验证 GEO Pipeline Headroom 压缩下的端到端工作更新配置文档
**Dependencies**: U1, U2, U3, U4, U5
**Files**:
- `tests/integration/test_geo_compression.py` 新增GEO Pipeline 压缩集成测试
- `configs/agentkit.example.yaml` 修改完整 compression 配置示例
**Approach**:
1. 编写 GEO Pipeline 端到端压缩测试
- 启用 Headroom 压缩执行完整 7 GEO Pipeline
- 验证每步工具输出被压缩
- 验证 CCR 检索可取回原始数据
- 验证最终输出质量不受压缩影响
2. 对比测试同一任务压缩 vs 不压缩的 token 消耗
3. 更新 agentkit.example.yaml 添加完整 compression 配置段和注释
**Test scenarios**:
- GEO Pipeline 启用压缩后端到端执行成功
- 工具输出baidu_search, web_crawl, schema_extract, schema_generate被压缩
- headroom_retrieve 可取回原始搜索结果
- 压缩后 Pipeline 输出与不压缩时语义一致
- compression.enabled=false Pipeline 行为与之前完全一致
**Verification**: 集成测试通过配置文档完整
---
## Scope Boundaries
### In Scope
- CompressionStrategy Protocol 定义和工厂函数
- HeadroomCompressor 实现SmartCrusher + CodeCompressor
- ReAct 循环内工具结果压缩和增量压缩
- ServerConfig compression 配置
- CCR headroom_retrieve 工具
- GEO Pipeline 压缩验证
### Deferred to Follow-Up Work
- Kompress-base 文本压缩模型集成 PyTorch体积过大
- CacheAligner KV Cache 前缀稳定化需深入理解各 LLM Provider 的缓存机制
- 压缩效果 A/B 测试框架需真实 API 调用对比属于产品验证范畴
- Agent 共享压缩上下文Headroom SharedContext需多 Agent 架构先就绪
- 压缩指标 Dashboard Grafana/Prometheus 集成属于运维范畴
- headroom learn 自学习优化需长期运行数据积累
---
## Risks & Dependencies
| 风险 | 影响 | 缓解 |
|------|------|------|
| headroom-ai Beta 版本 API 可能 break | 压缩功能失效 | 锁定 minor 版本 `>=0.22,<0.23`try/except 保护所有调用 |
| SmartCrusher GEO 结构化数据过度压缩 | 引用检测丢失关键字段 | min_length 阈值 + CCR 可逆 + 默认关闭 |
| 压缩增加延迟 | ReAct 循环变慢 | Headroom 本地运行毫秒级延迟异步调用 |
| ConfigDrivenAgent 修改影响现有 Agent | 回归 | compressor 默认 None向后兼容测试 |
| CCR 缓存内存占用 | 长时间运行内存膨胀 | ccr_ttl 默认 300 LRU 淘汰 |
---
## Open Questions
- headroom-ai compress() 是否为 async若为 sync需用 asyncio.to_thread() 包装 实现时验证
- SmartCrusher 对中文 JSON 的压缩效果如何需实际测试 延迟到 U6 集成验证