Records
text is the only required field. Everything else is a signal, and each one
earns its place:
Record kinds
A
kind outside this list is currently rejected with a 500 rather than a
400 (known rough edge).
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 will follow: metering is not live yet, and when it arrives it 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 now — 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 is not wired yet — send few, or none
The field exists so the engine can judge whether the conversation has drifted,
which feeds the decision to engage or stand down.
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 built from your record’s own content. Nothing is paraphrased or summarised into new prose, and no model writes replacement text. It is not guaranteed to be a byte-identical span of your input, and the difference is worth knowing before you build on it:If you need byte-identical spans — because you are resolving citations back to offsets, or re-parsing an excerpt as JSON — useselected[].record_idto look the record up in your own store and use your copy. That is what the ids are for. -
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.