Weaviate Cloud only

The Weaviate Query Agent enables users to perform Weaviate searches or ask questions about their data using natural language.

An agentic LLM will dynamically determine query terms and search strategies based on the natural language query.

:::callout{intent="info" title="First time using the Query Agent?"}
The Query Agent is only available for Weaviate Cloud instances. See the [full setup guide](../agents/installation.md) for setup and instantiation details.
:::

First, you must define the Query Agent class, setup with a client pointing towards your Weaviate cloud cluster.

:::code-group{sync="languages"}
```python title="Python"
from weaviate.agents.query import QueryAgent

# Instantiate a new agent object
qa = QueryAgent(
    client=client, # Your Weaviate client object
    collections=["ECommerce", "FinancialContracts", "Weather"]
)
```

```typescript title="JavaScript/TypeScript"
import { QueryAgent } from 'weaviate-agents';

// Instantiate a new agent object
const queryAgent = new QueryAgent(
    client, // Your Weaviate client object
    {
        collections: ['ECommerce', 'FinancialContracts', 'Weather'],

    }
);
```
:::

## Basic search

Use `search` to retrieve relevant objects based on a natural language query:

:::code-group{sync="languages"}
```python title="Python"
# Perform a search using Search Mode (retrieval only, no answer generation)
search_response = qa.search("Find me some vintage shoes under $70", limit=10)

# Access the search results
for obj in search_response.search_results.objects:
    print(f"Product: {obj.properties['name']} - ${obj.properties['price']}")
```

```typescript title="JavaScript/TypeScript"
// Perform a search using Search Mode (retrieval only, no answer generation)
const basicSearchResponse = await qa.search("Find me some vintage shoes under $70", {
    limit: 10
})

// Access the search results
for (const obj of basicSearchResponse.searchResults.objects) {
    console.log(`Product: ${obj.properties['name']} - ${obj.properties['price']}`)
}
```
:::

:::accordion{title="Example response"}
The response includes retrieved objects with their properties:

```python
Product: Vintage Scholar Turtleneck - $55.0
Product: Glide Platforms - $69.0
Product: Sky Shimmer Sneaks - $69.0
```
:::

## Ask with answer generation

Use `ask` to get a generated answer based on your data:

:::code-group{sync="languages"}
```python title="Python"
# Perform a query using Ask Mode (with answer generation)
response = qa.ask(
    "I like vintage clothes and nice shoes. Recommend some of each below $60."
)

# Print the response
response.display()
```

```typescript title="JavaScript/TypeScript"
// Perform a query
const basicQuery = "I like vintage clothes and nice shoes. Recommend some of each below $60."
const basicResponse = await qaWithConfig.ask(basicQuery);

basicResponse.display();
```
:::

:::accordion{title="Example response"}
The response includes a generated answer plus supporting information:

```text
📝 Final Answer:
For vintage clothing under $60, you might like the Vintage Philosopher
Midi Dress by Echo & Stitch. It features deep green velvet fabric with
antique gold button details, tailored fit, and pleated skirt.

For nice shoes under $60, consider the Glide Platforms by Vivid Verse.
These are high-shine pink platform sneakers with cushioned soles.

🔭 Searches Executed:
- queries=['vintage clothing'], filters=[[price < 60]], collection='ECommerce'
- queries=['nice shoes'], filters=[[price < 60]], collection='ECommerce'

📊 Usage Statistics:
- LLM Requests: 5
- Input Tokens: 288
- Output Tokens: 17
- Total Time: 7.58s
```
:::

## Paginate search results

Search supports pagination for large result sets:

:::code-group{sync="languages"}
```python title="Python"
# Search with pagination
response_page_1 = qa.search(
    "Find summer shoes and accessories between $50 and $100 that have the tag 'sale'",
    limit=3,
)

# Get the next page of results
response_page_2 = response_page_1.next(limit=3, offset=3)

# Continue paginating
response_page_3 = response_page_2.next(limit=3, offset=3)

# Access results from each page
for page_num, page_response in enumerate(
    [response_page_1, response_page_2, response_page_3], 1
):
    print(f"Page {page_num}:")
    for obj in page_response.search_results.objects:
        # Safely access properties in case they don't exist
        name = obj.properties.get("name", "Unknown Product")
        price = obj.properties.get("price", "Unknown Price")
        print(f"  {name} - ${price}")
    print()
```

```typescript title="JavaScript/TypeScript"
// Search with pagination
const responsePage1 = await qa.search(
    "Find summer shoes and accessories between $50 and $100 that have the tag 'sale'", {
    limit: 3,
})

// Get the next page of results
const responsePage2 = await responsePage1.next({
    limit: 3,
    offset: 3,
})

// Continue paginating
const responsePage3 = await responsePage2.next({
    limit: 3,
    offset: 6,
})

const pages = [responsePage1, responsePage2, responsePage3];

pages.forEach((pageResponse, index) => {
    const pageNum = index + 1;
    console.log(`Page ${pageNum}:`);

    pageResponse.searchResults.objects.forEach(obj => {
        // Safely access properties in case they don't exist
        const name = obj.properties.name || "Unknown Product";
        const price = obj.properties.price || "Unknown Price";
        console.log(`${name} - $${price}`);
    });
});
```
:::

:::accordion{title="Example response"}
Results are returned page by page:

```text
Page 1:
  Glide Platforms - $90.0
  Garden Haven Tote - $58.0
  Sky Shimmer Sneaks - $69.0

Page 2:
  Garden Haven Tote - $58.0
  Celestial Step Platform Sneakers - $90.0
  Eloquent Satchel - $59.0
```
:::

## Multi-turn conversations

Build conversational flows by passing message history:

:::code-group{sync="languages"}
```python title="Python"
from weaviate.agents.classes import ChatMessage

# Create a conversation with multiple turns
conversation = [
    ChatMessage(role="user", content="Hi!"),
    ChatMessage(role="assistant", content="Hello! How can I assist you today?"),
    ChatMessage(
        role="user",
        content="I have some questions about the weather data. You can assume the temperature is in Fahrenheit and the wind speed is in mph.",
    ),
    ChatMessage(
        role="assistant",
        content="I can help with that. What specific information are you looking for?",
    ),
]

# Add the user's query
conversation.append(
    ChatMessage(
        role="user",
        content="What's the average wind speed, the max wind speed, and the min wind speed",
    )
)

# Get the response
response = qa.ask(conversation)
print(response.final_answer)

# Continue the conversation
conversation.append(ChatMessage(role="assistant", content=response.final_answer))
conversation.append(ChatMessage(role="user", content="and for the temperature?"))

response = qa.ask(conversation)
print(response.final_answer)
```

```typescript title="JavaScript/TypeScript"
// Create a conversation with multiple turns
const conversation: ChatMessage[] = [
    {
        role: "user",
        content: "Hi!"
    },
    {
        role: "assistant",
        content: "Hello! How can I assist you today?"
    },
    {
        role: "user",
        content: "I have some questions about the weather data. You can assume the temperature is in Fahrenheit and the wind speed is in mph.",
    },
    {
        role: "assistant",
        content: "I can help with that. What specific information are you looking for?",
    },
]

// Add the user's query
conversation.push(
    {
        role: "user",
        content: "What's the average wind speed, the max wind speed, and the min wind speed",
    }
)

// Get the response
const response = await qaWithConfig.ask(conversation)
console.log(response.finalAnswer)

// Continue the conversation
conversation.push({ role: "assistant", content: response.finalAnswer })
conversation.push({ role: "user", content: "and for the temperature?" })

const followUpResponse = await qaWithConfig.ask(conversation)
console.log(followUpResponse.finalAnswer)
```
:::

:::accordion{title="Example response"}
The agent uses conversation history for context:

```text
User: What's the weather like?
Assistant: The average temperature is 15°C with moderate humidity.

User: Is that good for outdoor activities?
Assistant: Yes, 15°C is comfortable for most outdoor activities.
The moderate humidity levels make it pleasant for hiking, cycling, or sports.
```
:::

## Stream responses

:::callout{intent="tip" title="When to use streaming"}
Use streaming for long-running or complex queries. Streaming provides progress updates and maintains the connection through heartbeats, preventing timeouts on difficult queries that take longer to process.
:::

Stream responses to receive answers as they are generated:

:::code-group{sync="languages"}
```python title="Python"
from weaviate.agents.classes import ProgressMessage, StreamedTokens

for output in qa.ask_stream(
    query,
    # Setting this to false will skip ProgressMessages, and only stream
    # the StreamedTokens / the final QueryAgentResponse
    include_progress=True,  # Default is True
    include_final_state=True,  # Default is True
):
    if isinstance(output, ProgressMessage):
        # The message is a human-readable string, structured info available in output.details
        print(output.message)
    elif isinstance(output, StreamedTokens):
        # The delta is a string containing the next chunk of the final answer
        print(output.delta, end="", flush=True)
    else:
        # This is the final response, as returned by QueryAgent.ask()
        output.display()
```

```typescript title="JavaScript/TypeScript"
// Setting includeProgress to false will skip progressMessages, and only stream
// the streamedTokens / the final response.
for await (const event of qa.askStream(query, {
    includeProgress: true,      // Default: True
    includeFinalState: true,    // Default: True
})) {
    if (event.outputType === "progressMessage") {
        // The message is a human-readable string, structured info available in event.details
        console.log(event.message);
    } else if (event.outputType === "streamedTokens") {
        // The delta is a string containing the next chunk of the final answer
        process.stdout.write(event.delta);
    } else {
        // This is the final response, as returned by queryAgent.ask()
        event.display();
    }
}
```
:::

:::accordion{title="Example output"}
Responses are streamed as they're generated:

```text
Searching... ⏳
Processing results... 🔍
For vintage... clothing... under $60... you might like... the Vintage...
Philosopher Midi Dress... by Echo & Stitch...
✓ Complete
```
:::

## Override collections at query time

Override the agent-configured collections for a specific query:

:::code-group{sync="languages"}
```python title="Python"
response = qa.ask(
    "What kinds of contracts are listed? What's the most common type of contract?",
    collections=["FinancialContracts"],
)

response.display()
```

```typescript title="JavaScript/TypeScript"
const contractResponse = await qa.ask(
    "What kinds of contracts are listed? What's the most common type of contract?", {
    collections: ['FinancialContracts']
});

contractResponse.display();
```
:::

## Configure collections in detail

Specify additional collection options like target vectors, properties, tenants, and filters:

:::code-group{sync="languages"}
```python title="Python"
from weaviate.agents.classes import QueryAgentCollectionConfig

response = qa.ask(
    "I like vintage clothes and nice shoes. Recommend some of each below $60.",
    collections=[
        # Use QueryAgentCollectionConfig class to provide further collection configuration
        QueryAgentCollectionConfig(
            name="ECommerce",  # The name of the collection to query
            target_vector=[
                "name_description_brand_vector"
            ],  # Required target vector name(s) for collections with named vectors
            view_properties=[
                "name",
                "description",
                "category",
                "brand",
            ],  # Optional list of property names the agent can view
        ),
        QueryAgentCollectionConfig(
            name="FinancialContracts",  # The name of the collection to query
            # Optional tenant name for collections with multi-tenancy enabled
            # tenant="tenantA"
        ),
    ],
)

response.display()
```

```typescript title="JavaScript/TypeScript"
const clothingResponse = await qaWithConfig.ask(
    "I like vintage clothes and nice shoes. Recommend some of each below $60.", {
    collections: [
        {
            name: 'ECommerce',
            targetVector: ['name_description_brand_vector'],
            viewProperties: ['name', 'description', 'category', 'brand']
        },
        {
            name: 'FinancialContracts'
        }
    ]
});

clothingResponse.display();
```
:::

## Apply user-defined filters

Apply persistent filters that combine with agent-generated filters using logical `AND`:

:::code-group{sync="languages"}
```python title="Python"
from weaviate.agents.query import QueryAgent
from weaviate.agents.classes import QueryAgentCollectionConfig
from weaviate.classes.query import Filter

# Apply persistent filters that will always be combined with agent-generated filters
qa = QueryAgent(
    client=client,
    collections=[
        QueryAgentCollectionConfig(
            name="ECommerce",
            # This filter ensures only items above $50 are considered
            additional_filters=Filter.by_property("price").greater_than(50),
            target_vector=[
                "name_description_brand_vector"
            ],  # Required target vector name(s) for collections with named vectors
        ),
    ],
```

```typescript title="JavaScript/TypeScript"
// Apply persistent filters that will always be combined with agent-generated filters

const eCommerceCollection = client.collections.use("ECommerce")

const qaWithFilter = new QueryAgent(
    client, {
    collections: [{
        name: "ECommerce",
        // This filter ensures only items above $50 are considered
        additionalFilters: eCommerceCollection.filter.byProperty("price").greaterThan(50),
        targetVector: [
            "name_description_brand_vector"
        ],  // Required target vector name(s) for collections with named vectors
    }],
})

// The agent will automatically combine these filters with any it generates
const responseWithFilter = await qaWithFilter.ask("Find me some affordable clothing items")

responseWithFilter.display()

// You can also apply filters dynamically at runtime
const runtimeConfig = {
    name: "ECommerce",
    additionalFilters: eCommerceCollection.filter.byProperty("category").equal("Footwear"),
    targetVector: ["name_description_brand_vector"]
}

const responseWithRuntimeFilter = await queryAgent.ask("What products are available?", {
    collections: [runtimeConfig]
})

responseWithRuntimeFilter.display()
```
:::

:::accordion{title="Example behavior"}
User-defined filters are always applied in addition to agent-generated filters:

```python
# Configuration: price < 100
# User query: "red shoes"
# Actual query: (semantic search for "red shoes") AND (price < 100) AND (color = "red")
```
:::

## Inspect response details

Access detailed information about searches performed, aggregations, and token usage:

:::code-group{sync="languages"}
```python title="Python"
print("\n=== Query Agent Response ===")
print(f"Original Query: {response.searches[0].query}\n")

print("🔍 Final Answer Found:")
print(f"{response.final_answer}\n")

print("🔍 Searches Executed:")
for search in response.searches:
    print(f"- {search}\n")

if len(response.aggregations) > 0:
    print("📊 Aggregation Results:")
    for agg in response.aggregations:
        print(f"- {agg}\n")

if response.missing_information:
    if response.is_partial_answer:
        print("⚠️ Answer is Partial - Missing Information:")
    else:
        print("⚠️ Missing Information:")
    for missing in response.missing_information:
        print(f"- {missing}")
```

```typescript title="JavaScript/TypeScript"
console.log('\n=== Query Agent Response ===');
console.log(`Original Query: ${basicQuery}\n`); // Pre-defined by user

console.log('🔍 Final Answer Found:');
console.log(`${basicResponse.finalAnswer}\n`);

console.log('🔍 Searches Executed:');
for (const collectionSearches of basicResponse.searches) {
    console.log(`- ${collectionSearches.query}\n`);
}

if (basicResponse.aggregations) {
    console.log('📊 Aggregation Results:');
    for (const agg of basicResponse.aggregations) {
        console.log(`- ${JSON.stringify(agg)}\n`);
    }
}

if (basicResponse.missingInformation && basicResponse.missingInformation.length > 0) {
    if (basicResponse.isPartialAnswer) {
        console.log('⚠️ Answer is Partial - Missing Information:');
    } else {
        console.log('⚠️ Missing Information:');
    }
    for (const missing of basicResponse.missingInformation) {
        console.log(`- ${missing}`);
    }
}
```
:::

:::accordion{title="Example output"}
Response inspection reveals the agent's execution details:

```text
=== Query Agent Response ===
Original Query: vintage style clothing

🔍 Final Answer Found:
For vintage-style clothing under $60, I recommend the Vintage Scholar
Turtleneck priced at $55. It features soft, stretchable fabric with
timeless pleated details, perfect for a Dark Academia-inspired look.

However, no shoes under $60 were found based on available information.

🔍 Searches Executed:
- query: 'vintage style clothing'
- filters: price < 60
- collection: 'ECommerce'

- query: 'nice shoes'
- filters: price < 60
- collection: 'ECommerce'

⚠️ Answer is Partial - Missing Information:
- No recommendations were provided for nice shoes under $60
```
:::

## Further resources

- [Query Agent full documentation](../agents/overview.md) - Complete guide with setup and advanced features
- [Connect to Weaviate Cloud](../manage-clusters/connect.md)
- [Weaviate Cloud documentation](../cloud/overview.md)

## Questions and feedback

Have a question or feedback? Here's how to reach us.

::::card-grid
:::card{title="Community Forum" href="https://forum.weaviate.io/c/support" icon="messages-square"}
Ask questions and connect with other developers on our **Community forum**.
:::

:::card{title="Support" href="/guides/support-overview" icon="life-buoy"}
Weaviate Cloud user or customer? Find the right channel on the **Support page**.
:::
::::

## Related pages

- [Agents](./agents-index.md)
- [AI-assisted Weaviate code generation](./ai-assisted-vibe-coding-index.md)
- [APIs](./apis-index.md)
- [Authorization and authentication](./authorization-and-authentication-index.md)
- [Benchmarks](./benchmarks-index.md)
- [Best practices](./best-practices-index.md)
- [Client libraries](./clients-index.md)
- [Client Libraries / SDKs](./client-libraries-index.md)
- [Cloud](./cloud-index.md)
- [Cloud account management](./cloud-account-management-index.md)

# Agent Instructions

This portal answers questions programmatically. To receive a synthesized,
source-cited answer instead of crawling page by page, append the `?ask=`
query parameter to any page URL on this site:

    /guides/quickstart?ask=how+do+I+authenticate

Optional parameters:

- `&goal=<what-you-are-trying-to-do>` steers the answer toward your
  objective (e.g. `&goal=write+a+python+client`).
- `&version=<label>` scopes the answer to a mounted version when the
  portal publishes more than one.

The response is `text/markdown`: the answer followed by a `# Sources` list
of the portal pages it was grounded in. Status codes are the contract:

- `200` — the answer; `402` — the portal owner’s plan or answer credits are
  exhausted (surface this to your operator; do NOT retry); `429` — you are
  rate-limited; back off for the `Retry-After` seconds; `503` — the answer
  lane is temporarily unavailable; fall back to crawling the `.md` pages.

For the full corpus map read `llms.txt` at the site root; for the tool
surface (search + page fetch as MCP tools) see `/mcp`.
