Skip to main content
A language model cannot read two million rows, and semantic search over them returns fragments, not totals. Neither gives an exact answer to “how many babies were named Mary since 1880?” This tutorial takes a different path: you load a large CSV as structured data, and the agent answers by generating a query the database runs over the whole table. The model writes the question into a query; the engine does the counting. The result is exact, whatever the row count. The dataset is the Social Security Administration’s national baby-name records: about 1.8 million rows in roughly 30 MB, which sits well within the 250 MB free-tier upload limit. The examples are in Python; the TypeScript and Go SDKs mirror them.

Prerequisites

  • The Meibel Python SDK: pip install meibel. See Installation.
  • Your API key in the MEIBEL_API_KEY environment variable.

1. Get the dataset

Download the Social Security Administration’s national baby-name records. This is public-domain data; SSA blocks scripted downloads from its own site, so Meibel hosts a copy for this tutorial. The file has about 1.8 million rows, with columns year, name, sex, and count.
curl -fsSL https://storage.googleapis.com/meibel-examples/tutorials/ssa-baby-names.csv.gz | gunzip > names.csv

2. Create a datasource for the CSV

The datasource is the container the table will live in. Create an empty one to load into.
import os
from meibel import MeibelClient

client = MeibelClient(api_key=os.environ["MEIBEL_API_KEY"])

datasource = client.datasources.create(
    name="Baby Names",
    description="SSA national baby-name counts by year",
)

print(datasource.id)

3. Upload the CSV and wait for it to land

Uploads are asynchronous, so upload the file and then wait for it to finish before ingesting. Triggering ingestion before the upload completes would ingest nothing.
upload = client.datasources.file_uploads.upload_content(
    datasource_id=datasource.id,
    files=open("names.csv", "rb"),
    files_name="names.csv",
)

# The progress stream ends once the file has fully landed.
for _event in client.datasources.file_uploads.stream_upload_progress(upload.upload_id):
    pass

4. Ingest into a queryable table

Ingestion registers the CSV as a table, one column per field, and infers each column’s type. It also generates a description for every column from the column’s own values, which the agent later uses to write accurate queries. Trigger it and poll until it completes (about 20 seconds for this file).
import time

client.datasources.ingest.trigger(datasource_id=datasource.id)

while True:
    status = client.datasources.ingest.get_status(datasource_id=datasource.id)
    if "completed" in str(status.status).lower():
        break
    time.sleep(3)
Confirm the table and its columns were registered.
for table in client.datasources.tables.list(datasource_id=datasource.id, include_columns=True):
    print(table.table_name, [c.column_name for c in table.columns])
# names ['count', 'name', 'sex', 'year']
Column descriptions are generated automatically, and you can refine them for clarity. Clear descriptions help the agent map a plain-language question to the right columns. See Managing datasource metadata.

5. Give an agent access to the table

Bind the datasource to an agent. The binding gives the agent a tool that queries the table, and instructions steer it toward using that tool for quantitative questions.
from meibel.models import CreateAgentDefinitionRequest

agent = client.agents.create(body=CreateAgentDefinitionRequest(
    display_name="Names Analyst",
    description="Answers quantitative questions about the baby-name data",
    instructions="You answer questions about the baby-name data. For counts, totals, rankings, and other aggregates, query the table so the numbers are exact. State the figure plainly.",
    datasources=[datasource.id],
))

print(agent.id)

6. Ask for exact numbers

Open a session and ask questions that only an exact computation can answer. Each one becomes a query the database runs over every row, so the totals are true totals rather than estimates.
from meibel.models import ChatMessageRequest

session = client.agents.sessions.create(agent_id=agent.id)

def ask(question):
    answer = client.agents.sessions.send_chat_message(
        session_id=session.session_id,
        body=ChatMessageRequest(user_message=question),
    )
    print(question)
    print(answer.response.message)
    print()

ask("How many name records are in the dataset?")
ask("What was the most popular girls' name in 1990, and how many babies received it?")
ask("How many babies have been named Mary in total since 1880?")
ask("Which five names were the most common across the 1980s?")
Each answer is computed across the full table. The count reflects every row, the total for “Mary” sums every matching year, and the ranking considers every name. Because the database performs the computation, the numbers stay exact whether the table holds a few thousand rows or a few million.

Why the answers are precise

Two things make this different from asking a model to read data. First, the model’s job is to translate the question into a query, not to tally the rows itself, so it does not approximate or lose track across a long file. Second, the query runs against the complete table, with no sampling and no retrieval of a subset, so an aggregate reflects the whole dataset. A question about totals or top-N returns the same answer the database would give a SQL analyst. This is the structured-data half of a datasource. For questions that live in prose rather than tables, an agent searches documents instead. A datasource can hold both, and an agent bound to it moves between them. See Datasources for how the two shapes work together.

What you built

You turned a 1.8-million-row public dataset into a queryable table, connected an agent to it, and asked for exact figures in plain language. The agent turned each question into a query the database answered over every row.

Datasources

How structured and unstructured data are queried and combined.

Managing Datasource Metadata

Refine column descriptions so queries stay accurate.

Sessions & Chat

Send messages, stream responses, and read session history.