AI Agent 运行态
AI Agent 由 Backend 统一承载,负责会话、run、消息、事件、工具调用、HITL requirement 和模型调用链路。
运行链路
- Editor 先提交会话 run 或 paused run 的继续请求,再通过独立 SSE 请求订阅事件;内容模型不直接接收图片像素。
- Backend 写入
ai_agent_runs、用户消息和run.started事件,并把执行阶段交给应用级AgentBackgroundRunManager。 - 后台管理器创建独立数据库会话,
pydantic_runner.py在其中创建 Pydantic AIAgent,装配模型、instructions、平台工具和 deps。 pydantic_tools.py把平台工具对象包装为 Pydantic AI Tool,并负责上下文注入和返回值序列化。platform_runtime.py把模型流式事件、工具事件、HITL requirement 和终态事件持久化为平台运行态。- Editor 通过 SSE 实时消费事件;刷新或重连时,从
ai_agent_run_events按事件顺序回放。
普通 Run 的后台能力以单个 Backend 进程为边界。关闭侧栏、切换路由、刷新页面或断开 SSE 只会取消订阅,不会取消执行;重新进入会话后按 event_index 回放。Backend 正常退出会取消仍在运行的进程内任务并写入 AI_RUN_PROCESS_STOPPED,异常退出则由 active-run 空闲超时收敛,不自动重跑。页面变更和图片生成等 external job 的领域执行仍由各自持久化租约队列负责;模型续跑统一由 ai-external-task-coordinator 认领 AiAgentExternalBatch,不能与进程内 Run 管理器合并。
多 Backend 副本部署时,普通 Run 同样遵守上述语义:不承诺跨实例无中断续跑,各副本只收敛本进程遗留 Run。停机后的用户恢复路径是「在原会话重试」或「新建 Run」(继承消息历史与焦点,不继承中断执行栈);详见多 Backend 副本与密钥一致性「普通 AI Run 停机语义」。
事件追加的并发边界
event_index 的单调性由数据库承担:run_event_writer.allocate_run_event_index() 用一条 UPDATE ai_agent_runs SET event_index = event_index + 1 ... RETURNING event_index 原子分配游标,并把后台续跑的写围栏条件并入同一条语句。该语句在 SQLite 与 PostgreSQL 上语义一致,属跨库共享实现,不需要方言分支。
platform_runtime._RUN_EVENT_LOCKS 是进程内按 run 复用的 asyncio.Lock,只用于串行化同一 run 的追加重试路径,避免退避期间的 rollback 与其他追加交错。它不是分布式互斥原语:多副本下只在本进程生效,跨实例单调性完全依赖上述数据库原子递增,不得用它替代 Backend × N 的互斥。同理 _has_sqlite_write_transaction() 只在 SQLite 下可能为真,用途是避免「进程锁 → 数据库写锁」的锁序反转;PostgreSQL 恒为 False,直接走数据库路径。
CP6 评估结论(PostgreSQL 不可跳过进程锁):event_index 的跨进程单调性已由 allocate_run_event_index 的原子 UPDATE ... RETURNING 保证,与本锁无关。本锁串行化的是同一 PlatformRuntime / 同一 AsyncSession 上的并发 append_event 与重试 rollback——SQLAlchemy 会话本身不可并发共用,这与方言无关。只有在改为「每次追加独立会话」之后,才可能在 PostgreSQL 跳过本锁;当前形态下 SQLite 与 PostgreSQL 都必须持有。
事实源
AI 会话、run、事件、消息、工具调用和 HITL 状态写入 Backend 主库 ai_agent_* 表。Redis 不保存 AI run/HITL 事实源,也不保存正在执行的 Python 协程;它只保留预览、截图和构建等临时运行态。
| 表 | 作用 |
|---|---|
ai_agent_sessions | 会话主记录 |
ai_agent_runs | 单次运行状态、输入、聚合内容、错误信息和 Pydantic AI message history |
ai_agent_messages | 会话消息 |
ai_agent_run_events | SSE 事件事实源,按 run_id + event_index 单调排序 |
ai_agent_tool_calls | 可展示工具调用详情 |
ai_agent_requirements | pending/resolved HITL 动作 |
ai_agent_image_attachments | 会话图片附件和 run 绑定 |
ai_image_generation_jobs | 图片生成/编辑持久化任务、租约、进度、取消和输出关联 |
工具确认
发布、构建、跨焦点写入以及批量归档等高风险工具调用按真实业务语义请求确认。项目路由整树更新和展示配置更新属于普通写入,由 update_entity 承载。内容助手不具备永久删除能力;单项归档免确认,两个及以上目标由 archive_entity 在运行时动态触发确认。
工具确认会通过 Pydantic AI deferred approval 暂停 run。平台保存 AgentPendingRequirement,前端展示确认面板;用户确认后,Backend 构造 deferred tool 结果继续同一个 run。
继续 paused run 时需要满足这些条件:
- 当前 active run 状态是
paused。 - pending requirement 的
tool_call_id未过期。 - Backend 以数据库 requirement payload 为准,合并前端提交的
tool_execution。 - 如果旧 run 的 message history 为空,Backend 会使用输入 payload 和 deferred tool call 重建最小上下文,避免继续运行时报空 history。
ask_user 是结构化提问工具,前端回放只解析结构化 JSON 格式。不要再依赖旧的项目符号文本格式。
内容助手会话固定工作空间,不保存项目或页面 scope。会话偏好支持跟随路由、固定项目和工作空间级三种焦点模式,以及全部项目或显式项目集合两种工作范围;偏好修改只影响后续 Run。每个 Run 将 workspace/project/page/component/source、工作集和 focus_version 固化到快照,确认恢复、页面队列和图片任务均从原 Run 恢复,不能接收新路由焦点。
项目工作集限制项目、页面及项目级 action;工作空间组件、资源、主题和样式不受其过滤。焦点外读取允许,焦点外写入按工具调用动态确认且不缓存授权。默认上下文同时注入工作空间、焦点对象和工作集项目的名称与 ID,名称用于模型理解和界面展示,ID 仍是工具调用与权限校验依据;不加载页面源码、完整样式、建议组件或建议资源,需要时通过 list_entities 罗列或搜索集合,通过 get_entity 读取详情、共享配置、源码、历史版本和依赖。所有业务查询只返回 active 对象,归档内容不能由内容助手读取或恢复。创建工具按 new/copy/upload 区分来源,校验工具不落库,生命周期命令当前只开放组件发布。Run 输入快照和 run.focus.snapshot 事件保存同一份带名称摘要,历史时间线据此在每轮消息开头恢复焦点与工作范围。get_operation_guide 通过 operation_key 返回精确模型操作说明,不参与授权、审批或 run 恢复。
平台目录只登记 agent-coordinator 一个助手,不再创建隔离子运行或披露自委派工具。历史 component_manager 与 resource_manager 模型槽位不再开放。
诊断 CLI
排查 AI run 时使用只读诊断 CLI:
uv run --project backend python -m app.scripts.diagnose_ai_run --run-id <run_id> --format summary
uv run --project backend python -m app.scripts.diagnose_ai_run --run-id <run_id> --format json
uv run --project backend python -m app.scripts.diagnose_ai_run --session-id <session_id> --format summary
uv run --project backend python -m app.scripts.diagnose_ai_run --session-id <session_id> --format json --output .tmp/ai-session-diagnostics.json诊断输出包含:
- run 基本状态、scope、聚合 content/reasoning 和错误信息。
ai_agent_run_events事件序列。ai_agent_tool_calls工具调用输入、输出和状态。ai_agent_requirementspending/resolved HITL 状态。- 会话消息摘要。
- Pydantic AI
message_history_json的消息数量、消息类型和 part 类型摘要。
--session-id 模式会输出会话基本信息、会话消息和该会话下所有 run 的诊断集合。--output <path> 会将结果写入 UTF-8 文件,并自动创建父目录;未指定时输出到 stdout。
从根仓运行 CLI 时会自动补读 backend/.env;已存在的环境变量优先,不会被 .env 覆盖。
诊断 CLI 只能只读查询 ai_agent_* 表,不得修改 run、requirement、tool call、message 或 Redis 状态。run_id/session_id 不存在时应返回非 0 退出码。历史坏数据修复必须通过单独的一次性维护脚本完成,并明确限定 run_id/session_id/user_id 范围。
LLM HTTP trace
本地需要查看模型供应商请求时,可临时开启:
AI_LLM_HTTP_TRACE_ENABLED=true
AI_LLM_HTTP_TRACE_DIR=.tmp/llm-http-trace
AI_LLM_HTTP_TRACE_BODY_MAX_BYTES=200000非 2xx 响应会额外记录脱敏后的供应商错误 body;正常 2xx 流式响应不会读取 body。trace 文件可能包含用户输入、页面源码或业务敏感内容,提交复现材料前必须检查和脱敏。