Tarski/ docsJoin the waitlist

The agent query relay

How an LLM agent reads the worldview — proposal, decision, admitted intent, governed effect, and results that re-enter as gated evidence.

A model never queries anything directly. It requests named declared queries; ordinary Tarski decision rules ratify or deny; an admitted intent executes the read at a pinned head under the interface’s projection; and the result re-enters the lineage as an observation feeding the next turn. Reads get the same governance as writes — and the whole path replays deterministically.

The read loop

  1. Request. The platform composes a reserved query-request section into each relay-participating interface’s response schema. The model’s reply may carry an array of (query_name, bindings) — only agent-exposed declared queries are nameable; free-form query text is refused.
  2. Capture → proposal. The runtime stamps a relay envelope on the model-call result and derives typed proposal atoms with no mapper involved: proposal.query_requested.<query_name>(…) carrying the binding values.
  3. Decision. Your rules approve or deny — joining on binding values, sessions, consent facts, budgets. Approval is never implicit; even trivial approval is an explicit, auditable rule.
  4. Admission. intent.query_execute derives only from committed decisions. The admitted intent pins the full execution contract: query digest, canonical bindings, head, and projection identity. Pinned query IR is durably retained, so hot-reload between admission and execution can never swap in a different query.
  5. Execution. The worldview.query effect runs the engine at the pinned head under the interface’s projection. Success appends a worldview.query_result observation; failure appends typed worldview.query_failed — so rules can react to failed reads.
  6. Results, gated. queryresult.outcome rows are trusted lifecycle metadata (session, request id, digests, outcome class — never row contents). The data rows queryresult.<query_name> are fallible-relay-class: no trusted fact without decision.accepted.*, no intent without decision.approved.*. Even one bit of “the query was non-empty” cannot touch the world without an approval.

Independent reads batch into one turn and execute concurrently at the same head. The proposal and result families are system-owned — app declarations or mapper writes into them are load errors, and externally submitted observations claiming those kinds are rejected at ingress. Identity is replay-deterministic (request ids derive from the capture digest), so a clean-store replay reproduces an entire multi-hop deliberation with identical digests.

Wiring it in the manifest

[capabilities]
worldview_query = true

[capabilities.intents."intent.query_execute"]
capability = "worldview.query"
identity_keys = ["session_id", "request_id"]

Relay-participating llm.complete bindings name their interface (binding the session contract and projection); consultation bindings set model_consultation = true — the narrow carve-out that lets trusted queryresult.outcome lifecycle rows drive a follow-up model turn.

Budgets and denials

Budgets are decision rules, not configuration magic:

-- two reads per session; deny with a reason a PM can read
decision.rejected.triage.query_execute(session, req, "budget_exhausted") :-
  proposal.query_requested.impacted_services(session, req, root),
  triage.session_query_count(session, n),
  n >= 2.

A backstop invariant (admitted query intents per session ≤ budget) makes over-approval an impossible state. Denial reports flow into the session’s next-turn context, so the model learns a read was refused — with the reason.

Authoring patterns that matter

  • The consultation guard. Any model-consultation turn that re-derives over queryresult.outcome must be guarded on “no unsettled requests in the session” — otherwise the changing results section re-admits the producing turn every round and the agent loops.
  • Compound queries over multi-hop. Queries are full Datalog programs; pre-join in one declared query instead of chaining reads.
  • Prove the loop with fixtures. tarski fixture template query-relay <query> mints a complete request/result lifecycle; digest refresh keeps recorded identities honest. The incident-response example ships fixtures for approved, denied, budget-exhausted, and failed reads.

Model context, curated

Each turn’s context assembles in prefix-stable order — prompt bundle, ontology schema section (“the menu of reads”), the declared ambient world slice, the session’s query-results section (results and denials), request arguments — every section digest-stamped and joined into the provider request digest. New results change only the tail, keeping provider prompt caches warm. Studio’s model-turn context manifest and GET /v1/queries/turns/{turn_id}/context disclose exactly what a turn saw.