콘텐츠로 이동

에이전트 실행

에이전트는 스스로 아무 것도 하지 않습니다. Runner 클래스나 run() 유틸리티로 에이전트를 실행(run)합니다.

단순 실행
import { Agent, run } from '@openai/agents';
const agent = new Agent({
name: 'Assistant',
instructions: 'You are a helpful assistant',
});
const result = await run(
agent,
'Write a haiku about recursion in programming.',
);
console.log(result.finalOutput);
// Code within the code,
// Functions calling themselves,
// Infinite loop's dance.

커스텀 러너가 필요하지 않다면 싱글톤 기본 Runner 인스턴스를 실행하는 run() 유틸리티를 사용할 수 있습니다.

또는 직접 러너 인스턴스를 만들 수도 있습니다:

단순 실행
import { Agent, Runner } from '@openai/agents';
const agent = new Agent({
name: 'Assistant',
instructions: 'You are a helpful assistant',
});
// You can pass custom configuration to the runner
const runner = new Runner();
const result = await runner.run(
agent,
'Write a haiku about recursion in programming.',
);
console.log(result.finalOutput);
// Code within the code,
// Functions calling themselves,
// Infinite loop's dance.

에이전트를 실행한 후에는 최종 출력과 실행 전체 이력을 담은 실행 결과 객체를 받게 됩니다.

Runner의 run 메서드를 사용할 때 시작할 에이전트와 입력을 전달합니다. 입력은 문자열(사용자 메시지로 간주) 또는 OpenAI Responses API의 항목들로 구성된 입력 항목 리스트일 수 있습니다.

러너는 다음과 같은 루프를 실행합니다:

  1. 현재 입력으로 현재 에이전트의 모델을 호출
  2. LLM 응답을 검사
    • 최종 출력 → 반환
    • 핸드오프 → 새 에이전트로 전환, 누적된 대화 이력 유지, 1로 이동
    • 도구 호출 → 도구 실행, 결과를 대화에 추가, 1로 이동
  3. maxTurns에 도달하면 MaxTurnsExceededError 발생

앱이 시작될 때 Runner를 생성하고 요청 간에 재사용하세요. 인스턴스는 모델 프로바이더와 트레이싱 옵션 같은 전역 구성을 저장합니다. 완전히 다른 구성이 필요할 때만 다른 Runner를 생성하세요. 간단한 스크립트에서는 내부적으로 기본 러너를 사용하는 run()을 호출할 수도 있습니다.

run() 메서드의 입력은 실행을 시작할 초기 에이전트, 실행 입력, 그리고 옵션 세트입니다.

입력은 문자열(사용자 메시지로 간주), 입력 항목 리스트, 또는 휴먼 인 더 루프 (HITL) 에이전트를 구축하는 경우 RunState 객체일 수 있습니다.

추가 옵션은 다음과 같습니다:

OptionDefaultDescription
streamfalsetrue이면 호출이 StreamedRunResult를 반환하고 모델에서 도착하는 대로 이벤트를 내보냅니다.
context모든 tool / guardrail / handoff에 전달되는 컨텍스트 객체. 컨텍스트 관리에서 더 알아보기.
maxTurns10안전 한도 – 도달 시 MaxTurnsExceededError 발생.
signal취소를 위한 AbortSignal
session세션 지속성 구현. 세션 참고.
sessionInputCallback세션 이력과 새 입력의 사용자 정의 병합 로직; 모델 호출 전에 실행. 세션 참고.
callModelInputFilter모델 호출 직전에 모델 입력(items + 선택적 instructions)을 수정하는 훅. Call model input filter 참고.
toolErrorFormatter모델에 반환되는 도구 승인 거부 메시지를 커스터마이즈하는 훅. Tool error formatter 참고.
tracing실행별 트레이싱 구성 오버라이드(예: export API 키).
errorHandlers지원되는 런타임 오류 처리기(현재 maxTurns). Error handlers 참고.
conversationId서버 측 대화 재사용(OpenAI Responses API + Conversations API 전용)
previousResponseId대화를 생성하지 않고 이전 Responses API 호출에서 계속하기(OpenAI Responses API 전용)

스트리밍을 사용하면 LLM이 실행되는 동안 추가로 스트리밍 이벤트를 받을 수 있습니다. 스트림이 시작되면 StreamedRunResult에는 생성된 모든 새 출력 등을 포함해 실행에 대한 완전한 정보가 담깁니다. for await 루프로 스트리밍 이벤트를 순회할 수 있습니다. 자세한 내용은 스트리밍 가이드를 참고하세요.

자신의 Runner 인스턴스를 만들 경우, Runner를 구성하기 위해 RunConfig 객체를 전달할 수 있습니다.

FieldTypePurpose
modelstring | Model실행의 모든 에이전트에 대해 특정 모델을 강제합니다.
modelProviderModelProvider모델 이름 해석자 – 기본은 OpenAI 프로바이더입니다.
modelSettingsModelSettings에이전트별 설정을 오버라이드하는 전역 튜닝 매개변수
handoffInputFilterHandoffInputFilter핸드오프 수행 시 입력 항목을 변형(핸드오프 자체에 정의되지 않은 경우)
inputGuardrailsInputGuardrail[]초기 사용자 입력에 적용되는 가드레일
outputGuardrailsOutputGuardrail[]최종 출력에 적용되는 가드레일
tracingDisabledbooleanOpenAI 트레이싱을 완전히 비활성화
traceIncludeSensitiveDataboolean스팬은 유지하면서 트레이스에서 LLM/도구 입력과 출력을 제외
workflowNamestringTraces 대시보드에 표시 – 관련 실행을 그룹화하는 데 도움
traceId / groupIdstringSDK가 생성하도록 두는 대신 트레이스 또는 그룹 ID를 수동으로 지정
traceMetadataRecord<string, string>모든 스팬에 첨부할 임의의 메타데이터
tracingTracingConfig실행별 트레이싱 오버라이드(예: export API 키)
sessionInputCallbackSessionInputCallback이 러너의 모든 실행에 대한 기본 이력 병합 전략
callModelInputFilterCallModelInputFilter각 모델 호출 전에 모델 입력을 편집하는 전역 훅
toolErrorFormatterToolErrorFormatter모델에 반환되는 도구 승인 거부 메시지를 커스터마이즈하는 전역 훅

runner.run()(또는 run() 유틸리티)의 각 호출은 애플리케이션 수준 대화에서 한 번의 을 나타냅니다. 최종 사용자에게 RunResult의 어느 정도를 보여줄지는 선택할 수 있습니다. 때로는 finalOutput만, 다른 때는 생성된 모든 항목을 보여줄 수 있습니다.

대화 기록을 이어가는 예시
import { Agent, run } from '@openai/agents';
import type { AgentInputItem } from '@openai/agents';
let thread: AgentInputItem[] = [];
const agent = new Agent({
name: 'Assistant',
});
async function userSays(text: string) {
const result = await run(
agent,
thread.concat({ role: 'user', content: text }),
);
thread = result.history; // Carry over history + newly generated items
return result.finalOutput;
}
await userSays('What city is the Golden Gate Bridge in?');
// -> "San Francisco"
await userSays('What state is it in?');
// -> "California"

대화형 버전은 채팅 예제를 참고하세요.

매 턴마다 전체 로컬 대화 로그를 전송하는 대신 OpenAI Responses API가 대화 이력을 유지하도록 할 수 있습니다. 이는 긴 대화나 여러 서비스를 조율할 때 유용합니다. 자세한 내용은 Conversation state 가이드를 확인하세요.

OpenAI는 서버 측 상태를 재사용하는 두 가지 방법을 제공합니다:

Conversations API로 한 번 대화를 생성한 뒤, 각 턴에서 해당 ID를 재사용할 수 있습니다. SDK는 자동으로 새로 생성된 항목만 포함합니다.

서버 대화 재사용
import { Agent, run } from '@openai/agents';
import { OpenAI } from 'openai';
const agent = new Agent({
name: 'Assistant',
instructions: 'Reply very concisely.',
});
async function main() {
// Create a server-managed conversation:
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);
// -> "San Francisco"
const second = await run(agent, 'What state is it in?', { conversationId });
console.log(second.finalOutput);
// -> "California"
}
main().catch(console.error);

2. 마지막 턴에서 계속하기 위한 previousResponseId

섹션 제목: “2. 마지막 턴에서 계속하기 위한 previousResponseId”

어차피 Responses API만으로 시작하려는 경우, 각 요청을 이전 응답에서 반환된 ID로 연결할 수 있습니다. 이렇게 하면 전체 대화 리소스를 만들지 않고도 턴 간 컨텍스트가 유지됩니다.

previousResponseId로 연결
import { Agent, run } from '@openai/agents';
const agent = new Agent({
name: 'Assistant',
instructions: 'Reply very concisely.',
});
async function main() {
const first = await run(agent, 'What city is the Golden Gate Bridge in?');
console.log(first.finalOutput);
// -> "San Francisco"
const previousResponseId = first.lastResponseId;
const second = await run(agent, 'What state is it in?', {
previousResponseId,
});
console.log(second.finalOutput);
// -> "California"
}
main().catch(console.error);

callModelInputFilter를 사용해 모델이 호출되기 바로 직전에 모델 입력을 편집합니다. 이 훅은 현재 에이전트, 컨텍스트, 결합된 입력 항목(세션 이력이 있는 경우 이를 포함)을 받습니다. 민감한 데이터 마스킹, 오래된 메시지 제거, 추가 시스템 지침 주입 등을 위해 업데이트된 input 배열과 선택적 instructions를 반환하세요.

runner.run(..., { callModelInputFilter })로 실행별로 설정하거나, Runner 구성(RunConfigcallModelInputFilter)에서 기본값으로 설정할 수 있습니다.

도구 호출이 거부됐을 때 모델로 되돌려 보내는 승인-거부 메시지를 커스터마이즈하려면 toolErrorFormatter를 사용하세요. 이를 통해 SDK 기본 메시지 대신 도메인별 문구(예: 컴플라이언스 가이드)를 반환할 수 있습니다.

포매터는 실행별(runner.run(..., { toolErrorFormatter })) 또는 전역(RunConfig에서 new Runner(...)toolErrorFormatter)으로 설정할 수 있습니다.

errorHandlers를 사용하여 지원되는 런타임 오류를 throw하지 않고 최종 출력으로 변환합니다. 현재는 maxTurns만 지원됩니다.

  • errorHandlers.maxTurns는 최대 턴 오류만 처리
  • errorHandlers.default는 지원되는 종류에 대한 폴백으로 사용
  • 핸들러는 { error, context, runData }를 받고 { finalOutput, includeInHistory? }를 반환할 수 있음

SDK는 캐치할 수 있는 소수의 오류를 발생시킵니다:

모두 기본 AgentsError 클래스를 확장하며, 현재 실행 상태에 접근할 수 있는 state 속성을 제공할 수 있습니다.

다음은 GuardrailExecutionError를 처리하는 코드 예제입니다. 입력 가드레일은 첫 번째 사용자 입력에만 실행되므로, 예제는 원래 입력과 컨텍스트로 실행을 다시 시작합니다. 또한 모델을 다시 호출하지 않고 저장된 상태를 재사용하여 출력 가드레일을 재시도하는 방법도 보여줍니다:

가드레일 실행 오류
import {
Agent,
GuardrailExecutionError,
InputGuardrail,
InputGuardrailTripwireTriggered,
OutputGuardrail,
OutputGuardrailTripwireTriggered,
run,
} from '@openai/agents';
import { z } from 'zod';
// Shared guardrail agent to avoid re-creating it on every fallback run.
const guardrailAgent = new Agent({
name: 'Guardrail check',
instructions: 'Check if the user is asking you to do their math homework.',
outputType: z.object({
isMathHomework: z.boolean(),
reasoning: z.string(),
}),
});
async function main() {
const input = 'Hello, can you help me solve for x: 2x + 3 = 11?';
const context = { customerId: '12345' };
// Input guardrail example
const unstableInputGuardrail: InputGuardrail = {
name: 'Math Homework Guardrail (unstable)',
execute: async () => {
throw new Error('Something is wrong!');
},
};
const fallbackInputGuardrail: InputGuardrail = {
name: 'Math Homework Guardrail (fallback)',
execute: async ({ input, context }) => {
const result = await run(guardrailAgent, input, { context });
const isMathHomework =
result.finalOutput?.isMathHomework ??
/solve for x|math homework/i.test(JSON.stringify(input));
return {
outputInfo: result.finalOutput,
tripwireTriggered: isMathHomework,
};
},
};
const agent = new Agent({
name: 'Customer support agent',
instructions:
'You are a customer support agent. You help customers with their questions.',
inputGuardrails: [unstableInputGuardrail],
});
try {
// Input guardrails only run on the first turn of a run, so retries must start a fresh run.
await run(agent, input, { context });
} catch (e) {
if (e instanceof GuardrailExecutionError) {
console.error(`Guardrail execution failed (input): ${e}`);
try {
agent.inputGuardrails = [fallbackInputGuardrail];
// Retry from scratch with the original input and context.
await run(agent, input, { context });
} catch (ee) {
if (ee instanceof InputGuardrailTripwireTriggered) {
console.log('Math homework input guardrail tripped on retry');
} else {
throw ee;
}
}
} else {
throw e;
}
}
// Output guardrail example
const replyOutputSchema = z.object({ reply: z.string() });
const unstableOutputGuardrail: OutputGuardrail<typeof replyOutputSchema> = {
name: 'Answer review (unstable)',
execute: async () => {
throw new Error('Output guardrail crashed.');
},
};
const fallbackOutputGuardrail: OutputGuardrail<typeof replyOutputSchema> = {
name: 'Answer review (fallback)',
execute: async ({ agentOutput }) => {
const outputText =
typeof agentOutput === 'string'
? agentOutput
: (agentOutput?.reply ?? JSON.stringify(agentOutput));
const flagged = /math homework|solve for x|x =/i.test(outputText);
return {
outputInfo: { flaggedOutput: outputText },
tripwireTriggered: flagged,
};
},
};
const agent2 = new Agent<unknown, typeof replyOutputSchema>({
name: 'Customer support agent (output check)',
instructions: 'You are a customer support agent. Answer briefly.',
outputType: replyOutputSchema,
outputGuardrails: [unstableOutputGuardrail],
});
try {
await run(agent2, input, { context });
} catch (e) {
if (e instanceof GuardrailExecutionError && e.state) {
console.error(`Guardrail execution failed (output): ${e}`);
try {
agent2.outputGuardrails = [fallbackOutputGuardrail];
// Output guardrails can be retried using the saved state without another model call.
await run(agent2, e.state);
} catch (ee) {
if (ee instanceof OutputGuardrailTripwireTriggered) {
console.log('Output guardrail tripped after retry with saved state');
} else {
throw ee;
}
}
} else {
throw e;
}
}
}
main().catch(console.error);

입력 vs. 출력 재시도:

  • 입력 가드레일은 실행의 첫 번째 사용자 입력에서만 실행되므로, 동일한 입력/컨텍스트로 새 실행을 시작해야 재시도할 수 있습니다. 저장된 state를 전달해도 입력 가드레일은 다시 트리거되지 않습니다.
  • 출력 가드레일은 모델 응답 이후에 실행되므로, GuardrailExecutionError에서 저장된 state를 재사용해 모델을 다시 호출하지 않고 출력 가드레일만 재실행할 수 있습니다.

위 예제를 실행하면 다음과 같은 출력이 표시됩니다:

Guardrail execution failed (input): Error: Input guardrail failed to complete: Error: Something is wrong!
Math homework input guardrail tripped on retry
Guardrail execution failed (output): Error: Output guardrail failed to complete: Error: Output guardrail crashed.
Output guardrail tripped after retry with saved state