Skip to main content

agent-debugging

Implements systematic debugging workflows for LLM agent failures including hallucination detection, infinite loop recovery, context window exhaustion, tool call errors, and cascading failure diagnosis using distributed tracing patterns.

설치로 이동

소스 정보

저장소
paulpas/agent-skill-router
최근 소스 활동
2026년 6월 4일 23:31
감지된 SKILL.md 언어
영어
스타
4
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
agent-debugging
description
Implements systematic debugging workflows for LLM agent failures including hallucination detection, infinite loop recovery, context window exhaustion, tool call errors, and cascading failure diagnosis using distributed tracing patterns.
license
MIT
compatibility
opencode
metadata
{"version":"1.0.0","domain":"agent","triggers":"agent debugging, LLM agent failure, hallucination detection, infinite loop recovery, context window exhaustion, tool call error, how do i debug an agent","role":"implementation","scope":"implementation","output-format":"code","content-types":["code","guidance","do-dont"],"related-skills":"observability-patterns, agentic-evaluation, multi-agent-orchestration","archetypes":["tactical","diagnostic"],"anti_triggers":"brainstorming, vague ideation, long-form architecture planning","response_profile":{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"}}
# Agent Debugging Toolkit Diagnoses and resolves LLM agent failures using systematic debugging workflows. Covers hallucination detection, infinite loop recovery, context window management, tool call validation, and cascading failure diagnosis through distributed tracing patterns. ## TL;DR for Code Generation - Always create and propagate a correlation `run_id` via `contextvars` across async boundaries before any agent step - Wrap every tool invocation with structured logging that records arguments, output, latency, and status - Track tool call sequences to detect infinite loops — trigger a circuit breaker after 10 identical or near-identical iterations - Count tokens per step; when usage exceeds 80% of context budget, activate sliding-window summarization - Validate every tool name against the registered schema before execution to catch hallucinations early --- ## When to Use Use this skill when: - An agent is stuck in an infinite loop of retries or repeated tool calls with identical arguments - The LLM hallucinates non-existent tool names, parameters, or output fields causing repeated failures - Context window overflow causes the agent to lose early system instructions and produce erratic behavior - Tool call argument mismatches between the registered schema and actual input cause silent failures - Multiple agents in a multi-agent pipeline fail cascadingly and you need to isolate the root failure point - Latency degradation is accelerating — each iteration takes progressively longer due to context bloat --- ## When NOT to Use Avoid this skill for: - Production monitoring, dashboards, and metric collection — use `observability-patterns` instead - Systematic quality evaluation and regression testing of agent outputs — use `agentic-evaluation` instead - Prompt design and system instruction optimization — use a prompt engineering skill instead - High-level architectural debugging (e.g., microservice topology issues) — route to `multi-agent-orchestration` for cross-boundary coordination problems --- ## Core Workflow 1. **Generate Run ID** — Create a unique correlation ID for the agent invocation using `uuid4()` and propagate it via `contextvars.ContextVar` across all async boundaries. **Checkpoint:** Ensure every log line, trace span, and error message includes the run ID so you can reconstruct the full execution timeline. 2. **Map Execution Path** — Trace the full sequence: user input → router decision → tool selection → tool execution → LLM response. Instrument each step with timing metadata. **Checkpoint:** Identify the exact step where behavior diverges from expected output — do not assume the first visible symptom is the root cause. 3. **Classify Failure Tier** — Determine if the failure is Tier 1 (high-frequency: hallucination, incorrect tool use, infinite loops), Tier 2 (structural: missing context, prompt injection, cascading failures), or Tier 3 (operational: unbounded token spend, latency degradation, resource exhaustion). **Checkpoint:** Each tier requires distinct diagnostic patterns — do not apply a Tier 1 fix to a Tier 2 structural problem. 4. **Apply Targeted Diagnostic** — Select the implementation pattern matching your failure tier (see Implementation Patterns below). Apply the fix in an isolated test context before deploying to production. **Checkpoint:** Verify the fix resolves the original failure without introducing regressions in related paths. 5. **Validate Fix** — Re-run the agent with the fix applied across 3+ diverse test inputs covering edge cases. Confirm correct behavior, bounded iteration counts, and stable token usage. **Checkpoint:** All metrics (iterations, token count, tool call success rate) must remain within defined thresholds before clearing the incident. --- ## Implementation Patterns ### Pattern 1: Run ID Propagation with `contextvars` Create and propagate a correlation run ID across async boundaries using Python's `contextvars`. This enables full trace reconstruction from any single log line. ```python import uuid import contextvars import functools import time import logging from typing import Any, Callable, TypeVar, ParamSpec logger = logging.getLogger(__name__) # Singleton context variable for run ID propagation across async boundaries _run_id: contextvars.ContextVar[str] = contextvars.ContextVar("run_id", default="") _task_name: contextvars.ContextVar[str] = contextvars.ContextVar("task_name", default="") P = ParamSpec("P") R = TypeVar("R") def generate_run_id() -> str: """Generate a unique correlation ID for an agent invocation. Returns: A UUID4 string formatted as run-<uuid>. """ run_id = f"run-{uuid.uuid4().hex[:12]}" _run_id.set(run_id) logger.info("Generated new run ID: %s", run_id) return run_id def get_run_id() -> str: """Retrieve the current run ID from context. Returns: The active run ID string, or 'no-run-id' if none is set. """ current = _run_id.get() return current if current else "no-run-id" def with_tracing(task_name: str): """Decorator that instruments a function with tracing metadata. Wraps the decorated function to log entry/exit, duration, and any exceptions, all tagged with the active run ID. Args: task_name: Human-readable label for this execution step. Returns: A decorator that adds tracing instrumentation. """ def decorator(func: Callable[P, R]) -> Callable[P, R]: @functools.wraps(func) async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: run_id = get_run_id() _task_name.set(task_name) start = time.perf_counter() logger.info( "[run=%s] START task=%s", run_id, task_name, extra={"args_count": len(args), "kwargs_keys": list(kwargs.keys())}, ) try: result = await func(*args, **kwargs) duration_ms = (time.perf_counter() - start) * 1000 logger.info( "[run=%s] DONE task=%s duration=%.1fms", run_id, task_name, duration_ms, ) return result except Exception as exc: duration_ms = (time.perf_counter() - start) * 1000 logger.exception( "[run=%s] FAIL task=%s duration=%.1fms error=%s", run_id, task_name, duration_ms, type(exc).__name__, ) raise return wrapper # type: ignore[return-value] return decorator ``` Usage — attach to LLM call and tool execution functions: ```python @with_tracing("llm_completion") async def call_llm(messages: list[dict[str, str]], model: str) -> dict[str, Any]: """LLM API wrapper tagged with run ID and timing.""" ... @with_tracing("search_database") async def search_database(query: str) -> list[dict]: """Tool execution with full trace logging.""" ... ``` --- ### Pattern 2: Infinite Loop Detection and Circuit Breaker Tracks tool call sequences and detects repetition using exact-match and argument-similarity checks. Triggers a circuit breaker that forces fallback behavior after N consecutive repeats. ```python import time from collections import deque from dataclasses import dataclass, field from typing import Any, Optional @dataclass class ToolCallRecord: """Immutable snapshot of a single tool invocation.""" tool_name: str arguments: tuple[Any, ...] output_hash: str timestamp: float = field(default_factory=time.time) success: bool = True def to_tuple(self) -> tuple[str, tuple[Any, ...], str]: """Serialize for comparison and hashing.""" return (self.tool_name, self.arguments, self.output_hash) class CircuitBreakerError(Exception): """Raised when the circuit breaker opens due to repeated failures.""" def __init__(self, message: str, failed_tool: str | None = None) -> None: self.failed_tool = failed_tool super().__init__(message) class InfiniteLoopDetector: """Detects infinite loops by tracking tool call sequences and enforcing a circuit breaker when repeated patterns exceed a threshold. Attributes: max_repeats: Max consecutive identical-or-near-identical calls before breaking. window_size: Number of recent calls to compare against for similarity detection. """ def __init__( self, max_repeats: int = 3, window_size: int = 5, fallback_handler: Optional[Any] = None, ) -> None: self._max_repeats = max_repeats self._window_size = window_size self._fallback_handler = fallback_handler self._call_history: deque[ToolCallRecord] = deque(maxlen=window_size) self._repeat_count = 0 def record_call( self, tool_name: str, arguments: dict[str, Any], output_hash: str, success: bool = True, ) -> ToolCallRecord: """Record a tool call and check for infinite loop conditions. Args: tool_name: Name of the tool being called. arguments: Dictionary of arguments passed to the tool. output_hash: Hash of the tool's output for change detection. success: Whether the tool call succeeded. Returns: The recorded ToolCallRecord. Raises: CircuitBreakerError: If repeated identical calls exceed threshold. """ args_tuple = tuple(sorted(arguments.items())) record = ToolCallRecord( tool_name=tool_name, arguments=args_tuple, output_hash=output_hash, success=success, ) self._call_history.append(record) if len(self._call_history) < 2: return record # Check for exact repeat: same tool + same args + same output recent = list(self._call_history)[-self._max_repeats:] is_repeat = all( r.tool_name == record.tool_name and r.arguments == record.arguments for r in recent[:-1] # compare with all but the newest ) if is_repeat: self._repeat_count += 1 run_id = get_run_id() logger.warning( "[run=%s] LOOP DETECTED: tool='%s' repeated %d/%d times", run_id, tool_name, self._repeat_count, self._max_repeats, ) else: # Reset counter on any non-repeating call self._repeat_count = 0 if self._repeat_count >= self._max_repeats: run_id = get_run_id() logger.error( "[run=%s] CIRCUIT BREAKER OPEN after %d repeats of tool='%s'", run_id, self._repeat_count, tool_name, ) if self._fallback_handler: return self._fallback_handler(tool_name, arguments) raise CircuitBreakerError( f"Tool '{tool_name}' repeated {self._max_repeats} times with identical " f"arguments — circuit breaker opened. Possible infinite loop." ) return record def reset(self) -> None: """Reset the detector state after a successful recovery.""" self._call_history.clear() self._repeat_count = 0 run_id = get_run_id() logger.info("[run=%s] Circuit breaker reset", run_id) ``` Usage — wrap tool calls in an agent loop: ```python detector = InfiniteLoopDetector(max_repeats=3, window_size=5) async def agent_step(user_input: str, tools: dict[str, Callable]) -> str: """Single agent iteration with circuit breaker protection.""" llm_response = await call_llm( [{"role": "user", "content": user_input}], model="gpt-4o", ) for action in llm_response.get("actions", []): tool_name = action["tool"] arguments = action["arguments"] if tool_name not in tools: raise ValueError(f"Unknown tool: {tool_name} — possible hallucination") try: output = await tools[tool_name](**arguments) detector.record_call( tool_name=tool_name, arguments=arguments, output_hash=hash(str(output)), success=True, ) except CircuitBreakerError: return f"Circuit breaker triggered. Falling back to safe mode for tool '{tool_name}'." except Exception as exc: detector.record_call( tool_name=tool_name, arguments=arguments, output_hash="", success=False, ) raise return llm_response.get("response", "") ``` --- ### Pattern 3: Context Window Exhaustion Prevention Monitors token usage per step and activates a sliding-window summarization fallback when approaching context limits. Prevents silent data loss from truncation. ```python from __future__ import annotations import logging from dataclasses import dataclass, field from typing import Optional logger = logging.getLogger(__name__) @dataclass class TokenBudget: """Tracks token usage against a configurable context budget. Attributes: max_tokens: Maximum tokens allowed in the conversation window. warning_threshold_pct: Percentage at which to trigger proactive summarization (0.0–1.0). emergency_threshold_pct: Percentage at which truncation becomes mandatory (0.0–1.0). """ max_tokens: int = 128_000 warning_threshold_pct: float = 0.75 emergency_threshold_pct: float = 0.90 current_usage: int = field(default=0, init=False) budget_exhausted: bool = field(default=False, init=False) @property def warning_threshold(self) -> int: return int(self.max_tokens * self.warning_threshold_pct) @property def emergency_threshold(self) -> int: return int(self.max_tokens * self.emergency_threshold_pct) def usage_percent(self) -> float: """Return current usage as a fraction of the budget.""" if self.budget_exhausted: return 1.0 return self.current_usage / self.max_tokens def add_usage(self, tokens: int) -> None: """Add consumed tokens and check thresholds. Args: tokens: Number of tokens consumed by the latest step. Raises: BudgetExhaustionError: When emergency threshold is exceeded. """ self.current_usage += tokens pct = self.usage_percent() if pct >= 1.0 and not self.budget_exhausted: self.budget_exhausted = True run_id = get_run_id() logger.error( "[run=%s] CONTEXT BUDGET EXHAUSTED: %d/%d tokens", run_id, self.current_usage, self.max_tokens, ) raise BudgetExhaustionError( f"Context window full: {self.current_usage}/{self.max_tokens} tokens. " "Summarize history or truncate messages immediately." ) if pct >= 0.90 and not self.budget_exhausted: logger.warning( "[run=%s] EMERGENCY threshold reached: %d/%d (%.0f%%)", get_run_id(), self.current_usage, self.max_tokens, pct * 100, ) elif pct >= 0.75 and not self.budget_exhausted: logger.warning( "[run=%s] WARNING threshold reached: %d/%d (%.0f%%)", get_run_id(), self.current_usage, self.max_tokens, pct * 100, ) def reset(self) -> None: """Reset counters after a summarization cycle.""" old_usage = self.current_usage self.current_usage = 0 self.budget_exhausted = False logger.info( "[run=%s] Token budget reset (freed ~%d tokens from summarization)",
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기