도구
도구는 에이전트가 행동을 취할 수 있게 합니다. 예를 들어 데이터 가져오기, 코드 실행, 외부 API 호출, 심지어 컴퓨터 사용까지 가능합니다. SDK 는 다섯 가지 카테고리를 지원합니다:
- OpenAI 호스트하는 도구: OpenAI 서버에서 모델과 함께 실행됩니다
- 로컬 런타임 도구: 사용자 환경에서 실행됩니다(컴퓨터 사용, 셸, 패치 적용)
- 함수 호출: 어떤 Python 함수든 도구로 감쌉니다
- Agents as tools: 완전한 핸드오프 없이 에이전트를 호출 가능한 도구로 노출합니다
- 실험적: Codex 도구: 도구 호출로 워크스페이스 범위의 Codex 작업을 실행합니다
호스티드 툴
OpenAI 는OpenAIResponsesModel 사용 시 몇 가지 내장 도구를 제공합니다:
WebSearchTool은 에이전트가 웹을 검색할 수 있게 합니다FileSearchTool은 OpenAI 벡터 스토어에서 정보를 검색할 수 있게 합니다CodeInterpreterTool은 LLM 이 샌드박스 환경에서 코드를 실행할 수 있게 합니다HostedMCPTool은 원격 MCP 서버의 도구를 모델에 노출합니다ImageGenerationTool은 프롬프트로부터 이미지를 생성합니다
fromagentsimportAgent,FileSearchTool,Runner,WebSearchToolagent=Agent(name="Assistant",tools=[WebSearchTool(),FileSearchTool(max_num_results=3,vector_store_ids=["VECTOR_STORE_ID"],),],)asyncdefmain():result=awaitRunner.run(agent,"Which coffee shop should I go to, taking into account my preferences and the weather today in SF?")print(result.final_output)로컬 런타임 도구
로컬 런타임 도구는 사용자 환경에서 실행되며, 사용자가 구현을 제공해야 합니다:
ComputerTool: GUI/브라우저 자동화를 활성화하려면Computer또는AsyncComputer인터페이스를 구현합니다ShellTool또는LocalShellTool: 명령을 실행할 셸 실행기를 제공합니다ApplyPatchTool: 로컬에서 diff 를 적용하려면ApplyPatchEditor를 구현합니다
fromagentsimportAgent,ApplyPatchTool,ShellToolfromagents.computerimportAsyncComputerfromagents.editorimportApplyPatchResult,ApplyPatchOperation,ApplyPatchEditorclassNoopComputer(AsyncComputer):environment="browser"dimensions=(1024,768)asyncdefscreenshot(self):return""asyncdefclick(self,x,y,button):...asyncdefdouble_click(self,x,y):...asyncdefscroll(self,x,y,scroll_x,scroll_y):...asyncdeftype(self,text):...asyncdefwait(self):...asyncdefmove(self,x,y):...asyncdefkeypress(self,keys):...asyncdefdrag(self,path):...classNoopEditor(ApplyPatchEditor):asyncdefcreate_file(self,op:ApplyPatchOperation):returnApplyPatchResult(status="completed")asyncdefupdate_file(self,op:ApplyPatchOperation):returnApplyPatchResult(status="completed")asyncdefdelete_file(self,op:ApplyPatchOperation):returnApplyPatchResult(status="completed")asyncdefrun_shell(request):return"shell output"agent=Agent(name="Local tools agent",tools=[ShellTool(executor=run_shell),ApplyPatchTool(editor=NoopEditor()),# ComputerTool expects a Computer/AsyncComputer implementation; omitted here for brevity.],)함수 도구
어떤 Python 함수든 도구로 사용할 수 있습니다. Agents SDK 가 도구를 자동으로 설정합니다:
- 도구 이름은 Python 함수 이름이 됩니다(또는 이름을 제공할 수 있습니다)
- 도구 설명은 함수의 docstring 에서 가져옵니다(또는 설명을 제공할 수 있습니다)
- 함수 입력의 스키마는 함수 인자에서 자동으로 생성됩니다
- 각 입력에 대한 설명은 비활성화하지 않는 한 함수의 docstring 에서 가져옵니다
Python 의inspect 모듈을 사용해 함수 시그니처를 추출하고,griffe 로 docstring 을 파싱하며,pydantic 으로 스키마를 생성합니다.
importjsonfromtyping_extensionsimportTypedDict,AnyfromagentsimportAgent,FunctionTool,RunContextWrapper,function_toolclassLocation(TypedDict):lat:floatlong:float@function_tool# (1)!asyncdeffetch_weather(location:Location)->str:# (2)!"""Fetch the weather for a given location. Args: location: The location to fetch the weather for. """# In real life, we'd fetch the weather from a weather APIreturn"sunny"@function_tool(name_override="fetch_data")# (3)!defread_file(ctx:RunContextWrapper[Any],path:str,directory:str|None=None)->str:"""Read the contents of a file. Args: path: The path to the file to read. directory: The directory to read the file from. """# In real life, we'd read the file from the file systemreturn"<file contents>"agent=Agent(name="Assistant",tools=[fetch_weather,read_file],# (4)!)fortoolinagent.tools:ifisinstance(tool,FunctionTool):print(tool.name)print(tool.description)print(json.dumps(tool.params_json_schema,indent=2))print()- 함수 인자로 어떤 Python 타입이든 사용할 수 있으며, 함수는 동기 또는 비동기일 수 있습니다
- docstring 이 있으면, 설명과 인자 설명을 캡처하는 데 사용됩니다
- 함수는 선택적으로
context를 받을 수 있습니다(첫 번째 인자여야 함). 또한 도구 이름, 설명, 사용할 docstring 스타일 등 오버라이드도 설정할 수 있습니다 - 데코레이트된 함수를 tools 리스트에 전달할 수 있습니다
출력 보기 위해 펼치기
fetch_weatherFetch the weather for a given location.{"$defs": { "Location": { "properties": { "lat": { "title": "Lat", "type": "number" }, "long": { "title": "Long", "type": "number" } }, "required": [ "lat", "long" ], "title": "Location", "type": "object" }},"properties": { "location": { "$ref": "#/$defs/Location", "description": "The location to fetch the weather for." }},"required": [ "location"],"title": "fetch_weather_args","type": "object"}fetch_dataRead the contents of a file.{"properties": { "path": { "description": "The path to the file to read.", "title": "Path", "type": "string" }, "directory": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "The directory to read the file from.", "title": "Directory" }},"required": [ "path"],"title": "fetch_data_args","type": "object"}함수 도구에서 이미지 또는 파일 반환
텍스트 출력뿐 아니라, 함수 도구의 출력으로 하나 또는 여러 개의 이미지나 파일을 반환할 수 있습니다. 이를 위해 다음 중 아무 것이나 반환할 수 있습니다:
- 이미지:
ToolOutputImage(또는 TypedDict 버전인ToolOutputImageDict) - 파일:
ToolOutputFileContent(또는 TypedDict 버전인ToolOutputFileContentDict) - 텍스트: 문자열 또는 문자열로 변환 가능한 객체, 또는
ToolOutputText(또는 TypedDict 버전인ToolOutputTextDict)
커스텀 함수 도구
때로는 Python 함수를 도구로 사용하고 싶지 않을 수 있습니다. 원한다면FunctionTool 을 직접 만들 수 있습니다. 다음을 제공해야 합니다:
namedescriptionparams_json_schema: 인자에 대한 JSON 스키마on_invoke_tool:ToolContext와 인자를 JSON 문자열로 받아, 도구 출력을 문자열로 반환해야 하는 비동기 함수
fromtypingimportAnyfrompydanticimportBaseModelfromagentsimportRunContextWrapper,FunctionTooldefdo_some_work(data:str)->str:return"done"classFunctionArgs(BaseModel):username:strage:intasyncdefrun_function(ctx:RunContextWrapper[Any],args:str)->str:parsed=FunctionArgs.model_validate_json(args)returndo_some_work(data=f"{parsed.username} is{parsed.age} years old")tool=FunctionTool(name="process_user",description="Processes extracted user data",params_json_schema=FunctionArgs.model_json_schema(),on_invoke_tool=run_function,)인자 및 docstring 자동 파싱
앞서 언급했듯이, 도구의 스키마를 추출하기 위해 함수 시그니처를 자동으로 파싱하고, 도구 및 개별 인자에 대한 설명을 추출하기 위해 docstring 을 파싱합니다. 이에 대한 참고 사항은 다음과 같습니다:
- 시그니처 파싱은
inspect모듈로 수행됩니다. 인자 타입을 이해하기 위해 타입 어노테이션을 사용하고, 전체 스키마를 나타내는 Pydantic 모델을 동적으로 구성합니다. Python 기본 타입, Pydantic 모델, TypedDict 등 대부분의 타입을 지원합니다 - docstring 파싱에는
griffe를 사용합니다. 지원되는 docstring 형식은google,sphinx,numpy입니다. docstring 형식을 자동으로 감지하려고 시도하지만 최선의 노력(best-effort)이며,function_tool호출 시 명시적으로 설정할 수 있습니다. 또한use_docstring_info를False로 설정해 docstring 파싱을 비활성화할 수 있습니다
스키마 추출 코드는agents.function_schema 에 있습니다.
Agents as tools
일부 워크플로에서는 제어권을 핸드오프하기보다, 중앙 에이전트가 전문화된 에이전트 네트워크를 멀티 에이전트 오케스트레이션하도록 만들고 싶을 수 있습니다. 이를 위해 에이전트를 도구로 모델링할 수 있습니다.
fromagentsimportAgent,Runnerimportasynciospanish_agent=Agent(name="Spanish agent",instructions="You translate the user's message to Spanish",)french_agent=Agent(name="French agent",instructions="You translate the user's message to French",)orchestrator_agent=Agent(name="orchestrator_agent",instructions=("You are a translation agent. You use the tools given to you to translate.""If asked for multiple translations, you call the relevant tools."),tools=[spanish_agent.as_tool(tool_name="translate_to_spanish",tool_description="Translate the user's message to Spanish",),french_agent.as_tool(tool_name="translate_to_french",tool_description="Translate the user's message to French",),],)asyncdefmain():result=awaitRunner.run(orchestrator_agent,input="Say 'Hello, how are you?' in Spanish.")print(result.final_output)도구-에이전트 커스터마이징
agent.as_tool 함수는 에이전트를 도구로 쉽게 바꾸기 위한 편의 메서드입니다.max_turns,run_config,hooks,previous_response_id,conversation_id,session,needs_approval 같은 일반적인 런타임 옵션을 지원합니다. 또한parameters,input_builder,include_input_schema 로 구조화된 입력도 지원합니다. 고급 오케스트레이션(예: 조건부 재시도, 폴백 동작, 여러 에이전트 호출 체이닝)의 경우, 도구 구현에서Runner.run 을 직접 사용하세요:
@function_toolasyncdefrun_my_agent()->str:"""A tool that runs the agent with custom configs"""agent=Agent(name="My agent",instructions="...")result=awaitRunner.run(agent,input="...",max_turns=5,run_config=...)returnstr(result.final_output)도구-에이전트를 위한 구조화된 입력
기본적으로Agent.as_tool() 은 단일 문자열 입력({"input": "..."})을 기대하지만,parameters(Pydantic 모델 또는 dataclass 타입)를 전달해 구조화된 스키마를 노출할 수 있습니다.
추가 옵션:
include_input_schema=True는 생성된 중첩 입력에 전체 JSON Schema 를 포함합니다input_builder=...는 구조화된 도구 인자가 중첩된 에이전트 입력이 되는 방식을 완전히 커스터마이징할 수 있게 합니다RunContextWrapper.tool_input에는 중첩 run 컨텍스트 내부에서 파싱된 구조화 페이로드가 들어 있습니다
frompydanticimportBaseModel,FieldclassTranslationInput(BaseModel):text:str=Field(description="Text to translate.")source:str=Field(description="Source language.")target:str=Field(description="Target language.")translator_tool=translator_agent.as_tool(tool_name="translate_text",tool_description="Translate text between languages.",parameters=TranslationInput,include_input_schema=True,)완전히 실행 가능한 예시는examples/agent_patterns/agents_as_tools_structured.py 를 참고하세요.
도구-에이전트를 위한 승인 게이트
Agent.as_tool(..., needs_approval=...) 는function_tool 과 동일한 승인 플로우를 사용합니다. 승인이 필요하면 run 이 일시정지되고 보류 항목이result.interruptions 에 나타납니다. 이후result.to_state() 를 사용하고,state.approve(...) 또는state.reject(...) 호출 후 재개하세요. 전체 일시정지/재개 패턴은Human-in-the-loop guide 를 참고하세요.
커스텀 출력 추출
일부 경우에는 도구-에이전트의 출력을 중앙 에이전트에 반환하기 전에 수정하고 싶을 수 있습니다. 이는 다음과 같은 경우에 유용할 수 있습니다:
- 하위 에이전트의 채팅 히스토리에서 특정 정보(예: JSON 페이로드)를 추출
- 에이전트의 최종 답변을 변환 또는 재포맷(예: Markdown 을 일반 텍스트 또는 CSV 로 변환)
- 출력 유효성 검증 또는 응답이 누락되었거나 형식이 잘못된 경우 폴백 값 제공
이를 위해as_tool 메서드에custom_output_extractor 인자를 제공할 수 있습니다:
asyncdefextract_json_payload(run_result:RunResult)->str:# Scan the agent’s outputs in reverse order until we find a JSON-like message from a tool call.foriteminreversed(run_result.new_items):ifisinstance(item,ToolCallOutputItem)anditem.output.strip().startswith("{"):returnitem.output.strip()# Fallback to an empty JSON object if nothing was foundreturn"{}"json_tool=data_agent.as_tool(tool_name="get_data_json",tool_description="Run the data agent and return only its JSON payload",custom_output_extractor=extract_json_payload,)중첩 에이전트 run 스트리밍
as_tool 에on_stream 콜백을 전달하면, 스트림이 완료된 뒤 최종 출력을 반환하면서도 중첩 에이전트가 내보내는 스트리밍 이벤트를 수신할 수 있습니다.
fromagentsimportAgentToolStreamEventasyncdefhandle_stream(event:AgentToolStreamEvent)->None:# Inspect the underlying StreamEvent along with agent metadata.print(f"[stream]{event['agent'].name} ::{event['event'].type}")billing_agent_tool=billing_agent.as_tool(tool_name="billing_helper",tool_description="Answer billing questions.",on_stream=handle_stream,# Can be sync or async.)예상 동작:
- 이벤트 타입은
StreamEvent["type"]를 미러링합니다:raw_response_event,run_item_stream_event,agent_updated_stream_event on_stream을 제공하면 중첩 에이전트가 자동으로 스트리밍 모드로 실행되며, 최종 출력을 반환하기 전에 스트림을 모두 소비합니다- 핸들러는 동기 또는 비동기일 수 있으며, 각 이벤트는 도착하는 순서대로 전달됩니다
- 도구가 모델 도구 호출을 통해 호출되면
tool_call이 존재하며, 직접 호출은None으로 남을 수 있습니다 - 완전히 실행 가능한 샘플은
examples/agent_patterns/agents_as_tools_streaming.py를 참고하세요
조건부 도구 활성화
is_enabled 매개변수를 사용해 런타임에 에이전트 도구를 조건부로 활성화 또는 비활성화할 수 있습니다. 이를 통해 컨텍스트, 사용자 선호, 또는 런타임 조건에 따라 LLM 에 사용 가능한 도구를 동적으로 필터링할 수 있습니다.
importasynciofromagentsimportAgent,AgentBase,Runner,RunContextWrapperfrompydanticimportBaseModelclassLanguageContext(BaseModel):language_preference:str="french_spanish"deffrench_enabled(ctx:RunContextWrapper[LanguageContext],agent:AgentBase)->bool:"""Enable French for French+Spanish preference."""returnctx.context.language_preference=="french_spanish"# Create specialized agentsspanish_agent=Agent(name="spanish_agent",instructions="You respond in Spanish. Always reply to the user's question in Spanish.",)french_agent=Agent(name="french_agent",instructions="You respond in French. Always reply to the user's question in French.",)# Create orchestrator with conditional toolsorchestrator=Agent(name="orchestrator",instructions=("You are a multilingual assistant. You use the tools given to you to respond to users. ""You must call ALL available tools to provide responses in different languages. ""You never respond in languages yourself, you always use the provided tools."),tools=[spanish_agent.as_tool(tool_name="respond_spanish",tool_description="Respond to the user's question in Spanish",is_enabled=True,# Always enabled),french_agent.as_tool(tool_name="respond_french",tool_description="Respond to the user's question in French",is_enabled=french_enabled,),],)asyncdefmain():context=RunContextWrapper(LanguageContext(language_preference="french_spanish"))result=awaitRunner.run(orchestrator,"How are you?",context=context.context)print(result.final_output)asyncio.run(main())is_enabled 매개변수는 다음을 받을 수 있습니다:
- 불리언 값:
True(항상 활성) 또는False(항상 비활성) - 호출 가능한 함수:
(context, agent)를 받아 불리언을 반환하는 함수 - 비동기 함수: 복잡한 조건 로직을 위한 비동기 함수
비활성화된 도구는 런타임에서 LLM 에 완전히 숨겨지므로, 다음에 유용합니다:
- 사용자 권한에 따른 기능 게이팅
- 환경별 도구 가용성(dev vs prod)
- 서로 다른 도구 구성에 대한 A/B 테스트
- 런타임 상태에 따른 동적 도구 필터링
실험적: Codex 도구
codex_tool 은 Codex CLI 를 래핑하여, 에이전트가 도구 호출 중에 워크스페이스 범위 작업(셸, 파일 편집, MCP 도구)을 실행할 수 있게 합니다.이 인터페이스는 실험적이며 변경될 수 있습니다.기본적으로 도구 이름은codex 입니다. 커스텀 이름을 설정한다면codex 이거나codex_ 로 시작해야 합니다.에이전트에 여러 Codex 도구가 포함될 때는, 각각이 고유한 이름을 사용해야 합니다(Codex 도구끼리뿐 아니라 비 Codex 도구와도 포함하여).
fromagentsimportAgentfromagents.extensions.experimental.codeximportThreadOptions,TurnOptions,codex_toolagent=Agent(name="Codex Agent",instructions="Use the codex tool to inspect the workspace and answer the question.",tools=[codex_tool(sandbox_mode="workspace-write",working_directory="/path/to/repo",default_thread_options=ThreadOptions(model="gpt-5.2-codex",model_reasoning_effort="low",network_access_enabled=True,web_search_mode="disabled",approval_policy="never",),default_turn_options=TurnOptions(idle_timeout_seconds=60,),persist_session=True,)],)알아둘 점:
- 인증:
CODEX_API_KEY(권장) 또는OPENAI_API_KEY를 설정하거나,codex_options={"api_key": "..."}를 전달하세요 - 런타임:
codex_options.base_url이 CLI base URL 을 오버라이드합니다 - 바이너리 해석: CLI 경로를 고정하려면
codex_options.codex_path_override(또는CODEX_PATH)를 설정하세요. 그렇지 않으면 SDK 가PATH에서codex를 찾고, 이후 번들된 vendor 바이너리로 폴백합니다 - 환경:
codex_options.env가 서브프로세스 환경을 완전히 제어합니다. 이것이 제공되면 서브프로세스는os.environ을 상속하지 않습니다 - 스트림 제한:
codex_options.codex_subprocess_stream_limit_bytes(또는OPENAI_AGENTS_CODEX_SUBPROCESS_STREAM_LIMIT_BYTES)가 stdout/stderr 리더 제한을 제어합니다. 유효 범위는65536~67108864이며 기본값은8388608입니다 - 입력: 도구 호출에는
inputs에{ "type": "text", "text": ... }또는{ "type": "local_image", "path": ... }항목이 최소 1개 포함되어야 합니다 - 스레드 기본값:
model_reasoning_effort,web_search_mode(레거시web_search_enabled보다 권장),approval_policy,additional_directories를 위해default_thread_options를 구성하세요 - 턴 기본값:
idle_timeout_seconds와 취소signal을 위해default_turn_options를 구성하세요 - 안전:
sandbox_mode를working_directory와 함께 사용하고, Git 저장소 밖에서는skip_git_repo_check=True를 설정하세요 - run 컨텍스트 스레드 지속성:
use_run_context_thread_id=True는 동일한 컨텍스트를 공유하는 run 들에 걸쳐 run 컨텍스트에thread_id를 저장하고 재사용합니다. 이를 위해서는 변경 가능한 run 컨텍스트(예:dict또는 쓰기 가능한 객체 필드)가 필요합니다 - run 컨텍스트 키 기본값: 저장 키는
name="codex"일 때 기본적으로codex_thread_id,name="codex_<suffix>"일 때는codex_thread_id_<suffix>입니다. 오버라이드하려면run_context_thread_id_key를 설정하세요 - Thread ID 우선순위: 호출별
thread_id입력이 최우선이며, 그 다음은(활성화된 경우) run 컨텍스트thread_id, 그 다음은 구성된thread_id옵션입니다 - 스트리밍:
on_stream은 스레드/턴 라이프사이클 이벤트와 아이템 이벤트(reasoning,command_execution,mcp_tool_call,file_change,web_search,todo_list,error아이템 업데이트)를 받습니다 - 출력: 결과에는
response,usage,thread_id가 포함되며, usage 는RunContextWrapper.usage에 추가됩니다 - 구조:
output_schema는 타입 있는 출력이 필요할 때 구조화된 Codex 응답을 강제합니다 - 완전히 실행 가능한 샘플은
examples/tools/codex.py와examples/tools/codex_same_thread.py를 참고하세요
함수 도구에서 오류 처리
@function_tool 로 함수 도구를 만들 때failure_error_function 을 전달할 수 있습니다. 이는 도구 호출이 크래시했을 때 LLM 에 제공할 오류 응답을 만들어주는 함수입니다.
- 기본값(아무것도 전달하지 않으면)으로는
default_tool_error_function이 실행되며, LLM 에 오류가 발생했다고 알립니다 - 사용자 정의 오류 함수를 전달하면, 대신 그 함수가 실행되어 응답이 LLM 으로 전송됩니다
None을 명시적으로 전달하면, 도구 호출 오류가 다시 raise 되어 사용자가 처리해야 합니다. 이는 모델이 유효하지 않은 JSON 을 생성한 경우의ModelBehaviorError일 수도 있고, 코드가 크래시한 경우의UserError일 수도 있습니다
fromagentsimportfunction_tool,RunContextWrapperfromtypingimportAnydefmy_custom_error_function(context:RunContextWrapper[Any],error:Exception)->str:"""A custom function to provide a user-friendly error message."""print(f"A tool call failed with the following error:{error}")return"An internal server error occurred. Please try again later."@function_tool(failure_error_function=my_custom_error_function)defget_user_profile(user_id:str)->str:"""Fetches a user profile from a mock API. This function demonstrates a 'flaky' or failing API call. """ifuser_id=="user_123":return"User profile for user_123 successfully retrieved."else:raiseValueError(f"Could not retrieve profile for user_id:{user_id}. API returned an error.")FunctionTool 객체를 수동으로 생성하는 경우에는on_invoke_tool 함수 내부에서 오류를 처리해야 합니다.