25 KiB
| status | date | origin |
|---|---|---|
| active | 2026-06-05 | docs/brainstorms/2026-06-05-agentkit-architecture-gap-analysis-requirements.md |
AgentKit v2 Phase 2: 架构完善实施计划
类型: refactor
文件: docs/plans/2026-06-05-006-refactor-agentkit-v2-phase2-plan.md
深度: Deep — 跨模块改造,涉及安全、异步、流式、进化 4 个层面
问题框架
AgentKit v2 Phase 1 已实现 12 个核心模块、535 个测试通过,但存在 4 个关键缺口使其无法被称为"生产就绪的标准 Agent 框架":
- 服务化安全缺失 — 无认证、无限流、CORS 配置不当、SSRF 风险
- 异步任务占位符 — 任务状态查询返回 placeholder,同步阻塞调用
- 流式输出不支持 — 长时间 ReAct 循环无中间进展反馈
- Evolution 未集成 — 自我进化代码完整但未接入 Agent 生命周期
本计划按 B → D → C → A 顺序补齐这 4 个缺口。(需求来源见 origin 文档)
架构总览
+------------------------+
| User / Consumer |
+-----------+------------+
|
+-----------v------------+
| AgentKit Server |
| [Auth + Rate Limit] | ← Phase B 新增
+-----------+------------+
|
+-----------v------------+
| Task Manager |
| [Async + Streaming] | ← Phase D + C 新增
+-----------+------------+
|
+----------+----------+----------+----------+
| | | | |
+------v---+ +---v----+ +---v----+ +---v----+ |
| ReAct | | Skill | |Quality | | Intent | |
| [Stream] | | System | | Gate | | Router | |
+----+-----+ +--------+ +--------+ +--------+ |
| |
+----v------------------------------------------v----+
| ConfigDrivenAgent / BaseAgent |
| [+ Evolution Hooks] | ← Phase A 新增
+------+---------+---------+---------+---------+------+
| | | | |
+------v---+ +---v----+ +---v----+ +---v----+ +---v----+
| LLM | | Tool | | Memory | | MCP | |Pipeline|
| [Stream] | | System | | System | | Bridge | |Engine |
+----------+ +--------+ +--------+ +--------+ +--------+
关键技术决策(复用 origin 文档 KTD1-KTD5)
| 决策 | 选择 | 理由 |
|---|---|---|
| 认证方案 | API Key(非 JWT/OAuth) | 服务间调用,API Key 足够简单有效 |
| 速率限制 | 内存计数器(非 Redis) | 单实例足够,后续可升级 |
| 异步存储 | Redis + 内存降级 | 已有 Redis 依赖 |
| 流式协议 | SSE(非 WebSocket) | 单向推送足够,HTTP 兼容性好 |
| Evolution | 可选集成 | 通过 YAML evolution.enabled 控制 |
高层次技术设计
中间件链(Phase B)
Request → CORS Middleware → API Key Auth → Rate Limiter → Route Handler
↓ 401 ↓ 429
Unauthorized Too Many Requests
异步任务流(Phase D)
POST /tasks → 生成 task_id → 存入 TaskStore(PENDING)
→ 后台 asyncio.create_task() 执行
→ 更新 TaskStore(RUNNING → COMPLETED/FAILED)
→ 返回 {"task_id": "...", "status": "PENDING"}
GET /tasks/{id} → 查询 TaskStore → 返回真实状态
GET /tasks/{id}/result → 查询 TaskStore → 返回结果或 404
流式输出流(Phase C)
POST /tasks/stream → SSE endpoint
→ 后台执行任务
→ 每步发出事件:
event: step
data: {"type": "think|act|observe", "step": 1, "content": "..."}
→ 完成时发出:
event: done
data: {"status": "completed", "output": {...}}
Evolution 生命周期钩子(Phase A)
BaseAgent.execute():
on_task_start()
handle_task()
quality_gate → retry
on_task_complete()
└─→ [NEW] evolve_after_task() ← EvolutionMixin
└─→ Reflector.reflect()
└─→ PromptOptimizer.optimize() [if suggestions]
└─→ ABTester.evaluate() [if optimized]
└─→ EvolutionStore.apply/rollback()
输出结构
src/agentkit/
├── server/
│ ├── middleware.py # NEW: Auth + Rate Limit 中间件
│ ├── task_store.py # NEW: 任务状态存储
│ ├── routes/
│ │ └── streaming.py # NEW: SSE 流式端点
│ ├── app.py # MODIFIED: 注册中间件
│ ├── client.py # MODIFIED: 添加流式 + 异步方法
│ └── routes/
│ └── tasks.py # MODIFIED: 异步任务 + 状态查询
├── core/
│ ├── base.py # MODIFIED: 集成 Evolution
│ ├── dispatcher.py # MODIFIED: Callback URL 验证
│ ├── config_driven.py # MODIFIED: handler 白名单 + evolution 配置
│ └── protocol.py # MODIFIED: 新增 TaskState 枚举
├── llm/
│ ├── gateway.py # MODIFIED: 新增 stream() 方法
│ └── providers/
│ └── openai.py # MODIFIED: 支持 stream=True
├── skills/
│ └── base.py # MODIFIED: 添加 evolution 配置
├── core/
│ └── react.py # MODIFIED: 新增 execute_streaming()
└── evolution/ # 现有代码,无需修改
Implementation Units
U1. CORS 修复 + API Key 认证中间件
Goal: 修复 CORS 配置冲突,添加 API Key 认证保护所有 API 端点(健康检查除外)。
Requirements: R1, R3
Dependencies: 无
Files:
- Create:
src/agentkit/server/middleware.py - Modify:
src/agentkit/server/app.py - Test:
tests/unit/test_server_middleware.py
Approach:
- 新建
middleware.py,实现APIKeyAuthMiddleware类(Starlette middleware 接口) - 从环境变量
AGENTKIT_API_KEY读取密钥,未设置时跳过认证(开发模式) - 验证
X-API-Key请求头,不匹配时返回 401 - 白名单路径:
/api/v1/health不需要认证 - 修改
app.py:- 移除
allow_credentials=True(与allow_origins=["*"]冲突) - 添加
app.add_middleware(APIKeyAuthMiddleware)
- 移除
- 在
create_app()中添加api_key: str | None = None参数,允许程序化配置
Patterns to follow: Starlette BaseHTTPMiddleware 模式,参考 FastAPI 中间件文档
Test scenarios:
- 无 API Key 访问受保护端点 → 401 Unauthorized
- 错误 API Key → 401 Unauthorized
- 正确 API Key → 200 OK
- 健康检查端点无需 API Key → 200 OK
- AGENTKIT_API_KEY 未设置时 → 跳过认证(开发模式)
- 程序化传入 api_key 参数 → 使用传入的值
Verification: pytest tests/unit/test_server_middleware.py -v 全部通过,现有测试不受影响
U2. 速率限制中间件
Goal: 添加基于固定窗口的速率限制,防止 LLM 成本耗尽。
Requirements: R2
Dependencies: U1(中间件基础设施)
Files:
- Modify:
src/agentkit/server/middleware.py - Test:
tests/unit/test_server_middleware.py(追加)
Approach:
- 在
middleware.py中实现RateLimiter类 - 使用
time.time()+defaultdict(list)实现固定窗口计数器 - 默认限制:60 requests/minute,通过环境变量
AGENTKIT_RATE_LIMIT_PER_MINUTE配置 - 基于请求 IP(
request.client.host)或 API Key 进行独立计数 - 超过限制时返回 429 Too Many Requests,响应头包含
Retry-After - 在
app.py中注册速率限制中间件(在 Auth 之后)
Test scenarios:
- 请求在限制内 → 正常通过
- 超过限制 → 429 Too Many Requests
Retry-After响应头正确设置- 不同 IP 独立计数
- 时间窗口过后计数器重置
- 可配置 rate_limit_per_minute
Verification: 新增测试通过,不影响现有路由测试
U3. Callback URL SSRF 防护
Goal: 验证 TaskDispatcher 的 callback URL,防止 SSRF 攻击。
Requirements: R4
Dependencies: 无
Files:
- Modify:
src/agentkit/core/dispatcher.py - Test:
tests/unit/test_dispatcher.py(追加)
Approach:
- 在
dispatcher.py中添加_validate_callback_url(url: str) -> bool函数 - 使用
urllib.parse.urlparse解析 URL - 校验规则:
- 协议必须是
http或https - 主机不能是内网 IP(127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1)
- 主机不能是
localhost
- 协议必须是
- 在
_trigger_callback()中调用验证,无效 URL 记录 warning 并跳过 - 对
socket.gethostbyname()做 try/except 防止 DNS 解析失败崩溃
Test scenarios:
- 合法公网 URL(如
https://example.com/callback)→ 验证通过 - localhost URL → 拒绝
- 127.0.0.1 URL → 拒绝
- 10.x.x.x 内网 URL → 拒绝
- 192.168.x.x 内网 URL → 拒绝
- ftp:// 协议 → 拒绝
- file:// 协议 → 拒绝
- 无效 URL 格式 → 拒绝
Verification: 新增测试通过,现有 dispatcher 测试不受影响
U4. custom_handler 模块前缀白名单
Goal: 为 ConfigDrivenAgent._import_handler() 添加模块前缀白名单,防止任意代码执行。
Requirements: R4(安全加固补充)
Dependencies: 无
Files:
- Modify:
src/agentkit/core/config_driven.py - Test:
tests/unit/test_config_driven.py(追加)
Approach:
- 在
ConfigDrivenAgent类中添加_ALLOWED_HANDLER_PREFIXES = ("agentkit.", "app.agent_framework.") - 在
_import_handler()开头添加前缀校验 - 不在白名单中的路径抛出
ConfigValidationError - 参考
QualityGate._import_validator()的白名单实现模式
Test scenarios:
agentkit.xxx.handler→ 允许app.agent_framework.handlers.xxx→ 允许os.system→ 拒绝(ConfigValidationError)subprocess.run→ 拒绝- 空路径 → 拒绝
Verification: 新增测试通过
U5. 任务状态存储
Goal: 实现任务状态存储,支持 Redis 和内存两种后端。
Requirements: R5, R7
Dependencies: 无
Files:
- Create:
src/agentkit/server/task_store.py - Test:
tests/unit/test_task_store.py
Approach:
- 定义
TaskState枚举:PENDING,RUNNING,COMPLETED,FAILED - 定义
TaskRecorddataclass:task_id,state,input_data,output_data,error_message,created_at,updated_at,started_at - 定义
TaskStoreABC:create(),update(),get(),list_tasks(),cleanup() - 实现
InMemoryTaskStore:使用dict+asyncio.Lock保证线程安全 - 实现
RedisTaskStore:使用 Redis hash 存储,TTL 24 小时自动清理 - 提供
create_task_store(redis_url: str | None = None) -> TaskStore工厂函数 - Redis 不可用时自动降级到 InMemory
Patterns to follow: 参考 WorkingMemory 的 Redis 模式和 UsageTracker 的内存模式
Test scenarios:
- InMemoryTaskStore: create → get 返回正确记录
- InMemoryTaskStore: update 状态从 PENDING → RUNNING → COMPLETED
- InMemoryTaskStore: get 不存在的 task_id 返回 None
- InMemoryTaskStore: list_tasks 返回所有记录
- InMemoryTaskStore: 并发安全(asyncio.Lock)
- RedisTaskStore: create → get 返回正确记录(skip if no Redis)
- 工厂函数: Redis 可用时返回 RedisTaskStore
- 工厂函数: Redis 不可用时降级到 InMemoryTaskStore
Verification: pytest tests/unit/test_task_store.py -v 全部通过
U6. 异步任务执行
Goal: POST /api/v1/tasks 改为异步提交,100ms 内返回 task_id。
Requirements: R5, R6
Dependencies: U5
Files:
- Modify:
src/agentkit/server/routes/tasks.py - Test:
tests/unit/test_server_routes.py(更新现有测试) - Test:
tests/integration/test_server_e2e.py(更新)
Approach:
- 在
tasks.py中注入TaskStore(通过req.app.state.task_store) - 在
app.py的create_app()中初始化task_store并设置到app.state - 修改
submit_task路由:- 生成
task_id,创建TaskRecord(PENDING)存入 TaskStore - 使用
asyncio.create_task()后台执行任务 - 立即返回
{"task_id": task_id, "status": "PENDING"}
- 生成
- 后台任务逻辑:
- 更新 TaskStore 为 RUNNING
- 执行
agent.execute(task) - 更新 TaskStore 为 COMPLETED/FAILED,存储 output_data
- 运行 quality gate 和 output standardizer(存储结果)
- 添加可选参数
sync: bool = False,当sync=true时保持原有同步行为
Test scenarios:
- 提交任务 → 100ms 内返回 task_id + PENDING
- 后台任务执行 → TaskStore 状态变为 COMPLETED
- 后台任务失败 → TaskStore 状态变为 FAILED
- sync=true 参数 → 同步执行(原有行为)
- 输入验证失败 → 400/413 错误(同步返回)
Verification: 路由测试通过,E2E 测试验证异步行为
U7. 任务状态查询 + 结果获取
Goal: GET /api/v1/tasks/{task_id} 返回真实状态,新增结果获取端点。
Requirements: R6, R7
Dependencies: U5, U6
Files:
- Modify:
src/agentkit/server/routes/tasks.py - Test:
tests/unit/test_server_routes.py(追加)
Approach:
- 修改
get_task_status路由:- 从 TaskStore 查询 task_id
- 返回
{"task_id": ..., "status": "...", "created_at": "...", "updated_at": "..."} - 不存在时返回 404
- 新增
GET /api/v1/tasks/{task_id}/result路由:- 从 TaskStore 查询 task_id
- 如果状态是 COMPLETED → 返回完整结果(含 quality_result, standard_output)
- 如果状态是 PENDING/RUNNING → 返回 202 Accepted +
{"status": "..."} - 如果状态是 FAILED → 返回错误信息
- 不存在时返回 404
Test scenarios:
- 查询存在的 task_id → 返回正确状态
- 查询不存在的 task_id → 404
- PENDING 状态查询结果 → 202 Accepted
- COMPLETED 状态查询结果 → 返回完整输出
- FAILED 状态查询结果 → 返回错误信息
Verification: 路由测试通过
U8. LLM Gateway 流式支持
Goal: LLM Gateway 支持 streaming 模式,逐 chunk 返回 LLM 响应。
Requirements: R8
Dependencies: 无
Files:
- Modify:
src/agentkit/llm/gateway.py - Modify:
src/agentkit/llm/protocol.py - Modify:
src/agentkit/llm/providers/openai.py - Test:
tests/unit/test_llm_gateway.py(追加) - Test:
tests/unit/test_llm_provider.py(追加)
Approach:
- 在
protocol.py中添加LLMStreamChunkdataclass:content: str(增量文本)tool_calls: list[ToolCall] | Nonefinish_reason: str | None(stop,tool_calls,length)usage: TokenUsage | None(仅在最后一个 chunk 有值)
- 在
LLMProviderABC 中添加stream()抽象方法:async def stream(request: LLMRequest) -> AsyncIterator[LLMStreamChunk]
- 在
OpenAICompatibleProvider中实现stream():- 使用
httpx.AsyncClient.stream()发送请求 - 解析 SSE 格式响应(
data: {...}行) - yield
LLMStreamChunk对象
- 使用
- 在
LLMGateway中添加stream()方法:- 解析模型别名和 provider
- 调用 provider 的
stream()方法 - 转发 chunk
Patterns to follow: OpenAI Python SDK 的 streaming 模式,response.iter_lines() 解析 SSE
Test scenarios:
- OpenAICompatibleProvider.stream() 逐 chunk yield 内容
- 最后一个 chunk 包含 usage 信息
- finish_reason 为 stop 时流结束
- finish_reason 为 tool_calls 时包含 tool_calls 信息
- LLMGateway.stream() 正确转发 chunk
- 网络错误时抛出 LLMProviderError
Verification: 新增流式测试通过
U9. ReAct Engine 事件流
Goal: ReAct Engine 支持 streaming 事件输出,实时推送 Think/Act/Observe 进展。
Requirements: R9
Dependencies: U8
Files:
- Modify:
src/agentkit/core/react.py - Modify:
src/agentkit/core/protocol.py - Test:
tests/unit/test_react_engine.py(追加)
Approach:
- 在
protocol.py中添加ReActEventdataclass:event_type: str(think_start,think_end,tool_call,tool_result,final_answer)step: intdata: dict(事件具体数据)timestamp: datetime
- 在
ReActEngine中添加execute_streaming()方法:- 参数与
execute()相同,返回AsyncIterator[ReActEvent] - Think 前 yield
think_start事件 - 调用 LLM stream 后 yield
think_end事件 - 每个工具调用 yield
tool_call事件 - 工具执行完成后 yield
tool_result事件 - 最终答案 yield
final_answer事件
- 参数与
- 保持原有
execute()方法不变(向后兼容)
Test scenarios:
- execute_streaming() 按顺序 yield 事件
- Think → Act → Observe 事件顺序正确
- 最终 yield final_answer 事件
- 事件中包含 step 编号和 timestamp
- 工具调用失败时 yield tool_result(含 error)
- 与 execute() 结果一致(同一输入产生相同输出)
Verification: 新增流式测试通过
U10. SSE 流式端点 + Client SDK
Goal: Server 提供 SSE 流式端点,Client SDK 支持流式消费。
Requirements: R10
Dependencies: U8, U9
Files:
- Create:
src/agentkit/server/routes/streaming.py - Modify:
src/agentkit/server/app.py - Modify:
src/agentkit/server/client.py - Test:
tests/unit/test_streaming_routes.py - Test:
tests/unit/test_client_streaming.py
Approach:
- 新建
streaming.py,实现POST /api/v1/tasks/stream端点:- 使用
StreamingResponse+text/event-streamcontent type - 后台执行任务,调用
react_engine.execute_streaming() - 每个
ReActEvent序列化为 SSE 格式:event: <type>\ndata: <json>\n\n - 完成后发送
event: done\ndata: <json>\n\n
- 使用
- 在
app.py中注册 streaming router - 在
client.py中添加submit_task_streaming()方法:- 使用
httpx.AsyncClient.stream()消费 SSE - yield
ReActEvent对象 - 支持 async iterator 协议
- 使用
Patterns to follow: Starlette EventSourceResponse 或 StreamingResponse,参考 FastAPI SSE 文档
Test scenarios:
- SSE 端点返回 text/event-stream content type
- 事件按 Think → Act → Observe → done 顺序
- 每个事件包含正确的 event type 和 JSON data
- Client SDK 消费 SSE 流
- Client SDK 正确解析 ReActEvent
- 任务失败时发送 error 事件
Verification: 流式路由和客户端测试通过
U11. Evolution 生命周期钩子集成
Goal: 将 EvolutionMixin 集成到 BaseAgent,任务完成后自动触发进化流程。
Requirements: R11
Dependencies: 无
Files:
- Modify:
src/agentkit/core/base.py - Modify:
src/agentkit/evolution/lifecycle.py - Test:
tests/unit/test_evolution_lifecycle.py(更新) - Test:
tests/unit/test_base_agent_v2.py(追加)
Approach:
- 在
BaseAgent中添加 Evolution 相关属性:_reflector: Reflector | None_prompt_optimizer: PromptOptimizer | None_ab_tester: ABTester | None_evolution_store: EvolutionStore | None_evolution_enabled: bool = False
- 在
BaseAgent中添加use_evolution()方法:- 接受
reflector,prompt_optimizer,ab_tester,evolution_store参数 - 设置所有 Evolution 组件
- 设置
_evolution_enabled = True
- 接受
- 修改
BaseAgent.execute()方法:- 在
on_task_complete()之后,如果_evolution_enabled为 True:- 调用
EvolutionMixin.evolve_after_task(task, result)(非阻塞,asyncio.create_task())
- 调用
- 在
- 在
EvolutionMixin.evolve_after_task()中添加开关检查:- 如果任何组件为 None,跳过对应步骤并记录 debug 日志
Patterns to follow: 参考 use_tool(), use_memory() 的插件注入模式
Test scenarios:
- evolution_enabled=False → 不触发进化流程
- evolution_enabled=True → evolve_after_task 被调用
- Reflector 为 None → 跳过反思
- 完整流程:Reflect → Optimize → AB Test → Apply
- 进化流程非阻塞(不阻塞 execute 返回)
- EvolutionMixin 混入 ConfigDrivenAgent 正常工作
Verification: Evolution 集成测试通过,现有测试不受影响
U12. Evolution 配置化
Goal: Agent 可通过 YAML 配置启用/禁用 Evolution 功能。
Requirements: R12
Dependencies: U11
Files:
- Modify:
src/agentkit/core/config_driven.py - Modify:
src/agentkit/skills/base.py - Test:
tests/unit/test_config_driven.py(追加) - Test:
tests/unit/test_skill_config.py(追加)
Approach:
- 在
AgentConfig中添加evolution: dict[str, Any] | None字段 - 定义
EvolutionConfigdataclass:enabled: bool = Falsereflect_after_task: bool = Trueab_test_threshold: float = 0.95max_optimization_rounds: int = 3
- 在
SkillConfig中继承 evolution 配置 - 修改
ConfigDrivenAgent.__init__():- 从 config.evolution 解析 EvolutionConfig
- 如果
evolution.enabled = True,自动创建默认组件并调用use_evolution() - 默认组件:Reflector(启发式评分)、PromptOptimizer、ABTester、EvolutionStore(内存模式)
- YAML 配置示例文档化
Test scenarios:
- YAML 中 evolution.enabled=true → Agent 自动启用进化
- YAML 中 evolution.enabled=false → Agent 不启用进化
- YAML 中无 evolution 字段 → 默认不启用
- EvolutionConfig 字段默认值正确
- SkillConfig 继承 evolution 配置
Verification: 配置化测试通过
范围和边界
包含
- Phase B:服务化安全(R1-R4)→ U1-U4
- Phase D:异步任务(R5-R7)→ U5-U7
- Phase C:流式输出(R8-R10)→ U8-U10
- Phase A:Evolution 集成(R11-R12)→ U11-U12
不包含
- GEO 项目的任何改动
- 新的 LLM Provider 实现
- 前端 UI 开发
- 生产环境部署配置(K8s、Prometheus 等)
- pgvector 向量检索实现
推迟到后续工作
- WebSocket 推送(当前使用 SSE)
- Redis 滑动窗口速率限制(当前使用内存计数器)
- Anthropic/Google 原生 Provider
- Evolution 的分布式 A/B 测试
- 任务优先级队列
风险和缓解
| 风险 | 影响 | 缓解 |
|---|---|---|
| 流式输出改动大 | ReAct Engine 需要重构 | 保持原有同步接口不变,新增 streaming 接口 |
| 异步任务需要 Redis | 测试环境可能没有 Redis | InMemoryTaskStore 降级方案 |
| API Key 认证破坏现有测试 | 测试需要传递 API Key | 测试环境不设置 AGENTKIT_API_KEY(跳过认证) |
| Evolution 集成后 Agent 变慢 | 反思和优化增加延迟 | 异步执行(asyncio.create_task),可配置关闭 |
| SSE 端点与现有同步端点冲突 | 路由冲突 | 使用不同路径 /tasks/stream |
测试策略
- TDD 原则:每个单元先写测试,再写实现
- 测试覆盖目标:总测试数 600+(当前 535)
- 分层测试:
- 单元测试:mock 外部依赖,验证逻辑
- 集成测试:使用真实 Redis/PostgreSQL(docker-compose.test.yml)
- E2E 测试:验证完整链路
- 回归保护:每次修改后运行全量测试
执行顺序
Phase B(安全) Phase D(异步任务) Phase C(流式输出) Phase A(Evolution)
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ U1 │ │ U5 │ │ U8 │ │ U11 │
│ Auth│ │Store│ │LLM │ │Hooks│
└──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘
│ └──┬──┘ └──┬──┘ └──┬──┘
┌──▼──┐ ┌▼────┐ ┌─▼───┐ ┌──▼──┐
│ U2 │ │ U6 │ │ U9 │ │ U12 │
│Rate │ │Async│ │React│ │Config│
└─────┘ └──┬──┘ └──┬──┘ └─────┘
└──┬──┘ └──┬──┘
┌────▼────┐ ┌───▼────┐
│ U7 │ │ U10 │
│Status │ │SSE+SDK │
└─────────┘ └────────┘
可并行:U3 + U4(无依赖,可与任何单元并行)