Agent Governance Foundation

OpenAI Agents SDK integration

One governance surface here — every tool is a FunctionTool. guard_function_tool wraps it and enforces policy before it runs, including calls the model makes itself.

Install

pip install agf-sdk[openai-agents]

Requires Python ≥ 3.10.

Set your API key

export AGF_API_KEY=agfk_your_key_here

Generate a key from Settings → API Keys.

Guard a function tool

guard_function_tool wraps an already-built FunctionTool and returns a new one — the original is never mutated. Wire the guarded tool into your agent in place of the original.

import os
from agents import Agent, function_tool
from agf.client import AGFClient
from agf.keys import build_self_signed_chain
from agf.openai_agents import guard_function_tool

client = AGFClient(api_key=os.environ["AGF_API_KEY"])
AGF_AGENT_ID = "did:agf:agt_01abc"


def _refund_chain_provider(ctx):
    # Built fresh per call — a chain built once expires in 5 minutes.
    return build_self_signed_chain(AGF_PRIVATE_KEY_PEM, AGF_AGENT_ID, "tool:issue_refund", "agf")


@function_tool
def issue_refund(order_id: str, amount: str) -> str:
    return process_refund(order_id, amount)


guarded_issue_refund = guard_function_tool(
    issue_refund,
    client,
    agent_id=AGF_AGENT_ID,
    action_type="tool:issue_refund",
    chain_provider=_refund_chain_provider,
    validate_execution=True,
    report_outcome=True,
)

agent = Agent(name="support-agent", tools=[guarded_issue_refund])
Why not the SDK's own guardrail hook? The native ToolInputGuardrail collapses every failure into one generic exception and has no output-side hook that sees both success and failure — wrapping on_invoke_tool directly preserves the real AGFDeniedError/AGFReviewRequiredError types and lets report_outcome work correctly.

Accurate outcome reporting on tool errors

This is an OpenAI Agents SDK behavior, not an AGF limitation — worth knowing if you turn on report_outcome:

# By default, this SDK catches a tool's internal exception and turns it
# into a string result the model sees — report_outcome never sees the
# failure. If you need accurate "not executed" reporting on internal
# tool errors, disable that catch on the tool itself:
@function_tool(failure_error_function=None)
def issue_refund(order_id: str, amount: str) -> str:
    return process_refund(order_id, amount)

Related