1from openai import OpenAI
2from dataclasses import dataclass, field
3from typing import Callable
4import json
5
6client = OpenAI()
7
8@dataclass
9class Agent:
10 name: str
11 instructions: str
12 tools: list[dict] = field(default_factory=list)
13 handoffs: list[str] = field(default_factory=list)
14
15AGENTS: dict[str, Agent] = {
16 "triage": Agent(
17 name="triage",
18 instructions="Route requests to the right specialist. Handoff to 'researcher' for factual questions, 'coder' for code tasks.",
19 handoffs=["researcher", "coder"],
20 ),
21 "researcher": Agent(
22 name="researcher",
23 instructions="You research factual questions thoroughly. Provide sources. Handoff back to 'triage' when done.",
24 tools=[{"type": "function", "function": {"name": "web_search", "description": "Search the web", "parameters": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}}}],
25 handoffs=["triage"],
26 ),
27 "coder": Agent(
28 name="coder",
29 instructions="Write clean, tested code. Add docstrings. Handoff back to 'triage' when done.",
30 handoffs=["triage"],
31 ),
32}
33
34def build_handoff_tool(target: str) -> dict:
35 return {"type": "function", "function": {"name": f"handoff_to_{target}", "description": f"Transfer conversation to {target} agent", "parameters": {"type": "object", "properties": {}, "required": []}}}
36
37def run_orchestration(task: str, start_agent: str = "triage") -> str:
38 current_agent = start_agent
39 messages = [{"role": "user", "content": task}]
40 for _ in range(10): # max iterations
41 agent = AGENTS[current_agent]
42 all_tools = agent.tools + [build_handoff_tool(h) for h in agent.handoffs]
43 response = client.chat.completions.create(
44 model="gpt-4o", tools=all_tools or None, tool_choice="auto",
45 messages=[{"role": "system", "content": agent.instructions}] + messages,
46 )
47 msg = response.choices[0].message
48 if msg.tool_calls:
49 for tc in msg.tool_calls:
50 if tc.function.name.startswith("handoff_to_"):
51 current_agent = tc.function.name.replace("handoff_to_", "")
52 break
53 else:
54 return msg.content
55 return "Max iterations reached"