Skip to content

API reference

Factories

deep_wiki_agent.factory.create_wiki_manager_agent

create_wiki_manager_agent(
    *,
    model: str | BaseChatModel,
    wiki_path: str | Path | None = None,
    backend: BackendProtocol | None = None,
    create_if_missing: bool = True,
    protect_raw: bool = True,
    enable_lint_tool: bool = True,
    virtual_mode: bool = True,
    embeddings: Embeddings | None = None,
    vector_store: VectorStore | None = None,
    search_k: int = 5,
    semantic_config: SemanticConfig | None = None,
    system_prompt: str | SystemMessage | None = None,
    tools: Sequence[BaseTool] = (),
    permissions: list[FilesystemPermission] | None = None,
    middleware: Sequence[AgentMiddleware] = (),
    **create_deep_agent_kwargs: Any,
) -> CompiledStateGraph

Create an agent that builds and maintains an OKF wiki.

The returned deep agent has read/write access to the bundle at wiki_path. Its operating instructions — bundle structure, frontmatter conformance, and the ingest / query / lint / bootstrap workflows — are in its system prompt, so they are in force from the first turn with nothing to load and nothing that can be skipped.

Parameters:

Name Type Description Default
model str | BaseChatModel

Model for the agent. Required — either a provider string (e.g. "anthropic:claude-sonnet-5") or a BaseChatModel.

required
wiki_path str | Path | None

Local directory of the OKF bundle, placed at the virtual root. Required unless backend is given.

None
backend BackendProtocol | None

Pre-built backend, used verbatim instead of the one this factory would assemble. Use it for non-local storage (state, store, sandbox). Mutually exclusive with wiki_path.

None
create_if_missing bool

When True (default), wiki_path is created if it does not exist, so the agent can bootstrap a new bundle into it. When False, a missing directory raises.

True
protect_raw bool

When True (default), writes under /raw are denied at the tool boundary. Source documents are immutable in the OKF wiki convention, and this makes that structural rather than a matter of the model's compliance. When False, no permission rules are applied at all.

True
enable_lint_tool bool

When True (default), attach the okf_lint tool so the agent can run the conformance check its prompt asks for (it has no shell). Works against any backend — local directory, state, store, or sandbox — since the linter walks it through the backend's own glob/read/edit methods.

True
virtual_mode bool

Confine the filesystem backend to the bundle directory, blocking ../ and ~/ escapes. Leave enabled unless you have a specific reason not to.

True
embeddings Embeddings | None

Embedding model enabling semantic search. Given together with vector_store, the agent gains semantic_ingest and semantic_search and a prompt section on keeping the index current. Requires the optional semantic extra.

None
vector_store VectorStore | None

Store the chunks are written to and searched in. A store configured for hybrid retrieval (dense + BM25) keeps its keyword half — the query reaches it as text rather than as a vector.

None
search_k int

Default number of passages a semantic search returns.

5
semantic_config SemanticConfig | None

Index configuration — chunking, which directories may be indexed, batch sizes, the manifest location. Defaults to :class:~deep_wiki_agent.semantic.index.SemanticConfig, which indexes the wiki's pages and the sources under /raw.

None
system_prompt str | SystemMessage | None

Override for the built-in manager prompt. Overriding it replaces the OKF instructions wholesale, so restate whatever of them you still want in force; the rest of the wiring is unchanged.

None
tools Sequence[BaseTool]

Extra tools, added alongside okf_lint and the built-in deepagents tools. This is where document loaders (PDF, docx, URL fetchers) belong — the library ships none, since which formats a wiki ingests is domain-specific.

()
permissions list[FilesystemPermission] | None

Override for the computed filesystem permissions. Passing this replaces the raw protection entirely.

None
middleware Sequence[AgentMiddleware]

Extra middleware passed through to create_deep_agent.

()
**create_deep_agent_kwargs Any

Any remaining create_deep_agent parameter (subagents, skills, checkpointer, store, interrupt_on, response_format, ...), passed unchanged.

{}

Returns:

Type Description
CompiledStateGraph

The compiled deep agent graph, ready for invoke / astream.

Raises:

Type Description
ValueError

If model is missing, if neither or both of wiki_path and backend are given, or if only one of embeddings and vector_store is given.

FileNotFoundError

If wiki_path does not exist and create_if_missing is False.

deep_wiki_agent.factory.create_deep_wiki_agent

create_deep_wiki_agent(
    *,
    model: str | BaseChatModel,
    wiki_path: str | Path | None = None,
    backend: BackendProtocol | None = None,
    not_found_message: str = DEFAULT_NOT_FOUND_MESSAGE,
    structured_output: bool = False,
    virtual_mode: bool = True,
    embeddings: Embeddings | None = None,
    vector_store: VectorStore | None = None,
    search_k: int = 5,
    semantic_config: SemanticConfig | None = None,
    system_prompt: str | SystemMessage | None = None,
    tools: Sequence[BaseTool] = (),
    permissions: list[FilesystemPermission] | None = None,
    middleware: Sequence[AgentMiddleware] = (),
    **create_deep_agent_kwargs: Any,
) -> CompiledStateGraph

Create an agent that answers questions from an existing OKF wiki.

The returned deep agent is read-only over the bundle: every write operation is denied by FilesystemMiddleware at the tool boundary, not merely discouraged by the prompt, so a consultation session cannot corrupt the knowledge base regardless of what the model is asked to do.

Its answering contract is closed-book over the bundle: it navigates the indexes and the link graph following the query protocol in its prompt, cites the pages it used, and — when the bundle does not cover the question — replies with not_found_message instead of falling back on the model's own knowledge.

Parameters:

Name Type Description Default
model str | BaseChatModel

Model for the agent. Required — either a provider string (e.g. "anthropic:claude-sonnet-5") or a BaseChatModel.

required
wiki_path str | Path | None

Local directory of the OKF bundle to consult, placed at the virtual root. Required unless backend is given. Must already exist: this agent never creates a bundle.

None
backend BackendProtocol | None

Pre-built backend, used verbatim instead of the one this factory would assemble — for a bundle held in a store, a sandbox, or any non-local storage. Mutually exclusive with wiki_path.

None
not_found_message str

The exact sentence the agent must return when the bundle does not contain the requested information. Override it to match your product's voice or language; the surrounding contract (no guessing, no outside knowledge, partial answers allowed and labelled) is unchanged.

DEFAULT_NOT_FOUND_MESSAGE
structured_output bool

When True, the agent answers with a :class:~deep_wiki_agent.schemas.WikiAnswer — answer, citations, not_covered, found — found under result["structured_response"], and its prompt gains a section explaining how to fill those fields. This is what makes "the wiki does not cover this" testable as found is False instead of a string comparison against not_found_message. The cost is the model's freedom to shape a prose answer to the question, which is why the free-text default stays the default. Mutually exclusive with passing your own response_format.

False
virtual_mode bool

Confine the filesystem backend to the bundle directory, blocking ../ and ~/ escapes.

True
embeddings Embeddings | None

Embedding model enabling semantic search. Given together with vector_store, the agent gains semantic_search — and only that: the ingestion tool writes, and this agent is read-only by construction. Building and refreshing the index is the manager's job, or a job for :func:~deep_wiki_agent.semantic.tools.ingest_semantic_index. Requires the optional semantic extra.

None
vector_store VectorStore | None

Store holding the index to search. Must be the one the bundle was ingested into: this agent never writes to it.

None
search_k int

Default number of passages a semantic search returns.

5
semantic_config SemanticConfig | None

Index configuration. Only its search-side fields matter here (over_fetch, snippet_chars, filter_builder); the ingestion fields are unused, since this agent never ingests.

None
system_prompt str | SystemMessage | None

Override for the built-in reader prompt. Note that the not-found contract, the query protocol and the field-filling instructions structured_output would add all live in that prompt: if you replace it, restate whichever you still want. The read-only enforcement, by contrast, is in the permissions and survives any prompt.

None
tools Sequence[BaseTool]

Extra read-only tools. Do not pass tools that can write to the bundle: the filesystem permissions cannot police tools they do not mediate.

()
permissions list[FilesystemPermission] | None

Override for the read-only permission set. Passing this replaces the deny-all-writes rule — the agent is then only as read-only as your rules make it.

None
middleware Sequence[AgentMiddleware]

Extra middleware passed through to create_deep_agent.

()
**create_deep_agent_kwargs Any

Any remaining create_deep_agent parameter (subagents, skills, checkpointer, store, response_format, ...), passed unchanged.

{}

Returns:

Type Description
CompiledStateGraph

The compiled deep agent graph, ready for invoke / astream.

Raises:

Type Description
ValueError

If model is missing, if neither or both of wiki_path and backend are given, if only one of embeddings and vector_store is given, or if structured_output is combined with an explicit response_format.

FileNotFoundError

If wiki_path does not exist.

Structured responses

deep_wiki_agent.schemas.WikiAnswer

Bases: BaseModel

A reader agent's answer, as data rather than prose.

The field descriptions are part of the schema handed to the model, so they are written as instructions to it rather than as notes to the reader of this file.

Opt in with create_deep_wiki_agent(..., structured_output=True). The answer then arrives as a WikiAnswer under result["structured_response"], and the reader's prompt gains a section explaining how the fields map onto the not-found contract.

The value is that found is False replaces a string comparison against not_found_message, which stops being a reliable test the moment the message is reworded or translated. The cost is the model's freedom to shape a prose answer to the question, which is why free text remains the default.

The flag sets response_format on the underlying deep agent, so it is mutually exclusive with passing response_format yourself — a ValueError if you pass both. Passing your own schema that way remains supported, and skips the prompt section, since that section describes WikiAnswer's fields specifically.

Permissions

deep_wiki_agent.backends.read_only_permissions

read_only_permissions() -> list[FilesystemPermission]

Deny every write on the whole virtual filesystem.

Enforced by FilesystemMiddleware at the tool boundary, so it holds even if the model is talked into ignoring its system prompt.

Returns:

Type Description
list[FilesystemPermission]

A one-rule permission list denying write everywhere.

deep_wiki_agent.backends.write_protect_permissions

write_protect_permissions(
    paths: list[str],
) -> list[FilesystemPermission]

Deny writes under the given path prefixes.

Parameters:

Name Type Description Default
paths list[str]

Directory paths to protect, e.g. ["/raw"]. Both the directory itself and everything below it are protected.

required

Returns:

Type Description
list[FilesystemPermission]

A one-rule permission list suitable for create_deep_agent, or an

list[FilesystemPermission]

empty list when paths is empty — a rule with no patterns would

list[FilesystemPermission]

restrict nothing while still looking like a restriction.

Linting

deep_wiki_agent.tools.lint.create_okf_lint_tool

create_okf_lint_tool(
    wiki_path: str | Path | None = None,
    *,
    backend: BackendProtocol | None = None,
) -> BaseTool

Build an okf_lint tool bound to one bundle.

The bundle — a local directory or a pre-built backend — is captured in the closure rather than taken as a tool argument, so the model cannot point the linter (and its fix writes) at an arbitrary location.

Parameters:

Name Type Description Default
wiki_path str | Path | None

Directory of the bundle this tool validates, on the local filesystem. Mutually exclusive with backend.

None
backend BackendProtocol | None

A pre-built deepagents backend (state, store, sandbox, or filesystem) holding the bundle this tool validates. Mutually exclusive with wiki_path.

None

Returns:

Type Description
BaseTool

A tool taking a single optional fix boolean and returning a

BaseTool

plain-text conformance report.

Raises:

Type Description
ValueError

If neither or both of wiki_path and backend are given.

deep_wiki_agent.tools.lint.run_okf_lint

run_okf_lint(
    wiki_path: str | Path | None = None,
    *,
    backend: BackendProtocol | None = None,
    fix: bool = False,
) -> dict[str, Any]

Validate an OKF bundle.

Parameters:

Name Type Description Default
wiki_path str | Path | None

Directory of the bundle to validate, on the local filesystem. Mutually exclusive with backend.

None
backend BackendProtocol | None

A pre-built deepagents backend (state, store, sandbox, or filesystem) holding the bundle, validated in place through its glob/read/edit methods. Mutually exclusive with wiki_path.

None
fix bool

When True, malformed timestamps are normalized in place (preserving the date they state whenever it is parseable) and absolute links and frontmatter paths whose target exists are rewritten relative to their page. False reports only.

False

Returns:

Type Description
dict[str, Any]

A dict with errors, warnings and fixes lists, each item a

dict[str, Any]

{"file": ..., "msg": ...} mapping.

Raises:

Type Description
ValueError

If neither or both of wiki_path and backend are given.

NotADirectoryError

If wiki_path is not an existing directory.

RuntimeError

If a backend operation the linter needs fails.

deep_wiki_agent.okf_lint.lint

lint(
    root: Path | Backend, *, fix: bool = False
) -> LintReport

Validate an OKF bundle.

Parameters:

Name Type Description Default
root Path | Backend

Bundle root directory, or an object implementing :class:Backend for bundles held somewhere other than the local filesystem (see :mod:deep_wiki_agent.tools.lint for the deepagents adapter).

required
fix bool

When True, two classes of defect are repaired in place and reported as fixes instead of errors: malformed timestamps, which keep the date they state whenever it is parseable, and absolute links and frontmatter paths whose target exists, which are rewritten relative to the page holding them. Nothing else is modified.

False

Returns:

Type Description
LintReport

An (errors, warnings, fixes) triple. Each finding is a

LintReport

{"file": ..., "msg": ...} mapping whose file is a

LintReport

bundle-relative path.

Source documents

Both require the optional documents extra (pip install "deep-wiki-agent[documents]"), which pulls in markitdown. Without it, the module still imports — the dependency is loaded inside the converter — and a conversion attempt raises ImportError with the install command.

deep_wiki_agent.tools.documents.create_read_document_tool

create_read_document_tool(
    wiki_path: str | Path | None = None,
    *,
    backend: BackendProtocol | None = None,
    root: str = RAW_DIR,
    max_chars: int = DEFAULT_MAX_CHARS,
    virtual_mode: bool = True,
) -> BaseTool

Build a read_document tool bound to one bundle.

Pass the result to a factory's tools::

from deep_wiki_agent import (
    create_read_document_tool,
    create_wiki_manager_agent,
)

manager = create_wiki_manager_agent(
    model="anthropic:claude-sonnet-5",
    wiki_path="./my-wiki",
    tools=[create_read_document_tool("./my-wiki")],
)

The bundle is captured in the closure and reads are confined to root, so the only thing the model chooses is which source document to open.

Parameters:

Name Type Description Default
wiki_path str | Path | None

Directory of the bundle this tool reads from, on the local filesystem. Mutually exclusive with backend.

None
backend BackendProtocol | None

A pre-built deepagents backend (state, store, sandbox, or filesystem) holding the bundle. Mutually exclusive with wiki_path. Must implement download_files, since converting a binary format needs the bytes rather than decoded text.

None
root str

Directory reads are confined to. Defaults to /raw, the OKF wiki's immutable source directory — the agent already reads the bundle's own markdown pages with its built-in file tools.

RAW_DIR
max_chars int

Truncate the returned markdown beyond this many characters, appending a note saying so. Guards the context window against a single oversized source.

DEFAULT_MAX_CHARS
virtual_mode bool

Confine the assembled filesystem backend to wiki_path. Ignored when backend is given.

True

Returns:

Type Description
BaseTool

A tool taking a single path string and returning the document as

BaseTool

markdown, or an ERROR: line explaining why it could not.

Raises:

Type Description
ValueError

If neither or both of wiki_path and backend are given, or if max_chars is not positive.

NotADirectoryError

If wiki_path is not an existing directory.

deep_wiki_agent.tools.documents.read_document

read_document(
    path: str,
    *,
    wiki_path: str | Path | None = None,
    backend: BackendProtocol | None = None,
    root: str = RAW_DIR,
) -> str

Read a source document from the bundle and return it as markdown.

Parameters:

Name Type Description Default
path str

Path of the document, relative to root or absolute within the bundle (paper.pdf, raw/paper.pdf and /raw/paper.pdf all name the same file).

required
wiki_path str | Path | None

Directory of the bundle holding the document, on the local filesystem. Mutually exclusive with backend.

None
backend BackendProtocol | None

A pre-built deepagents backend (state, store, sandbox, or filesystem) holding the bundle. Mutually exclusive with wiki_path.

None
root str

Directory reads are confined to. Defaults to /raw, the OKF wiki's immutable source directory.

RAW_DIR

Returns:

Type Description
str

The document converted to markdown, in full — unlike the tool built by

str

func:create_read_document_tool, this function never truncates.

Raises:

Type Description
ValueError

If neither or both of wiki_path and backend are given, or if path falls outside root.

NotADirectoryError

If wiki_path is not an existing directory.

ImportError

If the optional documents extra is not installed.

RuntimeError

If the document cannot be read or converted.

All of the following require the optional semantic extra (pip install "deep-wiki-agent[semantic]"), which brings langchain-text-splitters and includes the documents extra — ingestion has to read whatever sits in raw/, and a PDF there is the common case. No vector store is pinned: pass any LangChain VectorStore.

Enabling it is a matter of handing embeddings and vector_store to either factory. The manager then gets semantic_ingest and semantic_search; the reader gets semantic_search alone, since the ingestion tool writes and that agent is read-only by construction. Both gain the prompt section that explains their half — for the reader, that a hit is an entry point rather than an answer, and that citations name the page and never the excerpt.

deep_wiki_agent.semantic.tools.create_semantic_tools

create_semantic_tools(
    embeddings: Embeddings,
    vector_store: VectorStore,
    *,
    wiki_path: str | Path | None = None,
    backend: BackendProtocol | None = None,
    search_k: int = 5,
    config: SemanticConfig | None = None,
    virtual_mode: bool = True,
) -> SemanticTools

Build the ingestion and search tools for one bundle and one store.

Pass the result's tools to a factory, or let :func:~deep_wiki_agent.factory.create_wiki_manager_agent build them for you by handing it embeddings and vector_store directly::

from deep_wiki_agent import create_semantic_tools

semantic = create_semantic_tools(
    OpenAIEmbeddings(model="text-embedding-3-small"),
    QdrantVectorStore(...),
    wiki_path="./my-wiki",
)
semantic.ingest()          # deterministic, no agent involved

Parameters:

Name Type Description Default
embeddings Embeddings

Embedding model for the chunks and the queries.

required
vector_store VectorStore

Store the chunks are written to and searched in. A store configured for hybrid retrieval keeps its keyword half: the query reaches it as text rather than as a vector.

required
wiki_path str | Path | None

Directory of the bundle, on the local filesystem. Mutually exclusive with backend.

None
backend BackendProtocol | None

A pre-built deepagents backend (state, store, sandbox or filesystem) holding the bundle. Mutually exclusive with wiki_path. Must implement download_files if the bundle holds sources that need converting.

None
search_k int

Default number of passages a search returns.

5
config SemanticConfig | None

Index configuration — chunking, the directories ingestion may read, batch sizes, the manifest location. Defaults to :class:~deep_wiki_agent.semantic.index.SemanticConfig.

None
virtual_mode bool

Confine the assembled filesystem backend to wiki_path. Ignored when backend is given.

True

Returns:

Type Description
SemanticTools

The two tools and the index they drive.

Raises:

Type Description
ValueError

If neither or both of wiki_path and backend are given, or if search_k is not positive.

NotADirectoryError

If wiki_path is not an existing directory.

deep_wiki_agent.semantic.tools.ingest_semantic_index

ingest_semantic_index(
    embeddings: Embeddings,
    vector_store: VectorStore,
    *,
    wiki_path: str | Path | None = None,
    backend: BackendProtocol | None = None,
    patterns: Sequence[str] | None = None,
    tags: Sequence[str] | None = None,
    only_modified: bool = True,
    config: SemanticConfig | None = None,
    virtual_mode: bool = True,
) -> IngestReport

Index a bundle without an agent in the loop.

The same code path the semantic_ingest tool runs, exposed for a deterministic job — a cron entry, a post-commit hook, the rebuild step of a deployment — where nothing should depend on a model deciding to call a tool.

Parameters:

Name Type Description Default
embeddings Embeddings

Embedding model for the chunks.

required
vector_store VectorStore

Store the chunks are written to.

required
wiki_path str | Path | None

Directory of the bundle, on the local filesystem. Mutually exclusive with backend.

None
backend BackendProtocol | None

A pre-built deepagents backend holding the bundle. Mutually exclusive with wiki_path.

None
patterns Sequence[str] | None

Files, directories or globs to index. Defaults to the whole bundle.

None
tags Sequence[str] | None

Labels stored with every chunk this call indexes.

None
only_modified bool

Skip files that have not changed since the last run.

True
config SemanticConfig | None

Index configuration.

None
virtual_mode bool

Confine the assembled filesystem backend to wiki_path.

True

Returns:

Type Description
IngestReport

What the run did, as an

IngestReport

class:~deep_wiki_agent.semantic.index.IngestReport.

Raises:

Type Description
ValueError

If neither or both of wiki_path and backend are given.

NotADirectoryError

If wiki_path is not an existing directory.

deep_wiki_agent.semantic.tools.SemanticTools dataclass

The tools of one index, plus the functions underneath them.

Attributes:

Name Type Description
ingest_tool BaseTool

The semantic_ingest tool.

search_tool BaseTool

The semantic_search tool.

index SemanticIndex

The :class:~deep_wiki_agent.semantic.index.SemanticIndex both tools drive, for callers who want it directly.

ingest property

ingest: Callable[..., IngestReport]

The ingestion function, without the tool wrapper.

search property

search: Callable[..., list[dict[str, Any]]]

The search function, without the tool wrapper.

as_list

as_list() -> list[BaseTool]

Both tools, in the order an agent should be given them.

deep_wiki_agent.semantic.index.SemanticConfig dataclass

Everything about the index that the model does not get to choose.

Attributes:

Name Type Description
chunking ChunkingConfig

How a page is cut into chunks.

ingest_roots tuple[str, ...]

Directories ingestion is confined to. A pattern that resolves outside them is dropped — the patterns can come from the model, the roots cannot.

text_extensions tuple[str, ...]

Suffixes read as text through the backend.

document_extensions tuple[str, ...]

Suffixes converted with markitdown first. Requires the documents extra, which semantic pulls in.

max_files_per_call int

Ceiling on the files one ingest touches. When it trips, stale-chunk pruning is skipped for that call: a partial scan cannot tell a deleted file from one it never reached.

batch_size int

Documents per add_documents call.

deterministic_ids bool

Derive each chunk's id from its page, section and position, so re-ingesting a file updates its chunks instead of duplicating them. Turn it off only for a store that assigns ids itself, and accept that incremental ingest degrades with it.

trust_timestamps bool

Skip downloading a file whose size and modification time both match the manifest. Saves re-reading large sources; turn it off when a backend's timestamps are too coarse to trust.

over_fetch int

Multiplier on k when filters are applied client-side, so filtering does not empty the result set.

snippet_chars int

Longest snippet per hit in the text handed to the model. The full chunk always travels in the tool's artifact.

corpus_name str | None

Label written into every chunk's metadata.

filter_builder Callable[[dict[str, Any]], Any] | None

Converts the active filters into the store's own filter object, for server-side filtering. Left None, filters are applied client-side.

manifest_path str

Where the ingest manifest is kept, on the bundle's virtual filesystem. The default sits under a dot-directory, so the linter (which walks **/*.md) never sees it.

deep_wiki_agent.semantic.index.IngestReport dataclass

What one ingest did.

Attributes:

Name Type Description
files int

Files whose chunks were written.

skipped int

Files left alone because they had not changed.

chunks int

Chunks written.

table_chunks int

How many of those chunks are tables.

deleted int

Chunks removed from the store — superseded by a re-ingest, or belonging to a file that is gone.

matched_files list[str]

Paths of the files that were ingested.

errors list[str]

Per-file failures, as "<path>: <reason>". One unreadable source does not abort the run.

truncated bool

Whether max_files_per_call cut the file list short.

summary

summary() -> str

Render the report as the one-paragraph text a model reads.

deep_wiki_agent.semantic.chunking.ChunkingConfig dataclass

How a page is cut into chunks.

Fixed when the tools are built, never exposed to the model: chunk sizes are a property of the index, and an agent that could change them per call would produce an index whose parts do not compare.

Attributes:

Name Type Description
headers_to_split_on tuple[tuple[str, str], ...]

(marker, metadata key) pairs marking a section boundary, from the outermost level inwards.

chunk_size int

Target size, in characters, of a prose chunk.

chunk_overlap int

Characters repeated between adjacent prose chunks, so a sentence cut in two is still retrievable from either side.

prepend_header_path bool

Put the heading hierarchy at the top of each chunk's text. Costs a few tokens and buys a chunk that reads as self-contained once it is out of its page.

extract_tables bool

Index GFM tables as atomic chunks instead of letting the character splitter cut them.

table_max_chars int

Size past which a table is split, repeating its header and separator rows in every part.

deep_wiki_agent.semantic.index.SemanticIndex

Ingestion and search over one bundle and one vector store.

The bundle (through its backend), the embedding model, the store and the configuration are all fixed at construction. What a caller — or a model through a tool — chooses per call is only which files to ingest and what to search for.

__init__

__init__(
    embeddings: Embeddings,
    vector_store: VectorStore,
    backend: BackendProtocol,
    *,
    search_k: int = 5,
    config: SemanticConfig | None = None,
) -> None

Bind an index to a bundle and a store.

Parameters:

Name Type Description Default
embeddings Embeddings

Embedding model. Used directly only for a dense-only store, where embedding the query here avoids a round trip.

required
vector_store VectorStore

Where the chunks live. Hybrid dense + BM25 stores are detected and driven through their text query path, so the keyword half is not silently dropped.

required
backend BackendProtocol

Backend holding the bundle. Every read goes through it.

required
search_k int

Default number of results a search returns.

5
config SemanticConfig | None

Index configuration. Defaults to :class:SemanticConfig.

None

ingest

ingest(
    patterns: Sequence[str] | None = None,
    *,
    tags: Sequence[str] | None = None,
    only_modified: bool = True,
) -> IngestReport

Index the bundle, or the part of it the patterns name.

Parameters:

Name Type Description Default
patterns Sequence[str] | None

Directories, file paths or globs to index. Defaults to the configured ingest_roots, i.e. the whole bundle. Anything resolving outside those roots is dropped.

None
tags Sequence[str] | None

Labels written into every chunk's metadata, to filter searches by later.

None
only_modified bool

Skip files whose content has not changed since the last ingest. Pass False to force a full rebuild — after a change to the chunking parameters, for instance, which the manifest has no way to notice.

True

Returns:

Type Description
IngestReport

What the run did, as an :class:IngestReport.

search

search(
    query: str,
    *,
    k: int | None = None,
    area: str = "any",
    content_type: str = "any",
    path_contains: str | None = None,
    section_contains: str | None = None,
) -> list[dict[str, Any]]

Find the passages closest to a query.

Parameters:

Name Type Description Default
query str

What to look for, in natural language. Both halves of the search see it: the dense one embeds it, and the lexical one — when the store is hybrid — matches its terms.

required
k int | None

How many results to return. Defaults to the index's search_k.

None
area str

"wiki" for the wiki's own pages, "raw" for the source documents, "any" for both.

'any'
content_type str

"table", "text", or "any".

'any'
path_contains str | None

Restrict to files whose path contains this substring.

None
section_contains str | None

Restrict to sections whose heading path contains this substring.

None

Returns:

Type Description
list[dict[str, Any]]

One dict per hit — rank, score, text, file, section, content type

list[dict[str, Any]]

and the full chunk metadata — ordered best first.

Constants

Name Value Meaning
WIKI_ROOT "/" mount point of the OKF bundle
RAW_DIR "/raw" the immutable source-document directory
BUNDLE_SKELETON see below the layout the manager bootstraps
DEFAULT_NOT_FOUND_MESSAGE see below the reader's not-found answer
BUNDLE_SKELETON = (
    "AGENTS.md",
    "raw/",
    "wiki/index.md",
    "wiki/log.md",
    "wiki/assets/",
    "wiki/documents/",
    "wiki/entities/",
    "wiki/concepts/",
    "wiki/syntheses/",
)

DEFAULT_NOT_FOUND_MESSAGE = (
    "I could not find the requested information in the wiki knowledge base."
)

BUNDLE_SKELETON is the same layout section 1 of both prompts draws, as bundle-relative paths — directories carry a trailing slash, and each category directory also holds its own index.md. Use it to pre-create a bundle, or to check one you were handed; tests/test_prompt_paths.py uses it to verify that every path the prompts cite exists in the layout they describe.

Prompt templates

MANAGER_SYSTEM_PROMPT_TEMPLATE and READER_SYSTEM_PROMPT_TEMPLATE carry the agents' full operating instructions — bundle layout, frontmatter conformance, the workflows. They are exported so you can inspect or extend them rather than rewriting from scratch. They are str.format templates:

Template Placeholders
MANAGER_SYSTEM_PROMPT_TEMPLATE wiki_root, raw_dir, lint_block, semantic_block
READER_SYSTEM_PROMPT_TEMPLATE wiki_root, raw_dir, not_found_message, semantic_block, structured_output_block

lint_block is filled with LINT_TOOL_BLOCK when the okf_lint tool is attached, and with an empty string otherwise. semantic_block and structured_output_block work the same way: SEMANTIC_MANAGER_BLOCK / SEMANTIC_READER_BLOCK when semantic search is enabled, and STRUCTURED_OUTPUT_BLOCK_TEMPLATE when structured_output=True, empty strings otherwise. The blocks are plain strings, so an omitted one costs the agent nothing.

STRUCTURED_OUTPUT_BLOCK_TEMPLATE is itself a str.format template taking wiki_root and raw_dir — render it before substituting it in, since str.format does not recurse into the values it interpolates.

from deep_wiki_agent import READER_SYSTEM_PROMPT_TEMPLATE

prompt = (
    READER_SYSTEM_PROMPT_TEMPLATE.format(
        wiki_root="/",
        raw_dir="/raw",
        not_found_message="Nothing found in the knowledge base.",
        structured_output_block="",
    )
    + "\n\nAlways answer in Italian."
)

Warning

Passing system_prompt to either factory replaces the built-in instructions wholesale — for the reader, that includes the not-found contract and the query protocol, so restate what you still want in force. The read-only enforcement is separate: it lives in the filesystem permissions and survives any prompt.

Migrating from 0.1.x

Removed Replacement
build_wiki_backend FilesystemBackend(root_dir=wiki_path, virtual_mode=True)
normalize_mount —
bundled_skills_dir, okf_wiki_skill_dir, okf_lint_script —
OKF_WIKI_SKILL_NAME, DEFAULT_SKILLS_MOUNT —
skills_mount, skills_dir, extra_skills system_prompt= to change the instructions; create_deep_agent's own skills= passthrough for genuinely extra skills
scripts/okf_lint.py inside the installed skill the okf-lint console script, or python -m deep_wiki_agent.okf_lint