Skip to content

Commit 65ade02

Browse files
Patori Patch ManagerAstrBot Local
authored andcommitted
fix: context overflow - tool result truncation + multi-round halving + auto compact
Changes: 1. tool_loop_agent_runner.py: - Truncate tool results > 12000 chars to prevent single-tool context bloat - Replace single-round halving with multi-round (up to 5x) aggressive halving on token_limit errors, ensuring retry succeeds 2. astrbot_plugin_memory_system/main.py: - on_llm_request: add pre-emptive truncation when history > 200k chars (~50k tokens) truncates to 20 most recent turns before sending to LLM - /compact: enhanced command with modes: /compact [N] - LLM summary compress, keep N recent turns (default 6) /compact hard - fast truncate to 20 recent turns, no LLM cost /compact clear - clear all history
1 parent cf9ee6f commit 65ade02

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

astrbot/core/agent/runners/tool_loop_agent_runner.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,80 @@ async def _iter_llm_responses_with_fallback(
265265
return
266266
except Exception as exc: # noqa: BLE001
267267
last_exception = exc
268+
# Auto-compress context when model_max_prompt_tokens_exceeded
269+
_exc_str = str(exc).lower()
270+
if (
271+
"model_max_prompt_tokens_exceeded" in _exc_str
272+
or "prompt token count" in _exc_str
273+
or "tokens_exceeded" in _exc_str
274+
or "context_length_exceeded" in _exc_str
275+
or ("token" in _exc_str and "exceed" in _exc_str)
276+
):
277+
logger.warning(
278+
"Chat Model %s: token limit exceeded, forcing context compression and retrying...",
279+
candidate_id,
280+
)
281+
try:
282+
from astrbot.core.agent.context.truncator import ContextTruncator
283+
_truncator = ContextTruncator()
284+
_before_total = len(self.run_context.messages)
285+
# Aggressively halve until small enough (up to 5 rounds)
286+
_compression_success = False
287+
for _halve_round in range(5):
288+
_before = len(self.run_context.messages)
289+
self.run_context.messages = _truncator.truncate_by_halving(
290+
self.run_context.messages
291+
)
292+
_after = len(self.run_context.messages)
293+
logger.info(
294+
"Forced context truncation round %d: %d -> %d messages.",
295+
_halve_round + 1, _before, _after,
296+
)
297+
if _after <= 4:
298+
break # Can't shrink further
299+
# Retry same candidate
300+
try:
301+
async for resp in self._iter_llm_responses(include_model=idx == 0):
302+
if resp.is_chunk:
303+
has_stream_output = True
304+
yield resp
305+
continue
306+
yield resp
307+
logger.info(
308+
"Context truncation succeeded after %d round(s): %d -> %d messages.",
309+
_halve_round + 1, _before_total, _after,
310+
)
311+
_compression_success = True
312+
return
313+
if has_stream_output:
314+
_compression_success = True
315+
return
316+
_compression_success = True
317+
break # succeeded without stream output
318+
except Exception as retry_exc:
319+
_exc_str2 = str(retry_exc).lower()
320+
if not (
321+
"model_max_prompt_tokens_exceeded" in _exc_str2
322+
or "prompt token count" in _exc_str2
323+
or "tokens_exceeded" in _exc_str2
324+
or "context_length_exceeded" in _exc_str2
325+
or ("token" in _exc_str2 and "exceed" in _exc_str2)
326+
):
327+
last_exception = retry_exc
328+
logger.warning(
329+
"Chat Model %s retry after compression failed: %s",
330+
candidate_id, retry_exc,
331+
)
332+
break
333+
last_exception = retry_exc
334+
logger.warning(
335+
"Chat Model %s still token-exceeded after round %d, halving again...",
336+
candidate_id, _halve_round + 1,
337+
)
338+
continue
339+
except Exception as compress_exc:
340+
logger.error("Failed to compress context: %s", compress_exc)
341+
continue
268342
logger.warning(
269343
"Chat Model %s request error: %s",
270344
candidate_id,
@@ -657,6 +731,14 @@ async def _handle_function_tools(
657731
logger.info(f"Agent 使用工具: {llm_response.tools_call_name}")
658732

659733
def _append_tool_call_result(tool_call_id: str, content: str) -> None:
734+
# Truncate oversized tool results to prevent context bloat
735+
MAX_TOOL_RESULT_CHARS = 12000
736+
if len(content) > MAX_TOOL_RESULT_CHARS:
737+
truncated_len = len(content) - MAX_TOOL_RESULT_CHARS
738+
content = (
739+
content[:MAX_TOOL_RESULT_CHARS]
740+
+ f"\n... [TRUNCATED {truncated_len} chars to prevent context overflow]"
741+
)
660742
tool_call_result_blocks.append(
661743
ToolCallMessageSegment(
662744
role="tool",

0 commit comments

Comments
 (0)