zhuyongxin
|
9050487307
|
feat: add chat verifier agent
|
2026-07-03 10:54:33 +08:00 |
|
zhuyongxin
|
bb44140901
|
feat(knowledge): 会话级去重 + 知识域地图注入 Planner 解决 ISS-001 重复检索
- RetrievedDocTracker: sessionId → Set<filePath> 会话级去重,LookupKnowledgeTool Step 5 过滤已检索文档
- KnowledgeDomainService: 域级聚合,LLM 生成 when_to_retrieve,构建 knowledge map YAML
- DocumentFieldEnricher: 上传时 LLM 补全 covers + whenToRetrieve(含同域文档排除上下文)
- KnowledgeDomain entity + V009 迁移: 域级元数据持久化,避免重启重复 LLM 调用
- ChatService: 注入 knowledge map 到 Planner prompt,会话结束时清理去重状态
- KnowledgeIndexService: 手写 JSON 解析替换为 Jackson ObjectMapper,启动时补建缺失域记录
- chat-planner-prompt: 新增知识库检索规则(按域 when_to_retrieve 判断,每域最多一次检索)
- doc-field-enricher-prompt / domain-summary-prompt: 外部化 LLM 提示词
|
2026-07-01 10:47:46 +08:00 |
|
zhuyongxin
|
2a796da490
|
feat(feedback): 置信度评分与用户反馈机制 & 归档 confidence-feedback change
|
2026-06-30 18:01:06 +08:00 |
|
zhuyongxin
|
a3abe3f7a2
|
refactor(session): 清理代码 & RunnableConfig 传 sessionId
- AgentLoggingHook 改为从 config.metadata 读取 sessionId(线程安全)
- 移除 AgentLoggingHook 调试用的 metadata 日志
- TokenTrackingChatModel 日志降为 debug
- SessionContextHolder 移除未使用的 setAgentName/getAgentName
- ChatService 清理无用 import
- 修复 stream 路径下 ThreadLocal NPE
|
2026-06-26 17:28:30 +08:00 |
|
zhuyongxin
|
0d9cce75f9
|
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
|
2026-06-26 16:22:05 +08:00 |
|
zhuyongxin
|
7c8758d7fa
|
refactor(observability): 简化 ChatService 日志,避免与 Hook 重复
## 改动内容
### 修改前:重复的日志
```java
// ChatService.executeChat()
logger.info("========== Agent 执行开始 ==========");
logger.info("📝 用户问题: {}", question);
logger.info("🚀 执行 ReactAgent.call() - 自动处理工具调用");
// ... Agent 执行 ...
logger.info("========== Agent 执行完成 ==========");
logger.info("⏱️ 执行耗时: {} ms", duration);
logger.info("📤 最终输出内容:");
// 打印完整答案
```
**问题**:
- 与 `AgentLoggingHook` 的日志重复
- 日志过于冗长
- Hook 已经覆盖了 Agent 的思考过程
---
### 修改后:精简的日志
```java
// ChatService.executeChat() - 只保留最外层框架
logger.info("========================================");
logger.info("📝 用户问题: {}", question);
// ... Agent 执行(Hook 负责内部日志)...
logger.info("⏱️ 总耗时: {} ms", duration);
logger.info("📏 输出长度: {} 字符", answer.length());
logger.info("========================================");
```
**优势**:
- 职责清晰:ChatService 只记录最外层信息
- 避免重复:思考过程由 Hook 负责
- 更简洁:减少噪音日志
---
## 日志分层
| 层级 | 负责类 | 职责 |
|------|--------|------|
| **外层框架** | `ChatService` | 用户问题、总耗时、输出长度 |
| **思考过程** | `AgentLoggingHook` | 每轮思考、模型决策、消息流转 |
| **工具执行** | `LookupKnowledgeTool` | L0/L1 检索、工具参数/返回 |
---
## 日志输出对比
### 修改前(重复冗长)
```
========================================
========== Agent 执行开始 ========== ← ChatService
========================================
📝 用户问题: 支付为什么会失败? ← ChatService
----------------------------------------
🚀 执行 ReactAgent.call() ← ChatService
*** [Agent 思考] 第 1 轮思考开始 ← Hook
...
*** [Agent 思考] 第 1 轮思考完成 ← Hook
>>> [工具调用] lookup_knowledge ← Tool
...
<<< [工具返回] lookup_knowledge ← Tool
*** [Agent 思考] 第 2 轮思考开始 ← Hook
...
*** [Agent 思考] 第 2 轮思考完成 ← Hook
========================================
========== Agent 执行完成 ========== ← ChatService (重复)
========================================
⏱️ 执行耗时: 1523 ms ← ChatService
📤 最终输出内容: ← ChatService
根据知识库的记录...(完整答案) ← ChatService (太长)
========================================
```
---
### 修改后(清晰简洁)
```
========================================
📝 用户问题: 支付为什么会失败? ← ChatService (简洁)
*** [Agent 思考] 第 1 轮思考开始 ← Hook
...
*** [Agent 思考] 第 1 轮思考完成 ← Hook
>>> [工具调用] lookup_knowledge ← Tool
...
<<< [工具返回] lookup_knowledge ← Tool
*** [Agent 思考] 第 2 轮思考开始 ← Hook
...
*** [Agent 思考] 第 2 轮思考完成 ← Hook
*** [Agent 思考] 这是最终答案 ← Hook (已说明)
⏱️ 总耗时: 1523 ms ← ChatService (简洁)
📏 输出长度: 456 字符 ← ChatService (摘要)
========================================
```
**改进**:
- ✅ 去掉重复的"开始/完成"标记
- ✅ 不再打印完整答案(通过 HTTP 响应已返回)
- ✅ Hook 已说明"这是最终答案"
- ✅ 日志更紧凑,信噪比更高
---
## 设计原则
### 1. 单一职责
- **ChatService**:顶层编排,只记录执行框架
- **Hook**:Agent 内部状态,记录思考过程
- **Tool**:工具执行细节,记录检索过程
### 2. 避免重复
- 不在多处打印相同信息
- Hook 已说明"最终答案",ChatService 不再重复
### 3. 信息密度
- 关键信息:保留(用户问题、耗时、长度)
- 冗余信息:删除(重复标题、完整答案)
---
## 查看日志
```bash
# 完整日志
tail -f logs/application.log
# 只看框架
tail -f logs/application.log | grep "📝\|⏱️\|📏"
# 只看思考过程
tail -f logs/application.log | grep "Agent 思考"
# 只看工具调用
tail -f logs/application.log | grep "工具调用\|工具返回"
```
---
## 提交历史
```
当前 refactor(observability): 简化 ChatService 日志,避免与 Hook 重复
b3ea6e2 feat(observability): 添加 Agent 思考过程日志 Hook
8890cd2 feat(observability): 增强 Agent 和工具调用的可观测日志
```
|
2026-06-25 17:24:59 +08:00 |
|
zhuyongxin
|
b3ea6e202d
|
feat(observability): 添加 Agent 思考过程日志 Hook
## 改动内容
### 1. 创建 AgentLoggingHook
基于 Spring AI Alibaba 的 `MessagesModelHook` 实现:
```java
@HookPositions({HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL})
public class AgentLoggingHook extends MessagesModelHook {
// 在模型调用前
public AgentCommand beforeModel(List<Message> messages, RunnableConfig config)
// 在模型调用后
public AgentCommand afterModel(List<Message> messages, RunnableConfig config)
}
```
---
### 2. 集成到 ReactAgent
在 `ChatService.createReactAgent()` 中添加 Hook:
```java
ReactAgent.builder()
.name("intelligent_assistant")
.model(chatModel)
.hooks(new AgentLoggingHook()) // ✅ 添加日志 Hook
.build();
```
---
## 日志输出示例
### 完整的 Agent 思考流程
```
========================================
========== Agent 执行开始 ==========
========================================
📝 用户问题: 支付为什么会失败?
----------------------------------------
🚀 执行 ReactAgent.call() - 自动处理工具调用
========================================
*** [Agent 思考] 第 1 轮思考开始
*** [Agent 思考] 当前消息数量: 2
*** [Agent 思考] 最近 2 条消息:
[1] 角色: User(用户), 类型: UserMessage
[2] 角色: User(用户), 类型: UserMessage
*** [Agent 思考] 准备调用模型...
========================================
========================================
*** [Agent 思考] 第 1 轮思考完成
*** [Agent 思考] 模型输出: <AssistantMessage>
*** [Agent 思考] 模型决定调用 1 个工具:
- 工具: lookup_knowledge, 参数: {"query":"支付失败原因"}
*** [Agent 思考] 等待工具执行结果...
========================================
========================================
>>> [工具调用] lookup_knowledge
>>> 参数: query = "支付失败原因"
>>> RequestId: a3b4c5d6
----------------------------------------
[L0 精确匹配] 完成: matches=0, time=2ms
[L1 语义检索] L0非唯一匹配,触发L1语义检索...
[L1 语义检索] 完成: matches=1, time=245ms
<<< [工具返回] lookup_knowledge
<<< 结果: found=true, matchType=semantic_L1, confidence=medium
========================================
========================================
*** [Agent 思考] 第 2 轮思考开始
*** [Agent 思考] 当前消息数量: 4
*** [Agent 思考] 最近 3 条消息:
[1] 角色: User(用户), 类型: UserMessage
[2] 角色: Assistant(模型), 类型: AssistantMessage
[3] 角色: Tool(工具返回), 类型: ToolResponseMessage
*** [Agent 思考] 准备调用模型...
========================================
========================================
*** [Agent 思考] 第 2 轮思考完成
*** [Agent 思考] 模型输出: <AssistantMessage>
*** [Agent 思考] 模型决定不调用工具
*** [Agent 思考] 这是最终答案,准备返回给用户
========================================
========================================
========== Agent 执行完成 ==========
========================================
⏱️ 执行耗时: 1523 ms
📏 最终输出长度: 456 字符
📤 最终输出内容:
根据知识库的记录,支付失败的主要原因包括...
========================================
```
---
## 核心观测点
| 阶段 | 日志标识 | 信息 |
|------|---------|------|
| **Agent 开始** | `Agent 执行开始` | 用户问题 |
| **思考开始** | `第 N 轮思考开始` | 消息数量、最近消息 |
| **思考完成** | `第 N 轮思考完成` | 模型决策(调用工具 or 返回答案) |
| **工具调用** | `工具调用 lookup_knowledge` | 工具名称、参数 |
| **工具返回** | `工具返回 lookup_knowledge` | 结果摘要、耗时 |
| **Agent 完成** | `Agent 执行完成` | 总耗时、最终输出 |
---
## Hook 机制说明
### MessagesModelHook
- **触发时机**:
- `BEFORE_MODEL`:模型调用前
- `AFTER_MODEL`:模型调用后
- **消息流转**:
```
用户问题
↓
[第1轮] beforeModel → 模型决定调用工具 → afterModel
↓
工具执行(lookup_knowledge)
↓
[第2轮] beforeModel → 模型生成最终答案 → afterModel
↓
返回给用户
```
- **轮次统计**:
- 每次调用模型计为一轮
- 通常需要 2 轮:第 1 轮调用工具,第 2 轮生成答案
---
## 技术细节
### 1. 为什么不用 ModelHook?
`ModelHook` 需要处理 `OverAllState`,更复杂。`MessagesModelHook` 直接操作消息列表,更简单。
### 2. 为什么跳过消息内容?
Spring AI 的 `Message` 接口没有统一的 `getContent()` 方法,不同实现类有不同的访问方式。工具调用的详细内容已在工具层日志体现。
### 3. 消息类型识别
```java
UserMessage → "User(用户)"
AssistantMessage → "Assistant(模型)"
ToolResponseMessage → "Tool(工具返回)"
```
---
## 验证方法
```bash
# 1. 启动应用
mvn spring-boot:run
# 2. 提问
curl -X POST http://localhost:9900/api/chat \
-H "Content-Type: application/json" \
-d '{"id":"test","question":"支付为什么会失败?"}'
# 3. 查看完整日志
tail -f logs/application.log
# 4. 过滤关键日志
tail -f logs/application.log | grep -E "Agent|思考|工具|输出"
```
---
## 提交历史
```
当前 feat(observability): 添加 Agent 思考过程日志 Hook
8890cd2 feat(observability): 增强 Agent 和工具调用的可观测日志
f4f0c63 fix(knowledge): 修复 readDocument 文件路径拼接问题
```
|
2026-06-25 17:02:13 +08:00 |
|
zhuyongxin
|
8890cd2806
|
feat(observability): 增强 Agent 和工具调用的可观测日志
## 改动内容
### 1. ChatService - Agent 执行日志
在 `executeChat` 方法中添加:
```
========================================
========== Agent 执行开始 ==========
========================================
📝 用户问题: 支付为什么会失败?
----------------------------------------
🚀 执行 ReactAgent.call() - 自动处理工具调用
========================================
========== Agent 执行完成 ==========
========================================
⏱️ 执行耗时: 1523 ms
📏 最终输出长度: 456 字符
----------------------------------------
📤 最终输出内容:
根据知识库的记录,支付失败的主要原因是...
========================================
```
**关键信息**:
- 用户问题
- 执行耗时
- 最终输出长度和内容
---
### 2. LookupKnowledgeTool - 工具调用详细日志
```
========================================
>>> [工具调用] lookup_knowledge
>>> 参数: query = "支付为什么会失败?"
>>> RequestId: a3b4c5d6
----------------------------------------
[L0 精确匹配] 完成: matches=0, time=3ms
[置信度判断] highConfidence=false, reason=多个或零个匹配
[L1 语义检索] L0非唯一匹配,触发L1语义检索...
[L1 语义检索] 完成: matches=1, time=245ms
[L1 语义检索] 找到文档:
- [1] 文档ID: doc-123, 相似度得分: 0.82
----------------------------------------
<<< [工具返回] lookup_knowledge
<<< 结果: found=true, matchType=semantic_L1, confidence=medium
<<< 总耗时: 248ms (L0=3ms, L1=245ms)
<<< 返回内容长度: 1234 字符
<<< 内容预览: ## 支付网关错误码定义...
========================================
```
**关键信息**:
- 工具名称和参数
- L0/L1 执行时间和结果
- 匹配文档列表
- 返回结果摘要
---
## 日志格式说明
### 符号约定
- `>>>` - 工具调用(入参)
- `<<<` - 工具返回(出参)
- `***` - Agent 思考过程(暂未实现)
- `📝` - 用户输入
- `📤` - Agent 输出
- `⏱️` - 性能指标
### 日志级别
- `INFO` - 关键节点和结果
- `DEBUG` - 详细的中间状态(已设置但默认不显示)
---
## 使用场景
### 1. 调试工具调用
```bash
# 查看工具调用详情
grep "工具调用\|工具返回" logs/application.log
# 输出示例
>>> [工具调用] lookup_knowledge
>>> 参数: query = "ERR_TIMEOUT"
<<< [工具返回] lookup_knowledge
<<< 结果: found=true, matchType=exact_L0, confidence=high
```
### 2. 性能分析
```bash
# 查看执行耗时
grep "执行耗时\|总耗时" logs/application.log
# 输出示例
⏱️ 执行耗时: 1523 ms
<<< 总耗时: 248ms (L0=3ms, L1=245ms)
```
### 3. L0/L1 验证
```bash
# 查看检索路径
grep "L0精确匹配\|L1语义检索" logs/application.log
# 示例 - L0 命中
[L0 精确匹配] 完成: matches=1, time=3ms
[L0 精确匹配] 找到文档:
- [1] 标题: 支付网关错误码定义, 路径: api/payment-errors.md
[L1 语义检索] L0唯一匹配,跳过L1检索
# 示例 - L1 命中
[L0 精确匹配] 完成: matches=0, time=2ms
[L1 语义检索] L0非唯一匹配,触发L1语义检索...
[L1 语义检索] 完成: matches=1, time=245ms
```
---
## 后续优化
### 可能的增强(未实现)
由于阿里云 ReactAgent 不支持内置监听器,以下功能暂时无法实现:
- ❌ Agent 思考过程实时监听(`onStateUpdate`)
- ❌ 工具调用前拦截(`onToolCall`)
- ❌ 工具返回后拦截(`onToolResponse`)
如需这些功能,需要:
1. 包装每个工具,统一添加日志
2. 或使用支持监听器的 Agent 框架
当前实现已满足基本可观测需求。
---
## 验证
```bash
# 1. 启动应用
mvn spring-boot:run
# 2. 发起对话
curl -X POST http://localhost:9900/api/chat \
-H "Content-Type: application/json" \
-d '{"id":"test","question":"支付为什么会失败?"}'
# 3. 查看日志
tail -f logs/application.log | grep -E "Agent|工具|输出"
```
|
2026-06-25 16:16:46 +08:00 |
|
zhuyongxin
|
dec587959c
|
feat(knowledge): 添加知识库批量初始化接口
## 新增功能
1. **KnowledgeBaseController**
- POST /api/knowledge/init - 批量初始化知识库
- GET /api/knowledge/stats - 查询统计信息
2. **KnowledgeBaseInitService**
- 递归扫描 knowledge_base 目录所有 .md 文件
- 解析 frontmatter 提取元数据
- 自动去重(基于文件路径)
- 数据入库到 api_document 表
- 自动加入 L0 内存索引
## 核心特性
### 去重机制
- 基于文件相对路径去重
- 支持 force=true 强制重新导入
- 跳过已存在文档,避免重复插入
### 数据存储
- 数据库:保存文档元数据(title、keywords、summary 等)
- L0 索引:加入 KnowledgeIndexService 内存索引
- L1 索引:暂未实现(TODO)
### 错误处理
- 格式无效:frontmatter 解析失败
- 缺少标题:必填字段验证
- 详细的错误信息反馈
## API 示例
```bash
# 首次导入
curl -X POST http://localhost:9900/api/knowledge/init
# 强制重新导入
curl -X POST http://localhost:9900/api/knowledge/init?force=true
# 查询统计
curl http://localhost:9900/api/knowledge/stats
```
## 响应示例
```json
{
"success": true,
"scanned": 6,
"skipped": 0,
"inserted": 6,
"failed": 0,
"details": {
"api/payment-errors.md": "导入成功(L0)"
}
}
```
## 后续扩展
- [ ] L1 向量索引(Milvus)集成
- [ ] 文档更新检测(基于文件哈希)
- [ ] 批量删除接口
- [ ] 进度回调支持
## 文档
- 使用文档:.docs/2026-06-25-knowledge-base-init-api.md
|
2026-06-25 10:49:30 +08:00 |
|
zhuyongxin
|
c3a232540a
|
refactor(phase1): 完成包名重构 (org.example → com.superbiz.agent)
Task 4.1: 包名统一重构
- 重命名 41 个 Java 文件的包名
- 更新所有 import 语句
- 恢复枚举类(FaultCategory、DiagnosisStatus、SourceType)
- 更新测试类的 import
重构范围:
- domain/entity: 3 个实体类
- domain/model: 2 个数据类
- domain/enums: 3 个枚举类
- repository: 3 个接口
- service/session: 2 个类(接口 + 实现)
- config: 9 个配置类
- controller: 2 个控制器
- agent/tool: 4 个工具类
- client: 1 个客户端
- Main.java: 主类
验证结果:
- 编译成功,无错误
- 所有测试通过 (27/27)
- ApiDocumentRepositoryTest: 7/7 ✅
- CaseLibraryRepositoryTest: 6/6 ✅
- DiagnosisRecordRepositoryTest: 6/6 ✅
- RedisSessionManagerTest: 8/8 ✅
Progress: 21/33 tasks completed (64%)
|
2026-06-23 14:56:04 +08:00 |
|