How to Build a Custom VikingBot with the OpenViking Python Client Library
You can build a custom VikingBot by instantiating the SyncOpenViking or AsyncOpenViking client, initializing the embedded backend, ingesting resources into the vector index, and implementing a chat loop that uses semantic search to retrieve context-aware answers.
The OpenViking repository provides a self-contained Python API that treats a vector-search-enabled knowledge base as a programmable "brain" for your bot. This guide walks through the architecture, initialization, resource ingestion, and conversation management required to build a production-ready VikingBot.
Understanding the OpenViking Architecture
OpenViking organizes functionality into four distinct layers. Understanding these layers helps you decide where to inject custom logic.
Client Layer: SyncOpenViking and AsyncOpenViking
The entry points for all bot interactions are the thin wrapper classes exposed in openviking/__init__.py.
SyncOpenViking(defined inopenviking/sync_client.py) provides blocking convenience methods ideal for scripts and Jupyter notebooks.AsyncOpenViking(defined inopenviking/async_client.py) offers fully asynchronous I/O for high-throughput services.
Both classes share identical method signatures, allowing you to switch from synchronous prototyping to async production without refactoring call sites.
Service Layer: LocalClient
Beneath the wrappers lies LocalClient (implemented in openviking/client.py). This class manages the embedded services including VikingFS, the vector index, and the summarization engine. The async client forwards all calls to LocalClient and handles lazy initialization of these services.
Workspace and Memory Layers
The Workspace Layer persists user-added resources, generated abstracts, and custom skills in the local filesystem under your configured path. The Memory Layer (accessed via session objects) records dialogue history and extracts long-term memories using the commit_session method.
Initializing the VikingBot Client
Start by creating a client instance pointing to a local workspace directory. The path stores the vector database, file cache, and skill definitions.
Synchronous Initialization
import openviking as ov
# OpenViking is an alias for SyncOpenViking
client = ov.OpenViking(path="./my_viking_data")
client.initialize() # Blocking call that starts embedded services
Asynchronous Initialization
from openviking import AsyncOpenViking
client = AsyncOpenViking(path="./my_viking_data")
await client.initialize() # Non-blocking for async event loops
The initialize() method starts the VikingFS storage engine and prepares the vector index backend (configured in openviking/storage/viking_vector_index_backend.py).
Ingesting Knowledge Resources
A VikingBot requires a knowledge base. OpenViking can ingest URLs, local files, or entire directories, automatically extracting text, building vector embeddings, and generating summaries.
Adding URLs and Local Files
Use add_resource() to ingest content. The method supports remote URLs and local filesystem paths.
res = client.add_resource(
path="https://raw.githubusercontent.com/volcengine/OpenViking/main/README.md",
wait=True, # Block until parsing and indexing complete
build_index=True, # Generate vector embeddings automatically
summarize=True, # Create a short abstract/overview
)
root_uri = res["root_uri"] # e.g., "viking://user/default/resources/..."
For local directories, pass a filesystem path to path and the library recursively processes supported file types.
Building the Vector Index
The build_index=True parameter triggers the vector database adapter (located in openviking/storage/vectordb_adapters/). This layer selects the appropriate backend—whether VolcEngine VDB, local Faiss, or HTTP remote—and persists embeddings for semantic search.
Implementing Custom Skills
Skills extend your bot's capabilities beyond retrieval. They are Python modules stored in the workspace that expose a run entry point.
skill_code = """
def run(context):
# context contains session state and client methods
return f"Processed: {context.get('last_query', 'none')}"
"""
client.add_skill(
data={
"name": "data_processor",
"description": "Processes the last user query",
"code": skill_code,
},
wait=True,
)
Skills reside in workspace/skills/<name>/SKILL.md and can be invoked by the LLM via the read_file tool, allowing dynamic tool use within conversations.
Managing Conversations and Memory
VikingBot maintains state through sessions—logical containers for conversation history and working memory.
Creating Sessions
session = client.create_session()
session_id = session["session_id"]
Sessions persist across method calls and store the message history used for context-aware responses.
Semantic Search and Retrieval
During a conversation, use find() or search() to retrieve relevant context from the knowledge base.
results = client.find(
query=user_message,
target_uri=root_uri,
limit=5,
)
The find method performs semantic vector search using the index built during resource ingestion. For hierarchical or filtered queries, use client.search().
Retrieve specific content with read():
if results.resources:
content = client.read(results.resources[0].uri, limit=1000)
Committing Long-Term Memory
After processing a turn, persist extracted memories to the knowledge base:
client.add_message(session_id, role="assistant", content=answer)
client.commit_session(session_id) # Extracts long-term memories
The commit_session method (implemented in openviking/async_client.py) analyzes the conversation history and stores salient facts as searchable memories for future sessions.
Complete VikingBot Implementation Example
Wrap the components into a reusable class suitable for production deployment:
import openviking as ov
class VikingBot:
def __init__(self, storage_path="./viking_workspace"):
self.client = ov.OpenViking(path=storage_path)
self.client.initialize()
self.session_id = self.client.create_session()["session_id"]
self.root_uris = []
def add_knowledge(self, path, **kwargs):
"""Ingest a URL or file into the knowledge base."""
res = self.client.add_resource(
path=path,
wait=True,
build_index=True,
summarize=True,
**kwargs
)
self.root_uris.append(res["root_uri"])
return res["root_uri"]
def add_skill(self, name, description, code):
"""Register a custom Python skill."""
self.client.add_skill(
data={"name": name, "description": description, "code": code},
wait=True,
)
def ask(self, question: str) -> str:
"""Process a user query and return an answer."""
self.client.add_message(self.session_id, role="user", content=question)
# Search across all knowledge bases
results = self.client.find(query=question, limit=5)
if results.resources:
top = results.resources[0]
content = self.client.read(top.uri, limit=800)
answer = f"Based on {top.uri}:\n{content}"
else:
answer = "I don't have information about that topic."
self.client.add_message(self.session_id, role="assistant", content=answer)
self.client.commit_session(self.session_id)
return answer
# Usage
if __name__ == "__main__":
bot = VikingBot(storage_path="./my_bot_data")
bot.add_knowledge("https://example.com/docs")
print(bot.ask("What are the main features?"))
This implementation encapsulates the full workflow: initialization, resource ingestion, skill registration, semantic search, and memory persistence.
Summary
- OpenViking provides
SyncOpenVikingandAsyncOpenVikingwrappers inopenviking/sync_client.pyandopenviking/async_client.pyto interact with the embedded knowledge base. - Initialization requires calling
initialize()to start the VikingFS storage, vector index, and summarization services. - Resource ingestion uses
add_resource()withbuild_index=Trueto automatically extract text, generate embeddings, and create searchable abstracts. - Custom skills extend bot capabilities through
add_skill(), storing Python modules in the workspace that expose arunentry point. - Conversation management relies on
create_session()for state isolation,find()for semantic retrieval, andcommit_session()to persist long-term memories.
Frequently Asked Questions
What is the difference between SyncOpenViking and AsyncOpenViking?
SyncOpenViking provides blocking methods suitable for scripts and interactive use, while AsyncOpenViking offers non-blocking I/O for high-throughput applications. Both classes share identical method signatures defined in openviking/sync_client.py and openviking/async_client.py, allowing seamless migration from prototyping to production without changing business logic.
How does OpenViking handle vector search?
OpenViking automatically builds vector embeddings when you ingest resources with build_index=True. The system uses adapters in openviking/storage/vectordb_adapters/ to interface with backends like Faiss or VolcEngine VDB. When you call find(), the query is embedded and matched against the index via openviking/storage/viking_vector_index_backend.py, returning semantically similar resources ranked by relevance.
Can I use OpenViking without external cloud services?
Yes. OpenViking is designed as a self-contained, embedded knowledge base. The LocalClient implementation in openviking/client.py runs VikingFS, vector indexing, and summarization entirely on your local machine using local storage and embedded models. You only need cloud services if you explicitly configure external vector database adapters.
How do I persist conversation memory across restarts?
Conversation history persists automatically because sessions and messages are stored in the local VikingFS workspace. To ensure long-term memories survive restarts, always call commit_session(session_id) after processing user turns. This method, implemented in openviking/async_client.py, extracts salient facts from the conversation and indexes them as searchable resources, making them available to future sessions even after the process restarts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →