chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:39:17 +08:00
commit 4ed4e9ff99
1368 changed files with 334957 additions and 0 deletions
@@ -0,0 +1,71 @@
# Healthcare support
This example shows how to build a healthcare support workflow with Agents SDK using both standard agents and a sandbox agent. The scenario is intentionally synthetic and generic: a patient asks a billing or coverage question, the workflow checks local records, inspects policy documents in an isolated sandbox workspace, writes support artifacts, and optionally routes one ambiguous case to a human reviewer.
## What this example demonstrates
- **Standard agent orchestration** with a top-level support orchestrator and a benefits subagent.
- **Sandbox agents** with a mounted workspace, shell commands, a generated output folder, and runtime-selected sandbox config.
- **Sandbox capabilities** including `Shell`, `Filesystem`, and lazy-loaded `Skills`.
- **Human-in-the-loop approvals** using an approval-gated queue-routing tool.
- **Persistent memory** with `SQLiteSession`, shared across scenario runs.
- **Structured outputs** for each specialist agent and the final case resolution.
- **Tracing** so you can inspect every model call and tool call in the OpenAI trace viewer.
- **CLI-first workflow** that can be run scenario by scenario from the repository checkout.
## Architecture
The workflow has two execution modes working together:
1. A **standard orchestrator agent** runs in the normal Agents SDK loop, calls the benefits subagent first, then calls a sandbox agent tool, and decides whether to request a human handoff.
2. A **sandbox policy agent** runs behind `agents.sandbox`, reads the mounted case files and policy documents, uses shell commands plus a lazily loaded skill, writes markdown artifacts into `output/`, and returns a structured policy summary.
The local fixture data lives in `data/scenarios/*.json` and `data/fixtures/*.json`. The sandbox policy library lives in `policies/*.md`. Generated artifacts are copied to `.cache/healthcare_support/output/<scenario_id>/`.
## Scenarios
The built-in scenarios increase in complexity:
- `eligibility_verification_basic` checks a straightforward benefits question.
- `referral_status_check` adds a referral lookup.
- `blue_cross_pt_benefits` shows a follow-up turn that benefits from the shared SQLite memory.
- `prior_auth_confusion_ct` focuses on prior-authorization and intake-routing confusion.
- `billing_coverage_clarification` combines benefits lookup with sandbox policy search and document generation.
- `messy_ambiguous_knee_case` triggers the human approval flow before queueing a handoff.
## Run the CLI demo
From the repository root:
```bash
uv run python examples/sandbox/healthcare_support/main.py
```
Useful options:
```bash
uv run python examples/sandbox/healthcare_support/main.py --list-scenarios
uv run python examples/sandbox/healthcare_support/main.py --scenario blue_cross_pt_benefits
uv run python examples/sandbox/healthcare_support/main.py --scenario messy_ambiguous_knee_case
uv run python examples/sandbox/healthcare_support/main.py --reset-memory
```
For unattended runs, set `EXAMPLES_INTERACTIVE_MODE=auto` to auto-answer prompts:
```bash
EXAMPLES_INTERACTIVE_MODE=auto uv run python examples/sandbox/healthcare_support/main.py --scenario messy_ambiguous_knee_case
```
## Files to read first
- [`main.py`](./main.py) runs the standalone CLI demo.
- [`workflow.py`](./workflow.py) contains the shared workflow execution logic, sandbox setup, artifact copying, tracing, and approval resume loop.
- [`support_agents.py`](./support_agents.py) defines the orchestrator, benefits subagent, sandbox policy agent, and memory recap agent.
- [`tools.py`](./tools.py) defines the local lookup tools and the approval-gated human handoff tool.
- [`skills/prior-auth-packet-builder/SKILL.md`](./skills/prior-auth-packet-builder/SKILL.md) is the sandbox skill loaded at runtime.
## Notes
- This is a demo workflow, not a production healthcare system.
- All patient, payer, and policy data in this example is synthetic.
- The example loads environment defaults from the repository-root `.env` file and from this demo's optional local `.env` file.
@@ -0,0 +1 @@
"""Synthetic healthcare support sandbox example."""
+197
View File
@@ -0,0 +1,197 @@
from __future__ import annotations
import json
import os
import re
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any
from examples.sandbox.healthcare_support.models import KnowledgeSnippet, ScenarioCase
EXAMPLE_ROOT = Path(__file__).resolve().parent
SCENARIOS_DIR = EXAMPLE_ROOT / "data" / "scenarios"
FIXTURES_DIR = EXAMPLE_ROOT / "data" / "fixtures"
POLICIES_DIR = EXAMPLE_ROOT / "policies"
ROOT_ENV_PATH = EXAMPLE_ROOT.parents[2] / ".env"
DEMO_ENV_PATH = EXAMPLE_ROOT / ".env"
def load_root_env() -> None:
"""Load environment defaults from the repository root and this demo folder."""
for env_path in (ROOT_ENV_PATH, DEMO_ENV_PATH):
if not env_path.exists():
continue
for line in env_path.read_text(encoding="utf-8").splitlines():
stripped = line.strip()
if not stripped or stripped.startswith("#") or "=" not in stripped:
continue
key, value = stripped.split("=", 1)
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
def normalize_text(value: str) -> str:
return " ".join(re.findall(r"[a-z0-9]+", value.lower()))
def tokenize(value: str) -> set[str]:
return set(re.findall(r"[a-z0-9]+", value.lower()))
def normalize_date(value: str | None) -> str:
if not value:
return ""
for fmt in ("%Y-%m-%d", "%m/%d/%Y", "%Y/%m/%d", "%m-%d-%Y"):
try:
return datetime.strptime(value, fmt).strftime("%Y-%m-%d")
except ValueError:
continue
return "".join(re.findall(r"\d+", value))
@dataclass
class PolicyDocument:
document_id: str
title: str
text: str
@dataclass
class HealthcareSupportDataStore:
scenarios: dict[str, ScenarioCase]
patient_records: list[dict[str, Any]]
eligibility_records: list[dict[str, Any]]
referral_records: list[dict[str, Any]]
policy_documents: list[PolicyDocument]
@classmethod
def load(cls) -> HealthcareSupportDataStore:
scenarios = {
path.stem: ScenarioCase.model_validate(json.loads(path.read_text(encoding="utf-8")))
for path in sorted(SCENARIOS_DIR.glob("*.json"))
}
patient_records = json.loads(
(FIXTURES_DIR / "patient_profiles.json").read_text(encoding="utf-8")
)["records"]
eligibility_records = json.loads(
(FIXTURES_DIR / "insurance_eligibility.json").read_text(encoding="utf-8")
)["records"]
referral_records = json.loads(
(FIXTURES_DIR / "referral_status.json").read_text(encoding="utf-8")
)["records"]
policy_documents = [
PolicyDocument(
document_id=path.stem,
title=path.stem.replace("_", " ").title(),
text=path.read_text(encoding="utf-8"),
)
for path in sorted(POLICIES_DIR.glob("*.md"))
]
return cls(
scenarios=scenarios,
patient_records=patient_records,
eligibility_records=eligibility_records,
referral_records=referral_records,
policy_documents=policy_documents,
)
def list_scenario_ids(self) -> list[str]:
return sorted(self.scenarios)
def get_scenario(self, scenario_id: str) -> ScenarioCase:
try:
return self.scenarios[scenario_id]
except KeyError as exc:
raise KeyError(f"Unknown scenario_id: {scenario_id}") from exc
def search_policies(self, query: str, top_k: int = 4) -> list[KnowledgeSnippet]:
query_terms = tokenize(query)
if not query_terms:
return []
scored: list[KnowledgeSnippet] = []
for document in self.policy_documents:
matched_terms = sorted(query_terms & tokenize(document.text))
if not matched_terms:
continue
score = round(len(matched_terms) / max(len(query_terms), 1), 4)
snippet = " ".join(document.text.split())[:320]
scored.append(
KnowledgeSnippet(
document_id=document.document_id,
title=document.title,
chunk_id=f"{document.document_id}:0",
score=score,
snippet=snippet,
matched_terms=matched_terms,
)
)
scored.sort(key=lambda item: item.score, reverse=True)
return scored[:top_k]
def lookup_patient(
self,
*,
patient_id: str | None = None,
phone: str | None = None,
name: str | None = None,
) -> dict[str, Any]:
for record in self.patient_records:
if patient_id and record.get("patient_id") == patient_id:
return {"lookup_status": "matched", "record": record}
if phone and record.get("phone") == phone:
return {"lookup_status": "matched", "record": record}
if name and normalize_text(record.get("name", "")) == normalize_text(name):
return {"lookup_status": "matched", "record": record}
return {"lookup_status": "not_found", "record": None}
def lookup_eligibility(
self,
*,
payer: str | None = None,
member_id: str | None = None,
dob: str | None = None,
) -> dict[str, Any]:
payer_norm = normalize_text(payer or "")
dob_norm = normalize_date(dob)
fallback_match: dict[str, Any] | None = None
for record in self.eligibility_records:
if member_id and record.get("member_id") != member_id:
continue
if dob_norm and normalize_date(record.get("dob")) != dob_norm:
continue
if payer_norm:
if normalize_text(record.get("payer", "")) == payer_norm:
return {"lookup_status": "matched", **record}
continue
if fallback_match is None:
fallback_match = {"lookup_status": "matched", **record}
if fallback_match is not None:
return fallback_match
return {
"lookup_status": "not_found",
"eligibility_status": "unknown",
"notes": "No eligibility match. Ask for payer, member ID, and date of birth.",
}
def lookup_referral(
self,
*,
referral_id: str | None = None,
patient_id: str | None = None,
) -> dict[str, Any]:
for record in self.referral_records:
if referral_id and record.get("referral_id") == referral_id:
return {"lookup_status": "matched", **record}
if patient_id and record.get("patient_id") == patient_id:
return {"lookup_status": "matched", **record}
return {"lookup_status": "not_found", "status": "unknown"}
@@ -0,0 +1,99 @@
{
"records": [
{
"payer": "Blue Cross",
"member_id": "BCX-4439201",
"dob": "1985-02-14",
"plan_name": "Blue Cross PPO Silver 4500",
"eligibility_status": "active",
"copay_primary_care": "$35",
"copay_specialist": "$60",
"deductible_remaining": "$1,200",
"prior_auth_required_services": [
"mri",
"ct angiogram",
"elective surgery"
],
"notes": "Coverage active. MRI requires prior authorization except emergency use."
},
{
"payer": "UnitedHealthcare",
"member_id": "UHC-771032",
"dob": "1990-09-03",
"plan_name": "UHC Choice Plus Bronze",
"eligibility_status": "active",
"copay_primary_care": "$30",
"copay_specialist": "$75",
"deductible_remaining": "$2,050",
"prior_auth_required_services": [
"ct angiogram",
"inpatient admission",
"outpatient surgery"
],
"notes": "Prior auth required for CT angiogram unless ordered in emergency setting."
},
{
"payer": "Aetna",
"member_id": "AET-562100",
"dob": "1978-11-20",
"plan_name": "Aetna Open Access Basic",
"eligibility_status": "active",
"copay_primary_care": "$25",
"copay_specialist": "$50",
"deductible_remaining": "$850",
"prior_auth_required_services": [
"specialist consult"
],
"notes": "Referral on file for specialist consult."
},
{
"payer": "Cigna",
"member_id": "CG-291001",
"dob": "1982-06-30",
"plan_name": "Cigna Connect Gold",
"eligibility_status": "active",
"copay_primary_care": "$20",
"copay_specialist": "$45",
"deductible_remaining": "$300",
"prior_auth_required_services": [
"advanced imaging",
"elective procedures"
],
"notes": "Claims for advanced imaging can deny if authorization is missing."
},
{
"payer": "Blue Cross",
"member_id": "BCX-8822009",
"dob": "1974-05-12",
"plan_name": "Blue Cross PPO Platinum",
"eligibility_status": "active",
"copay_primary_care": "$20",
"copay_specialist": "$40",
"deductible_remaining": "$0",
"prior_auth_required_services": [
"physical therapy after 12 visits"
],
"notes": "Physical therapy benefit allows 12 visits without prior authorization per calendar year."
},
{
"payer": "Blue Cross",
"member_id": "BCX-9017710",
"dob": "1992-04-17",
"plan_name": "Blue Cross PPO Silver 3000",
"eligibility_status": "active",
"copay_primary_care": "$30",
"copay_specialist": "$55",
"deductible_remaining": "$1,600",
"prior_auth_required_services": [
"mri",
"knee surgery consult",
"outpatient surgery"
],
"notes": "Prior auth normally required for knee surgery consult and advanced imaging."
}
],
"default_response": {
"eligibility_status": "unknown",
"notes": "No eligibility match. Confirm payer, member ID, and DOB."
}
}
@@ -0,0 +1,58 @@
{
"records": [
{
"patient_id": "PAT-1001",
"name": "Maya Thompson",
"dob": "1985-02-14",
"phone": "555-0111",
"payer": "Blue Cross",
"member_id": "BCX-4439201",
"referral_id": "REF-44120"
},
{
"patient_id": "PAT-1002",
"name": "Victor Chen",
"dob": "1990-09-03",
"phone": "555-0122",
"payer": "UnitedHealthcare",
"member_id": "UHC-771032",
"referral_id": "REF-77100"
},
{
"patient_id": "PAT-1003",
"name": "Nora Patel",
"dob": "1978-11-20",
"phone": "555-0133",
"payer": "Aetna",
"member_id": "AET-562100",
"referral_id": "REF-88421"
},
{
"patient_id": "PAT-1004",
"name": "Luis Romero",
"dob": "1982-06-30",
"phone": "555-0144",
"payer": "Cigna",
"member_id": "CG-291001",
"referral_id": "REF-12880"
},
{
"patient_id": "PAT-1005",
"name": "Ella Brooks",
"dob": "1974-05-12",
"phone": "555-0155",
"payer": "Blue Cross",
"member_id": "BCX-8822009",
"referral_id": "REF-33002"
},
{
"patient_id": "PAT-1006",
"name": "Jordan Lee",
"dob": "1992-04-17",
"phone": "555-0134",
"payer": "Blue Cross",
"member_id": "BCX-9017710",
"referral_id": "REF-90171"
}
]
}
@@ -0,0 +1,34 @@
{
"records": [
{
"referral_id": "REF-88421",
"patient_id": "PAT-1003",
"status": "approved",
"specialty": "Cardiology",
"requested_provider": "Dr. Ramos",
"authorized_visits": 6,
"remaining_visits": 4,
"notes": "Authorization valid through 2026-07-31."
},
{
"referral_id": "REF-77100",
"patient_id": "PAT-1002",
"status": "pending_clinical_review",
"specialty": "Radiology",
"requested_provider": "Riverfront Imaging",
"authorized_visits": 1,
"remaining_visits": 0,
"notes": "Pending prior authorization packet completion."
},
{
"referral_id": "REF-90171",
"patient_id": "PAT-1006",
"status": "pending",
"specialty": "Orthopedics",
"requested_provider": "Summit Ortho Group",
"authorized_visits": 8,
"remaining_visits": 8,
"notes": "Awaiting payer determination."
}
]
}
@@ -0,0 +1,30 @@
{
"scenario_id": "billing_coverage_clarification",
"description": "Patient received an unexpected imaging bill and wants coverage clarification.",
"transcript": "Hey, this is Luis Romero. I got a bill after an ultrasound on 2026-02-08 and I thought it was covered.\nMy insurance is Cigna and my member ID is CG-291001.\nCan someone explain what happened and what I should do now?",
"patient_metadata": {
"patient_id": "PAT-1004"
},
"followup_qa": {
"date of service": "2026-02-08",
"payer": "Cigna"
},
"expected": {
"intent": "billing_coverage_clarification",
"required_entities": {
"payer": "Cigna",
"member_id": "CG-291001"
},
"required_tool_calls": [
"insurance_eligibility_lookup"
],
"required_resolution_elements": [
"billing coverage review",
"recommended next step"
],
"expected_payer": "Cigna"
},
"gold": {
"expected_next_step": "Route to billing review with EOB and service date context."
}
}
@@ -0,0 +1,30 @@
{
"scenario_id": "blue_cross_pt_benefits",
"description": "Blue Cross member asks about remaining physical therapy benefit and coverage path.",
"transcript": "This is Ella Brooks. I am a Blue Cross member and my ID is BCX-8822009.\nI am trying to continue physical therapy and need to know if I still have covered visits left.\nI do not have my date of birth in front of me if you need it.",
"patient_metadata": {
"patient_id": "PAT-1005"
},
"followup_qa": {
"date of birth": "05/12/1974",
"physical therapy": "physical therapy"
},
"expected": {
"intent": "eligibility_verification",
"required_entities": {
"payer": "Blue Cross",
"member_id": "BCX-8822009"
},
"required_tool_calls": [
"insurance_eligibility_lookup"
],
"required_resolution_elements": [
"eligibility verified",
"recommended next step"
],
"expected_payer": "Blue Cross"
},
"gold": {
"expected_next_step": "Confirm PT visit limits and advise on when additional review is needed."
}
}
@@ -0,0 +1,30 @@
{
"scenario_id": "eligibility_verification_basic",
"description": "Basic eligibility verification call with clear Blue Cross identifiers.",
"transcript": "Hi, this is Maya Thompson. I have an MRI next week and I want to confirm if it is covered.\nI have Blue Cross and my member ID is BCX-4439201. My date of birth is 02/14/1985.\nCan you tell me what my benefits look like and what I should do next?",
"patient_metadata": {
"patient_id": "PAT-1001"
},
"followup_qa": {
"member ID": "BCX-4439201",
"date of birth": "02/14/1985"
},
"expected": {
"intent": "eligibility_verification",
"required_entities": {
"payer": "Blue Cross",
"member_id": "BCX-4439201"
},
"required_tool_calls": [
"insurance_eligibility_lookup"
],
"required_resolution_elements": [
"eligibility verified",
"recommended next step"
],
"expected_payer": "Blue Cross"
},
"gold": {
"expected_next_step": "Confirm prior auth requirement for MRI and proceed with scheduling."
}
}
@@ -0,0 +1,34 @@
{
"scenario_id": "messy_ambiguous_knee_case",
"description": "Messy real-world call with ambiguous details requiring follow-up, retrieval, and multiple tool invocations.",
"transcript": "Hi, this is Jordan Lee. I had a knee surgery consult and maybe some imaging planned, then I got mixed messages about auth.\nI also saw a bill and I am not sure if this is Blue something PPO or what.\nMy phone is 555-0134 and I think the referral might be REF-90171.\nCan you figure out what I need to do next?",
"patient_metadata": {
"patient_id": "PAT-1006"
},
"followup_qa": {
"insurance payer": "Blue Cross",
"member ID": "BCX-9017710",
"date of birth": "04/17/1992",
"procedure or visit type": "knee surgery consult",
"referral ID": "REF-90171"
},
"expected": {
"intent": "prior_auth_confusion",
"required_entities": {
"payer": "Blue Cross",
"member_id": "BCX-9017710"
},
"required_tool_calls": [
"insurance_eligibility_lookup",
"appointment_referral_status_lookup"
],
"required_resolution_elements": [
"prior authorization",
"recommended next step"
],
"expected_payer": "Blue Cross"
},
"gold": {
"expected_next_step": "Route to auth queue and share referral pending status with patient."
}
}
@@ -0,0 +1,32 @@
{
"scenario_id": "prior_auth_confusion_ct",
"description": "Caller is confused about whether CT angiogram needs prior auth and what intake should do.",
"transcript": "This is Victor Chen. I was told to schedule a CT angiogram, but another office said prior authorization is missing.\nMy insurance is UnitedHealthcare and I think my ID is UHC-771032.\nI need to know if I can move forward or if you need more information.",
"patient_metadata": {
"patient_id": "PAT-1002"
},
"followup_qa": {
"date of birth": "09/03/1990",
"procedure or visit type": "CT angiogram",
"payer": "UnitedHealthcare",
"member ID": "UHC-771032"
},
"expected": {
"intent": "prior_auth_confusion",
"required_entities": {
"payer": "UnitedHealthcare",
"member_id": "UHC-771032"
},
"required_tool_calls": [
"insurance_eligibility_lookup"
],
"required_resolution_elements": [
"prior authorization",
"recommended next step"
],
"expected_payer": "UnitedHealthcare"
},
"gold": {
"expected_next_step": "Route to utilization review with CT angiogram authorization packet."
}
}
@@ -0,0 +1,29 @@
{
"scenario_id": "referral_status_check",
"description": "Patient asks for specialist referral status with known referral ID.",
"transcript": "Hi, this is Nora Patel. I am checking on referral number REF-88421 for cardiology with Dr. Ramos.\nCan you tell me if it has been approved and how many visits I still have?",
"patient_metadata": {
"patient_id": "PAT-1003"
},
"followup_qa": {
"referral number": "REF-88421",
"provider": "Dr. Ramos"
},
"expected": {
"intent": "referral_status_question",
"required_entities": {
"referral_id": "REF-88421"
},
"required_tool_calls": [
"appointment_referral_status_lookup"
],
"required_resolution_elements": [
"referral",
"remaining authorized visits"
],
"expected_payer": "Aetna"
},
"gold": {
"expected_next_step": "Notify patient referral is approved and proceed to specialist scheduling."
}
}
+152
View File
@@ -0,0 +1,152 @@
from __future__ import annotations
import argparse
import asyncio
import json
import sys
from pathlib import Path
from typing import Any
if __package__ is None or __package__ == "":
_DEMO_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(_DEMO_DIR.parents[2]))
sys.path.insert(0, str(_DEMO_DIR))
from examples.auto_mode import confirm_with_fallback, input_with_fallback # noqa: E402
from examples.sandbox.healthcare_support.data import ( # noqa: E402
HealthcareSupportDataStore,
load_root_env,
)
from examples.sandbox.healthcare_support.models import ScenarioCase # noqa: E402
from examples.sandbox.healthcare_support.tools import HealthcareSupportContext # noqa: E402
from examples.sandbox.healthcare_support.workflow import ( # noqa: E402
CACHE_ROOT,
DEFAULT_SESSION_ID,
SESSION_DB_PATH,
build_context,
run_healthcare_support_workflow,
)
DEFAULT_SCENARIO_ID = "eligibility_verification_basic"
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Run the healthcare support Agents SDK demo from the command line.",
)
parser.add_argument(
"--scenario",
dest="scenario_id",
default=None,
help="Scenario ID to run. If omitted, the CLI asks interactively.",
)
parser.add_argument(
"--list-scenarios",
action="store_true",
help="Print the built-in scenario IDs and exit.",
)
parser.add_argument(
"--reset-memory",
action="store_true",
help="Delete the shared SQLite session database before running.",
)
return parser
def _print_scenarios(store: HealthcareSupportDataStore) -> None:
print("Available scenarios:\n")
for scenario_id in store.list_scenario_ids():
scenario = store.get_scenario(scenario_id)
print(f"- {scenario.scenario_id}")
print(f" {scenario.description}")
def _pick_scenario(store: HealthcareSupportDataStore, requested_id: str | None) -> ScenarioCase:
if requested_id:
return store.get_scenario(requested_id)
scenario_id = input_with_fallback(
"Enter a scenario ID: ",
DEFAULT_SCENARIO_ID,
).strip()
if not scenario_id:
scenario_id = DEFAULT_SCENARIO_ID
return store.get_scenario(scenario_id)
async def _approval_handler(request: dict[str, Any]) -> bool:
print("\nHuman approval requested")
print(f"Agent: {request.get('agent', 'unknown')}")
print(f"Tool: {request.get('tool', 'route_to_human_queue')}")
print(json.dumps(request.get("arguments", {}), indent=2))
return confirm_with_fallback("Approve handoff to a human queue? [y/N]: ", True)
def _print_run_header(*, scenario: ScenarioCase, context: HealthcareSupportContext) -> None:
print("\n" + "=" * 80)
print("Healthcare Support Agents SDK Demo")
print(f"Scenario: {scenario.scenario_id}")
print(f"Description: {scenario.description}")
print(f"SQLite memory session: {context.session_id}")
print("\nCustomer transcript:\n")
print(scenario.transcript)
def _print_run_result(payload: dict[str, Any]) -> None:
print("\nTrace URL:")
print(payload["trace_url"])
print("\nPatient-facing response:\n")
print(payload["resolution"]["patient_facing_response"])
print("\nInternal summary:")
print(payload["resolution"]["internal_summary"])
print("\nNext step:")
print(payload["resolution"]["next_step"])
if payload["resolution"].get("handoff_id"):
print("\nHuman handoff:")
print(payload["resolution"]["handoff_id"])
print("\nGenerated sandbox artifacts:")
for artifact in payload.get("artifacts", []):
print(f"- {artifact['path']}")
print("\nMemory recap:")
print(json.dumps(payload["memory_recap"], indent=2))
print(f"\nSession memory items: {payload['session_memory_items']}")
async def main() -> None:
load_root_env()
args = _build_parser().parse_args()
store = HealthcareSupportDataStore.load()
if args.list_scenarios:
_print_scenarios(store)
return
if args.reset_memory and SESSION_DB_PATH.exists():
SESSION_DB_PATH.unlink()
scenario = _pick_scenario(store, args.scenario_id)
context = build_context(
store=store,
scenario_id=scenario.scenario_id,
session_id=DEFAULT_SESSION_ID,
)
CACHE_ROOT.mkdir(parents=True, exist_ok=True)
_print_run_header(scenario=scenario, context=context)
payload = await run_healthcare_support_workflow(
context=context,
scenario_id=scenario.scenario_id,
approval_handler=_approval_handler,
)
_print_run_result(payload)
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,83 @@
from __future__ import annotations
from typing import Any, Literal
from pydantic import BaseModel, Field
IntentName = Literal[
"eligibility_verification",
"prior_auth_confusion",
"referral_status_question",
"billing_coverage_clarification",
"general_intake",
]
class ScenarioExpectation(BaseModel):
intent: IntentName
required_entities: dict[str, str] = Field(default_factory=dict)
required_tool_calls: list[str] = Field(default_factory=list)
required_resolution_elements: list[str] = Field(default_factory=list)
expected_payer: str | None = None
class ScenarioCase(BaseModel):
scenario_id: str
description: str
transcript: str
patient_metadata: dict[str, Any] = Field(default_factory=dict)
followup_qa: dict[str, str] = Field(default_factory=dict)
expected: ScenarioExpectation
gold: dict[str, Any] = Field(default_factory=dict)
class KnowledgeSnippet(BaseModel):
document_id: str
title: str
chunk_id: str
score: float
snippet: str
matched_terms: list[str] = Field(default_factory=list)
class BenefitReview(BaseModel):
patient_name: str
patient_id: str
payer: str
member_id: str
eligibility_status: str
plan_summary: str
referral_status: str
prior_auth_recommended: bool
recommended_queue: str
summary: str
class SandboxPolicyPacket(BaseModel):
matched_policy_files: list[str] = Field(default_factory=list)
generated_files: list[str] = Field(default_factory=list)
shell_commands: list[str] = Field(default_factory=list)
policy_summary: str
human_review_recommended: bool
class CaseResolution(BaseModel):
scenario_id: str
intent: IntentName
patient_name: str
benefits_summary: str
policy_summary: str
next_step: str
route_to_human: bool
handoff_id: str | None = None
generated_files: list[str] = Field(default_factory=list)
internal_summary: str
patient_facing_response: str
class MemoryRecap(BaseModel):
remembered_patient: str | None = None
remembered_intent: IntentName | None = None
remembered_next_step: str
remembered_handoff: str | None = None
remembered_files: list[str] = Field(default_factory=list)
@@ -0,0 +1,6 @@
# Auth Review Queue Routing
- Route to auth-review-queue when prior authorization is required, likely required, or blocked by missing CPT/diagnosis details.
- Route to care-team-intake-queue when referral or scheduling data is incomplete but payer auth is not yet indicated.
- Route to billing-review-queue only for claim denial, refund, or balance disputes.
- High-priority auth review applies when surgery or advanced imaging is expected within 14 days.
@@ -0,0 +1,6 @@
# Billing After Consult FAQ
- A consult bill can be generated before imaging or surgery authorization is complete.
- Patients often confuse referral approval, prior authorization, and claim adjudication.
- Staff should explain that consult billing does not confirm surgery authorization.
- If the patient reports a bill plus auth confusion, verify eligibility and route to billing only when the question is about claim denial or patient balance.
@@ -0,0 +1,6 @@
# Blue Cross Benefits Reference
- Common PPO orthopedic specialist copays range from $40 to $75 depending on employer group.
- Deductible and coinsurance still apply to imaging and outpatient surgery.
- Benefit verification should capture specialist copay, deductible remaining, and coinsurance.
- Benefits data should be summarized separately from authorization status.
@@ -0,0 +1,7 @@
# Blue Cross PPO Prior Authorization
- PPO members require prior authorization for inpatient surgery, outpatient surgery over $1,500, and advanced imaging tied to surgical planning.
- Knee surgery consults do not require prior authorization by themselves.
- MRI or CT imaging ordered after the consult may require prior authorization if performed at a hospital outpatient department.
- If referral status is pending, route to auth review before scheduling imaging.
- Required fields: member ID, date of birth, ordering provider, CPT code, diagnosis code.
@@ -0,0 +1,6 @@
# Blue Cross Referral Rules
- PPO plans do not usually require a PCP referral for orthopedic consults.
- Some employer groups still require a referral number for specialist scheduling.
- If a referral exists but is pending, staff should verify status before confirming downstream imaging or surgery appointments.
- Pending referrals should be routed to the care-team intake queue or auth-review queue depending on whether authorization is also required.
@@ -0,0 +1,6 @@
# Commercial Eligibility Checklist
- Verify payer name, member ID, date of birth, and plan status.
- Confirm effective date, termination date, copay, deductible, and coinsurance.
- If payer name is ambiguous, use member ID and DOB to identify the most likely eligibility match.
- Eligibility verification does not replace prior authorization review.
@@ -0,0 +1,5 @@
# Human Escalation Policy
- Escalate to a human when payer is ambiguous, prior authorization is likely, referral is pending, or procedure coding is incomplete.
- Escalate when patient asks for next steps and multiple operational dependencies are unresolved.
- Human queue payloads should include patient summary, payer, member ID, referral ID, requested service, and missing information.
@@ -0,0 +1,5 @@
# Knee Surgery Medical Necessity
- Surgical review packets should include consult notes, imaging results, diagnosis, failed conservative treatment, and requested CPT code.
- Missing imaging results are a common reason for delayed authorization.
- If the patient has a consult but no final procedure code, route to human review for packet completion before payer submission.
@@ -0,0 +1,6 @@
# Orthopedic Imaging Policy
- X-ray does not require prior authorization for most commercial plans.
- MRI of knee without contrast often requires prior authorization when ordered before surgery.
- CT lower extremity may require prior authorization when tied to operative planning.
- Imaging requests should include laterality, diagnosis code, and conservative treatment history when available.
@@ -0,0 +1,5 @@
# Outbound Fax Packet Requirements
- Prior auth packets should include cover sheet, demographics, insurance card data, consult notes, imaging reports, and requested CPT/ICD-10 codes.
- If any required artifact is missing, create a missing-items checklist before faxing.
- Human review is required before outbound fax when packet data is incomplete or referral status is pending.
@@ -0,0 +1,6 @@
# Patient Messaging Guidelines
- Use plain language and separate what is verified from what is still under review.
- Do not tell a patient that surgery is approved unless payer authorization is confirmed.
- If referral is pending, say that the referral is still being reviewed and that the care team is checking whether payer authorization is also needed.
- Provide one clear next step and one expected owner queue.
@@ -0,0 +1,6 @@
# Referral Pending SOP
- Confirm referral ID, patient identity, and rendering specialist before escalation.
- If referral status is pending for more than two business days, send to care-team intake queue.
- If referral is pending and prior authorization is also likely, send to auth-review queue with a note that referral clearance is still outstanding.
- Patient messaging should distinguish referral review from payer authorization.
@@ -0,0 +1,5 @@
# Scheduling Hold Policy
- Do not schedule surgery until required payer authorization is approved.
- Imaging may be tentatively scheduled only when policy allows no-auth outpatient imaging.
- If referral or authorization is pending, place a scheduling hold and notify the patient of the review owner.
@@ -0,0 +1,31 @@
---
name: prior-auth-packet-builder
description: Build a concise prior authorization packet from local case files and payer policy docs.
---
# Prior Auth Packet Builder
Use this skill when a case requires prior authorization review, referral validation, imaging review, or payer-specific policy checks.
## Workflow
1. Inspect `case/scenario.json` and `case/transcript.txt`.
2. Use `rg` against `policies/` to find payer, prior auth, referral, imaging, and PPO guidance.
3. Read only the most relevant policy files.
4. Create `output/policy_findings.md` with:
- case summary
- matched policy files
- prior auth determination
- referral determination
- missing information
5. Create `output/human_review_checklist.md` with:
- what a human reviewer should verify
- what to tell the patient
- what queue should own the case
## Rules
- Use targeted `rg` searches over broad file reads.
- Only cite policy files you actually inspected.
- Keep outputs concise and operational.
- If referral status is pending and prior auth is unclear, recommend human review.
@@ -0,0 +1,162 @@
from __future__ import annotations
from pathlib import Path
from openai.types.shared import Reasoning
from agents import Agent, AgentOutputSchema, ModelSettings, Tool
from agents.sandbox import SandboxAgent
from agents.sandbox.capabilities import Filesystem, LocalDirLazySkillSource, Shell, Skills
from agents.sandbox.entries import LocalDir
from examples.sandbox.healthcare_support.models import (
BenefitReview,
CaseResolution,
MemoryRecap,
SandboxPolicyPacket,
)
from examples.sandbox.healthcare_support.tools import (
HealthcareSupportContext,
lookup_insurance_eligibility,
lookup_patient,
lookup_referral_status,
route_to_human_queue,
)
BENEFITS_PROMPT = """
You are a healthcare benefits specialist in a synthetic support workflow.
Use the available lookup tools to verify patient, eligibility, and referral details, then return a
structured benefits review.
Rules:
1. Call `patient_info_lookup` first when you have a patient ID, phone number, or patient name.
2. Call `insurance_eligibility_lookup` when payer, member ID, or date of birth is available.
3. Call `appointment_referral_status_lookup` when referral ID or patient ID is available.
4. Recommend prior-auth review only when the case involves imaging, surgery, a pending referral, or
policy-specific authorization language.
5. Set `recommended_queue` to one of `care-team-intake-queue`, `auth-review-queue`, or
`billing-review-queue`.
6. Keep the summary concise and grounded in tool output.
""".strip()
POLICY_SANDBOX_PROMPT = """
You are a policy packet specialist running inside a sandbox workspace.
Inspect the case files and local policy library, generate concise markdown artifacts in `output/`,
and return a structured packet summary.
You must:
1. Load and use the `prior-auth-packet-builder` skill.
2. Inspect the workspace with shell commands before writing anything.
3. Use `rg` against `policies/` for prior-auth, imaging, referral, billing, PPO, and Blue Cross
policy guidance.
4. Create `output/policy_findings.md` with the most relevant policy guidance.
5. Create `output/human_review_checklist.md` with a short checklist for a human reviewer.
6. Set `human_review_recommended=true` only when the policy search or case input shows missing
authorization/referral details that should be reviewed by a human before responding.
7. Include the exact shell commands you ran in `shell_commands`.
8. Return only facts grounded in the files you inspected.
""".strip()
ORCHESTRATOR_PROMPT = """
You are a healthcare support orchestrator.
Coordinate a synthetic support case by combining a benefits review, a sandbox policy packet review,
and a human handoff only when the case genuinely needs it.
Rules:
1. Always call `benefits_review` first.
2. Always call `sandbox_policy_packet` second.
3. For this demo, call `route_to_human_queue` only for the
`messy_ambiguous_knee_case` scenario when the sandbox packet recommends human review.
4. Do not escalate the other four scenarios; answer those directly from the benefits and sandbox
outputs.
5. If you call `route_to_human_queue`, include the returned `handoff_id` and set
`route_to_human=true`.
6. Produce a clear patient-facing response, a short internal summary, and a concrete next step.
7. Use only facts from the tool outputs and the supplied scenario payload.
""".strip()
MEMORY_PROMPT = """
Summarize what you remember from this SQLite-backed session about the prior patient support cases.
Include the most recently remembered patient, intent, handoff status, generated files, and next
step. Do not call tools.
""".strip()
benefits_agent = Agent[HealthcareSupportContext](
name="HealthcareBenefitsAgent",
model="gpt-5.6-sol",
instructions=BENEFITS_PROMPT,
model_settings=ModelSettings(reasoning=Reasoning(effort="low"), verbosity="low"),
tools=[
lookup_patient,
lookup_insurance_eligibility,
lookup_referral_status,
],
output_type=AgentOutputSchema(BenefitReview, strict_json_schema=False),
)
def build_policy_sandbox_agent(*, skills_root: Path) -> SandboxAgent[HealthcareSupportContext]:
return SandboxAgent[HealthcareSupportContext](
name="HealthcarePolicySandboxAgent",
model="gpt-5.6-sol",
instructions=(
POLICY_SANDBOX_PROMPT + "\n\n"
"Use `load_skill` before reading the skill file. Use `exec_command` with `pwd`, "
"`ls`, `cat`, and `rg` to inspect the sandbox workspace. Use `apply_patch` to create "
"`output/policy_findings.md` and `output/human_review_checklist.md`."
),
capabilities=[
Shell(),
Filesystem(),
Skills(
lazy_from=LocalDirLazySkillSource(
# This is a host path read by the SDK process.
# Requested skills are copied into `skills_path` in the sandbox.
source=LocalDir(src=skills_root),
)
),
],
model_settings=ModelSettings(
reasoning=Reasoning(effort="low"),
verbosity="low",
tool_choice="required",
),
output_type=AgentOutputSchema(SandboxPolicyPacket, strict_json_schema=False),
)
def build_orchestrator(*, sandbox_policy_tool: Tool) -> Agent[HealthcareSupportContext]:
return Agent[HealthcareSupportContext](
name="HealthcareSupportOrchestrator",
model="gpt-5.6-sol",
instructions=ORCHESTRATOR_PROMPT,
model_settings=ModelSettings(
reasoning=Reasoning(effort="low"),
verbosity="low",
),
tools=[
benefits_agent.as_tool(
tool_name="benefits_review",
tool_description="Review patient eligibility, benefits, and referral status.",
),
sandbox_policy_tool,
route_to_human_queue,
],
output_type=AgentOutputSchema(CaseResolution, strict_json_schema=False),
)
memory_recap_agent = Agent[HealthcareSupportContext](
name="HealthcareSupportMemoryAgent",
model="gpt-5.6-sol",
instructions=MEMORY_PROMPT,
model_settings=ModelSettings(reasoning=Reasoning(effort="low"), verbosity="low"),
output_type=AgentOutputSchema(MemoryRecap, strict_json_schema=False),
)
@@ -0,0 +1,112 @@
from __future__ import annotations
import hashlib
import json
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from typing import Any
from agents import RunContextWrapper, function_tool
from examples.sandbox.healthcare_support.data import HealthcareSupportDataStore
from examples.sandbox.healthcare_support.models import ScenarioCase
@dataclass
class HealthcareSupportContext:
store: HealthcareSupportDataStore
scenario: ScenarioCase
session_id: str = ""
human_handoffs: list[dict[str, Any]] = field(default_factory=list)
human_handoff_approved: bool = False
emit_event: Callable[[dict[str, Any]], Awaitable[None]] | None = None
async def emit(self, event_name: str, **payload: Any) -> None:
if self.emit_event is None:
return
await self.emit_event(
{
"type": "workflow_event",
"event": event_name,
**payload,
}
)
@function_tool(name_override="patient_info_lookup")
def lookup_patient(
context: RunContextWrapper[HealthcareSupportContext],
patient_id: str | None = None,
phone: str | None = None,
name: str | None = None,
) -> dict[str, Any]:
"""Look up a synthetic patient profile by patient ID, phone, or name."""
return context.context.store.lookup_patient(
patient_id=patient_id,
phone=phone,
name=name,
)
@function_tool(name_override="insurance_eligibility_lookup")
def lookup_insurance_eligibility(
context: RunContextWrapper[HealthcareSupportContext],
payer: str | None = None,
member_id: str | None = None,
dob: str | None = None,
) -> dict[str, Any]:
"""Look up synthetic insurance eligibility by payer, member ID, and DOB."""
return context.context.store.lookup_eligibility(
payer=payer,
member_id=member_id,
dob=dob,
)
@function_tool(name_override="appointment_referral_status_lookup")
def lookup_referral_status(
context: RunContextWrapper[HealthcareSupportContext],
referral_id: str | None = None,
patient_id: str | None = None,
) -> dict[str, Any]:
"""Look up synthetic referral status by referral ID or patient ID."""
return context.context.store.lookup_referral(
referral_id=referral_id,
patient_id=patient_id,
)
async def _needs_human_approval(
context: RunContextWrapper[HealthcareSupportContext],
_params: dict[str, Any],
_call_id: str,
) -> bool:
return not context.context.human_handoff_approved
@function_tool(name_override="route_to_human_queue", needs_approval=_needs_human_approval)
def route_to_human_queue(
context: RunContextWrapper[HealthcareSupportContext],
queue: str,
priority: str,
reason: str,
summary: str,
) -> dict[str, Any]:
"""Route a synthetic case to a human queue after explicit approval."""
payload = {
"queue": queue,
"priority": priority,
"reason": reason,
"summary": summary,
"scenario_id": context.context.scenario.scenario_id,
}
digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()[:12]
result = {
"status": "queued",
"handoff_id": f"HUMAN-{digest.upper()}",
"queue": queue,
"priority": priority,
"reason": reason,
"summary": summary,
}
context.context.human_handoffs.append({"payload": payload, "result": result})
return result
@@ -0,0 +1,419 @@
from __future__ import annotations
import json
from collections.abc import Awaitable, Callable
from pathlib import Path
from typing import Any, cast
from pydantic import BaseModel
from agents import (
Agent,
AgentHookContext,
RunContextWrapper,
RunHooks,
Runner,
SQLiteSession,
Tool,
gen_trace_id,
trace,
)
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxPathGrant, SandboxRunConfig
from agents.sandbox.entries import Dir, File, LocalDir
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient
from agents.tool_context import ToolContext
from examples.sandbox.healthcare_support.data import HealthcareSupportDataStore
from examples.sandbox.healthcare_support.models import (
CaseResolution,
MemoryRecap,
ScenarioCase,
)
from examples.sandbox.healthcare_support.support_agents import (
build_orchestrator,
build_policy_sandbox_agent,
memory_recap_agent,
)
from examples.sandbox.healthcare_support.tools import HealthcareSupportContext
EXAMPLE_ROOT = Path(__file__).resolve().parent
POLICIES_ROOT = EXAMPLE_ROOT / "policies"
SKILLS_ROOT = EXAMPLE_ROOT / "skills"
SDK_ROOT = EXAMPLE_ROOT.parents[2]
CACHE_ROOT = SDK_ROOT / ".cache" / "healthcare_support"
SESSION_DB_PATH = CACHE_ROOT / "sessions.db"
DEFAULT_SESSION_ID = "healthcare-support-demo-memory"
ApprovalHandler = Callable[[dict[str, Any]], Awaitable[bool]]
class WorkflowHooks(RunHooks[HealthcareSupportContext]):
async def on_agent_start(
self,
context: AgentHookContext[HealthcareSupportContext],
agent: Agent[HealthcareSupportContext],
) -> None:
await context.context.emit("agent_start", agent=agent.name)
async def on_agent_end(
self,
context: RunContextWrapper[HealthcareSupportContext],
agent: Agent[HealthcareSupportContext],
output: Any,
) -> None:
await context.context.emit(
"agent_end",
agent=agent.name,
output=_to_jsonable(output),
)
async def on_tool_start(
self,
context: RunContextWrapper[HealthcareSupportContext],
agent: Agent[HealthcareSupportContext],
tool: Tool,
) -> None:
tool_context = cast(ToolContext[HealthcareSupportContext], context)
await context.context.emit(
"tool_start",
agent=agent.name,
tool=tool.name,
call_id=tool_context.tool_call_id,
arguments=tool_context.tool_arguments,
)
async def on_tool_end(
self,
context: RunContextWrapper[HealthcareSupportContext],
agent: Agent[HealthcareSupportContext],
tool: Tool,
result: object,
) -> None:
tool_context = cast(ToolContext[HealthcareSupportContext], context)
await context.context.emit(
"tool_end",
agent=agent.name,
tool=tool.name,
call_id=tool_context.tool_call_id,
output=_to_jsonable(result),
)
def _to_jsonable(value: Any) -> Any:
if isinstance(value, BaseModel):
return value.model_dump(mode="json")
if isinstance(value, dict | list | str | int | float | bool) or value is None:
return value
try:
return json.loads(json.dumps(value, default=str))
except Exception:
return str(value)
def build_context(
*,
store: HealthcareSupportDataStore,
scenario_id: str = "eligibility_verification_basic",
session_id: str = DEFAULT_SESSION_ID,
emit_event: Callable[[dict[str, Any]], Awaitable[None]] | None = None,
) -> HealthcareSupportContext:
return HealthcareSupportContext(
store=store,
scenario=store.get_scenario(scenario_id),
session_id=session_id,
emit_event=emit_event,
)
def _build_manifest(scenario: ScenarioCase) -> Manifest:
return Manifest(
extra_path_grants=(
SandboxPathGrant(path=str(POLICIES_ROOT), read_only=True),
SandboxPathGrant(path=str(SKILLS_ROOT), read_only=True),
),
entries={
"case": Dir(
children={
"scenario.json": File(
content=json.dumps(scenario.model_dump(mode="json"), indent=2).encode(
"utf-8"
)
),
"transcript.txt": File(content=scenario.transcript.encode("utf-8")),
},
description="Synthetic support request and scenario metadata.",
),
"policies": LocalDir(
src=POLICIES_ROOT,
description="Local healthcare policy and workflow documents.",
),
"output": Dir(description="Generated support artifacts for this case."),
},
)
async def _structured_tool_output_extractor(result: Any) -> str:
final_output = result.final_output
if isinstance(final_output, BaseModel):
return json.dumps(final_output.model_dump(mode="json"), sort_keys=True)
return str(final_output)
def _fallback_artifacts(*, scenario: ScenarioCase, resolution: CaseResolution) -> dict[str, str]:
policy_doc = f"""# Policy Findings
## Case
{scenario.description}
## Policy summary
{resolution.policy_summary}
## Next step
{resolution.next_step}
"""
checklist_doc = f"""# Human Review Checklist
- Confirm whether the request needs prior authorization for this service and payer.
- Verify referral state and any missing clinical or billing identifiers.
- Use this internal summary: {resolution.internal_summary}
- Patient-facing response: {resolution.patient_facing_response}
"""
return {
"policy_findings.md": policy_doc,
"human_review_checklist.md": checklist_doc,
}
async def _copy_output_files(
*,
sandbox: Any,
scenario: ScenarioCase,
resolution: CaseResolution,
) -> list[dict[str, str]]:
scenario_id = scenario.scenario_id
destination_root = CACHE_ROOT / "output" / scenario_id
destination_root.mkdir(parents=True, exist_ok=True)
copied_by_name: dict[str, dict[str, str]] = {}
for entry in await sandbox.ls("output"):
entry_path = Path(entry.path)
if entry.is_dir():
continue
handle = await sandbox.read(entry_path)
try:
payload = handle.read()
finally:
handle.close()
local_path = destination_root / entry_path.name
if isinstance(payload, str):
content = payload
local_path.write_text(content, encoding="utf-8")
else:
content = bytes(payload).decode("utf-8", errors="replace")
local_path.write_text(content, encoding="utf-8")
copied_by_name[entry_path.name] = {
"name": entry_path.name,
"path": str(local_path),
"content": content,
}
for filename, content in _fallback_artifacts(
scenario=scenario,
resolution=resolution,
).items():
if filename in copied_by_name:
continue
local_path = destination_root / filename
local_path.write_text(content, encoding="utf-8")
copied_by_name[filename] = {
"name": filename,
"path": str(local_path),
"content": content,
}
return [copied_by_name[name] for name in sorted(copied_by_name)]
async def _resolve_interruptions(
*,
result: Any,
orchestrator: Agent[HealthcareSupportContext],
context: HealthcareSupportContext,
conversation_session: SQLiteSession,
hooks: WorkflowHooks,
approval_handler: ApprovalHandler | None,
) -> Any:
approval_round = 0
while result.interruptions:
approval_round += 1
if approval_round > 5:
raise RuntimeError("Exceeded 5 approval rounds while resuming the workflow.")
state = result.to_state()
CACHE_ROOT.mkdir(parents=True, exist_ok=True)
state_payload = state.to_json(
context_serializer=lambda value: {
"scenario_id": value.scenario.scenario_id,
"session_id": value.session_id,
"human_handoffs": value.human_handoffs,
}
)
(CACHE_ROOT / "pending_state.json").write_text(
json.dumps(state_payload, indent=2),
encoding="utf-8",
)
for interruption in result.interruptions:
request = {
"agent": interruption.agent.name,
"tool": interruption.name,
"arguments": _to_jsonable(interruption.arguments),
}
await context.emit("human_approval_requested", request=request)
approved = True if approval_handler is None else await approval_handler(request)
if approved:
context.human_handoff_approved = True
state.approve(interruption, always_approve=False)
await context.emit("human_approval_resolved", approved=True, request=request)
else:
context.human_handoff_approved = False
state.reject(interruption)
await context.emit("human_approval_resolved", approved=False, request=request)
result = await Runner.run(
orchestrator,
state,
session=conversation_session,
hooks=hooks,
)
return result
def _workflow_prompt(scenario: ScenarioCase) -> str:
return json.dumps(
{
"scenario_id": scenario.scenario_id,
"description": scenario.description,
"transcript": scenario.transcript,
"patient_metadata": scenario.patient_metadata,
"followup_answers": scenario.followup_qa,
},
indent=2,
)
async def run_healthcare_support_workflow(
*,
context: HealthcareSupportContext,
scenario_id: str,
approval_handler: ApprovalHandler | None = None,
) -> dict[str, Any]:
scenario = context.store.get_scenario(scenario_id)
context.scenario = scenario
context.human_handoffs.clear()
context.human_handoff_approved = False
await context.emit(
"scenario_loaded",
scenario_id=scenario.scenario_id,
description=scenario.description,
transcript=scenario.transcript,
)
CACHE_ROOT.mkdir(parents=True, exist_ok=True)
conversation_session = SQLiteSession(
session_id=context.session_id or DEFAULT_SESSION_ID, db_path=SESSION_DB_PATH
)
await context.emit("memory_ready", session_id=conversation_session.session_id)
hooks = WorkflowHooks()
sandbox_client = UnixLocalSandboxClient()
sandbox = await sandbox_client.create(manifest=_build_manifest(scenario))
await context.emit(
"sandbox_ready",
backend="unix_local",
workspace=["case/scenario.json", "case/transcript.txt", "policies/", "output/"],
)
policy_agent = build_policy_sandbox_agent(skills_root=SKILLS_ROOT)
sandbox_policy_tool = policy_agent.as_tool(
tool_name="sandbox_policy_packet",
tool_description="Inspect policy files in a sandbox and generate support artifacts.",
custom_output_extractor=_structured_tool_output_extractor,
run_config=RunConfig(
sandbox=SandboxRunConfig(session=sandbox),
workflow_name="Healthcare support sandbox packet",
),
hooks=hooks,
max_turns=20,
)
orchestrator = build_orchestrator(sandbox_policy_tool=sandbox_policy_tool)
trace_id = gen_trace_id()
trace_url = f"https://platform.openai.com/traces/trace?trace_id={trace_id}"
try:
async with sandbox:
await context.emit("trace_ready", trace_id=trace_id, trace_url=trace_url)
with trace(
"Healthcare support workflow",
trace_id=trace_id,
group_id=scenario.scenario_id,
):
result = await Runner.run(
orchestrator,
_workflow_prompt(scenario),
context=context,
session=conversation_session,
hooks=hooks,
)
result = await _resolve_interruptions(
result=result,
orchestrator=orchestrator,
context=context,
conversation_session=conversation_session,
hooks=hooks,
approval_handler=approval_handler,
)
resolution = result.final_output_as(CaseResolution)
copied_files = await _copy_output_files(
sandbox=sandbox,
scenario=scenario,
resolution=resolution,
)
await context.emit("artifacts_ready", files=copied_files)
memory_result = await Runner.run(
memory_recap_agent,
(
"Summarize what you remember from the session. Include patient, intent, "
"handoff state, generated files, and next step."
),
context=context,
session=conversation_session,
hooks=hooks,
)
recap = memory_result.final_output_as(MemoryRecap)
history_items = await conversation_session.get_items()
payload = {
"scenario_id": scenario.scenario_id,
"description": scenario.description,
"transcript": scenario.transcript,
"trace_id": trace_id,
"trace_url": trace_url,
"resolution": resolution.model_dump(mode="json"),
"memory_recap": recap.model_dump(mode="json"),
"artifacts": copied_files,
"session_id": conversation_session.session_id,
"session_memory_items": len(history_items),
}
await context.emit("workflow_complete", payload=payload)
return payload
finally:
await sandbox_client.delete(sandbox)
await context.emit("sandbox_stopped", backend="unix_local")