콘텐츠로 이동

에이전트 실행

Runner 클래스를 통해 에이전트를 실행할 수 있습니다. 선택지는 3가지입니다:

  1. Runner.run(): 비동기로 실행되며 RunResult 를 반환합니다
  2. Runner.run_sync(): 동기 메서드로, 내부적으로 .run() 을 실행합니다
  3. Runner.run_streamed(): 비동기로 실행되며 RunResultStreaming 을 반환합니다. LLM 을 스트리밍 모드로 호출하고, 수신되는 대로 이벤트를 스트리밍합니다
from agents import Agent, Runner

async def main():
    agent = Agent(name="Assistant", instructions="You are a helpful assistant")

    result = await Runner.run(agent, "Write a haiku about recursion in programming.")
    print(result.final_output)
    # Code within the code,
    # Functions calling themselves,
    # Infinite loop's dance

자세한 내용은 결과 가이드를 참조하세요.

에이전트 루프

Runner 의 run 메서드를 사용할 때 시작 에이전트와 입력을 전달합니다. 입력은 문자열(사용자 메시지로 간주) 또는 OpenAI Responses API 의 입력 아이템 목록이 될 수 있습니다.

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

  1. 현재 입력과 함께 현재 에이전트에 대해 LLM 을 호출합니다
  2. LLM 이 출력을 생성합니다
    1. LLM 이 final_output 을 반환하면 루프가 종료되고 결과를 반환합니다
    2. LLM 이 핸드오프를 수행하면 현재 에이전트와 입력을 업데이트하고 루프를 다시 실행합니다
    3. LLM 이 도구 호출을 생성하면 해당 도구 호출을 실행하고 결과를 추가한 뒤 루프를 다시 실행합니다
  3. 전달된 max_turns 를 초과하면 MaxTurnsExceeded 예외를 발생시킵니다

Note

LLM 출력이 "최종 출력" 으로 간주되는 규칙은 원하는 타입의 텍스트 출력을 생성하고, 도구 호출이 없을 때입니다.

스트리밍

스트리밍을 사용하면 LLM 이 실행되는 동안 추가로 스트리밍 이벤트를 수신할 수 있습니다. 스트림이 완료되면 RunResultStreaming 에는 실행에 대한 완전한 정보가 포함되며, 생성된 모든 새로운 출력이 포함됩니다. 스트리밍 이벤트는 .stream_events() 를 호출해 수신할 수 있습니다. 자세한 내용은 스트리밍 가이드에서 확인하세요.

실행 구성

run_config 매개변수를 사용하여 에이전트 실행에 대한 전역 설정을 구성할 수 있습니다:

  • model: 각 Agent 의 model 설정과 무관하게 사용할 전역 LLM 모델을 설정
  • model_provider: 모델 이름을 조회하기 위한 모델 제공자, 기본값은 OpenAI
  • model_settings: 에이전트별 설정을 재정의. 예를 들어 전역 temperature 또는 top_p 를 설정 가능
  • input_guardrails, output_guardrails: 모든 실행에 포함할 입력 또는 출력 가드레일 목록
  • handoff_input_filter: 핸드오프에 이미 설정된 필터가 없다면 모든 핸드오프에 적용할 전역 입력 필터. 이 입력 필터를 사용하여 새 에이전트로 전송되는 입력을 수정할 수 있습니다. 자세한 내용은 Handoff.input_filter 문서를 참조하세요
  • nest_handoff_history: True(기본값) 인 경우, 러너는 다음 에이전트를 호출하기 전에 이전 대화를 하나의 assistant 메시지로 접습니다. 도우미는 콘텐츠를 <CONVERSATION HISTORY> 블록 안에 배치하고 이후 핸드오프가 발생할 때마다 새 턴을 계속 추가합니다. 원문(transcript)을 그대로 전달하려면 이를 False 로 설정하거나 원하는 대로 대화를 전달하는 사용자 지정 handoff 필터를 제공하세요. 모든 Runner 메서드RunConfig 를 전달하지 않으면 자동으로 생성하므로, 빠른 시작과 code examples 는 이 기본값을 자동으로 사용하며 명시적인 Handoff.input_filter 콜백이 있으면 계속해서 이를 우선합니다. 개별 핸드오프는 Handoff.nest_handoff_history 를 통해 이 설정을 재정의할 수 있습니다
  • handoff_history_mapper: nest_handoff_historyTrue 일 때 정규화된 대본(히스토리 + 핸드오프 아이템)을 수신하는 선택적 호출 가능 객체. 다음 에이전트로 전달할 입력 아이템의 정확한 리스트를 반환해야 하며, 전체 handoff 필터를 작성하지 않고도 내장 요약을 대체할 수 있습니다
  • tracing_disabled: 전체 실행에 대해 트레이싱 을 비활성화할 수 있습니다
  • trace_include_sensitive_data: LLM 및 도구 호출 입력/출력 등 잠재적으로 민감한 데이터를 트레이스에 포함할지 여부를 설정
  • workflow_name, trace_id, group_id: 실행에 대한 트레이싱 워크플로 이름, 트레이스 ID 및 트레이스 그룹 ID를 설정. 최소한 workflow_name 설정을 권장합니다. 그룹 ID는 여러 실행에 걸친 트레이스를 연결할 수 있는 선택적 필드입니다
  • trace_metadata: 모든 트레이스에 포함할 메타데이터

기본적으로 SDK 는 이제 한 에이전트가 다른 에이전트로 핸드오프할 때 이전 턴을 단일 assistant 요약 메시지 안에 중첩합니다. 이는 반복되는 assistant 메시지를 줄이고 전체 대화를 새 에이전트가 빠르게 스캔할 수 있는 단일 블록 안에 유지합니다. 레거시 동작으로 돌아가려면 RunConfig(nest_handoff_history=False) 를 전달하거나, 대화를 필요한 그대로 전달하는 handoff_input_filter(또는 handoff_history_mapper) 를 제공하세요. 특정 핸드오프에 대해 옵트아웃(또는 옵트인)하려면 handoff(..., nest_handoff_history=False) 또는 True 로 설정하세요. 사용자 지정 매퍼를 작성하지 않고 생성된 요약에 사용되는 래퍼 텍스트를 변경하려면 set_conversation_history_wrappers 를 호출하세요(기본값으로 복원하려면 reset_conversation_history_wrappers).

대화/채팅 스레드

어떤 run 메서드를 호출하더라도 하나 이상의 에이전트가 실행될 수 있으며(따라서 하나 이상의 LLM 호출), 채팅 대화의 단일 논리적 턴을 나타냅니다. 예:

  1. 사용자 턴: 사용자가 텍스트 입력
  2. Runner 실행: 첫 번째 에이전트가 LLM 을 호출하고 도구를 실행하고 두 번째 에이전트로 핸드오프, 두 번째 에이전트가 더 많은 도구를 실행한 뒤 출력을 생성

에이전트 실행이 끝나면 사용자에게 무엇을 보여줄지 선택할 수 있습니다. 예를 들어 에이전트가 생성한 모든 새 아이템을 보여주거나 최종 출력만 보여줄 수 있습니다. 어느 쪽이든, 사용자가 후속 질문을 할 수 있으며, 그 경우 run 메서드를 다시 호출하면 됩니다.

수동 대화 관리

다음 턴의 입력을 얻기 위해 RunResultBase.to_input_list() 메서드를 사용하여 대화 기록을 수동으로 관리할 수 있습니다:

async def main():
    agent = Agent(name="Assistant", instructions="Reply very concisely.")

    thread_id = "thread_123"  # Example thread ID
    with trace(workflow_name="Conversation", group_id=thread_id):
        # First turn
        result = await Runner.run(agent, "What city is the Golden Gate Bridge in?")
        print(result.final_output)
        # San Francisco

        # Second turn
        new_input = result.to_input_list() + [{"role": "user", "content": "What state is it in?"}]
        result = await Runner.run(agent, new_input)
        print(result.final_output)
        # California

Sessions 를 통한 자동 대화 관리

더 간단한 접근을 원한다면 Sessions 를 사용하여 .to_input_list() 를 수동 호출하지 않고 대화 기록을 자동으로 관리할 수 있습니다:

from agents import Agent, Runner, SQLiteSession

async def main():
    agent = Agent(name="Assistant", instructions="Reply very concisely.")

    # Create session instance
    session = SQLiteSession("conversation_123")

    thread_id = "thread_123"  # Example thread ID
    with trace(workflow_name="Conversation", group_id=thread_id):
        # First turn
        result = await Runner.run(agent, "What city is the Golden Gate Bridge in?", session=session)
        print(result.final_output)
        # San Francisco

        # Second turn - agent automatically remembers previous context
        result = await Runner.run(agent, "What state is it in?", session=session)
        print(result.final_output)
        # California

Sessions 는 다음을 자동으로 수행합니다:

  • 각 실행 전 대화 기록을 가져옴
  • 각 실행 후 새로운 메시지를 저장
  • 서로 다른 세션 ID 에 대해 별도의 대화를 유지

자세한 내용은 Sessions 문서에서 확인하세요.

서버 관리 대화

OpenAI 대화 상태 기능을 사용하여 to_input_list() 또는 Sessions 로 로컬에서 처리하는 대신 서버 측에서 대화 상태를 관리할 수도 있습니다. 이를 통해 과거 모든 메시지를 수동으로 다시 보내지 않고도 대화 기록을 유지할 수 있습니다. 자세한 내용은 OpenAI Conversation state 가이드를 참조하세요.

OpenAI 는 턴 간 상태를 추적하는 두 가지 방법을 제공합니다:

1. conversation_id 사용

먼저 OpenAI Conversations API 를 사용해 대화를 생성하고, 이후의 모든 호출에서 해당 ID 를 재사용합니다:

from agents import Agent, Runner
from openai import AsyncOpenAI

client = AsyncOpenAI()

async def main():
    # Create a server-managed conversation
    conversation = await client.conversations.create()
    conv_id = conversation.id    

    agent = Agent(name="Assistant", instructions="Reply very concisely.")

    # First turn
    result1 = await Runner.run(agent, "What city is the Golden Gate Bridge in?", conversation_id=conv_id)
    print(result1.final_output)
    # San Francisco

    # Second turn reuses the same conversation_id
    result2 = await Runner.run(
        agent,
        "What state is it in?",
        conversation_id=conv_id,
    )
    print(result2.final_output)
    # California

2. previous_response_id 사용

또 다른 옵션은 response chaining 으로, 각 턴이 이전 턴의 response ID 에 명시적으로 연결됩니다.

from agents import Agent, Runner

async def main():
    agent = Agent(name="Assistant", instructions="Reply very concisely.")

    # First turn
    result1 = await Runner.run(agent, "What city is the Golden Gate Bridge in?")
    print(result1.final_output)
    # San Francisco

    # Second turn, chained to the previous response
    result2 = await Runner.run(
        agent,
        "What state is it in?",
        previous_response_id=result1.last_response_id,
    )
    print(result2.final_output)
    # California

장기 실행 에이전트 및 휴먼인더루프 (HITL)

Agents SDK 의 Temporal 통합을 사용하면 휴먼인더루프 작업을 포함해 내구성 있는 장기 실행 워크플로를 실행할 수 있습니다. Temporal 과 Agents SDK 가 함께 작동하여 장기 작업을 완료하는 데모는 이 영상에서 확인하고, 문서는 여기에서 확인하세요.

예외

SDK 는 특정 경우 예외를 발생시킵니다. 전체 목록은 agents.exceptions 에 있습니다. 개요는 다음과 같습니다:

  • AgentsException: SDK 내에서 발생하는 모든 예외의 기본 클래스입니다. 다른 모든 구체적인 예외의 상위 일반 타입으로 사용됩니다
  • MaxTurnsExceeded: 에이전트 실행이 max_turns 한도를 초과하여 Runner.run, Runner.run_sync, Runner.run_streamed 메서드에서 발생하는 예외입니다. 이는 에이전트가 지정된 상호작용 턴 수 내에 작업을 완료하지 못했음을 나타냅니다
  • ModelBehaviorError: 기본 모델(LLM) 이 예상치 못한 또는 유효하지 않은 출력을 생성할 때 발생합니다. 다음을 포함할 수 있습니다:
    • 잘못된 JSON: 모델이 도구 호출 또는 직접 출력에 대해 잘못된 JSON 구조를 제공하는 경우, 특히 특정 output_type 이 정의된 경우
    • 예상치 못한 도구 관련 실패: 모델이 예상된 방식으로 도구를 사용하지 못한 경우
  • UserError: SDK 를 사용하는 코드 작성자(당신)가 SDK 사용 중 오류를 범했을 때 발생합니다. 일반적으로 잘못된 코드 구현, 잘못된 구성, SDK API 오사용에서 비롯됩니다
  • InputGuardrailTripwireTriggered, OutputGuardrailTripwireTriggered: 각각 입력 가드레일 또는 출력 가드레일의 조건이 충족될 때 발생합니다. 입력 가드레일은 처리 전에 들어오는 메시지를 검사하고, 출력 가드레일은 전달 전에 에이전트의 최종 응답을 검사합니다