feat(session): 会话存储体系实现 & Chat多Agent路由

- 新增诊断会话(diagnosis_session/agent_step/tool_invocation)三表
- AgentLoggingHook 持久化 agent_step,记录决策链和耗时
- LookupKnowledgeTool 写入 tool_invocation,记录L0/L1检索质量
- TokenTrackingChatModel 捕获真实token用量
- Chat接口支持意图路由:简单问题单Agent,复杂问题多Agent(Planner+Executor)
- Prompt外置到 src/main/resources/prompts/
- 删除旧 diagnosis_record 表及相关文件
- 新增SessionContextHolder(ThreadLocal传递sessionId)
- QuestionComplexity 复杂度判断工具
- 测试覆盖三张新表的Repository
This commit is contained in:
zhuyongxin
2026-06-26 16:22:05 +08:00
parent a74ccea5be
commit 0d9cce75f9
33 changed files with 1941 additions and 470 deletions
@@ -0,0 +1,44 @@
package com.superbiz.agent.util;
import java.util.List;
/**
* 问题复杂度判断
* 用于决定使用单 Agent 还是多 Agent(Planner + Executor)处理
*/
public class QuestionComplexity {
/** 复杂问题关键词 — 需要多步分析、排查、根因定位 */
private static final List<String> COMPLEX_KEYWORDS = List.of(
"排查", "分析", "为什么", "根因", "调查", "对比", "影响范围",
"原因", "故障", "告警", "诊断", "链路", "流程", "步骤",
"root cause", "troubleshoot", "investigate"
);
/** 极简问题关键词 — 快速回答,无需多 Agent */
private static final List<String> SIMPLE_KEYWORDS = List.of(
"是什么", "查一下", "什么是", "时间", "天气", "定义",
"查", "找", "what is", "define", "time"
);
/**
* 判断是否为复杂问题
*/
public static boolean isComplex(String question) {
if (question == null || question.isBlank()) return false;
String q = question.toLowerCase();
// 复杂关键词匹配 → 多 Agent
for (String kw : COMPLEX_KEYWORDS) {
if (q.contains(kw)) return true;
}
// 简单关键词匹配 → 单 Agent
for (String kw : SIMPLE_KEYWORDS) {
if (q.contains(kw)) return false;
}
// 默认:长问题(>30 字)视为复杂,短问题视为简单
return question.length() > 30;
}
}
@@ -0,0 +1,39 @@
package com.superbiz.agent.util;
/**
* 会话上下文持有者(基于 ThreadLocal)
* <p>
* 用于在执行链路中传递 sessionId 和 agentName,覆盖 AgentLoggingHook 和
* LookupKnowledgeTool 等无法直接通过 RunnableConfig 获取上下文的组件。
* <p>
* 使用规范:
* 1. 调用方(ChatService/AiOpsService)在 Agent 执行前调用 setSessionId() 和 setAgentName()
* 2. AgentLoggingHook 和工具类通过 getSessionId() / getAgentName() 读取
* 3. 必须在 finally 块中调用 clear(),防止内存泄漏和线程污染
*/
public class SessionContextHolder {
private static final ThreadLocal<String> SESSION_ID = new ThreadLocal<>();
private static final ThreadLocal<String> AGENT_NAME = new ThreadLocal<>();
public static void setSessionId(String sessionId) {
SESSION_ID.set(sessionId);
}
public static String getSessionId() {
return SESSION_ID.get();
}
public static void setAgentName(String agentName) {
AGENT_NAME.set(agentName);
}
public static String getAgentName() {
return AGENT_NAME.get();
}
public static void clear() {
SESSION_ID.remove();
AGENT_NAME.remove();
}
}