How to Implement Intent Analysis for Query Preprocessing in OpenViking

OpenViking performs intent-driven hierarchical retrieval by analyzing user queries through the IntentAnalyzer class, which assembles session context, calls a VLM, and produces a structured QueryPlan to drive downstream retrieval.

The volcengine/OpenViking repository implements a sophisticated retrieval system that preprocesses user queries through intent analysis before executing hierarchical searches. By understanding whether a user seeks memories, resources, or skills, the system optimizes retrieval paths through typed query decomposition. This article explains how to implement and customize this intent analysis pipeline using the actual source code from the OpenViking project.

Understanding the IntentAnalyzer Architecture

The IntentAnalyzer class in openviking/retrieve/intent_analyzer.py serves as the central component for query preprocessing. It transforms raw user input into a structured retrieval plan by coordinating context assembly, LLM interaction, and response parsing.

Core Responsibilities

The analyzer executes a four-step pipeline:

  1. Context Collection – Aggregates session compression summaries, recent message history, current user queries, optional ContextType constraints, and target directory abstracts.
  2. LLM Invocation – Dispatches the assembled prompt to the configured vision language model via vlm.get_completion_async.
  3. Response Parsing – Extracts JSON payloads using parse_json_from_response from openviking_cli/utils/llm.py, handling the queries list and reasoning fields.
  4. QueryPlan Generation – Instantiates TypedQuery objects for each entry in the JSON array, wrapping them in a QueryPlan with session context and raw reasoning.

Implementing Intent Analysis in Your Application

Direct Usage of IntentAnalyzer

To implement intent analysis independently of the high-level file system API, instantiate the analyzer and provide session context:

from openviking.retrieve.intent_analyzer import IntentAnalyzer
from openviking_cli.retrieve.types import ContextType

# Configure session data

compression_summary = "User is discussing project X architecture."
recent_messages = [
    {"role": "user", "content": "How do I store vectors?"},
    {"role": "assistant", "content": "You can use VikingDB..."},
]
current_query = "Show me examples of hierarchical retrieval."

# Optional constraints

target_context_type = ContextType.RESOURCE
target_abstract = "Resource folder for retrieval utilities."

# Initialize analyzer

analyzer = IntentAnalyzer(max_recent_messages=5)

# Generate query plan

query_plan = await analyzer.analyze(
    compression_summary=compression_summary,
    messages=recent_messages,
    current_message=current_query,
    context_type=target_context_type,
    target_abstract=target_abstract,
)

# Process typed queries

for typed_q in query_plan.queries:
    print(f"[{typed_q.context_type}] ({typed_q.priority}) {typed_q.query}")
    print(f"  Intent: {typed_q.intent}")

This implementation calls _build_context_prompt internally to assemble the prompt, then parses the LLM's JSON response into TypedQuery objects containing query, context_type, intent, priority, and optional reasoning fields.

Integration with VikingFS.search

In production deployments, intent analysis triggers automatically within the high-level storage API. The search method in openviking/storage/viking_fs.py (approximately lines 640-700) implements the following logic:


# Inside VikingFS.search method

if session_summary or recent_messages:
    analyzer = IntentAnalyzer(max_recent_messages=5)
    query_plan = await analyzer.analyze(
        compression_summary=session_summary or "",
        messages=recent_messages or [],
        current_message=query,
        context_type=target_context_type,
        target_abstract=target_abstract,
    )
    typed_queries = query_plan.queries
    
    # Apply directory constraints if specified

    if target_uri:
        for tq in typed_queries:
            tq.target_directories = [target_uri]
else:
    # Fallback without intent analysis

    typed_queries = [
        TypedQuery(
            query=query,
            context_type=target_context_type,
            intent="",
            priority=3
        )
    ]

# typed_queries now drives HierarchicalRetriever

This integration demonstrates session-aware preprocessing: the analyzer only executes when session context exists, otherwise the system falls back to raw query processing.

Customizing the Intent Analysis Prompt

The IntentAnalyzer delegates prompt construction to openviking/prompts.py using the template key retrieval.intent_analysis. The _build_context_prompt method (lines 20-27 in intent_analyzer.py) injects the following variables:

  • compression_summary – Session overview or "None"
  • recent_messages – Formatted message history ([role]: content)
  • current_message – The active user query
  • context_type – Optional constraint ("memory", "resource", or "skill")
  • target_abstract – Directory description for targeted searches

To customize behavior, modify the prompt template in openviking/prompts.py or override _build_context_prompt in a subclass. The LLM must return JSON matching the schema expected by parse_json_from_response in openviking_cli/utils/llm.py.

Summary

  • IntentAnalyzer in openviking/retrieve/intent_analyzer.py provides the core preprocessing logic for OpenViking's retrieval system.
  • The analyzer assembles session context, invokes the VLM via vlm.get_completion_async, and parses structured JSON into TypedQuery objects.
  • VikingFS.search automatically triggers intent analysis when session summaries or recent messages are present, falling back to raw queries otherwise.
  • The prompt template retrieval.intent_analysis controls LLM behavior and expects variables for compression summaries, message history, and target context types.

Frequently Asked Questions

How does OpenViking handle queries without session context?

When no session_summary or recent_messages are provided to VikingFS.search, the system bypasses IntentAnalyzer entirely. It creates a single TypedQuery object using the raw user query string, sets the priority to 3, and passes this directly to the HierarchicalRetriever.

What is the difference between TypedQuery and QueryPlan?

A TypedQuery represents a single decomposed search intent with fields for query text, context_type (memory/resource/skill), intent description, `

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →