OllamaChatGenerator — Ollama 채팅 생성

OllamaChatGenerator — Ollama 채팅 생성

이 컴포넌트는 Ollama에서 실행 중인 대규모 언어 모델(LLM)을 이용해 채팅 완성(chat completion)을 만들 수 있게 해 줘요. 로컬에서 돌아가는 모델을 Haystack 파이프라인에서 호출하는 한 조각이라고 보시면 돼요.

출처: 공식 문서 — OllamaChatGenerator

개요 (Overview)

Ollama는 LLM을 로컬에서 실행하는 데 초점을 맞춘 프로젝트예요. 내부적으로 기본적으로 양자화된 GGUF 형식을 사용해요. 덕분에 복잡한 설치 과정을 거치지 않고도 표준 머신(GPU가 없어도)에서 LLM을 실행할 수 있어요.

OllamaChatGenerator는 Ollama에서 실행되는 모델을 지원해요. llama2, mixtral 같은 모델들이죠. 지원되는 모델의 전체 목록은 여기에서 찾을 수 있어요.

OllamaChatGenerator는 동작하려면 model 이름과 url이 필요해요. 기본값은 모델 "qwen3:0.6b", URL "http://localhost:11434"예요.

OllamaChatGenerator를 사용하는 방식은 ChatMessage 객체를 쓰는 거예요. ChatMessage는 메시지와 역할(누가 만들었는지 — user, assistant, system, tool 등), 선택적 메타데이터를 담는 데이터 클래스예요. 사용 예시는 사용법 섹션을 보면 돼요.

도구 지원 (Tool Support)

OllamaChatGeneratortools 파라미터를 통해 함수 호출(function calling)을 지원해요. 이 파라미터는 유연한 도구 구성을 받아들여요.

  • Tool 객체 리스트: 개별 도구를 리스트로 전달
  • 단일 Toolset: Toolset 하나를 통째로 전달
  • Tool과 Toolset 혼합: 여러 Toolset과 단독 도구를 한 리스트에 섞어서 전달

이렇게 하면 관련된 도구들을 논리적인 그룹으로 묶으면서, 필요할 때 단독 도구도 같이 포함할 수 있어요.

from haystack.tools import Tool, Toolset
from haystack_integrations.components.generators.ollama import OllamaChatGenerator

# Create individual tools
weather_tool = Tool(
    name="weather", description="Get weather info", parameters=..., function=...
)
news_tool = Tool(
    name="news", description="Get latest news", parameters=..., function=...
)

# Group related tools into a toolset
math_toolset = Toolset([add_tool, subtract_tool, multiply_tool])

# Pass mixed tools and toolsets to the generator
generator = OllamaChatGenerator(
    model="llama2",
    tools=[math_toolset, weather_tool, news_tool],  # Mix of Toolset and Tool objects
)

도구를 다루는 더 자세한 내용은 ToolToolset 문서를 참고하세요.

스트리밍 (Streaming)

출력이 생성되는 그대로 스트리밍할 수 있어요. streaming_callback에 콜백을 넘기면 되고, 내장 함수 print_streaming_chunk를 쓰면 텍스트 토큰과 도구 이벤트(도구 호출과 도구 결과)를 출력해 줘요.

from haystack.components.generators.utils import print_streaming_chunk

# Configure any `Generator` or `ChatGenerator` with a streaming callback
component = SomeGeneratorOrChatGenerator(streaming_callback=print_streaming_chunk)

# If this is a `ChatGenerator`, pass a list of messages:
# from haystack.dataclasses import ChatMessage
# component.run([ChatMessage.from_user("Your question here")])

# If this is a (non-chat) `Generator`, pass a prompt:
# component.run({"prompt": "Your prompt here"})

참고 스트리밍은 단일 응답일 때만 동작해요. 공급자가 여러 후보를 지원한다면 n=1로 설정하세요.

StreamingChunk가 어떻게 동작하는지, 커스텀 콜백을 어떻게 작성하는지 더 알고 싶다면 Streaming Support 문서를 참고하세요.

기본적으로는 print_streaming_chunk를 쓰는 걸 권장해요. 커스텀 콜백은 특별한 전송 방식(예: SSE/WebSocket)이나 커스텀 UI 포맷이 필요할 때만 작성하면 돼요.

도구와 함께 스트리밍 (Streaming with Tools)

스트리밍과 도구 호출을 함께 쓸 수도 있어요. toolsstreaming_callback을 모두 넘기면, 모델이 도구를 호출하기로 결정했을 때 스트리밍된 청크가 텍스트 토큰 대신 도구 호출 델타(tool-call delta)를 담아요. 그리고 최종적으로 재구성된 ChatMessagereplies[0]에 해석된 tool_calls 리스트를 노출해 줘요.

from haystack.dataclasses import ChatMessage
from haystack.dataclasses.streaming_chunk import StreamingChunk
from haystack.tools import create_tool_from_function
from haystack_integrations.components.generators.ollama import OllamaChatGenerator


def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"Sunny, 22°C in {city}"


def callback(chunk: StreamingChunk) -> None:
    if chunk.tool_calls:
        print(f"[tool delta] {chunk.tool_calls}")
    elif chunk.content:
        print(chunk.content, end="", flush=True)


generator = OllamaChatGenerator(
    model="llama3.1:8b",
    generation_kwargs={"temperature": 0.0},
    tools=[create_tool_from_function(get_weather)],
    streaming_callback=callback,
)

response = generator.run(
    messages=[
        ChatMessage.from_user(
            "What's the weather in Berlin? Use the get_weather tool.",
        ),
    ],
)

# Final reconstructed message: tool_calls populated, text is None
assistant_message = response["replies"][0]
print(assistant_message.tool_calls)
# -> [ToolCall(tool_name='get_weather', arguments={'city': 'Berlin'}, ...)]

자체 콜백을 작성하는 대신 내장 print_streaming_chunk 콜백(텍스트 토큰과 도구 이벤트를 모두 처리)을 쓸 수도 있어요.

사용법 (Usage)

  1. 실행 중인 Ollama 인스턴스가 필요해요. 설치 방법은 Ollama GitHub 저장소에 있어요. Ollama를 빨리 실행하는 방법은 Docker를 쓰는 거예요.
docker run -d -p 11434:11434 --name ollama ollama/ollama:latest
  1. 원하는 LLM을 다운로드하거나 pull 해야 해요. 모델 라이브러리는 Ollama 웹사이트에서 볼 수 있어요. Docker를 쓴다면 예를 들어 Zephyr 모델을 pull 할 수 있어요.
docker exec ollama ollama pull zephyr

Ollama를 시스템에 이미 설치했다면 다음 명령을 실행하면 돼요.

ollama pull zephyr

팁 — 모델의 특정 버전 고르기

태그를 지정해서 모델의 특정(양자화된) 버전을 고를 수도 있어요. 사용 가능한 태그는 Ollama 모델 라이브러리의 모델 카드에 표시돼요. Zephyr의 예시가 이렇죠. 이 경우 이렇게 실행하면 됩니다.

# ollama pull model:tag
ollama pull zephyr:7b-alpha-q3_K_S
  1. 그리고 ollama-haystack 패키지도 설치해야 해요.
pip install ollama-haystack

단독 사용 (On its own)

from haystack_integrations.components.generators.ollama import OllamaChatGenerator
from haystack.dataclasses import ChatMessage

generator = OllamaChatGenerator(
    model="zephyr",
    url="http://localhost:11434",
    generation_kwargs={
        "num_predict": 100,
        "temperature": 0.9,
    },
)

messages = [
    ChatMessage.from_system("\nYou are a helpful, respectful and honest assistant"),
    ChatMessage.from_user("What's Natural Language Processing?"),
]

print(generator.run(messages=messages))
# >> {
# >>     "replies": [
# >>         ChatMessage(
# >>             _role=<ChatRole.ASSISTANT: 'assistant'>,
# >>             _content=[
# >>                 TextContent(
# >>                     text=(
# >>                         "Natural Language Processing (NLP) is a subfield of "
# >>                         "Artificial Intelligence that deals with understanding, "
# >>                         "interpreting, and generating human language in a meaningful "
# >>                         "way. It enables tasks such as language translation, sentiment "
# >>                         "analysis, and text summarization."
# >>                     )
# >>                 )
# >>             ],
# >>             _name=None,
# >>             _meta={
# >>                 "model": "zephyr",...
# >>             }
# >>         )
# >>     ]
# >> }

멀티모달 입력으로 사용:

from haystack.dataclasses import ChatMessage, ImageContent
from haystack_integrations.components.generators.ollama import OllamaChatGenerator

llm = OllamaChatGenerator(model="llava", url="http://localhost:11434")

image = ImageContent.from_file_path("apple.jpg")
user_message = ChatMessage.from_user(
    content_parts=["What does the image show? Max 5 words.", image],
)

response = llm.run([user_message])["replies"][0].text
print(response)

# Red apple on straw.

파이프라인에서 사용 (In a Pipeline)

from haystack.components.builders import ChatPromptBuilder
from haystack_integrations.components.generators.ollama import OllamaChatGenerator
from haystack.dataclasses import ChatMessage
from haystack import Pipeline

# no parameter init, we don't use any runtime template variables
prompt_builder = ChatPromptBuilder()
generator = OllamaChatGenerator(
    model="zephyr",
    url="http://localhost:11434",
    generation_kwargs={
        "temperature": 0.9,
    },
)

pipe = Pipeline()
pipe.add_component("prompt_builder", prompt_builder)
pipe.add_component("llm", generator)
pipe.connect("prompt_builder.prompt", "llm.messages")
location = "Berlin"
messages = [
    ChatMessage.from_system(
        "Always respond in Spanish even if some input data is in other languages."
    ),
    ChatMessage.from_user("Tell me about {{location}}"),
]
print(
    pipe.run(
        data={
            "prompt_builder": {
                "template_variables": {"location": location},
                "template": messages,
            }
        }
    )
)
# >> {
# >>     "llm": {
# >>         "replies": [
# >>             ChatMessage(
# >>                 _role=<ChatRole.ASSISTANT: 'assistant'>,
# >>                 _content=[
# >>                     TextContent(
# >>                         text=(
# >>                             "Berlín es la capital y la mayor ciudad de Alemania. "
# >>                             "Está ubicada en el estado federado de Berlín, y tiene más..."
# >>                         )
# >>                     )
# >>                 ],
# >>                 _name=None,
# >>                 _meta={
# >>                     "model": "zephyr",...
# >>                 }
# >>             )
# >>         ]
# >>     }
# >> }