Recursive Language Models Need Queries Agents Can Write
RLMs treat long context as a REPL, not a stuffed window. Let the agent write Qdrant queries, then inject tenant filters before search.

TL;DR
- Recursive Language Models (Zhang, Kraska, Khattab) treat the long prompt as an external object the model programs against, not a stuffed context window.1
- A hardcoded
search(query, limit=8)API takes that ability away. A raw Qdrant key gives it back and leaks tenants.- Let the agent write QQL (CTEs, hybrid RRF, filters). Parse it. Inject the tenant filter. Then execute.
- Isolation is AST rewrite, not a system prompt that says “only use your tenant.”
Recursive Language Models need a query the agent can write. Zhang, Kraska, and Khattab’s RLM setup puts the long prompt in a REPL and lets the model slice, grep, and recurse instead of swallowing ten million tokens in one window.1 Retrieval agents that only expose search(query, k) put the bottleneck back: the model cannot change prefetch, fusion, or filters.
Handing the agent a Qdrant API key is the other failure. Similarity search has no tenant. A prompt-injected agent that omits tenant_id reads the whole collection.
The path in between: the agent writes QQL, your host checks identity, inject_filter stamps the tenant on the AST, Qdrant only sees the isolated plan. The primitive still ships in qql-rs: inject_filter. The Go JWT gateway write-up is archived with qql-go.
For an agent to reason effectively, it needs runtime control over its retrieval environment. Yet, in multi-tenant enterprise software, handing an autonomous agent raw database credentials is a severe security failure. If the agent is prompt-injected or makes a logical error, it can effortlessly leak cross-tenant data.
The solution isn’t to throttle the agent with static API endpoints. The solution is to handle data governance at the query compilation layer using QQL (Qdrant Query Language) and AST-level filter injection. Similar to how Lossless Context Management (LCM)2 refines the RLM model by replacing GOTO-like model loops with structured, engine-managed control flows (such as hierarchical summary DAGs), the QQL Security Gateway imposes a structured, compiler-driven control plane over the agent’s database exploration.
Why a hardcoded search(query, k) starves an RLM
In a standard agentic toolset, developers routinely build rigid search hooks like this:
def search_knowledge_base(query: str, limit: int = 5):
# Hardcoded context pipeline
return client.search(collection_name="docs", query_vector=embed(query), limit=limit)This structural pattern fails the moment an agent faces a non-trivial reasoning task. If an RLM is investigating a distributed system outage, a simple semantic look-up falls short. To find the root cause, the agent needs the autonomy to orchestrate multi-layered retrieval plans:
- Hybrid Retrieval Engineering: Execute dense vector sweeps over raw system metrics, trigger sparse token matching on stack traces, and unify the disparate data via Reciprocal Rank Fusion (RRF).
- Contextual & Temporal Decay: Programmatically scale down historical logs while mathematically boosting real-time anomalies.
- Partitioned Routing & Aggregations: Query broadly across varying cluster boundaries, grouping entities by server class or instance id to avoid duplicate context pollution.
If every search strategy must be hardcoded by a developer, the agent cannot adapt to new tasks. To build smart agents, we need to let them query and explore the database dynamically.
In long-context tasks, static APIs force the system to stuff all logs and docs directly into the prompt. This triggers “context rot”, which makes the model perform worse as the prompt gets longer1. If we want agents to search effectively without wasting context space, we need a query language they can use to filter data before reading it.
Let the agent write QQL, not JSON tool args
Large Language Models excel at declarative code generation. They generate SQL, write clean Python, and parse structured syntax with high accuracy, whereas deep, deeply nested JSON payloads often break their JSON-schema formatting constraints.
QQL (Qdrant Query Language), originally created by Kameshwara Pavan Kumar Mantha as a SQL-like interface for Qdrant3, leverages this exact strength by exposing an expressive, declarative query syntax to Qdrant (the CLI ops post is historical; current command surface is qql.veristamp.in(opens in a new tab)). Instead of crafting heavily nested vector database payloads, an RLM writes its discovery logic directly as a query string:
WITH
dense_vectors AS (
QUERY 'network isolation anomaly' USING dense LIMIT 30
),
sparse_tokens AS (
QUERY 'sys_timeout_err' USING sparse LIMIT 50
)
QUERY '' FROM telemetry_logs LIMIT 5
PREFETCH (dense_vectors, sparse_tokens)
FUSION RRF
BOOST (
CASE WHEN environment = 'production' THEN score * 2.5
ELSE score
END
)With QQL provisioned as an active tool, the model operates a true Read-Eval-Print-Loop (REPL), a core design requirement of the RLM model where the prompt and database are treated as a dynamic, queryable external environment1. It can request an execution breakdown using EXPLAIN, evaluate its search performance, and calibrate its prefetches or mathematical boosting structures entirely in-flight based on real-time findings.
Inject the tenant after parse, before Qdrant
Exposing an expressive, flexible query syntax to runtime AI agents creates an immediate security challenge: What stops a compromised or prompt-injected agent from modifying its query to view data outside its scope?
-- Malicious payload generated by an injected agent
SCROLL FROM operational_logs WHERE tenant_id = 'target_enterprise_corp' LIMIT 100To eliminate this vulnerability, the QQL Security Gateway functions as a zero-trust compilation engine. The autonomous agent never directly manages raw database credentials or bypasses authorization. Instead, its interaction is mediated by the gateway and securely bound to a cryptographic JWT.
The system avoids unreliable string sanitation and regex parsers, processing incoming queries structurally via an Abstract Syntax Tree (AST) compiler:

1. Operation & Collection Containment
The gateway first screens the root query structure to verify that the operation type and target tables comply with the current user’s security level:
func (a *ASTInjector) EnforceOperation(node ast.ASTNode) error {
op := operationFromNode(node)
if !a.policy.IsOperationAllowed(op) {
return fmt.Errorf("operation %s not permitted for current token", op)
}
return nil
}If a read-only retrieval agent tries to inject a write command like DELETE or DROP, the operation check flags it immediately, terminating the execution window before any query compilation occurs.
2. Multi-Tenant AST Modification
Next, the engine pulls security data from the validated JWT token, such as tenant_id or assigned access_group, and traverses the AST nodes, automatically appending secure tenant boundaries to every query path.
func (a *ASTInjector) injectFilter(q *ast.QueryStmt) {
for _, f := range a.policy.InjectFilters {
value := f.Value
if f.FromClaim != "" && a.claims != nil {
value = extractStringClaimFromJWT(a.claims, f.FromClaim)
}
if value == "" {
continue
}
compare := a.buildFilterExpr(f.Field, f.Op, value)
q.QueryFilter = mergeFilters(q.QueryFilter, compare) // Appends via safe AND logic
}
// Recursively apply across Common Table Expressions (CTEs)
for i := range q.CTEs {
if q.CTEs[i].Stmt != nil {
a.injectFilter(q.CTEs[i].Stmt)
}
}
}This deep, structural mutation secures the query across all its expressions. If an agent attempts an evaluation bypass by targeting another tenant’s data:
SCROLL FROM docs WHERE tenant_id = 'competitor_corp' LIMIT 100The gateway’s structural mergeFilters function wraps the incoming logic, rewriting the final operational expression to evaluate safely:
func mergeFilters(existing ast.FilterExpr, injected ast.FilterExpr) ast.FilterExpr {
if existing == nil { return injected }
if injected == nil { return existing }
return ast.AndExpr{Operands: []ast.FilterExpr{existing, injected}}
}This transforms the running tree to enforce isolation parameters directly:
WHERE tenant_id = 'competitor_corp' AND tenant_id = 'acme_corp'Boolean reduction prevents any chance of a logic breach, ensuring data isolation stays intact regardless of the prompt input.
GOTO vs. Structured Control Flow: RLM, LCM, and the QQL Gateway
The academic debate between Recursive Language Models (RLMs)1 and Lossless Context Management (LCM)2 outlines a fundamental tension in agentic systems engineering:
- The RLM Model (“GOTO”): Exposing a raw REPL to the LLM provides maximum flexibility. The model decides how to partition tasks, recursively inspect snippets, and navigate its memory space dynamically1. However, this lack of standardized control flow can lead to termination failures, infinite loops, and unpredictability in production.
- The LCM Model (“Structured Control Flow”): LCM replaces model-managed symbolic recursion with engine-driven primitives. Using a hierarchical summary Directed Acyclic Graph (DAG) for lossless context compression and parallel operators like
LLM-Map, the runtime guarantees reliability, resource bounds, and deterministic memory layouts2.
The QQL Gateway bridges this dialectic for vector search. It gives the agent the declarative, REPL-like flexibility of QQL to formulate hypotheses and tune retrieval weights dynamically (the RLM philosophy), but wraps execution in a strict, compiler-enforced policy framework (the LCM philosophy).
Rather than hoping the model writes code that respects tenant boundaries or terminates cleanly, the gateway intercepts the AST. If the model attempts an unconstrained sweep or a logic bypass, the compiler automatically rewrites the execution tree, capping limits and merging tenant-isolation conditions.
Production Security Layout: policies.yaml
A declarative policies.yaml file configures the gateway rules. The rule set below scopes a runtime retrieval agent, allowing flexible data access while enforcing strict security constraints (see our QQL Gateway setup guide to inspect the full policies specification):
rules:
- match:
claims:
role: autonomous_agent_retriever
allow: [QUERY, SCROLL, SELECT, EXPLAIN] # Restricts mutations (e.g. INSERT, DELETE, DROP)
collections: ["telemetry_*", "knowledge_base"] # Glob pattern matching for collection boundaries
inject:
filters:
- field: tenant_id
from_claim: organization_uuid
op: "="
- field: classification_level
from_claim: permission_tier
op: "not_in"
limits:
max_limit: 25 # Caps memory usage against scraper loopsUnder this configuration:
- Collection Glob Isolation: The engine parses patterns like
telemetry_*. If an agent tries to target an unauthorized index, such as a core database config, the gateway blocks the request at compile time. - Dynamic Claim Extraction: The engine automatically resolves
from_claimlookups at runtime, matching them directly with the user’s active session state. - Resource Capping: The gateway enforces a strict
max_limitconstraint. If an agent requests large datasets to build an un-optimized context window, the gateway automatically caps theLIMITto protect resources:
func (a *ASTInjector) enforceLimit(limit *int) {
if a.policy.MaxLimit > 0 && *limit > a.policy.MaxLimit {
*limit = a.policy.MaxLimit
}
}The Dynamic Agent Loop in Action
Let’s walk through the runtime sequence of an agent query during an incident diagnosis workflow.
Phase 1: Initial Discovery query
The RLM generates its first QQL search payload to scan system errors:
QUERY 'auth timeout exceptions' FROM telemetry_logs LIMIT 50Phase 2: Gateway Interception and Injection
The gateway parses the query string, validates the token, and transforms the internal AST. It automatically injects the tenant credentials extracted from the active session context:
QUERY 'auth timeout exceptions' FROM telemetry_logs
WHERE tenant_id = 'enterprise_alpha'
AND classification_level NOT IN ('top_secret')
LIMIT 25 -- Capped automatically by max_limit policyPhase 3: In-flight Evaluation and Tuning
The RLM inspects the returned results. It detects that old entries from days ago are filling up the limits. It dynamically tweaks its retrieval parameters on the fly, adding temporal conditions and boosting critical microservices:
QUERY 'auth timeout exceptions' FROM telemetry_logs
WHERE timestamp >= 1781878400
BOOST (
CASE WHEN status_code = 500 THEN score * 2.0
ELSE score
END
)
LIMIT 5Because the gateway continuously mutates the query AST on every roundtrip, the agent retains absolute autonomy to iterate and refine its search strategy while remaining securely locked inside its compliance sandbox.
Observable Auditing of Agent Lifecycles
Because agents iteratively adjust their retrieval steps at runtime, maintaining a complete audit log is essential for evaluating performance and security. The gateway logs all incoming requests, tracking both the raw input query and the final, modified AST output:
{
"ts": "2026-06-20T05:12:04Z",
"subject": "acme_alice",
"email": "[email protected]",
"tenant_id": "acme",
"roles": ["reader"],
"operation": "SCROLL",
"collection": "docs",
"query": "SCROLL FROM docs LIMIT 20",
"filters_injected": ["org = 'acme'", "team IN ('engineering', 'all')", "access != 'confidential'"],
"limit_capped": 50,
"allowed": true,
"denied": false,
"latency_ms": 3,
"status": "allowed"
}This audit log gives engineering teams complete visibility into how the agent’s raw query intent translates into a secure database operation.
Direct Architectural Impact
| Perf Metric | Traditional API Tools | Dynamic QQL Gateway |
|---|---|---|
| Agent Exploration Scope | Low (Bound to static code functions) | High (Flexible, developer-level querying) |
| System Security Surface | High (Requires individual validation code per endpoint) | Low (Unified, centralized AST modification) |
| Context Window Health | Low (Returns broad datasets that waste tokens) | High (Optimized, pre-filtered data chunks) |
| Feature Velocity | Low (New workflows require backend adjustments) | High (Agents build data paths independently at runtime) |
Frequently asked questions
What is a Recursive Language Model?
An inference setup from Zhang, Kraska, and Khattab: the long prompt lives outside the window, and the model writes programs (and recursive sub-calls) over slices of it instead of swallowing the whole thing.1 It is not “RAG with a loop.” RAG injects chunks into the prompt. An RLM queries an environment.
Why can’t I just expose search(query, limit) to the agent?
That API cannot express prefetch, fusion, or a tenant-aware filter the model needs for the next hop. A fixed k is how context rot comes back. A raw database key is how the next hop leaks.
How do I let an RLM query Qdrant without cross-tenant leaks?
The agent writes QQL. A gateway verifies the JWT and calls inject_filter with the tenant from the token, not from the query text. Qdrant never sees the unfiltered statement. Walkthrough: Stop cross-tenant RAG leaks.
RLMs need a query language the agent can write, and a compiler that the agent cannot veto. Pair the sandbox with Code Mode. Isolation is inject_filter in qql-rs. The Go gateway post is archived with qql-go. Current docs: qql.veristamp.in(opens in a new tab)4.
References
Footnotes
-
Alex L. Zhang, Tim Kraska, and Omar Khattab. “Recursive Language Models.” MIT CSAIL. arXiv.24601. https://arxiv.org/abs/2512.24601(opens in a new tab) ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Clint Ehrlich and Theodore Blackman. “LCM: Lossless Context Management.” Voltropy PBC. arXiv.04050. https://arxiv.org/abs/2605.04050(opens in a new tab) ↩ ↩2 ↩3
-
Kameshwara Pavan Kumar Mantha. “QQL - Qdrant Query Language.” GitHub. https://github.com/pavanjava/qql(opens in a new tab) ↩
-
QQL (current). Language, CLI, and SDK docs. https://qql.veristamp.in(opens in a new tab) ↩