How Vane Defines Classification Prompts for LLM Search Routing
Vane uses a dedicated system prompt called classifierPrompt to programmatically determine whether to run a web search, query personal documents, display a widget, or skip retrieval entirely.
In the Vane open-source search engine, classification prompts serve as the decision-making layer that routes user queries to the appropriate backend service. Rather than hardcoding routing logic, Vane delegates this responsibility to an LLM using a strictly structured prompt template that returns a typed JSON response.
Classification Prompt Structure in classifier.ts
The classification prompt lives in src/lib/prompts/search/classifier.ts and is exported as a constant string template. It is organized into four distinct XML-style sections that instruct the model how to analyze incoming requests:
<role>– Defines the assistant’s objective: analyze the user query plus conversation history and select the correct classification labels.<labels>– Enumerates eight boolean decision flags (skipSearch,personalSearch,academicSearch,discussionSearch,showWeatherWidget,showStockWidget,showCalculationWidget) with explicit criteria for when each should be true or false.<standalone_followup>– Directs the model to rewrite the latest user question as a self-contained query that includes necessary context from the conversation history.<output_format>– Enforces strict JSON output containing the classification object and the reformulatedstandaloneFollowUpstring.
The prompt is exported as:
export const classifierPrompt = `...`; // Multi-section template
This declarative approach keeps the routing criteria human-readable and easy to modify without touching TypeScript logic.
The Eight Classification Labels
The <labels> section trains the LLM to set eight boolean flags that control Vane’s execution path:
skipSearch– Returns true when no external data retrieval is needed (e.g., casual greetings or off-topic banter).personalSearch– Activates when the query asks about content the user previously uploaded or indexed in their personal library.academicSearch– Triggers for research-oriented questions requiring scholarly sources.discussionSearch– Used when the user asks for opinions, forums, or community discussions.showWeatherWidget– True for meteorological queries that should render a weather component.showStockWidget– True for financial ticker or market data requests.showCalculationWidget– True for mathematical or computational tasks best handled by a calculation tool.
These flags are mutually exclusive in practice, allowing the downstream agent to branch into exactly one execution mode.
The Classify Agent Implementation
The runtime logic resides in src/lib/agents/search/classifier.ts, which imports the prompt and executes the classification. The classify function constructs a structured LLM request using Zod for response validation.
Schema Definition
Before invoking the model, Vane defines a strict Zod schema that guarantees type safety:
import { z } from 'zod';
const schema = z.object({
classification: z.object({
skipSearch: z.boolean(),
personalSearch: z.boolean(),
academicSearch: z.boolean(),
discussionSearch: z.boolean(),
showWeatherWidget: z.boolean(),
showStockWidget: z.boolean(),
showCalculationWidget: z.boolean(),
}),
standaloneFollowUp: z.string(),
});
Message Construction and Execution
The classify function assembles the LLM messages and calls generateObject:
import { classifierPrompt } from '@/lib/prompts/search/classifier';
import formatChatHistoryAsString from '@/lib/utils/formatHistory';
export const classify = async (input: ClassifierInput) => {
const output = await input.llm.generateObject<typeof schema>({
messages: [
{ role: 'system', content: classifierPrompt },
{
role: 'user',
content: `<conversation_history>\n${formatChatHistoryAsString(
input.chatHistory,
)}\n</conversation_history>\n<user_query>\n${input.query}\n</user_query>`,
},
],
schema,
});
return output; // { classification: {...}, standaloneFollowUp: "…" }
};
The function performs three critical steps:
- System Context – Injects the full
classifierPromptto establish behavior rules. - User Context – Wraps the formatted chat history and current query in
<conversation_history>and<user_query>tags usingformatChatHistoryAsStringfromsrc/lib/utils/formatHistory.ts. - Structured Generation – Invokes
generateObjecton the LLM provider (e.g., OpenAI, Ollama), which validates the raw model output against the Zod schema before returning.
Practical Usage Example
To classify a query in your own Vane implementation, initialize your LLM provider and call classify:
import { classify } from '@/lib/agents/search/classifier';
import { openaiLLM } from '@/lib/models/providers/openai/openaiLLM';
import { ChatMessage } from '@/lib/types';
const chatHistory: ChatMessage[] = [
{ role: 'assistant', content: 'Sure, I can help!' },
];
const result = await classify({
llm: openaiLLM,
chatHistory,
query: 'What’s the weather in Berlin tomorrow?',
});
console.log(result);
// {
// classification: {
// skipSearch: false,
// personalSearch: false,
// academicSearch: false,
// discussionSearch: false,
// showWeatherWidget: true,
// showStockWidget: false,
// showCalculationWidget: false,
// },
// standaloneFollowUp: 'What is the weather forecast for Berlin tomorrow?'
// }
The returned object tells the search orchestrator to render a weather widget while skipping general web search, academic sources, and personal document retrieval.
Summary
- Vane’s classification logic is defined in a single prompt template located at
src/lib/prompts/search/classifier.ts. - The prompt contains four sections (
<role>,<labels>,<standalone_followup>,<output_format>) that guide the LLM to return structured JSON. - Eight boolean flags determine whether to execute a web search, personal search, academic search, discussion search, or display specialized widgets (weather, stock, calculation).
- The
classifyfunction insrc/lib/agents/search/classifier.tsvalidates responses using a Zod schema and wraps conversational context in XML tags before sending to the LLM. - This architecture separates declarative routing rules (the prompt) from imperative execution logic (the TypeScript code).
Frequently Asked Questions
Where is the classification prompt defined in Vane?
The classification prompt is defined in src/lib/prompts/search/classifier.ts as the exported constant classifierPrompt. This file contains the full system prompt template that instructs the LLM how to categorize incoming queries and what JSON structure to return.
How does Vane ensure the LLM returns valid classification data?
Vane uses Zod schema validation enforced through the generateObject method. The schema in src/lib/agents/search/classifier.ts requires eight specific boolean flags and a standaloneFollowUp string. The LLM provider validates the model’s output against this schema before the function returns, ensuring type-safe downstream processing.
What are the eight classification labels available in Vane?
The eight boolean flags are: skipSearch, personalSearch, academicSearch, discussionSearch, showWeatherWidget, showStockWidget, and showCalculationWidget. These flags are defined in the <labels> section of the classifier prompt and correspond to distinct search strategies or UI widgets in the Vane application.
Can I customize which LLM provider handles the classification?
Yes. The classify function accepts any LLM instance that implements the generateObject method. You can pass OpenAI, Ollama, or other providers from src/lib/models/providers/, allowing you to swap models without modifying the classification logic or prompt templates.
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 →