logic-engine / examples /ace /rr_demo.py
ghostdrive1's picture
Upload folder using huggingface_hub
116524e verified
Raw
History Blame Contribute Delete
9.92 kB
#!/usr/bin/env python3
"""Demo of the Recursive Reflector (RR) pipeline with a real LLM.
Shows the RR analyzing agent traces, iterating in its Python REPL sandbox,
and producing structured learnings. Requires an API key for LiteLLM.
Usage:
# Default model (Bedrock Claude Haiku):
uv run python examples/ace/rr_demo.py
# Custom model:
ACE_MODEL=bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0 uv run python examples/ace/rr_demo.py
"""
import json
import logging
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
# Ensure project root is importable
_root = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(_root))
load_dotenv(_root / ".env")
from ace.steps.rr_step import RRConfig, RRStep, TraceSandbox
from ace.core.context import ACEStepContext, SkillbookView
from ace.core.skillbook import Skillbook
MODEL = os.getenv("ACE_MODEL", "bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0")
# Show what the RR is doing at each iteration
logging.basicConfig(
level=logging.INFO,
format=" %(name)s | %(message)s",
)
# Quiet the noisy libraries
for name in ("LiteLLM", "litellm", "httpx", "httpcore"):
logging.getLogger(name).setLevel(logging.WARNING)
def section(name: str) -> None:
print(f"\n{'=' * 60}\n {name}\n{'=' * 60}\n")
def print_result(result):
"""Print a ReflectorOutput nicely."""
print(f"\n --- Result ---")
print(f" Reasoning: {result.reasoning[:300]}")
print(f" Key insight: {result.key_insight}")
if result.error_identification:
print(f" Error: {result.error_identification}")
if result.root_cause_analysis:
print(f" Root cause: {result.root_cause_analysis}")
if result.correct_approach:
print(f" Correct approach: {result.correct_approach}")
raw = result.raw or {}
if "rr_trace" in raw:
rt = raw["rr_trace"]
print(f"\n RR trace: depth={rt.get('depth')}, "
f"iterations={rt.get('total_iterations')}, "
f"compactions={rt.get('compactions')}, "
f"timed_out={rt.get('timed_out')}")
if "usage" in raw:
u = raw["usage"]
print(f" Usage: {u.get('input_tokens')} in, "
f"{u.get('output_tokens')} out, "
f"{u.get('total_tokens')} total, "
f"{u.get('requests')} requests")
# ---------------------------------------------------------------------------
# Demo 1: RRStep — agent got the wrong answer (simple)
# ---------------------------------------------------------------------------
def demo_wrong_answer():
"""RR analyzes a trace where the agent answered incorrectly."""
section("Demo 1: RRStep — wrong answer")
rr = RRStep(
MODEL,
config=RRConfig(max_requests=15, max_depth=0),
)
ctx = ACEStepContext(
trace={
"question": "What is the largest planet in our solar system by mass?",
"ground_truth": "Jupiter",
"feedback": "Incorrect. The correct answer is Jupiter, not Saturn.",
"steps": [
{
"role": "agent",
"reasoning": (
"The user is asking about the largest planet. "
"Saturn has those huge rings and is very large. "
"I'll go with Saturn."
),
"answer": "Saturn",
"skill_ids": [],
}
],
},
skillbook=SkillbookView(Skillbook()),
)
result_ctx = rr(ctx)
print_result(result_ctx.reflections[0])
# ---------------------------------------------------------------------------
# Demo 2: RRStep — multi-step tool-use failure
# ---------------------------------------------------------------------------
def demo_tool_failure():
"""RR analyzes a trace with tool-use errors."""
section("Demo 2: RRStep — tool-use failure trace")
rr = RRStep(
MODEL,
config=RRConfig(max_requests=15, max_depth=0),
)
ctx = ACEStepContext(
trace={
"question": "What's the current weather in Tokyo?",
"ground_truth": '{"temp_c": 22, "condition": "partly cloudy", "humidity": 65}',
"feedback": (
"Failed. Agent called the weather API with 'Tokio' (misspelled) "
"and got a 404 error, then guessed instead of retrying."
),
"steps": [
{
"role": "agent",
"reasoning": (
"I need to call the weather API for Tokyo. "
"Let me use get_weather(city='Tokio')."
),
"answer": "Error: 404 - City 'Tokio' not found",
"skill_ids": [],
},
{
"role": "agent",
"reasoning": (
"The API returned an error. I'll estimate based on "
"general knowledge — Tokyo is warm in summer."
),
"answer": "It's probably around 28C and sunny in Tokyo.",
"skill_ids": [],
},
],
},
skillbook=SkillbookView(Skillbook()),
)
result_ctx = rr(ctx)
print_result(result_ctx.reflections[0])
# ---------------------------------------------------------------------------
# Demo 3: Real benchmark trace (if available)
# ---------------------------------------------------------------------------
def demo_real_trace():
"""RR analyzes a real benchmark trace."""
section("Demo 3: Real benchmark trace")
traces_path = _root / "ace-eval" / "results" / "benchmarks" / "bench_20260314_154608" / "benchmark" / "traces.json"
if not traces_path.exists():
print(" Benchmark traces not found, skipping.")
return
data = json.loads(traces_path.read_text())
# Find a failed trace (reward=0)
trace_dict = None
for key, val in data.items():
for trial in val.get("trials", []):
if trial.get("reward", 1.0) == 0.0 and trial.get("trace"):
trace_dict = trial["trace"]
print(f" Using trace: task {key}, question: {trace_dict.get('question', '')[:100]}...")
break
if trace_dict:
break
if not trace_dict:
print(" No failed traces found, skipping.")
return
rr = RRStep(
MODEL,
config=RRConfig(max_requests=20, max_depth=0),
)
ctx = ACEStepContext(
trace=trace_dict,
skillbook=SkillbookView(Skillbook()),
)
result_ctx = rr(ctx)
print_result(result_ctx.reflections[0])
# ---------------------------------------------------------------------------
# Demo 4: Batch traces with recursion
# ---------------------------------------------------------------------------
def demo_batch_recursion():
"""RR analyzes multiple traces using recurse tool."""
section("Demo 4: Batch traces with recursion (depth=1)")
traces_path = _root / "ace-eval" / "results" / "benchmarks" / "bench_20260314_154608" / "benchmark" / "traces.json"
if not traces_path.exists():
print(" Benchmark traces not found, skipping.")
return
data = json.loads(traces_path.read_text())
# Collect first 3 failed traces as batch items
batch_items = []
for key, val in data.items():
for trial in val.get("trials", []):
if trial.get("reward", 1.0) == 0.0 and trial.get("trace"):
t = trial["trace"]
batch_items.append({
"task_id": f"task_{key}",
"question": t.get("question", ""),
"feedback": t.get("feedback", ""),
"trace": t,
})
if len(batch_items) >= 3:
break
if len(batch_items) >= 3:
break
if len(batch_items) < 2:
print(f" Only {len(batch_items)} failed traces found, need at least 2. Skipping.")
return
print(f" Batch: {len(batch_items)} failed traces")
for bi in batch_items:
print(f" - {bi['task_id']}: {bi['question'][:80]}...")
rr = RRStep(
MODEL,
config=RRConfig(
max_requests=30,
max_depth=1, # allow one level of recursion
),
)
ctx = ACEStepContext(
trace={
"question": "Analyze these failed agent traces and extract common patterns",
"batch_items": batch_items,
"item_ids": [bi["task_id"] for bi in batch_items],
},
skillbook=SkillbookView(Skillbook()),
)
result_ctx = rr(ctx)
for i, ref in enumerate(result_ctx.reflections):
print(f"\n --- Reflection {i} ---")
print_result(ref)
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="RR Demo")
parser.add_argument("--demo", type=int, default=0,
help="Run specific demo (1-4), 0=all")
args = parser.parse_args()
print(f"Model: {MODEL}")
demos = {
1: demo_wrong_answer,
2: demo_tool_failure,
3: demo_real_trace,
4: demo_batch_recursion,
}
if args.demo:
demos[args.demo]()
else:
for d in demos.values():
d()
section("Done")