개발자 가이드

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 호출을 위임장 검사가 필요한 모든 도구 안에 두고, 작업을 하기 전에 해당 작업을 전달하세요.

모든 결과 처리하기

전송 실패, 잘못된 형식의 응답 또는 알 수 없는 결과는 error가 되며 정확한 permit 만 execute()에 도달합니다.

참고 자료

검사 사용해 보기 · 서명된 영수증 검증하기 · OpenAPI 문서 · 에이전트용 요약