RAG Architecture
February 3, 2026 · View on GitHub
This document describes the Retrieval-Augmented Generation (RAG) architecture in Don, including implementation status, design decisions, and usage.
Overview
RAG support allows Don agents to retrieve relevant information from documents (URLs, local files, or directories) to answer questions. The implementation provides a document processing foundation that integrates with the cagent library's RAG system.
Architecture
Components
┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ cmd/agent.go: --rag flag, config merging │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ Agent Layer │
│ pkg/agent/agent.go: RAG source processing │
│ pkg/agent/rag.go: Document processing │
│ pkg/agent/cagent_runtime.go: cagent integration │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ Document Processing Layer │
│ pkg/rag/downloader.go: HTTP downloads with caching │
│ pkg/rag/scanner.go: Local file/directory scanning │
│ pkg/rag/cache.go: Platform-specific cache management │
└─────────────────────────────────────────────────────────────┘
Package Structure
pkg/rag/ - Document Processing
Core document processing functionality:
-
types.go: Core types and interfaces
DocumentSource: Represents a document (URL or local path)Downloader: Interface for downloading documentsScanner: Interface for scanning local filesDownloaderConfig: Configuration for document processing
-
downloader.go: HTTP document downloader
- Intelligent caching with ETag/Last-Modified headers
- Content-type validation (text files only)
- Size limits and timeout handling
- Automatic re-download when documents are stale
-
scanner.go: File and directory scanner
- Recursive directory scanning
- Text file detection based on extension
- Path validation and security checks
-
cache.go: Cache management
- Platform-specific cache directories:
- macOS:
~/Library/Caches/don/rag/ - Linux:
~/.cache/don/rag/ - Windows:
%LOCALAPPDATA%\don\rag\
- macOS:
- Override with
DON_RAG_CACHE_DIRenvironment variable - Path traversal prevention
- Metadata serialization
- Platform-specific cache directories:
-
errors.go: Custom error types for RAG operations
pkg/agent/ - RAG Integration
Integration with agent functionality:
-
config_file.go: RAG configuration structures
RAGSourceConfig: Top-level RAG source definitionRAGStrategyConfig: Retrieval strategy (chunked-embeddings, BM25)RAGChunkingConfig: Document chunking parametersRAGFusionConfig: Multi-strategy result fusionRAGResultsConfig: Result processing options
-
rag.go: Document processing logic
ProcessRAGSources(): Downloads URLs and scans local files- Converts all document sources to local paths
- Returns processed configuration ready for indexing
-
agent.go: Agent initialization
- Added
RAGSources []stringandRAGConfig map[string]RAGSourceConfig - Processes RAG sources before agent starts
- Documents downloaded and scanned during initialization
- Added
-
cagent_runtime.go: cagent integration
- Uses teamloader to load team with full RAG support
- Generates cagent-compatible YAML configuration
- RAG managers created automatically by cagent
cmd/ - CLI Integration
Command-line interface:
-
root.go: Flag variables
agentRAGSources []string: RAG source names to enable
-
agent.go: Agent command
--ragflag: Specify RAG sources from config (repeatable)buildAgentConfig(): Merges CLI flags with config file- Validates RAG sources exist in agent config
Configuration
Agent Configuration File
RAG sources are defined in the agent configuration file (e.g.,
~/.don/agent.yaml):
agent:
models:
- model: "gpt-4o"
class: "openai"
default: true
api-key: "${OPENAI_API_KEY}"
rag:
# Define RAG sources
docs:
description: "Project documentation"
# Documents to index (URLs and local paths)
docs:
- "https://example.com/docs/getting-started.md"
- "https://example.com/docs/api-reference.md"
- "./docs" # Local directory
- "./README.md" # Local file
# Retrieval strategies
strategies:
- type: "chunked-embeddings"
model: "text-embedding-3-small"
chunking:
size: 1000
overlap: 200
respect_word_boundaries: true
limit: 5
# Result configuration
results:
limit: 10
deduplicate: true
include_score: false
Configuration Options
RAG Source Fields:
description: Human-readable description of the knowledge sourcedocs: List of URLs or local paths (files or directories)strategies: List of retrieval strategies to useresults: Result processing configuration
Strategy Types:
chunked-embeddings: Vector similarity search with embeddingsbm25: BM25 keyword-based search
Chunking Options:
size: Chunk size in characters (default: 1000)overlap: Overlap between chunks in characters (default: 200)respect_word_boundaries: Don't split words (default: true)
Results Options:
limit: Maximum number of results to returnfusion: Multi-strategy result fusion (rrf, weighted, max)deduplicate: Remove duplicate resultsinclude_score: Include similarity scoresreturn_full_content: Return full documents instead of chunks
Usage
Command-Line
Enable RAG sources when running the agent:
# Single RAG source
don agent --tools=tools.yaml --rag=docs "How do I get started?"
# Multiple RAG sources
don agent --tools=tools.yaml --rag=docs --rag=api "Explain the API"
Example Workflow
-
Create agent configuration with RAG sources:
cat > ~/.don/agent.yaml << EOF agent: models: - model: "gpt-4o" class: "openai" default: true api-key: "${OPENAI_API_KEY}" rag: docs: description: "Project documentation" docs: - "https://example.com/docs" - "./docs" strategies: - type: "chunked-embeddings" model: "text-embedding-3-small" EOF -
Run agent with RAG enabled:
don agent --tools=tools.yaml --rag=docs "What is this project about?" -
Documents are processed:
- URLs are downloaded to cache
- Local files and directories are scanned
- All documents converted to local paths
- Ready for indexing by cagent's RAG system
Implementation Status
✅ Phase 1: Document Processing (COMPLETE)
Status: Fully implemented and tested
Features:
- HTTP document downloader with intelligent caching
- File scanner for local files and directories
- Platform-specific cache directories
- Comprehensive security features
- All tests passing
Files:
pkg/rag/types.go- Core types and interfacespkg/rag/errors.go- Custom error typespkg/rag/cache.go- Cache managementpkg/rag/downloader.go- HTTP downloader (297 lines)pkg/rag/scanner.go- File scanner (159 lines)pkg/rag/cache_test.go- Test suite
✅ Phase 2: RAG Integration (COMPLETE)
Status: Fully implemented using cagent's teamloader
Features:
- ✅ RAG configuration parsing from YAML
- ✅ Document downloading from URLs with caching
- ✅ Local file and directory scanning
- ✅ Document path resolution and validation
- ✅ All processed documents available as local paths
- ✅ Config conversion to cagent v2 YAML format
- ✅ RAG manager creation via teamloader
- ✅ Document indexing with embeddings (via cagent)
- ✅ RAG tool creation and registration (via cagent)
- ✅ Manager lifecycle (automatic via teamloader)
- ✅ Multi-strategy retrieval support
- ✅ Result fusion (RRF, weighted, max)
Implementation Approach:
Don uses cagent's teamloader.Load() to create teams with full RAG support:
-
Config Conversion (
pkg/agent/config_yaml.go):- Converts Don config to cagent v2 YAML format
- Maps models, RAG sources, and strategies
- Generates temporary config file
-
Team Loading (
pkg/agent/cagent_runtime.go):- Calls
teamloader.Load()with generated config - Teamloader handles all RAG setup automatically
- RAG managers, tools, and indexing done by cagent
- Calls
-
MCP Tools (subprocess):
- MCP server started as subprocess via
command: "don mcp --tools <file>" - Clean separation of concerns
- Aligns with cagent's design
- MCP server started as subprocess via
Files:
pkg/agent/config_file.go- RAG configuration structurespkg/agent/config_yaml.go- Config conversion to cagent format (NEW - 317 lines)pkg/agent/rag.go- Document processing logicpkg/agent/agent.go- RAG source processingpkg/agent/cagent_runtime.go- Teamloader integration (REFACTORED - 183 lines)go.mod- Upgraded cagent to v1.9.26
✅ Phase 3: CLI Integration (COMPLETE)
Status: Fully implemented
Features:
--ragflag for specifying RAG sources- Configuration merging in
buildAgentConfig() - Example configuration with comprehensive documentation
- Updated README with RAG support note
Files:
cmd/root.go- RAG flag variablecmd/agent.go- RAG flag and configuration mergingexamples/agent_with_rag.yaml- Example configurationREADME.md- RAG documentation
Testing
Unit Tests
All unit tests pass:
$ go test ./pkg/rag/...
=== RUN TestGetCacheDir
--- PASS: TestGetCacheDir (0.00s)
=== RUN TestHashURL
--- PASS: TestHashURL (0.00s)
=== RUN TestSaveAndLoadMetadata
--- PASS: TestSaveAndLoadMetadata (0.00s)
=== RUN TestValidatePath
--- PASS: TestValidatePath (0.00s)
PASS
ok github.com/inercia/Don/pkg/rag (cached)
Integration Tests
Comprehensive integration test suite:
Test Documents (tests/agent/rag_docs/):
product_info.txt- Product information and featuresapi_reference.txt- API documentationsecurity_guide.txt- Security guidelines
Test Configuration (tests/agent/tools/test_agent_rag.yaml):
- Agent with two RAG sources (product_docs, security_docs)
- Uses OpenAI GPT-4o-mini model
- Chunked-embeddings strategy
Test Script (tests/agent/test_agent_rag.sh):
- Tests single RAG source retrieval
- Tests multiple RAG sources simultaneously
- Verifies agent responses include RAG information
- Automatically skips if OPENAI_API_KEY not set
Running Tests:
# Set OpenAI API key
export OPENAI_API_KEY="sk-..."
# Run RAG test
./tests/agent/test_agent_rag.sh
# Or run all tests
make test-e2e
Security
Path Traversal Prevention
All paths are validated to prevent directory traversal attacks:
func ValidatePath(path string) error {
// Check for path traversal before and after cleaning
if strings.Contains(path, "..") {
return ErrInvalidPath
}
cleaned := filepath.Clean(path)
if strings.Contains(cleaned, "..") {
return ErrInvalidPath
}
return nil
}
URL Validation
Only HTTP/HTTPS URLs are allowed:
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
return "", fmt.Errorf("invalid URL scheme: %s", url)
}
Content-Type Validation
Only text-based content types are downloaded:
contentType := resp.Header.Get("Content-Type")
if !strings.HasPrefix(contentType, "text/") &&
!strings.Contains(contentType, "json") &&
!strings.Contains(contentType, "yaml") {
return "", fmt.Errorf("unsupported content type: %s", contentType)
}
Size Limits
Documents are limited to prevent memory exhaustion:
const DefaultMaxSize = 10 * 1024 * 1024 // 10MB
Next Steps for Full RAG Support
To complete the RAG integration, choose one of two approaches:
Option A: Use cagent's teamloader (Recommended)
Approach: Replace custom agent creation with cagent's teamloader
Pros:
- Full RAG support out of the box
- Follows cagent's intended usage pattern
- Less custom code to maintain
- Automatic updates when cagent changes
Cons:
- Major refactoring of
pkg/agent/cagent_runtime.go - Lose some control over agent creation
- Need to adapt config format to cagent's expectations
Implementation Steps:
- Convert our config to cagent's
latest.Configformat - Replace
CreateCagentRuntime()to useteamloader.Load() - Pass processed RAG documents to the teamloader
- Test with example RAG configurations
Estimated Effort: 1-2 days
Option B: Manual RAG Manager Creation
Approach: Manually create RAG managers and tools
Pros:
- Keep current agent creation approach
- More control over RAG setup
- Can customize RAG behavior
Cons:
- Complex implementation
- Tightly coupled to cagent internals
- May break with cagent updates
- More code to maintain
Implementation Steps:
- Study cagent's
pkg/rag/builder.goandpkg/teamloader/teamloader.go - Implement conversion from our config to cagent's RAG config format
- Create
ManagersBuildConfigwith required dependencies - Call
rag.NewManagers()to create managers - Create RAG tools using
builtin.NewRAGTool() - Add RAG tools to agent's toolset
- Pass RAG managers to team via
team.WithRAGManagers()
Estimated Effort: 3-5 days
Recommendation
Use Option A (teamloader) for the following reasons:
- It's the intended way to use cagent's RAG functionality
- Less code to maintain and debug
- Automatic compatibility with future cagent updates
- The refactoring is manageable and well-scoped
Design Decisions
Why Platform-Specific Cache Directories?
Following OS conventions for cache storage:
- Better integration with OS cache management
- Users expect caches in standard locations
- Easier to find and clear caches
- Respects OS-specific cleanup policies
Why Separate Document Processing Layer?
Separation of concerns:
- Document processing is independent of RAG indexing
- Can be tested in isolation
- Reusable for other purposes
- Clear interface boundaries
Why Support Both URLs and Local Paths?
Flexibility for different use cases:
- URLs: Public documentation, API references
- Local paths: Private docs, code repositories
- Directories: Scan entire doc trees
- Files: Specific documents
Why Intelligent Caching?
Performance and cost optimization:
- Avoid re-downloading unchanged documents
- Reduce network traffic
- Faster agent startup
- Lower API costs for embeddings
Examples
See examples/agent_with_rag.yaml for a complete example configuration with:
- Three RAG sources (project_docs, api_docs, code_examples)
- Multiple retrieval strategies
- Result fusion configuration
- Comprehensive inline documentation
References
- cagent RAG PR #843 - Original RAG implementation
- cagent Documentation - cagent library
- Model Context Protocol - MCP specification