8.7 KiB
8.7 KiB
Agent 后端硬切重构设计
目标
- 一次性移除现有 Agent 运行时代码、测试和旧文档契约,避免新旧方案并存。
- 仅从后端重新设计 Agent 体系,不依赖前端实现细节。
- 新方案必须满足以下六项要求:
- 配置层可通过
.env驱动 LLM API Key。 - 对话与 resume 通过 Celery 队列处理,不阻塞 Web 主线程。
v1/agent仅负责路由组织与服务调用,核心逻辑在core/agent。- 按 CrewAI 官方模型组织 Agent/Task/Crew/Flow/Tools。
- 按 AG-UI 协议输出事件,优先使用
ag-ui-crewai适配库。 - 使用 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) 核心运行链路
POST /api/v1/agent/runs只负责参数校验和鉴权。- 路由调用
AgentRunAppService.enqueue_run(),写入 run 记录并投递 Celery。 - Worker 执行
run_agent_task:- 读取 run 上下文。
- 构建 CrewAI
Agent/Task/Crew/Flow。 - 通过
ag-ui-crewai将执行事件转为 AG-UI 标准事件。 - 每次 LLM 调用由 LiteLLM 中间层记录 token/cost。
- 事件落库并发布到事件通道(Redis Stream/Channel)。
- SSE 接口从事件通道读取并持续推送,直到
RUN_FINISHED或RUN_ERROR。
3) Resume 链路
POST /api/v1/agent/runs/{run_id}/resume校验interrupt_id与决策 payload。- 调用
enqueue_resume()投递resume_agent_task。 - Worker 在事务内做并发控制:
run_id + interrupt_id幂等锁。- 过期校验与状态迁移。
- 恢复后继续 CrewAI Flow,事件按 AG-UI 继续输出。
4) Session 状态持久化
- 使用
sessions.state_snapshot作为运行态单一快照来源。 - 快照至少包含:
- run 上下文(thread_id、run_id、stage)
- pending_tool_calls(tool_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
- 成功:
- 新建 run 时写入:
- 消息表:
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__DASHSCOPESOCIAL_LLM__PROVIDER_KEYS__MINIMAXSOCIAL_LLM__PROVIDER_KEYS__MOONSHOTSOCIAL_LLM__PROVIDER_KEYS__DEEPSEEKSOCIAL_LLM__PROVIDER_KEYS__ARKSOCIAL_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与 ownership,CAS 更新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并记录审计日志。
- 每个 tool_result 必须携带
测试策略
- 单元测试:
- 配置解析与 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 相关旧测试与旧文档契约已移除。
- 新方案设计文档明确覆盖六项要求并可进入实现阶段。