ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
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
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
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
"""
|
||||
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"]
|
||||
Reference in New Issue
Block a user