Files
liqiang b119135836
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
2026-08-20 13:12:50 +00:00

363 lines
12 KiB
Python

"""
Trajectory Summarizer for Learning from Experience
This module summarizes successful task trajectories into reusable experiences.
"""
import json
import logging
import re
from typing import Dict, Any, List, Optional
from AWorld.aworld.models.llm import get_llm_model
from AWorld.aworld.config.conf import AgentConfig
logger = logging.getLogger(__name__)
class TrajectorySummarizer:
"""
Summarizes task execution trajectories into natural language experiences.
"""
def __init__(
self,
llm_config: Optional[AgentConfig] = None,
model_name: str = "gpt-5.6-luna",
temperature: float = 0.3
):
"""
Initialize the trajectory summarizer.
Args:
llm_config: LLM configuration
model_name: Model to use for summarization
temperature: Temperature for generation
"""
self.llm_config = llm_config
self.model_name = model_name
self.temperature = temperature
# Initialize LLM for summarization
if llm_config:
self.llm = get_llm_model(
provider=llm_config.llm_provider,
model_name=model_name,
api_key=llm_config.llm_api_key,
base_url=llm_config.llm_base_url
)
else:
self.llm = None
async def summarize(
self,
question: str,
response: Any,
trajectory: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""
Summarize a successful trajectory into reusable experience.
Args:
question: The original question
response: The successful response
trajectory: The execution trajectory
Returns:
Summarized experience dictionary
"""
if not self.llm:
# Fallback to rule-based summarization
return self._rule_based_summary(question, response, trajectory)
try:
# Prepare trajectory for LLM
trajectory_text = self._format_trajectory(trajectory)
# Create summarization prompt
prompt = self._create_summary_prompt(question, response.answer, trajectory_text)
# Get summary from LLM
summary_response = await self.llm.acompletion(
messages=[
{"role": "system", "content": "You are an expert at analyzing task execution trajectories and extracting key insights for future problem-solving."},
{"role": "user", "content": prompt}
],
temperature=self.temperature
)
# Parse the response
summary = self._parse_llm_summary(summary_response)
# Extract tools used from trajectory
tools_used = self._extract_tools_from_trajectory(trajectory)
summary['tools_used'] = tools_used
return summary
except Exception as e:
logger.error(f"LLM summarization failed: {e}")
# Fallback to rule-based
return self._rule_based_summary(question, response, trajectory)
def _create_summary_prompt(self, question: str, answer: str, trajectory_text: str) -> str:
"""
Create a prompt for LLM summarization.
Args:
question: The original question
answer: The final answer
trajectory_text: Formatted trajectory
Returns:
Prompt string
"""
prompt = f"""Analyze this successful task execution and extract key insights for solving similar problems in the future.
Question: {question}
Final Answer: {answer}
Execution Trajectory:
{trajectory_text}
Please provide a concise summary with the following structure:
1. APPROACH: Describe the high-level approach taken to solve this problem (2-3 sentences)
2. KEY INSIGHTS: What are the critical insights or patterns that made this solution successful? (2-3 bullet points)
3. GENERAL STRATEGY: How could this approach be generalized to similar problems? (1-2 sentences)
Format your response as JSON with keys: "summary", "approach", "key_insights" (list), "general_strategy"
"""
return prompt
def _parse_llm_summary(self, response: Any) -> Dict[str, Any]:
"""
Parse LLM response into structured summary.
Args:
response: LLM response object
Returns:
Parsed summary dictionary
"""
try:
# Extract JSON from response
content = response.content if hasattr(response, 'content') else str(response)
# Try to find JSON in the response
json_match = re.search(r'\{.*\}', content, re.DOTALL)
if json_match:
summary_json = json.loads(json_match.group())
return {
'summary': summary_json.get('summary', ''),
'approach': summary_json.get('approach', ''),
'key_insights': summary_json.get('key_insights', []),
'general_strategy': summary_json.get('general_strategy', '')
}
except Exception as e:
logger.error(f"Failed to parse LLM summary: {e}")
# Fallback: treat entire response as summary
return {
'summary': content,
'approach': 'See summary for details',
'key_insights': [],
'general_strategy': ''
}
def _rule_based_summary(
self,
question: str,
response: Any,
trajectory: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""
Create a rule-based summary when LLM is not available.
Args:
question: The original question
response: The successful response
trajectory: The execution trajectory
Returns:
Summary dictionary
"""
# Extract tools used
tools_used = self._extract_tools_from_trajectory(trajectory)
# Analyze trajectory patterns
action_types = self._analyze_action_types(trajectory)
# Create basic summary
summary = {
'summary': f"Successfully answered question using {len(tools_used)} tools with {len(trajectory)} steps.",
'approach': self._infer_approach(action_types, tools_used),
'key_insights': self._extract_key_patterns(trajectory),
'general_strategy': f"Use combination of {', '.join(tools_used[:3])} for similar tasks",
'tools_used': tools_used
}
return summary
def _format_trajectory(self, trajectory: List[Dict[str, Any]]) -> str:
"""
Format trajectory for readability.
Args:
trajectory: Raw trajectory data
Returns:
Formatted trajectory string
"""
formatted_steps = []
for i, step in enumerate(trajectory, 1):
action = step.get('action', {})
# Extract key information
tool_name = action.get('tool_name') or 'unknown'
action_name = action.get('action_name', '')
params = action.get('params', {})
# Format step
step_text = f"Step {i}: {tool_name}"
if action_name:
step_text += f".{action_name}"
if params:
# Simplify params for readability
param_str = ', '.join([f"{k}={self._truncate_value(v)}" for k, v in params.items()])
step_text += f"({param_str})"
formatted_steps.append(step_text)
return '\n'.join(formatted_steps)
def _truncate_value(self, value: Any, max_length: int = 50) -> str:
"""
Truncate long values for display.
Args:
value: Value to truncate
max_length: Maximum length
Returns:
Truncated string representation
"""
str_value = str(value)
if len(str_value) > max_length:
return str_value[:max_length] + "..."
return str_value
def _extract_tools_from_trajectory(self, trajectory: List[Dict[str, Any]]) -> List[str]:
"""
Extract unique tools used from trajectory.
Args:
trajectory: Execution trajectory
Returns:
List of unique tool names
"""
tools = set()
for step in trajectory:
action = step.get('action', {})
tool_name = action.get('tool_name')
if tool_name:
# Clean tool name
base_tool = tool_name.split('__')[0] if '__' in tool_name else tool_name
tools.add(base_tool)
return sorted(list(tools))
def _analyze_action_types(self, trajectory: List[Dict[str, Any]]) -> Dict[str, int]:
"""
Analyze types of actions in trajectory.
Args:
trajectory: Execution trajectory
Returns:
Count of each action type
"""
action_types = {}
for step in trajectory:
action = step.get('action', {})
tool_name = action.get('tool_name') or 'unknown'
# Categorize action
if 'search' in tool_name.lower():
category = 'search'
elif 'browser' in tool_name.lower() or 'web' in tool_name.lower():
category = 'web_interaction'
elif 'file' in tool_name.lower() or 'read' in tool_name.lower():
category = 'file_operation'
elif 'calculate' in tool_name.lower() or 'compute' in tool_name.lower():
category = 'computation'
else:
category = 'other'
action_types[category] = action_types.get(category, 0) + 1
return action_types
def _infer_approach(self, action_types: Dict[str, int], tools_used: List[str]) -> str:
"""
Infer the approach based on action types.
Args:
action_types: Count of action types
tools_used: List of tools used
Returns:
Inferred approach description
"""
# Determine dominant strategy
if action_types.get('search', 0) > 2:
approach = "Information gathering through multiple searches"
elif action_types.get('web_interaction', 0) > 3:
approach = "Web-based research and navigation"
elif action_types.get('file_operation', 0) > 1:
approach = "File analysis and processing"
elif action_types.get('computation', 0) > 0:
approach = "Computational problem solving"
else:
approach = "Multi-step problem decomposition"
# Add tool specifics
if tools_used:
approach += f" using {', '.join(tools_used[:2])}"
return approach
def _extract_key_patterns(self, trajectory: List[Dict[str, Any]]) -> List[str]:
"""
Extract key patterns from trajectory.
Args:
trajectory: Execution trajectory
Returns:
List of key patterns/insights
"""
patterns = []
# Check for search refinement pattern
search_count = sum(1 for step in trajectory
if 'search' in str(step.get('action', {})).lower())
if search_count > 1:
patterns.append("Multiple searches refined the query")
# Check for verification pattern
if len(trajectory) > 5:
patterns.append("Thorough verification of results")
# Check for tool combination
tools = self._extract_tools_from_trajectory(trajectory)
if len(tools) > 2:
patterns.append(f"Combined {len(tools)} different tools effectively")
# Limit to 3 patterns
return patterns[:3] if patterns else ["Direct problem solving approach"]