빠른 시작
작동하는 chat() 호출이 있고 턴 또는 세션에 걸쳐 컨텍스트를 기억하게 하려고 합니다. 이 가이드를 마치면 memoryMiddleware가 관련 메모리를 프롬프트에 recall하고, 서버에서 검증한 세션으로 안전하게 범위를 지정하여 실제 어댑터를 통해 완료된 각 턴을 저장합니다.
전체 계약을 먼저 확인하려면 Overview를 참조하세요.
1단계: 패키지 설치
pnpm add @tanstack/ai-memory
@tanstack/ai-memory는 memoryMiddleware, MemoryAdapter 계약, 내장 어댑터와 벤더 어댑터를 제공합니다(각 어댑터는 자체 하위 경로에 있습니다).
2단계: 어댑터 선택
인메모리:
inMemory()는 의존성이 없으며 레코드를Map에 저장합니다. 로컬 개발, 테스트 및 단일 프로세스 데모에 사용합니다. 재시작하면 레코드가 사라집니다.Redis:
redis({ redis })는 재시작 후에도 영속화되며 프로세스 간 상태를 공유합니다. 클라이언트(ioredis또는fromNodeRedis를 통한redis)는 직접 준비합니다.벤더:
hindsight(),mem0(),honcho()는 호스팅된 메모리 서비스에 작업을 위임합니다.
사용자 지정 어댑터는 recall/save 계약을 구현합니다. Custom Adapter를 참조하세요.
3단계: memoryMiddleware를 chat()에 연결
작동하는 설정으로 가는 가장 빠른 방법인 인메모리 어댑터부터 시작합니다.
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { memoryMiddleware } from '@tanstack/ai-memory'
import { inMemory } from '@tanstack/ai-memory/in-memory'
const memory = inMemory()
const stream = chat({
adapter: openaiText('gpt-5.5'),
messages: [{ role: 'user', content: 'Hello' }],
middleware: [
memoryMiddleware({
adapter: memory,
scope: { threadId: 'demo-thread', userId: 'alice' },
}),
],
})
각 턴마다 미들웨어는 관련 메모리를 시스템 프롬프트로 recall하고(기본적으로 어휘 점수화), 스트림이 끝난 후 사용자 및 어시스턴트 턴을 지연 저장합니다.
배포할 준비가 되면 어댑터를 교체하고 나머지는 그대로 유지합니다.
import Redis from 'ioredis'
import { memoryMiddleware } from '@tanstack/ai-memory'
import { redis } from '@tanstack/ai-memory/redis'
import type { MemoryScope } from '@tanstack/ai-memory'
declare const scope: MemoryScope // from Step 5
const client = new Redis(process.env.REDIS_URL ?? 'redis://localhost:6379')
const memory = redis({ redis: client })
memoryMiddleware({ adapter: memory, scope })
호스팅 서비스를 사용하나요?
inMemory()를hindsight({ user }),mem0({ user })또는honcho({ user })로 교체합니다. 미들웨어 연결 방식은 동일합니다. 어댑터가recall/save를 벤더 API에 매핑합니다.
4단계: 의미 기반 점수화(선택 사항)
내장 어댑터는 기본적으로 어휘 기반으로 점수를 계산합니다. 범위가 커지거나 쿼리가 저장된 텍스트와 키워드를 공유하지 않을 때 의미 기반 recall을 사용하려면 embedder를 전달합니다.
import OpenAI from 'openai'
import { inMemory } from '@tanstack/ai-memory/in-memory'
const openai = new OpenAI()
const memory = inMemory({
embedder: {
async embed(text) {
const result = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text,
})
const embedding = result.data[0]?.embedding
if (!embedding) throw new Error('embedding request returned no vector')
return embedding
},
},
})
5단계: 서버 측에서 scope 파생
scope는 격리 경계입니다. 정적 scope는 픽스처에 적합하지만, 실제 앱에서는 요청 본문에서 가져오지 말고 서버에서 검증한 세션 데이터로 요청마다 scope를 파생합니다.
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { memoryMiddleware } from '@tanstack/ai-memory'
import type { ModelMessage } from '@tanstack/ai'
import type { MemoryAdapter } from '@tanstack/ai-memory'
// From earlier steps / your auth layer.
declare const messages: Array<ModelMessage>
declare const memory: MemoryAdapter
declare const session: { userId: string; threadId: string }
declare function getSession(ctx: unknown): { threadId: string; userId: string }
const stream = chat({
adapter: openaiText('gpt-5.5'),
messages,
context: { session }, // attached by your auth middleware, not from req.body
middleware: [
memoryMiddleware({
adapter: memory,
scope: (ctx) => {
const session = getSession(ctx)
return { threadId: session.threadId, userId: session.userId }
},
}),
],
})
클라이언트에서 메모리 연결 방식은 변하지 않습니다. 다른 chat() 엔드포인트와 마찬가지로 동일한 스트림을 소비합니다.
import { useChat, fetchServerSentEvents } from '@tanstack/ai-react'
function Chat() {
const { messages, sendMessage, isLoading } = useChat({
connection: fetchServerSentEvents('/api/chat'),
})
// Memory is entirely server-side; the client only sees the usual message stream.
return (
// render messages, input, sendMessage, isLoading…
null
)
}
다음 단계
- Overview:
recall/save계약, scope 및 턴의 흐름 - Adapters: 각 어댑터의 옵션 및 각각의 예시
- Operating memory: 옵션, 텔레메트리, devtools 이벤트 및 오류
- Custom Adapter: 제공되지 않는 백엔드에
recall/save를 구현하는 방법