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, thorough, or feature-gated experimental minimal. |
limit |
12 |
Candidate cap. Values are clamped to 1 through 100. |
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 candidates below this score inside each strategy, before fusion. |
source_filters |
none | Restrict by provenance kind. |
deadline |
profile default | "disabled" removes the time budget — offline use only. |
minimal is rejected unless
MEMHOUSE_EXPERIMENTAL_MINIMAL_RECALL=true. Its dual-lane experiment defaults
are runtime-owned; stored profile overrides remain limited to fast,
balanced, and thorough while the rollback path is evaluated.
Reading the response¶
{
"data": {
"profile": "balanced",
"profile_version": "f7-1",
"candidates": [
{ "id": "...", "fusion_score": 0.84, "rrf_score": 0.84 }
],
"contributed_strategies": ["semantic", "lexical", "entity_match"],
"empty_strategies": [],
"dropped_strategies": ["temporal"],
"degraded": true,
"degraded_components": ["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.- Each candidate's
strategieslist names the retrieval strategies that returned it. Itsfusion_scoreis the normalized, weighted fusion value used for ordering. It is not a probability or relevance percentage. Do not compare it across profiles or apply a relevance threshold to it.rrf_scoreis a deprecated alias for the same value. 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,invalid_result, orpartial_rankings. It contains no query or candidate text.degradedis the short form of the same news:truewhen a component was dropped or completed with a reason class, with the names indegraded_components. A droppedrerankeris the case to watch, because the results still look model-ordered when they are ordered by fusion alone.pre_rerank_remaining_msshows how much of the hard ceiling remained before reranking. Reranking receives the smaller of that value andMEMHOUSE_RETRIEVAL_RERANK_TIMEOUT_MS, and a run withdeadlineset to"disabled"is not capped at all.reserved_rerank_msshows how much of the deadline was kept back from the strategies for that stage. A reranking profile spends it last but reserves it first, so a slow strategy costs recall instead of costing the ordering.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.
entity_match weights each name your query mentions by how much it narrows the
scope. A name carried by most of the scope separates nothing, so it is ranked
low and, past
MEMHOUSE_RETRIEVAL_ENTITY_FREQUENCY_CEILING,
not ranked on at all. A query naming only such people appears in
empty_strategies rather than returning the scope in extractor-confidence
order. Add a distinguishing term — a place, an artifact, a date — to get it
back.
Check the flag, not the page
temporal runs only with an explicit as_of and returns dated text matches.
salience_recency does not read your query text and 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, or that every name you searched for is too common in it to rank on.
- 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. Add effort: "low", "medium", or "high" to use the bounded
read-only planner. It may select governed knowledge through stable profile and
lineage reads; it has no write tool. Use include_source_exact_recall and
include_source_semantic_recall to select either source tool independently.
include_source_recall: true enables both for compatibility. Set
include_stable_profile_recall: false to remove profile lookup from an ablation.
An effort level by itself never grants source reads. Every preset independently caps iterations, admitted
items, retrieval/model calls, query tokens, total admitted evidence tokens, and
elapsed time; exhaustion returns the best bounded evidence accumulated so far.
The response is the search payload plus answer, citations, abstained,
answer_confidence, answer_degraded, answer_context_count, and
answerer_prompt_tokens. The candidate list remains complete up to limit.
Only its bounded, final-ranked head enters the answer prompt.
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.
A degraded answer is not a low-confidence one
When the answering model call itself fails, answer_degraded names the
failure instead of null, abstained is true, and answer_confidence
is 0. answer states that the call failed — it is never the retrieved
statements presented as a conclusion. Those statements are still there, as
plain text in supporting_statements, so you lose nothing but the false
confidence.
Fixed retrieval for ask is restricted to governed knowledge. With a named
effort, a citation may instead name an authorized immutable source message and
the response includes its bounded excerpt and stable source metadata. Citation
ids the model invented or did not retrieve are removed; if none survive, the
answer becomes the empty abstention with answer_confidence 0.
citations remains a list of string ids for response compatibility. Resolve
each id against the governed evidence returned for the answer: ordinary
knowledge evidence uses its Knowledge id, while an authorized source item is
typed source_message in recall_evidence and uses its immutable Message id.
Knowledge admitted through the stable profile is re-read under the request's
current authorization and carries its scope_id, source_message_ids, and
bounded typed source_references; it is not detached profile text.
The exact read locks those governed knowledge rows inside its Account-scoped
transaction and rebuilds their references from current provenance. Every source
is then reauthorized as a Message or DocumentVersion in the requested scopes;
neither an erased message nor a mismatched provenance row can copy a source id
back from the earlier profile projection into recall_evidence.
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.