Skip to content

OpenAI Agents SDK

pip install "mimir-decisions[local,openai-agents]"

mimir.integrations.openai_agents.as_function_tool converts a decision tool into a FunctionTool for use in an Agent's tool list. The typed result is returned as structured data.

guard(tool, check) gates any FunctionTool with a tool-call check: a certified denial is rejected with the check's reason, and an escalated call pauses the run with RunState.approve or RunState.reject for human review.

Tested from openai-agents 0.21.

Native tools

"""An OpenAI Agents SDK agent that routes support tickets with a MIMIR decision tool.

Install `mimir-decisions[local,openai-agents]`, set `OPENAI_API_KEY`, then run
`make example NAME=openai_agents/agent`.
"""

import asyncio
from typing import Final

from agents import Agent, Model, Runner

from mimir import Choice, Decider, Mimir
from mimir.integrations.openai_agents import as_function_tool

TEAMS: Final = {
    "billing": "Billing: payments, refunds and invoices",
    "security": "Security: account access, passwords and fraud",
    "shipping": "Shipping: deliveries, tracking and returns",
}
INSTRUCTIONS: Final = (
    "Route the customer's ticket with route_ticket, then tell the customer which team will "
    "answer. If the decision is deferred, say that a person will review the ticket."
)


def build_agent(decider: Decider, model: str | Model | None = None) -> Agent[None]:
    """The support agent, answering with `decider`; `model` defaults to the SDK's."""
    route_ticket = decider.tool(
        "route_ticket",
        Choice("Which team should handle this ticket?", TEAMS),
        description="Route a customer support ticket to the team that owns it.",
    )
    return Agent(
        name="support",
        instructions=INSTRUCTIONS,
        tools=[as_function_tool(route_ticket)],
        model=model,
    )


async def main() -> None:
    agent = build_agent(Mimir.from_pretrained("Mythologic/MIMIR-1"))
    result = await Runner.run(agent, "I was charged twice for order 4412.")
    print(result.final_output)


if __name__ == "__main__":
    asyncio.run(main())

Tool-call check

"""An OpenAI Agents SDK agent whose refunds MIMIR checks against the refund rules first.

A certified yes runs the refund, a certified no rejects it with the reason, and anything else
pauses the run until a person at the console approves or rejects it. Install
`mimir-decisions[local,openai-agents]`, set `OPENAI_API_KEY`, then run
`make example NAME=openai_agents/guarded_agent`.
"""

import asyncio
from typing import Final

from agents import Agent, FunctionTool, Model, Runner, RunResult, function_tool

from mimir import Decider, Mimir
from mimir.integrations.openai_agents import guard

RULES: Final = (
    "A refund is at most the amount the customer paid for the order.",
    "Refunds above 500 dollars need a manager's approval.",
)
INSTRUCTIONS: Final = "Issue the refunds customers ask for with issue_refund."


@function_tool
def issue_refund(order: str, amount: float) -> str:
    """Refund `amount` dollars on `order`."""
    return f"Refunded {amount:.2f} dollars on order {order}."


def build_agent(
    decider: Decider, refund: FunctionTool = issue_refund, model: str | Model | None = None
) -> Agent[None]:
    """The refunds agent, its `refund` tool checked by `decider` against `RULES`."""
    check = decider.tool_call_check(RULES, tools=[refund.name])
    return Agent(
        name="refunds", instructions=INSTRUCTIONS, tools=[guard(refund, check)], model=model
    )


async def run_with_approvals(agent: Agent[None], request: str) -> RunResult:
    """Run `agent`, asking at the console about each call the check escalates."""
    result = await Runner.run(agent, request)
    while result.interruptions:
        state = result.to_state()
        for pending in result.interruptions:
            answer = input(f"Approve {pending.name} {pending.arguments}? [y/N] ")
            if answer.strip().lower() == "y":
                state.approve(pending)
            else:
                state.reject(pending)
        result = await Runner.run(agent, state)
    return result


async def main() -> None:
    agent = build_agent(Mimir.from_pretrained("Mythologic/MIMIR-1"))
    result = await run_with_approvals(agent, "Refund 900 dollars on order 4412.")
    print(result.final_output)


if __name__ == "__main__":
    asyncio.run(main())

Over MCP

"""An OpenAI Agents SDK agent using MIMIR's MCP server over stdio.

The server runs `examples/tools.yaml`, so the agent sees `route_ticket` and passes only the
ticket. Install `openai-agents` and `uv`, set `OPENAI_API_KEY`, then run
`make example NAME=openai_agents/mcp_agent`.
"""

import asyncio
from collections.abc import Sequence
from pathlib import Path
from typing import Final

from agents import Agent, Model, Runner
from agents.mcp import MCPServer, MCPServerStdio

TOOLS: Final = Path(__file__).parents[1] / "tools.yaml"
SERVER: Final = (
    "uvx",
    ("--from", "mimir-decisions[local,mcp]", "mimir-decisions", "mcp", "--tools", str(TOOLS)),
)
INSTRUCTIONS: Final = (
    "Route the customer's ticket with route_ticket, then tell the customer which team will "
    "answer. If the decision is deferred, say that a person will review the ticket."
)


def mimir_server(command: str, arguments: Sequence[str]) -> MCPServerStdio:
    """MIMIR's MCP server, started as `command` with `arguments`."""
    return MCPServerStdio(
        params={"command": command, "args": list(arguments)},
        name="mimir",
        client_session_timeout_seconds=90,
    )


def build_agent(server: MCPServer, model: str | Model | None = None) -> Agent[None]:
    """The support agent, with the tools of `server`; `model` defaults to the SDK's."""
    return Agent(name="support", instructions=INSTRUCTIONS, mcp_servers=[server], model=model)


async def main() -> None:
    async with mimir_server(*SERVER) as server:
        result = await Runner.run(build_agent(server), "I was charged twice for order 4412.")
        print(result.final_output)


if __name__ == "__main__":
    asyncio.run(main())