> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.twelvelabs.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.twelvelabs.io/_mcp/server.

# Agentic search

> Perform agentic search over a knowledge store. Jockey interprets your natural-language query, reasons across the content, and returns structured, explained results.

This recipe shows you how to perform agentic search over a knowledge store. [Jockey](/v1.3/agents/concepts/jockey) interprets your query, reasons across the content, and returns structured results that you can shape and refine. Each result can include an item reference, timestamp, description, and relevance. Use the results in a search interface or a processing pipeline.

For a direct, single-call search that retrieves ranked matches, see [Search a knowledge store](/v1.3/agents/guides/search-a-knowledge-store). Use agentic search when your query is interpretive or subjective, or when you want Jockey to explain and refine the results.

**Use cases**:

* **Interpretive queries**: Search for moments that need interpretation, ranking, or judgment
* **Explained results**: Receive a description and a relevance explanation for each match
* **Custom output**: Shape the results into a structure your application consumes

# Key concepts

* **Knowledge store**: A persistent store of your videos and images plus the understanding the platform derives from them - spatiotemporal context, a typed ontology, and embeddings - that together enable corpus-level reasoning.
* **Structured output**: A JSON schema you provide with your request. Jockey constrains its output to match your schema, so you retrieve machine-readable results instead of plain text. For a guide on designing schemas and parsing responses, see the [Structured output](/v1.3/agents/guides/create-a-response/structured-output) page.

# Workflow

This recipe combines two elements: a JSON schema that defines the result data structure and a prompt that describes what you want to find. Jockey reasons across your knowledge store, matches moments against visual content, audio, and semantic meaning, and returns structured search results. You can adapt this recipe to search for different content types or match different criteria.

# Prerequisites

* You've already created a knowledge store with at least one item in `ready` status. See the [Create a knowledge store](/v1.3/agents/guides/create-a-knowledge-store) and [Add assets to a knowledge store](/v1.3/agents/guides/add-assets) guides for details.
* You're familiar with the request and response format. See the [Create a response](/v1.3/agents/guides/create-a-response) page for details.

# Complete example

Copy and paste the code below, replacing the placeholders surrounded by `<>` with your values. The example pairs a result schema with a prompt that searches for presentations. Adapt the schema and prompt to match what you want to find.

**`Python`**

```python Python maxlines=40
import json
from twelvelabs import TwelveLabs, TextParam
from twelvelabs.types.text_param_format import TextParamFormat_JsonSchema

client = TwelveLabs(api_key="<YOUR_API_KEY>")
STORE_ID = "<YOUR_KNOWLEDGE_STORE_ID>"

# Define a JSON schema for the search results
search_schema = {
    "type": "object",
    "properties": {
        "results": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "item_reference": {"type": "string", "description": "The plain UUID of the source item"},  # Source item identifier
                    "timestamp": {"type": "string"},          # When the moment occurs
                    "description": {"type": "string"},        # What happens at this moment
                    "relevance": {"type": "string"},          # Why this matches the query
                },
            },
        },
        "total_results": {"type": "integer"},       # Number of matches found
        "query_interpretation": {"type": "string"},  # How Jockey interpreted the query
    },
}

# Run the search
response = client.responses.create(
    knowledge_store_id=STORE_ID,
    input=[{"type": "message", "role": "user", "content": "Find all moments where someone is presenting to an audience"}],
    text=TextParam(format=TextParamFormat_JsonSchema(name="search_results", schema_=search_schema)),
)

# Parse and read the results
for output in response.output:
    if output.type == "message":
        for content in output.content:
            search = json.loads(content.text)
            print(f"Found {search['total_results']} results")
            print(f"Interpreted as: {search['query_interpretation']}\n")
            for r in search["results"]:
                print(f"  [{r['timestamp']}] {r['item_reference']}")
                print(f"    {r['description']}")
                print(f"    Relevance: {r['relevance']}\n")
```

**`Node.js`**

```javascript Node.js maxlines=40
import { TwelveLabs } from "twelvelabs-js";

const client = new TwelveLabs({ apiKey: "<YOUR_API_KEY>" });
const storeId = "<YOUR_KNOWLEDGE_STORE_ID>";

// Define a JSON schema for the search results
const searchSchema = {
  type: "object",
  properties: {
    results: {
      type: "array",
      items: {
        type: "object",
        properties: {
          item_reference: { type: "string", description: "The plain UUID of the source item" }, // Source item identifier
          timestamp: { type: "string" }, // When the moment occurs
          description: { type: "string" }, // What happens at this moment
          relevance: { type: "string" }, // Why this matches the query
        },
      },
    },
    total_results: { type: "integer" }, // Number of matches found
    query_interpretation: { type: "string" }, // How Jockey interpreted the query
  },
};

// Run the search
const response = await client.responses.create({
  knowledgeStoreId: storeId,
  input: [{ type: "message", role: "user", content: "Find all moments where someone is presenting to an audience" }],
  text: { format: { type: "json_schema", name: "search_results", schema: searchSchema } },
});

// Parse and read the results
for (const output of response.output ?? []) {
  if (output.type === "message") {
    for (const content of output.content ?? []) {
      const search = JSON.parse(content.text);
      console.log(`Found ${search.total_results} results`);
      console.log(`Interpreted as: ${search.query_interpretation}\n`);
      for (const r of search.results) {
        console.log(`  [${r.timestamp}] ${r.item_reference}`);
        console.log(`    ${r.description}`);
        console.log(`    Relevance: ${r.relevance}`);
      }
    }
  }
}
```

# Code explanation

#### Python

To run an agentic search, call the [`responses.create`](/v1.3/sdk-reference/python/responses#create-a-response) method with a `text` parameter that describes your result schema and a prompt that describes what to find.\

**Parameters**:

* `knowledge_store_id`: The unique identifier of the knowledge store to search.
* `input`: An array of input items. Each item is a message you send to Jockey. This example searches for moments where someone presents to an audience.
* `text`: An object that specifies the response format. To return structured JSON, set the `format` field. Within it, the `schema_` field defines the structure the results must match, and the `name` field identifies the schema. Design the schema to fit your use case; the inline comments in the example describe each field. For the schema rules, see the [Structured output](/v1.3/agents/guides/create-a-response/structured-output#json-schema-requirements) page.\


**Return value**: An object of type `ResponseObject` containing, among other information, the following fields:

* `id`: The unique identifier of the response.
* `knowledge_store_id`: The knowledge store this response was generated against.
* `session_id`: The session identifier. Pass it in a follow-up request to continue the conversation.
* `status`: The status of the response. The possible values are `completed`, `failed`, `in_progress`, and `incomplete`.
* `output`: The response output items. The `text` field of each content part in a `message` item is a JSON string that matches your schema. Parse it with the `json.loads()` method, then iterate the `results` array to read each match.
* `usage`: Token usage statistics, including the `input_tokens` and `output_tokens` fields.

#### Node.js

To run an agentic search, call the [`responses.create`](/v1.3/sdk-reference/node-js/responses#create-a-response) method with a `text` parameter that describes your result schema and a prompt that describes what to find. You pass all parameters as properties of a single object.\

**Parameters**:

* `knowledgeStoreId`: The unique identifier of the knowledge store to search.
* `input`: An array of input items. Each item is a message you send to Jockey. This example searches for moments where someone presents to an audience.
* `text`: An object that specifies the response format. To return structured JSON, set the `format` field. Within it, the `type` field must be `"json_schema"`, the `schema` field defines the structure the results must match, and the `name` field identifies the schema. Design the schema to fit your use case; the inline comments in the example describe each field. For the schema rules, see the [Structured output](/v1.3/agents/guides/create-a-response/structured-output#json-schema-requirements) page.\


**Return value**: An `HttpResponsePromise` that resolves to an object of type `ResponseObject` containing, among other information, the following fields:

* `id`: The unique identifier of the response.
* `knowledgeStoreId`: The knowledge store this response was generated against.
* `sessionId`: The session identifier. Pass it in a follow-up request to continue the conversation.
* `status`: The status of the response. The possible values are `completed`, `failed`, `in_progress`, and `incomplete`.
* `output`: The response output items. The `text` field of each content part in a `message` item is a JSON string that matches your schema. Parse it with the `JSON.parse()` method, then iterate the `results` array to read each match.
* `usage`: Token usage statistics, including the `inputTokens` and `outputTokens` fields.

# Example response

The `text` field inside each content part is a JSON string that matches your schema. After parsing, a typical result looks like this:

```json
{
  "results": [
    {
      "item_reference": "069eb4e8-aeb0-7e83-8000-86413fcc296a",
      "timestamp": "05:22-07:14",
      "description": "Speaker walks to the front of the room and begins a slide presentation",
      "relevance": "Very high. Presenter addressing a seated audience with visual aids"
    },
    {
      "item_reference": "069e1e97-27f8-7a8e-8000-bf32ecd7fc8c",
      "timestamp": "12:45-14:30",
      "description": "Keynote speaker demonstrates product features on stage",
      "relevance": "High. Live presentation to a large audience with product demo"
    }
  ],
  "total_results": 2,
  "query_interpretation": "Find moments where a person is actively presenting or speaking to a group of people"
}
```

> **Note**
>
> The `item_reference` field is the unique identifier of the knowledge store item that contains the match, as a plain UUID. The `timestamp` field gives the matching range in `MM:SS-MM:SS` format. Re-add the `ksi_` prefix to this value to use it with the knowledge store items endpoints.

# Variations

Change the prompt and schema to adapt this recipe for different search scenarios. Jockey matches against visual content, audio, and semantic meaning.

* **Search by visual description**: Change the prompt to describe a scene, such as "outdoor scenes with water" or "product being held up to camera."
* **Search by audio or speech**: Search for spoken content, such as "someone laughing" or "mentions of quarterly revenue."
* **Search by tone or mood**: Describe the feel of a moment, such as "heated discussion" or "celebratory reactions."
* **Rank results**: Ask Jockey to rank matches: "Find and rank the top 5 most visually striking moments."
* **Narrow the scope**: Add the `instructions` parameter to constrain the search. For example, "Only search the first 2 minutes of each video."
* **Refine with follow-up turns**: Use a [multi-turn session](/v1.3/agents/guides/create-a-response/multi-turn-sessions) to adjust results: "Show me more like the third result."

# Jupyter notebook

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/twelvelabs-io/twelvelabs-developer-experience/blob/main/quickstarts/jockey/recipes/search_videos.ipynb)

# See also

* [Search a knowledge store](/v1.3/agents/guides/search-a-knowledge-store): Retrieve ranked clips and images without agentic reasoning
* [Assemble highlight reels](/v1.3/agents/recipes/assemble-highlight-reels): Find clips for assembly
* [Structured output](/v1.3/agents/guides/create-a-response/structured-output): More on JSON Schema responses
* [Create a response](/v1.3/api-reference/responses/create): API reference for the responses endpoint