689 lines
25 KiB
Markdown
689 lines
25 KiB
Markdown
---
|
||
status: active
|
||
date: 2026-06-05
|
||
origin: 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 框架":
|
||
|
||
1. **服务化安全缺失** — 无认证、无限流、CORS 配置不当、SSRF 风险
|
||
2. **异步任务占位符** — 任务状态查询返回 placeholder,同步阻塞调用
|
||
3. **流式输出不支持** — 长时间 ReAct 循环无中间进展反馈
|
||
4. **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**:
|
||
1. 新建 `middleware.py`,实现 `APIKeyAuthMiddleware` 类(Starlette middleware 接口)
|
||
2. 从环境变量 `AGENTKIT_API_KEY` 读取密钥,未设置时跳过认证(开发模式)
|
||
3. 验证 `X-API-Key` 请求头,不匹配时返回 401
|
||
4. 白名单路径:`/api/v1/health` 不需要认证
|
||
5. 修改 `app.py`:
|
||
- 移除 `allow_credentials=True`(与 `allow_origins=["*"]` 冲突)
|
||
- 添加 `app.add_middleware(APIKeyAuthMiddleware)`
|
||
6. 在 `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**:
|
||
1. 在 `middleware.py` 中实现 `RateLimiter` 类
|
||
2. 使用 `time.time()` + `defaultdict(list)` 实现固定窗口计数器
|
||
3. 默认限制:60 requests/minute,通过环境变量 `AGENTKIT_RATE_LIMIT_PER_MINUTE` 配置
|
||
4. 基于请求 IP(`request.client.host`)或 API Key 进行独立计数
|
||
5. 超过限制时返回 429 Too Many Requests,响应头包含 `Retry-After`
|
||
6. 在 `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**:
|
||
1. 在 `dispatcher.py` 中添加 `_validate_callback_url(url: str) -> bool` 函数
|
||
2. 使用 `urllib.parse.urlparse` 解析 URL
|
||
3. 校验规则:
|
||
- 协议必须是 `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`
|
||
4. 在 `_trigger_callback()` 中调用验证,无效 URL 记录 warning 并跳过
|
||
5. 对 `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**:
|
||
1. 在 `ConfigDrivenAgent` 类中添加 `_ALLOWED_HANDLER_PREFIXES = ("agentkit.", "app.agent_framework.")`
|
||
2. 在 `_import_handler()` 开头添加前缀校验
|
||
3. 不在白名单中的路径抛出 `ConfigValidationError`
|
||
4. 参考 `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**:
|
||
1. 定义 `TaskState` 枚举:`PENDING`, `RUNNING`, `COMPLETED`, `FAILED`
|
||
2. 定义 `TaskRecord` dataclass:`task_id`, `state`, `input_data`, `output_data`, `error_message`, `created_at`, `updated_at`, `started_at`
|
||
3. 定义 `TaskStore` ABC:`create()`, `update()`, `get()`, `list_tasks()`, `cleanup()`
|
||
4. 实现 `InMemoryTaskStore`:使用 `dict` + `asyncio.Lock` 保证线程安全
|
||
5. 实现 `RedisTaskStore`:使用 Redis hash 存储,TTL 24 小时自动清理
|
||
6. 提供 `create_task_store(redis_url: str | None = None) -> TaskStore` 工厂函数
|
||
7. 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**:
|
||
1. 在 `tasks.py` 中注入 `TaskStore`(通过 `req.app.state.task_store`)
|
||
2. 在 `app.py` 的 `create_app()` 中初始化 `task_store` 并设置到 `app.state`
|
||
3. 修改 `submit_task` 路由:
|
||
- 生成 `task_id`,创建 `TaskRecord(PENDING)` 存入 TaskStore
|
||
- 使用 `asyncio.create_task()` 后台执行任务
|
||
- 立即返回 `{"task_id": task_id, "status": "PENDING"}`
|
||
4. 后台任务逻辑:
|
||
- 更新 TaskStore 为 RUNNING
|
||
- 执行 `agent.execute(task)`
|
||
- 更新 TaskStore 为 COMPLETED/FAILED,存储 output_data
|
||
- 运行 quality gate 和 output standardizer(存储结果)
|
||
5. 添加可选参数 `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**:
|
||
1. 修改 `get_task_status` 路由:
|
||
- 从 TaskStore 查询 task_id
|
||
- 返回 `{"task_id": ..., "status": "...", "created_at": "...", "updated_at": "..."}`
|
||
- 不存在时返回 404
|
||
2. 新增 `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**:
|
||
1. 在 `protocol.py` 中添加 `LLMStreamChunk` dataclass:
|
||
- `content: str`(增量文本)
|
||
- `tool_calls: list[ToolCall] | None`
|
||
- `finish_reason: str | None`(`stop`, `tool_calls`, `length`)
|
||
- `usage: TokenUsage | None`(仅在最后一个 chunk 有值)
|
||
2. 在 `LLMProvider` ABC 中添加 `stream()` 抽象方法:
|
||
- `async def stream(request: LLMRequest) -> AsyncIterator[LLMStreamChunk]`
|
||
3. 在 `OpenAICompatibleProvider` 中实现 `stream()`:
|
||
- 使用 `httpx.AsyncClient.stream()` 发送请求
|
||
- 解析 SSE 格式响应(`data: {...}` 行)
|
||
- yield `LLMStreamChunk` 对象
|
||
4. 在 `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**:
|
||
1. 在 `protocol.py` 中添加 `ReActEvent` dataclass:
|
||
- `event_type: str`(`think_start`, `think_end`, `tool_call`, `tool_result`, `final_answer`)
|
||
- `step: int`
|
||
- `data: dict`(事件具体数据)
|
||
- `timestamp: datetime`
|
||
2. 在 `ReActEngine` 中添加 `execute_streaming()` 方法:
|
||
- 参数与 `execute()` 相同,返回 `AsyncIterator[ReActEvent]`
|
||
- Think 前 yield `think_start` 事件
|
||
- 调用 LLM stream 后 yield `think_end` 事件
|
||
- 每个工具调用 yield `tool_call` 事件
|
||
- 工具执行完成后 yield `tool_result` 事件
|
||
- 最终答案 yield `final_answer` 事件
|
||
3. 保持原有 `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**:
|
||
1. 新建 `streaming.py`,实现 `POST /api/v1/tasks/stream` 端点:
|
||
- 使用 `StreamingResponse` + `text/event-stream` content type
|
||
- 后台执行任务,调用 `react_engine.execute_streaming()`
|
||
- 每个 `ReActEvent` 序列化为 SSE 格式:`event: <type>\ndata: <json>\n\n`
|
||
- 完成后发送 `event: done\ndata: <json>\n\n`
|
||
2. 在 `app.py` 中注册 streaming router
|
||
3. 在 `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**:
|
||
1. 在 `BaseAgent` 中添加 Evolution 相关属性:
|
||
- `_reflector: Reflector | None`
|
||
- `_prompt_optimizer: PromptOptimizer | None`
|
||
- `_ab_tester: ABTester | None`
|
||
- `_evolution_store: EvolutionStore | None`
|
||
- `_evolution_enabled: bool = False`
|
||
2. 在 `BaseAgent` 中添加 `use_evolution()` 方法:
|
||
- 接受 `reflector`, `prompt_optimizer`, `ab_tester`, `evolution_store` 参数
|
||
- 设置所有 Evolution 组件
|
||
- 设置 `_evolution_enabled = True`
|
||
3. 修改 `BaseAgent.execute()` 方法:
|
||
- 在 `on_task_complete()` 之后,如果 `_evolution_enabled` 为 True:
|
||
- 调用 `EvolutionMixin.evolve_after_task(task, result)`(非阻塞,`asyncio.create_task()`)
|
||
4. 在 `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**:
|
||
1. 在 `AgentConfig` 中添加 `evolution: dict[str, Any] | None` 字段
|
||
2. 定义 `EvolutionConfig` dataclass:
|
||
- `enabled: bool = False`
|
||
- `reflect_after_task: bool = True`
|
||
- `ab_test_threshold: float = 0.95`
|
||
- `max_optimization_rounds: int = 3`
|
||
3. 在 `SkillConfig` 中继承 evolution 配置
|
||
4. 修改 `ConfigDrivenAgent.__init__()`:
|
||
- 从 config.evolution 解析 EvolutionConfig
|
||
- 如果 `evolution.enabled = True`,自动创建默认组件并调用 `use_evolution()`
|
||
- 默认组件:Reflector(启发式评分)、PromptOptimizer、ABTester、EvolutionStore(内存模式)
|
||
5. 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(无依赖,可与任何单元并行)
|
||
```
|