에이전트를 정의하는 것은 준비 단계입니다. 런타임에서는 한 번의 실행에서 어떤 작업이 이루어지는지, 대화가 다음 턴으로 어떻게 이어지는지, 승인이나 도구 작업을 위해 실행이 일시 중지될 때 워크플로우가 어떻게 동작하는지 살펴봐야 합니다.
에이전트 루프
SDK 실행 한 번은 애플리케이션에서 한 턴에 해당합니다. 러너는 실행을 멈출 조건이 충족될 때까지 다음 과정을 반복합니다.
- 준비된 입력으로 현재 에이전트의 모델을 호출합니다.
- 모델의 출력을 확인합니다.
- 모델이 도구 호출을 생성하면 해당 호출을 실행하고 루프를 계속합니다.
- 모델이 다른 전문 에이전트에 작업을 넘기면 해당 에이전트로 전환하고 루프를 계속합니다.
- 모델이 최종 답변을 생성했고 남은 도구 작업이 없으면 결과를 반환합니다.
이 루프는 SDK의 핵심입니다. 도구, 핸드오프, 승인, 스트리밍은 모두 이 루프를 기반으로 동작합니다.
대화 상태 관리 전략 선택하기
대화 상태를 다음 턴으로 전달하는 데 흔히 쓰이는 방법은 네 가지입니다.
| 전략 | 상태 저장 위치 | 적합한 경우 | 다음 턴에 전달할 값 |
|---|---|---|---|
result.history |
애플리케이션 | 간단한 채팅 루프를 구현하거나 대화 기록을 직접 세밀하게 관리하려는 경우 | 입력으로 다시 사용할 수 있는 대화 기록 |
session |
SDK와 연동한 자체 저장소 | 채팅 상태를 지속적으로 보관하거나, 실행을 재개하거나, 저장소를 직접 관리하려는 경우 | 동일한 세션 |
conversationId |
OpenAI Conversations API | 여러 워커나 서비스가 서버에서 관리하는 상태를 공유해야 하는 경우 | 동일한 대화 ID와 새 턴의 입력만 전달 |
previousResponseId |
OpenAI Responses API | 서버에서 관리하는 상태로 한 응답에서 다음 응답으로 가장 간단하게 이어 가려는 경우 | 마지막 응답 ID와 새 턴의 입력만 전달 |
대부분의 애플리케이션에서는 대화마다 한 가지 전략을 선택하세요. 로컬 대화 기록을 다시 전달하면서 서버에서 관리하는 상태도 함께 사용하면, 양쪽 기록을 명시적으로 조정하지 않는 한 컨텍스트가 중복될 수 있습니다.
import { Agent, MemorySession, run } from "@openai/agents";
const agent = new Agent({
name: "Tour guide",
instructions: "Answer with compact travel facts.",
});
const session = new MemorySession();
const firstTurn = await run(agent, "What city is the Golden Gate Bridge in?", {
session,
});
console.log(firstTurn.finalOutput);
const secondTurn = await run(agent, "What state is it in?", { session });
console.log(secondTurn.finalOutput);메모리를 지속적으로 보관하거나, 승인 플로우를 재개하거나, 애플리케이션에서 저장소를 직접 관리해야 한다면 세션을 기본 전략으로 선택하는 것이 가장 좋습니다.
import { Agent, run } from "@openai/agents";
import OpenAI from "openai";
const agent = new Agent({
name: "Assistant",
instructions: "Reply very concisely.",
});
const client = new OpenAI();
const { id: conversationId } = await client.conversations.create({});
const first = await run(agent, "What city is the Golden Gate Bridge in?", {
conversationId,
});
console.log(first.finalOutput);
const second = await run(agent, "What state is it in?", {
conversationId,
});
console.log(second.finalOutput);여러 시스템에서 이름이 지정된 대화 하나를 공유해야 한다면 conversationId를 사용하세요. 한 응답에서 다음 응답으로 이어 가는 데 드는 비용을 최소화하려면 previousResponseId
실행 중에 출력 스트리밍하기
스트리밍에서도 동일한 에이전트 루프와 상태 관리 전략을 사용합니다. 유일한 차이는 실행이 진행되는 동안 이벤트를 받아 처리한다는 점입니다.
import { Agent, run } from "@openai/agents";
const agent = new Agent({
name: "Planet guide",
instructions: "Answer with short facts.",
});
const stream = await run(agent, "Give me three short facts about Saturn.", {
stream: true,
});
for await (const event of stream) {
if (
event.type === "raw_model_stream_event" &&
event.data.type === "output_text_delta"
) {
process.stdout.write(event.data.delta);
}
}
await stream.completed;
console.log("\nFinal:", stream.finalOutput);스트리밍을 처리할 때는 다음 세 가지를 지키세요.
- 스트림이 끝날 때까지 기다린 다음 실행 상태가 확정된 것으로 처리하세요.
- 승인을 기다리느라 실행이 일시 중지되면
interruptions의 승인 요청을 처리하고state에서 실행을 재개하세요. 이때 새 사용자 턴을 시작하지 마세요. - 턴 도중에 스트림을 취소했다가 나중에 같은 턴을 이어 가려면
state에서 완료되지 않은 턴을 재개하세요.
일시 중지와 실패 구분하기
실행이 정상적으로 완료되지 않는 경우는 크게 두 가지로 나뉩니다.
- 런타임 또는 검증 실패는 최대 턴 수에 도달하거나 가드레일 예외 또는 도구 오류가 발생한 경우입니다.
- 예상된 일시 중지는 사람에게 승인을 요청할 때처럼 의도적으로 실행을 멈추는 경우입니다. 이후 같은 상태에서 실행을 재개해야 합니다.
승인을 기다리는 동안에는 기존 턴의 실행이 일시 중지된 것으로 처리하세요. 이를 새 턴과 구분해야 턴 수와 대화 기록, 서버에서 대화를 이어 가는 데 사용하는 ID의 일관성을 유지할 수 있습니다.
다음 단계
런타임 루프의 동작 방식을 이해했다면 워크플로우에서 다음으로 설계할 부분에 맞는 가이드를 선택하세요.