# How to Build a Custom VikingBot with the OpenViking Python Client Library

> Build a custom VikingBot using the OpenViking Python client library. Instantiate clients, ingest resources, and implement a chat loop for context-aware answers.

- Repository: [Volcengine/OpenViking](https://github.com/volcengine/OpenViking)
- Tags: how-to-guide
- Published: 2026-03-08

---

**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`](https://github.com/volcengine/OpenViking/blob/main/openviking/__init__.py). 

- **`SyncOpenViking`** (defined in [`openviking/sync_client.py`](https://github.com/volcengine/OpenViking/blob/main/openviking/sync_client.py)) provides blocking convenience methods ideal for scripts and Jupyter notebooks.
- **`AsyncOpenViking`** (defined in [`openviking/async_client.py`](https://github.com/volcengine/OpenViking/blob/main/openviking/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`](https://github.com/volcengine/OpenViking/blob/main/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

```python
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

```python
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`](https://github.com/volcengine/OpenViking/blob/main/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.

```python
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.

```python
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

```python
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.

```python
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()`:

```python
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:

```python
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`](https://github.com/volcengine/OpenViking/blob/main/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:

```python
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 `SyncOpenViking` and `AsyncOpenViking` wrappers in [`openviking/sync_client.py`](https://github.com/volcengine/OpenViking/blob/main/openviking/sync_client.py) and [`openviking/async_client.py`](https://github.com/volcengine/OpenViking/blob/main/openviking/async_client.py) to 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()` with `build_index=True` to 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 a `run` entry point.
- **Conversation management** relies on `create_session()` for state isolation, `find()` for semantic retrieval, and `commit_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`](https://github.com/volcengine/OpenViking/blob/main/openviking/sync_client.py) and [`openviking/async_client.py`](https://github.com/volcengine/OpenViking/blob/main/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`](https://github.com/volcengine/OpenViking/blob/main/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`](https://github.com/volcengine/OpenViking/blob/main/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`](https://github.com/volcengine/OpenViking/blob/main/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.