# How Vane Defines Classification Prompts for LLM Search Routing

> Learn how Vane defines classification prompts for LLM search routing. Discover the `classifierPrompt` system prompt and its role in web search, personal document queries, and more.

- Repository: [Kushagra Srivastava/Vane](https://github.com/ItzCrazyKns/Vane)
- Tags: how-to-guide
- Published: 2026-03-11

---

**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`](https://github.com/ItzCrazyKns/Vane/blob/main/classifier.ts)

The classification prompt lives in **[`src/lib/prompts/search/classifier.ts`](https://github.com/ItzCrazyKns/Vane/blob/main/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 reformulated `standaloneFollowUp` string.

The prompt is exported as:

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

1. **`skipSearch`** – Returns true when no external data retrieval is needed (e.g., casual greetings or off-topic banter).
2. **`personalSearch`** – Activates when the query asks about content the user previously uploaded or indexed in their personal library.
3. **`academicSearch`** – Triggers for research-oriented questions requiring scholarly sources.
4. **`discussionSearch`** – Used when the user asks for opinions, forums, or community discussions.
5. **`showWeatherWidget`** – True for meteorological queries that should render a weather component.
6. **`showStockWidget`** – True for financial ticker or market data requests.
7. **`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`](https://github.com/ItzCrazyKns/Vane/blob/main/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:

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

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

1. **System Context** – Injects the full `classifierPrompt` to establish behavior rules.
2. **User Context** – Wraps the formatted chat history and current query in `<conversation_history>` and `<user_query>` tags using `formatChatHistoryAsString` from **[`src/lib/utils/formatHistory.ts`](https://github.com/ItzCrazyKns/Vane/blob/main/src/lib/utils/formatHistory.ts)**.
3. **Structured Generation** – Invokes `generateObject` on 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`:

```typescript
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`](https://github.com/ItzCrazyKns/Vane/blob/main/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 **`classify`** function in **[`src/lib/agents/search/classifier.ts`](https://github.com/ItzCrazyKns/Vane/blob/main/src/lib/agents/search/classifier.ts)** validates 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`](https://github.com/ItzCrazyKns/Vane/blob/main/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`](https://github.com/ItzCrazyKns/Vane/blob/main/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.