Files
opsxdb/docs/16-AI-Session-Harness.md
T
logan d543f3b924 feat(ai): session persist + continue HTTP/UI, checkpoints, queue
落地 docs/16 P1:会话落盘与真续跑(prior tool 轨迹)、incomplete「加预算继续」、
awaiting_confirm outcome、同参拒+第4/6轮检查点、侧栏忙时单槽排队。
Ask 真 token 流式记缺口(三协议 SSE 成本高)。
2026-10-15 14:18:18 +08:00

12 KiB
Raw Blame History

16 - AI 会话产品层:状态机 · incomplete · Harness · 排队

更新:2026-10-15 · 承接 docs/15-AI-MCP-Agent.md(工具面已齐)
借鉴:/Users/logan/devops/aiops/docs/ai-agent 的 03 / 05 / 06 / 13 / 14
产品锁:MCP-first;默认只读;写需 mode=read_write + 确认;桌面单用户;不做 Evolver / 审批流 / 跨产品共用执行器

本文定义 OpsxDB 会话产品层契约:工具层(aitools / MCP)之上,侧栏 Agent 如何持久化、触顶、续跑、排队与安全确认。


1. 与 MCP / Agent 的关系

外部 Cursor/Claude ──stdio──▶ opsxdb-mcp ──┐
                                           ▼
侧栏 Ask(不调工具)                  aitools.Runtime(同源)
侧栏 Agent ──▶ ai.runAgent / Harness ──▶ Runtime.Call
                     │                      │
                     ▼                      ▼
              会话落盘(目标)          mcp-audit.jsonl(含 run_id)
层 职责 不负责
aitools.Runtime 白名单、只读/可写闸、危险 SQL、确认令牌、审计 多轮对话、预算、续跑
MCP stdio 把 Runtime 暴露给外部 Agent 会话 UI、incomplete
ai.RunAgent ReAct 循环、步骤事件、SSE 连接增删、公网后端
会话产品层(本文) 落盘轨迹、RunOutcome、Harness 预算、排队、脱敏、确认绑定 Evolver、审批流

真·续跑(对齐 aiops 13):必须同时满足 (1) 持久化含 tool_calls + tool observation(含 tool_call_id);(2) 下次 Run 把历史注入引擎。仅恢复气泡不算 resume。


2. 会话状态机

2.1 Session(会话)

状态 含义
idle 有历史或空会话,无进行中的 run
running 正在执行一轮 Agent
awaiting_confirm 写操作待用户确认(可与 running 同条气泡内嵌)
incomplete 上一趟因预算/超时停住,轨迹可续
completed 上一趟正常结束
failed 不可恢复错误
cancelled 用户停止

桌面单用户:会话默认落 ~/.opsxdb/ai-sessions/<session_id>.json(目标态;首版可仅内存 + 可选落盘)。

2.2 RunOutcome(单次运行结束,必填)

对齐 aiops 13-运行结果状态与可续跑契约.md:

outcome 含义 UI
completed 预算内给出最终答案 正常
incomplete 未完成但有部分轨迹 「加预算继续」/「继续」
failed 协议/鉴权/致命工具错 失败
cancelled 用户 Abort 已取消
awaiting_confirm 安全闸等待确认 确认条

2.3 IncompleteReason

reason 触发
max_steps 触达 MaxRounds / MaxIterations
context_budget trim 后无法安全继续
tool_timeout 单工具超时
llm_timeout LLM 超时
safety_abort 安全中止(非等待确认)

规则:触顶步数 → incomplete + max_steps,不得伪装成 completed(现状 agent.go 触顶仍发 done,见 §6 审查)。

2.4 对外字段(API / SSE 目标态)

run_id                 # 一次 Run 全链路 ID(审计 / 步骤共用)
session_id             # 可选;有落盘时必填
run_outcome            # completed | incomplete | failed | cancelled | awaiting_confirm
incomplete_reason?     # 见上表
resumable              # 是否满足真·续跑条件
steps_used / steps_budget
partial_summary?       # 触顶时进展摘要

SSE:在现有 step_start / step / delta / done / error 上,目标增加显式 incomplete 事件(或 done 携带 run_outcome=incomplete)。首版代码已在 AgentRes / AgentEvent 挂上 outcome 字段。


3. Harness 预算与反馈

参考 aiops 03;OpsxDB 裁剪为桌面 DB Agent:

旋钮 默认 说明
MaxRounds 8(上限 16) ReAct 轮数预算,非质量承诺
MaxToolResultChars 12000 回灌 LLM 前截断
MaxContextTokens 8000(目标) trimMessages 预算
ToolTimeout 与 db 层对齐(目标显式化) 单工具
SameCallLimit 3(目标) 同工具同参循环拒
Checkpoints 第 4 / 6 轮(目标) 提示换思路 / 收尾

3.1 trimMessages(目标行为)

  1. 永远保留 system
  2. 从最近向前保留,约 MaxContextTokens * 90%
  3. 至少保留最近 4 条
  4. 裁剪后按 tool_call_id 整组保留 assistant.tool_calls + tool 消息

代码:ai.TrimMessages 钩子已落地;Agent 主循环接入时机在「多轮续跑 / 长会话」打开时启用(单次短问可跳过)。

3.2 检查点与循环检测

目标:同参 ≥3 次拒绝并写入 tool observation;第 4/6 轮向 msgs 注入短 system/user 提醒。不做 Evolver。


4. 会话落盘与真·续跑

4.1 存储形状(目标)

{
  "id": "sess_…",
  "title": "列出 demo 表",
  "createdAt": "…",
  "updatedAt": "…",
  "status": "incomplete",
  "messages": [
    {"role":"system","content":"…"},
    {"role":"user","content":"…"},
    {"role":"assistant","content":"…","toolCalls":[{"id":"c1","function":{…}}]},
    {"role":"tool","toolCallId":"c1","name":"opsxdb_list_tables","content":"…"}
  ],
  "runs": [{"runId":"run_…","outcome":"incomplete","reason":"max_steps","stepsUsed":8}]
}

脱敏后落盘(见 §5)。密码、连接串明文、大结果全文不进会话文件。

4.2 API 契约(目标,大 UI 可后做)

方法 路径 说明
GET /api/ai/sessions 列表
GET /api/ai/sessions/:id 含 messages
POST /api/ai/agent 现有;增加可选 sessionId / continue:true / prior
POST /api/ai/sessions/:id/continue 注入 prior + 可选提高 MaxRounds

骨架:server/internal/ai/session.go(文件存储接口 + 类型;HTTP 路由可后挂)。


5. 脱敏与确认绑定

5.1 脱敏(落盘 / 审计 / 展示)

对齐 aiops 05,桌面范围:

类型 示例
Bearer / API Key Bearer …、sk-…
密码 password=、pwd=
连接串 mysql://user:pass@host
PEM 私钥块 BEGIN … PRIVATE KEY

ai.RedactSensitive 已提供;会话落盘与审计 summary 应过脱敏。

5.2 确认绑定(写操作)

写确认不得只靠 UI 气泡里的明文 SQL 再次提交而无绑定:

confirm_bind = sha256(connection_id + "\n" + normalize(sql_or_command))

规则:

  1. needConfirm 响应带上 confirmBind(或服务端会话内缓存)
  2. /api/ai/agent/confirm 校验:connection_id + 语句 与绑定一致,否则拒绝并要求重确认
  3. 用户改了 SQL → 哈希变 → 必须重新确认(防 TOCTOU)

首版:aitools.ConfirmBindHash + Runtime needConfirm 返回字段;confirm 端点校验(见代码)。


6. 忙时单槽排队

对齐 aiops 14;桌面侧栏:

动作 语义
排队 running 时新输入进 pending(单槽;再提交覆盖)
自动续发 当前 run 结束(任一 outcome)后若有 pending → 自动 continue
立即 停止当前 → 立刻跑 pending
编辑 / 撤销 拉回输入框 / 丢弃

与 incomplete 续跑可叠加:排队是同会话并发输入;incomplete 是上一趟未完成再继续。

首版:文档 + 前端状态契约;服务端仍单请求单 run(Abort 用 ctx cancel)。不做跨会话队列、不做长 FIFO。


7. 细节审查:现状 → 缺口 → 建议

7.1 Agent loop(server/internal/ai/agent.go)

现状 ReAct:ChatWithTools → execOneTool → 回灌;默认 MaxRounds=8(帽 16);触顶文案后仍 AgentEventDone;无 session_id / prior;取消走 ctx → error 事件;NeedConfirm 回灌模型但不暂停 loop 为 awaiting_confirm outcome
缺口 无结构化 run_outcome;触顶伪装 completed;无真·续跑;无同参循环拒;无中段检查点;确认不打断为等待态
建议 触顶 → incomplete/max_steps + resumable;AgentRes 带 outcome/run_id;可选 Prior []Message;NeedConfirm 可提前结束本 run 为 awaiting_confirm(UI 已有确认条,语义对齐即可)

7.2 Harness 架构

现状 预算散落常量(defaultAgentMaxRounds、maxToolResultFeedChars);无独立 Harness 配置对象;无 trim / 循环检测 / checkpoint;SSE 事件无 outcome
缺口 与 aiops 03 的「工程 Harness」不对齐;上下文窗口未 own;长会话必炸或静默丢配对
建议 HarnessConfig + TrimMessages + DetectRepeatedToolCall;Run 开头生成 run_id 写入 Runtime.Audit;检查点用轻量 user 消息注入,勿上 Evolver

7.3 MCP 同源 Runtime(server/internal/aitools)

现状 MCP 与 Agent 共用 Runtime.Call;Policy 白名单 + 只读默认;P1 read_write + needConfirm;审计 JSONL 有 source;无 run_id;确认端点不校验语句哈希绑定;Agent 剥离模型 confirm_danger
缺口 审计无法按一次 Agent run 聚合;确认存在「改 SQL 仍用旧气泡点执行」窗口;MCP 外部客户端若带 confirm_danger 可在 read_write 下写(设计允许外部 Agent 自带确认语义,但桌面应对齐绑定)
建议 AuditEntry.RunID;ConfirmBindHash;confirm API 校验绑定;MCP 文档写明:外部客户端的 confirm 等同操作者授权,桌面 GUI 必须绑定哈希

8. 落地节奏(本轮 / 下一刀)

优先级 项 本轮
P0 文档契约(本文)+ 15 交叉引用 ✅
P0 RunOutcome 类型、触顶 incomplete、run_id 进审计 ✅ 代码
P0 TrimMessages / Redact / ConfirmBind 钩子 ✅ 代码
P1 会话落盘 + continue API + UI 恢复 文档契约 + session 骨架
P1 单槽排队 UI 文档;前端后做
P2 同参循环拒、检查点、Ask 真 token 流 后置

9. 验收(会话层)

  • go test ./internal/ai/ ./internal/aitools/ 通过(含 outcome / trim / redact / bind)
  • 触顶轮次:响应 run_outcome=incomplete,incomplete_reason=max_steps
  • 审计行含 run_id(Agent 路径)
  • confirm:改 SQL 后旧 bind 拒绝
  • (P1)关窗再开:同 session 注入 prior 能继续调工具

文档版本: v1.0 | 2026-10-15 CST | 先契约与小改,大 UI 后挂


10. 仍未完清单(对照本契约 · 2026-10-15)

状态 项
✅ MCP 工具面、Ask|Agent、SSE、P1 确认写、run_id / confirmBind、触顶 incomplete
✅ 设置 → MCP 子页(安装检测、只读/危险 SQL、白名单、多客户端片段)
✅ 官网 AI/MCP 块 + 文档 /docs/mcp
✅ 会话落盘 + HTTP 续跑(GET/DELETE /api/ai/sessions、POST …/continue)+ UI「加预算继续」
✅ NeedConfirm 结束 run 为 awaiting_confirm outcome
✅ 同参循环拒 + 中段检查点(第 4 / 6 轮)接入主循环
✅ 忙时单槽排队 UI(覆盖 / 撤销 / 立即 / 结束后自动续发)
⬜ Ask 真 token 流式(非假打字)— 成本高:需 openai-chat / anthropic / responses 三套 SSE;本轮记缺口,Ask 仍一次性 Complete
⬜ MCP「断开」结束外部 Cursor 会话(当前仅 UI 提示)
⬜ 设置页一键 npm install -g(现为复制命令;避免桌面代跑需授权)

10.1 本轮验收要点

  • go test ./internal/ai/ ./internal/aitools/ ./internal/server/
  • Agent 触顶 → run_outcome=incomplete + 气泡「加预算继续」→ continue=true 注入落盘 prior
  • 写工具无确认 → awaiting_confirm;确认条仍走 /api/ai/agent/confirm + confirmBind
  • Agent 忙碌时再发送 → 单槽排队条;结束后自动发或点「立即」

本节随交付勾选;Ask SSE 与 MCP 杀外部进程后置。