Files
SuperBizAgent-java/mvp/plan/session-storage-design.md
T
zhuyongxin 9b52afce07 docs: 合并 .docs/mvp 到根 mvp 目录并更新文档
- 删除 .docs/mvp 目录,内容合并到根目录 mvp/
- 更新 session-storage-design.md 实现变更记录
- 修复引用路径
2026-06-26 17:29:27 +08:00

12 KiB
Raw Blame History

会话存储方案设计

日期: 2026-06-26
类型: 架构设计
状态: 已实现 (2026-06-26)


一、背景与目标

1.1 现状问题

当前仅有一张 diagnosis_record 表,存在以下问题:

问题 说明
语义耦合 fault_category、error_code、root_cause、solution 等字段耦合在"告警分析"领域语义,ChatService 通用问答场景用不上
Agent 维度缺失 只有一个 tool_calls JSON 字段,存不下两个 Agent 的多轮决策链
检索质量不可追溯 没有记录 L0/L1 命中层、截断信息、召回内容长度
指标不完整 有 duration 和 confidence,但缺 token 用量、自评信号、采纳率

1.2 存储范围

需要覆盖四个层面的数据:

诊断级元数据
  ├── 单次诊断的唯一 ID、查询问题、状态
  ├── 会话级决策链
  │     ├── agent_step:每个 Agent 的每一步(输入、输出、延迟、Token)
  │     └── tool_invocation:每次工具调用(参数、结果、耗时)
  ├── 检索质量明细
  │     └── 每次 lookup_knowledge 的命中层(L0/L1)、内容长度、是否截断
  └── 自评估信号
        └── LLM 对结论的置信度自评

1.3 设计目标

  • 可观测:Debug 时能回溯完整决策链
  • 可评估:能统计 L0/L1 命中率、平均 Token 消耗、工具采纳率等指标
  • 可演进:覆盖当前两个 Agent(ChatService / AiOpsService),未来新增 Agent 也能接入

二、存储选型分析

2.1 方案对比

维度 SQL + JSON 列 NoSQL 文档库
基础设施 已有的 MySQL,零新增 需新部署 MongoDB 等
层级查询 WHERE session_id=? AND agent_name=? 高效 需二级索引
指标聚合 AVG(token_count) GROUP BY agent_name 原生支持 聚合管道,学习成本
非结构化内容 JSON 列(MySQL 8+ 支持良好) 天然支持
MVP 迭代速度 JPA Entity + Flyway 快速迭代 新 ORM 学习成本

2.2 结论

采用 MySQL + JSON 列。结构化字段做查询和聚合,JSON 列存非结构化载荷。MVP 阶段数据量可控,等后续 > 百万级或需要更灵活 schema 时再评估 NoSQL。


三、存储模型

3.1 整体关系

diagnosis_session (1)
      │
      └── agent_step (0:N) —— 单次诊断的每一步 Agent 决策
              │
              └── tool_invocation (0:N) —— 每步中的工具调用

3.2 表设计

表 1:diagnosis_session(诊断会话)

CREATE TABLE diagnosis_session (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    session_id       VARCHAR(64) UNIQUE NOT NULL  COMMENT '会话唯一 ID',

    -- 请求
    query            TEXT         NOT NULL         COMMENT '用户原始问题',
    status           VARCHAR(16)  DEFAULT 'PENDING' COMMENT 'PENDING / RUNNING / SUCCESS / FAILED',
    agent_flow       VARCHAR(32)                    COMMENT 'CHAT / AI_OPS',

    -- 汇总指标
    total_duration_ms   INT                        COMMENT '总耗时(毫秒)',
    total_token_count   INT                        COMMENT '总 Token 消耗',
    step_count          INT                        COMMENT 'Agent 步数',
    tool_call_count     INT                        COMMENT '工具调用次数',

    -- 自评估信号(模型对结论的置信度自评)
    self_evaluation     JSON                       COMMENT '{"confidence": 0-100, "reasoning": "...", "evidence_count": 3}',

    -- 用户反馈
    feedback            VARCHAR(16)                COMMENT 'useful / not_useful / null',

    -- 元数据
    created_at          DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_created_at (created_at),
    INDEX idx_status (status),
    INDEX idx_agent_flow (agent_flow)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='诊断会话表';

表 2:agent_step(Agent 决策步骤)

CREATE TABLE agent_step (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    session_id       VARCHAR(64) NOT NULL           COMMENT '关联 diagnosis_session',

    step_index       INT         NOT NULL           COMMENT '当前 Agent 的第几步(从0开始)',
    agent_name       VARCHAR(32) NOT NULL           COMMENT 'intelligent_assistant / planner / executor / supervisor',

    -- 模型调用(输入输出摘要,非完整消息体)
    model_input      JSON                           COMMENT '模型输入摘要 [{role, content_truncated}, ...]',
    model_output     JSON                           COMMENT '模型输出摘要 {text, tool_calls, ...}',
    thought          TEXT                           COMMENT 'Agent 思考过程文本',
    has_tool_call    BOOLEAN     DEFAULT FALSE       COMMENT '本轮是否调用了工具',

    -- 性能指标
    duration_ms      INT                            COMMENT '本轮耗时',
    token_count      INT                            COMMENT '本轮 Token 消耗',

    created_at       DATETIME DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_session_step (session_id, step_index),
    INDEX idx_agent_name (agent_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent 决策步骤表';

表 3:tool_invocation(工具调用明细)

CREATE TABLE tool_invocation (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    session_id        VARCHAR(64) NOT NULL           COMMENT '关联 diagnosis_session',
    step_id           BIGINT                         COMMENT '关联 agent_step.id(可为空,不强制外键)',

    tool_name         VARCHAR(64) NOT NULL           COMMENT 'lookup_knowledge / queryPrometheusAlerts / 等',

    -- 调用信息
    input_params      JSON         NOT NULL           COMMENT '工具入参',
    output_preview    TEXT                            COMMENT '输出前500字符(可观测用,不存完整输出)',
    output_length     INT                             COMMENT '输出总字符数',

    -- 检索质量(仅 lookup_knowledge 时有意义)
    retrieval_layer   VARCHAR(8)                      COMMENT 'L0 / L1 / L0+L1',
    l0_match_count    INT                             COMMENT 'L0 匹配数',
    l1_match_count    INT                             COMMENT 'L1 匹配数',
    is_truncated      BOOLEAN     DEFAULT FALSE        COMMENT '返回内容是否被截断',
    retrieval_details JSON                            COMMENT '{"l0_titles":[], "l1_scores":[], "has_supplement": true}',

    -- 性能 & 状态
    duration_ms       INT                             COMMENT '工具执行耗时',
    success           BOOLEAN     DEFAULT TRUE         COMMENT '是否成功',
    error_message     TEXT                            COMMENT '失败原因',

    created_at        DATETIME DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_session_id (session_id),
    INDEX idx_tool_name (tool_name),
    INDEX idx_retrieval_layer (retrieval_layer)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工具调用明细表';

四、数据流设计

4.1 完整链路

用户请求
   │
   ▼
1. 创建 diagnosis_session(status=RUNNING)
   │
   ▼
2. Agent Loop(可能多轮)
   │
   ├── beforeModel()
   │     └── AgentLoggingHook 记录 model_input + 开始时间 → 写入 agent_step(先创建,duration 待填)
   │
   ├── afterModel()
   │     └── AgentLoggingHook 记录 model_output + token_count + 工具调用决策 → 更新 agent_step
   │
   ├── 工具执行(如 lookup_knowledge)
   │     └── LookupKnowledgeTool 记录 tool_invocation(L0/L1 明细、耗时、是否截断)
   │
   └── 循环直到模型不再调用工具
   │
   ▼
3. 诊断完成 → 更新 diagnosis_session
   ├── status = SUCCESS / FAILED
   ├── 汇总指标:total_duration_ms / total_token_count / step_count / tool_call_count
   └── self_evaluation(可选,由 LLM 自评)

4.2 变更点

模块 当前行为 改造后
AgentLoggingHook 只打日志到 stdout 同时写入 agent_step 表
LookupKnowledgeTool 只打日志到 stdout 同时写入 tool_invocation 表
ChatService / AiOpsService 执行前后无持久化 创建 + 更新 diagnosis_session

五、可观测能力

5.1 查询场景

需求 SQL 说明
某次诊断用了哪些工具 SELECT * FROM tool_invocation WHERE session_id=? 按 session 关联
lookup_knowledge 的 L0/L1 命中率 SELECT retrieval_layer, COUNT(*) FROM tool_invocation WHERE tool_name='lookup_knowledge' GROUP BY retrieval_layer 聚合检索层分布
某个 Agent 的平均思考耗时 SELECT AVG(duration_ms) FROM agent_step WHERE agent_name=? 按 Agent 分组
某次诊断的完整决策链 SELECT * FROM agent_step WHERE session_id=? ORDER BY step_index 按步骤号排序
被截断的检索占比 SELECT COUNT(*) FROM tool_invocation WHERE is_truncated=true AND tool_name='lookup_knowledge' 条件计数
高置信度但用户反馈 negative SELECT * FROM diagnosis_session WHERE JSON_EXTRACT(self_evaluation, '$.confidence') > 80 AND feedback='not_useful' JSON 条件查询

5.2 评估指标

指标 计算方式 数据来源
平均诊断耗时 AVG(total_duration_ms) diagnosis_session
平均 Token 消耗 AVG(total_token_count) diagnosis_session
工具采纳率 tools_accepted / tools_proposed self_evaluation
L0 命中率 l0_match_count > 0 的比例 tool_invocation
截断率 is_truncated=true 的比例 tool_invocation
用户满意度 feedback='useful' 的比例 diagnosis_session

六、与现有表的关系

6.1 diagnosis_session vs 现有 diagnosis_record

  • diagnosis_record 保持不动,继续用于"告警分析"场景的领域字段(root_cause、solution 等)
  • diagnosis_session 是通用会话存储,覆盖 ChatService 和 AiOpsService
  • 两者通过 session_id 可关联

6.2 迁移策略

阶段 动作
MVP 新建三张表,新代码写入新表
V1.1 评估是否将 diagnosis_record 合并回 diagnosis_session(加 fault 相关字段到 JSON)
V1.2 数据量 > 10 万时评估是否需要归档或迁移

七、未完成事项

  • AI Ops Supervisor 的 Agent 执行步骤如何对应 agent_step 表(Supervisor 内嵌的子 Agent 步骤归到同一个 session 还是独立)
  • self_evaluation 的 confidence 自评通过什么方式获取(单独的 LLM 调用还是在 prompt 中要求输出)
  • feedback 字段和前端的交互方式
  • Tool_invocation 的 output_preview 截断策略(当前建议 500 字符)

八、实现变更记录

8.1 与设计文档的差异

设计 实现 原因
AgentLoggingHook 为 @Component 改为 POJO(构造注入 Repository + agentName) 需要为 ChatService / AiOpsService 创建多个 Hook 实例(不同 agentName)
sessionId 通过 RunnableConfig 的 metadata 携带 通过 RunnableConfig.builder().addMetadata("sessionId", id) 构建 确认框架 API 原生支持,线程安全
Token 从 ChatResponse 获取 增加了 TokenTrackingChatModel 包装器拦截 ChatModel.call() 框架的 _TOKEN_USAGE_ 仅在 stream 路径可用,call 路径需自行拦截
sessionId 汇总后回填 backfillSessionMetrics() 从 agent_step 表统计 避免在 Hook 中维护累加状态

8.2 新增文件(超出原设计)

文件 用途
TokenTrackingChatModel.java ChatModel 包装器,拦截 call() 获取实际 token 用量
TokenUsageHolder.java ThreadLocal 传递 token 数给 Hook
QuestionComplexity.java 问题复杂度判断,路由单 Agent / 多 Agent
SessionContextHolder.java ThreadLocal 传递 sessionId(同步路径兜底)

8.3 删除文件

文件 原因
DiagnosisRecord.java / DiagnosisRecordRepository.java / DiagnosisStatus.java 被新三表替代,V007 Flyway 迁移删除
.docs/mvp/ 内容合并到根目录 mvp/