개발자 가이드
OpenAI Agents SDK 에이전트에 행위 전 검사 추가
도구 래퍼가 제안된 행위를 먼저 검사하지 않으면 에이전트 함수 도구는 작업을 수행할 수 있습니다.
검사가 하는 일
검사는 permit, deny, 또는 escalate 를 판단을 정한 조항 및 서명된 영수증과 함께 돌려줍니다. 검사는 조언형이며 실행을 거부하는 것은 함수 도구 래퍼입니다. 호스팅 검사는 요청을 메모리에서 평가하고 요청 내용을 보관하지 않습니다.
코드
다음 위치에 게시된 감사된 예시와 정확히 같습니다: public/examples/frameworks/.
"""OpenAI Agents SDK tool wrapper that checks HANRIA before execution."""
from __future__ import annotations
import json
from collections.abc import Callable
from typing import Any
from urllib.error import URLError
from urllib.request import Request, urlopen
from agents import Agent, function_tool
CHECK_URL = "https://check.hanria.ai/v1/check"
MANDATE = {
"schema_version": "0.2-draft",
"mandate_id": "read-text-files",
"purpose": "Permit reads of text files under the example directory.",
"default": "deny",
"clauses": [
{
"id": "permit-example-read",
"effect": "permit",
"match": {
"kind": ["file"],
"verb": ["read"],
"target_prefix": ["/tmp/example/"],
},
}
],
}
def check_action(action: dict[str, Any], check_url: str = CHECK_URL) -> dict[str, Any]:
"""Return a HANRIA decision, mapping every transport or response failure to error."""
body = json.dumps({"mandate": MANDATE, "action": action}).encode()
request = Request(check_url, data=body, headers={"content-type": "application/json"})
try:
with urlopen(request, timeout=10) as response:
decision = json.load(response)
except (OSError, URLError, ValueError, json.JSONDecodeError) as error:
return {"outcome": "error", "reason": f"HANRIA check failed: {error}"}
if not isinstance(decision, dict) or decision.get("outcome") not in {
"permit", "deny", "escalate", "error"
}:
return {"outcome": "error", "reason": "HANRIA returned an invalid decision"}
return decision
def guarded_tool_call(
tool_name: str,
operation: dict[str, Any],
execute: Callable[[], Any],
check_url: str = CHECK_URL,
) -> Any:
"""Call execute only after HANRIA returns permit for the proposed tool call."""
action = {
"schema_version": "0.1-draft",
"requested_by": {"agent": "openai-agents-example"},
"operation": operation,
"justification": f"Run {tool_name} through the OpenAI Agents SDK.",
}
decision = check_action(action, check_url)
if decision.get("outcome") != "permit":
return (
f"Tool refused. HANRIA outcome={decision.get('outcome', 'error')}; "
f"reason={decision.get('reason', 'no reason returned')}. Do not retry blindly."
)
return execute()
def build_agent(check_url: str = CHECK_URL) -> Agent:
"""Construct an agent whose file tool is wrapped by the HANRIA check."""
@function_tool
def read_text_file(path: str) -> str:
"""Read one UTF-8 text file after the mandate permits it."""
return guarded_tool_call(
"read_text_file",
{"kind": "file", "verb": "read", "target": path},
lambda: open(path, encoding="utf-8").read(),
check_url,
)
return Agent(
name="HANRIA guarded reader",
instructions="Use read_text_file only when it is needed.",
tools=[read_text_file],
)
if __name__ == "__main__":
agent = build_agent()
print(f"Constructed {agent.name} with {len(agent.tools)} guarded tool.")
실행하기
openai_agents.py 파일을 저장한 다음 해당 폴더에서 다음 명령을 실행하세요.
uv run --with openai-agents==0.23.1 python openai_agents.py
이 명령은 보호된 함수 도구 하나를 가진 에이전트를 구성합니다. 검사는 기술한 작업만 판단하므로 각 도구가 실제로 할 일에서 그 도구의 작업을 구성하세요. guarded_tool_call 호출을 위임장 검사가 필요한 모든 도구 안에 두고, 작업을 하기 전에 해당 작업을 전달하세요.
모든 결과 처리하기
- permit: 래퍼는
execute()를 호출합니다. 위임장 조항이 제안된 행위를 허용하기 때문입니다. - deny: 래퍼는 거부 문자열을 돌려줍니다. 다른 도구로 행위를 시도하지 마세요.
- escalate: 래퍼는 거부 문자열을 돌려줍니다. 이 래퍼 밖에서 운영자에게 결정을 요청하세요.
- error: 래퍼는 거부 문자열을 돌려줍니다. deny로 취급하고 이유를 보고하며 무작정 재시도하지 마세요.
전송 실패, 잘못된 형식의 응답 또는 알 수 없는 결과는 error가 되며 정확한 permit 만 execute()에 도달합니다.