스트리밍(Streaming)

스트리밍(Streaming)

스트리밍을 쓰면 모델이 텍스트를 생성하는 그 순간부터 화면에 그려나갈 수 있어요. 모델이 답을 다 만들 때까지 기다렸다가 한 번에 띄우는 대신, 토큰이 나오는 대로 이어서 보여주는 방식이에요.

출처: 공식문서

REST API에서는 스트리밍이 기본적으로 켜져 있지만, SDK에서는 기본적으로 꺼져 있습니다. SDK에서 스트리밍을 쓰려면 stream 파라미터를 True로 설정하면 돼요.

핵심 스트리밍 개념

  1. 채팅(Chatting) — 보조(assistant) 메시지의 일부를 스트리밍합니다. 각 청크에는 content가 담겨 있어서, 도착하는 대로 메시지를 그려나갈 수 있어요.
  2. 씽킹(Thinking) — 추론이 가능한 모델은 일반 콘텐츠와 함께 thinking 필드를 각 청크마다 내보냅니다. 이 필드를 감지해서 최종 답이 오기 전에 추론 과정을 보여주거나 숨길 수 있어요.
  3. 도구 호출(Tool calling) — 각 청크에서 스트리밍되는 tool_calls를 감지해 요청된 도구를 실행하고, 그 결과를 다시 대화에 추가합니다.

스트리밍 청크 다루기

대화의 히스토리를 유지하려면 부분적으로 도착하는 필드를 누적(accumulate)해야 합니다. 특히 도구 호출에서는 모델의 씽킹·도구 호출·실행된 도구 결과를 다음 요청에 다시 넘겨줘야 하므로 이 누적이 중요해요.

Python 예시입니다. in_thinking 플래그로 씽킹 구간과 답변 구간을 구분하며, 각 블록을 화면에 출력하면서 동시에 변수에 누적합니다.

from ollama import chat

stream = chat(
  model='qwen3',
  messages=[{'role': 'user', 'content': 'What is 17 × 23?'}],
  stream=True,
)

in_thinking = False
content = ''
thinking = ''
for chunk in stream:
  if chunk.message.thinking:
    if not in_thinking:
      in_thinking = True
      print('Thinking:\n', end='', flush=True)
    print(chunk.message.thinking, end='', flush=True)
    # accumulate the partial thinking 
    thinking += chunk.message.thinking
  elif chunk.message.content:
    if in_thinking:
      in_thinking = False
      print('\n\nAnswer:\n', end='', flush=True)
    print(chunk.message.content, end='', flush=True)
    # accumulate the partial content
    content += chunk.message.content

  # append the accumulated fields to the messages for the next request
  new_messages = [{ role: 'assistant', thinking: thinking, content: content }]

JavaScript도 같은 흐름입니다.

import ollama from 'ollama'

async function main() {
  const stream = await ollama.chat({
    model: 'qwen3',
    messages: [{ role: 'user', content: 'What is 17 × 23?' }],
    stream: true,
  })

  let inThinking = false
  let content = ''
  let thinking = ''

  for await (const chunk of stream) {
    if (chunk.message.thinking) {
      if (!inThinking) {
        inThinking = true
        process.stdout.write('Thinking:\n')
      }
      process.stdout.write(chunk.message.thinking)
      // accumulate the partial thinking
      thinking += chunk.message.thinking
    } else if (chunk.message.content) {
      if (inThinking) {
        inThinking = false
        process.stdout.write('\n\nAnswer:\n')
      }
      process.stdout.write(chunk.message.content)
      // accumulate the partial content
      content += chunk.message.content
    }
  }

  // append the accumulated fields to the messages for the next request
  new_messages = [{ role: 'assistant', thinking: thinking, content: content }]
}

main().catch(console.error)

더 알아보기 (Learn more)

  • Thinking — 추론 과정을 스트리밍으로 받기
  • Tool calling — 스트리밍과 함께 도구 호출하기