Claude Managed Agents 提示词怎么版本管理与回滚:评估 v1、上线 v2、检出回归并固定会话版本
【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks
如果你的产品用 LLM 做客服工单分派,提示词通常躺在代码库里:改一行提示词要提 PR、跑 CI、走部署,回滚也要重来一遍。Claude Managed Agents(本仓库claude-cookbooks的managed_agents/目录收录了相关 cookbook)把提示词放在服务端:每次agents.update都会生成一个不可变的新版本,会话通过sessions.create的agent参数按 ID 加版本号来选择用哪个版本。这样"上线"只是改一个配置里的版本号,"回滚"就是把调用方指回旧版本,不需要重新部署。
这篇文章按 CMA_prompt_versioning_and_rollback.ipynb 的顺序,用一个工单分诊 agent 走完整条路径:创建 v1、对带标签的测试集打分、上线 v2、按团队准确率比较检出回归、再把会话固定回 v1 完成回滚。
准备条件
Notebook 列出的前置要求:
- Python 3.11+
anthropic>=0.91.0- 一个 Anthropic API key(设置
ANTHROPIC_API_KEY环境变量,或放进.env文件) - 测试数据 fixture:support_tickets.jsonl,共 20 条工单、每个团队 5 条,每条已带正确的
team标签,就是后面打分的基准
Notebook 的 Setup 单元格安装依赖(脚本形式等价如下):
pip install "anthropic>=0.91.0" python-dotenv初始化客户端的单元格:
import json import os import time from collections import defaultdict from pathlib import Path from anthropic import Anthropic from dotenv import load_dotenv load_dotenv() if not os.getenv("ANTHROPIC_API_KEY"): raise RuntimeError("Set ANTHROPIC_API_KEY in your environment or .env file") MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6") client = Anthropic()模型名默认claude-sonnet-4-6,可用环境变量COOKBOOK_MODEL覆盖;Notebook 中该变量名为MODEL。
创建环境和 v1 agent
先创建 Environment(agent 运行所用的容器模板),再创建 agent 本身。v1 的 system prompt 很短:把每张工单分类到团队和优先级,只回一行原始 JSON。
env = client.beta.environments.create(name="ticket-triage-env") ENV_ID = env.id V1_SYSTEM = """You are a support-ticket triage agent for a usage-billed API product. Read the ticket and respond with ONLY a single line of raw JSON (no code fences, no prose): {"team": "<billing|auth|api-platform|dashboard>", "priority": "<P1|P2|P3>"} Route based on the customer's actual problem, not surface keywords.""" agent = client.beta.agents.create(name="ticket-triage", model=MODEL, system=V1_SYSTEM) AGENT_ID = agent.id print(f"env={ENV_ID} agent={AGENT_ID} v{agent.version}")运行后(文档示例):
env=env_017FKqTdU6dFgEe6zbbVQvE5 agent=agent_011CZo6t9Cr6Eg5TeFFPWPpi v1agents.create没有显式要求版本,但返回体里带version: 1。之后每次更新都会得到同一个id下的新版本号,这个号就是后面固定会话和回滚时用的东西。
加载带标签的测试集
fixture = Path("example_data/prompt_versioning_and_rollback/support_tickets.jsonl") tickets = [json.loads(line) for line in fixture.read_text().splitlines()] teams = sorted({t["team"] for t in tickets}) print(f"{len(tickets)} tickets across {len(teams)} teams: {teams}")路径相对于managed_agents/目录(在仓库中的完整路径是managed_agents/example_data/prompt_versioning_and_rollback/support_tickets.jsonl)。文档示例输出:
20 tickets across 4 teams: ['api-platform', 'auth', 'billing', 'dashboard']评估 v1:用固定版本的会话跑分
评估的核心是一个triage辅助函数:开一个固定到指定版本的会话,发一张工单,轮询events.list直到会话进入 idle,再从agent.message事件里解析出 JSON 结论。对版本管理来说,关键是sessions.create的agent参数:
- 传字符串
agent=AGENT_ID:拿到的是最新版本; - 传
{"type": "agent", "id": ..., "version": ...}:固定到精确版本,受控对比需要的是后者。
def triage(version: int, ticket: dict) -> dict: """Run one ticket through a pinned agent version and return its verdict.""" session = client.beta.sessions.create( agent={"type": "agent", "id": AGENT_ID, "version": version}, environment_id=ENV_ID, ) try: prompt = "Subject: " + ticket["subject"] + "\n\n" + ticket["body"] client.beta.sessions.events.send( session.id, events=[{"type": "user.message", "content": [{"type": "text", "text": prompt}]}], ) deadline = time.time() + 60 while time.time() < deadline: events = client.beta.sessions.events.list(session.id).data if events and events[-1].type == "session.status_idle": break time.sleep(1) else: raise TimeoutError(f"session {session.id} did not idle within 60s") agent_events = [e for e in events if e.type == "agent.message"] reply = "".join(b.text for e in agent_events for b in e.content) return json.loads(reply) finally: try: client.beta.sessions.archive(session.id) except Exception: # noqa: S110 pass def score(version: int) -> dict: """Evaluate all tickets against the given version and return per-team accuracy.""" hits = defaultdict(lambda: [0, 0]) for t in tickets: pred = triage(version, t) hits[t["team"]][1] += 1 if pred.get("team") == t["team"]: hits[t["team"]][0] += 1 return dict(hits) v1_scores = score(version=1) print("v1 results:") for team, (correct, total) in sorted(v1_scores.items()): print(f" {team:14s} {correct}/{total}")实现细节:会话 60 秒内没有进入session.status_idle就抛TimeoutError;每张工单跑完后在finally里sessions.archive清理会话,避免测试会话堆积。
v1 的文档示例输出:
v1 results: api-platform 5/5 auth 5/5 billing 4/5 dashboard 5/5上线 v2:agents.update生成新版本
PM 要改的路由规则:凡是提到 API 用量或限流的工单都归平台团队。
V2_SYSTEM = V1_SYSTEM + ( "\n\nROUTING RULE: If the ticket text mentions API usage, rate limits, quotas, " "or request volume, route to api-platform. Apply this rule before any other " "consideration; do not second-guess it based on the rest of the ticket." ) agent = client.beta.agents.update(AGENT_ID, version=agent.version, system=V2_SYSTEM) print(f"agent {AGENT_ID} now at v{agent.version}")文档示例输出:
agent agent_011CZo6t9Cr6Eg5TeFFPWPpi now at v2Notebook 在这里专门讨论了评审环节去哪了:agents.update没有内置审批流,workspace 里任何 key 都能调用它;如果调用方传的是裸 agent ID 而不是固定版本,它们下一次会话就会直接用上 v2 的提示词。这是免部署换来的代价,和你用 API 管理配置而不是代码时面临的取舍一样。Notebook 给出的做法是:生产调用方永远固定到显式版本,被固定的那个版本号才是受变更控制的对象。任何人可以创建 v2、v3、v10,这些版本挂在服务端但没有流量;"上线"指更新告诉生产调用方该传哪个版本的配置,而这个更新走正常评审流程。创建版本保持廉价,SDLC 不变,且变更只动配置值、所有 runner 自动生效。
复检 v2 并检出回归
同一套评估,固定到 v2,然后逐团队比较两个版本的命中数,v2 低于 v1 的团队打上回归标记:
v2_scores = score(version=agent.version) for team in sorted(v1_scores): c1, n1 = v1_scores[team] c2, n2 = v2_scores[team] flag = " <-- regressed" if c2 < c1 else "" print(team) print(f" v1: {c1}/{n1}") print(f" v2: {c2}/{n2}{flag}")文档示例输出:
api-platform v1: 5/5 v2: 5/5 auth v1: 5/5 v2: 5/5 billing v1: 4/5 v2: 2/5 <-- regressed dashboard v1: 5/5 v2: 5/5回归出现在 billing:新规则写得宽,而按用量计费的 API 产品里,billing 工单本身就在谈 API usage,规则把这些工单也划去了 api-platform。检出回归的判断就是这段比较代码本身:按团队对比 v1/v2 的命中数,c2 < c1即标记regressed。
回滚:把会话固定回 v1
v1 还挂在服务端,回滚不需要部署,调用方改回传version: 1即可。Notebook 的验证方式是把 5 条 billing 工单重新用 v1 跑一遍:
billing = [t for t in tickets if t["team"] == "billing"] rerun = [triage(version=1, ticket=t).get("team") for t in billing] print(f"billing tickets via version=1: {rerun.count('billing')}/{len(billing)} correct")文档示例输出:
billing tickets via version=1: 4/5 correct回滚完成后,v2 依然留在服务端。PM 可以继续在上面迭代,或把一小部分流量路由到 v2 当金丝雀;修好后创建 v3,再走一遍评估和上线流程。
清理
实验结束后归档本次创建的 agent 和 environment(注意这会归档你本流程创建的资源):
client.beta.agents.archive(AGENT_ID) client.beta.environments.archive(ENV_ID) print("archived")可以带进工作流的版本管理约定
Notebook 的 Recap 总结了三点,都直接服务于上面的操作路径:
- 生产调用方固定到显式版本,不传裸 agent ID。新版本在你显式上线之前对生产不可见。
- 把被固定的版本号当作改提示词的闸门:创建版本是探索性的,更新生产固定的版本才是走评审的那一步。
- 对更重要的 agent,先把一小部分流量路由到新版本做对比(本文的跑分即此模式),再全量上线。这个版本机制可以直接当 feature flag 用。
下一步查版本:client.beta.agents.versions.list(AGENT_ID)可以返回一个 agent 的所有版本。managed_agents/目录下的其他 notebook 覆盖会话、自定义工具和端到端模式,README 中注明CMA_iterate_fix_failing_tests.ipynb介绍了其余 notebook 用到的 agent / environment / session 全部 API 形态,可作为延伸阅读。
【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考