fix(report): strip fabricated tool_result blocks to prevent LLM hallucination loop
This commit is contained in:
parent
866ed421e2
commit
49f4da51b1
|
|
@ -1132,7 +1132,14 @@ class ReportAgent:
|
||||||
data["parameters"] = data.pop("params")
|
data["parameters"] = data.pop("params")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _strip_fake_tool_results(response: str) -> str:
|
||||||
|
"""Strip <tool_result> blocks fabricated by the LLM to prevent hallucination loops."""
|
||||||
|
cleaned = re.sub(r'<tool_result>.*?</tool_result>', '', response, flags=re.DOTALL)
|
||||||
|
cleaned = re.sub(r'\n{3,}', '\n\n', cleaned)
|
||||||
|
return cleaned.strip()
|
||||||
|
|
||||||
def _get_tools_description(self) -> str:
|
def _get_tools_description(self) -> str:
|
||||||
"""Generate tool description text"""
|
"""Generate tool description text"""
|
||||||
desc_parts = ["Available tools:"]
|
desc_parts = ["Available tools:"]
|
||||||
|
|
@ -1322,7 +1329,7 @@ class ReportAgent:
|
||||||
logger.warning(t('report.sectionIterNone', title=section.title, iteration=iteration + 1))
|
logger.warning(t('report.sectionIterNone', title=section.title, iteration=iteration + 1))
|
||||||
# If iterations remain, append a message and retry
|
# If iterations remain, append a message and retry
|
||||||
if iteration < max_iterations - 1:
|
if iteration < max_iterations - 1:
|
||||||
messages.append({"role": "assistant", "content": "(empty response)"})
|
messages.append({"role": "assistant", "content": ReportAgent._strip_fake_tool_results("(empty response)")})
|
||||||
messages.append({"role": "user", "content": "Please continue generating content."})
|
messages.append({"role": "user", "content": "Please continue generating content."})
|
||||||
continue
|
continue
|
||||||
# Last iteration also returned None; break out of the loop and force a conclusion
|
# Last iteration also returned None; break out of the loop and force a conclusion
|
||||||
|
|
@ -1344,7 +1351,7 @@ class ReportAgent:
|
||||||
|
|
||||||
if conflict_retries <= 2:
|
if conflict_retries <= 2:
|
||||||
# First two occurrences: discard this response and ask the LLM to reply again
|
# First two occurrences: discard this response and ask the LLM to reply again
|
||||||
messages.append({"role": "assistant", "content": response})
|
messages.append({"role": "assistant", "content": ReportAgent._strip_fake_tool_results(response)})
|
||||||
messages.append({
|
messages.append({
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": (
|
"content": (
|
||||||
|
|
@ -1384,7 +1391,7 @@ class ReportAgent:
|
||||||
if has_final_answer:
|
if has_final_answer:
|
||||||
# Insufficient tool calls — reject and require more tool usage
|
# Insufficient tool calls — reject and require more tool usage
|
||||||
if tool_calls_count < min_tool_calls:
|
if tool_calls_count < min_tool_calls:
|
||||||
messages.append({"role": "assistant", "content": response})
|
messages.append({"role": "assistant", "content": ReportAgent._strip_fake_tool_results(response)})
|
||||||
unused_tools = all_tools - used_tools
|
unused_tools = all_tools - used_tools
|
||||||
unused_hint = f"(These tools have not been used yet, consider trying them: {', '.join(unused_tools)})" if unused_tools else ""
|
unused_hint = f"(These tools have not been used yet, consider trying them: {', '.join(unused_tools)})" if unused_tools else ""
|
||||||
messages.append({
|
messages.append({
|
||||||
|
|
@ -1414,7 +1421,7 @@ class ReportAgent:
|
||||||
if has_tool_calls:
|
if has_tool_calls:
|
||||||
# Tool quota exhausted → notify clearly and require Final Answer output
|
# Tool quota exhausted → notify clearly and require Final Answer output
|
||||||
if tool_calls_count >= self.MAX_TOOL_CALLS_PER_SECTION:
|
if tool_calls_count >= self.MAX_TOOL_CALLS_PER_SECTION:
|
||||||
messages.append({"role": "assistant", "content": response})
|
messages.append({"role": "assistant", "content": ReportAgent._strip_fake_tool_results(response)})
|
||||||
messages.append({
|
messages.append({
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": REACT_TOOL_LIMIT_MSG.format(
|
"content": REACT_TOOL_LIMIT_MSG.format(
|
||||||
|
|
@ -1462,7 +1469,7 @@ class ReportAgent:
|
||||||
if unused_tools and tool_calls_count < self.MAX_TOOL_CALLS_PER_SECTION:
|
if unused_tools and tool_calls_count < self.MAX_TOOL_CALLS_PER_SECTION:
|
||||||
unused_hint = REACT_UNUSED_TOOLS_HINT.format(unused_list=", ".join(unused_tools))
|
unused_hint = REACT_UNUSED_TOOLS_HINT.format(unused_list=", ".join(unused_tools))
|
||||||
|
|
||||||
messages.append({"role": "assistant", "content": response})
|
messages.append({"role": "assistant", "content": ReportAgent._strip_fake_tool_results(response)})
|
||||||
messages.append({
|
messages.append({
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": REACT_OBSERVATION_TEMPLATE.format(
|
"content": REACT_OBSERVATION_TEMPLATE.format(
|
||||||
|
|
@ -1477,7 +1484,7 @@ class ReportAgent:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# ── Case 3: Neither a tool call nor a Final Answer ──
|
# ── Case 3: Neither a tool call nor a Final Answer ──
|
||||||
messages.append({"role": "assistant", "content": response})
|
messages.append({"role": "assistant", "content": ReportAgent._strip_fake_tool_results(response)})
|
||||||
|
|
||||||
if tool_calls_count < min_tool_calls:
|
if tool_calls_count < min_tool_calls:
|
||||||
# Insufficient tool calls — recommend unused tools
|
# Insufficient tool calls — recommend unused tools
|
||||||
|
|
@ -1867,7 +1874,7 @@ class ReportAgent:
|
||||||
tool_calls_made.append(call)
|
tool_calls_made.append(call)
|
||||||
|
|
||||||
# Append the results to the messages
|
# Append the results to the messages
|
||||||
messages.append({"role": "assistant", "content": response})
|
messages.append({"role": "assistant", "content": ReportAgent._strip_fake_tool_results(response)})
|
||||||
observation = "\n".join([f"[{r['tool']} result]\n{r['result']}" for r in tool_results])
|
observation = "\n".join([f"[{r['tool']} result]\n{r['result']}" for r in tool_results])
|
||||||
messages.append({
|
messages.append({
|
||||||
"role": "user",
|
"role": "user",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue