Back to .md Directory

๐Ÿ“ Development Notes

Documents an AI agent server's architecture, bugs, routing flow, and production gaps for developers building similar systems.

May 2, 2026
0 downloads
1 views
ai agent llm rag prompt eval claude gemini
View source

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

TypeScriptExpressGemini APINode.js

๐Ÿ“ 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:

  1. Initial Project Structure (AI-generated base, heavily modified)

    • Basic TypeScript configuration
    • Package.json dependencies list
    • Express server boilerplate
  2. 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
  3. 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
  4. 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

โœ‹ Hand-Written Components (~60% of codebase)

The core architecture and business logic was entirely my design:

  1. System Architecture & Design

    • Complete system design and component interaction
    • Plugin manager architecture and intent detection
    • Memory management strategy
    • RAG workflow and retrieval logic
  2. Vector Store Implementation

    • Custom cosine similarity implementation
    • Document chunking strategy
    • Embedding storage and retrieval logic
    • All in src/services/vectorStore.ts
  3. Plugin System Architecture

    • Plugin interface design
    • Intent detection patterns (regex-based)
    • Plugin execution workflow
    • Result integration with LLM context
  4. Prompt Engineering

    • Complete system prompt design
    • Context injection strategy
    • Memory integration patterns
    • Plugin result formatting
  5. Agent Service Orchestration

    • Message processing pipeline
    • Service coordination
    • Error handling and recovery
    • Response formatting
  6. 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

  1. No Persistent Storage: All data lost on restart
  2. No Authentication: Open endpoints
  3. No Rate Limiting: Vulnerable to abuse
  4. Single Instance: No horizontal scaling
  5. No Monitoring: No metrics or logging infrastructure

Production Migration Path

  1. Database Layer: PostgreSQL + Redis
  2. Authentication: JWT tokens or API keys
  3. Rate Limiting: Redis-based token bucket
  4. Scaling: Docker + Kubernetes deployment
  5. Monitoring: Prometheus + Grafana metrics
  6. Vector DB: Migrate to Pinecone or Weaviate
  7. 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-flash with your LLM model ID
  • Replace Khalid2093/ai-agent-server with your repository name
  • Replace src/plugins/mathPlugin.ts with your own plugin file paths
  • Replace src/services/vectorStore.ts with 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