Files
2026-07-13 12:39:17 +08:00

117 lines
4.6 KiB
Python

"""
Show how a sandbox agent can combine three tool sources in one run.
This example gives the model:
1. A sandbox workspace to inspect with the shared shell capability.
2. A normal local function tool for approval routing.
3. A local stdio MCP server for reference policy lookups.
"""
import argparse
import asyncio
import sys
from pathlib import Path
from agents import Runner, function_tool
from agents.mcp import MCPServerStdio
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from examples.sandbox.misc.example_support import text_manifest, tool_call_name
from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability
DEFAULT_QUESTION = (
"Review this enterprise renewal request. Tell me who needs to approve the discount, "
"whether security review is still open, and the most important note for the account team. "
"Confirm the approval and security answers against the reference policy server before you respond."
)
@function_tool
def get_discount_approval_path(discount_percent: int) -> str:
"""Return the approver required for a proposed discount percentage."""
if discount_percent <= 10:
return "The account executive can approve discounts up to 10 percent."
if discount_percent <= 15:
return "The regional sales director must approve discounts from 11 to 15 percent."
return "Finance and the regional sales director must both approve discounts above 15 percent."
async def main(model: str, question: str) -> None:
# This manifest becomes the workspace that the sandbox agent can inspect.
manifest = text_manifest(
{
"renewal_request.md": (
"# Renewal request\n\n"
"- Customer: Contoso Manufacturing.\n"
"- Requested discount: 14 percent.\n"
"- Renewal term: 12 months.\n"
"- Requested close date: March 28.\n"
),
"account_notes.md": (
"# Account notes\n\n"
"- The customer expanded usage in two plants this quarter.\n"
"- Security review for the new data export workflow was opened last week.\n"
"- Procurement wants a final approval map before they send the order form.\n"
),
}
)
# The reference MCP server is another local process. The agent can call its tools alongside
# the sandbox shell tool and the normal Python function tool.
async with MCPServerStdio(
name="Reference Policy Server",
params={
"command": sys.executable,
"args": [
str(Path(__file__).resolve().parent / "misc" / "reference_policy_mcp_server.py")
],
},
) as server:
agent = SandboxAgent(
name="Renewal Review Assistant",
model=model,
instructions=(
"You review renewal requests. Inspect the packet, use "
"`get_discount_approval_path` for discount routing, and use the MCP reference "
"policy server when you need confirmation. Before you answer, you must call "
"`get_discount_approval_path` and at least one MCP policy tool. "
"Keep the answer concise and business-ready. Mention which policy topic you "
"confirmed through MCP."
),
default_manifest=manifest,
tools=[get_discount_approval_path],
mcp_servers=[server],
capabilities=[WorkspaceShellCapability()],
)
result = await Runner.run(
agent,
question,
run_config=RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())),
)
tool_names: list[str] = []
for item in result.new_items:
if getattr(item, "type", None) != "tool_call_item":
continue
name = tool_call_name(item.raw_item)
if name:
tool_names.append(name)
if tool_names:
print(f"[tools used] {', '.join(tool_names)}")
print(result.final_output)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--model", default="gpt-5.6-sol", help="Model name to use.")
parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.")
args = parser.parse_args()
asyncio.run(main(args.model, args.question))