Skip to content

Commit ca2784c

Browse files
authored
Fix/process validation 2026 03 09 (#152)
1 parent a2f060d commit ca2784c

File tree

11 files changed

+917
-173
lines changed

11 files changed

+917
-173
lines changed

REACT_MASTER_V2_LOOP_BUG_FIX.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# ReActMasterV2 循环执行工具 Bug 修复报告
2+
3+
## 问题概述
4+
5+
ReActMasterV2 Agent 在执行过程中出现循环调用同一个工具的问题,导致任务无法正常完成。
6+
7+
### 现象
8+
9+
用户询问"今天12点30是否有系统异常"时,Agent 反复执行同一个工具调用:
10+
11+
```
12+
view {"path": "/Users/tuyang/GitHub/OpenDerisk/pilot/data/skill/open_rca_diagnosis/SKILL.md"}
13+
```
14+
15+
这个工具被调用了 3 次以上,形成无限循环。
16+
17+
## 根本原因
18+
19+
### 问题定位
20+
21+
`packages/derisk-core/src/derisk/agent/core/base_agent.py``generate_reply` 方法中(line 820-827),存在一个条件判断错误:
22+
23+
```python
24+
if self.current_retry_counter > 0:
25+
if self.run_mode != AgentRunMode.LOOP: # ❌ 问题所在
26+
if self.enable_function_call:
27+
tool_messages = self.function_callning_reply_messages(
28+
agent_llm_out, act_outs
29+
)
30+
all_tool_messages.extend(tool_messages)
31+
```
32+
33+
### 问题分析
34+
35+
1. **ReActMasterV2 的运行模式**
36+
- ReActMasterV2 使用 `AgentRunMode.LOOP` 模式
37+
- 这意味着它会循环执行多个迭代,直到任务完成
38+
39+
2. **Bug 的影响**
40+
- 条件 `self.run_mode != AgentRunMode.LOOP` 导致 LOOP 模式的 Agent **不会**将工具调用结果追加到 `all_tool_messages`
41+
- 结果:LLM 在每次迭代时都看不到之前的工具调用结果
42+
- LLM 认为还没有调用过工具,于是再次调用同一个工具
43+
- 形成无限循环
44+
45+
3. **为什么 WorkLog 没起作用**
46+
- WorkLog 确实记录了工具调用(通过 `_record_action_to_work_log`
47+
- 但 WorkLog 的注入只在循环开始前执行一次(条件 `self.current_retry_counter == 0`
48+
- 在 LOOP 模式的后续迭代中,WorkLog 不会被重新获取
49+
- 即使 WorkLog 记录了工具调用,它也不会被转换为 tool_messages 传给 LLM
50+
51+
## 修复方案
52+
53+
### 代码修改
54+
55+
移除 `self.run_mode != AgentRunMode.LOOP` 条件,让所有模式的 Agent 都能接收工具调用结果:
56+
57+
**修改前(BUGGY)**
58+
```python
59+
if self.current_retry_counter > 0:
60+
if self.run_mode != AgentRunMode.LOOP: # ❌ 移除这个条件
61+
if self.enable_function_call:
62+
tool_messages = self.function_callning_reply_messages(
63+
agent_llm_out, act_outs
64+
)
65+
all_tool_messages.extend(tool_messages)
66+
```
67+
68+
**修改后(FIXED)**
69+
```python
70+
if self.current_retry_counter > 0:
71+
if self.enable_function_call: # ✅ 所有模式都执行
72+
tool_messages = self.function_callning_reply_messages(
73+
agent_llm_out, act_outs
74+
)
75+
all_tool_messages.extend(tool_messages)
76+
```
77+
78+
### 修复文件
79+
80+
- **文件路径**`packages/derisk-core/src/derisk/agent/core/base_agent.py`
81+
- **修改行**:Line 821
82+
- **修改类型**:移除条件判断
83+
84+
## 修复效果
85+
86+
### 修复前的行为
87+
88+
```
89+
Iteration 1:
90+
- LLM 调用 view("/path/to/SKILL.md")
91+
- 结果:技能文件内容
92+
- ❌ 结果未添加到 all_tool_messages(因为是 LOOP 模式)
93+
94+
Iteration 2:
95+
- LLM prompt:与 iteration 1 相同(没有工具结果可见)
96+
- LLM 认为:"我应该加载技能文件"
97+
- LLM 再次调用 view("/path/to/SKILL.md") ← 相同调用
98+
- ❌ 结果未添加到 all_tool_messages
99+
100+
Iteration 3:
101+
- 与 iteration 2 相同
102+
- 无限循环!
103+
```
104+
105+
### 修复后的行为
106+
107+
```
108+
Iteration 1:
109+
- LLM 调用 view("/path/to/SKILL.md")
110+
- 结果:技能文件内容
111+
- ✅ 结果添加到 all_tool_messages
112+
113+
Iteration 2:
114+
- LLM prompt:包含 iteration 1 的工具结果
115+
- LLM 看到:"我已经加载了技能文件,现在应该..."
116+
- LLM 根据技能内容调用下一个工具
117+
- 结果:分析数据
118+
- ✅ 结果添加到 all_tool_messages
119+
120+
Iteration 3:
121+
- LLM prompt:包含 iterations 1 和 2 的结果
122+
- LLM 做出最终决策
123+
- 调用 terminate 完成任务
124+
```
125+
126+
## 验证
127+
128+
### 诊断脚本
129+
130+
创建了两个诊断脚本:
131+
132+
1. **`diagnose_loop_tool_messages.py`**:检测 bug 是否存在
133+
2. **`verify_loop_fix.py`**:验证修复是否成功
134+
135+
### 验证结果
136+
137+
```bash
138+
$ python3 verify_loop_fix.py
139+
140+
✅ FIX APPLIED: Buggy condition has been removed
141+
✅ CORRECT CODE: Tool messages are now appended for all modes
142+
```
143+
144+
## 影响范围
145+
146+
### 受影响的 Agent
147+
148+
- **ReActMasterV2**:主要受影响的 Agent
149+
- **所有使用 AgentRunMode.LOOP 模式的 Agent**
150+
151+
### 受益的功能
152+
153+
- ✅ 工具调用结果现在能正确传递给 LLM
154+
- ✅ LLM 能基于历史结果做出明智决策
155+
- ✅ 防止因 LLM 不知道工具已调用而导致的无限循环
156+
- ✅ WorkLog 记录现在能通过 tool_messages 对 LLM 可见
157+
158+
## 后续步骤
159+
160+
1. **重启服务器**:应用代码修改
161+
2. **测试验证**
162+
- 使用之前导致循环的查询进行测试
163+
- 验证工具结果现在在 LLM prompt 中可见
164+
- 确认任务能正常完成
165+
166+
3. **监控**
167+
- 观察 ReActMasterV2 的执行日志
168+
- 确认不再出现重复工具调用
169+
- 验证任务完成效率提升
170+
171+
## 总结
172+
173+
这个 bug 是一个典型的"上下文丢失"问题:
174+
175+
- **症状**:Agent 循环调用同一个工具
176+
- **根因**:LOOP 模式的 Agent 在迭代间丢失了工具调用结果
177+
- **修复**:移除错误的条件判断,让所有模式都能接收工具结果
178+
- **效果**:Agent 现在能基于历史结果做出正确决策,避免无限循环
179+
180+
修复后,ReActMasterV2 将能够:
181+
- 正确执行多步骤任务
182+
- 基于前序工具结果做决策
183+
- 高效完成任务,不再陷入循环
184+
185+
---
186+
187+
**修复日期**:2026-03-09
188+
**修复文件**`packages/derisk-core/src/derisk/agent/core/base_agent.py`
189+
**修复行数**:Line 821
190+
**修复类型**:移除条件判断 `self.run_mode != AgentRunMode.LOOP`

diagnose_loop_tool_messages.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Diagnostic script to verify ReActMasterV2 loop tool_messages bug.
4+
5+
Issue: In AgentRunMode.LOOP mode, tool call results are NOT appended to all_tool_messages,
6+
causing LLM to repeatedly call the same tool because it doesn't see previous results.
7+
8+
Root Cause: Line 821 in base_agent.py has condition `self.run_mode != AgentRunMode.LOOP`
9+
which skips appending tool_messages for LOOP mode agents.
10+
11+
Expected Behavior: Tool call results should be appended to all_tool_messages for ALL modes.
12+
"""
13+
14+
import sys
15+
from pathlib import Path
16+
17+
# Add project paths
18+
_project_root = Path(__file__).parent
19+
sys.path.insert(0, str(_project_root / "packages/derisk-core/src"))
20+
21+
22+
def check_base_agent_code():
23+
"""Check if the bug exists in base_agent.py"""
24+
print("=" * 80)
25+
print("Checking base_agent.py for LOOP mode tool_messages bug")
26+
print("=" * 80)
27+
28+
base_agent_path = (
29+
_project_root / "packages/derisk-core/src/derisk/agent/core/base_agent.py"
30+
)
31+
32+
if not base_agent_path.exists():
33+
print(f"❌ File not found: {base_agent_path}")
34+
return False
35+
36+
with open(base_agent_path, "r") as f:
37+
lines = f.readlines()
38+
39+
# Find the problematic code section (around line 820-827)
40+
print("\n📍 Checking lines 820-827 for the bug condition:\n")
41+
42+
bug_found = False
43+
for i in range(819, min(828, len(lines))):
44+
line = lines[i]
45+
line_num = i + 1
46+
print(f" {line_num:4d}: {line.rstrip()}")
47+
48+
# Check for the bug condition
49+
if "if self.run_mode != AgentRunMode.LOOP:" in line:
50+
bug_found = True
51+
print(
52+
"\n ⚠️ BUG FOUND: This condition prevents LOOP mode agents from getting tool_messages!"
53+
)
54+
55+
print("\n" + "-" * 80)
56+
57+
if bug_found:
58+
print("❌ BUG CONFIRMED: LOOP mode agents will NOT receive tool call results")
59+
print("\n🔧 Impact:")
60+
print(" - ReActMasterV2 (LOOP mode) will repeatedly call the same tool")
61+
print(" - LLM doesn't see previous tool results in next iteration")
62+
print(" - WorkLog records tools but doesn't inject them to LLM prompt")
63+
print("\n💡 Fix: Remove the 'self.run_mode != AgentRunMode.LOOP' condition")
64+
print(" OR handle LOOP mode specially to inject tool messages")
65+
else:
66+
print("✅ No bug found in this section (may have been fixed)")
67+
68+
return bug_found
69+
70+
71+
def explain_the_bug():
72+
"""Explain the bug in detail"""
73+
print("\n" + "=" * 80)
74+
print("DETAILED BUG EXPLANATION")
75+
print("=" * 80)
76+
77+
print("""
78+
## Problem
79+
80+
ReActMasterV2 uses AgentRunMode.LOOP mode to execute multiple iterations.
81+
In each iteration, it should:
82+
1. Call a tool
83+
2. Get result
84+
3. Pass result to LLM in next iteration
85+
4. LLM decides next action based on results
86+
87+
## What Actually Happens
88+
89+
In base_agent.py generate_reply() method (line 820-827):
90+
91+
if self.current_retry_counter > 0:
92+
if self.run_mode != AgentRunMode.LOOP: # ⚠️ PROBLEM: This excludes LOOP mode!
93+
if self.enable_function_call:
94+
tool_messages = self.function_callning_reply_messages(agent_llm_out, act_outs)
95+
all_tool_messages.extend(tool_messages) # ❌ NOT executed for LOOP mode
96+
97+
Result:
98+
- For LOOP mode agents, tool_messages are NEVER appended to all_tool_messages
99+
- LLM sees the SAME context in each iteration (no tool results)
100+
- LLM calls the same tool again → infinite loop
101+
102+
## Why WorkLog Doesn't Help
103+
104+
WorkLog injection happens only ONCE at the start (line 798-804):
105+
106+
if self.enable_function_call and self.current_retry_counter == 0:
107+
worklog_messages = await self._get_worklog_tool_messages()
108+
all_tool_messages.extend(worklog_messages)
109+
110+
The condition `self.current_retry_counter == 0` means WorkLog is only fetched once.
111+
In subsequent LOOP iterations, WorkLog is NOT re-fetched.
112+
113+
## Solution
114+
115+
Remove the `self.run_mode != AgentRunMode.LOOP` condition to allow LOOP mode agents
116+
to receive tool call results in each iteration:
117+
118+
if self.current_retry_counter > 0:
119+
if self.enable_function_call:
120+
tool_messages = self.function_callning_reply_messages(agent_llm_out, act_outs)
121+
all_tool_messages.extend(tool_messages)
122+
""")
123+
124+
125+
def suggest_fix():
126+
"""Suggest the fix"""
127+
print("\n" + "=" * 80)
128+
print("SUGGESTED FIX")
129+
print("=" * 80)
130+
131+
print("""
132+
## File: packages/derisk-core/src/derisk/agent/core/base_agent.py
133+
134+
## Location: Line 820-827
135+
136+
## Current Code (BUGGY):
137+
```python
138+
if self.current_retry_counter > 0:
139+
if self.run_mode != AgentRunMode.LOOP: # ❌ Remove this condition
140+
if self.enable_function_call:
141+
tool_messages = self.function_callning_reply_messages(agent_llm_out, act_outs)
142+
all_tool_messages.extend(tool_messages)
143+
```
144+
145+
## Fixed Code:
146+
```python
147+
if self.current_retry_counter > 0:
148+
if self.enable_function_call:
149+
tool_messages = self.function_callning_reply_messages(agent_llm_out, act_outs)
150+
all_tool_messages.extend(tool_messages)
151+
```
152+
153+
## Why This Works:
154+
- Removes the LOOP mode exclusion
155+
- All agents (including ReActMasterV2) will now receive tool call results
156+
- LLM can see previous tool results and make informed decisions
157+
- Prevents infinite loops caused by LLM not knowing tools were already called
158+
""")
159+
160+
161+
def main():
162+
print("\n" + "🔍" * 40)
163+
print("ReActMasterV2 LOOP Mode Tool Messages Bug Diagnostic")
164+
print("🔍" * 40 + "\n")
165+
166+
# Check for the bug
167+
bug_exists = check_base_agent_code()
168+
169+
# Explain the bug
170+
explain_the_bug()
171+
172+
# Suggest fix
173+
suggest_fix()
174+
175+
# Summary
176+
print("\n" + "=" * 80)
177+
print("SUMMARY")
178+
print("=" * 80)
179+
180+
if bug_exists:
181+
print("❌ Bug confirmed in base_agent.py line 821")
182+
print("✅ Fix: Remove 'self.run_mode != AgentRunMode.LOOP' condition")
183+
print(
184+
"\nThis will resolve the issue where ReActMasterV2 repeatedly calls tools"
185+
)
186+
print("without seeing previous results, causing infinite loops.")
187+
return 1
188+
else:
189+
print("✅ Bug may have been fixed or code has changed")
190+
print("Please verify manually that LOOP mode agents receive tool messages")
191+
return 0
192+
193+
194+
if __name__ == "__main__":
195+
sys.exit(main())

0 commit comments

Comments
 (0)