Files
social-app/docs/plans/2026-03-04-agent-hard-reset-design.md
T

202 lines
8.7 KiB
Markdown
Raw 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.
# Agent 后端硬切重构设计
## 目标
- 一次性移除现有 Agent 运行时代码、测试和旧文档契约,避免新旧方案并存。
- 仅从后端重新设计 Agent 体系,不依赖前端实现细节。
- 新方案必须满足以下六项要求:
1. 配置层可通过 `.env` 驱动 LLM API Key。
2. 对话与 resume 通过 Celery 队列处理,不阻塞 Web 主线程。
3. `v1/agent` 仅负责路由组织与服务调用,核心逻辑在 `core/agent`
4. 按 CrewAI 官方模型组织 Agent/Task/Crew/Flow/Tools。
5. 按 AG-UI 协议输出事件,优先使用 `ag-ui-crewai` 适配库。
6. 使用 LiteLLM 统计每次 LLM 调用的 token 和 cost。
## 设计原则
- 单一职责:HTTP 层只做协议和鉴权,编排与执行下沉到核心层。
- 异步优先:长耗时推理、工具调用、恢复流程全部异步化。
- 协议优先:AG-UI 作为唯一事件契约,不维护自定义事件方言。
- 可观测性优先:每次 run、每次 stage、每次 LLM 调用可追踪。
- 配置单一来源:所有密钥和模型配置只走 `core.config.settings`
## 目标架构
### 1) 分层
- `backend/src/v1/agent/`
- `router.py`: 暴露 HTTP/SSE 接口。
- `schemas.py`: 请求/响应 DTO 和输入校验。
- `dependencies.py`: DI 装配。
- `service.py`: 薄服务,仅调用 `core/agent` 应用服务。
- `backend/src/core/agent/`
- `application/`: run/resume 应用服务。
- `domain/`: run 状态机、resume 幂等语义、错误模型。
- `infrastructure/crewai/`: CrewAI Agent/Task/Crew/Flow 装配与执行。
- `infrastructure/agui/`: AG-UI 事件映射与 SSE 序列化。
- `infrastructure/litellm/`: LiteLLM 客户端与 usage/cost 拦截器。
- `infrastructure/queue/`: Celery task producer/consumer。
### 1.1) 配置来源与合并策略
- Agent 运行配置由两部分组成:
- 数据库存量配置:`system_agents`(每种 agent_type 对应 llm 与 llm_config)。
- 静态模板配置:`backend/src/core/config/static/crewai/*.yaml`(角色描述、任务模板、workflow、tools)。
- 合并策略:
- `llm``llm_config``system_agents` 为准。
- prompt 模板、task 描述、flow stage、tool 白名单以 static/crewai 为准。
- 若任一 agent_type 在 `system_agents` 缺失,运行前失败并返回受控错误。
### 2) 核心运行链路
1. `POST /api/v1/agent/runs` 只负责参数校验和鉴权。
2. 路由调用 `AgentRunAppService.enqueue_run()`,写入 run 记录并投递 Celery。
3. Worker 执行 `run_agent_task`
- 读取 run 上下文。
- 构建 CrewAI `Agent/Task/Crew/Flow`
- 通过 `ag-ui-crewai` 将执行事件转为 AG-UI 标准事件。
- 每次 LLM 调用由 LiteLLM 中间层记录 token/cost。
4. 事件落库并发布到事件通道(Redis Stream/Channel)。
5. SSE 接口从事件通道读取并持续推送,直到 `RUN_FINISHED``RUN_ERROR`
### 3) Resume 链路
1. `POST /api/v1/agent/runs/{run_id}/resume` 校验 `interrupt_id` 与决策 payload。
2. 调用 `enqueue_resume()` 投递 `resume_agent_task`
3. Worker 在事务内做并发控制:
- `run_id + interrupt_id` 幂等锁。
- 过期校验与状态迁移。
4. 恢复后继续 CrewAI Flow,事件按 AG-UI 继续输出。
### 4) Session 状态持久化
- 使用 `sessions.state_snapshot` 作为运行态单一快照来源。
- 快照至少包含:
- run 上下文(thread_id、run_id、stage
- pending_tool_callstool_call_id、tool_name、args、status、expires_at
- correlation 索引(tool_call_id -> message_id / step_id
- 所有中断/恢复均以 `state_snapshot` 事务更新为准,避免内存态漂移。
### 5) 会话与消息落库模型
- 会话主表:`sessions`
- 新建 run 时写入:`id/user_id/session_type/status=running/last_activity_at`
- 运行中持续更新:`status``last_activity_at``message_count``total_tokens``total_cost``state_snapshot`
- 运行结束更新:
- 成功:`status=completed`
- 失败:`status=failed`
- 消息表:`messages`
- 用户输入落库为 `role=user`(每次 run 开始时先写入)。
- 模型输出落库为 `role=assistant`(按最终聚合文本落库,保留 metadata 记录增量信息)。
- 工具调用结果落库为 `role=tool`,并写入 `tool_name``metadata.tool_call_id`
- `seq` 由每个 `session_id` 内单调递增分配,满足 `uq_messages_session_seq`
- 计量落库:每次 LLM 调用的 usage/cost 先写消息级,再聚合更新到 session 级。
## 六项要求落地映射
### 要求 1: `.env` 驱动 LLM API Key
- 新增 `LLMSettings``core.config.settings.Settings`,统一定义:
- `SOCIAL_LLM__PROVIDER_KEYS__DASHSCOPE`
- `SOCIAL_LLM__PROVIDER_KEYS__MINIMAX`
- `SOCIAL_LLM__PROVIDER_KEYS__MOONSHOT`
- `SOCIAL_LLM__PROVIDER_KEYS__DEEPSEEK`
- `SOCIAL_LLM__PROVIDER_KEYS__ARK`
- `SOCIAL_LLM__PROVIDER_KEYS__ZAI`
- 禁止 `os.environ` 直接读取密钥。
### 要求 2: 对话和 resume 走 Celery
- Web 层不直接执行编排。
- `run`/`resume` 一律入队,Worker 处理,Web 仅做事件流转发。
- 加入任务级超时、重试、死信策略。
### 要求 3: v1 仅路由与调用
- `v1/agent/service.py` 仅保留应用服务调用和错误映射。
- 任何编排、状态机、工具执行逻辑禁止进入 `v1`
### 要求 4: CrewAI 官方流程
- 采用 CrewAI 原生对象:`Agent``Task``Crew``Flow`
- tools 通过 CrewAI Tool 机制注册,不做平行实现。
- 任务模板与 agent 配置集中化(静态模板 + 运行时拼装)。
- 配置拼装明确依赖 `system_agents + static/crewai`,不再使用双套来源。
### 要求 5: AG-UI + ag-ui-crewai
- 事件集遵循 AG-UI 协议,生命周期闭环:
- `RUN_STARTED`
- 流式消息和工具事件
- 终态 `RUN_FINISHED``RUN_ERROR`
- 优先引入 `ag-ui-crewai` 做 CrewAI 到 AG-UI 的桥接,避免重复造轮子。
### 要求 6: LiteLLM token/cost 统计
- 所有 LLM 调用通过 LiteLLM 统一出入口。
- 按调用粒度记录:`input_tokens``output_tokens``total_tokens``cost``currency`
- 按 run 粒度聚合并落库,支持后续计费和审计。
## 数据与可观测性
- 保留现有 Agent 相关表结构,不在本次硬切做数据库破坏性变更。
- 新增事件日志与调用指标落点(如已有字段不足,后续增量迁移)。
- 日志使用结构化字段:`run_id``task_id``stage``tool_name``llm_model``latency_ms`
- 持久化原则:run/resume 的关键状态变更必须可重放,禁止仅保存在内存。
## 事务边界
- `run` 入口事务:创建或加载 `session` + 写入用户消息。
- `worker` 执行事务(可分阶段短事务):
- 阶段开始:更新 `session.status/state_snapshot`
- LLM 返回:写 assistant/tool 消息 + 更新 token/cost 聚合。
- 中断:写 `pending_tool_calls``state_snapshot` 并提交。
- 完成:更新终态 `session.status` 并提交。
- `resume` 事务:校验 `interrupt_id` 与 ownershipCAS 更新 `state_snapshot`,然后进入后续执行事务。
## 错误处理与安全
- API Key 缺失启动即失败,不进入运行态。
- 外部工具入参统一白名单和 schema 校验。
- resume 决策必须鉴权与会话所有权校验。
- 错误响应遵循 RFC 7807,避免泄漏敏感上下文。
## 工具调用与恢复语义
- 工具分三类:
- 前端工具:由 `RunAgentInput.tools` 提供能力声明,触发 interrupt,由客户端执行并回传 result。
- 后端工具(需审批):先 interrupt 给前端审批;审批通过后由后端执行,不由前端执行。
- 后端工具(直执):后端直接执行。
- 一致性约束:
- 每个 tool_result 必须携带 `tool_call_id`
- 后端仅接受当前 `state_snapshot.pending_tool_calls` 中存在且状态合法的 `tool_call_id`
- 若收到未知/已消费/过期 `tool_call_id`,立即产出 `RUN_ERROR` 并记录审计日志。
## 测试策略
- 单元测试:
- 配置解析与 key 解析
- run/resume 状态机与幂等
- LiteLLM usage 聚合
- 集成测试:
- API 入队
- Worker 消费
- SSE 事件顺序与终态
- E2E
- run 成功链路
- interrupt + resume 链路
- tool 调用链路
## 迁移策略
- 阶段 0(本次):硬切删除旧代码、旧测试、旧文档契约。
- 阶段 1:搭建新架构骨架和最小可运行 run 流程。
- 阶段 2:接入 CrewAI + ag-ui-crewai + LiteLLM 完整链路。
- 阶段 3:补齐可观测性、压测与稳定性治理。
## 验收标准
- 后端仓库不存在旧 `v1/agent``core/agent` 旧实现。
- 所有 Agent 相关旧测试与旧文档契约已移除。
- 新方案设计文档明确覆盖六项要求并可进入实现阶段。