chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: "AnswerBuilder"
|
||||
id: answerbuilder
|
||||
slug: "/answerbuilder"
|
||||
description: "Use this component in pipelines that contain a Generator to parse its replies."
|
||||
---
|
||||
|
||||
# AnswerBuilder
|
||||
|
||||
Use this component in pipelines that contain a Generator to parse its replies.
|
||||
|
||||
<div className="key-value-table">
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| **Most common position in a pipeline** | Use in pipelines (such as a RAG pipeline) after a [Generator](../generators.mdx) component to create [`GeneratedAnswer`](../../concepts/data-classes.mdx#generatedanswer) objects from its replies. |
|
||||
| **Mandatory run variables** | `query`: A query string <br /> <br />`replies`: A list of strings, or a list of [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) objects that are replies from a Generator |
|
||||
| **Output variables** | `answers`: A list of `GeneratedAnswer` objects |
|
||||
| **API reference** | [Builders](/reference/builders-api) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/builders/answer_builder.py |
|
||||
| **Package name** | `haystack-ai` |
|
||||
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
`AnswerBuilder` takes a query and the replies a Generator returns as input and parses them into `GeneratedAnswer` objects. Optionally, it also takes documents and metadata from the Generator as inputs to enrich the `GeneratedAnswer` objects.
|
||||
|
||||
The `AnswerBuilder` works with both Chat and non-Chat Generators.
|
||||
|
||||
The optional `pattern` parameter defines how to extract answer texts from replies. It needs to be a regular expression with a maximum of one capture group. If a capture group is present, the text matched by the capture group is used as the answer. If no capture group is present, the whole match is used as the answer. If no `pattern` is set, the whole reply is used as the answer text.
|
||||
|
||||
The optional `reference_pattern` parameter can be set to a regular expression that parses referenced documents from the replies so that only those referenced documents are listed in the `GeneratedAnswer` objects. Haystack assumes that documents are referenced by their index in the list of input documents and that indices start at 1. For example, if you set the `reference_pattern` to _`\\[(\\d+)\\]`,_ it finds “1” in a string "This is an answer[1]". If `reference_pattern` is not set, all input documents are listed in the `GeneratedAnswer` objects.
|
||||
|
||||
## Usage
|
||||
|
||||
### On its own
|
||||
|
||||
Below is an example where we’re using the `AnswerBuilder` to parse a string that could be the reply received from a Generator using a custom regular expression. Any text other than the answer will not be included in the `GeneratedAnswer` object constructed by the builder.
|
||||
|
||||
```python
|
||||
from haystack.components.builders import AnswerBuilder
|
||||
|
||||
builder = AnswerBuilder(pattern="Answer: (.*)")
|
||||
builder.run(
|
||||
query="What's the answer?",
|
||||
replies=["This is an argument. Answer: This is the answer."],
|
||||
)
|
||||
```
|
||||
|
||||
### In a pipeline
|
||||
|
||||
Below is an example of a RAG pipeline where we use an `AnswerBuilder` to create `GeneratedAnswer` objects from the replies returned by a Generator. In addition to the text of the reply, these objects also hold the query, the referenced docs, and metadata returned by the Generator.
|
||||
|
||||
```python
|
||||
from haystack import Pipeline
|
||||
from haystack.document_stores.in_memory import InMemoryDocumentStore
|
||||
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.builders.chat_prompt_builder import ChatPromptBuilder
|
||||
from haystack.components.builders.answer_builder import AnswerBuilder
|
||||
from haystack.utils import Secret
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.dataclasses import Document
|
||||
|
||||
prompt_template = [
|
||||
ChatMessage.from_system("You are a helpful assistant."),
|
||||
ChatMessage.from_user(
|
||||
"Given these documents, answer the question.\nDocuments:\n"
|
||||
"{% for doc in documents %}{{ doc.content }}{% endfor %}\n"
|
||||
"Question: {{query}}\nAnswer:",
|
||||
),
|
||||
]
|
||||
|
||||
docs = [
|
||||
Document(content="The capital of France is Paris"),
|
||||
Document(content="The capital of England is London"),
|
||||
]
|
||||
document_store = InMemoryDocumentStore()
|
||||
document_store.write_documents(docs)
|
||||
|
||||
p = Pipeline()
|
||||
p.add_component(
|
||||
instance=InMemoryBM25Retriever(document_store=document_store),
|
||||
name="retriever",
|
||||
)
|
||||
p.add_component(
|
||||
instance=ChatPromptBuilder(
|
||||
template=prompt_template,
|
||||
required_variables={"query", "documents"},
|
||||
),
|
||||
name="prompt_builder",
|
||||
)
|
||||
p.add_component(
|
||||
instance=OpenAIChatGenerator(api_key=Secret.from_env_var("OPENAI_API_KEY")),
|
||||
name="llm",
|
||||
)
|
||||
p.add_component(instance=AnswerBuilder(), name="answer_builder")
|
||||
p.connect("retriever", "prompt_builder.documents")
|
||||
p.connect("prompt_builder", "llm.messages")
|
||||
p.connect("llm.replies", "answer_builder.replies")
|
||||
p.connect("retriever", "answer_builder.documents")
|
||||
|
||||
query = "What is the capital of France?"
|
||||
result = p.run(
|
||||
{
|
||||
"retriever": {"query": query},
|
||||
"prompt_builder": {"query": query},
|
||||
"answer_builder": {"query": query},
|
||||
},
|
||||
)
|
||||
|
||||
print(result)
|
||||
```
|
||||
@@ -0,0 +1,477 @@
|
||||
---
|
||||
title: "ChatPromptBuilder"
|
||||
id: chatpromptbuilder
|
||||
slug: "/chatpromptbuilder"
|
||||
description: "This component constructs prompts dynamically by processing chat messages."
|
||||
---
|
||||
|
||||
# ChatPromptBuilder
|
||||
|
||||
This component constructs prompts dynamically by processing chat messages.
|
||||
|
||||
<div className="key-value-table">
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| **Most common position in a pipeline** | Before a [Generator](../generators.mdx) |
|
||||
| **Mandatory init variables** | `template`: A list of [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) objects or a special string template. Needs to be provided either during init or run. |
|
||||
| **Mandatory run variables** | `**kwargs`: Any strings that should be used to render the prompt template. See [Variables](#variables) section for more details. |
|
||||
| **Output variables** | `prompt`: A dynamically constructed prompt |
|
||||
| **API reference** | [Builders](/reference/builders-api) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/builders/chat_prompt_builder.py |
|
||||
| **Package name** | `haystack-ai` |
|
||||
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
The `ChatPromptBuilder` component creates prompts using static or dynamic templates written in [Jinja2](https://palletsprojects.com/p/jinja/) syntax, by processing a list of chat messages or a special string template. The templates contain placeholders like `{{ variable }}` that are filled with values provided during runtime. You can use it for static prompts set at initialization or change the templates and variables dynamically while running.
|
||||
|
||||
To use it, start by providing a list of `ChatMessage` objects or a special string as the template.
|
||||
|
||||
[`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) is a data class that includes message content, a role (who generated the message, such as `user`, `assistant`, `system`, `tool`), and optional metadata.
|
||||
|
||||
The builder looks for placeholders in the template and identifies the required variables. You can also list these variables manually. During runtime, the `run` method takes the template and the variables, fills in the placeholders, and returns the completed prompt. If required variables are missing. If the template is invalid, the builder raises an error.
|
||||
|
||||
For example, you can create a simple translation prompt:
|
||||
|
||||
```python
|
||||
template = [ChatMessage.from_user("Translate to {{ target_language }}: {{ text }}")]
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
result = builder.run(target_language="French", text="Hello, how are you?")
|
||||
```
|
||||
|
||||
Or you can also replace the template at runtime with a new one:
|
||||
|
||||
```python
|
||||
new_template = [
|
||||
ChatMessage.from_user("Summarize in {{ target_language }}: {{ content }}"),
|
||||
]
|
||||
result = builder.run(
|
||||
template=new_template,
|
||||
target_language="English",
|
||||
content="A detailed paragraph.",
|
||||
)
|
||||
```
|
||||
|
||||
### Variables
|
||||
|
||||
The template variables found in the init template are used as input types for the component. If there are no `required_variables` set, all variables are considered optional by default. In this case, any missing variables are replaced with empty strings, which can lead to unintended behavior, especially in complex pipelines.
|
||||
|
||||
Use `required_variables` and `variables` to specify the input types and required variables:
|
||||
|
||||
- `required_variables`
|
||||
- Defines which template variables must be provided when the component runs.
|
||||
- If any required variable is missing, the component raises an error and halts execution.
|
||||
- You can:
|
||||
- Pass a list of required variable names (such as `["name"]`), or
|
||||
- Use `"*"` to mark all variables in the template as required.
|
||||
|
||||
- `variables`
|
||||
- Lists all variables that can appear in the template, whether required or optional.
|
||||
- Optional variables that aren't provided are replaced with an empty string in the rendered prompt.
|
||||
- This allows partial prompts to be constructed without errors, unless a variable is marked as required.
|
||||
|
||||
In the example below, only _name_ is required to run the component, while _topic_ is only an optional variable:
|
||||
|
||||
```python
|
||||
template = [
|
||||
ChatMessage.from_user("Hello, {{ name }}. How can I assist you with {{ topic }}?"),
|
||||
]
|
||||
|
||||
builder = ChatPromptBuilder(
|
||||
template=template,
|
||||
required_variables=["name"],
|
||||
variables=["name", "topic"],
|
||||
)
|
||||
|
||||
result = builder.run(name="Alice")
|
||||
# Output: "Hello, Alice. How can I assist you with ?"
|
||||
```
|
||||
|
||||
The component only waits for the required inputs before running.
|
||||
|
||||
### Roles
|
||||
|
||||
A [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) represents a single message in the conversation and can have one of three class methods that build the chat messages: `from_user`, `from_system`, or `from_assistant`. `from_user` messages are inputs provided by the user, such as a query or request. `from_system` messages provide context or instructions to guide the LLM’s behavior, such as setting a tone or purpose for the conversation. `from_assistant` defines the expected or actual response from the LLM.
|
||||
|
||||
Here’s how the roles work together in a `ChatPromptBuilder`:
|
||||
|
||||
```python
|
||||
system_message = ChatMessage.from_system(
|
||||
"You are an assistant helping tourists in {{ language }}.",
|
||||
)
|
||||
|
||||
user_message = ChatMessage.from_user("What are the best places to visit in {{ city }}?")
|
||||
|
||||
assistant_message = ChatMessage.from_assistant(
|
||||
"The best places to visit in {{ city }} include the Eiffel Tower, Louvre Museum, and Montmartre.",
|
||||
)
|
||||
```
|
||||
|
||||
### String Templates
|
||||
|
||||
Instead of a list of `ChatMessage` objects, you can also express the template as a special string.
|
||||
|
||||
This template format allows you to define `ChatMessage` sequences using Jinja2 syntax. Each `{% message %}` block defines a single message with a specific role, and you can insert dynamic content using `{{ variables }}`.
|
||||
|
||||
Compared to using a list of `ChatMessage`s, this format is more flexible and allows including structured parts like images in the templatized `ChatMessage`; to better understand this use case, check out the [multimodal example](#multimodal) in the Usage section below.
|
||||
|
||||
#### The `insert` Tag
|
||||
|
||||
String templates also support an `{% insert %}` tag. It is a placeholder that evaluates an expression to a `ChatMessage` or a list of `ChatMessage` objects and expands it into the prompt, so messages provided at runtime can be interleaved with literal `{% message %}` blocks. For example, you can wrap the runtime messages with a system message above and a templated user message below, then pass the messages (and any template variables) at run time:
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
template = """
|
||||
{% message role="system" %}You are a helpful assistant.{% endmessage %}
|
||||
{% insert messages %}
|
||||
{% message role="user" %}{{ query }}{% endmessage %}
|
||||
"""
|
||||
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
result = builder.run(
|
||||
messages=[ChatMessage.from_user("Hi"), ChatMessage.from_assistant("Hello!")],
|
||||
query="What's the weather?",
|
||||
)
|
||||
# result["prompt"] -> [system, user "Hi", assistant "Hello!", user "What's the weather?"]
|
||||
```
|
||||
|
||||
All content types (tool calls, tool call results, images, reasoning, `name`, and `meta`) round trip without loss. A missing or empty value expands to nothing.
|
||||
|
||||
The expression can be a plain variable (`{% insert messages %}`), a slice or index (`{% insert messages[-1:] %}`, `{% insert messages[-1] %}`), or a combination of variables (`{% insert previous + current %}`). Multiple `{% insert %}` tags can be used in a single template, so the runtime messages can be split, reordered, or repeated across different positions.
|
||||
|
||||
### Jinja2 Time Extension
|
||||
|
||||
`PromptBuilder` supports the Jinja2 TimeExtension, which allows you to work with datetime formats.
|
||||
|
||||
The Time Extension provides two main features:
|
||||
|
||||
1. A `now` tag that gives you access to the current time,
|
||||
2. Date/time formatting capabilities through Python's datetime module.
|
||||
|
||||
To use the Jinja2 TimeExtension, you need to install a dependency with:
|
||||
|
||||
```shell
|
||||
pip install arrow>=1.3.0
|
||||
```
|
||||
|
||||
#### The `now` Tag
|
||||
|
||||
The `now` tag creates a datetime object representing the current time, which you can then store in a variable:
|
||||
|
||||
```jinja2
|
||||
{% now 'utc' as current_time %}
|
||||
The current UTC time is: {{ current_time }}
|
||||
```
|
||||
|
||||
You can specify different timezones:
|
||||
|
||||
```jinja2
|
||||
{% now 'America/New_York' as ny_time %}
|
||||
The time in New York is: {{ ny_time }}
|
||||
```
|
||||
|
||||
If you don't specify a timezone, your system's local timezone will be used:
|
||||
|
||||
```jinja2
|
||||
{% now as local_time %}
|
||||
Local time: {{ local_time }}
|
||||
```
|
||||
|
||||
#### Date Formatting
|
||||
|
||||
You can format the datetime objects using Python's `strftime` syntax:
|
||||
|
||||
```jinja2
|
||||
{% now as current_time %}
|
||||
Formatted date: {{ current_time.strftime('%Y-%m-%d %H:%M:%S') }}
|
||||
```
|
||||
|
||||
The common format codes are:
|
||||
|
||||
- `%Y`: 4-digit year (for example, 2025)
|
||||
- `%m`: Month as a zero-padded number (01-12)
|
||||
- `%d`: Day as a zero-padded number (01-31)
|
||||
- `%H`: Hour (24-hour clock) as a zero-padded number (00-23)
|
||||
- `%M`: Minute as a zero-padded number (00-59)
|
||||
- `%S`: Second as a zero-padded number (00-59)
|
||||
|
||||
#### Example
|
||||
|
||||
```python
|
||||
from haystack.components.builders.chat_prompt_builder import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
template = [
|
||||
ChatMessage.from_user("Current date is: {% now 'UTC' %}"),
|
||||
ChatMessage.from_assistant("Thank you for providing the date"),
|
||||
ChatMessage.from_user("Yesterday was: {% now 'UTC' - 'days=1' %}"),
|
||||
]
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
|
||||
result = builder.run()["prompt"]
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### On its own
|
||||
|
||||
#### With static template
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
template = [
|
||||
ChatMessage.from_user(
|
||||
"Translate to {{ target_language }}. Context: {{ snippet }}; Translation:",
|
||||
),
|
||||
]
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
builder.run(target_language="spanish", snippet="I can't speak spanish.")
|
||||
```
|
||||
|
||||
#### With special string template
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
template = """
|
||||
{% message role="user" %}
|
||||
Hello, my name is {{name}}!
|
||||
{% endmessage %}
|
||||
"""
|
||||
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
result = builder.run(name="John")
|
||||
|
||||
assert result["prompt"] == [ChatMessage.from_user("Hello, my name is John!")]
|
||||
```
|
||||
|
||||
#### Specifying name and meta in a ChatMessage
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
template = """
|
||||
{% message role="user" name="John" meta={"key": "value"} %}
|
||||
Hello from {{country}}!
|
||||
{% endmessage %}
|
||||
"""
|
||||
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
result = builder.run(country="Italy")
|
||||
assert result["prompt"] == [
|
||||
ChatMessage.from_user("Hello from Italy!", name="John", meta={"key": "value"}),
|
||||
]
|
||||
```
|
||||
|
||||
#### Multiple ChatMessages with different roles
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
template = """
|
||||
{% message role="system" %}
|
||||
You are a {{adjective}} assistant.
|
||||
{% endmessage %}
|
||||
|
||||
{% message role="user" %}
|
||||
Hello, my name is {{name}}!
|
||||
{% endmessage %}
|
||||
|
||||
{% message role="assistant" %}
|
||||
Hello, {{name}}! How can I help you today?
|
||||
{% endmessage %}
|
||||
"""
|
||||
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
result = builder.run(name="John", adjective="helpful")
|
||||
assert result["prompt"] == [
|
||||
ChatMessage.from_system("You are a helpful assistant."),
|
||||
ChatMessage.from_user("Hello, my name is John!"),
|
||||
ChatMessage.from_assistant("Hello, John! How can I help you today?"),
|
||||
]
|
||||
```
|
||||
|
||||
#### Overriding static template at runtime
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
template = [
|
||||
ChatMessage.from_user(
|
||||
"Translate to {{ target_language }}. Context: {{ snippet }}; Translation:",
|
||||
),
|
||||
]
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
builder.run(target_language="spanish", snippet="I can't speak spanish.")
|
||||
|
||||
summary_template = [
|
||||
ChatMessage.from_user(
|
||||
"Translate to {{ target_language }} and summarize. Context: {{ snippet }}; Summary:",
|
||||
),
|
||||
]
|
||||
builder.run(
|
||||
target_language="spanish",
|
||||
snippet="I can't speak spanish.",
|
||||
template=summary_template,
|
||||
)
|
||||
```
|
||||
|
||||
#### Multimodal
|
||||
|
||||
The `| templatize_part` filter in the example below tells the template engine to insert structured (non-text) objects, such as images, into the message content. These are treated differently from plain text and are rendered as special content parts in the final `ChatMessage`.
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage, ImageContent
|
||||
|
||||
template = """
|
||||
{% message role="user" meta={"key": "value"}%}
|
||||
Hello! I am {{user_name}}. What's the difference between the following images?
|
||||
{% for image in images %}
|
||||
{{ image | templatize_part }}
|
||||
{% endfor %}
|
||||
{% endmessage %}
|
||||
"""
|
||||
builder = ChatPromptBuilder(template=template)
|
||||
images = [
|
||||
ImageContent.from_file_path("apple.jpg"),
|
||||
ImageContent.from_file_path("kiwi.jpg"),
|
||||
]
|
||||
result = builder.run(user_name="John", images=images)
|
||||
|
||||
assert result["prompt"] == [
|
||||
ChatMessage.from_user(
|
||||
content_parts=[
|
||||
"Hello! I am John. What's the difference between the following images?",
|
||||
*images,
|
||||
],
|
||||
meta={"key": "value"},
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
### In a pipeline
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack import Pipeline
|
||||
from haystack.utils import Secret
|
||||
|
||||
# no parameter init, we don't use any runtime template variables
|
||||
prompt_builder = ChatPromptBuilder()
|
||||
llm = OpenAIChatGenerator()
|
||||
|
||||
pipe = Pipeline()
|
||||
pipe.add_component("prompt_builder", prompt_builder)
|
||||
pipe.add_component("llm", llm)
|
||||
pipe.connect("prompt_builder.prompt", "llm.messages")
|
||||
|
||||
location = "Berlin"
|
||||
language = "English"
|
||||
system_message = ChatMessage.from_system(
|
||||
"You are an assistant giving information to tourists in {{language}}",
|
||||
)
|
||||
messages = [system_message, ChatMessage.from_user("Tell me about {{location}}")]
|
||||
|
||||
res = pipe.run(
|
||||
data={
|
||||
"prompt_builder": {
|
||||
"template_variables": {"location": location, "language": language},
|
||||
"template": messages,
|
||||
},
|
||||
},
|
||||
)
|
||||
print(res)
|
||||
```
|
||||
|
||||
Then, you could ask about the weather forecast for the said location. The `ChatPromptBuilder` fills in the template with the new `day_count` variable and passes it to an LLM once again:
|
||||
|
||||
```python
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack import Pipeline
|
||||
from haystack.utils import Secret
|
||||
|
||||
# no parameter init, we don't use any runtime template variables
|
||||
prompt_builder = ChatPromptBuilder()
|
||||
llm = OpenAIChatGenerator()
|
||||
|
||||
pipe = Pipeline()
|
||||
pipe.add_component("prompt_builder", prompt_builder)
|
||||
pipe.add_component("llm", llm)
|
||||
pipe.connect("prompt_builder.prompt", "llm.messages")
|
||||
|
||||
location = "Berlin"
|
||||
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user(
|
||||
"What's the weather forecast for {{location}} in the next {{day_count}} days?",
|
||||
),
|
||||
]
|
||||
res = pipe.run(
|
||||
data={
|
||||
"prompt_builder": {
|
||||
"template_variables": {"location": location, "day_count": "5"},
|
||||
"template": messages,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
print(res)
|
||||
```
|
||||
|
||||
### In YAML
|
||||
|
||||
This is the YAML representation of the pipeline shown above. It dynamically constructs a prompt and generates an answer using a chat model.
|
||||
|
||||
```yaml
|
||||
components:
|
||||
llm:
|
||||
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-4o-mini
|
||||
organization: null
|
||||
streaming_callback: null
|
||||
timeout: null
|
||||
tools: null
|
||||
tools_strict: false
|
||||
type: haystack.components.generators.chat.openai.OpenAIChatGenerator
|
||||
prompt_builder:
|
||||
init_parameters:
|
||||
required_variables: null
|
||||
template: null
|
||||
variables: null
|
||||
type: haystack.components.builders.chat_prompt_builder.ChatPromptBuilder
|
||||
connection_type_validation: true
|
||||
connections:
|
||||
- receiver: llm.messages
|
||||
sender: prompt_builder.prompt
|
||||
max_runs_per_component: 100
|
||||
metadata: {}
|
||||
```
|
||||
|
||||
## Additional References
|
||||
|
||||
🧑🍳 Cookbook: [Advanced Prompt Customization for Anthropic](https://haystack.deepset.ai/cookbook/prompt_customization_for_anthropic)
|
||||
@@ -0,0 +1,306 @@
|
||||
---
|
||||
title: "PromptBuilder"
|
||||
id: promptbuilder
|
||||
slug: "/promptbuilder"
|
||||
description: "Use this component in pipelines before a Generator to render a prompt template and fill in variable values."
|
||||
---
|
||||
|
||||
# PromptBuilder
|
||||
|
||||
Use this component in pipelines before a Generator to render a prompt template and fill in variable values.
|
||||
|
||||
<div className="key-value-table">
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| **Most common position in a pipeline** | In a querying pipeline, before a [Generator](../generators.mdx) |
|
||||
| **Mandatory init variables** | `template`: A prompt template string that uses Jinja2 syntax |
|
||||
| **Mandatory run variables** | `**kwargs`: Any strings that should be used to render the prompt template. See [Variables](#variables) section for more details. |
|
||||
| **Output variables** | `prompt`: A string that represents the rendered prompt template |
|
||||
| **API reference** | [Builders](/reference/builders-api) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/builders/prompt_builder.py |
|
||||
| **Package name** | `haystack-ai` |
|
||||
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
`PromptBuilder` is initialized with a prompt template and renders it by filling in parameters passed through keyword arguments, `kwargs`. With `kwargs`, you can pass a variable number of keyword arguments so that any variable used in the prompt template can be specified with the desired value. Values for all variables appearing in the prompt template need to be provided through the `kwargs`.
|
||||
|
||||
The template that is provided to the `PromptBuilder` during initialization needs to conform to the [Jinja2](https://palletsprojects.com/p/jinja/) template language.
|
||||
|
||||
### Variables
|
||||
|
||||
The template variables found in the init template are used as input types for the component. If there are no `required_variables` set, all variables are considered optional by default. In this case, any missing variables are replaced with empty strings, which can lead to unintended behavior, especially in complex pipelines.
|
||||
|
||||
Use `required_variables` and `variables` to specify the input types and required variables:
|
||||
|
||||
- `required_variables`
|
||||
- Defines which template variables must be provided when the component runs.
|
||||
- If any required variable is missing, the component raises an error and halts execution.
|
||||
- You can:
|
||||
- Pass a list of required variable names (such as `["query"]`), or
|
||||
- Use `"*"` to mark all variables in the template as required.
|
||||
|
||||
- `variables`
|
||||
- Lists all variables that can appear in the template, whether required or optional.
|
||||
- Optional variables that aren't provided are replaced with an empty string in the rendered prompt.
|
||||
- This allows partial prompts to be constructed without errors, unless a variable is marked as required.
|
||||
|
||||
```python
|
||||
from haystack.components.builders import PromptBuilder
|
||||
|
||||
# All variables optional (default to empty string)
|
||||
builder = PromptBuilder(
|
||||
template="Hello {{name}}! {{greeting}}",
|
||||
required_variables=[], # or omit this parameter entirely
|
||||
)
|
||||
|
||||
# Some variables required
|
||||
builder = PromptBuilder(
|
||||
template="Hello {{name}}! {{greeting}}",
|
||||
required_variables=["name"], # 'greeting' remains optional
|
||||
)
|
||||
```
|
||||
|
||||
The component only waits for the required inputs before running.
|
||||
|
||||
### Jinja2 Time Extension
|
||||
|
||||
`PromptBuilder` supports the Jinja2 TimeExtension, which allows you to work with datetime formats.
|
||||
|
||||
The Time Extension provides two main features:
|
||||
|
||||
1. A `now` tag that gives you access to the current time,
|
||||
2. Date/time formatting capabilities through Python's datetime module.
|
||||
|
||||
To use the Jinja2 TimeExtension, you need to install a dependency with:
|
||||
|
||||
```shell
|
||||
pip install arrow>=1.3.0
|
||||
```
|
||||
|
||||
#### The `now` Tag
|
||||
|
||||
The `now` tag creates a datetime object representing the current time, which you can then store in a variable:
|
||||
|
||||
```jinja2
|
||||
{% now 'utc' as current_time %}
|
||||
The current UTC time is: {{ current_time }}
|
||||
```
|
||||
|
||||
You can specify different timezones:
|
||||
|
||||
```jinja2
|
||||
{% now 'America/New_York' as ny_time %}
|
||||
The time in New York is: {{ ny_time }}
|
||||
```
|
||||
|
||||
If you don't specify a timezone, your system's local timezone will be used:
|
||||
|
||||
```jinja2
|
||||
{% now as local_time %}
|
||||
Local time: {{ local_time }}
|
||||
```
|
||||
|
||||
#### Date Formatting
|
||||
|
||||
You can format the datetime objects using Python's `strftime` syntax:
|
||||
|
||||
```jinja2
|
||||
{% now as current_time %}
|
||||
Formatted date: {{ current_time.strftime('%Y-%m-%d %H:%M:%S') }}
|
||||
```
|
||||
|
||||
The common format codes are:
|
||||
|
||||
- `%Y`: 4-digit year (for example, 2025)
|
||||
- `%m`: Month as a zero-padded number (01-12)
|
||||
- `%d`: Day as a zero-padded number (01-31)
|
||||
- `%H`: Hour (24-hour clock) as a zero-padded number (00-23)
|
||||
- `%M`: Minute as a zero-padded number (00-59)
|
||||
- `%S`: Second as a zero-padded number (00-59)
|
||||
|
||||
#### Example
|
||||
|
||||
```python
|
||||
from haystack.components.builders import PromptBuilder
|
||||
|
||||
# Define template using Jinja-style formatting
|
||||
template = """
|
||||
Current date is: {% now 'UTC' %}
|
||||
Thank you for providing the date
|
||||
Yesterday was: {% now 'UTC' - 'days=1' %}
|
||||
"""
|
||||
|
||||
builder = PromptBuilder(template=template)
|
||||
|
||||
result = builder.run()["prompt"]
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### On its own
|
||||
|
||||
Below is an example of using the `PromptBuilder` to render a prompt template and fill it with `target_language` and `snippet`. The PromptBuilder returns a prompt with the string `Translate the following context to spanish. Context: I can't speak spanish.; Translation:`.
|
||||
|
||||
```python
|
||||
from haystack.components.builders import PromptBuilder
|
||||
|
||||
template = "Translate the following context to {{ target_language }}. Context: {{ snippet }}; Translation:"
|
||||
builder = PromptBuilder(template=template)
|
||||
builder.run(target_language="spanish", snippet="I can't speak spanish.")
|
||||
```
|
||||
|
||||
### In a pipeline
|
||||
|
||||
Below is an example of a RAG pipeline where we use a `PromptBuilder` to render a custom prompt template and fill it with the contents of retrieved documents and a query. The rendered prompt is then sent to a Generator.
|
||||
|
||||
```python
|
||||
from haystack import Pipeline, Document
|
||||
from haystack.utils import Secret
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.builders.prompt_builder import PromptBuilder
|
||||
|
||||
# in a real world use case documents could come from a retriever, web, or any other source
|
||||
documents = [
|
||||
Document(content="Joe lives in Berlin"),
|
||||
Document(content="Joe is a software engineer"),
|
||||
]
|
||||
prompt_template = """
|
||||
Given these documents, answer the question.\nDocuments:
|
||||
{% for doc in documents %}
|
||||
{{ doc.content }}
|
||||
{% endfor %}
|
||||
|
||||
\nQuestion: {{query}}
|
||||
\nAnswer:
|
||||
"""
|
||||
p = Pipeline()
|
||||
p.add_component(instance=PromptBuilder(template=prompt_template), name="prompt_builder")
|
||||
p.add_component(
|
||||
instance=OpenAIChatGenerator(api_key=Secret.from_env_var("OPENAI_API_KEY")),
|
||||
name="llm",
|
||||
)
|
||||
p.connect("prompt_builder", "llm")
|
||||
|
||||
question = "Where does Joe live?"
|
||||
result = p.run({"prompt_builder": {"documents": documents, "query": question}})
|
||||
print(result)
|
||||
```
|
||||
|
||||
#### Changing the template at runtime (Prompt Engineering)
|
||||
|
||||
`PromptBuilder` allows you to switch the prompt template of an existing pipeline. The example below builds on top of the existing pipeline in the previous section. We are invoking the existing pipeline with a new prompt template:
|
||||
|
||||
```python
|
||||
documents = [
|
||||
Document(content="Joe lives in Berlin", meta={"name": "doc1"}),
|
||||
Document(content="Joe is a software engineer", meta={"name": "doc1"}),
|
||||
]
|
||||
new_template = """
|
||||
You are a helpful assistant.
|
||||
Given these documents, answer the question.
|
||||
Documents:
|
||||
{% for doc in documents %}
|
||||
Document {{ loop.index }}:
|
||||
Document name: {{ doc.meta['name'] }}
|
||||
{{ doc.content }}
|
||||
{% endfor %}
|
||||
|
||||
Question: {{ query }}
|
||||
Answer:
|
||||
"""
|
||||
p.run(
|
||||
{
|
||||
"prompt_builder": {
|
||||
"documents": documents,
|
||||
"query": question,
|
||||
"template": new_template,
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
If you want to use different variables during prompt engineering than in the default template, you can do so by setting `PromptBuilder`'s variables init parameter accordingly.
|
||||
|
||||
#### Overwriting variables at runtime
|
||||
|
||||
In case you want to overwrite the values of variables, you can use `template_variables` during runtime, as shown below:
|
||||
|
||||
```python
|
||||
language_template = """
|
||||
You are a helpful assistant.
|
||||
Given these documents, answer the question.
|
||||
Documents:
|
||||
{% for doc in documents %}
|
||||
Document {{ loop.index }}:
|
||||
Document name: {{ doc.meta['name'] }}
|
||||
{{ doc.content }}
|
||||
{% endfor %}
|
||||
|
||||
Question: {{ query }}
|
||||
Please provide your answer in {{ answer_language | default('English') }}
|
||||
Answer:
|
||||
"""
|
||||
p.run(
|
||||
{
|
||||
"prompt_builder": {
|
||||
"documents": documents,
|
||||
"query": question,
|
||||
"template": language_template,
|
||||
"template_variables": {"answer_language": "German"},
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Note that `language_template` introduces `answer_language` variable which is not bound to any pipeline variable. If not set otherwise, it would use its default value, "English". In this example, we overwrite its value to "German".
|
||||
The `template_variables` allows you to overwrite pipeline variables (such as documents) as well.
|
||||
|
||||
### In YAML
|
||||
|
||||
This is the YAML representation of the RAG pipeline shown above. It renders a custom prompt template by filling it with the contents of retrieved documents and a query, then sends the rendered prompt to a generator.
|
||||
|
||||
```yaml
|
||||
components:
|
||||
llm:
|
||||
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-mini
|
||||
organization: null
|
||||
streaming_callback: null
|
||||
timeout: null
|
||||
tools: null
|
||||
tools_strict: false
|
||||
type: haystack.components.generators.chat.openai.OpenAIChatGenerator
|
||||
prompt_builder:
|
||||
init_parameters:
|
||||
required_variables: '*'
|
||||
template: "\n Given these documents, answer the question.\nDocuments:\n \
|
||||
\ {% for doc in documents %}\n {{ doc.content }}\n {% endfor %}\n\
|
||||
\n \nQuestion: {{query}}\n \nAnswer:\n "
|
||||
variables: null
|
||||
type: haystack.components.builders.prompt_builder.PromptBuilder
|
||||
connection_type_validation: true
|
||||
connections:
|
||||
- receiver: llm.messages
|
||||
sender: prompt_builder.prompt
|
||||
max_runs_per_component: 100
|
||||
metadata: {}
|
||||
```
|
||||
|
||||
## Additional References
|
||||
|
||||
🧑🍳 Cookbooks:
|
||||
|
||||
- [Advanced Prompt Customization for Anthropic](https://haystack.deepset.ai/cookbook/prompt_customization_for_anthropic)
|
||||
- [Prompt Optimization with DSPy](https://haystack.deepset.ai/cookbook/prompt_optimization_with_dspy)
|
||||
Reference in New Issue
Block a user