-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupgrade_to_multiagent.py
More file actions
124 lines (100 loc) · 4.53 KB
/
upgrade_to_multiagent.py
File metadata and controls
124 lines (100 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""One-shot: roll out the multi-agent topology to the live CMA agents.
Phase 2 of the upgrade plan. After this runs:
- 5 sub-agents (governance / product / tech support / sales /
escalation) exist with focused per-mode system prompts and the
tool subsets they need.
- The coordinator agent (named `boardbreeze-concierge`, the one that
has been serving SMS sessions all along) has its system prompt
replaced with the routing prompt and its `multiagent` config
attached referencing the 5 sub-agent IDs.
- Existing `phone_sessions` rows continue to work — the coordinator
keeps its agent_id.
What this script does NOT do:
- Touch the voice path. Voice (`app/voice_pipeline.py`) keeps using
the merged single-agent `agent_spec.SYSTEM_PROMPT`. Phase 3
(voice ↔ CMA reunify) is what brings voice onto the new topology.
- Migrate sessions or re-introduce callers. Sessions resume against
the same agent_id with the new spec on the next event.
Workflow:
1. ensure_agents() finds-or-creates all 6 agents (idempotent).
2. For each sub-agent: agents.update(...) with the focused spec, in
case the agent already existed with a stale prompt/tools.
3. For the coordinator: agents.update(...) with the routing system
prompt, empty tools list, AND multiagent config referencing the
5 sub-agent IDs.
Run:
.venv/bin/python -m scripts.upgrade_to_multiagent
Safe to run multiple times — every step is idempotent on agent name +
spec content.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
load_dotenv(Path(__file__).resolve().parent.parent / ".env")
import anthropic # noqa: E402
from app.managed_agents import agents # noqa: E402
from app.managed_agents.client import ensure_agents # noqa: E402
def _tool_names(tools) -> list[str]:
out: list[str] = []
for t in tools or []:
if isinstance(t, dict):
out.append(t.get("name", "?"))
else:
out.append(getattr(t, "name", "?"))
return out
def _refresh_subagent(c: anthropic.Anthropic, mod, agent_id: str) -> None:
"""Update the sub-agent's spec to whatever its module currently
defines. Bumps version on the server. Existing sessions keep
running their pinned version per CMA semantics."""
current = c.beta.agents.retrieve(agent_id)
print(f" - {mod.AGENT_NAME} (id={agent_id}, v={current.version})")
print(f" before tools: {_tool_names(current.tools)}")
kwargs = mod.agent_create_kwargs()
updated = c.beta.agents.update(
agent_id=agent_id,
version=current.version,
system=kwargs["system"],
tools=kwargs["tools"],
model=kwargs["model"],
)
print(f" after tools: {_tool_names(updated.tools)} (v={updated.version})")
def _attach_coordinator(c: anthropic.Anthropic, agent_id: str, roster: list[str]) -> None:
"""Update the coordinator's spec AND multiagent config in one call."""
current = c.beta.agents.retrieve(agent_id)
coord_kwargs = agents.coordinator.agent_create_kwargs()
multiagent = agents.coordinator.multiagent_config(roster)
print(
f" - {agents.coordinator.AGENT_NAME} (id={agent_id}, v={current.version})"
)
print(f" roster: {roster}")
updated = c.beta.agents.update(
agent_id=agent_id,
version=current.version,
system=coord_kwargs["system"],
tools=coord_kwargs["tools"], # empty list — coordinator routes, doesn't tool
model=coord_kwargs["model"],
multiagent=multiagent,
)
print(
f" attached multiagent.type={updated.multiagent.type if updated.multiagent else None} "
f"(v={updated.version})"
)
def main() -> int:
c = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
print("Step 1: ensure all 6 agents exist…")
name_to_id = ensure_agents()
for name, aid in name_to_id.items():
print(f" {name} → {aid}")
print("\nStep 2: refresh each sub-agent spec…")
for mod in agents.SUBAGENT_MODULES:
_refresh_subagent(c, mod, name_to_id[mod.AGENT_NAME])
print("\nStep 3: attach multiagent roster to the coordinator…")
roster = [name_to_id[mod.AGENT_NAME] for mod in agents.SUBAGENT_MODULES]
_attach_coordinator(c, name_to_id[agents.coordinator.AGENT_NAME], roster)
print("\nDone. Existing CMA sessions preserved (coordinator agent_id unchanged).")
print("Voice path is unaffected — it still uses the merged single-agent prompt.")
return 0
if __name__ == "__main__":
sys.exit(main())