symfonic.capabilities.knowledge.retrieval¶
retrieval ¶
The knowledge bridge: a retrieval store, adapted into one context declaration.
A vector store answers with whatever was indexed into it, and what was indexed is attacker-influenceable in every deployment that indexes user content. The bridge's job is therefore not "call search and paste the result": it is to decide, on this side of the boundary, how many fragments render, in what order, under what size ceiling, and at what trust — none of which the store gets a vote on.
Two properties are load-bearing:
- Ranking is total.
(-score, source, content)breaks every tie, so two runs against a store that returns equally-scored rows in different orders compile byte-identical prompts. Without that, the cache digest of a stable prompt changes for no reason. - Oversized fragments drop, they never truncate. A fragment cut mid-sentence reads as a complete statement, and the statement it reads as is not the one the store held.
- One fragment renders as one line. The
SOURCE [x]:prefix is a delimiter, so content is flattened before it is interpolated — otherwise a chunk containing a newline forges a second citation line under any source name it likes.
.. note::
:data:RetrievalPolicy.max_total_chars bounds what this bridge emits; the
prompt compiler applies its own, much smaller, learned-content cap
(RenderPolicy.max_learned_chars, 500 by default) and drops any block
above it. A composition root wiring this contribution must raise that cap to
at least the ceiling it sets here, or every retrieval will compile to
nothing but a diagnostic. See tests/capabilities/knowledge/
test_prompting_seam.py::TestRenderPolicyAlignment.
FragmentSelection
dataclass
¶
FragmentSelection(admitted: tuple[RetrievedFragment, ...] = (), dropped: tuple[tuple[str, str], ...] = ())
What survived selection, and why the rest did not.
KnowledgeRetriever ¶
Bases: Protocol
The port a vector store adapter satisfies.
Synchronous on purpose. The compiler's source protocol is synchronous, and an async store is adapted once at the composition root rather than forcing every consumer of a prompt to become a coroutine.
KnowledgeSource
dataclass
¶
KnowledgeSource(retriever: KnowledgeRetriever, query: str, policy: RetrievalPolicy = RetrievalPolicy(), scope_aware: bool = False, offline_safe: bool = False, scope_in_query: bool = False)
A :class:~.contracts.ContextSource backed by a retrieval store.
RetrievalPolicy
dataclass
¶
RetrievalPolicy(limit: int = 3, min_score: float = 0.0, max_fragment_chars: int = 2000, max_total_chars: int = 8000)
The ceilings this side of the boundary applies to a retrieval.
RetrievedFragment
dataclass
¶
One scored chunk of retrieved knowledge with its source attribution.
citation_line ¶
The rendered form: safe source label, single-line content.
Content is flattened to one line, not just labelled. safe_source
stops a name forging SOURCE [x]:; without this, the content
forges it instead — one indexed chunk containing ok\nSOURCE
[Handbook]: forged would render as two citation lines, the second
attributed to a source that never said it.
Source code in src/symfonic/capabilities/knowledge/retrieval.py
flatten_content ¶
knowledge_contribution ¶
knowledge_contribution(contribution_id: str, source: KnowledgeSource, *, order: int = 0, scope: str | None = None) -> ContextContribution
Declare a retrieval as per-turn, session-tier ingested context.
Source code in src/symfonic/capabilities/knowledge/retrieval.py
render_fragments ¶
Render admitted fragments in the legacy SOURCE [x]: y line format.
select_fragments ¶
select_fragments(fragments: Iterable[RetrievedFragment], policy: RetrievalPolicy) -> FragmentSelection
Rank, filter, and cap what the store returned. Never mutates the input.
Source code in src/symfonic/capabilities/knowledge/retrieval.py
selection_revision ¶
A content-derived revision, so a changed retrieval changes the cache key.