ChatPromptBuilder
ChatPromptBuilder
ChatPromptBuilder는 채팅 메시지를 처리해서 프롬프트를 동적으로 만들어 주는 컴포넌트예요.
| 항목 | 내용 |
|---|---|
| 파이프라인에서의 위치 | Generator 앞 |
| 필수 init 변수 | template: ChatMessage 객체의 리스트 또는 특별한 문자열 템플릿. init이나 run 중 한쪽에서 반드시 제공해야 해요. |
| 필수 run 변수 | **kwargs: 프롬프트 템플릿을 렌더링하는 데 쓸 문자열들. Variables 섹션 참고 |
| 출력 변수 | prompt: 동적으로 만들어진 프롬프트 |
| API 레퍼런스 | Builders |
| GitHub 링크 | https://github.com/deepset-ai/haystack/blob/main/haystack/components/builders/chat_prompt_builder.py |
| 패키지명 | haystack-ai |
출처: 공식문서
개요 (Overview)
ChatPromptBuilder는 Jinja2 문법으로 쓰인 정적 또는 동적 템플릿으로 프롬프트를 만들어요. 채팅 메시지 리스트나 특별한 문자열 템플릿을 처리하죠. 템플릿에는 {{ variable }} 같은 플레이스홀더가 있고, 런타임에 제공되는 값으로 채워져요. 초기화 시 설정한 정적 프롬프트로 쓰거나, 실행 중에 템플릿과 변수를 동적으로 바꿀 수도 있어요.
사용하려면 먼저 템플릿으로 ChatMessage 객체 리스트나 특별한 문자열을 제공하면 돼요.
ChatMessage는 메시지 내용과 역할(누가 메시지를 만들었는지 — user, assistant, system, tool), 그리고 선택적인 메타데이터를 담는 데이터 클래스예요.
빌더는 템플릿에서 플레이스홀더를 찾아 필요한 변수를 식별해요. 변수를 수동으로 나열해 줄 수도 있죠. 런타임에 run 메서드는 템플릿과 변수를 받아 플레이스홀더를 채우고 완성된 프롬프트를 반환해요. 필요한 변수가 빠져 있거나 템플릿이 유효하지 않으면 에러를 내요.
예를 들어 간단한 번역 프롬프트를 만들 수 있어요.
template = [ChatMessage.from_user("Translate to {{ target_language }}: {{ text }}")]
builder = ChatPromptBuilder(template=template)
result = builder.run(target_language="French", text="Hello, how are you?")
런타임에 템플릿을 새것으로 교체할 수도 있어요.
new_template = [
ChatMessage.from_user("Summarize in {{ target_language }}: {{ content }}"),
]
result = builder.run(
template=new_template,
target_language="English",
content="A detailed paragraph.",
)
변수 (Variables)
init 템플릿에서 발견되는 템플릿 변수들은 컴포넌트의 입력 타입으로 사용돼요. 기본적으로 required_variables 값이 "*"로 설정돼서 템플릿의 모든 변수가 필수가 돼요. 런타임에 하나라도 빠지면 컴포넌트는 에러를 내고 실행을 멈춰요.
required_variables와 variables로 입력 타입과 필수 변수를 지정할 수 있어요.
-
required_variables- 컴포넌트가 실행될 때 반드시 제공돼야 하는 템플릿 변수를 정의해요.
- 필수 변수가 하나라도 없으면 에러를 내고 실행을 멈춰요.
- 지정 방법은 다양해요:
"*"(기본값)를 쓰면 템플릿의 모든 변수를 필수로, 또는- 필수 변수 이름 목록(예:
["name"])을 넘기면 그 변수들만 필수로 만들고 나머지는 선택으로, 또는 - 빈 목록(
[])이나None을 넘기면 모든 변수를 선택으로 만들어요.None을 명시하면 경고 로그가 찍히는데, 빠진 변수를 조용히 빈 문자열로 대체하면 특히 복잡한 파이프라인에서 예상치 못한 동작이 생길 수 있어서예요.
-
variables- 필수든 선택이든 템플릿에 등장할 수 있는 모든 변수를 나열해요.
- 제공되지 않은 선택 변수는 렌더링된 프롬프트에서 빈 문자열로 대체돼요.
- 덕분에 어떤 변수가 필수로 표시돼 있지 않은 한, 오류 없이 부분 프롬프트를 만들 수 있어요.
아래 예시에서는 컴포넌트를 실행하는 데 name만 필수이고, topic은 선택 변수예요.
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")
# >> "Hello, Alice. How can I assist you with ?"
컴포넌트는 필수 입력만 갖춰지면 바로 실행돼요.
역할 (Roles)
ChatMessage는 대화 안의 한 메시지를 나타내며, 채팅 메시지를 만드는 클래스 메서드 즉 from_user, from_system, from_assistant 중 하나를 써서 만들 수 있어요. from_user 메시지는 사용자가 제공하는 입력(질문이나 요청)이에요. from_system 메시지는 LLM의 동작을 안내하는 맥락이나 지시(예: 대화의 톤이나 목적 설정)를 제공해요. from_assistant는 LLM의 예상 응답 또는 실제 응답을 정의해요.
역할들이 ChatPromptBuilder 안에서 어떻게 함께 쓰이는지 보여 줄게요.
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.",
)
문자열 템플릿
ChatMessage 객체 리스트 대신 특별한 문자열로 템플릿을 표현할 수도 있어요.
이 템플릿 형식은 Jinja2 문법으로 ChatMessage 시퀀스를 정의할 수 있게 해줘요. 각 {% message %} 블록은 특정 역할을 가진 메시지 하나를 정의하고, {{ variables }}로 동적 콘텐츠를 넣을 수 있어요.
ChatMessage 리스트를 쓰는 방식보다 유연해서, 이미지 같은 구조적인 부분을 템플릿화한 ChatMessage에 포함시킬 수 있어요. 그 사용 사례가 궁금하다면 아래 Usage 섹션의 멀티모달 예시를 확인해 보세요.
insert 태그
문자열 템플릿은 {% insert %} 태그도 지원해요. 이 태그는 식을 평가해서 ChatMessage 하나 또는 그 리스트로 만든 뒤 프롬프트 안에 펼쳐 넣는 플레이스홀더예요. 그래서 런타임에 제공되는 메시지들을 리터럴 {% message %} 블록 사이사이에 끼워 넣을 수 있어요. 예를 들어 런타임 메시지 위로 시스템 메시지를, 아래로 템플릿화한 사용자 메시지를 감싼 뒤, 그 메시지들(과 템플릿 변수)을 run 때 넘겨 줄 수 있어요.
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?"]
모든 콘텐츠 타입(툴 호출, 툴 호출 결과, 이미지, 추론, name, meta)은 손실 없이 왕복해요. 빠지거나 빈 값은 아무것도 펼쳐지지 않아요.
식은 단순 변수({% insert messages %}), 슬라이스나 인덱스({% insert messages[-1:] %}, {% insert messages[-1] %}), 변수 조합({% insert previous + current %})이 될 수 있어요. 템플릿 하나에 {% insert %} 태그를 여러 개 쓸 수 있어서, 런타임 메시지를 여러 위치로 나누거나 재배치하거나 반복할 수 있어요.
Jinja2 시간 확장 (Time Extension)
ChatPromptBuilder는 Jinja2 TimeExtension을 지원해서 datetime 형식을 다룰 수 있어요.
시간 확장은 크게 두 가지 기능을 제공해요.
- 현재 시각을 얻을 수 있는
now태그, - Python의 datetime 모듈을 통한 날짜·시간 포맷 기능.
Jinja2 TimeExtension을 쓰려면 의존성을 설치해야 해요.
pip install arrow>=1.3.0
now 태그
now 태그는 현재 시각을 나타내는 datetime 객체를 만든 뒤 변수에 저장할 수 있게 해줘요.
{% now 'utc' as current_time %}
The current UTC time is: {{ current_time }}
다른 시간대도 지정할 수 있어요.
{% now 'America/New_York' as ny_time %}
The time in New York is: {{ ny_time }}
시간대를 지정하지 않으면 시스템의 로컬 시간대가 사용돼요.
{% now as local_time %}
Local time: {{ local_time }}
날짜 포맷 (Date Formatting)
datetime 객체는 Python의 strftime 문법으로 포맷할 수 있어요.
{% now as current_time %}
Formatted date: {{ current_time.strftime('%Y-%m-%d %H:%M:%S') }}
자주 쓰는 포맷 코드는 이러해요.
%Y: 4자리 연도 (예: 2025)%m: 0으로 채운 월 (01-12)%d: 0으로 채운 일 (01-31)%H: 0으로 채운 시(24시간제) (00-23)%M: 0으로 채운 분 (00-59)%S: 0으로 채운 초 (00-59)
예시
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)
단독으로 쓰기
정적 템플릿으로
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.")
특별한 문자열 템플릿으로
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!")]
ChatMessage에서 name과 meta 지정하기
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"}),
]
역할이 다른 여러 ChatMessage
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?"),
]
런타임에 정적 템플릿 덮어쓰기
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)
아래 예시의 | templatize_part 필터는 템플릿 엔진에게 이미지 같은 구조적인(비텍스트) 객체를 메시지 콘텐츠 안에 넣으라고 알려 줘요. 이런 값들은 일반 텍스트와 다르게 취급되며, 최종 ChatMessage에서 특별한 콘텐츠 조각으로 렌더링돼요.
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"},
),
]
파이프라인에서 쓰기
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)
그러면 이어서 그 위치의 날씨 예보를 물어볼 수도 있어요. ChatPromptBuilder가 새 day_count 변수로 템플릿을 채우고 다시 LLM으로 보내요.
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)
YAML에서 쓰기
아래는 위 파이프라인의 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: '*'
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: {}