UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

137 lines • 6.14 kB
import type { Passage, RetrieveOptions, Retriever } from '../../spi/retriever.js'; import { type TreeBudget, type TreeLlm } from './llm.js'; import type { DocumentTreeStore } from './store.js'; export interface TreeNavigationRetrieverOptions { store: DocumentTreeStore; llm: TreeLlm; /** * A tree whose outline (titles, pages, summaries) fits in this many characters is navigated in * **one** call that sees the whole outline — PageIndex's tree search, the variant measured in the * benchmark. Bigger trees are walked level by level (a beam). Default 40 000 (~10k tokens). */ singlePassMaxChars?: number; /** Nodes the navigator may expand or read per beam step. Default 4. */ beamWidth?: number; /** Beam levels, at most. Nodes still marked "expand" at the last level are read whole. Default 4. */ maxSteps?: number; /** Nodes returned per document, at most. Default 5. */ maxNodes?: number; /** * Hard per-document limits for one navigation. Defaults: 6 calls, 60k input tokens, 4k output * tokens, 60 s. Running out mid-walk returns what was chosen so far (the nodes last marked for * expansion are read whole) and records `stoppedBy: 'budget'`. */ budget?: TreeBudget; /** Characters of text per returned node. Default 12 000. */ maxPassageChars?: number; /** Units (pages/sections) read per returned node, at most. Default 25. */ maxUnitsPerNode?: number; /** Characters across all passages of one navigation (every document). Default 48 000 (~12k tokens). */ maxContextChars?: number; /** * {@link TreeNavigationRetriever.retrieve} with no first stage: documents navigated per query. When * more trees pass the filter, the LLM picks this many from their titles and descriptions * (PageIndex's document selection). Default 3. */ maxDocuments?: number; /** Trees shown to document selection, at most. Default 200. */ maxSelectionCandidates?: number; /** Documents navigated in parallel. Default 3. */ concurrency?: number; } /** One step of a navigation, for the audit trail. */ export interface NavigationStep { step: number; /** Node ids shown to the LLM this step. */ shown: string[]; /** Node ids it chose to open further. */ expanded: string[]; /** Node ids it chose to read. */ read: string[]; /** The LLM's stated reasoning, verbatim (capped). */ reasoning?: string; } /** A node navigation chose, with the path that led to it. */ export interface NavigatedNode { nodeId: string; title: string; /** Ancestors from the top level down, then the node itself. */ path: { id: string; title: string; }[]; pageStart?: number; pageEnd?: number; unitStart: number; unitEnd: number; } /** The auditable record of navigating one document. */ export interface DocumentNavigation { documentId: string; title?: string; mode: 'single-pass' | 'beam'; steps: NavigationStep[]; nodes: NavigatedNode[]; llmCalls: number; inputTokens: number; outputTokens: number; /** Why the walk stopped early, when it did. */ stoppedBy?: 'budget' | 'timeout' | 'maxSteps' | 'error' | 'not-found'; error?: string; } export interface NavigationResult { passages: Passage[]; documents: DocumentNavigation[]; /** Set when {@link TreeNavigationRetriever.retrieve} had the LLM pick the documents. */ selection?: { candidates: number; selected: string[]; reasoning?: string; error?: string; }; } export interface NavigateOptions { /** Access filter applied to every tree read (vector-store semantics). */ filter?: Record<string, unknown>; /** Overrides the retriever's per-document budget. */ budget?: TreeBudget; signal?: AbortSignal; } /** * Tree navigation retrieval ("vectorless", reasoning-based — PageIndex's method): an LLM reads a * document's table of contents (titles, page ranges, summaries — no body text) and picks the * sections that answer the question; their text comes back as passages, with the path taken and the * model's reasoning recorded so the choice is auditable. * * A small tree is navigated in one call over the whole outline. A large one is walked level by * level: the model sees one level of candidates, opens (`expand`) the promising ones and takes * (`read`) the ones that already answer, until it reaches leaves, runs out of `maxSteps`, or runs * out of its hard per-document {@link TreeBudget} — in which case it returns what it had. * * It pays off on **long structured documents** (regulations, contracts, filings, manuals) where * chunk ranking struggles among hundreds of look-alike passages; on short notes it costs seconds and * LLM spend for nothing. Compose it behind a first stage with {@link TwoStageRetriever}, or give it * to the agent as a tool with {@link createNavigateDocumentTool}. * * As a plain `Retriever`, `retrieve(query, { filter })` navigates the trees that pass `filter`: * all of them when there are at most `maxDocuments`, otherwise the ones the LLM picks from their * descriptions. */ export declare class TreeNavigationRetriever implements Retriever { private readonly options; readonly store: DocumentTreeStore; private readonly llm; constructor(options: TreeNavigationRetrieverOptions); retrieve(query: string, options?: RetrieveOptions): Promise<Passage[]>; /** {@link retrieve} with the audit trail: which documents, which nodes, why. */ search(query: string, options?: NavigateOptions): Promise<NavigationResult>; /** * Navigate the given documents (in parallel, up to `concurrency`) and return their passages in * the order given, within `maxContextChars`. Documents without a tree, or whose tree fails * `filter`, yield no passages and `stoppedBy: 'not-found'`. */ navigate(query: string, documentIds: readonly string[], options?: NavigateOptions): Promise<NavigationResult>; private walk; private passagesOf; } //# sourceMappingURL=navigate.d.ts.map