Search and ask¶
search returns ranked evidence. ask writes a cited answer over that evidence
or abstains.
search¶
curl -fsS -X POST http://127.0.0.1:4000/api/v1/search \
-H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{
"query": "who signs off on campaign copy",
"scope_path": "/marketing/social",
"profile": "balanced",
"limit": 12
}'
| Field | Default | Notes |
|---|---|---|
query |
"" |
The search text. A full question works; "phrase", -term, and or narrow it. |
scope_path |
"/poc" |
Selects this scope and its ancestors. |
profile |
"balanced" |
fast, balanced, or thorough. |
limit |
12 |
Candidate cap. |
include_cross_links |
off | Follow scope relations you are authorised for at both ends. |
as_of |
now | Read memory as it stood at a point in time. |
min_score |
none | Drop weakly fused candidates. |
source_filters |
none | Restrict by provenance kind. |
deadline |
profile default | "disabled" removes the time budget — offline use only. |
Reading the response¶
{
"data": {
"profile": "balanced",
"profile_version": "f7-1",
"candidates": [ ... ],
"contributed_strategies": ["semantic", "lexical", "entity_match"],
"empty_strategies": [],
"dropped_strategies": ["temporal"],
"disagreement": { "query_dependent_empty": false }
}
}
candidatesis already in the right order. Do not re-sort by a per-strategy score: those scores live in different spaces and comparing them degrades results.dropped_strategieslists strategies that did not run: they missed the deadline or a dependency was unavailable.semanticappears here when the embedder failed. Frequent deadline drops mean the profile's budget is too tight for your data size.retrieval_outcomesgives each component's status, deterministic drop reason, elapsed time, and remaining budget. The reason is one ofdisabled,deadline_exhausted_before_start,timeout,dependency_unavailable,provider_error, orinvalid_result. It contains no query or candidate text.pre_rerank_remaining_msshows how much of the hard ceiling remained before reranking. Reranking receives the smaller of that value andCARTULARY_RETRIEVAL_RERANK_TIMEOUT_MS.empty_strategieslists strategies that ran and matched nothing. That is a result, not a failure — but a strategy in this list did not vote on the order.
Check the flag, not the page
temporal and salience_recency never read your query text, so they do not
run for an ordinary text search: temporal needs an explicit as_of, and
salience_recency only serves a blank-query context read. When
disagreement.query_dependent_empty is true, none of the strategies that
do read your text produced a candidate, and the search returns an empty
page rather than the scope in recency order. A run in that state usually
means embeddings or entity mentions have not been rebuilt for the scope
yet.
- Account, scope authorisation, and lifecycle filtering already happened inside retrieval. You do not need to post-filter.
ask¶
curl -fsS -X POST http://127.0.0.1:4000/api/v1/ask \
-H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{
"question": "Who signs off on campaign copy?",
"scope_path": "/marketing/social"
}'
question is required. All search parameters apply; profile defaults to
thorough.
The response is the search payload plus answer, citations, abstained, and
answer_confidence.
Read the confidence, not only the answer
ask does not refuse. It answers with what the retrieved statements make
most probable and reports its certainty as answer_confidence, an integer
from 0 to 100. Below 50 the response also sets abstained, which marks the
answer as a lead rather than a conclusion. Both still carry citations, so
you can check the reasoning yourself.
An empty citation list with abstained means no statement survived to
ground an answer on. That is a report about the index, not a guess about
the subject. An answer invented from an empty candidate set is worse than
silence, and much harder to notice.
Retrieval for ask is restricted to knowledge items, so every citation is a
governed statement rather than a raw message. Citation ids the model invented
or did not retrieve are removed; if none survive, the answer becomes the empty
abstention with answer_confidence 0.
Choosing a profile¶
flowchart TD
Q{What is this read for?}
Q -->|"A user is waiting on an answer"| T["thorough — every strategy, reranked, 1500 ms"]
Q -->|"Interactive search box"| B["balanced — four strategies, 300 ms"]
Q -->|"Filling a context window"| F["fast — two strategies, 100 ms"]
Profiles inherit down the scope tree, nearest-wins, so a scope can be tuned without a global change. See Retrieval and context for the exact strategy sets and weights.
Time travel with as_of¶
as_of reads past belief-time: what the system believed then, not what was true
then.
What you will not find¶
Search and ask return no entity rows, names, aliases, surface forms, or ids. These internal caches improve matching without affecting authorization boundaries.
get_context is the one exception, and a narrow one: an entity card names
itself with a wording from its own scope. See
Context.