본문으로 건너뛰기

빠른 시작

작동하는 chat() 호출이 있고 턴 또는 세션에 걸쳐 컨텍스트를 기억하게 하려고 합니다. 이 가이드를 마치면 memoryMiddleware가 관련 메모리를 프롬프트에 recall하고, 서버에서 검증한 세션으로 안전하게 범위를 지정하여 실제 어댑터를 통해 완료된 각 턴을 저장합니다.

전체 계약을 먼저 확인하려면 Overview를 참조하세요.

1단계: 패키지 설치

pnpm add @tanstack/ai-memory

@tanstack/ai-memorymemoryMiddleware, MemoryAdapter 계약, 내장 어댑터와 벤더 어댑터를 제공합니다(각 어댑터는 자체 하위 경로에 있습니다).

2단계: 어댑터 선택

인메모리: inMemory()는 의존성이 없으며 레코드를 Map에 저장합니다. 로컬 개발, 테스트 및 단일 프로세스 데모에 사용합니다. 재시작하면 레코드가 사라집니다.

Redis: redis({ redis })는 재시작 후에도 영속화되며 프로세스 간 상태를 공유합니다. 클라이언트(ioredis 또는 fromNodeRedis를 통한 redis)는 직접 준비합니다.

벤더: hindsight(), mem0(), honcho()는 호스팅된 메모리 서비스에 작업을 위임합니다.

사용자 지정 어댑터는 recall/save 계약을 구현합니다. Custom Adapter를 참조하세요.

3단계: memoryMiddlewarechat()에 연결

작동하는 설정으로 가는 가장 빠른 방법인 인메모리 어댑터부터 시작합니다.

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를 구현하는 방법