Records
text is the only required field. Everything else is a signal, and each one
earns its place:
Record order is part of the request
The same request under the sameoperating_point returns the same answer. That
guarantee is scoped to the request exactly as sent, and the order of records
is part of it: two requests that differ only in record order can keep different
records under a tight budget.
Send a stable order if you want stable results. The order your own store returns
them in is usually fine, as long as it does not change between calls for reasons
unrelated to the content.
Record kinds
A
kind outside this list is rejected with a 400, before the engine runs and
without a charge (rejected values).
tool_schema today behaves like any other record. A protected-kind
behaviour exists for it (never dropped, never lossily excerpted), but it is not
enabled on the operating points serving today. Label your schemas correctly
anyway: protection is enabled per operating point, and mislabelled records will
not benefit when it is.Records are sent every time
There is no upload step and no index. The records ride in on each request and are gone when it returns. That is what makes the service stateless. It is also what the cost model follows: the meter counts what you send, every time you send it. So if the same 40 documents accompany every call in a loop, that is 40 documents metered on every call. Worth designing around from the start: pre-filter with your retriever, or keep a working set. It is not worth working around by splitting one call into several, which costs more rather than less. The same records also count against your per-request ceiling today, which is live.Tasks
Nothing in the request pins a record today
required_record_ids is the field designed to do it: a constraint the engine
satisfies, as against importance, which is only a prior it weighs.
Keep sending required_record_ids for the records where a pin is a preference
rather than a requirement. It costs nothing and starts working without a change
on your side.
recent_prompts costs allowance without affecting selection
The field is accepted and is intended as a drift signal for the engage or stand
down decision.
Three mistakes that cost you quality
Labelling everything external_data
Labelling everything external_data
It is the default, so it is what you get if you never think about
kind. But
a tool result, a schema and a retrieved page are treated differently, and
flattening them into one role discards a signal that costs you nothing to
supply.Omitting record ids
Omitting record ids
Without
id, selected[].record_id is a server-generated value you cannot
join back to anything, so you can render rendered_context but you cannot
reason about which of your objects survived. Supply ids from the first call.Sending one record that concatenates everything
Sending one record that concatenates everything
Selection works at record granularity. One 200 KB record is one keep-or-drop
decision, so there is nothing to select between: you get pass-through or
nothing. Split along the boundaries that already exist in your system: one
tool result, one document, one turn.
What comes back
selected[] carries, per record: record_id, kind, title, source,
score, reason, excerpt, excerpt_format and selected_tokens.
-
excerptis extractive. It is built from your record’s own content. Nothing is paraphrased or summarised into new prose, and no model writes replacement text. What comes back is your material, or some of it. It is not guaranteed to be a byte-identical span of your input. Four things an excerpt may do, any of which breaks an exact-bytes assumption:Treatexcerpt_formatas an open set.plainis text; any other value indicates a re-encoding, and the safe fallback is to handle the value as text. If you need exact bytes, because you are resolving citations back to offsets or re-parsing an excerpt as JSON, join onselected[].record_idand use your copy of the record. That is what the ids are for, and it is the supported way to get byte fidelity. -
scoreis comparable within one response, and is not calibrated across requests or across operating points. Rank with it; do not threshold on an absolute value. -
reasonis open-ended. Show it in a debugging view, do not branch on it.
return_per_record: false if you only want rendered_context and the token
arithmetic: the response body gets materially smaller.