Natural language interface for OMOP CDM clinical databases using multi-agent workflows.
FastOMOP translates natural language clinical queries into OMOP CDM-compliant SQL queries through a two-stage pipeline:
- Semantic Agent: Maps clinical terms to OMOP concepts and evaluates query intent
- Database Agent: Generates and executes OMOP CDM queries with knowledge-augmented retrieval
All executions are traced to Langfuse for evaluation and prompt optimization.
- Python 3.13+
- UV package manager
- OMOP CDM database
- LLM provider (one of: Azure OpenAI, OpenAI, Anthropic, or Ollama)
- Langfuse account (optional — enable with
LANGFUSE_ENABLED=true)
FastOMOP uses Python's standard logging module. Set the verbosity via the
LOG_LEVEL env var (DEBUG | INFO | WARNING | ERROR | CRITICAL,
default INFO):
LOG_LEVEL=DEBUG uv run agno_fastomop runEach subcommand also accepts a CLI-level --log-level flag that takes
precedence over the env var:
uv run agno_fastomop run --log-level DEBUGInteractive CLI banners (welcome / prompt / goodbye) still go to stdout via
print() — they are part of the REPL UI, not diagnostic output.
# Clone repository
git clone <repository-url>
cd agno_fastomop
# Install dependencies with UV
uv sync
# Set up environment variables
cp .env.example .env
# Edit .env with your credentialsCreate a .env file with the following required variables:
# OMOP Database (required)
DB_PATH=/path/to/omop.duckdb # or see OMCP docs for postgresql/general db set-up
# OMCP server checkout (required — used to launch the MCP server)
OMCP_SERVER_DIR=/path/to/omcp_server
# Langfuse (optional — only enforced when LANGFUSE_ENABLED=true)
LANGFUSE_ENABLED=false
# LANGFUSE_PUBLIC_KEY=pk-lf-...
# LANGFUSE_SECRET_KEY=sk-lf-...
# LANGFUSE_HOST=https://cloud.langfuse.comAdd credentials for your chosen LLM provider:
Azure OpenAI:
AZURE_OPENAI_API_KEY=your_api_key
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
AZURE_OPENAI_DEPLOYMENT=your_deployment_nameOpenAI:
OPENAI_API_KEY=sk-...Anthropic:
ANTHROPIC_API_KEY=sk-ant-...Ollama:
OLLAMA_HOST=http://localhost:11434Edit config.toml to configure model providers and agent settings.
Azure OpenAI:
[models]
default_provider = "azure"
[models.providers.azure]
provider = "azure"
model_id = "gpt-4.1"
api_version = "2025-01-01-preview"
[agents.database]
model_provider = "azure"
[agents.semantic]
model_provider = "azure"OpenAI:
[models]
default_provider = "openai"
[models.providers.openai]
provider = "openai"
model_id = "gpt-4-turbo-preview"
[agents.database]
model_provider = "openai"
[agents.semantic]
model_provider = "openai"Anthropic:
[models]
default_provider = "anthropic"
[models.providers.anthropic]
provider = "anthropic"
model_id = "claude-3-5-sonnet-20241022"
[agents.database]
model_provider = "anthropic"
[agents.semantic]
model_provider = "anthropic"Ollama:
[models]
default_provider = "ollama"
[models.providers.ollama]
provider = "ollama"
model_id = "qwen2.5:7b"
[agents.database]
model_provider = "ollama"
[agents.semantic]
model_provider = "ollama"Mixed Providers (cost optimization):
[models]
default_provider = "azure"
[agents.database]
model_provider = "azure" # Use Azure GPT-4 for complex SQL generation
[agents.semantic]
model_provider = "ollama" # Use local model for concept mappingThe OMOP MCP server command is templated in config.toml and resolves
${OMCP_SERVER_DIR} from your .env at load time:
[omcp]
transport = "stdio"
command = "uv run --directory ${OMCP_SERVER_DIR} python src/omcp/main.py"Set OMCP_SERVER_DIR in .env to the path of your omcp_server checkout,
or override the whole command via the MCP_COMMAND env var.
Upload agent prompts to Langfuse before first use (only required when
LANGFUSE_ENABLED=true; otherwise the agents fall back to the local
prompt files under src/agno_fastomop/prompts/):
uv run agno_fastomop bootstrapuv run agno_fastomop runEnter queries at the prompt:
Enter your query: Patients taking metformin
Processing...
==================================================
The query found 127 patients taking metformin.
==================================================
Type exit to quit.
Create a JSON file with queries:
[
"Patients taking amlodipine 2.5 MG Oral Tablet",
"Distribution of patients by birth year",
"Patients with diabetes and hypertension within 90 days"
]Run batch processing:
uv run agno_fastomop run --batch queries.jsonResults are saved to queries_results.json with the following structure:
{
"metadata": {
"total_queries": 10,
"successful_queries": 9,
"failed_queries": 1,
"average_execution_time": 25.3
},
"results": [
{
"query_id": 1,
"query": "Patients taking metformin",
"status": "success",
"response": "The query found 127 patients taking metformin.",
"execution_time": 22.1
}
]
}Batch mode accepts multiple JSON formats:
Simple list:
["query 1", "query 2"]With metadata:
[
{
"query": "Patients with diabetes",
"category": "condition",
"expected_count": 100
}
]Dictionary format:
{
"queries": ["query 1", "query 2"]
}Launch the web-based interface powered by AgentOS:
uv run agno_fastomop webThe backend runs at http://localhost:7777 and exposes the API (workflows, agents). To use the web UI (chat, workflow execution, monitoring), you must connect it via the AgentOS Control Plane:
- With the backend running, open os.agno.com and sign in
- Click "Add new OS"
- Add your instance:
- Endpoint URL:
http://localhost:7777(orhttp://<your-ip>:7777for network access) - OS Name: e.g. "FastOMOP" or "Development"
- Endpoint URL:
- Use the Control Plane to chat with agents, run workflows, and view traces.
The Control Plane connects directly from your browser to your AgentOS runtime. Auto-reload is disabled on the backend to prevent DuckDB file locking conflicts.
User Query → Semantic Agent → Database Agent → Response
- Semantic Agent: Queries OMOP concept table to map terms to standard concepts
- Database Agent: Generates SQL using retrieved concepts and OMOP world model knowledge
All workflow executions are traced to Langfuse via the @observe() decorator:
- Full execution traces with timing
- Agent interactions and tool calls
- LLM prompts and completions
- MCP tool execution results
Access traces at your Langfuse dashboard for evaluation and debugging.
The database agent grounds its SQL generation on the system prompt
(prompts/database_agent.txt) and the live schema returned by the
OMCP MCP server. There is no separate vector knowledge base.
Agent prompts are stored in Langfuse and loaded dynamically:
semantic_agent: Concept mapping instructionsdatabase_agent: SQL generation guidelinessupervisor: Orchestration logic (alternative pattern)
Prompts can be versioned and A/B tested through Langfuse.
The repository uses the Astral stack:
- uv for package management and builds (
uv sync,uv build). - Ruff for linting and formatting (
uv run ruff check,uv run ruff format). - prek as a drop-in modern replacement for pre-commit (
uvx prek install,uvx prek run --all-files). - ty for type checking (alpha; advisory only —
uv run ty check src). - zensical for the documentation site (
uv sync --extra docs && uv run zensical serve).
Install everything in one go:
uv sync --all-extras
uvx prek installagno_fastomop/
├── src/agno_fastomop/
│ ├── agents/ # Agent definitions
│ │ ├── database.py
│ │ ├── semantic.py
│ │ ├── supervisor.py
│ │ └── factory.py
│ ├── workflows/ # Workflow orchestration
│ │ └── omop_workflow.py
│ ├── schemas/ # Pydantic models
│ │ └── schemas.py
│ ├── prompts/ # Local prompt templates (uploaded to Langfuse)
│ ├── observability/ # Langfuse integration
│ └── bootstrap.py # Uploads prompts to Langfuse
├── config.toml # Configuration
└── README.md
- Update the relevant prompt under
src/agno_fastomop/prompts/ - Re-run bootstrap to upload to Langfuse
- Test with sample queries
- Monitor execution in Langfuse
- Edit local prompt files in
src/agno_fastomop/prompts/ - Run bootstrap to upload to Langfuse
- Agents will use new prompts on next execution
# Run interactive mode with test queries
uv run agno_fastomop run
# Run batch mode with evaluation dataset
uv run agno_fastomop run --batch test_queries.jsonVerify configuration:
uv run python -c "from agno_fastomop.config import validate_config; validate_config(); print('Configuration valid')"This project is licensed under the Apache License 2.0 - see the LICENSE file for details.