fix: filter history to one user and one assistant per turn

This commit is contained in:
zhayujie
2026-02-28 18:09:02 +08:00
parent 6ed85029c5
commit a773eb7893

View File

@@ -168,62 +168,75 @@ class AgentInitializer:
@staticmethod @staticmethod
def _filter_text_only_messages(messages: list) -> list: def _filter_text_only_messages(messages: list) -> list:
""" """
Filter messages to keep only user text and assistant text. Extract clean user/assistant turn pairs from raw message history.
Strips out: Groups messages into turns (each starting with a real user query),
- assistant messages that only contain tool_use (no text) then keeps only:
- user messages that only contain tool_result (no text) - The first user text in each turn (the actual user input)
- internal hint messages injected by the agent loop - The last assistant text in each turn (the final answer)
Keeps: All tool_use, tool_result, intermediate assistant thoughts, and
- user messages with actual text content internal hint messages injected by the agent loop are discarded.
- assistant messages with text content (tool_use blocks removed,
only the text portion is kept)
""" """
filtered = []
for msg in messages:
role = msg.get("role")
content = msg.get("content")
def _extract_text(content) -> str:
if isinstance(content, str): if isinstance(content, str):
if content.strip(): return content.strip()
filtered.append(msg) if isinstance(content, list):
continue parts = [
if not isinstance(content, list):
continue
if role == "user":
text_parts = [
b.get("text", "") b.get("text", "")
for b in content for b in content
if isinstance(b, dict) and b.get("type") == "text" if isinstance(b, dict) and b.get("type") == "text"
] ]
return "\n".join(p for p in parts if p).strip()
return ""
def _is_real_user_msg(msg: dict) -> bool:
"""True for actual user input, False for tool_result or internal hints."""
if msg.get("role") != "user":
return False
content = msg.get("content")
if isinstance(content, list):
has_tool_result = any( has_tool_result = any(
isinstance(b, dict) and b.get("type") == "tool_result" isinstance(b, dict) and b.get("type") == "tool_result"
for b in content for b in content
) )
if has_tool_result: if has_tool_result:
continue return False
text = "\n".join(p for p in text_parts if p).strip() text = _extract_text(content)
if text: return bool(text)
filtered.append({
"role": "user",
"content": [{"type": "text", "text": text}]
})
elif role == "assistant": # Group into turns: each turn starts with a real user message
text_parts = [ turns = []
b.get("text", "") current_turn = None
for b in content for msg in messages:
if isinstance(b, dict) and b.get("type") == "text" if _is_real_user_msg(msg):
] if current_turn is not None:
text = "\n".join(p for p in text_parts if p).strip() turns.append(current_turn)
current_turn = {"user": msg, "assistants": []}
elif current_turn is not None and msg.get("role") == "assistant":
text = _extract_text(msg.get("content"))
if text: if text:
filtered.append({ current_turn["assistants"].append(text)
"role": "assistant", if current_turn is not None:
"content": [{"type": "text", "text": text}] turns.append(current_turn)
})
# Build result: one user msg + one assistant msg per turn
filtered = []
for turn in turns:
user_text = _extract_text(turn["user"].get("content"))
if not user_text:
continue
filtered.append({
"role": "user",
"content": [{"type": "text", "text": user_text}]
})
if turn["assistants"]:
final_reply = turn["assistants"][-1]
filtered.append({
"role": "assistant",
"content": [{"type": "text", "text": final_reply}]
})
return filtered return filtered