# 会话存储 — 功能规格 ## Requirement 1:三张新表的 DDL **路径**:`src/main/resources/db/migration/V005__create_session_storage.sql` **内容**: - 创建 `diagnosis_session` 表(DDL 见 design.md) - 创建 `agent_step` 表(DDL 见 design.md) - 创建 `tool_invocation` 表(DDL 见 design.md) - 三条 DDL 写在同一个迁移文件中 **验收标准**: - [ ] Flyway migrate 后三张表均存在 - [ ] 表结构字段类型、索引与设计一致 - [ ] JSON 列使用 `JSON` 类型(MySQL 8+) --- ## Requirement 2:JPA Entity + Repository ### 2.1 实体类 **路径**: - `src/main/java/com/superbiz/agent/domain/entity/DiagnosisSession.java` - `src/main/java/com/superbiz/agent/domain/entity/AgentStep.java` - `src/main/java/com/superbiz/agent/domain/entity/ToolInvocation.java` **要求**: - 使用 `@Entity` + `@Table(name = "...")` 映射 - JSON 字段使用 `@JdbcTypeCode(SqlTypes.JSON)`(同现有 `DiagnosisRecord.toolCalls`) - `@PrePersist` 自动填充 `createdAt` - 使用 Lombok `@Data @Builder @NoArgsConstructor @AllArgsConstructor` ### 2.2 Repository 接口 **路径**: - `src/main/java/com/superbiz/agent/repository/DiagnosisSessionRepository.java` - `src/main/java/com/superbiz/agent/repository/AgentStepRepository.java` - `src/main/java/com/superbiz/agent/repository/ToolInvocationRepository.java` **要求**: - 继承 `JpaRepository` - `DiagnosisSessionRepository`:`findBySessionId(String sessionId)` - `AgentStepRepository`:`findBySessionIdOrderByStepIndex(String sessionId)`、`countBySessionId(String sessionId)` - `ToolInvocationRepository`:`findBySessionId(String sessionId)`、`findByToolName(String toolName)` **验收标准**: - [ ] 3 个 Entity 编译通过 - [ ] 3 个 Repository 编译通过 - [ ] 自定义查询方法命名符合 Spring Data JPA 规范 --- ## Requirement 3:AgentLoggingHook 改造为 Spring Bean **路径**:`src/main/java/com/superbiz/agent/hook/AgentLoggingHook.java` **变更**: - 类上加 `@Component` 注解 - 不再通过 new 创建实例 - 构造注入 `AgentStepRepository` - beforeModel:创建 `AgentStep` 记录,设置 `modelInput`,记录开始时间到 `RunnableConfig` - afterModel:更新对应 `AgentStep`,设置 `modelOutput`、`thought`、`hasToolCall`、`durationMs`、`tokenCount` - `modelInput` 和 `modelOutput` 只存摘要(前 500 字符),不存完整消息体 **session_id 传递机制**: - 调用方(ChatService/AiOpsService)通过 `RunnableConfig.metadata()` 传入 `sessionId` - Hook 从 `config.getMetadata("sessionId")` 读取 **验收标准**: - [ ] Hook 可注入 AgentStepRepository - [ ] beforeModel 创建 agent_step 记录并写入 DB - [ ] afterModel 更新对应 agent_step 记录 - [ ] model_input/output 摘要不超过 500 字符 - [ ] 从 RunnableConfig 正确读取 sessionId - [ ] 原日志输出行为保持不变 --- ## Requirement 4:ChatService 集成 **路径**:`src/main/java/com/superbiz/agent/service/ChatService.java` **变更**: - 注入 `DiagnosisSessionRepository` - `executeChat()` 中: - 执行前:创建 `DiagnosisSession`(status=RUNNING),生成 `sessionId`,生成 `agent_flow=CHAT` - 通过 `RunnableConfig` 将 sessionId 传给 Hook - 执行后:更新 `DiagnosisSession`(status=SUCCESS/FAILED,汇总 step_count、tool_call_count、total_duration_ms) - 不再通过 `new AgentLoggingHook()` 创建 Hook,改为注入 Bean 的 Hook **验收标准**: - [ ] executeChat 执行前后分别创建和更新 diagnosis_session - [ ] sessionId 通过 RunnableConfig 正确传递给 Hook - [ ] 汇总指标(duration、step_count)正确写入 - [ ] 异常路径正确设置 status=FAILED --- ## Requirement 5:AiOpsService 集成 **路径**:`src/main/java/com/superbiz/agent/service/AiOpsService.java` **变更**: - 注入 `DiagnosisSessionRepository` 和 `AgentLoggingHook` - `executeAiOpsAnalysis()` 中: - 执行前:创建 `DiagnosisSession`(status=RUNNING, agent_flow=AI_OPS) - 构建 Planner 和 Executor 时传入 `AgentLoggingHook` 实例(使用注入的 Bean) - 通过 `RunnableConfig` 将 sessionId 传给 Hook - 执行后:更新 `DiagnosisSession`(汇总指标) - Supervisor 不加 Hook **验收标准**: - [ ] AiOpsService 执行前后分别创建和更新 diagnosis_session - [ ] Planner 和 Executor 各带 AgentLoggingHook - [ ] 两个 Hook 使用相同的 sessionId - [ ] Supervisor 不产生 agent_step 记录 --- ## Requirement 6:LookupKnowledgeTool 写入 tool_invocation **路径**:`src/main/java/com/superbiz/agent/tool/LookupKnowledgeTool.java` **变更**: - 注入 `ToolInvocationRepository` - `lookupKnowledge()` 执行后: - 创建 `ToolInvocation` 记录 - 写入 `toolName=lookup_knowledge`、`inputParams`(query)、`outputPreview`(前 500 字符) - 写入检索质量:`retrievalLayer`、`l0MatchCount`、`l1MatchCount`、`isTruncated`、`retrievalDetails` - 写入 `durationMs`、`success` - `sessionId` 和 `stepId` 如何获取需要方案设计(见开放问题) **验收标准**: - [ ] lookup_knowledge 每次调用后创建 tool_invocation 记录 - [ ] 检索质量字段(L0/L1 明细)正确写入 - [ ] 工具执行失败的场景正确记录 --- ## Requirement 7:构造注入适配(无 @Async) **路径**:所有涉及新增 Repository 注入的类 **要求**: - 所有新注入使用构造注入(`@RequiredArgsConstructor` 或显式构造器) - 不在 MV 阶段引入 @Async 异步基础设施 - Hook 中的 DB 写入是同步的,作为已知的技术债记录 **验收标准**: - [ ] 没有使用 @Autowired 字段注入新 Repository(保持项目已有风格) - [ ] 没有引入 @Async / @EnableAsync