๐ Development Notes
Documents an AI agent server's architecture, bugs, routing flow, and production gaps for developers building similar systems.
What this file does
Documents an AI agent server's architecture, bugs, routing flow, and production gaps for developers building similar systems.
When to use it
- You want to understand how to structure an agent with plugins and RAG
- You need to document bugs and solutions from your own agent project
- You are planning a production migration from a prototype agent
- You want to see a concrete example of intent detection and memory management
Assumes this stack
๐ Development Notes
AI-Generated vs Hand-Written Code
๐ค AI-Generated Components (~40% of codebase)
I used Claude/GPT assistance for the following components, which I then extensively modified and enhanced:
-
Initial Project Structure (AI-generated base, heavily modified)
- Basic TypeScript configuration
- Package.json dependencies list
- Express server boilerplate
-
Mathematical Expression Parser (AI-generated core, safety-enhanced by me)
- File:
src/plugins/mathPlugin.ts-evaluateExpression()method - The recursive descent parser logic was AI-generated
- I added safety validations and error handling
- File:
-
Sample Document Content (AI-generated, curated by me)
- Content in
src/services/vectorStore.ts-createSampleDocuments() - AI generated the technical documentation content
- I structured and organized it for better RAG performance
- Content in
-
Gemini API Integration Patterns (AI-suggested patterns, implemented by me)
- Basic API call structure in
src/services/llmService.ts - I customized the prompt engineering and error handling
- Basic API call structure in
โ Hand-Written Components (~60% of codebase)
The core architecture and business logic was entirely my design:
-
System Architecture & Design
- Complete system design and component interaction
- Plugin manager architecture and intent detection
- Memory management strategy
- RAG workflow and retrieval logic
-
Vector Store Implementation
- Custom cosine similarity implementation
- Document chunking strategy
- Embedding storage and retrieval logic
- All in
src/services/vectorStore.ts
-
Plugin System Architecture
- Plugin interface design
- Intent detection patterns (regex-based)
- Plugin execution workflow
- Result integration with LLM context
-
Prompt Engineering
- Complete system prompt design
- Context injection strategy
- Memory integration patterns
- Plugin result formatting
-
Agent Service Orchestration
- Message processing pipeline
- Service coordination
- Error handling and recovery
- Response formatting
-
Session Memory Management
- Memory structure design
- Conversation history management
- Memory cleanup policies
๐ Bugs Faced & Solutions
1. Vector Store Initialization Race Condition
Problem: Vector store wasn't initialized before first requests
// Problematic code:
app.post("/agent/message", async (req, res) => {
// vectorStore.retrieveSimilarChunks() called before initialization
});
Solution: Added explicit initialization check and async initialization
async retrieveSimilarChunks(query: string, topK: number = 3): Promise<DocumentChunk[]> {
if (!this.isInitialized) {
await this.initialize(); // Auto-initialize if needed
}
// ... rest of method
}
2. Embedding API Rate Limits
Problem: When processing multiple documents simultaneously, hit Gemini rate limits
Solution: Added sequential processing with error handling
for (let i = 0; i < chunks.length; i++) {
try {
const embedding = await generateEmbedding(chunk);
// Process one at a time to avoid rate limits
} catch (error) {
console.error(`Error processing chunk ${i}:`, error);
// Continue with other chunks instead of failing completely
}
}
3. Math Plugin Security Vulnerability
Problem: Initial implementation used eval() which is dangerous
// Dangerous approach:
const result = eval(mathExpression); // NEVER DO THIS
Solution: Built custom recursive descent parser
private evaluateExpression(expr: string): number {
// Validate input first
if (!/^[0-9+\-*/.()]+$/.test(expr)) {
throw new Error('Invalid characters in expression');
}
// Use safe parsing instead of eval
return this.parseExpression(expr, 0).value;
}
4. Memory Leak in Session Storage
Problem: Sessions growing infinitely without cleanup
Solution: Added automatic memory management
// Keep only last 20 messages to prevent memory bloat
if (session.messages.length > 20) {
session.messages = session.messages.slice(-20);
}
5. Plugin Intent Detection False Positives
Problem: Simple keyword matching triggered wrong plugins
// Too broad:
if (message.includes('weather')) // Triggered on "whether or not"
Solution: Used more specific regex patterns
// More precise:
if (/weather.*in\s+\w+/i.test(message) || /what.*weather/i.test(message))
6. Document Chunking Edge Cases
Problem: Very short chunks or empty chunks causing embedding failures
Solution: Added chunk validation and filtering
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
if (chunk.trim().length < 50) continue; // Skip very short chunks
// ... process chunk
}
๐ Agent Routing Flow
Message Processing Pipeline
1. HTTP Request
โ
2. Input Validation
โ
3. RAG Retrieval (Vector Store)
โโโ Generate query embedding
โโโ Calculate cosine similarities
โโโ Return top-3 chunks
โ
4. Memory Retrieval
โโโ Get last 4 messages for session
โโโ Format for LLM context
โ
5. Plugin Detection & Execution
โโโ Weather intent detection
โโโ Math intent detection
โโโ Execute matching plugins
โ
6. LLM Generation
โโโ Build system prompt with:
โ โโโ Instructions
โ โโโ Retrieved context
โ โโโ Plugin results
โ โโโ Memory
โโโ Call Gemini gemini-2.5-flash
โโโ Get response
โ
7. Memory Storage
โโโ Store user message
โโโ Store assistant response
โ
8. HTTP Response
Plugin Call Routing
// Intent Detection (PluginManager)
async detectAndExecutePlugins(message: string): Promise<PluginResult[]> {
const results: PluginResult[] = [];
// Weather detection
if (/weather.*in\s+\w+/i.test(message)) {
results.push(await this.weatherPlugin.execute(message));
}
// Math detection
if (/\d+\s*[+\-*/]\s*\d+/i.test(message)) {
results.push(await this.mathPlugin.execute(message));
}
return results;
}
Memory + Context Integration
// LLM Service - Context Building
private buildSystemPrompt(retrievedChunks: DocumentChunk[], pluginResults: PluginResult[]): string {
let prompt = `You are an intelligent AI assistant...`;
// Add RAG context
if (retrievedChunks.length > 0) {
prompt += `KNOWLEDGE BASE CONTEXT:\n`;
retrievedChunks.forEach((chunk, index) => {
prompt += `Context ${index + 1} (from ${chunk.metadata.source}):\n${chunk.content}\n\n`;
});
}
// Add plugin results
if (pluginResults.length > 0) {
prompt += `TOOL RESULTS:\n`;
pluginResults.forEach((result, index) => {
prompt += `Tool ${index + 1} (${result.plugin_name}): ${JSON.stringify(result.result)}\n`;
});
}
return prompt;
}
๐ Performance Optimizations Implemented
1. Embedding Caching Strategy
- Generated embeddings are stored in memory
- No re-computation for same documents
- ~90% faster on subsequent queries
2. Memory Management
- Limited conversation history (20 messages max)
- Only last 4 messages sent to LLM
- Prevents context window overflow
3. Chunk Size Optimization
- 300 character chunks for optimal embedding quality
- Balanced between context and specificity
- Tested various sizes (150, 300, 500 chars)
4. Plugin Execution Optimization
- Parallel plugin detection
- Early return on intent match
- Mock data fallbacks for failed API calls
๐ง Architecture Decisions
Why Custom Vector Store?
- Requirement: No FAISS allowed in TypeScript
- Options Considered: ChromaDB, Pinecone, Weaviate
- Decision: Custom implementation using cosine similarity
- Trade-offs: Simpler but less scalable than dedicated vector DBs
Why In-Memory Storage?
- Requirement: 1-day challenge timeline
- Decision: In-memory for speed of development
- Trade-offs: Not production-scalable, but perfect for demo
- Future: Would use Redis for sessions, PostgreSQL for persistence
Plugin Architecture Choice
- Pattern: Intent-based detection with regex
- Alternative: LLM-based intent classification
- Decision: Regex for speed and deterministic behavior
- Trade-offs: Less flexible but more reliable
Prompt Engineering Strategy
- Approach: Explicit section-based prompts
- Sections: Instructions โ Context โ Tools โ History
- Decision: Clear structure over conversational prompts
- Result: More consistent and controllable responses
๐ฏ Production Readiness Gaps
Current Limitations
- No Persistent Storage: All data lost on restart
- No Authentication: Open endpoints
- No Rate Limiting: Vulnerable to abuse
- Single Instance: No horizontal scaling
- No Monitoring: No metrics or logging infrastructure
Production Migration Path
- Database Layer: PostgreSQL + Redis
- Authentication: JWT tokens or API keys
- Rate Limiting: Redis-based token bucket
- Scaling: Docker + Kubernetes deployment
- Monitoring: Prometheus + Grafana metrics
- Vector DB: Migrate to Pinecone or Weaviate
- CI/CD: GitHub Actions deployment pipeline
๐งช Testing Strategy
Manual Testing Done
- โ Knowledge base queries (AI fundamentals, web dev, etc.)
- โ Weather plugin with various city formats
- โ Math plugin with complex expressions
- โ Session memory across multiple messages
- โ Plugin combination scenarios
- โ Error handling (invalid inputs, API failures)
- โ RAG retrieval accuracy
Test Cases Validated
# Knowledge retrieval
"What is machine learning?" โ Retrieved AI fundamentals doc
# Plugin detection
"Weather in Mumbai" โ Weather plugin triggered
"Calculate 15 * 8 + 42" โ Math plugin triggered
# Memory persistence
Session 1: "Tell me about APIs" โ Response about REST
Session 1: "What HTTP methods?" โ Context-aware response
# Error handling
Invalid math: "Calculate abc" โ Graceful error message
Missing API key: Weather call โ Falls back to mock data
๐ก Key Learnings
1. Prompt Engineering is Critical
- Structure matters more than length
- Explicit instructions beat implicit expectations
- Context ordering affects response quality
2. Plugin Intent Detection Challenges
- Natural language is ambiguous
- Multiple intents in single message
- False positives require careful regex design
3. RAG Retrieval Quality
- Chunk size significantly impacts relevance
- Embedding model choice affects similarity accuracy
- Query preprocessing improves retrieval
4. Memory Management Balance
- Too much history โ expensive API calls
- Too little history โ loss of context
- Sweet spot: 4-6 recent messages
This system demonstrates production-ready patterns while being implementable in a 1-day timeframe. The architecture is designed for easy extension and production migration.
What's inside
8 sections: AI vs hand-written code, 6 bugs with solutions, routing flow, performance optimizations, architecture decisions, production gaps, testing strategy, key learnings
Change this for your project
- Replace
gemini-2.5-flashwith your LLM model ID - Replace
Khalid2093/ai-agent-serverwith your repository name - Replace
src/plugins/mathPlugin.tswith your own plugin file paths - Replace
src/services/vectorStore.tswith your vector store implementation
Where it goes
Reference documentation for a retrieval pipeline. Keep with the ingestion or retrieval code it describes.
Worth borrowing
- Document bugs with before/after code snippets to capture solutions
- Separate AI-generated from hand-written code to clarify ownership
- List production readiness gaps explicitly to guide future work
Related Documents
SUMMARY
Proposes three on-prem AI architectures, modular, hybrid, and fully local RAG, with hardware specs and vendor lists.
Retrieval & Prompts
Explains how CharMemory's extraction prompt and Vector Storage settings determine memory retrieval quality in SillyTavern.
Advanced RAG Techniques
Surveys advanced RAG architectures, Agentic, Corrective, Self-RAG, Graph RAG, RAPTOR, Multimodal, Temporal, with design trade-offs and decision guidance.
RFC-BLite: High-Performance Embedded Document Database for .NET
Specifies an embedded document database for.NET with zero-allocation I/O, C-BSON format, and ACID transactions.