← Back to Index

Agentic RAG Architecture for ZTAG Business Intelligence

A system that investigates, not just searches


Overview

Traditional RAG retrieves once and answers. Agentic RAG thinks, retrieves, thinks again, retrieves more, then synthesizes. This allows Claude to follow threads across meetings and emails, identify patterns, and provide analytical insights rather than just relevant quotes.

Why Agentic RAG for ZTAG

Simple RAG Agentic RAG
One retrieval, one answer Multiple retrievals with reasoning between
Finds similar text Investigates relationships
"Here are quotes about X" "Here's what the data tells us about X"
Can't follow threads Can trace customer journey across sources
Static context Dynamic, expanding context

Example Query Flow

User: "Which customers are at risk of churning and why?"

Round 1: Claude searches for complaint-related meetings
         β†’ Finds Jess Main said "when it works, kids love it"

Round 2: Claude gets Jess Main's full history
         β†’ Discovers 3 support tickets in 4 months, last contact 94 days ago

Round 3: Claude cross-references with email threads
         β†’ Finds frustrated tone in recent emails, unresolved issues

Round 4: Claude compares to healthy customer patterns
         β†’ Identifies churn risk factors

Output: Ranked list of at-risk customers with specific evidence and recommendations

Data Sources

Currently Available

Source Count Location
Meeting transcripts 747 raw_meetings/YYYY-MM-DD/*.json
Emails 3,000+ parsed_emails/emails.jsonl
Attachments 411 unique parsed_emails/attachments/
Strategic docs ~3 reports/MASTER_PLAN.md, reports/cmo_intelligence.md

Data Schema

Meeting JSON:

{
  "id": "123456789",
  "title": "Virtual ZTAG Demo with Kris",
  "created_at": "2025-06-10T14:30:00Z",
  "transcript_data": {
    "transcript": [
      {
        "speaker": {"display_name": "Jess Main"},
        "text": "But, like, yeah, when it works, like, the kids love it.",
        "start_time": 123.45
      }
    ]
  }
}

Email JSONL:

{
  "message_id": "<abc123@mail.gmail.com>",
  "thread_id": "thread_xyz",
  "date": "2025-08-02T10:15:00-07:00",
  "from": "jmain@school.edu",
  "to": ["sales@ztag.com"],
  "subject": "Re: Taggers not connecting",
  "body": "Same problem again...",
  "is_outbound": false,
  "is_reply": true,
  "labels": ["Support"]
}

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                           DATA LAYER                                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  raw_meetings/          parsed_emails/          reports/                    β”‚
β”‚  └─ 747 JSONs           β”œβ”€ emails.jsonl         β”œβ”€ MASTER_PLAN.md          β”‚
β”‚                         └─ attachments/         └─ cmo_intelligence.md      β”‚
β”‚                                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         INDEX LAYER                                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  ChromaDB (or Pinecone for production)                                      β”‚
β”‚                                                                              β”‚
β”‚  Collections:                                                                β”‚
β”‚  β”œβ”€ meetings     (chunks: speaker turns, ~500 tokens each)                  β”‚
β”‚  β”‚   └─ metadata: meeting_id, date, speaker, segment, customer              β”‚
β”‚  β”‚                                                                           β”‚
β”‚  β”œβ”€ emails       (chunks: one email = one chunk)                            β”‚
β”‚  β”‚   └─ metadata: thread_id, date, from, to, direction, customer            β”‚
β”‚  β”‚                                                                           β”‚
β”‚  └─ customers    (derived: profiles built from both sources)                β”‚
β”‚      └─ metadata: name, segment, first_contact, last_contact, health_score β”‚
β”‚                                                                              β”‚
β”‚  Embedding model: text-embedding-3-small or Voyage AI                       β”‚
β”‚                                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         TOOL LAYER                                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  Tools available to Claude:                                                 β”‚
β”‚                                                                              β”‚
β”‚  search_meetings(query, segment?, limit?)                                   β”‚
β”‚  └─ Semantic search over meeting transcripts                                β”‚
β”‚                                                                              β”‚
β”‚  search_emails(query, direction?, limit?)                                   β”‚
β”‚  └─ Semantic search over email threads                                      β”‚
β”‚                                                                              β”‚
β”‚  get_customer_history(customer_name, include_transcripts?)                  β”‚
β”‚  └─ ALL meetings + emails for a customer, chronological                     β”‚
β”‚                                                                              β”‚
β”‚  get_full_meeting(meeting_id)                                               β”‚
β”‚  └─ Complete transcript for deep reading                                    β”‚
β”‚                                                                              β”‚
β”‚  get_email_thread(thread_id)                                                β”‚
β”‚  └─ All emails in thread, chronological                                     β”‚
β”‚                                                                              β”‚
β”‚  list_customers(segment?, no_contact_days?)                                 β”‚
β”‚  └─ Filter customers by segment or recency                                  β”‚
β”‚                                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       ORCHESTRATOR (Claude)                                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  System prompt includes:                                                    β”‚
β”‚  - ZTAG business context (from MASTER_PLAN.md)                             β”‚
β”‚  - Segment definitions (institutional vs professional)                      β”‚
β”‚  - Investigation patterns (how to analyze churn, satisfaction, etc.)       β”‚
β”‚                                                                              β”‚
β”‚  Agentic loop:                                                              β”‚
β”‚  1. Receive user question                                                   β”‚
β”‚  2. Think: what do I need to investigate?                                   β”‚
β”‚  3. Call tools (can be multiple per round)                                  β”‚
β”‚  4. Receive results, think again                                            β”‚
β”‚  5. Repeat until confident (max 5 rounds)                                   β”‚
β”‚  6. Synthesize final answer with evidence                                   β”‚
β”‚                                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Implementation Steps

Phase 1: Index Layer

Script: scripts/build_index.py

  1. Chunk meetings by speaker turn (~500 tokens each)
  2. Chunk emails (one email = one chunk, already small)
  3. Embed using OpenAI text-embedding-3-small ($0.02/1M tokens)
  4. Store in ChromaDB with metadata
# Estimated indexing cost
# Meetings: 747 Γ— ~20 turns Γ— ~200 tokens = ~3M tokens = $0.06
# Emails: 3,000 Γ— ~500 tokens = ~1.5M tokens = $0.03
# Total one-time cost: ~$0.10

Phase 2: Entity Resolution

Script: scripts/build_customer_index.py

Link customers across sources:

Phase 3: Tool Functions

Script: scripts/rag_tools.py

Implement the 6 tools with proper ChromaDB queries:

def search_meetings(query: str, segment: str = None, limit: int = 10) -> list:
    """Semantic search over meeting transcripts."""
    where = {"segment": segment} if segment else None
    results = meetings_collection.query(
        query_texts=[query],
        n_results=limit,
        where=where
    )
    return format_results(results)

def get_customer_history(customer_name: str) -> dict:
    """Get ALL meetings and emails for a customer."""
    # Fuzzy match customer name
    customer = resolve_customer(customer_name)

    # Get all interactions
    meetings = get_meetings_by_customer(customer.id)
    emails = get_emails_by_customer(customer.id)

    return {
        "customer": customer.name,
        "segment": customer.segment,
        "first_contact": customer.first_contact,
        "last_contact": customer.last_contact,
        "meetings": meetings,
        "emails": emails,
        "support_tickets": len([e for e in emails if "support" in e.labels])
    }

Phase 4: Orchestrator

Script: scripts/agentic_rag.py

from anthropic import Anthropic

TOOLS = [
    {"name": "search_meetings", ...},
    {"name": "search_emails", ...},
    {"name": "get_customer_history", ...},
    {"name": "get_full_meeting", ...},
    {"name": "get_email_thread", ...},
    {"name": "list_customers", ...},
]

SYSTEM_PROMPT = """
You are ZTAG's business intelligence analyst. You have access to:
- 747 customer meeting transcripts (demos, training, support calls)
- 3,000+ sales emails
- Customer profiles across two segments: Institutional (schools) and Professional (GameTruck)

Your job is to INVESTIGATE questions by using the available tools. Don't guessβ€”retrieve the data.

For complex questions:
1. Start broad (search for relevant mentions)
2. Go deep (get full history for interesting customers)
3. Cross-reference (meetings + emails for same customer)
4. Synthesize (identify patterns, not just examples)
"""

def agentic_query(question: str, max_rounds: int = 5) -> str:
    messages = [{"role": "user", "content": question}]

    for round_num in range(max_rounds):
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            system=SYSTEM_PROMPT,
            tools=TOOLS,
            messages=messages
        )

        if response.stop_reason == "tool_use":
            # Execute tools and continue
            tool_results = execute_all_tools(response.content)
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # Done - return final answer
            return response.content[0].text

    return "Max rounds reached."

Phase 5: Interface

Option A: CLI

python scripts/ask.py "Which customers are at risk of churning?"

Option B: API (FastAPI)

@app.post("/ask")
async def ask(question: str):
    answer = agentic_query(question)
    return {"answer": answer}

Option C: Slack Bot


Example Queries

Customer Health

Product Intelligence

Competitive Analysis

Sales Enablement


Cost Estimates

Component One-time Per Query
Embedding (indexing) ~$0.10 -
ChromaDB storage Free (local) -
Claude Sonnet queries - ~$0.02-0.05
Estimated monthly (100 queries/day) - ~$60-150

Dependencies

anthropic>=0.18.0
chromadb>=0.4.0
openai>=1.0.0  # for embeddings
python-dotenv

File Structure

meetings/
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ build_index.py           # Phase 1: Index meetings + emails
β”‚   β”œβ”€β”€ build_customer_index.py  # Phase 2: Entity resolution
β”‚   β”œβ”€β”€ rag_tools.py             # Phase 3: Tool implementations
β”‚   β”œβ”€β”€ agentic_rag.py           # Phase 4: Orchestrator
β”‚   └── ask.py                   # Phase 5: CLI interface
β”œβ”€β”€ chroma_db/                   # Vector database storage
β”‚   β”œβ”€β”€ meetings/
β”‚   β”œβ”€β”€ emails/
β”‚   └── customers/
└── docs/
    └── AGENTIC_RAG_ARCHITECTURE.md  # This file

Future Enhancements

  1. Summarization Layer: Pre-summarize all meetings/threads to fit more in context
  2. Proactive Alerts: Daily digest of customers needing attention
  3. Email Draft Generation: Claude drafts follow-ups based on context
  4. CRM Integration: Sync customer profiles with HubSpot/Salesforce
  5. Voice Interface: Ask questions via phone/Slack voice

Document version: 1.0
Created: February 2026
Author: Claude + Quan