콘텐츠로 이동

세션

세션은 Agents SDK에 지속적인 메모리 계층을 제공합니다. Session 인터페이스를 구현한 객체를 Runner.run에 전달하면, 나머지는 SDK가 처리합니다. 세션이 있으면 runner는 자동으로 다음을 수행합니다:

  1. 이전에 저장된 대화 항목을 가져와 다음 턴의 앞에 추가합니다.
  2. 각 실행이 완료된 후 새로운 사용자 입력과 assistant 출력을 저장합니다.
  3. 새로운 사용자 텍스트로 runner를 호출하든, 인터럽트된 RunState에서 재개하든, 이후 턴에서도 세션을 계속 사용할 수 있게 합니다.

이렇게 하면 toInputList()를 수동으로 호출하거나 턴 사이의 히스토리를 직접 이어 붙일 필요가 없습니다. TypeScript SDK에는 두 가지 구현이 포함되어 있습니다. Conversations API용 OpenAIConversationsSession과 로컬 개발용 MemorySession입니다. 둘 다 Session 인터페이스를 공유하므로 자체 스토리지 백엔드를 연결할 수 있습니다. Conversations API 외의 예시가 필요하다면 examples/memory/ 아래의 sample session backends(Prisma, 파일 기반 등)를 살펴보세요. OpenAI Responses model을 사용할 때는 세션을 OpenAIResponsesCompactionSession으로 감싸면 responses.compact를 통해 저장된 대화 히스토리를 자동으로 줄일 수 있습니다.

팁: 이 페이지의 OpenAIConversationsSession 예제를 실행하려면 SDK가 Conversations API를 호출할 수 있도록 OPENAI_API_KEY 환경 변수를 설정하거나(또는 세션 생성 시 apiKey를 제공) 하세요.

세션은 SDK가 클라이언트 측 메모리를 대신 관리하도록 하고 싶을 때 사용하세요. 이미 conversationId 또는 previousResponseId로 OpenAI 서버 관리 상태를 사용하고 있다면, 일반적으로 같은 대화 히스토리에 대해 세션을 추가로 사용할 필요는 없습니다.


메모리를 Conversations API와 동기화하려면 OpenAIConversationsSession을 사용하고, 필요하면 다른 Session 구현으로 교체할 수 있습니다.

세션 메모리로 Conversations API 사용하기
import { Agent, OpenAIConversationsSession, run } from '@openai/agents';
const agent = new Agent({
name: 'TourGuide',
instructions: 'Answer with compact travel facts.',
});
// Any object that implements the Session interface works here. This example uses
// the built-in OpenAIConversationsSession, but you can swap in a custom Session.
const session = new OpenAIConversationsSession();
const firstTurn = await run(agent, 'What city is the Golden Gate Bridge in?', {
session,
});
console.log(firstTurn.finalOutput); // "San Francisco"
const secondTurn = await run(agent, 'What state is it in?', { session });
console.log(secondTurn.finalOutput); // "California"

같은 세션 인스턴스를 재사용하면 에이전트가 매 턴 전에 전체 대화 히스토리를 받고, 새 항목도 자동으로 저장됩니다. 다른 Session 구현으로 바꿔도 추가 코드 변경은 필요하지 않습니다.

로컬 데모, 테스트 또는 프로세스 로컬 채팅 상태에는 MemorySession이 OpenAI와 통신하지 않으면서도 동일한 인터페이스를 제공합니다:

로컬 상태에 MemorySession 사용하기
import { Agent, MemorySession, run } from '@openai/agents';
const agent = new Agent({
name: 'TourGuide',
instructions: 'Answer with compact travel facts.',
});
const session = new MemorySession();
const result = await run(agent, 'What city is the Golden Gate Bridge in?', {
session,
});
console.log(result.finalOutput);

OpenAIConversationsSession 생성자 옵션:

OptionTypeNotes
conversationIdstring지연 생성 대신 기존 대화를 재사용합니다.
clientOpenAI미리 설정된 OpenAI 클라이언트를 전달합니다.
apiKeystring내부 OpenAI 클라이언트를 생성할 때 사용하는 API 키입니다.
baseURLstringOpenAI 호환 엔드포인트용 기본 URL입니다.
organizationstring요청에 사용할 OpenAI organization ID입니다.
projectstring요청에 사용할 OpenAI project ID입니다.

MemorySession 생성자 옵션:

OptionTypeNotes
sessionIdstring로그나 테스트용 안정적인 식별자입니다. 기본적으로 자동 생성됩니다.
initialItemsAgentInputItem[]기존 히스토리로 세션을 초기화합니다.
loggerLogger디버그 출력에 사용하는 로거를 재정의합니다.

MemorySession은 모든 것을 로컬 프로세스 메모리에 저장하므로 프로세스가 종료되면 초기화됩니다.

세션을 만들기 전에 대화 ID를 미리 생성해야 한다면 startOpenAIConversationsSession(client?)를 사용하고, 반환된 ID를 conversationId로 전달하세요.


  • 각 실행 전 세션 히스토리를 가져와 새 턴의 입력과 병합한 뒤, 결합된 목록을 에이전트에 전달합니다
  • 비스트리밍 실행 후 session.addItems() 한 번으로 원래 사용자 입력과 최신 턴의 model 출력을 모두 저장합니다
  • 스트리밍 실행의 경우 먼저 사용자 입력을 기록하고, 턴이 완료되면 스트리밍된 출력을 추가합니다
  • RunResult.state에서 재개할 때 (승인 또는 기타 인터럽트의 경우) 같은 session을 계속 전달하세요. 재개된 턴은 입력을 다시 준비하지 않고 메모리에 추가됩니다

세션은 간단한 CRUD 도우미를 제공하므로 “실행 취소”, “채팅 지우기”, 감사 기능 등을 구현할 수 있습니다.

저장된 항목 읽기 및 수정하기
import { OpenAIConversationsSession } from '@openai/agents';
import type { AgentInputItem } from '@openai/agents-core';
// Replace OpenAIConversationsSession with any other Session implementation that
// supports get/add/pop/clear if you store history elsewhere.
const session = new OpenAIConversationsSession({
conversationId: 'conv_123', // Resume an existing conversation if you have one.
});
const history = await session.getItems();
console.log(`Loaded ${history.length} prior items.`);
const followUp: AgentInputItem[] = [
{
type: 'message',
role: 'user',
content: [{ type: 'input_text', text: 'Let’s continue later.' }],
},
];
await session.addItems(followUp);
const undone = await session.popItem();
if (undone?.type === 'message') {
console.log(undone.role); // "user"
}
await session.clearSession();

session.getItems()는 저장된 AgentInputItem[]를 반환합니다. 마지막 항목을 제거하려면 popItem()을 호출하세요. 에이전트를 다시 실행하기 전에 사용자 수정 사항을 반영할 때 유용합니다.


메모리를 Redis, DynamoDB, SQLite 또는 다른 데이터 저장소에 연결하려면 Session 인터페이스를 구현하세요. 필요한 것은 비동기 메서드 5개뿐입니다.

커스텀 인메모리 세션 구현
import { Agent, run } from '@openai/agents';
import { randomUUID } from '@openai/agents-core/_shims';
import { getLogger } from '@openai/agents-core';
import type { AgentInputItem, Session } from '@openai/agents-core';
/**
* Minimal example of a Session implementation; swap this class for any storage-backed version.
*/
export class CustomMemorySession implements Session {
private readonly sessionId: string;
private readonly logger: ReturnType<typeof getLogger>;
private items: AgentInputItem[];
constructor(
options: {
sessionId?: string;
initialItems?: AgentInputItem[];
logger?: ReturnType<typeof getLogger>;
} = {},
) {
this.sessionId = options.sessionId ?? randomUUID();
this.items = options.initialItems
? options.initialItems.map(cloneAgentItem)
: [];
this.logger = options.logger ?? getLogger('openai-agents:memory-session');
}
async getSessionId(): Promise<string> {
return this.sessionId;
}
async getItems(limit?: number): Promise<AgentInputItem[]> {
if (limit === undefined) {
const cloned = this.items.map(cloneAgentItem);
this.logger.debug(
`Getting items from memory session (${this.sessionId}): ${JSON.stringify(cloned)}`,
);
return cloned;
}
if (limit <= 0) {
return [];
}
const start = Math.max(this.items.length - limit, 0);
const items = this.items.slice(start).map(cloneAgentItem);
this.logger.debug(
`Getting items from memory session (${this.sessionId}): ${JSON.stringify(items)}`,
);
return items;
}
async addItems(items: AgentInputItem[]): Promise<void> {
if (items.length === 0) {
return;
}
const cloned = items.map(cloneAgentItem);
this.logger.debug(
`Adding items to memory session (${this.sessionId}): ${JSON.stringify(cloned)}`,
);
this.items = [...this.items, ...cloned];
}
async popItem(): Promise<AgentInputItem | undefined> {
if (this.items.length === 0) {
return undefined;
}
const item = this.items[this.items.length - 1];
const cloned = cloneAgentItem(item);
this.logger.debug(
`Popping item from memory session (${this.sessionId}): ${JSON.stringify(cloned)}`,
);
this.items = this.items.slice(0, -1);
return cloned;
}
async clearSession(): Promise<void> {
this.logger.debug(`Clearing memory session (${this.sessionId})`);
this.items = [];
}
}
function cloneAgentItem<T extends AgentInputItem>(item: T): T {
return structuredClone(item);
}
const agent = new Agent({
name: 'MemoryDemo',
instructions: 'Remember the running total.',
});
// Using the above custom memory session implementation here
const session = new CustomMemorySession({
sessionId: 'session-123-4567',
});
const first = await run(agent, 'Add 3 to the total.', { session });
console.log(first.finalOutput);
const second = await run(agent, 'Add 4 more.', { session });
console.log(second.finalOutput);

커스텀 세션을 사용하면 보존 정책을 적용하거나, 암호화를 추가하거나, 각 대화 턴에 메타데이터를 붙인 뒤 저장할 수 있습니다.


히스토리와 새 항목의 병합 방식 제어

섹션 제목: “히스토리와 새 항목의 병합 방식 제어”

실행 입력으로 AgentInputItem 배열을 전달할 때는 sessionInputCallback을 제공해 저장된 히스토리와 결정론적으로 병합할 수 있습니다. runner는 기존 히스토리를 불러오고, model 호출 전에 콜백을 실행한 뒤, 반환된 배열을 해당 턴의 전체 입력으로 model에 전달합니다. 이 훅은 오래된 항목을 잘라내거나, 도구 결과를 중복 제거하거나, model에 보여주고 싶은 컨텍스트만 강조할 때 적합합니다.

sessionInputCallback으로 히스토리 잘라내기
import { Agent, OpenAIConversationsSession, run } from '@openai/agents';
import type { AgentInputItem } from '@openai/agents-core';
const agent = new Agent({
name: 'Planner',
instructions: 'Track outstanding tasks before responding.',
});
// Any Session implementation can be passed here; customize storage as needed.
const session = new OpenAIConversationsSession();
const todoUpdate: AgentInputItem[] = [
{
type: 'message',
role: 'user',
content: [
{ type: 'input_text', text: 'Add booking a hotel to my todo list.' },
],
},
];
await run(agent, todoUpdate, {
session,
// function that combines session history with new input items before the model call
sessionInputCallback: (history, newItems) => {
const recentHistory = history.slice(-8);
return [...recentHistory, ...newItems];
},
});

문자열 입력의 경우 runner가 히스토리를 자동으로 병합하므로 콜백은 선택 사항입니다. 이 콜백은 턴 입력이 이미 항목 배열인 경우에만 실행됩니다.

conversationId 또는 previousResponseId도 함께 사용 중이라면, 콜백 결과에 현재 턴의 새 항목을 최소 하나 이상 남겨두세요. 이 서버 관리 API들은 현재 턴의 delta에 의존합니다. 콜백이 모든 새 항목을 제거하면, SDK는 빈 delta를 보내는 대신 원래의 새 입력을 복원하고 경고 로그를 남깁니다.


휴먼인더루프 (HITL) 플로우에서는 승인 대기를 위해 실행을 일시 중지하는 경우가 많습니다:

같은 세션으로 실행 재개하기
import { Agent, MemorySession, Runner } from '@openai/agents';
const agent = new Agent({
name: 'Trip Planner',
instructions: 'Plan trips and ask for approval before booking anything.',
});
const runner = new Runner();
const session = new MemorySession();
const result = await runner.run(agent, 'Search the itinerary', {
session,
});
if (result.interruptions?.length) {
// ... collect user feedback, then resume the agent in a later turn.
for (const interruption of result.interruptions) {
result.state.approve(interruption);
}
const continuation = await runner.run(agent, result.state, { session });
console.log(continuation.finalOutput);
}

이전 RunState에서 재개하면 새 턴이 같은 메모리 기록에 추가되어 하나의 대화 히스토리가 유지됩니다. 휴먼 인 더 루프 (HITL) 플로우와도 완전히 호환됩니다. 승인 체크포인트는 계속 RunState를 통해 왕복하고, 세션은 대화 히스토리를 완전하게 유지합니다.


OpenAI Responses 히스토리 자동 압축

섹션 제목: “OpenAI Responses 히스토리 자동 압축”

OpenAIResponsesCompactionSession은 모든 Session을 데코레이션할 수 있으며, OpenAI Responses API를 사용해 길게 저장된 히스토리를 더 짧지만 동등한 대화 항목 목록으로 대체합니다. 각 턴이 저장된 뒤 runner는 최신 responseIdrunCompaction에 전달하고, 결정 훅이 true를 반환하면 responses.compact를 호출합니다. compactionMode에 따라 요청은 최신 Responses API 체인 또는 세션의 현재 항목으로부터 구성됩니다. 기본 트리거는 사용자 항목이 아닌 항목이 10개 이상 누적되었을 때 압축을 수행하며, shouldTriggerCompaction을 재정의해 토큰 수나 커스텀 휴리스틱을 기준으로 결정할 수 있습니다. 압축이 반환되면 데코레이터는 기반 세션을 비우고 축소된 항목 목록으로 다시 기록하므로, 서버 관리 히스토리 흐름이 다른 OpenAIConversationsSession과 함께 사용하는 것은 피하세요.

OpenAIResponsesCompactionSession으로 세션 데코레이션하기
import {
Agent,
MemorySession,
OpenAIResponsesCompactionSession,
run,
} from '@openai/agents';
const agent = new Agent({
name: 'Support',
instructions: 'Answer briefly and keep track of prior context.',
model: 'gpt-5.4',
});
// Wrap any Session to trigger responses.compact once history grows beyond your threshold.
const session = new OpenAIResponsesCompactionSession({
// You can pass any Session implementation except OpenAIConversationsSession
underlyingSession: new MemorySession(),
// (optional) The model used for calling responses.compact API
model: 'gpt-5.4',
// (optional) your custom logic here
shouldTriggerCompaction: ({ compactionCandidateItems }) => {
return compactionCandidateItems.length >= 12;
},
});
await run(agent, 'Summarize order #8472 in one sentence.', { session });
await run(agent, 'Remind me of the shipping address.', { session });
// Compaction runs automatically after each persisted turn. You can also force it manually.
await session.runCompaction({ force: true });

OpenAIResponsesCompactionSession 생성자 옵션:

OptionTypeNotes
clientOpenAIresponses.compact에 사용하는 OpenAI 클라이언트입니다.
underlyingSessionSession압축된 항목으로 비우고 다시 기록할 기반 세션 저장소입니다. 데모용으로는 기본 인메모리 세션이 사용되며, OpenAIConversationsSession이어서는 안 됩니다.
modelOpenAI.ResponsesModel압축 요청에 사용하는 model입니다. 기본값은 SDK의 현재 기본 OpenAI model입니다.
compactionMode'auto' | 'previous_response_id' | 'input'압축 시 서버 응답 체인을 사용할지 로컬 입력 항목을 사용할지 제어합니다.
shouldTriggerCompaction(context) => boolean | Promise<boolean>responseId, compactionMode, 후보 항목, 현재 세션 항목을 기준으로 하는 커스텀 트리거 훅입니다.

compactionMode: 'previous_response_id'는 이미 Responses API response ID로 턴을 연결하고 있을 때 유용합니다. 반면 compactionMode: 'input'는 현재 세션 항목을 기반으로 압축 요청을 다시 구성하므로, 응답 체인을 사용할 수 없거나 기반 세션 내용을 신뢰할 수 있는 원본으로 삼고 싶을 때 유용합니다.

runCompaction(args) 옵션:

OptionTypeNotes
responseIdstringprevious_response_id 모드에서 사용할 최신 Responses API response id입니다.
compactionMode'auto' | 'previous_response_id' | 'input'설정된 모드에 대한 호출별 재정의 옵션입니다.
storeboolean마지막 실행이 서버 상태를 저장했는지 나타냅니다.
forcebooleanshouldTriggerCompaction을 우회하고 즉시 압축합니다.

저지연 스트리밍을 위한 수동 압축

섹션 제목: “저지연 스트리밍을 위한 수동 압축”

압축은 기반 세션을 비우고 다시 기록하므로, SDK는 스트리밍 실행을 resolve하기 전에 이 작업을 기다립니다. 압축 작업이 무거우면 마지막 출력 토큰 이후에도 result.completed가 몇 초 동안 대기 상태일 수 있습니다. 저지연 스트리밍이나 더 빠른 턴 전환이 필요하다면 자동 압축을 끄고 턴 사이(또는 유휴 시간)에 직접 runCompaction을 호출하세요.

자동 압축을 비활성화하고 턴 사이에 압축하기
import {
Agent,
MemorySession,
OpenAIResponsesCompactionSession,
run,
} from '@openai/agents';
const agent = new Agent({
name: 'Support',
instructions: 'Answer briefly and keep track of prior context.',
model: 'gpt-5.4',
});
// Disable auto-compaction to avoid delaying stream completion.
const session = new OpenAIResponsesCompactionSession({
underlyingSession: new MemorySession(),
shouldTriggerCompaction: () => false,
});
const result = await run(agent, 'Share the latest ticket update.', {
session,
stream: true,
});
// Wait for the streaming run to finish before compacting.
await result.completed;
// Choose force based on your own thresholds or heuristics, between turns or during idle time.
await session.runCompaction({ force: true });

히스토리를 보관하거나 핸드오프하기 전에 언제든 runCompaction({ force: true })를 호출해 히스토리를 줄일 수 있습니다. 압축 결정 과정을 추적하려면 DEBUG=openai-agents:openai:compaction으로 디버그 로그를 활성화하세요.