Skip to content

CampaignBrain AI Chat

Version: 0.4.81+ Last Updated: 2025-12-28

The AI Chat provides a natural language interface for querying voter data and campaign engagement records. Powered by Claude, it understands campaign terminology and converts plain English questions into executable queries.

Table of Contents

  1. Overview
  2. Architecture
  3. Data Sources
  4. Query Types
  5. Configuration
  6. API Endpoints
  7. Example Queries
  8. Technical Details

Overview

The AI Chat combines two powerful data sources:

Source Data Type Query Method
i360 Voters Voter demographics, scores, contact info QueryIR (structured JSON)
Campaign Data Donors, volunteers, event attendees Tool Calling

Users can ask questions in natural language, and Claude determines the best approach to answer:

  • Voter queries → Structured JSON output → DuckDB SQL execution
  • Campaign queries → Native tool calls → cbmodels API

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         User Message                             │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                      Claude AI (Sonnet)                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ System Prompt:                                              ││
│  │ - Data Dictionary (voter columns, stats, examples)          ││
│  │ - QueryIR format specification                              ││
│  │ - Campaign tool descriptions                                ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
                ┌───────────────┴───────────────┐
                │                               │
                ▼                               ▼
┌───────────────────────────┐   ┌───────────────────────────────┐
│      QueryIR (JSON)       │   │       Tool Use (API)          │
│                           │   │                               │
│ {                         │   │ list_campaign_sources()       │
│   "type": "query",        │   │ lookup_campaign_contact()     │
│   "intent": "list",       │   │ find_super_supporters()       │
│   "steps": [...]          │   │ enrich_with_campaign_data()   │
│ }                         │   │                               │
└───────────────────────────┘   └───────────────────────────────┘
                │                               │
                ▼                               ▼
┌───────────────────────────┐   ┌───────────────────────────────┐
│    CBQueryEngine          │   │    campaign_data_service      │
│    (DuckDB SQL)           │   │    (httpx → cbmodels API)     │
└───────────────────────────┘   └───────────────────────────────┘
                │                               │
                ▼                               ▼
┌───────────────────────────┐   ┌───────────────────────────────┐
│    i360_voters table      │   │    cbmodels Campaign API      │
│    (DuckDB)               │   │    (Tier 1 endpoints)         │
└───────────────────────────┘   └───────────────────────────────┘
                │                               │
                └───────────────┬───────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                       ChatResponse                               │
│  - message: Formatted results                                    │
│  - type: results | clarify | error                              │
│  - data: { count, samples, tool_results }                       │
│  - actions: [ Save as Segment, View in Search, Refine ]         │
└─────────────────────────────────────────────────────────────────┘

Data Sources

1. i360 Voter Data

The voter database contains 27+ columns of voter information:

Category Fields
Identity svid, first_name, last_name, suffix, gender, birth_year
Location address, city, state, zip, county, congressional_district, state_house, state_senate, precinct
Contact email_mydata, cell_phone_mydata, home_phone_mydata
Political party, voter_status
Scores turnout_score, trump_support_score, gop_primary_score, fiscal_score, 2a_score, pro_life_score
Demographics ethnicity, religion, marital_status, occupation

2. Campaign Data (cbmodels)

Campaign engagement data from imported files:

Data Type Examples
Donors 2022-Donor-MI.xlsx, WinRed donors
Volunteers Weekly volunteer lead files
Event Attendees Rally signups, town halls
Captains Trump Force captains, team leads
Commits BYV commits, pledge cards

Query Types

Voter Queries (QueryIR)

For demographic, geographic, and score-based filtering:

"Find Republicans in Wayne County with high turnout scores"
"Women over 65 who support Trump"
"Count Democrats in Congressional District 13"
"Young voters under 35 with cell phones"

Result: Returns voter count, sample records, and actions (Save as Segment, View in Search).

Campaign Data Queries (Tools)

For engagement history and cross-source analysis:

"What campaign data do we have?"
"Show me everything on john@example.com"
"Find our super supporters"
"Who appears in 5+ sources?"

Result: Returns campaign source listings, engagement records, or contact match data.

Combined Queries

For enriching voter results with campaign data:

"Which voters in district 5 have donated?"
"Find high-turnout Republicans who've volunteered"

Flow: First queries voters (QueryIR), then enriches with campaign data (tool).


Configuration

Required Environment Variables

# Claude AI (required)
ANTHROPIC_API_KEY=sk-ant-...
CB_CHAT_MODEL=claude-sonnet-4-20250514  # optional, defaults to sonnet

# Campaign Data (optional - enables campaign tools)
CBMODELS_BASE_URL=http://localhost:8001
CBMODELS_TENANT_ID=mi20

Integration Settings

Configure via the Integrations page (/integrations) or database:

Integration Setting Description
anthropic api_key Claude API key
anthropic model Model to use (claude-sonnet-4-20250514)
cbmodels base_url Campaign data API URL
cbmodels tenant_id Tenant slug (e.g., mi20)

API Endpoints

POST /api/cb-chat/message

Send a message to the AI chat.

Request:

{
  "message": "Find Republicans in Wayne County",
  "conversation_id": "optional-uuid",
  "history": [
    {"role": "user", "content": "previous message"},
    {"role": "assistant", "content": "previous response"}
  ]
}

Response:

{
  "message": "Finding registered Republican voters in Wayne County\n\n**Found:** 12,345 voters",
  "type": "results",
  "data": {
    "count": 12345,
    "samples": [...],
    "intent": "list"
  },
  "actions": [
    {
      "label": "Save as Segment",
      "action": "save_segment",
      "data": {"filters": {...}, "count": 12345}
    }
  ],
  "conversation_id": "uuid",
  "sql": "SELECT * FROM i360_voters WHERE party = 'R' AND county = 'Wayne' LIMIT 100"
}

GET /api/cb-chat/suggestions

Get example queries for the UI.

Response:

{
  "suggestions": [
    {"text": "How many voters do we have?", "category": "Overview"},
    {"text": "Find Republicans with high turnout scores", "category": "Targeting"},
    {"text": "What campaign data do we have?", "category": "Campaign"}
  ]
}

GET /api/cb-chat/health

Check chat system health.

Response:

{
  "status": "healthy",
  "anthropic_configured": true,
  "model": "claude-sonnet-4-20250514",
  "data_dictionary_loaded": true,
  "query_engine_available": true,
  "campaign_data_available": true
}


Example Queries

Voter Queries

Query What it does
"How many voters do we have?" Count all voters
"Find Republicans in Wayne County" Filter by party + county
"Women over 65 who support Trump" Gender + age + score filter
"Voters with email addresses in Detroit" Contact + city filter
"High turnout Independents" Score + party filter
"Young voters under 35 with cell phones" Age + contact filter

Campaign Data Queries

Query Tool Called What it does
"What campaign data do we have?" list_campaign_sources Lists all imported files with row counts
"Show me everything on john@example.com" lookup_campaign_contact All records for that email across sources
"Find super supporters" find_super_supporters Contacts in 2+ sources
"Who appears in 5+ sources?" find_super_supporters(min_sources=5) High-engagement contacts

Clarification Queries

When the chat needs more information:

Query Clarification
"Show me swing voters" "How would you like to define 'swing voter'?" with options
"Find engaged voters" May ask which score to use

Technical Details

Query Intermediate Representation (QueryIR)

The structured format for voter queries:

{
  "type": "query",
  "intent": "list",
  "explanation": "Finding registered Republican voters in Wayne County",
  "confidence": 0.95,
  "steps": [
    {"operation": "filter", "field": "party", "operator": "=", "value": "R"},
    {"operation": "filter", "field": "county", "operator": "=", "value": "Wayne"},
    {"operation": "limit", "value": 100}
  ]
}

Operations: - filter - WHERE clause (=, !=, >, <, >=, <=, LIKE, IN, IS NULL) - limit - LIMIT clause - order - ORDER BY clause (ASC/DESC)

Campaign Tools

Defined in cb_campaign_tools.py:

{
    "name": "list_campaign_sources",
    "description": "List all campaign data sources...",
    "input_schema": {"type": "object", "properties": {}, "required": []}
}

{
    "name": "lookup_campaign_contact",
    "description": "Find all campaign records for a contact...",
    "input_schema": {
        "type": "object",
        "properties": {
            "email": {"type": "string"},
            "phone": {"type": "string"}
        }
    }
}

{
    "name": "find_super_supporters",
    "description": "Find contacts in multiple sources...",
    "input_schema": {
        "type": "object",
        "properties": {
            "field": {"type": "string", "enum": ["email", "phone"]},
            "min_sources": {"type": "integer", "minimum": 2},
            "limit": {"type": "integer", "maximum": 100}
        }
    }
}

{
    "name": "enrich_with_campaign_data",
    "description": "Add campaign data to a list of contacts...",
    "input_schema": {
        "type": "object",
        "properties": {
            "emails": {"type": "array", "items": {"type": "string"}},
            "phones": {"type": "array", "items": {"type": "string"}}
        }
    }
}

Data Dictionary

Generated dynamically from i360_voters table with: - Column names and types - Value distributions (top values, counts) - Range statistics (min/max for scores, birth years) - Example queries for each column

This grounds Claude in actual data to prevent hallucination.

File Locations

File Purpose
src/api/routes/cb_chat.py Main chat endpoint, tool execution
src/api/services/cb_prompts.py System prompt template
src/api/services/cb_query_ir.py QueryIR models
src/api/services/cb_query_engine.py SQL generation + execution
src/api/services/cb_data_dictionary.py Dynamic data dictionary
src/api/services/cb_campaign_tools.py Tool definitions
src/api/services/campaign_data_service.py cbmodels API client
src/app/templates/cb_chat/index.html Chat UI

Segment Creation

Chat results can be saved as segments:

  1. User asks a voter query
  2. Chat returns results with "Save as Segment" action
  3. User clicks action, enters segment name
  4. Frontend calls POST /api/i360/save-as-segment
  5. Segment created with filters stored in filter_criteria JSON

Dynamic segments re-run the query on each execution. Static segments snapshot the voter IDs.


Performance Notes

  • Voter queries: Typically < 500ms (DuckDB is fast)
  • Campaign data: 20-50ms per tool call
  • Claude response: 1-3 seconds depending on complexity
  • Token usage: ~500-1000 tokens per query (input), ~200-500 tokens (output)

Troubleshooting

"Anthropic API key not configured"

Set ANTHROPIC_API_KEY in .env or via Integrations page.

"Campaign data not available"

Set CBMODELS_BASE_URL and CBMODELS_TENANT_ID in .env.

Query returns no results

  • Check if the voter database exists (db/i360.db)
  • Verify column names match the data dictionary
  • Try a simpler query to test connectivity

Claude returns clarification when not expected

  • The query may be ambiguous
  • Check if the field name is exact (e.g., "trump_support_score" not "trump score")
  • Provide more specific criteria

Version History

Version Changes
0.4.81 Added campaign data tools (Issue #62)
0.3.5 Initial AI Chat implementation