모델은 기억 안 해, 너는 기억한다
출처: Dev.to
소개
LLM을 공부하기 전에 나는 각 채팅이 자체 메모리 또는 컨텍스트를 가지고 있다고 생각했다. 그 순간 이 모든 메시지가 이어진 배열일 뿐이었다는 사실을 깨달았을 때 나는 통제력을 갖는 느낌이 들었다. 이걸 더 일찍 알았더라면 좋았을 텐데. 채팅 세션에서는 눈에 보이지 않으며, Claude와 OpenAI는 정확한 컨텍스트를 제공하기 위해 많은 스레드를 끌어올립니다. 먼저那些 스레드에 대해 알기 위해선 LLM API와 직접 fetch를 사용하고 SDK 없이, 요청/응답 사이클을 이해해야 했습니다.
강固한 기본을 구축하고 싶기 때문에 Anthropic SDK를 사용하지 않으면 우리가 인지하기 어려운 추상화에서 벗어날 수 있습니다. SDK는 이디오틱 인터페이스, 타입 안전성, 스트리밍, 재시도 및 오류 처리 기능을 내장하고 있습니다. SDK 없이 nada는 추상화되지 않습니다. 모든 결정이 보이며, 이것이 바로 목적입니다.
일반적으로 SDK를 사용해 API를 호출하려면 다음과 같은 스크립트를 추가해야 합니다:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const message = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1000,
messages: [
{
role: "user",
content: "What should I search for to find the latest developments in renewable energy?"
}
]
});
console.log(message.content);
그리고 원시 fetch를 사용하려면 헤더와 바디를 직접 관리해야 합니다:
const URL = `https://api.anthropic.com/v1/messages`;
const res = await fetch(URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': `${process.env.ANTHROPIC_API_KEY}`,
'anthropic-version': '2023-06-01',
},
body: JSON.stringify({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages: [
{
role: 'user',
content: 'Hello Claude',
},
],
}),
});
const data = await res.json();
console.log(data.content[0].text);
놀랍게도 이 방법을 선택하면 문서가 거의 없고, 이는 당연하지만 여전히 궁금증이 남습니다. 그리고 잘, 이것은 기본적인 요청-응답 동작에 불과합니다. 쿼리를 보낼 것이고, LLM이 응답을 주며 그게 전부입니다.
Messages API는 무상태이기 때문에 매번 요청을 보낼 때마다 전체 대화 히스토리를 다시 보내야 합니다. 우리는 여러 번의 대화 전환을 달성하고 싶습니다.
잠시 멈추어 이 ‘히스토리’를 어떻게 관리해야 할지 생각해 봅시다. 여기서 LLM 개발에서 가장 중요한 개념을 배우게 됩니다. 루프 개발을 진행하면서 우리 ‘기억’은 이전 메시지 배열과 최신 쿼리뿐이라는 것을 알게 되었습니다. 네, 이것이 바로 LLM이 컨텍스트를 관리하는 방식입니다. 모델이 스스로 이걸 관리하고 있다고 생각했는데, 배열을 이렇게 미세하게 제어할 수 있다는 사실이 큰 놀라움이었습니다. 두 번째 쿼리 후 우리 ‘기억’은 아래와 같은 스니펫과 같을 것입니다.
messages: [
{ role: "user", content: "Hello, Claude" },
{ role: "assistant", content: "Hello! How can I help you today?" },
{ role: "user", content: "Can you describe LLMs to me?" }
]
모델과 진정한 왕복 대화를 원한다면 먼저 다음과 같은 요구 사항이 필요합니다: 터미널에서 사용자 입력을 읽고, 이전 메시지와 새 메시지를 붙여 모델에 전달하고, 응답을 출력한 뒤 1단계로 돌아가며, 멋진 터치로 종료 옵션을 제공해야 합니다.
기본 루프 채팅의 전체 구현을 확인하고 싶다면 여기서 이 단계를 추가한 raw-claude-chat 스크립트를 확인하세요.
이 간단한 배열은 슬라이딩 윈도우, RAG, 의미 검색과 같은 많은 컨텍스트 전략의 씨앗이며, 나중에 정말 기능적인 채팅이 “기억”할 수 있도록 필수적입니다.
채팅과 상호작용할 때 우리는 단순히 메시지를 보내는 것이 아니라它에 특정 작업을 하도록 지시하고 싶을 수 있습니다. 이를 통해 도구 사용이 가능해지며, 모델이 실제로 실행하라는 명령을 수행하고, 한 작업씩 진행하며 필요할 때 올바른 도구를 선택할 수 있습니다.
서버 관점에서 도구를 만들었습니다, gitstoria. 이제 이 지식을 보완하기 위해 클라이언트 측을 이해하여 대응할 것입니다.