Files
wehub-resource-sync c56bef871b
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
Docker image release / Build base image (push) Has been cancelled
Sync docs with Docusaurus / sync (push) Has been cancelled
Tests / Check if changed (push) Has been cancelled
Tests / format (push) Has been cancelled
Tests / check-imports (push) Has been cancelled
Tests / Unit / macos-latest (push) Has been cancelled
Tests / Unit / ubuntu-latest (push) Has been cancelled
Tests / Unit / windows-latest (push) Has been cancelled
Tests / mypy (push) Has been cancelled
Tests / Integration / ubuntu-latest (push) Has been cancelled
Tests / Integration / macos-latest (push) Has been cancelled
Tests / Integration / windows-latest (push) Has been cancelled
Tests / notify-slack-on-failure (push) Has been cancelled
Tests / Mark tests as completed (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:28 +08:00

493 lines
21 KiB
Plaintext

---
title: "Agent"
id: agent
slug: "/agent"
description: "The `Agent` component is a tool-using agent that interacts with chat-based LLMs and tools to solve complex queries iteratively. It can execute external tools, manage state across multiple LLM calls, and stop execution based on configurable `exit_conditions`."
---
# Agent
The `Agent` component is a tool-using agent that interacts with chat-based LLMs and tools to solve complex queries iteratively. It can execute external tools, manage state across multiple LLM calls, and stop execution based on configurable `exit_conditions`.
<div className="key-value-table">
| | |
| --- | --- |
| **Most common position in a pipeline** | After a [`ChatPromptBuilder`](../builders/chatpromptbuilder.mdx) or user input |
| **Mandatory init variables** | `chat_generator`: An instance of a Chat Generator that supports tools |
| **Mandatory run variables** | `messages`: A list of [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx)s |
| **Output variables** | `messages`: Chat history with tool and model responses |
| **API reference** | [Agents](/reference/agents-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/agents/agent.py |
| **Package name** | `haystack-ai` |
</div>
## Overview
The `Agent` component is a loop-based system that uses a chat-based large language model (LLM) and external tools to solve complex user queries.
It works iteratively—calling tools, updating state, and generating prompts—until one of the configurable `exit_conditions` is met.
It can:
- Dynamically select tools based on user input,
- Maintain and validate runtime state using a schema,
- Stream token-level outputs from the LLM.
The `Agent` returns a dictionary containing:
- `messages`: the full conversation history,
- `last_message`: the final `ChatMessage` from the agent,
- `step_count`: the number of steps the agent ran,
- `token_usage`: aggregated token usage summed across every LLM call in the run,
- `tool_call_counts`: how many times each tool was invoked, keyed by tool name,
- Additional dynamic keys based on `state_schema`.
### Run Metadata
The `step_count`, `token_usage`, and `tool_call_counts` outputs are populated automatically during a run. They are added to the agent's `state_schema` behind the scenes, so tools registered with `inputs_from_state` can read them mid-run. They are outputs only — they cannot be passed as inputs to `run()` or `run_async()`, and using them as keys in your own `state_schema` raises a `ValueError`. See [State](./state.mdx#schema-definition) for details.
```python
response = agent.run(messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")])
print(response["step_count"]) # 2
print(
response["token_usage"],
) # {"prompt_tokens": 512, "completion_tokens": 86, ...}
print(response["tool_call_counts"]) # {"calculator": 1}
```
## Parameters
`chat_generator` is the only mandatory parameter — an instance of a Chat Generator that supports tools. All other parameters are optional.
- `tools`: A list of tool or toolset instances the agent can call. Supported types: [`Tool`](../../tools/tool.mdx), [`ComponentTool`](../../tools/componenttool.mdx), [`PipelineTool`](../../tools/pipelinetool.mdx), [`MCPTool`](../../tools/mcptool.mdx), [`Toolset`](../../tools/toolset.mdx), [`MCPToolset`](../../tools/mcptoolset.mdx), [`SearchableToolset`](../../tools/searchabletoolset.mdx). Tool names must be unique; duplicate names are detected at the start of each agent step, before the chat generator is called.
- `system_prompt`: A plain string or Jinja2 template used as the system message for every run. If the template contains Jinja2 variables, those variables become additional inputs to `run()`.
- `user_prompt`: A Jinja2 template appended to the user-provided messages on each run. Template variables become additional inputs to `run()`. Use `required_variables` to enforce which variables must be provided.
- `exit_conditions`: List of conditions that cause the agent to stop. Use `”text”` to stop when the LLM replies without a tool call, or a tool name to stop once that tool has been executed. Defaults to `[“text”]`. Exit conditions are evaluated at runtime rather than validated at initialization, so a condition can name a tool that is only loaded later — for example, a tool passed at runtime via `run(tools=...)` or one discovered by a [`SearchableToolset`](../../tools/searchabletoolset.mdx).
- `state_schema`: Defines the agent's runtime state — a dict mapping key names to type configs (e.g. `{“docs”: {“type”: list[Document]}}`). Tools can read from and write to state keys via `inputs_from_state` and `outputs_to_state`. See [State](./state.mdx) for full details.
- `streaming_callback`: A callback invoked for each streamed token. Use the built-in `print_streaming_chunk` for console output.
- `max_agent_steps`: Maximum number of LLM + tool call iterations before the agent stops. Defaults to `100`.
- `raise_on_tool_invocation_failure`: If `True`, raises an exception when a tool call fails. If `False` (default), the error is passed back to the LLM as a message so it can recover.
- `hooks`: A dict mapping a hook point (`"before_llm"`, `"before_tool"`, `"after_tool"`, `"on_exit"`) to a list of hooks the agent runs at that point. Hooks receive the live `State` and influence the run by mutating it — for example, to build run-time context or require human confirmation of tool calls. See [Hooks](./hooks.mdx) and [Human in the Loop](./human-in-the-loop.mdx).
- `tool_concurrency_limit`: Maximum number of tool calls to execute at the same time. Defaults to `4`; set to `1` to disable parallel tool execution.
- `tool_streaming_callback_passthrough`: If `True`, passes the streaming callback to tools that accept it.
### Runtime overrides
`run()` also accepts parameters that override the init-time configuration for a single call:
- `tools`: Pass a list of `Tool`/`Toolset` objects, or a list of tool name strings to select a subset of the agent's configured tools for this run.
- `generation_kwargs`: Additional keyword arguments forwarded to the LLM, overriding any set at init time (e.g. `{“temperature”: 0.2}`).
- `hook_context`: A dict of request-scoped resources made available to [hooks](./hooks.mdx) via `state.data["hook_context"]` — for example, a user ID or a WebSocket connection.
:::info
For the full parameter reference, see the [Agents API Documentation](/reference/agents-api).
:::
## Usage
### On its own
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import tool
from haystack.components.agents import Agent
from typing import Annotated
@tool(outputs_to_state={"calc_result": {"source": "result"}})
def calculator(
expression: Annotated[str, "Math expression to evaluate, e.g. '7 * (4 + 2)'"],
) -> dict:
"""Evaluate basic math expressions."""
try:
result = eval(expression, {"__builtins__": {}})
return {"result": result}
except Exception as e:
return {"error": str(e)}
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"),
tools=[calculator],
system_prompt="You are a helpful assistant. Always use the calculator tool to evaluate math expressions.",
state_schema={"calc_result": {"type": int}},
)
response = agent.run(messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")])
print(response["last_message"].text)
print("Calc Result:", response.get("calc_result"))
```
### In a pipeline
The example pipeline below creates a database assistant using `OpenAIChatGenerator`, `LinkContentFetcher`, and custom database tool.
It reads the given URL and processes the page content, then builds a prompt for the AI.
The assistant uses this information to write people's names and titles from the given page to the database.
```python
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.builders.chat_prompt_builder import ChatPromptBuilder
from haystack.components.converters.html import HTMLToDocument
from haystack.components.fetchers.link_content import LinkContentFetcher
from haystack import Document, Pipeline
from haystack.dataclasses import ChatMessage
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.tools import tool
from typing import Annotated, Optional
document_store = InMemoryDocumentStore() # create a document store or an SQL database
@tool
def add_database_tool(
name: Annotated[str, "First name of the person"],
surname: Annotated[str, "Last name of the person"],
job_title: Annotated[Optional[str], "Job title or role of the person"] = None,
other: Annotated[Optional[str], "Any other relevant information"] = None,
) -> str:
"""Add a person to the database with information about them."""
document_store.write_documents(
[
Document(
content=name + " " + surname + " " + (job_title or ""),
meta={"other": other},
),
],
)
# Returning a confirmation lets the agent know the tool call succeeded
return f"Successfully added {name} {surname} to the database."
database_assistant = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"),
tools=[add_database_tool],
system_prompt="""
You are a database assistant.
Your task is to extract the names of people mentioned in the given context and add them to a knowledge base,
along with additional relevant information about them that can be extracted from the context.
Do not use your own knowledge, stay grounded to the given context.
Do not ask the user for confirmation.
Instead, automatically update the knowledge base and return a brief summary of the people added,
including the information stored for each.
""",
)
extraction_agent = Pipeline()
extraction_agent.add_component("fetcher", LinkContentFetcher())
extraction_agent.add_component("converter", HTMLToDocument())
extraction_agent.add_component(
"builder",
ChatPromptBuilder(
template=[
ChatMessage.from_user("""
{% for doc in docs %}
{{ doc.content|default|truncate(25000) }}
{% endfor %}
"""),
],
required_variables=["docs"],
),
)
extraction_agent.add_component("database_agent", database_assistant)
extraction_agent.connect("fetcher.streams", "converter.sources")
extraction_agent.connect("converter.documents", "builder.docs")
extraction_agent.connect("builder", "database_agent")
agent_output = extraction_agent.run(
{
"fetcher": {
"urls": ["https://github.com/deepset-ai/haystack/releases/tag/v2.27.0"],
},
},
)
print(agent_output["database_agent"]["last_message"].text)
# Inspect what was written to the document store
written_docs = document_store.filter_documents()
print(f"\n{len(written_docs)} people added to the database:")
for doc in written_docs:
print(f" - {doc.content}")
```
### In YAML
The example pipeline below fetches a webpage, converts its HTML to text, and builds a chat prompt combining the page content with a user query.
The `Agent` then answers the question based on the provided content and can use its web search tool to find additional information if needed.
<details>
<summary>View YAML</summary>
```yaml
components:
agent:
init_parameters:
chat_generator:
init_parameters:
api_base_url: null
api_key:
env_vars:
- OPENAI_API_KEY
strict: true
type: env_var
generation_kwargs: {}
http_client_kwargs: null
max_retries: null
model: gpt-5.4-nano
organization: null
streaming_callback: null
timeout: null
tools: null
tools_strict: false
type: haystack.components.generators.chat.openai.OpenAIChatGenerator
exit_conditions:
- text
hooks: null
max_agent_steps: 5
raise_on_tool_invocation_failure: false
required_variables: null
state_schema: {}
streaming_callback: null
system_prompt: You are a helpful assistant. Use the web search tool to find
information when needed.
tool_concurrency_limit: 4
tool_streaming_callback_passthrough: false
tools:
- data:
component:
init_parameters:
allowed_domains: null
api_key:
env_vars:
- SERPERDEV_API_KEY
strict: true
type: env_var
exclude_subdomains: false
search_params: {}
top_k: 3
type: haystack_integrations.components.websearch.serperdev.websearch.SerperDevWebSearch
description: Search the web for current information on any topic
inputs_from_state: null
name: web_search
outputs_to_state: null
outputs_to_string: null
parameters: null
type: haystack.tools.component_tool.ComponentTool
user_prompt: null
type: haystack.components.agents.agent.Agent
converter:
init_parameters:
extraction_kwargs: {}
store_full_path: false
type: haystack.components.converters.html.HTMLToDocument
fetcher:
init_parameters:
client_kwargs:
follow_redirects: true
timeout: 3
http2: false
raise_on_failure: true
request_headers: {}
retry_attempts: 2
timeout: 3
user_agents:
- haystack/LinkContentFetcher/2.27.0rc0
type: haystack.components.fetchers.link_content.LinkContentFetcher
prompt_builder:
init_parameters:
required_variables:
- docs
- query
template:
- content:
- text: 'Based on the following content:
{% for doc in docs %}
{{ doc.content }}
{% endfor %}
Answer this question: {{ query }}'
meta: {}
name: null
role: user
variables: null
type: haystack.components.builders.chat_prompt_builder.ChatPromptBuilder
connection_type_validation: true
connections:
- receiver: converter.sources
sender: fetcher.streams
- receiver: prompt_builder.docs
sender: converter.documents
- receiver: agent.messages
sender: prompt_builder.prompt
max_runs_per_component: 100
metadata: {}
```
</details>
## Streaming
You can stream output as it's generated. Pass a callback to `streaming_callback`.
Use the built-in `print_streaming_chunk` to print text tokens and tool events (tool calls and tool results).
```python
from haystack.components.generators.utils import print_streaming_chunk
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"),
tools=[...],
system_prompt="...",
streaming_callback=print_streaming_chunk,
)
```
See our [Streaming Support](../generators/guides-to-generators/choosing-the-right-generator.mdx#streaming-support) docs to learn more how `StreamingChunk` works and how to write a custom callback.
Give preference to `print_streaming_chunk` by default.
Write a custom callback only if you need a specific transport (for example, SSE/WebSocket) or custom UI formatting.
## Multimodal Inputs
Agents support multimodal inputs when paired with a vision-capable model such as `gpt-5` (OpenAI) or `gemini-2.5-flash` (Google).
Pass images alongside text by including `ImageContent` objects in the `content_parts` of a `ChatMessage`:
```python
from haystack.dataclasses import ChatMessage, ImageContent
image = ImageContent.from_url("https://example.com/chart.png")
result = agent.run(
messages=[
ChatMessage.from_user(content_parts=["What does this chart show?", image]),
],
)
```
Tools can also return `ImageContent` directly, letting the agent fetch and reason about images dynamically during its loop.
Two things are required: set `outputs_to_string={"raw_result": True}` so the `ToolInvoker` skips string conversion, and return a `list[ImageContent]` (the tool result type is `str | Sequence[TextContent | ImageContent]`).
The standard Chat Completions API doesn't support images in tool results — use `OpenAIResponsesChatGenerator` (OpenAI's Responses API) instead:
```python
from typing import Annotated
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.dataclasses import ChatMessage, ImageContent
from haystack.tools import tool
@tool(outputs_to_string={"raw_result": True})
def fetch_image(
url: Annotated[str, "URL of the image to fetch and analyze"],
) -> list[ImageContent]:
"""Fetch an image from a URL so the agent can analyze its contents."""
return [ImageContent.from_url(url)]
agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5"),
tools=[fetch_image],
system_prompt="You are a helpful assistant that can fetch and analyze images from URLs.",
)
result = agent.run(
messages=[
ChatMessage.from_user(
"Fetch the image at https://picsum.photos/seed/haystack/640/480 and describe what you see.",
),
],
)
print(result["last_message"].text)
```
`ImageContent` can be created from a URL, a local file path, or a PDF page using the `PDFToImageContent` converter.
### In a pipeline
When an `Agent` sits inside a pipeline, use `ChatPromptBuilder` with its string template format and the `| templatize_part` filter to pass images as structured content parts:
```python
from haystack import Pipeline
from haystack.components.agents import Agent
from haystack.components.builders import ChatPromptBuilder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ImageContent
template = """
{% message role="user" %}
{{ question }}
{{ image | templatize_part }}
{% endmessage %}
"""
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-5"),
system_prompt="You are a helpful assistant that can analyze images.",
)
prompt_builder = ChatPromptBuilder(
template=template,
required_variables=["question", "image"],
)
pipeline = Pipeline()
pipeline.add_component("prompt_builder", prompt_builder)
pipeline.add_component("agent", agent)
pipeline.connect("prompt_builder.prompt", "agent.messages")
# Download or provide your own chart image as "chart.png"
image = ImageContent.from_file_path("chart.png")
result = pipeline.run(
{
"prompt_builder": {"question": "What does this chart show?", "image": image},
},
)
print(result["agent"]["last_message"].text)
```
:::tip
See these cookbooks for complete multimodal agent examples:
- [Multimodal Agents](https://haystack.deepset.ai/cookbook/multimodal_intro#multimodal-agent) — image inputs and tool use with agents
- [Gemma Chat RAG](https://haystack.deepset.ai/cookbook/gemma_chat_rag) — vision model in a RAG pipeline
:::
## Multi-Agent Systems
You can wrap an `Agent` as a tool to build multi-agent systems where specialist agents handle focused subtasks and a coordinator agent plans and delegates.
See [Multi-Agent Systems](../../concepts/agents/multi-agent-systems.mdx) for a full guide, including the recommended `@tool` decorator approach for full interface control and `ComponentTool` for declarative configuration.
## MCP Integration
Agents work with MCP in two directions:
- **Consuming MCP tools**: Pass `MCPTool` or `MCPToolset` instances in the `tools` list to call tools on any MCP-compatible server (filesystem, browser, databases, and more). See [MCPTool](../../tools/mcptool.mdx) and [MCPToolset](../../tools/mcptoolset.mdx).
- **Exposing as an MCP server**: Use [Hayhooks](../../development/hayhooks.mdx) to deploy your agent and expose it as an MCP server, making it callable from any MCP-compatible client such as Claude Desktop or Cursor.
## Additional References
📖 Related docs:
- [State](./state.mdx) — managing shared data between tools
- [Hooks](./hooks.mdx) — running custom logic at defined points of the run loop
- [Human in the Loop](./human-in-the-loop.mdx) — intercepting tool calls for human review
- [Tool Result Offloading](./tool-result-offloading.mdx) — keeping large tool results out of the context window
📚 Tutorials:
- [Build a Tool-Calling Agent](https://haystack.deepset.ai/tutorials/43_building_a_tool_calling_agent)
- [Creating a Multi-Agent System](https://haystack.deepset.ai/tutorials/45_creating_a_multi_agent_system)
- [Human-in-the-Loop with Haystack Agents](https://haystack.deepset.ai/tutorials/47_human_in_the_loop_agent/)
🧑‍🍳 Cookbook:
- [Build a GitHub Issue Resolver Agent](https://haystack.deepset.ai/cookbook/github_issue_resolver_agent)
- [Multimodal Agents](https://haystack.deepset.ai/cookbook/multimodal_intro#multimodal-agent)
- [Gemma Chat RAG](https://haystack.deepset.ai/cookbook/gemma_chat_rag)