Scope knowledge per phase

Narrow which knowledge bases a phase can search, so each stretch of the call retrieves only what it needs

Knowledge bases attach to an agent, and by default every phase of a call can search all of them. On a call with distinct stages that is often more than you want: a caller-verification phase has no reason to search a thousand-page formulary, and a plan-specific phase should look only at that plan’s documents rather than every plan you support.

knowledge_base_filter on a conversation phase narrows the search to a subset of the agent’s attached knowledge bases, for that phase only.

How it works

Each conversation phase (the subagent node) takes an optional knowledge_base_filter: an array of attached knowledge-base ids (kb_…).

  • Set it, and every search_knowledge call made while that phase is active is restricted to the listed knowledge bases.
  • Leave it empty or absent, and the phase searches the agent’s full attached set — so existing flows are unchanged.

Enforcement is server-side. The worker sends the active phase’s list on every search, and the control plane intersects it with the knowledge bases actually attached to the conversation’s agent. A filter can therefore only ever narrow — it can never reach a knowledge base the agent is not attached to. Referenced ids are validated when you save a draft and when you publish, so a typo fails fast rather than at call time.

Set a filter

Attach every knowledge base you need at the agent level first, then narrow per phase in the flow graph. The filter lives in the phase node’s config; here it is inside a complete graph — a start node, one answering phase, and an end node. Save this as flow.json:

1{
2 "nodes": [
3 { "key": "start", "type": "start" },
4 {
5 "key": "answer_plan_a",
6 "type": "subagent",
7 "name": "Answer — Plan A",
8 "config": {
9 "instructions": "Answer the caller's questions about their plan.",
10 "knowledge_base_filter": ["kb_01hzqplanabenefits", "kb_01hzqplanaformulary"],
11 "exit_conditions": [
12 { "name": "caller_done", "description": "The caller says they have no more questions." }
13 ]
14 }
15 },
16 { "key": "end", "type": "end" }
17 ],
18 "edges": [
19 { "from": "start", "to": "answer_plan_a", "type": "default" },
20 { "from": "answer_plan_a", "to": "end", "type": "llm_condition", "condition": "caller_done" }
21 ]
22}

Set AGENT_ID and SPEECHIFY_API_KEY, then save the draft and publish:

$# Replace the draft flow graph (validated, not yet live)
$curl -X PUT "https://api.speechify.ai/v1/agents/$AGENT_ID/flow" \
> -H "Authorization: Bearer $SPEECHIFY_API_KEY" \
> -H "Content-Type: application/json" \
> -d @flow.json
$
$# Publish the draft as a new active version
$curl -X POST "https://api.speechify.ai/v1/agents/$AGENT_ID/flow/publish" \
> -H "Authorization: Bearer $SPEECHIFY_API_KEY"

A worked example: one agent, many plans

A health plan runs welcome calls for twenty plan types, each with hundreds of pages of benefit and formulary documents. Attaching all of them at the agent level means every search fans out across the whole corpus — slower and more expensive on every turn.

Structure the call as phases and filter each one:

  1. Verify identityknowledge_base_filter set to the caller-verification KB only, so a date-of-birth check never fans out across the formulary.
  2. Route to the caller’s plan — a tool_call or expression edge picks the plan.
  3. Answer for that plan — one phase per plan, each with knowledge_base_filter set to just that plan’s knowledge bases.

The answering phase now searches only the relevant plan’s documents, not the other nineteen.

Verify the scope took effect

Every search is logged on the conversation with the knowledge bases it actually ran against. The retrieval log’s searched_knowledge_base_ids shows the narrowed subset when the active phase carried a filter, and the full attached set otherwise — so you can confirm a phase searched exactly what you scoped it to. See Transcripts.

knowledge_base_filter narrows to a subset — an empty or absent filter means “search everything,” not “search nothing.” There is no per-phase switch to disable knowledge search entirely; scope each phase to the smallest relevant set instead. Automatic retrieval already skips trivial turns — the prefetch that runs ahead of the model does not search on a greeting or a bare acknowledgement — but the model can still choose to call the search_knowledge tool on any turn, so treat this as a cost reducer, not a guarantee that a phase never searches.