parent
0e0a46019d
commit
156f98a2c2
@ -0,0 +1,58 @@
|
|||||||
|
# Swarms Cookbook Examples Index
|
||||||
|
|
||||||
|
This index provides a categorized list of examples and tutorials for using the Swarms Framework across different industries. Each example demonstrates practical applications and implementations using the framework.
|
||||||
|
|
||||||
|
## Finance & Trading
|
||||||
|
|
||||||
|
| Name | Description | Link |
|
||||||
|
|------|-------------|------|
|
||||||
|
| Tickr-Agent | Financial analysis agent for stock market data using multithreaded processing and AI integration | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/finance/multi_agent/Swarms_Cookbook_Tickr_Agent.ipynb) |
|
||||||
|
| CryptoAgent | Real-time cryptocurrency data analysis and insights using CoinGecko integration | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/finance/multi_agent/Swarms_Cookbook_CryptoAgent.ipynb) |
|
||||||
|
| 10-K Analysis (Custom) | Detailed analysis of SEC 10-K reports using specialized agents | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/finance/multi_agent/swarms_finance_10k_analysis_custom.ipynb) |
|
||||||
|
| 10-K Analysis (AgentRearrange) | Mixed sequential and parallel analysis of 10-K reports | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/finance/multi_agent/swarms_finance_10k_analysis_agentrearrange.ipynb) |
|
||||||
|
|
||||||
|
## Healthcare & Medical
|
||||||
|
|
||||||
|
| Name | Description | Link |
|
||||||
|
|------|-------------|------|
|
||||||
|
| MedInsight Pro | Medical research summarization and analysis using AI-driven agents | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/medical/physical_therapy/Swarms_Cookbook_MedInsight_Pro.ipynb) |
|
||||||
|
| Athletics Diagnosis | Diagnosis and treatment system for extreme athletics using AgentRearrange | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/medical/physical_therapy/swarms_diagnosis_treatment_extreme_athletics.ipynb) |
|
||||||
|
|
||||||
|
## Marketing & Content
|
||||||
|
|
||||||
|
| Name | Description | Link |
|
||||||
|
|------|-------------|------|
|
||||||
|
| NewsAgent | Real-time news aggregation and summarization for business intelligence | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/marketing/news/Swarms_Cookbook_NewsAgent.ipynb) |
|
||||||
|
| Social Media Marketing | Spreadsheet-based content generation for multi-platform marketing | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/marketing/content_generation/swarms_spreadsheet_analysis_walkthrough.ipynb) |
|
||||||
|
|
||||||
|
## Accounting & Finance Operations
|
||||||
|
|
||||||
|
| Name | Description | Link |
|
||||||
|
|------|-------------|------|
|
||||||
|
| Accounting Agents | Multi-agent system for financial projections and risk assessment | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/enterprise/accounting/multi_agent/accounting_agents_for_moa.ipynb) |
|
||||||
|
|
||||||
|
## Workshops & Tutorials
|
||||||
|
|
||||||
|
| Name | Description | Link |
|
||||||
|
|------|-------------|------|
|
||||||
|
| GPTuesday Event | Example of creating promotional content for tech events | [View Example](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/cookbook/workshops/sep_6_workshop/gptuesday_swarm.py) |
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
| Platform | Link | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| 📚 Documentation | [docs.swarms.world](https://docs.swarms.world) | Official documentation and guides |
|
||||||
|
| 📝 Blog | [Medium](https://medium.com/@kyeg) | Latest updates and technical articles |
|
||||||
|
| 💬 Discord | [Join Discord](https://discord.gg/jM3Z6M9uMq) | Live chat and community support |
|
||||||
|
| 🐦 Twitter | [@kyegomez](https://twitter.com/kyegomez) | Latest news and announcements |
|
||||||
|
| 👥 LinkedIn | [The Swarm Corporation](https://www.linkedin.com/company/the-swarm-corporation) | Professional network and updates |
|
||||||
|
| 📺 YouTube | [Swarms Channel](https://www.youtube.com/channel/UC9yXyitkbU_WSy7bd_41SqQ) | Tutorials and demos |
|
||||||
|
| 🎫 Events | [Sign up here](https://lu.ma/5p2jnc2v) | Join our community events |
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We welcome contributions! If you have an example or tutorial you'd like to add, please check our [contribution guidelines](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the [LICENSE](https://github.com/The-Swarm-Corporation/Cookbook/blob/main/LICENSE) file for details.
|
@ -0,0 +1,7 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class AgentRAGConfig(BaseModel):
|
||||||
|
"""
|
||||||
|
Configuration for the AgentRAG class.
|
||||||
|
"""
|
@ -0,0 +1,682 @@
|
|||||||
|
import time
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
from swarms.utils.litellm_tokenizer import count_tokens
|
||||||
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
|
||||||
|
|
||||||
|
class RAGConfig(BaseModel):
|
||||||
|
"""Configuration class for RAG operations"""
|
||||||
|
|
||||||
|
similarity_threshold: float = Field(
|
||||||
|
default=0.7,
|
||||||
|
ge=0.0,
|
||||||
|
le=1.0,
|
||||||
|
description="Similarity threshold for memory retrieval",
|
||||||
|
)
|
||||||
|
max_results: int = Field(
|
||||||
|
default=5,
|
||||||
|
gt=0,
|
||||||
|
description="Maximum number of results to return from memory",
|
||||||
|
)
|
||||||
|
context_window_tokens: int = Field(
|
||||||
|
default=2000,
|
||||||
|
gt=0,
|
||||||
|
description="Maximum number of tokens in the context window",
|
||||||
|
)
|
||||||
|
auto_save_to_memory: bool = Field(
|
||||||
|
default=True,
|
||||||
|
description="Whether to automatically save responses to memory",
|
||||||
|
)
|
||||||
|
save_every_n_loops: int = Field(
|
||||||
|
default=5, gt=0, description="Save to memory every N loops"
|
||||||
|
)
|
||||||
|
min_content_length: int = Field(
|
||||||
|
default=50,
|
||||||
|
gt=0,
|
||||||
|
description="Minimum content length to save to memory",
|
||||||
|
)
|
||||||
|
query_every_loop: bool = Field(
|
||||||
|
default=False,
|
||||||
|
description="Whether to query memory every loop",
|
||||||
|
)
|
||||||
|
enable_conversation_summaries: bool = Field(
|
||||||
|
default=True,
|
||||||
|
description="Whether to enable conversation summaries",
|
||||||
|
)
|
||||||
|
relevance_keywords: Optional[List[str]] = Field(
|
||||||
|
default=None, description="Keywords to check for relevance"
|
||||||
|
)
|
||||||
|
|
||||||
|
@field_validator("relevance_keywords", pre=True)
|
||||||
|
def set_default_keywords(cls, v):
|
||||||
|
if v is None:
|
||||||
|
return [
|
||||||
|
"important",
|
||||||
|
"key",
|
||||||
|
"critical",
|
||||||
|
"summary",
|
||||||
|
"conclusion",
|
||||||
|
]
|
||||||
|
return v
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
validate_assignment = True
|
||||||
|
json_schema_extra = {
|
||||||
|
"example": {
|
||||||
|
"similarity_threshold": 0.7,
|
||||||
|
"max_results": 5,
|
||||||
|
"context_window_tokens": 2000,
|
||||||
|
"auto_save_to_memory": True,
|
||||||
|
"save_every_n_loops": 5,
|
||||||
|
"min_content_length": 50,
|
||||||
|
"query_every_loop": False,
|
||||||
|
"enable_conversation_summaries": True,
|
||||||
|
"relevance_keywords": [
|
||||||
|
"important",
|
||||||
|
"key",
|
||||||
|
"critical",
|
||||||
|
"summary",
|
||||||
|
"conclusion",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AgentRAGHandler:
|
||||||
|
"""
|
||||||
|
Handles all RAG (Retrieval-Augmented Generation) operations for agents.
|
||||||
|
Provides memory querying, storage, and context management capabilities.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
long_term_memory: Optional[Any] = None,
|
||||||
|
config: Optional[RAGConfig] = None,
|
||||||
|
agent_name: str = "Unknown",
|
||||||
|
max_context_length: int = 158_000,
|
||||||
|
verbose: bool = False,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize the RAG handler.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
long_term_memory: The long-term memory store (must implement add() and query() methods)
|
||||||
|
config: RAG configuration settings
|
||||||
|
agent_name: Name of the agent using this handler
|
||||||
|
verbose: Enable verbose logging
|
||||||
|
"""
|
||||||
|
self.long_term_memory = long_term_memory
|
||||||
|
self.config = config or RAGConfig()
|
||||||
|
self.agent_name = agent_name
|
||||||
|
self.verbose = verbose
|
||||||
|
self.max_context_length = max_context_length
|
||||||
|
|
||||||
|
self._loop_counter = 0
|
||||||
|
self._conversation_history = []
|
||||||
|
self._important_memories = []
|
||||||
|
|
||||||
|
# Validate memory interface
|
||||||
|
if (
|
||||||
|
self.long_term_memory
|
||||||
|
and not self._validate_memory_interface()
|
||||||
|
):
|
||||||
|
logger.warning(
|
||||||
|
"Long-term memory doesn't implement required interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _validate_memory_interface(self) -> bool:
|
||||||
|
"""Validate that the memory object has required methods"""
|
||||||
|
required_methods = ["add", "query"]
|
||||||
|
for method in required_methods:
|
||||||
|
if not hasattr(self.long_term_memory, method):
|
||||||
|
logger.error(
|
||||||
|
f"Memory object missing required method: {method}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_enabled(self) -> bool:
|
||||||
|
"""Check if RAG is enabled (has valid memory store)"""
|
||||||
|
return self.long_term_memory is not None
|
||||||
|
|
||||||
|
def query_memory(
|
||||||
|
self,
|
||||||
|
query: str,
|
||||||
|
context_type: str = "general",
|
||||||
|
loop_count: Optional[int] = None,
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Query the long-term memory and return formatted context.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: The query string to search for
|
||||||
|
context_type: Type of context being queried (for logging)
|
||||||
|
loop_count: Current loop number (for logging)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted string of relevant memories, empty string if no results
|
||||||
|
"""
|
||||||
|
if not self.is_enabled():
|
||||||
|
return ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.verbose:
|
||||||
|
logger.info(
|
||||||
|
f"🔍 [{self.agent_name}] Querying RAG for {context_type}: {query[:100]}..."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Query the memory store
|
||||||
|
results = self.long_term_memory.query(
|
||||||
|
query=query,
|
||||||
|
top_k=self.config.max_results,
|
||||||
|
similarity_threshold=self.config.similarity_threshold,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
if self.verbose:
|
||||||
|
logger.info(
|
||||||
|
f"No relevant memories found for query: {context_type}"
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Format results for context
|
||||||
|
formatted_context = self._format_memory_results(
|
||||||
|
results, context_type, loop_count
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure context fits within token limits
|
||||||
|
if (
|
||||||
|
count_tokens(formatted_context)
|
||||||
|
> self.config.context_window_tokens
|
||||||
|
):
|
||||||
|
formatted_context = self._truncate_context(
|
||||||
|
formatted_context
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
logger.info(
|
||||||
|
f"✅ Retrieved {len(results)} relevant memories for {context_type}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return formatted_context
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error querying long-term memory: {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def _format_memory_results(
|
||||||
|
self,
|
||||||
|
results: List[Any],
|
||||||
|
context_type: str,
|
||||||
|
loop_count: Optional[int] = None,
|
||||||
|
) -> str:
|
||||||
|
"""Format memory results into a structured context string"""
|
||||||
|
if not results:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
loop_info = f" (Loop {loop_count})" if loop_count else ""
|
||||||
|
header = (
|
||||||
|
f"📚 Relevant Knowledge - {context_type.title()}{loop_info}:\n"
|
||||||
|
+ "=" * 50
|
||||||
|
+ "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
formatted_sections = [header]
|
||||||
|
|
||||||
|
for i, result in enumerate(results, 1):
|
||||||
|
content, score, source, metadata = (
|
||||||
|
self._extract_result_fields(result)
|
||||||
|
)
|
||||||
|
|
||||||
|
section = f"""
|
||||||
|
[Memory {i}] Relevance: {score} | Source: {source}
|
||||||
|
{'-' * 40}
|
||||||
|
{content}
|
||||||
|
{'-' * 40}
|
||||||
|
"""
|
||||||
|
formatted_sections.append(section)
|
||||||
|
|
||||||
|
formatted_sections.append(f"\n{'='*50}\n")
|
||||||
|
return "\n".join(formatted_sections)
|
||||||
|
|
||||||
|
def _extract_result_fields(self, result: Any) -> tuple:
|
||||||
|
"""Extract content, score, source, and metadata from a result object"""
|
||||||
|
if isinstance(result, dict):
|
||||||
|
content = result.get(
|
||||||
|
"content", result.get("text", str(result))
|
||||||
|
)
|
||||||
|
score = result.get(
|
||||||
|
"score", result.get("similarity", "N/A")
|
||||||
|
)
|
||||||
|
metadata = result.get("metadata", {})
|
||||||
|
source = metadata.get(
|
||||||
|
"source", result.get("source", "Unknown")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
content = str(result)
|
||||||
|
score = "N/A"
|
||||||
|
source = "Unknown"
|
||||||
|
metadata = {}
|
||||||
|
|
||||||
|
return content, score, source, metadata
|
||||||
|
|
||||||
|
def _truncate_context(self, content: str) -> str:
|
||||||
|
"""Truncate content to fit within token limits using smart truncation"""
|
||||||
|
max_chars = (
|
||||||
|
self.config.context_window_tokens * 3
|
||||||
|
) # Rough token-to-char ratio
|
||||||
|
|
||||||
|
if len(content) <= max_chars:
|
||||||
|
return content
|
||||||
|
|
||||||
|
# Try to cut at section boundaries first
|
||||||
|
sections = content.split("=" * 50)
|
||||||
|
if len(sections) > 2: # Header + sections + footer
|
||||||
|
truncated_sections = [sections[0]] # Keep header
|
||||||
|
current_length = len(sections[0])
|
||||||
|
|
||||||
|
for section in sections[1:-1]: # Skip footer
|
||||||
|
if current_length + len(section) > max_chars * 0.9:
|
||||||
|
break
|
||||||
|
truncated_sections.append(section)
|
||||||
|
current_length += len(section)
|
||||||
|
|
||||||
|
truncated_sections.append(
|
||||||
|
f"\n[... {len(sections) - len(truncated_sections)} more memories truncated for length ...]\n"
|
||||||
|
)
|
||||||
|
truncated_sections.append(sections[-1]) # Keep footer
|
||||||
|
return "=" * (50).join(truncated_sections)
|
||||||
|
|
||||||
|
# Fallback: simple truncation at sentence boundary
|
||||||
|
truncated = content[:max_chars]
|
||||||
|
last_period = truncated.rfind(".")
|
||||||
|
if last_period > max_chars * 0.8:
|
||||||
|
truncated = truncated[: last_period + 1]
|
||||||
|
|
||||||
|
return (
|
||||||
|
truncated + "\n\n[... content truncated for length ...]"
|
||||||
|
)
|
||||||
|
|
||||||
|
def should_save_response(
|
||||||
|
self,
|
||||||
|
response: str,
|
||||||
|
loop_count: int,
|
||||||
|
has_tool_usage: bool = False,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Determine if a response should be saved to long-term memory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
response: The response text to evaluate
|
||||||
|
loop_count: Current loop number
|
||||||
|
has_tool_usage: Whether tools were used in this response
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Boolean indicating whether to save the response
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
not self.is_enabled()
|
||||||
|
or not self.config.auto_save_to_memory
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Content length check
|
||||||
|
if len(response.strip()) < self.config.min_content_length:
|
||||||
|
return False
|
||||||
|
|
||||||
|
save_conditions = [
|
||||||
|
# Substantial content
|
||||||
|
len(response) > 200,
|
||||||
|
# Contains important keywords
|
||||||
|
any(
|
||||||
|
keyword in response.lower()
|
||||||
|
for keyword in self.config.relevance_keywords
|
||||||
|
),
|
||||||
|
# Periodic saves
|
||||||
|
loop_count % self.config.save_every_n_loops == 0,
|
||||||
|
# Tool usage indicates potentially important information
|
||||||
|
has_tool_usage,
|
||||||
|
# Complex responses (multiple sentences)
|
||||||
|
response.count(".") >= 3,
|
||||||
|
# Contains structured data or lists
|
||||||
|
any(
|
||||||
|
marker in response
|
||||||
|
for marker in ["- ", "1. ", "2. ", "* ", "```"]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
return any(save_conditions)
|
||||||
|
|
||||||
|
def save_to_memory(
|
||||||
|
self,
|
||||||
|
content: str,
|
||||||
|
metadata: Optional[Dict] = None,
|
||||||
|
content_type: str = "response",
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Save content to long-term memory with metadata.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: The content to save
|
||||||
|
metadata: Additional metadata to store
|
||||||
|
content_type: Type of content being saved
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Boolean indicating success
|
||||||
|
"""
|
||||||
|
if not self.is_enabled():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if (
|
||||||
|
not content
|
||||||
|
or len(content.strip()) < self.config.min_content_length
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create default metadata
|
||||||
|
default_metadata = {
|
||||||
|
"timestamp": time.time(),
|
||||||
|
"agent_name": self.agent_name,
|
||||||
|
"content_type": content_type,
|
||||||
|
"loop_count": self._loop_counter,
|
||||||
|
"saved_at": time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Merge with provided metadata
|
||||||
|
if metadata:
|
||||||
|
default_metadata.update(metadata)
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
logger.info(
|
||||||
|
f"💾 [{self.agent_name}] Saving to long-term memory: {content[:100]}..."
|
||||||
|
)
|
||||||
|
|
||||||
|
success = self.long_term_memory.add(
|
||||||
|
content, metadata=default_metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
if success and self.verbose:
|
||||||
|
logger.info(
|
||||||
|
f"✅ Successfully saved {content_type} to long-term memory"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Track important memories
|
||||||
|
if content_type in [
|
||||||
|
"final_response",
|
||||||
|
"conversation_summary",
|
||||||
|
]:
|
||||||
|
self._important_memories.append(
|
||||||
|
{
|
||||||
|
"content": content[:200],
|
||||||
|
"timestamp": time.time(),
|
||||||
|
"type": content_type,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error saving to long-term memory: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_conversation_summary(
|
||||||
|
self,
|
||||||
|
task: str,
|
||||||
|
final_response: str,
|
||||||
|
total_loops: int,
|
||||||
|
tools_used: List[str] = None,
|
||||||
|
) -> str:
|
||||||
|
"""Create a comprehensive summary of the conversation"""
|
||||||
|
tools_info = (
|
||||||
|
f"Tools Used: {', '.join(tools_used)}"
|
||||||
|
if tools_used
|
||||||
|
else "Tools Used: None"
|
||||||
|
)
|
||||||
|
|
||||||
|
summary = f"""
|
||||||
|
CONVERSATION SUMMARY
|
||||||
|
====================
|
||||||
|
Agent: {self.agent_name}
|
||||||
|
Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S')}
|
||||||
|
|
||||||
|
ORIGINAL TASK:
|
||||||
|
{task}
|
||||||
|
|
||||||
|
FINAL RESPONSE:
|
||||||
|
{final_response}
|
||||||
|
|
||||||
|
EXECUTION DETAILS:
|
||||||
|
- Total Reasoning Loops: {total_loops}
|
||||||
|
- {tools_info}
|
||||||
|
- Memory Queries Made: {len(self._conversation_history)}
|
||||||
|
|
||||||
|
KEY INSIGHTS:
|
||||||
|
{self._extract_key_insights(final_response)}
|
||||||
|
====================
|
||||||
|
"""
|
||||||
|
return summary
|
||||||
|
|
||||||
|
def _extract_key_insights(self, response: str) -> str:
|
||||||
|
"""Extract key insights from the response for summary"""
|
||||||
|
# Simple keyword-based extraction
|
||||||
|
insights = []
|
||||||
|
sentences = response.split(".")
|
||||||
|
|
||||||
|
for sentence in sentences:
|
||||||
|
if any(
|
||||||
|
keyword in sentence.lower()
|
||||||
|
for keyword in self.config.relevance_keywords[:5]
|
||||||
|
):
|
||||||
|
insights.append(sentence.strip())
|
||||||
|
|
||||||
|
if insights:
|
||||||
|
return "\n- " + "\n- ".join(
|
||||||
|
insights[:3]
|
||||||
|
) # Top 3 insights
|
||||||
|
return "No specific insights extracted"
|
||||||
|
|
||||||
|
def handle_loop_memory_operations(
|
||||||
|
self,
|
||||||
|
task: str,
|
||||||
|
response: str,
|
||||||
|
loop_count: int,
|
||||||
|
conversation_context: str = "",
|
||||||
|
has_tool_usage: bool = False,
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Handle all memory operations for a single loop iteration.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task: Original task
|
||||||
|
response: Current response
|
||||||
|
loop_count: Current loop number
|
||||||
|
conversation_context: Current conversation context
|
||||||
|
has_tool_usage: Whether tools were used
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Retrieved context string (empty if no relevant memories)
|
||||||
|
"""
|
||||||
|
self._loop_counter = loop_count
|
||||||
|
retrieved_context = ""
|
||||||
|
|
||||||
|
# 1. Query memory if enabled for this loop
|
||||||
|
if self.config.query_every_loop and loop_count > 1:
|
||||||
|
query_context = f"Task: {task}\nCurrent Context: {conversation_context[-500:]}"
|
||||||
|
retrieved_context = self.query_memory(
|
||||||
|
query_context,
|
||||||
|
context_type=f"loop_{loop_count}",
|
||||||
|
loop_count=loop_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Save response if criteria met
|
||||||
|
if self.should_save_response(
|
||||||
|
response, loop_count, has_tool_usage
|
||||||
|
):
|
||||||
|
self.save_to_memory(
|
||||||
|
content=response,
|
||||||
|
metadata={
|
||||||
|
"task_preview": task[:200],
|
||||||
|
"loop_count": loop_count,
|
||||||
|
"has_tool_usage": has_tool_usage,
|
||||||
|
},
|
||||||
|
content_type="loop_response",
|
||||||
|
)
|
||||||
|
|
||||||
|
return retrieved_context
|
||||||
|
|
||||||
|
def handle_initial_memory_query(self, task: str) -> str:
|
||||||
|
"""Handle the initial memory query before reasoning loops begin"""
|
||||||
|
if not self.is_enabled():
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return self.query_memory(task, context_type="initial_task")
|
||||||
|
|
||||||
|
def handle_final_memory_consolidation(
|
||||||
|
self,
|
||||||
|
task: str,
|
||||||
|
final_response: str,
|
||||||
|
total_loops: int,
|
||||||
|
tools_used: List[str] = None,
|
||||||
|
) -> bool:
|
||||||
|
"""Handle final memory consolidation after all loops complete"""
|
||||||
|
if (
|
||||||
|
not self.is_enabled()
|
||||||
|
or not self.config.enable_conversation_summaries
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create and save conversation summary
|
||||||
|
summary = self.create_conversation_summary(
|
||||||
|
task, final_response, total_loops, tools_used
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.save_to_memory(
|
||||||
|
content=summary,
|
||||||
|
metadata={
|
||||||
|
"task": task[:200],
|
||||||
|
"total_loops": total_loops,
|
||||||
|
"tools_used": tools_used or [],
|
||||||
|
},
|
||||||
|
content_type="conversation_summary",
|
||||||
|
)
|
||||||
|
|
||||||
|
def search_memories(
|
||||||
|
self,
|
||||||
|
query: str,
|
||||||
|
top_k: int = None,
|
||||||
|
similarity_threshold: float = None,
|
||||||
|
) -> List[Dict]:
|
||||||
|
"""
|
||||||
|
Search long-term memory and return raw results.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: Search query
|
||||||
|
top_k: Number of results to return (uses config default if None)
|
||||||
|
similarity_threshold: Similarity threshold (uses config default if None)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of memory results
|
||||||
|
"""
|
||||||
|
if not self.is_enabled():
|
||||||
|
return []
|
||||||
|
|
||||||
|
try:
|
||||||
|
results = self.long_term_memory.query(
|
||||||
|
query=query,
|
||||||
|
top_k=top_k or self.config.max_results,
|
||||||
|
similarity_threshold=similarity_threshold
|
||||||
|
or self.config.similarity_threshold,
|
||||||
|
)
|
||||||
|
return results if results else []
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error searching memories: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_memory_stats(self) -> Dict[str, Any]:
|
||||||
|
"""Get statistics about memory usage and operations"""
|
||||||
|
return {
|
||||||
|
"is_enabled": self.is_enabled(),
|
||||||
|
"config": self.config.__dict__,
|
||||||
|
"loops_processed": self._loop_counter,
|
||||||
|
"important_memories_count": len(self._important_memories),
|
||||||
|
"last_important_memories": (
|
||||||
|
self._important_memories[-3:]
|
||||||
|
if self._important_memories
|
||||||
|
else []
|
||||||
|
),
|
||||||
|
"memory_store_type": (
|
||||||
|
type(self.long_term_memory).__name__
|
||||||
|
if self.long_term_memory
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
def clear_session_data(self):
|
||||||
|
"""Clear session-specific data (not the long-term memory store)"""
|
||||||
|
self._loop_counter = 0
|
||||||
|
self._conversation_history.clear()
|
||||||
|
self._important_memories.clear()
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
logger.info(f"[{self.agent_name}] Session data cleared")
|
||||||
|
|
||||||
|
def update_config(self, **kwargs):
|
||||||
|
"""Update RAG configuration parameters"""
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
if hasattr(self.config, key):
|
||||||
|
setattr(self.config, key, value)
|
||||||
|
if self.verbose:
|
||||||
|
logger.info(
|
||||||
|
f"Updated RAG config: {key} = {value}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Unknown config parameter: {key}")
|
||||||
|
|
||||||
|
|
||||||
|
# # Example memory interface that your RAG implementation should follow
|
||||||
|
# class ExampleMemoryInterface:
|
||||||
|
# """Example interface for long-term memory implementations"""
|
||||||
|
|
||||||
|
# def add(self, content: str, metadata: Dict = None) -> bool:
|
||||||
|
# """
|
||||||
|
# Add content to the memory store.
|
||||||
|
|
||||||
|
# Args:
|
||||||
|
# content: Text content to store
|
||||||
|
# metadata: Additional metadata dictionary
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
# Boolean indicating success
|
||||||
|
# """
|
||||||
|
# # Your vector database implementation here
|
||||||
|
# return True
|
||||||
|
|
||||||
|
# def query(
|
||||||
|
# self,
|
||||||
|
# query: str,
|
||||||
|
# top_k: int = 5,
|
||||||
|
# similarity_threshold: float = 0.7
|
||||||
|
# ) -> List[Dict]:
|
||||||
|
# """
|
||||||
|
# Query the memory store for relevant content.
|
||||||
|
|
||||||
|
# Args:
|
||||||
|
# query: Search query string
|
||||||
|
# top_k: Maximum number of results to return
|
||||||
|
# similarity_threshold: Minimum similarity score
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
# List of dictionaries with keys: 'content', 'score', 'metadata'
|
||||||
|
# """
|
||||||
|
# # Your vector database query implementation here
|
||||||
|
# return [
|
||||||
|
# {
|
||||||
|
# 'content': 'Example memory content',
|
||||||
|
# 'score': 0.85,
|
||||||
|
# 'metadata': {'source': 'example', 'timestamp': time.time()}
|
||||||
|
# }
|
||||||
|
# ]
|
Loading…
Reference in new issue