# Agent Runtime Closed Loop E2E Design ## 背景 当前 `test_agent_sse_flow.py` 不能稳定证明真实闭环: - `session_id` 由随机 UUID 生成,导致 `POST /api/v1/agent/runs` 经常 404。 - 测试脚本存在不可达重复代码,诊断信息不完整。 - 未覆盖首聊自动建会话语义,和真实聊天入口不匹配。 目标是验证真实环境下业务闭环是否可用: 1. 用户请求 `agent` 路由 2. 请求进入异步任务 3. runtime 读取 `system_agents` 和 `llm` 配置并构建执行流程 4. 真实 LLM 请求发出并返回 5. `sessions`/`messages` 正确落库 6. 成本和 token 统计正确 7. 事件按 AG-UI 规范发布并可由 `stream_events` 订阅 ## 设计原则 - 真实优先:不使用 mock,不替换 queue/redis/db/llm。 - 双轨验证: - 诊断脚本用于本地排障(快速观察全链路状态)。 - pytest E2E 用例用于可重复回归。 - 明确前置条件:必须先使用 `infra/scripts/app.sh start` 启动 tmux 服务。 - 本地真实 LLM 基线:DashScope Qwen。 ## API 契约调整 ### `POST /api/v1/agent/runs` - 现状:`session_id` 必填且必须存在。 - 新契约:`session_id` 可选。 - 有值:复用现有会话,校验 owner。 - 无值:在服务层先创建会话,再入队 run。 - 响应扩展:返回 `created` 标识是否为首聊自动建会话。 该契约与聊天产品行为一致:用户首条消息即可开始,不需要前置调用创建会话接口。 ## 数据关系与删除语义 - `messages.session_id -> sessions.id` 为外键,且硬删除级联(`ondelete=CASCADE`)。 - 软删除需要补齐级联: - 软删 `sessions` 时,同事务更新对应 `messages.deleted_at`。 - E2E 增加验证,确保软删后默认查询不可见。 ## 测试架构 ### A. 诊断脚本(根目录) 重构 `test_agent_sse_flow.py`: - 增加环境健康检查(web/redis/db)。 - 支持两种模式: - `--new-session`:不传 `session_id`,验证首聊自动创建。 - `--reuse-session `:验证复聊路径。 - 输出结构化阶段日志:HTTP、task_id、SSE 事件、数据库断言、失败根因。 ### B. pytest E2E(`backend/tests/e2e`) 新增 `test_agent_closed_loop_live.py`: - 标记为 `live`,默认不在 CI 执行。 - 用真实 JWT、真实 HTTP 请求、真实 SSE 订阅。 - 断言最小闭环标准: - run 返回 202 - SSE 至少收到 `RUN_STARTED` 与终态(`RUN_FINISHED` 或 `RUN_ERROR`) - `sessions` 状态和计数更新 - `messages` 有新增记录 - token/cost 字段非负且会话聚合一致 ## 验收标准 - `uv run python test_agent_sse_flow.py --new-session` 通过。 - `uv run pytest backend/tests/e2e/test_agent_closed_loop_live.py -v -m live` 通过。 - 首聊场景不需要外部先建 `session_id`。 - 软删除会话后,消息软删除行为与约束一致。 ## 风险与回退 - 真实 LLM 网络抖动会造成不稳定:通过重试和超时策略降低误报。 - 生产契约变更风险:保持字段向后兼容(原 `session_id` 仍可传)。 - 如果新契约引入问题,可临时退回“必传 session_id”路径并保留测试脚本诊断能力。