/ docs-website / versioned_docs / version-2.21 / pipeline-components / builders / chatpromptbuilder.mdx
chatpromptbuilder.mdx
1 --- 2 title: "ChatPromptBuilder" 3 id: chatpromptbuilder 4 slug: "/chatpromptbuilder" 5 description: "This component constructs prompts dynamically by processing chat messages." 6 --- 7 8 # ChatPromptBuilder 9 10 This component constructs prompts dynamically by processing chat messages. 11 12 <div className="key-value-table"> 13 14 | | | 15 | --- | --- | 16 | **Most common position in a pipeline** | Before a [Generator](../generators.mdx) | 17 | **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. | 18 | **Mandatory run variables** | `**kwargs`: Any strings that should be used to render the prompt template. See [Variables](#variables) section for more details. | 19 | **Output variables** | `prompt`: A dynamically constructed prompt | 20 | **API reference** | [Builders](/reference/builders-api) | 21 | **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/builders/chat_prompt_builder.py | 22 23 </div> 24 25 ## Overview 26 27 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. 28 29 To use it, start by providing a list of `ChatMessage` objects or a special string as the template. 30 31 [`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. 32 33 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. 34 35 For example, you can create a simple translation prompt: 36 37 ```python 38 template = [ChatMessage.from_user("Translate to {{ target_language }}: {{ text }}")] 39 builder = ChatPromptBuilder(template=template) 40 result = builder.run(target_language="French", text="Hello, how are you?") 41 ``` 42 43 Or you can also replace the template at runtime with a new one: 44 45 ```python 46 new_template = [ 47 ChatMessage.from_user("Summarize in {{ target_language }}: {{ content }}"), 48 ] 49 result = builder.run( 50 template=new_template, 51 target_language="English", 52 content="A detailed paragraph.", 53 ) 54 ``` 55 56 ### Variables 57 58 The template variables found in the init template are used as input types for the component. If there are no `required_vairables` 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. 59 60 Use `required_variables` and `variables` to specify the input types and required variables: 61 62 - `required_variables` 63 - Defines which template variables must be provided when the component runs. 64 - If any required variable is missing, the component raises an error and halts execution. 65 - You can: 66 - Pass a list of required variable names (such as `["name"]`), or 67 - Use `"*"` to mark all variables in the template as required. 68 69 - `variables` 70 - Lists all variables that can appear in the template, whether required or optional. 71 - Optional variables that aren't provided are replaced with an empty string in the rendered prompt. 72 - This allows partial prompts to be constructed without errors, unless a variable is marked as required. 73 74 In the example below, only _name_ is required to run the component, while _topic_ is only an optional variable: 75 76 ```python 77 template = [ 78 ChatMessage.from_user("Hello, {{ name }}. How can I assist you with {{ topic }}?"), 79 ] 80 81 builder = ChatPromptBuilder( 82 template=template, 83 required_variables=["name"], 84 variables=["name", "topic"], 85 ) 86 87 result = builder.run(name="Alice") 88 ## Output: "Hello, Alice. How can I assist you with ?" 89 ``` 90 91 The component only waits for the required inputs before running. 92 93 ### Roles 94 95 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. 96 97 Here’s how the roles work together in a `ChatPromptBuilder`: 98 99 ```python 100 system_message = ChatMessage.from_system( 101 "You are an assistant helping tourists in {{ language }}.", 102 ) 103 104 user_message = ChatMessage.from_user("What are the best places to visit in {{ city }}?") 105 106 assistant_message = ChatMessage.from_assistant( 107 "The best places to visit in {{ city }} include the Eiffel Tower, Louvre Museum, and Montmartre.", 108 ) 109 ``` 110 111 ### String Templates 112 113 Instead of a list of `ChatMessage` objects, you can also express the template as a special string. 114 115 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 }}`. 116 117 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. 118 119 ### Jinja2 Time Extension 120 121 `PromptBuilder` supports the Jinja2 TimeExtension, which allows you to work with datetime formats. 122 123 The Time Extension provides two main features: 124 125 1. A `now` tag that gives you access to the current time, 126 2. Date/time formatting capabilities through Python's datetime module. 127 128 To use the Jinja2 TimeExtension, you need to install a dependency with: 129 130 ```shell 131 pip install arrow>=1.3.0 132 ``` 133 134 #### The `now` Tag 135 136 The `now` tag creates a datetime object representing the current time, which you can then store in a variable: 137 138 ```jinja2 139 {% now 'utc' as current_time %} 140 The current UTC time is: {{ current_time }} 141 ``` 142 143 You can specify different timezones: 144 145 ```jinja2 146 {% now 'America/New_York' as ny_time %} 147 The time in New York is: {{ ny_time }} 148 ``` 149 150 If you don't specify a timezone, your system's local timezone will be used: 151 152 ```jinja2 153 {% now as local_time %} 154 Local time: {{ local_time }} 155 ``` 156 157 #### Date Formatting 158 159 You can format the datetime objects using Python's `strftime` syntax: 160 161 ```jinja2 162 {% now as current_time %} 163 Formatted date: {{ current_time.strftime('%Y-%m-%d %H:%M:%S') }} 164 ``` 165 166 The common format codes are: 167 168 - `%Y`: 4-digit year (for example, 2025) 169 - `%m`: Month as a zero-padded number (01-12) 170 - `%d`: Day as a zero-padded number (01-31) 171 - `%H`: Hour (24-hour clock) as a zero-padded number (00-23) 172 - `%M`: Minute as a zero-padded number (00-59) 173 - `%S`: Second as a zero-padded number (00-59) 174 175 #### Example 176 177 ```python 178 from haystack.components.builders.chat_prompt_builder import ChatPromptBuilder 179 from haystack.dataclasses import ChatMessage 180 181 template = [ 182 ChatMessage.from_user("Current date is: {% now 'UTC' %}"), 183 ChatMessage.from_assistant("Thank you for providing the date"), 184 ChatMessage.from_user("Yesterday was: {% now 'UTC' - 'days=1' %}"), 185 ] 186 builder = ChatPromptBuilder(template=template) 187 188 result = builder.run()["prompt"] 189 ``` 190 191 ## Usage 192 193 ### On its own 194 195 #### With static template 196 197 ```python 198 from haystack.components.builders import ChatPromptBuilder 199 from haystack.dataclasses import ChatMessage 200 201 template = [ 202 ChatMessage.from_user( 203 "Translate to {{ target_language }}. Context: {{ snippet }}; Translation:", 204 ), 205 ] 206 builder = ChatPromptBuilder(template=template) 207 builder.run(target_language="spanish", snippet="I can't speak spanish.") 208 ``` 209 210 #### With special string template 211 212 ```python 213 from haystack.components.builders import ChatPromptBuilder 214 from haystack.dataclasses import ChatMessage 215 216 template = """ 217 {% message role="user" %} 218 Hello, my name is {{name}}! 219 {% endmessage %} 220 """ 221 222 builder = ChatPromptBuilder(template=template) 223 result = builder.run(name="John") 224 225 assert result["prompt"] == [ChatMessage.from_user("Hello, my name is John!")] 226 ``` 227 228 #### Specifying name and meta in a ChatMessage 229 230 ```python 231 from haystack.components.builders import ChatPromptBuilder 232 from haystack.dataclasses import ChatMessage 233 234 template = """ 235 {% message role="user" name="John" meta={"key": "value"} %} 236 Hello from {{country}}! 237 {% endmessage %} 238 """ 239 240 builder = ChatPromptBuilder(template=template) 241 result = builder.run(country="Italy") 242 assert result["prompt"] == [ 243 ChatMessage.from_user("Hello from Italy!", name="John", meta={"key": "value"}), 244 ] 245 ``` 246 247 #### Multiple ChatMessages with different roles 248 249 ```python 250 from haystack.components.builders import ChatPromptBuilder 251 from haystack.dataclasses import ChatMessage 252 253 template = """ 254 {% message role="system" %} 255 You are a {{adjective}} assistant. 256 {% endmessage %} 257 258 {% message role="user" %} 259 Hello, my name is {{name}}! 260 {% endmessage %} 261 262 {% message role="assistant" %} 263 Hello, {{name}}! How can I help you today? 264 {% endmessage %} 265 """ 266 267 builder = ChatPromptBuilder(template=template) 268 result = builder.run(name="John", adjective="helpful") 269 assert result["prompt"] == [ 270 ChatMessage.from_system("You are a helpful assistant."), 271 ChatMessage.from_user("Hello, my name is John!"), 272 ChatMessage.from_assistant("Hello, John! How can I help you today?"), 273 ] 274 ``` 275 276 #### Overriding static template at runtime 277 278 ```python 279 from haystack.components.builders import ChatPromptBuilder 280 from haystack.dataclasses import ChatMessage 281 282 template = [ 283 ChatMessage.from_user( 284 "Translate to {{ target_language }}. Context: {{ snippet }}; Translation:", 285 ), 286 ] 287 builder = ChatPromptBuilder(template=template) 288 builder.run(target_language="spanish", snippet="I can't speak spanish.") 289 290 summary_template = [ 291 ChatMessage.from_user( 292 "Translate to {{ target_language }} and summarize. Context: {{ snippet }}; Summary:", 293 ), 294 ] 295 builder.run( 296 target_language="spanish", 297 snippet="I can't speak spanish.", 298 template=summary_template, 299 ) 300 ``` 301 302 #### Multimodal 303 304 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`. 305 306 ```python 307 from haystack.components.builders import ChatPromptBuilder 308 from haystack.dataclasses import ChatMessage, ImageContent 309 310 template = """ 311 {% message role="user" %} 312 Hello! I am {{user_name}}. What's the difference between the following images? 313 {% for image in images %} 314 {{ image | templatize_part }} 315 {% endfor %} 316 {% endmessage %} 317 """ 318 builder = ChatPromptBuilder(template=template) 319 images = [ 320 ImageContent.from_file_path("apple.jpg"), 321 ImageContent.from_file_path("kiwi.jpg"), 322 ] 323 result = builder.run(user_name="John", images=images) 324 325 assert result["prompt"] == [ 326 ChatMessage.from_user( 327 content_parts=[ 328 "Hello! I am John. What's the difference between the following images?", 329 *images, 330 ], 331 ), 332 ] 333 ``` 334 335 ### In a pipeline 336 337 ```python 338 from haystack.components.builders import ChatPromptBuilder 339 from haystack.components.generators.chat import OpenAIChatGenerator 340 from haystack.dataclasses import ChatMessage 341 from haystack import Pipeline 342 from haystack.utils import Secret 343 344 ## no parameter init, we don't use any runtime template variables 345 prompt_builder = ChatPromptBuilder() 346 llm = OpenAIChatGenerator() 347 348 pipe = Pipeline() 349 pipe.add_component("prompt_builder", prompt_builder) 350 pipe.add_component("llm", llm) 351 pipe.connect("prompt_builder.prompt", "llm.messages") 352 353 location = "Berlin" 354 language = "English" 355 system_message = ChatMessage.from_system( 356 "You are an assistant giving information to tourists in {{language}}", 357 ) 358 messages = [system_message, ChatMessage.from_user("Tell me about {{location}}")] 359 360 res = pipe.run( 361 data={ 362 "prompt_builder": { 363 "template_variables": {"location": location, "language": language}, 364 "template": messages, 365 }, 366 }, 367 ) 368 print(res) 369 ``` 370 371 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: 372 373 ```python 374 from haystack.components.builders import ChatPromptBuilder 375 from haystack.components.generators.chat import OpenAIChatGenerator 376 from haystack.dataclasses import ChatMessage 377 from haystack import Pipeline 378 from haystack.utils import Secret 379 380 ## no parameter init, we don't use any runtime template variables 381 prompt_builder = ChatPromptBuilder() 382 llm = OpenAIChatGenerator() 383 384 pipe = Pipeline() 385 pipe.add_component("prompt_builder", prompt_builder) 386 pipe.add_component("llm", llm) 387 pipe.connect("prompt_builder.prompt", "llm.messages") 388 389 location = "Berlin" 390 391 messages = [ 392 system_message, 393 ChatMessage.from_user( 394 "What's the weather forecast for {{location}} in the next {{day_count}} days?", 395 ), 396 ] 397 res = pipe.run( 398 data={ 399 "prompt_builder": { 400 "template_variables": {"location": location, "day_count": "5"}, 401 "template": messages, 402 }, 403 }, 404 ) 405 406 print(res) 407 ``` 408 409 ## Additional References 410 411 🧑🍳 Cookbook: [Advanced Prompt Customization for Anthropic](https://haystack.deepset.ai/cookbook/prompt_customization_for_anthropic)