MIA — Map of Intent and Action. A framework for mapping natural language into structured, executable actions for AI agents.
Project description
MIA
Map of Intent and Action
A framework for mapping natural language into structured, executable actions.
Built for AI agents that need to understand what users mean — not just what they say.
The Problem · The Solution · How It Works · Quick Start · Using MIA as RAG · Architecture Patterns · Production Guide · License
The Problem
You're building an AI agent that converts natural language into actions — SQL queries, API calls, tool executions. The user asks:
"How much did I sell yesterday?"
Sounds simple. But:
- "I" — is this a salesperson asking about their sales? A manager asking about their store? A director asking about the region?
- "sell" — which table?
sales?orders?transactions?receipts? - "yesterday" —
CURRENT_DATE - 1? What about timezone?
Now multiply this by hundreds of possible questions, business jargon that no LLM knows, and domain-specific logic that lives only in people's heads.
This is the gap MIA fills.
The Solution
MIA (Map of Intent and Action) is a structured framework that decomposes ambiguous natural language into executable actions through four columns:
| Question | Intent | Operation | Action |
|---|---|---|---|
| What the user said | What they actually meant | How to solve it | What to execute |
Each column progressively removes ambiguity:
"How much did I sell yesterday?"
↓
"Total store revenue for the previous day" ← Intent (disambiguated)
↓
"Sum of sales values filtered by date = yesterday" ← Operation (logic)
↓
SQL: SELECT SUM(amount) FROM sales WHERE date = CURRENT_DATE - 1 ← Action (executable)
MIA captures what schemas can't
Business domains are full of implicit knowledge. In a supermarket, "How many customers did I have?" doesn't mean counting rows in a customers table — it means counting receipts issued. No database schema tells you that. No LLM knows that. But MIA does:
| Question | Intent | Operation | Action |
|---|---|---|---|
| How many customers did I have yesterday? | Number of receipts issued yesterday | Count of receipts filtered by date = yesterday | SELECT COUNT(*) FROM receipts WHERE date = CURRENT_DATE - 1 |
How It Works
A MIA file is an Excel spreadsheet with two sheets:
Sheet 1: Identity
Metadata that defines the scope of this MIA.
| Field | Description |
|---|---|
| Name | Unique identifier (e.g., "MIA Sales") |
| Domain | Business area covered (e.g., "Sales Module") |
| Data Source | Where the agent executes actions (e.g., "PostgreSQL sales_db") |
| Agent | Which agent consumes this MIA (e.g., "sales-agent-v1") |
| Version | Document evolution control |
| Description | One-line scope summary |
Sheet 2: Map
The core table. Four columns, always the same — regardless of whether it's an orchestrator, a specialist agent, or a monolith.
Examples across different action types:
| Question | Intent | Operation | Action |
|---|---|---|---|
| How much did I sell yesterday? | Total store revenue yesterday | Sum of sales filtered by date | SQL: SELECT SUM(amount) FROM sales WHERE date = CURRENT_DATE - 1 |
| What's the status of my order? | Track current order status | Lookup order by user ID via logistics API | API: GET /api/v1/tracking/{order_id} |
| Let the team know the report is ready | Send notification about report completion | Identify team channel and compose message | Tool: send_slack_message(channel='#sales', text='Report ready') |
| I need a report on sales and inventory | Generate cross-domain report | Identify domains: Sales + Inventory | Route -> Sales Agent + Inventory Agent |
Same question, different profiles
When user profiles change behavior, duplicate the row — the Intent naturally differentiates:
| Question | Intent | Operation | Action |
|---|---|---|---|
| How much did I sell yesterday? | Store revenue yesterday | Sum filtered by store and date | ...WHERE store_id = @store |
| How much did I sell yesterday? | Salesperson revenue yesterday | Sum filtered by salesperson and date | ...WHERE salesperson_id = @user |
No extra columns. No conditional logic. Clean rows, each self-contained.
Quick Start
Prerequisites
- Python 3.10+
Install
git clone https://github.com/otavioturatti/mia-framework.git
cd mia-framework
pip install -e .
This installs the mia package and two CLI commands: mia-generate and mia-export. For development, use pip install -e ".[dev]" to include pytest.
1. Generate the template
mia-generate
This creates MIA_Template.xlsx — a styled spreadsheet with examples and empty rows ready to fill.
2. Fill in your MIA
Open the Excel file and:
- Fill the Identity sheet with your project metadata
- Fill the Map sheet with your question-intent-operation-action rows
3. Export to JSON
mia-export examples/MIA_Template.xlsx
This generates a MIA_Template_export.json where each row becomes a structured document:
{
"id": "mia_sales_001",
"content_for_embedding": "How much did I sell yesterday? — Total store revenue for the previous day",
"content_for_reranking": "Total store revenue for the previous day — Sum of sales values filtered by date = yesterday",
"metadata": {
"question": "How much did I sell yesterday?",
"intent": "Total store revenue for the previous day",
"operation": "Sum of sales values filtered by date = yesterday",
"action": "SQL: SELECT SUM(amount) FROM sales WHERE date = CURRENT_DATE - 1",
"domain": "Sales",
"version": "1.0"
}
}
Two embedding fields by design:
content_for_embedding— Question + Intent for primary vector search (richer semantic surface than question alone)content_for_reranking— Intent + Operation for cross-encoder reranking (captures the "what" and "how")
## Using MIA as RAG
MIA is designed to be consumed in three ways:
### 1. Design document
Fill the MIA **before** building the agent. It forces you to think through every intent, every edge case, every piece of business jargon.
### 2. Few-shot examples
Feed MIA rows directly into the agent's prompt as examples of how to map questions to actions.
### 3. RAG knowledge base
Embed the MIA and let the agent **search for similar questions at runtime**:
User asks: "What were my sales last week?" ↓ Semantic search on MIA embeddings ↓ Closest match: "How much did I sell yesterday?" ↓ Agent reads: Intent + Operation + Action ↓ Adapts the pattern to generate the correct response
The `content_for_embedding` field contains the **Question + Intent** — this is what gets vectorized. Including Intent means that duplicate questions with different profiles (e.g., "store revenue" vs. "salesperson revenue") produce **different vectors**. The rest comes back as **metadata** after retrieval.
### Why this works
You don't need to list every possible phrasing. RAG works by **semantic similarity**, not exact matching:
- "how much did I sell yesterday"
- "what was yesterday's revenue"
- "yesterdays sales total"
- "hwat did i sel yestrday" *(typos included)*
All of these land close to the same embedding vector. **One representative question per intent is enough.**
## Architecture Patterns
### Monolith — Single MIA
One agent, one database, one MIA. Simple.
User Question → MIA (RAG) → Agent → Database
### Microservices — One MIA per domain
Each service has its own agent, its own data, and its own MIA. The MIA defines the "world" each agent can see.
User Question → Orchestrator MIA → Routes to domain ↓ ┌───────────────────────┐ │ Sales MIA → Sales DB │ │ Stock MIA → Stock DB │ │ HR MIA → HR System │ └───────────────────────┘
The orchestrator itself uses a MIA — its Actions are **routing decisions**, not SQL or API calls.
### The framework is role-agnostic
The four columns stay the same whether you're building an orchestrator or a specialist. What changes is what you write in the **Action** column:
| Role | Action column contains |
|------|----------------------|
| Specialist agent | SQL queries, API calls, tool executions |
| Orchestrator | Routing decisions (`Route -> Agent X`) |
Same framework. Same table. The intelligence is in how you fill it — not in the structure.
## Tips for filling a great MIA
- **One representative question per intent.** Don't list variations — RAG handles semantic similarity.
- **Capture business jargon in the Intent column.** This is where "customers" becomes "receipts issued" and "revenue" becomes `SUM(amount) FROM sales`.
- **Duplicate rows only when profiles change behavior.** If the question "How much did I sell?" means different things to a manager vs. a salesperson, create two rows.
- **The person filling the MIA should know the business.** This isn't a dev task — it's a domain expert task (or both working together).
- **Start small, grow organically.** Begin with the 20 most common questions. The MIA grows as the agent encounters new patterns.
## Production Guide
MIA is a **knowledge authoring framework** — it structures and exports your domain knowledge. The embedding, indexing, retrieval, and generation steps are the responsibility of your pipeline. This section covers how to integrate MIA exports into a production RAG system.
### Retrieval strategy
Use **hybrid search** (vector + keyword) for best results. Pure vector search can miss domain-specific jargon, acronyms, and technical terms that keyword matching handles well.
User query ↓ ┌──────────────────────────┐ │ Vector search (top 20) │ ← uses content_for_embedding │ + BM25 keyword (top 20) │ ← uses metadata.question └──────────────────────────┘ ↓ merge + deduplicate ┌──────────────────────────┐ │ Cross-encoder reranker │ ← uses content_for_reranking └──────────────────────────┘ ↓ top 3-5 Agent receives context
### Metadata filtering
When the same question maps to different actions per user profile, **filter by metadata at query time** — don't rely on embedding distance alone:
```python
results = vector_store.search(
query_vector=embed(user_query),
filters={"domain": "sales", "agent": "sales-agent-v1"},
limit=10,
)
The metadata.domain, metadata.agent, and metadata.data_source fields exist for this purpose.
Confidence threshold and fallback
Not every user question will have a match in the MIA. Define a minimum similarity threshold and a fallback strategy:
SIMILARITY_THRESHOLD = 0.75 # tune for your domain
results = search(user_query)
if not results or results[0].score < SIMILARITY_THRESHOLD:
return "I'm not sure I understand. Could you rephrase your question?"
In production, ~30-40% of queries may not have a direct match. Plan for it.
Note: Similarity thresholds vary drastically between embedding models. A score of 0.75 with
text-embedding-3-largemeans something completely different than 0.75 withBGE-M3. Always calibrate your threshold against a test set of known question-intent pairs from your specific MIA and model combination.
Action safety
The action field in MIA contains templates, not executable code. Never interpolate user input directly into action strings. Always:
- Use parameterized queries for SQL (
WHERE id = $1, not string formatting) - Validate and sanitize any dynamic values before execution
- Enforce role-based access control at the execution layer — the MIA defines what actions exist, not who can run them
Feedback loop
A MIA is a living document that improves over time. Track these metrics:
- Unmatched queries — questions with no results above threshold (candidates for new MIA rows)
- Retrieval quality — Recall@K, MRR on a test set of known question-intent pairs
- Action success rate — did the generated action actually work?
Periodically review unmatched queries and add new rows to the MIA. The more rows, the smarter the agent.
Scaling beyond Excel
Excel works well up to ~500 rows. Beyond that, consider:
- CSV/TSV as primary format, imported into the Excel template for review
- Database-backed MIA with an export step to JSON
- Multiple MIA files split by domain (the microservices pattern)
The export.py module works with any .xlsx that follows the MIA structure, regardless of how it was created.
Project Structure
mia-framework/
├── README.md ← You are here
├── LICENSE ← MIT License
├── pyproject.toml ← Dependencies, metadata, CLI entry points
├── src/mia/ ← Installable Python package
│ ├── __init__.py
│ ├── exceptions.py ← Custom MiaError exception
│ ├── generate_template.py ← Generates the Excel template (mia-generate)
│ └── export.py ← Exports Excel → structured JSON (mia-export)
├── tests/ ← Unit tests (pytest)
│ ├── test_generate_template.py
│ └── test_export.py
├── .github/workflows/
│ └── ci.yml ← GitHub Actions CI
└── examples/
└── MIA_Template.xlsx ← Ready-to-use template with examples
License
MIT License — do whatever you want with it, just keep the attribution.
Created with love by Joao Otavio Turatti Barbosa
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mia_framework-1.1.1.tar.gz.
File metadata
- Download URL: mia_framework-1.1.1.tar.gz
- Upload date:
- Size: 23.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb160cc6bbc3bca4f09414af6eb7953cb98b82638db7983332ffc525ec84a7ea
|
|
| MD5 |
b29a7f989e068d7defdff6eea2dc89ca
|
|
| BLAKE2b-256 |
e90cb1ae360a1fea3300be32fe18e8313c83846e1782182a3ab33edd57aaacf5
|
File details
Details for the file mia_framework-1.1.1-py3-none-any.whl.
File metadata
- Download URL: mia_framework-1.1.1-py3-none-any.whl
- Upload date:
- Size: 16.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d54c76601921d91b39167819e518c20c1dd6d166f6434fa94357f044608fa4a
|
|
| MD5 |
24f4b542df0ca02af611cc963984bb83
|
|
| BLAKE2b-256 |
a35dd4bb29cc7a014870b20a81bcfae8064f4f995731eab0d2efaed27878c5d3
|