최신글

LLM - 멀티턴 대화가 동작하는 원리

반응형

이 글은 LLM API를 호출할때 알아야하는 멀티턴에 대해 간단히 정리했습니다.

- 예제 실습코드: https://github.com/choisungwook/portfolio/tree/master/computer_science/ai/multi-turn-conversation/openai

 

멀티턴이란

멀티턴(multi-turn)은 이전 대화를 유지한 채 여러 번 질문과 답변을 주고받는 방식을 말합니다.

 

LLM은 이전 대화를 기억하지 못합니다

LLM은 stateless입니다. 이전 호출의 상태를 서버에 남기지 않기 때문에, 앞에서 무슨 대화를 했는지 모델은 알지 못합니다.

예를 들어 아래 코드처럼 내 이름을 알려준 대화를 LLM은 기억하지 못해, 이름을 알려주지 않았다고 답변합니다.

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
MODEL = "gpt-5.6-luna"
SYSTEM = {"role": "system", "content": "You are a concise assistant. Answer in Korean."}

second = client.chat.completions.create(
  model=MODEL,
  max_completion_tokens=2000,
  messages=[SYSTEM, {"role": "user", "content": "내 이름이 뭐라고 했지?"}],
)

 

이전 대화를 기억하게 하려면 매번 대화를 다시 보내야 합니다

모델이 이전 대화를 알게 하려면, 호출하는 쪽에서 이전 질문과 답변을 누적해 매 요청마다 함께 보내야 합니다. 즉 멀티턴은 모델이 기억하는 것이 아니라, 호출하는 쪽이 대화 기록을 관리하며 만들어내는 구조입니다.

 

아래 코드는 이전 대화를 history 변수에 저장하고, API를 호출할 때마다 history 전체를 messages에 추가합니다.

def ask(history: list[dict], question: str) -> str:
  """Append the question, resend the whole history, append the reply, return it."""
  history.append({"role": "user", "content": question})

  response = client.chat.completions.create(
    model=MODEL,
    max_completion_tokens=2000,
    messages=[SYSTEM] + history,  # the system prompt is messages[0] here
  )
  answer = response.choices[0].message.content

  history.append({"role": "assistant", "content": answer})
  print(f"  [{len(history)} messages sent, "
        f"{response.usage.prompt_tokens} prompt tokens]")
  return answer


def main() -> None:
  history: list[dict] = []
  print("turn 1 >", ask(history, "내 이름은 악분이야. 기억해줘."))
  print("turn 2 >", ask(history, "내 이름이 뭐라고 했지?"))
  print("turn 3 >", ask(history, "내가 지금까지 몇 번 질문했지?"))

 

대화 기록을 영구적으로 저장하려면 파일에 저장해야합니다.

이전 예제 코드는 대화 기록을 메모리에 저장하기 때문에, 애플리케이션이 종료되면 대화가 전부 사라집니다. 대화를 영구적으로 남기려면 파일이나 DB에 저장해야 합니다. 보통 jsonl 파일과 SQLite를 사용합니다. 저의 이전 블로그 글에 claude가 대화 내용을 json에 남긴는 것을 정리했습니다.
- claude가 jsonl에 대화 내용을 저장하는 방식

 

Claude Code 동작 원리 정리 - Harness, Context, Memory까지

이 글은 claude code를 몇번 번 사용해 본 유저를 대상으로 작성되었습니다. claude code를 처음 사용하는 사람에게 벅찬 내용일 수 있습니다. 놀랍게도 이 글의 60%이상이 claude code가 작성했습니다. 들

malwareanalysis.tistory.com

 

LLM 서버의 프롬프트 캐싱이 연산과 비용을 줄입니다

매번 이전 대화를 전부 토큰으로 보내면, 모델을 제공하는 프로바이더와 사용자 모두 비용과 연산 자원을 많이 씁니다. 그래서 LLM API 서비스들은 서버에서 프롬프트 캐싱을 적용해, 앞선 대화 중 동일한 앞부분은 다시 연산하지 않습니다.

 

LLM API를 호출하고 서버에서 캐시된 토큰 수는 API 응답에 함께 내려옵니다. 캐시 토큰이 많을수록 같은 대화를 이어가는 비용이 줄어듭니다.

반응형