vgtc-api / src /hermes /reasoning /reflexion.py
vora-sonnet's picture
Upload folder using huggingface_hub
0d3f7cc verified
Raw
History Blame Contribute Delete
8.54 kB
"""Reflexion reasoning pattern implementation.
Reflexion: Act -> Evaluate -> Self-Reflect -> Repeat
- Actor generates actions based on task context
- Evaluator checks correctness of the result
- Self-Reflection analyzes what went wrong and how to improve
- Reflections persist across attempts for continuous improvement
"""
from __future__ import annotations
import json
import logging
from typing import Any
from hermes.core.types import AgentStrategy
logger = logging.getLogger(__name__)
class ReflexionReasoner:
"""Implements the Reflexion pattern (Act -> Evaluate -> Reflect -> Repeat)."""
def __init__(self, max_attempts: int = 3) -> None:
self.strategy = AgentStrategy.REFLEXION
self.max_attempts = max_attempts
def create_actor_prompt(
self, task: str, tools: list[dict[str, Any]], reflections: list[str] | None = None
) -> str:
"""Create prompt for the actor to generate an action."""
tool_descriptions = "\n".join(
f"- {t['name']}: {t['description']}" for t in tools
)
reflections_section = ""
if reflections:
reflections_section = "\nReflections from previous attempts:\n"
for i, r in enumerate(reflections, 1):
reflections_section += f" {i}. {r}\n"
return f"""You are an AI agent that uses the Reflexion pattern to solve tasks through iterative refinement.
Task: {task}
Available tools:
{tool_descriptions}
{reflections_section}
Generate the next action to solve this task. Use the following format:
Thought: [your reasoning about what to do]
Action: [tool_name with arguments as JSON]
Expected: [what you expect the result to be]
If you have enough information to provide a final answer, use:
Thought: I now have enough information.
Final Answer: [your complete answer]
Important:
- Learn from past reflections and avoid repeating mistakes
- Be precise in your tool arguments
- Verify your assumptions"""
def create_evaluator_prompt(self, task: str, result: str) -> str:
"""Create prompt for the evaluator to check result correctness."""
return f"""Evaluate whether the following result correctly addresses the task.
Task: {task}
Result to evaluate:
{result}
Determine if the result is correct and complete.
Format:
Status: [correct / incorrect / partial]
Score: [0-100]
Issues:
- [issue 1]
Missing:
- [missing 1]"""
def create_reflection_prompt(
self, task: str, action: str, result: str, evaluation: str
) -> str:
"""Create prompt for self-reflection on what went wrong."""
return f"""Analyze what happened and generate a reflection to improve future attempts.
Task: {task}
Action taken:
{action}
Result obtained:
{result}
Evaluation:
{evaluation}
Generate a concise self-reflection that identifies:
1. What went wrong (if anything)
2. What could be improved
3. What to do differently next time
Format:
Reflection: [concise analysis of what happened]
Errors: [specific mistakes made]
Improvements: [specific changes for next attempt]
Key Lesson: [single most important lesson]"""
def parse_actor_response(self, response: str) -> dict[str, Any]:
"""Parse actor response into components."""
result: dict[str, Any] = {"thought": "", "action": None, "final_answer": None}
lines = response.strip().split("\n")
current_key = None
current_value: list[str] = []
for line in lines:
stripped = line.strip()
if stripped.startswith("Thought:"):
if current_key and current_value:
result[current_key] = "\n".join(current_value).strip()
current_key = "thought"
current_value = [stripped[len("Thought:"):].strip()]
elif stripped.startswith("Action:"):
if current_key and current_value:
result[current_key] = "\n".join(current_value).strip()
current_key = "action"
current_value = [stripped[len("Action:"):].strip()]
elif stripped.startswith("Expected:"):
if current_key and current_value:
result[current_key] = "\n".join(current_value).strip()
current_key = "expected"
current_value = [stripped[len("Expected:"):].strip()]
elif stripped.startswith("Final Answer:"):
if current_key and current_value:
result[current_key] = "\n".join(current_value).strip()
current_key = "final_answer"
current_value = [stripped[len("Final Answer:"):].strip()]
elif current_key:
current_value.append(stripped)
if current_key and current_value:
result[current_key] = "\n".join(current_value).strip()
if result["action"]:
try:
action_str = result["action"]
if isinstance(action_str, str) and "(" in action_str and action_str.endswith(")"):
tool_name = action_str[:action_str.index("(")]
args_str = action_str[action_str.index("(") + 1:-1]
try:
args = json.loads(args_str) if args_str.strip() else {}
except json.JSONDecodeError:
args = {"input": args_str}
result["action"] = {"tool": tool_name, "arguments": args}
except Exception:
pass
return result
def parse_evaluator_response(self, response: str) -> dict[str, Any]:
"""Parse evaluator response."""
result: dict[str, Any] = {"status": "incorrect", "score": 0, "issues": [], "missing": []}
lines = response.strip().split("\n")
current_section = None
current_value: list[str] = []
for line in lines:
stripped = line.strip()
if stripped.startswith("Status:"):
result["status"] = stripped[len("Status:"):].strip().lower()
elif stripped.startswith("Score:"):
try:
result["score"] = int(stripped.split(":")[1].strip())
except ValueError:
result["score"] = 0
elif stripped.startswith("Issues:"):
current_section = "issues"
current_value = []
elif stripped.startswith("Missing:"):
if current_section:
result[current_section] = current_value
current_section = "missing"
current_value = []
elif stripped.startswith("- ") and current_section:
current_value.append(stripped[2:].strip())
if current_section:
result[current_section] = current_value
return result
def parse_reflection_response(self, response: str) -> dict[str, Any]:
"""Parse reflection response."""
result: dict[str, Any] = {
"reflection": "", "errors": [], "improvements": [], "key_lesson": "",
}
lines = response.strip().split("\n")
current_section = None
current_value: list[str] = []
for line in lines:
stripped = line.strip()
if stripped.startswith("Reflection:"):
result["reflection"] = stripped[len("Reflection:"):].strip()
elif stripped.startswith("Errors:"):
current_section = "errors"
current_value = []
elif stripped.startswith("Improvements:"):
if current_section:
result[current_section] = current_value
current_section = "improvements"
current_value = []
elif stripped.startswith("Key Lesson:"):
if current_section:
result[current_section] = current_value
result["key_lesson"] = stripped[len("Key Lesson:"):].strip()
current_section = None
current_value = []
elif stripped.startswith("- ") and current_section:
current_value.append(stripped[2:].strip())
if current_section:
result[current_section] = current_value
return result
def should_continue(
self, status: str, score: int, attempt: int
) -> bool:
"""Determine if another attempt should be made."""
if status == "correct" and score >= 80:
return False
return attempt < self.max_attempts