Skip to main content
Meridian’s AI agents help you explore and analyze your data using natural language. Instead of writing SQL manually, describe what you want to know and let the AI do the heavy lifting.

Agent Types

Meridian provides two specialized agents, each optimized for different tasks:

Query Agent

Generates DuckDB SQL queries from natural language. Perfect for data transformation and complex queries.

Analysis Agent

Explores your data using multiple tools. Provides insights, comparisons, and quality assessments.

Switching Between Agents

Use the segmented control at the top of the agent panel to switch modes:
<SegmentedControl
  value={mode}
  onChange={(value) => onModeChange(value as 'query' | 'analysis')}
  data={[
    { value: 'query', label: 'Query Agent' },
    { value: 'analysis', label: 'Analysis Agent' }
  ]}
/>

Query Agent: Natural Language to SQL

The Query Agent converts your requests into executable SQL commands.
1

Select Query Mode

Ensure “Query Agent” is selected in the agent panel.
2

Describe Your Query

Type a natural language description of what you want to do:Example requests:
  • “Show me the top 10 customers by revenue”
  • “Calculate average order value by month”
  • “Update all products in the Electronics category to increase price by 10%”
  • “Find orders where the amount is greater than $1000”
3

AI Generates SQL

The agent analyzes your request and generates one or more SQL commands:
// Agent streams a structured response
const response = await agent.streamObject(ctx, threadReference, {
  prompt: contextualPrompt,
  schema: z.object({
    commands: z.array(z.string()).min(1).max(10),
    description: z.string()
  })
})
You’ll see:
  • A description of what the query does
  • The generated SQL commands
  • Number of commands in the queue
4

Review and Execute

The generated SQL appears in the query editor. You can:
  • Review the SQL before execution
  • Modify it if needed
  • Execute it directly from the agent response

Query Agent Examples

Request: “Show me all sales from the West region in 2024”Generated SQL:
SELECT * FROM sales 
WHERE region = 'West' 
  AND order_date >= '2024-01-01' 
  AND order_date < '2025-01-01';
Description: Filters sales records for West region during 2024.

Analysis Agent: AI-Powered Exploration

The Analysis Agent uses multiple tools to explore your data and provide insights.
1

Select Analysis Mode

Switch to “Analysis Agent” in the agent panel.
2

Ask a Question

Describe what you want to understand about your data:Example questions:
  • “What patterns do you see in this sales data?”
  • “Compare Q1 and Q2 revenue performance”
  • “Check data quality issues in this dataset”
  • “Find correlations between product price and sales volume”
3

Agent Uses Tools

The agent automatically selects and executes relevant tools:
// Available tools for the Analysis Agent
tools: {
  queryDuckDB,           // Run SQL queries
  getTableSchema,        // Inspect table structure
  getSampleRows,         // View sample data
  createChart,           // Generate visualizations
  generateInsights,      // AI-powered insights
  firecrawlSearch,       // Web search
  scrapeWebPage,         // Extract web data
  extractWebPage,        // Structured extraction
  analyzeDataQuality,    // Quality assessment
  compareTables,         // Compare datasets
  getTableList           // List available tables
}
You see real-time updates as each tool runs.
4

Streaming Results

The agent streams its analysis back to you:
  • Tool usage appears with glowing indicators while running
  • Completed tools show checkmarks
  • Final analysis appears as markdown text

Tool Execution Visualization

When the Analysis Agent runs tools, you see live updates:
// Running tool - shows with animated gradient
<Box style={{
  background: 'linear-gradient(135deg, rgba(55, 114, 255, 0.1) 0%, rgba(138, 43, 226, 0.1) 100%)',
  border: '2px solid rgba(55, 114, 255, 0.3)',
  animation: 'toolPulse 2s ease-in-out infinite'
}}>
  <Text>Running: queryDuckDB</Text>
  <Text>Args: {{ query: "SELECT * FROM sales LIMIT 10" }}</Text>
</Box>

// Completed tools - shown with reduced opacity
<Box style={{ opacity: 0.7 }}>
  <Text>✓ getTableSchema</Text>
</Box>

Analysis Agent Capabilities

The agent can assess your data quality automatically:
// Tool: analyzeDataQuality
{
  tableName: "customers",
  rowCount: 1000,
  columnCount: 8,
  issues: [
    {
      column: "email",
      issue: "High null percentage",
      severity: "high",
      details: "35.5% of values are null"
    },
    {
      column: "customer_id",
      issue: "Duplicate values found",
      severity: "high",
      details: "Found 12 duplicate values"
    }
  ],
  qualityScore: 75
}
Common issues detected:
  • High percentage of null values
  • Duplicate IDs or keys
  • Empty string values in text columns
  • Type inconsistencies
Automatically examine table structure:
-- Tool: getTableSchema
DESCRIBE table_name;
Returns column names, types, and metadata.
Fetch representative data samples:
-- Tool: getSampleRows
SELECT * FROM table_name LIMIT 10;
Helps the AI understand your data structure.
The agent can search the web for context:
// Tool: firecrawlSearch
const result = await firecrawl.search(query, {
  limit: 10
})
Useful for enriching analysis with external data.
Compare two datasets:
// Tool: compareTables
{
  table1: "sales_2023",
  table2: "sales_2024",
  metrics: ["revenue", "order_count", "avg_order_value"]
}
Identifies differences and trends.

Conversation Threads

Both agents maintain conversation context through threads, enabling multi-turn interactions.

Thread Management

// Threads are automatically created and tracked
agentThreads: defineTable({
  userId: v.string(),
  tableName: v.string(),
  agentThreadId: v.string(),
  agentName: v.string(),
  title: v.optional(v.string()),
  createdAt: v.number(),
  updatedAt: v.number(),
  lastMessageAt: v.number(),
  lastMessageSummary: v.optional(v.string()),
  lastMode: v.optional(v.union(
    v.literal('query'), 
    v.literal('analysis')
  ))
})

Using Threads

1

Automatic Thread Creation

When you start a new conversation, a thread is created automatically. The title is generated from your first message.
2

Select Existing Threads

Use the thread selector to switch between previous conversations:
<Select
  placeholder="Select a conversation"
  data={threads.map(thread => ({
    value: thread.agentThreadId,
    label: thread.title || 'Untitled conversation'
  }))}
  onChange={onThreadSelect}
/>
3

Create New Threads

Click the + button to start a fresh conversation while keeping old threads accessible.
4

Context Preservation

Each thread maintains full context:
  • Previous messages
  • Tool executions
  • Generated queries
  • Analysis results

Streaming Responses

Both agents stream responses in real-time for immediate feedback.

Query Agent Streaming

// Streams structured object with partial updates
for await (const partialObject of objstream.partialObjectStream) {
  if (partialObject) {
    // Update UI with partial commands and description
    finalObject.commands = partialObject.commands || []
    finalObject.description = partialObject.description || ''
    
    // Live update in database for real-time collaboration
    await ctx.runMutation(api.agent_utils.updateAgentMessageRecord, {
      content: finalObject.description,
      commands: finalObject.commands
    })
  }
}

Analysis Agent Streaming

// Streams text and tool executions
for await (const part of stream.fullStream) {
  if (part.type === 'text-delta') {
    assistantText += part.text
    // Update UI with accumulated text
  }
  
  if (part.type === 'tool-call') {
    // Show tool starting
    toolSteps.push({
      tool: part.toolName,
      args: part.input,
      finished: false
    })
  }
  
  if (part.type === 'tool-result') {
    // Update tool with result
    toolSteps[index].result = part.output
    toolSteps[index].finished = true
  }
}

Best Practices

More specific requests yield better results:❌ “Show me sales data” ✅ “Show me total sales by region for Q1 2024”❌ “Update prices” ✅ “Increase all Electronics category prices by 10%”
Write requests as you would ask a colleague:✅ “What are the top 5 products by revenue?” ✅ “Find customers who haven’t ordered in 90 days” ✅ “Calculate the average order value for each customer segment”
Always review SQL before executing, especially for:
  • UPDATE statements
  • DELETE operations
  • Complex joins
  • Production data
For multi-step analysis:
  1. Start a thread
  2. Ask exploratory questions
  3. Build on previous answers
  4. Refine your understanding
  5. Keep the thread for future reference
  • Query Agent: When you know you need SQL or data transformation
  • Analysis Agent: When exploring, investigating, or need insights
You can switch between them in the same thread.

Agent Architecture

Meridian’s agents are powered by Google’s Gemini model with Convex Agent Component:
// Agent configuration
const query_agent = new Agent(components.agent, {
  name: 'Query Agent',
  languageModel: google_gemini.languageModel('gemini-2.5-flash'),
  instructions: `
    You are an assistant that writes DuckDB SQL queries.
    Respond with valid DuckDB SQL commands and descriptions.
  `,
  maxSteps: 4,
  tools: { queryDuckDB, getTableSchema, getSampleRows, ... }
})

Context Management

To stay within API limits, Meridian trims prompts to ~8000 characters:
const GEMINI_PROMPT_LIMIT = 8000

function trimToLimit(str: string, limit = GEMINI_PROMPT_LIMIT) {
  if (str.length <= limit) return str
  
  // Keep start and end, lose middle
  const keep = Math.floor((limit - 200) / 2)
  return str.slice(0, keep) + 
         '\n\n... [TRUNCATED FOR LENGTH] ...\n\n' + 
         str.slice(str.length - keep - 200)
}

What’s Next?

Create Charts

Visualize agent analysis results with interactive charts

Collaboration Tips

Work with your team in real-time on data analysis

Build docs developers (and LLMs) love