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 文件路径拼接问题
```
This commit is contained in:
@@ -7,6 +7,7 @@ import com.superbiz.agent.agent.tool.InternalDocsTools;
|
||||
import com.superbiz.agent.agent.tool.QueryLogsTools;
|
||||
import com.superbiz.agent.agent.tool.QueryMetricsTools;
|
||||
import com.superbiz.agent.tool.LookupKnowledgeTool;
|
||||
import com.superbiz.agent.hook.AgentLoggingHook;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -176,6 +177,7 @@ public class ChatService {
|
||||
.systemPrompt(systemPrompt)
|
||||
.methodTools(buildMethodToolsArray())
|
||||
.tools(getToolCallbacks())
|
||||
.hooks(new AgentLoggingHook()) // 添加日志 Hook
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user