Agentic Operators
Overview
Agentic semantic operators run tool-using agents over a body of work (a The Corpus) in parallel. Unlike an LLM semantic operator — which applies one model call per record — an agentic operator gives each unit a full tool-calling agent that can take multiple steps, call tools (for example a sandboxed Python REPL), and reason before producing a result.
They are the right choice when a task is complex or ambiguous and benefits from more than a single LLM call — running code to compute an exact value, parsing a file, sweeping a codebase, or making a keep/drop decision that requires real investigation.
The single entry point is Corpus.agent:
result = corpus.agent(task="...", ops=["map", "reduce"], tools=[PythonREPLTool()])
You give it a corpus, a natural-language task, an ordered list of ops, and
optional tools. LOTUS plans how to split the work, runs the ops over the corpus, and
returns a Result.
The operators
An agentic pipeline is an ordered list of ops. Each op is either corpus → corpus (chainable) or corpus → answer (terminal):
Op |
Shape |
What each agent does |
|---|---|---|
|
Corpus → Corpus |
Transforms each unit in parallel; emits one output unit per input unit. |
|
Corpus → Corpus (subset) |
|
|
Corpus → single answer (terminal) |
Aggregates all current units into one result. Has tools, so aggregation is computed, not estimated. Must be the last op. |
How to think about them
Ops compose into a pipeline over the corpus. The current corpus is threaded through each op in order:
corpus --filter--> corpus --map--> corpus --reduce--> answer
map and filter are chainable (Corpus → Corpus); reduce is terminal (it collapses the corpus to a single answer), so it must come last. LOTUS validates the ordering.
Use ops standalone or together.
ops=["map"]maps and returns a corpus;ops=["filter"]returns the surviving subset;ops=["filter", "map", "reduce"]filters, then maps the survivors, then reduces to one answer. The default is["map", "reduce"].Tools are handled transparently. The tools you pass are available to every op; LOTUS describes them to the agents in a system-generated prompt, so your
tasknever has to mention them.You give one
task. A planner derives the per-op instruction (themap, thefiltercriterion, thereduce) plus sharding and parallelism. You can override any op’s instruction (see Agentic Map-Reduce).filter is an instantiation of map. Both run the same execution core over the corpus;
mapturns each unit’s agent result into a new unit, whilefilterreads that result as a keep/drop verdict and returns the surviving units. So anything true of map’s execution (parallelism, tools, strategies below) is true of filter.
Execution strategies
Each corpus op (map/filter) runs under a strategy that controls how much
context each per-unit decision gets — and how many agents run. The planner chooses one per
op from the task and a look at the corpus; you can override it per op via strategies=
(and contexts= for the shared background):
Strategy |
What it does / when to use |
|---|---|
|
One unit per agent, decided independently. Best for self-contained per-item work, or when units are large. |
|
Several units per agent (they see each other as context); the agent still returns
one result per unit. Best when the criterion is comparative/relative (“the
strongest”, dedup), or when units are tiny and many (batching cuts cost). Uses
|
|
One unit per agent, plus a shared background injected into every agent (a reference definition, schema, or keep/drop exemplars). Best when every unit is judged against the same fixed background. |
# Let the planner choose (default), or pin a strategy explicitly:
corpus.agent(task="Keep the strongest arguments.", ops=["filter"],
strategies={"filter": "batched"})
corpus.agent(task="Keep files that use the deprecated API.", ops=["filter"],
strategies={"filter": "shared_context"},
contexts={"filter": "Deprecated: foo(x, y). A call counts if it invokes foo()."})
Choosing between agentic and LLM operators
Reach for agentic operators when the per-item work needs tool calls or multi-step reasoning, or when the task is open-ended. Reach for LLM semantic operators (sem_map, sem_filter, sem_agg, sem_join, …) when the task is well defined and one model call per record suffices — they invoke far fewer calls and are transparently optimized by the query engine.
In this section
The Corpus — the input to every agentic operator, and its loaders.
Agentic Map-Reduce — the
map→reducepipeline, the API, and tools.Agentic Map-Reduce Examples — worked map-reduce examples (expense reports, a codebase sweep).
Agentic Filter — the agentic
filterop, with an example.