This guide provides detailed information for developers who want to extend or modify the Agentic RAG system.
The project is organized into several directories:
agentic-rag/
├── agents/ # Agent implementations
├── docs/ # Documentation
├── examples/ # Example usage scripts
├── memory/ # Memory components
├── planning/ # Planning components
├── tests/ # Test suite
├── app.py # Main application
├── api.py # API server
├── core.py # Core data models
├── config.json # Configuration
├── .env.example # Environment variables template
├── Dockerfile # Docker configuration
├── requirements.txt # Dependencies
└── README.md # Project overview
The core data models are defined in core.py:
Query: Represents a user query to the systemDocument: Represents a document retrieved or generated by the systemMemoryEntry: Represents an entry in the memory systemAgentType: Enum for agent typesAgentMessage: Message exchanged between agentsAgentResult: Result returned by an agent after processingPlanStep: Represents a step in an execution planPlan: Represents an execution plan for a queryRagOutput: Final output of the Agentic RAG system
The main application is defined in app.py. The AgenticRag class orchestrates the workflow between different components.
The API server is defined in api.py using FastAPI. It provides endpoints for interacting with the system.
To add a new agent:
- Create a new file in the
agentsdirectory (e.g.,agents/my_agent.py) - Define a class that inherits from
BaseAgent - Implement the required methods
- Add configuration in
config.json - Register the agent in
app.py
Example:
from core import AgentType, Query, Document, AgentResult
from agents.base import BaseAgent
class MyAgent(BaseAgent):
def __init__(self, custom_param: str = "default") -> None:
super().__init__(agent_type=AgentType.CUSTOM)
self.custom_param = custom_param
self.logger.info(f"MyAgent initialized with custom_param={custom_param}")
@BaseAgent.measure_execution_time
def process(self, query: Query) -> AgentResult:
self.logger.debug(f"Processing query: {query.id}")
try:
# Process the query
content = f"Result for query: {query.text}"
document = Document(content=content, source="my_agent")
return AgentResult(
agent_id=self.id,
agent_type=self.agent_type,
query_id=query.id,
documents=[document],
confidence=0.8,
processing_time=0.0, # Will be set by decorator
metadata={"custom_param": self.custom_param}
)
except Exception as e:
self.logger.error(f"Error processing query: {str(e)}")
return AgentResult(
agent_id=self.id,
agent_type=self.agent_type,
query_id=query.id,
documents=[],
confidence=0.0,
processing_time=0.0, # Will be set by decorator
metadata={"error": str(e)}
)To add a new memory component:
- Create a new file in the
memorydirectory (e.g.,memory/redis_memory.py) - Define a class that inherits from
BaseMemory - Implement the required methods
- Add configuration in
config.json - Register the memory component in
app.py
Example:
import json
import redis
from typing import Dict, List, Optional, Union
from core import Query, Document, MemoryEntry, AgentResult
from memory.base import BaseMemory
class RedisMemory(BaseMemory):
def __init__(self, host: str = "localhost", port: int = 6379, db: int = 0) -> None:
super().__init__()
self.host = host
self.port = port
self.db = db
self.client = redis.Redis(host=host, port=port, db=db)
self.logger.info(f"RedisMemory initialized with host={host}, port={port}, db={db}")
def retrieve(self, query: Query) -> Optional[AgentResult]:
# Implementation
pass
def store(self, query: Query, result: AgentResult) -> None:
# Implementation
pass
def update(self, memory_entry: MemoryEntry) -> None:
# Implementation
pass
def remove(self, memory_id: str) -> bool:
# Implementation
pass
def clear(self) -> None:
# Implementation
pass
def get_stats(self) -> Dict[str, Union[int, float]]:
# Implementation
pass
def close(self) -> None:
self.client.close()
self.logger.info("Redis connection closed")To add a new planner:
- Create a new file in the
planningdirectory (e.g.,planning/my_planner.py) - Define a class that inherits from
BasePlanner - Implement the required methods
- Add configuration in
config.json - Register the planner in
app.py
Example:
from typing import Dict, List, Optional
from core import AgentType, Query, Plan
from planning.base import BasePlanner
from agents.base import BaseAgent
class MyPlanner(BasePlanner):
def __init__(self, max_depth: int = 3) -> None:
super().__init__()
self.max_depth = max_depth
self.logger.info(f"MyPlanner initialized with max_depth={max_depth}")
def create_plan(self, query: Query, available_agents: Dict[AgentType, BaseAgent]) -> Plan:
# Implementation
pass
def adapt_plan(self, plan: Plan, results_so_far: List, available_agents: Dict[AgentType, BaseAgent]) -> Plan:
# Implementation
passTo integrate with the API:
- Use the
/queryendpoint to process queries - Use the
/healthendpoint to check system health - Use the
/statsendpoint to get system statistics
Example API request:
import requests
import json
def process_query(query_text, api_url="http://localhost:8000"):
response = requests.post(
f"{api_url}/query",
json={"text": query_text, "metadata": {"source": "api"}}
)
if response.status_code == 200:
return response.json()
else:
raise Exception(f"Error: {response.status_code} - {response.text}")
result = process_query("What are the latest developments in AI research?")
print(json.dumps(result, indent=2))The system includes a comprehensive test suite in the tests directory:
tests/test_core.py: Tests for core data modelstests/test_memory.py: Tests for memory componentstests/test_planning.py: Tests for planning componentstests/test_agent.py: Tests for agent componentstests/test_api.py: Tests for API components
To run the tests:
pytest tests/To run a specific test:
pytest tests/test_core.py::TestCoreModels::test_query_creationTo run tests with coverage:
pytest --cov=. tests/The system uses Python's built-in logging module:
import logging
# Get a logger
logger = logging.getLogger("agentic_rag.my_component")
# Log messages
logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")
logger.error("Error message")
logger.critical("Critical message")Configure logging in config.json:
"logging": {
"level": "debug",
"format": "text",
"output": "both",
"file_path": "logs/agentic-rag.log"
}Follow these guidelines for error handling:
- Use try/except blocks to catch exceptions
- Log errors with appropriate context
- Return graceful fallbacks when possible
- Propagate errors up when necessary
Example:
def process_data(data):
try:
# Process data
result = do_processing(data)
return result
except ValueError as e:
logger.warning(f"Invalid data format: {str(e)}")
return None
except Exception as e:
logger.error(f"Unexpected error processing data: {str(e)}")
raiseFollow these guidelines for documentation:
- Use docstrings for all modules, classes, and functions
- Follow the Google Python Style for docstrings
- Include type annotations
- Keep documentation up to date with code changes
Example:
def process_query(query_text: str) -> Dict[str, Any]:
"""
Process a query through the Agentic RAG system.
Args:
query_text: Text of the query to process
Returns:
Dictionary containing the response and metadata
Raises:
ValueError: If query_text is empty
"""
if not query_text:
raise ValueError("Query text cannot be empty")
# Process query
return {"response": "Result", "metadata": {}}- Use the
@BaseAgent.measure_execution_timedecorator to track performance - Monitor memory usage, especially in memory components
- Use caching where appropriate
- Consider asynchronous processing for I/O-bound operations
- Optimize database queries and indexing
- Validate all user input
- Use environment variables for sensitive configuration
- Implement proper authentication for API endpoints
- Keep dependencies up to date
- Follow the principle of least privilege
- Implement rate limiting and other protection measures
Deploy the system using Docker:
# Build the Docker image
docker build -t agentic-rag .
# Run the container
docker run -p 8000:8000 \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/data:/app/data \
-v $(pwd)/logs:/app/logs \
--env-file .env \
agentic-ragFor production environments:
- Use a reverse proxy (e.g., Nginx, Traefik)
- Implement proper authentication
- Set up monitoring and alerting
- Configure auto-scaling
- Use a container orchestration system (e.g., Kubernetes, Docker Swarm)
Set up CI/CD pipelines using GitHub Actions, GitLab CI, or Jenkins:
- Run linting and static analysis
- Run unit and integration tests
- Build Docker image
- Push to container registry
- Deploy to staging environment
- Run acceptance tests
- Deploy to production environment
For more information, see the Operations Guide.