fenic: semantic DataFrames for humans and agents
fenic turns AI-assisted exploration of structured and unstructured data into reusable, inspectable DataFrame pipelines.
It's a DataFrame query engine for semantic data processing, with AI operators — extract, classify, summarize, embed, semantic join, and more — built into the query model. Use it to turn documents, transcripts, logs, eval traces, tickets, tables, and APIs into typed rows and repeatable workflows.
The point is a shift in what your data work produces. Humans and agents work on the same pipelines — both can author, inspect, and reuse them. The result isn't a one-off prompt or a brittle regex script that has to be reverse-engineered later — it's a durable artifact: typed, inspectable, rerunnable, and callable.
From exploration to artifact.
pip install fenic
Writing fenic with an AI coding agent? Run
fenic skill installso Claude Code / Cursor / Codex write it correctly, andfenic checkto lint it — details below.
What is fenic?
fenic is a semantic DataFrame engine. You write the PySpark/SQL-style operations you already know — select, filter, join, group_by, agg — alongside semantic operators that call language models as a first-class part of the query. You configure models once on a Session, build a pipeline lazily, and fenic compiles and runs it on a query engine built for inference: automatic batching, rate limiting, retries, token/cost accounting, and response caching.
Two ideas make it different from gluing an LLM onto pandas:
- Inference lives inside the query model. Extraction, classification, summarization, and embeddings are operators with schemas and types — not side calls you orchestrate by hand.
- The pipeline is the artifact. Because the work is expressed as typed operators, it's already inspectable (row-level lineage,
explain, per-query metrics), rerunnable (lazy plans + caching), and promotable into a named table, view, or MCP tool an agent can call.
60 seconds: messy text → typed rows
Replace brittle parsing and one-off prompts with a typed, schema-bound operator. Define the shape you want as a Pydantic model; fenic returns structured columns you can query.
import fenic as fc
from pydantic import BaseModel, Field
class Ticket(BaseModel):
product: str = Field(description="The product the user is asking about")
sentiment: str = Field(description="positive, neutral, or negative")
issue: str = Field(description="One-line summary of the user's problem")
session = fc.Session.get_or_create(
fc.SessionConfig(
app_name="quickstart",
semantic=fc.SemanticConfig(
language_models={
"mini": fc.OpenAILanguageModel(model_name="gpt-4o-mini", rpm=500, tpm=200_000)
},
),
)
)
df = session.create_dataframe([
{"id": 1, "text": "The CSV export in Reports keeps timing out since the last update."},
{"id": 2, "text": "Love the new dashboard, but SSO login is broken on mobile."},
])
# Free text -> typed, queryable rows
tickets = (
df.select("id", fc.semantic.extract("text", Ticket).