Tracks
当我通过 API 调用尝试一个新模型时,第一次响应往往信息有限。我的第一次 Fable 5.1 运行返回了一个有效结构和一个通用计划。我想知道,当对话变长之后会发生什么:应用能否保持完整的历史、在不越界的前提下检查文件、报告进度,以及展示成本来源?
我们的 Claude Fable 5.1 概览涵盖了发布、基准测试和更广泛的模型对比。本文将从一个小型的 Python 调用开始,并围绕它构建代理循环。最终的代理会接收功能需求、读取一个 Flask 项目,并返回与其实际检查过的文件相对应的计划。
我们将介绍如何:
- 发起 Claude Fable 5.1 API 调用并安全读取内容块
- 设置推理强度,并在对话中途更改(测试版)
- 将系统指令限定到单回合(测试版)
- 用 Pydantic 返回结构化计划
- 添加具有项目根边界的只读仓库工具
- 运行多回合工具循环
- 在工具调用之间读取代理的进度更新(测试版)
- 用仅追加的历史保持思考块有效
- 缓存重复上下文并按公布费率估算请求成本
- 处理拒绝并通过 FastAPI 暴露代理
这些测试版功能使用带日期的头信息,因此在上线前请与 Anthropic 文档核对。
在代理循环中运行 Claude Fable 5.1 要花多少钱?
代理在每一回合都会重新发送相同的系统提示、工具定义和仓库上下文,因此决定账单的费率是缓存读取费率,而不是输入费率。
Fable 5.1 的价格为每百万输入 token 收费 $10、每百万输出 token 收费 $50,和 Fable 5 相同。缓存读取每百万 $0.25,低于之前的 $1;五分钟缓存写入仍为每百万 $12.50。我们的 Claude Fable 5.1 指南提供完整费率表以及 Anthropic 自身的节省估算。
读取已缓存的前缀很便宜。写入并不便宜,价格是读取的 50 倍,所以只有当某个前缀被多次回读时,循环才会划算。后文的成本拆解展示了实际运行中的落点,以及实际占比最高的类别。
上限来自模型,而非预算。Fable 5.1 提供 100 万 token 的上下文窗口,每次响应最多 128K 输出 token,并且 max_tokens是对思考与响应文本合计的硬性限制。在高强度下,二者都需要空间,这也是下面的代理循环设置为 16,000 而不是更整洁数值的原因。
数据保留、优先级层级和水印
在写代码前有几个访问细节很重要。其中两项会直接让您的请求失败:
-
Fable 5.1 要求 30 天数据保留,在零数据保留设置下不可用,除非 Anthropic 授权访问。来自不兼容工作区的请求会返回 400
invalid_request_error,且没有其他提示。 -
该模型不支持优先级层级(Priority Tier)。Fable 5 支持,所以这会坑到迁移的用户。
-
Fable 5.1 的文本输出带有 Anthropic 的文本水印。它不增加 token,也不需要更改请求。
通过 API 使用 Claude Fable 5.1 构建具备仓库感知能力的开发者代理
工作流分为两个阶段:
- 受限检查循环读取允许的项目文件。
- 使用结构化输出的最终请求将该上下文转化为一个计划。
示例项目是一个用于保存和搜索书签的小型 Flask JSON API,包含应用工厂、三个蓝图、一个配置模块、模型以及一个 pytest 测试套件。我选择限流作为持续的任务,因为代理需要检查应用初始化、路由、配置和测试,才能识别所需的文件和测试。完整代码与示例项目在 GitHub 仓库中提供。

请求仅能通过一个边界访问文件。作者供图。
代理只能使用三个工具: list_project_files、read_project_file 和 get_project_metadata。Claude 从不直接访问文件系统。它提出一个路径,由您的代码决定该路径是否允许。
在 Python 中设置 Claude Fable 5.1 API
从独立的 Python 环境开始,并将 API 密钥保存在服务器上。
先决条件
您需要 Python 3.10 或更高版本,以及具有 claude-fable-5-1 访问权限的 Anthropic API 密钥。
要创建 API 密钥,请登录 Claude 控制台,打开API 密钥页面,点击 Create key,然后复制该密钥。最佳实践是为其命名以便记忆用途、选择到期日期并安全存放。
安装 SDK 并添加 API 密钥
创建虚拟环境并安装依赖:
python -m venv .venv
source .venv/bin/activate # macOS or Linux
.venv\Scripts\Activate.ps1 # Windows PowerShell
pip install anthropic==1.3.0 pydantic fastapi uvicorn python-dotenv
请固定 SDK 版本,因为测试版功能经常变化。进度更新至少需要 1.1.0,示例使用 1.3.0。
将密钥放入 .env 文件,并在首次提交前把 .env 加入 .gitignore。它应保存在您可控的服务器上,切勿放在浏览器或可访问的仓库中。泄露可能导致未经授权的 API 使用及输入、输出和缓存操作的费用。
ANTHROPIC_API_KEY=sk-ant-your-key-here
设置完成后,客户端会自动找到该密钥。
用 Python 发起您的第一次 Claude Fable 5.1 API 调用
在其上层构建前,先发送尽可能小的API 请求。
发送第一次 API 请求
初始化客户端,发送一条用户消息,并打印响应元数据:
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv()
client = Anthropic()
MODEL = "claude-fable-5-1"
response = client.messages.create(
model=MODEL,
max_tokens=512,
messages=[{"role": "user", "content": "Reply in one sentence to confirm the API connection is working."}],
)
text = next((b.text for b in response.content if b.type == "text"), None)
print(text if text is not None else f"No text returned ({response.stop_reason})")
print(f"Model: {response.model}")
print(f"Stop reason: {response.stop_reason}")
print(f"Input tokens: {response.usage.input_tokens}")
print(f"Output tokens: {response.usage.output_tokens}")
print(f"Request ID: {response._request_id}")

第一次调用返回文本与元数据。作者供图。
next(...) 用于选择第一个文本块。自适应思考始终开启且无法禁用,因此响应可能以思考块开头;发送 thinking: {"type": "disabled"} 会返回 400,而不是将其关闭。当思考块位于首位时,直接访问 response.content[0].text 会抛出异常。
解决方法是按块类型过滤,而非假设固定位置。同时记录 response._request_id,因为 Anthropic 支持会用它来追踪请求。
以下是在规划与强度示例中使用的请求。它要求代理检查多个文件:
feature_request = (
"Add rate limiting to the public API endpoints so one client cannot exhaust "
"the search endpoint or brute force the token endpoint."
)
在比较不同强度和 token 计数时,请保持该文本不变。这样结果才能反映 API 设置差异,而不是提示词变化。
用 output_config 设置推理强度
通过 output_config 设置推理强度。可选值为 low、medium、high、xhigh 和 max。API 默认是 high。
response = client.messages.create(
model=MODEL,
max_tokens=8192,
output_config={"effort": "high"},
messages=[{"role": "user", "content": feature_request}],
)
强度会影响 token 使用、工具行为和延迟。我在四个强度级别下各运行三次相同的功能需求;下表显示平均值:
|
强度 |
秒数 |
思考 token |
输出 token 总数 |
成本 |
|---|---|---|---|---|
|
|
7.7 |
111 |
173 |
$0.0093 |
|
|
8.1 |
129 |
186 |
$0.0099 |
|
|
7.9 |
136 |
199 |
$0.0106 |
|
|
20.0 |
151 |
1,764 |
$0.0888 |
思考 token 已包含在输出 token 总数内,因此不要将两列相加。在这些运行中,low、medium 和 high 在时延和成本上相近。
xhigh 用时增加到两倍半,输出 token 接近九倍,成本约八倍。
结论:从 high 起步,常规步骤降到 medium ,仅在您自己的测试显示有可衡量提升时再提高级别。在 low 强度下,模型可能从记忆作答而不调用检索工具。如果该回合需要新信息,请说明或提高强度。
用系统提示约束代理范围
系统提示定义了代理的行为:
SYSTEM_PROMPT = """You are a senior engineer who turns feature requests into implementation plans for an existing codebase.
Stay inside the requested feature. Do not propose unrelated refactors, dependency upgrades, or style changes.
If a file or dependency you need does not exist, say so plainly instead of inventing it.
Write in plain sentences and do not use em dashes.
Finish with concrete guidance: what changes, where, in what order, what could break, and which tests to add."""
Anthropic 的提示编写指南指出,模型可能会扩大任务或过早结束。该提示要求其保持范围并以具体指导作结。稍后用架构来处理输出格式。
用 Pydantic 返回结构化计划
用 Pydantic 定义计划,以便应用进行验证并传递给其他代码:
from pydantic import BaseModel, Field
class FeaturePlan(BaseModel):
summary: str = Field(description="One or two sentences on what will be built.")
implementation_steps: list[str]
files_to_modify: list[str]
risks: list[str]
tests: list[str]
response = client.messages.parse(
model=MODEL,
max_tokens=8192,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": feature_request}],
output_format=FeaturePlan,
)
if response.stop_reason == "refusal":
category = (
response.stop_details.category
if response.stop_details and response.stop_details.category
else "unspecified"
)
print(f"Declined: {category}")
elif response.parsed_output is None:
print(f"No plan. Stop reason: {response.stop_reason}")
else:
print(response.parsed_output.summary)
messages.parse() 会将 Pydantic 模型转换为 JSON 架构、发送请求、验证回复,并在 parsed_output 上返回一个带类型的对象。结构化输出是普遍可用的功能,因此不涉及测试版头。先检查 stop_reason,因为一旦出现拒绝(后文介绍),将跳过架构并导致无内容可解析。
引言中那个通用结果有一点做得对:它没有提到自己看不到的文件。架构只验证结构,而不会验证事实依据。
Claude Fable 5.1 与 Fable 5:API 迁移变化
在添加工具前,请先考虑强制工具限制、思考块兼容性以及仅追加历史。
-
Fable 5.1 拒绝强制工具选择。下面的工具循环部分展示了错误以及所用的
auto配置。 -
思考块只有单向兼容。Fable 5.1 能读取更早 Claude 模型的块,但更早的模型无法读取它的块。
当路由器或回退将对话切换到旧模型时,API 会在目标模型看到历史之前移除不兼容的块。剩余历史保留,但旧模型需要在没有那些块的情况下进行规划。
编辑更早的回合会使其后的思考块失效。这可能破坏历史裁剪和客户端摘要。
迁移指南涵盖了完整的变更集。
添加只读仓库工具
现在通过只读工具为模型提供仓库上下文。
定义只读工具
工具层包含两部分:用于执行访问规则的 Python 函数,以及供 Claude 调用的架构。
将路径限制在项目根目录内
只读不等于安全。模型可以同样容易地请求 ../../.env 与 config.py,因此应将防护放在代码中,而不是提示词中:
def _resolve(self, relative_path: str) -> Path:
relative = Path(relative_path)
if relative.is_absolute() or relative.drive:
raise ToolError(f"path is outside the project root: {relative_path}")
cursor = self.root
for part in relative.parts:
cursor /= part
if cursor.is_symlink():
raise ToolError(f"symlinks are not followed: {relative_path}")
candidate = (self.root / relative).resolve()
# After resolving "..", the path still has to sit under the allowed root.
if candidate != self.root and self.root not in candidate.parents:
raise ToolError(f"path is outside the project root: {relative_path}")
if candidate.name in DENY_NAMES:
raise ToolError(f"reading {candidate.name} is not allowed")
return candidate
拒绝绝对路径和符号链接组件,然后解析路径并确认其仍位于项目根目录之下。请求 ../.env 会返回 “path is outside the project root.”。返回的工具错误让代理可以继续处理允许的文件。
定义严格的工具架构
读取器类控制 Python 可以打开的内容。Claude 还需要描述三个可请求操作的 JSON 架构:
EMPTY_SCHEMA = {
"type": "object",
"properties": {},
"additionalProperties": False,
}
TOOLS = [
{
"name": "list_project_files",
"description": "List readable text files in the project.",
"input_schema": EMPTY_SCHEMA,
"strict": True,
},
{
"name": "read_project_file",
"description": "Read one text file relative to the project root.",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
"additionalProperties": False,
},
"strict": True,
},
{
"name": "get_project_metadata",
"description": "Read project metadata and dependency manifests.",
"input_schema": EMPTY_SCHEMA,
"strict": True,
},
]
strict 会在模型选择工具时检查参数。它不会强制触发工具调用,这对 Fable 5.1 很重要。
运行多回合工具循环
从基础循环开始:发送工具,检查 stop_reason,执行所请求操作,附加结果并重复。
MAX_AGENT_TURNS = 8
reader = ProjectReader("sample_project")
messages = [{"role": "user", "content": feature_request}]
for turn in range(1, MAX_AGENT_TURNS + 1):
response = client.messages.create(
model=MODEL,
max_tokens=16000,
system=SYSTEM_PROMPT,
tools=TOOLS,
messages=messages,
)
if response.stop_reason == "refusal":
return declined(response.stop_details.category)
if response.stop_reason == "max_tokens":
return cutoff()
if response.stop_reason != "tool_use":
messages.append({"role": "assistant", "content": response.content})
break
messages.append({"role": "assistant", "content": response.content})
results = []
for block in response.content:
if block.type != "tool_use":
continue
output, is_error = reader.run(block.name, block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
"is_error": is_error,
})
messages.append({"role": "user", "content": results})
else:
return turn_limit()
MAX_AGENT_TURNS 限制的是模型请求次数,而非花费,所以如有需要请另行实施成本上限。循环直接处理 refusal、max_tokens 和 tool_use ;其他停止原因将结束检查阶段。is_error 字段会告知模型某个路径被拒绝,以便其选择其他操作。
为何强制工具选择会返回 400
在 Fable 5 上,您可以用 tool_choice: {"type": "any"} 强制第一次调用。Fable 5.1 会在请求执行前返回如下错误:
tool_choice: type "tool" and "any" are not supported for this model.
强制调用会跳过始终开启的思考。保持 tool_choice 为 auto,使用上面定义的严格架构,并在提示中点名需要某一步使用的工具。
Fable 5.1 有时每回合只发起一次工具调用,而 Fable 5 会批量几个调用。这会增加往返。可在提示中添加这句:“在同一回合内请求彼此独立的文件,而不是每回合只请求一个”。一次样例运行批量请求了 9 个独立文件,但数量会变化。
流式传输 Claude Fable 5.1 的响应与进度更新
文本流式传输会按生成进度输出内容;进度更新则覆盖工具调用之间的停顿。
流式传输文本响应
完整项目使用 context_system() 在开始流式传输前将 SYSTEM_PROMPT 与项目摘要结合:
with client.messages.stream(
model=MODEL,
max_tokens=8192,
system=context_system(),
messages=[{"role": "user", "content": feature_request}],
) as stream:
for chunk in stream.text_stream:
print(chunk, end="", flush=True)
final = stream.get_final_message()
print(f"\nOutput tokens: {final.usage.output_tokens}")
get_final_message() 会在流结束后返回带用量与停止原因的组装消息。流式分片不保证包含完整 JSON,因此在解析前请等待最终消息。
展示工具调用之间的进度
文本流不会覆盖工具调用期间的延迟。Fable 5.1 能在工具调用前写入简短的进度更新。在默认的 thinking.display 为 "omitted" 时,特定于进度的思考块为空,尽管模型可能仍会输出正常文本作为引言。
使用 display: "updates" 以及 thinking-display-updates-2026-08-18 测试版头时,API 文档将可读进度更新定义为一个非空的 thinking 块,同时隐藏推理。在本项目的实际运行中,thinking 字段保持为空,而可读状态作为紧接 tool_use 之前的普通 text 块出现。因此辅助函数会同时检查两种块类型,且循环仅在以 tool_use 结束的回合调用它:
PROGRESS_BETA = "thinking-display-updates-2026-08-18"
response = client.beta.messages.create(
model=MODEL,
max_tokens=16000,
betas=[PROGRESS_BETA],
thinking={"type": "adaptive", "display": "updates"},
system=SYSTEM_PROMPT,
tools=TOOLS,
messages=messages,
)
def status_lines(response) -> list[str]:
lines = []
for block in response.content:
if block.type == "thinking":
text = (block.thinking or "").strip()
elif block.type == "text":
text = (block.text or "").strip()
else:
continue
if text:
lines.append(text)
return lines
进度消息会描述模型计划读取的文件:“我将读取应用接线、配置、扩展、公共和认证路由,以及现有测试,因为限流会在这些位置挂接。”展示这些消息并忽略空块。

代理在报告进度的同时读取文件。作者供图。
Fable 5.1 写入的此类消息比 Fable 5 少,尤其在更高强度下。如果您的界面需要定期更新,请要求提供开场句、进度消息和收尾回顾。
在对话中途更改 Claude Fable 5.1 的强度
下一个功能非常实用。众所周知,仓库代理并不需要在每个回合都使用相同的推理深度。
在回合之间更改强度
在代理循环中,常规检索回合降低强度,最终规划回合再提高。
使用 mid-conversation-output-config-2026-07-01 测试版头,您可以追加仅更改强度级别的系统消息:
EFFORT_BETA = "mid-conversation-output-config-2026-07-01"
messages.append({"role": "system", "content": [], "output_config": {"effort": "low"}})
messages.append({"role": "user", "content": "Summarize the repository evidence in five words."})
response = client.beta.messages.create(
model=MODEL,
max_tokens=4096,
betas=[EFFORT_BETA],
output_config={"effort": "high"},
messages=messages,
)
新级别从下一次用户回合起生效,而非在当前回合中途生效,且不会使提示缓存失效。跨请求更改顶层 output_config.effort 会使其失效。
代理保持顶层设置为 high,在常规检索前追加每条消息的 medium 指令,并在最终计划前追加 high 指令。一组配对测试在较低强度下使用了 18 个输出 token,而之前设置为 76。请将该结果视为示例,而非预期降幅。
将系统指令应用到单回合
使用回合作用域的指令,在最终规划期间阻止额外的文件读取。
在带有 mid-conversation-system-clear-at-2026-08-21 测试版头的系统消息上设置 clear_at: "next_user_message"。API 会将其文本作为当前回合的系统指令处理,并在下一条用户消息后停止渲染。它仍保留在 messages 中,因此更早的历史不会改变、缓存仍可匹配,且被清除的消息不产生输入 token 成本。
SCOPED_SYSTEM_BETA = "mid-conversation-system-clear-at-2026-08-21"
messages.append({"role": "system", "content": [], "output_config": {"effort": "high"}})
messages.append({"role": "user", "content": "Write the implementation plan now."})
messages.append({
"role": "system",
"content": (
"For this turn only: do not request more files. Base the plan on what "
"you have already read, and name only paths you actually opened."
),
"clear_at": "next_user_message",
})
response = client.beta.messages.create(
model=MODEL,
max_tokens=16000,
betas=[EFFORT_BETA, SCOPED_SYSTEM_BETA],
tool_choice={"type": "none"},
output_config={"format": {"type": "json_schema", "schema": plan_schema()}},
system=agent_system(),
tools=TOOLS,
messages=messages,
)
tool_choice={"type": "none"} 会阻止最终请求再调用工具。作用域指令将计划限定为代理已检查过的文件。不要添加提醒并在下一次请求中删除它。这样的编辑会使后续思考块失效。
修复 Claude Fable 5.1 思考块 400 错误
出现 The block is bound to a different conversation 错误意味着思考块之前的历史发生了变化。每个 Fable 5.1 思考块都与其之前的系统提示、工具定义和消息精确绑定。
结果取决于您的账户创建时间。
-
2026 年 8 月 31 日或之后创建的账户会收到 400,提示该块绑定到不同的对话。
-
更早创建的账户,API 会记录不匹配,但仅当请求设置了
thinking.block_binding.prefix_mismatch_behavior时才会生效。
您可以通过 thinking-binding-controls-2026-08-01 测试版头、将 thinking.block_binding.prefix_mismatch_behavior 设为 "drop_block",并检查 input_transformations 数组来检测。被编辑的历史会显示为 reason: "prefix_binding_mismatch"。请在集成上至少运行一次该检查。
以下操作会触发不匹配:
-
在保留后续回合的情况下编辑、重排或移除较早回合
-
向较早回合注入每次请求的文本并在下一次请求中移除
-
在对话中途更改顶层
system提示或tools数组的内容或顺序 -
在后续请求中从图像或文档 URL 提供不同字节内容
每种情况都有保持绑定完整的替代方案:
-
用对话中途的系统消息添加指令,而非编辑
system。 -
用对话中途的工具变更来更改工具,而不是修改顶层数组。
-
用服务端上下文编辑或压缩裁剪历史,这些不算编辑。
-
不做改动地传回思考块。
移动 cache_control 标记和更改请求级别的强度都是安全的,不会使思考块绑定失效。但是,更改顶层强度会重启提示缓存,所以当希望保留缓存前缀时请使用每条消息的强度设置。
提示缓存与 Claude Fable 5.1 API 成本
以下运行将新鲜输入、缓存写入、缓存读取和输出成本分开统计。
添加自动提示缓存
提示缓存可降低跨回合重复上下文的成本。历史不断增长会改变断点位置,因此此处自动缓存更适用。
顶层 cache_control 字段会在每次请求中将断点移动到最新的可缓存块:
response = client.beta.messages.create(
model=MODEL,
cache_control={"type": "ephemeral"},
system=system,
tools=TOOLS,
messages=messages,
# Other request fields...
)
在 Fable 5.1 上,短于 512 token 的可缓存前缀即便标注了 cache_control 也不会被缓存。API 会正常处理并在两个缓存计数器上返回零。写入一个 583 token 的前缀成本为 $0.0073;在下一回合读取成本为 $0.00015。第二回合仍需将其新增部分写入缓存,所以命中缓存并不会消除所有输入成本。
估算考虑缓存的 API 成本
response.usage 会分别报告新鲜输入、缓存创建、缓存读取和输出。请分别为这四个计数定价;仅汇总输入与输出会隐藏缓存写入成本,并高估缓存命中的价格。
以下是一趟完整运行的成本拆解,它在三回合中读取了 12 个文件并生成了最终计划:
|
项目 |
Token |
估算成本 |
占比 |
|---|---|---|---|
|
输出 |
5,713 |
$0.2857 |
59.4% |
|
缓存写入 |
15,426 |
$0.1928 |
40.1% |
|
新鲜输入 |
50 |
$0.0005 |
0.1% |
|
缓存读取 |
6,549 |
$0.0016 |
0.3% |
|
合计 |
27,738 |
$0.4806 |
100% |
缓存读取在此次估算中占比不到半个百分点。按 Fable 5 的旧费率,此次运行约为 $0.4855 而非 $0.4806。当每回合复用的上下文更多时,节省会更明显。
在这次运行中,输出约占 60%,缓存写入约占 40%。在这里使用的五分钟费率下,一个缓存写入 token 的成本是缓存读取 token 的 50 倍。一个小时的缓存写入成本是 80 倍。
处理 Claude Fable 5.1 的拒绝与回退
拒绝与请求失败需要不同的应用行为。
在解析输出前检测拒绝
在输出返回前发生的拒绝会以 HTTP 200 返回,包含 stop_reason: "refusal"、空内容和 stop_details。其类别可能为 null。流中的后期拒绝可能发生在部分输出之后,应用应丢弃这些输出。围绕调用使用 try/except 并不能捕获这两种情况。
response = client.messages.create(model=MODEL, max_tokens=8192, messages=messages)
if response.stop_reason == "refusal":
category = (
response.stop_details.category
if response.stop_details and response.stop_details.category
else "unspecified"
)
return f"This request was declined ({category})."
将其作为应用状态来处理。若允许的请求不清晰,请改写得更明确。不要构建旨在绕过分类器的重试逻辑。

拒绝以 HTTP 200 返回。作者供图。
配置服务端回退
服务端回退可以在其他模型上重试被拒请求,使用 fallbacks: "default" 并配合 server-side-fallback-2026-07-01 测试版头。Fable 5.1 允许的目标是 Opus 4.8 和 Opus 5。
默认回退仅在拒绝类别有推荐目标时才会运行。一次经测试的 reasoning_extraction 拒绝并未触发回退;请检查 usage.iterations,而不要假设所有拒绝都会重试。如前所述,切换到旧模型也会丢弃 Fable 5.1 的思考块。
用 FastAPI 提供 Claude Fable 5.1 代理服务
本地代理现在可以通过 HTTP API 提供同样的工作流。
创建计划端点
如果您只需要本地脚本,可跳过本节。对于 Web 服务,请使用 FastAPI 并配合 AsyncAnthropic。在生命周期处理器中为进程创建一个客户端。从现有代理模块中导入架构和提示。
@asynccontextmanager
async def lifespan(_: FastAPI):
global client
client = AsyncAnthropic()
try:
yield
finally:
await client.close()
@app.post("/plan", response_model=PlanResponse)
async def create_plan(body: PlanRequest):
reader = resolve_project(body.project)
messages, totals, turns, tool_calls = await inspect(reader, body.feature_request)
plan, final_usage = await write_plan(messages)
totals.add(final_usage)
return PlanResponse(plan=plan, turns=turns, tool_calls=tool_calls, usage=as_usage(totals))
请注意,调用方发送的是项目名称而非路径。resolve_project() 会将其映射到少量允许的根目录之一,因此请求无法要求服务器读取任意位置。此服务将拒绝映射为 422,这是应用层的选择。Claude API 本身会以 HTTP 200 返回。
用 uvicorn app:app --reload 运行。交互式文档位于 http://localhost:8000/docs。
端点返回带有估算成本的计划。作者视频。
/plan/stream 端点在后台任务中运行检查,将进度和工具事件放入 asyncio.Queue,并通过 StreamingResponse 发送。当流关闭时,生成器会取消后台任务。仓库中的 Streamlit 界面也会渲染同一事件流。
Streamlit 展示代理的实时进度。作者视频。
Claude Fable 5.1 代理部署清单
此前构建的限制与检查仍是服务的一部分。部署前请添加本地运行中不可见的运维要素。
-
审查 SDK 针对 429 与 5xx 响应的默认两次重试,然后将
max_retries和超时设为符合服务延迟预算 -
设置请求超时,并确认现有 SSE 任务取消会在客户端断开时停止未完成工作
-
为每次运行记录模型 ID、SDK 版本、请求 ID、停止原因及四类 token
-
对缓存写入、输出 token、拒绝和达到回合上限的运行设置告警
-
确认账户的数据保留设置与模型要求一致
-
固定 SDK 版本,并在每次发布前重新检查测试版头
何时使用 Claude Fable 5.1 而不是 Opus 5 或 Sonnet 5
- Anthropic 建议以 Opus 5 作为合理默认。
- 当 Opus 5 在长仓库分析、困难调试或具有大型上下文的代理型任务上表现不足时,测试 Fable 5.1。
- 对于仓库工作与日常任务,请在质量、延迟和成本上比较 Sonnet 5 和 Opus 5。
- 对于分类、抽取、简短回答和更简单的请求,Sonnet 5 是不错的默认选择;对于最简单的任务,Haiku 4.5 可能也足够强大。
不要仅因为它是新版本就选择 Fable 5.1。单次请求同样可以使用强度和结构化输出;流式传输也可用。它并不受益于此处使用的循环或重复前缀缓存。
总结
我第一次调用得到的通用计划,只有在代理读取仓库之后才变得有用。在完整运行中,它在三回合中检查了 12 个文件,而输出与缓存写入合计占估算成本的 99.5%。我会保留路径边界和仅追加历史,然后测试较低强度是否能在不让模型跳过仓库工具的前提下降低成本。
如果一次响应就能完成任务,请止步于结构化输出。当答案必须依赖仓库文件,或需要在调用之间汇报进度时,再使用工具循环。
关于模型选择的更多细节,建议学习我们的 Introduction to Claude Models 课程。关于提示与代理工作流,请参阅我们的 Software Development with Cursor 课程。
FAQs
Claude Fable 5.1 能阅读图像和代码吗?
可以。它支持图像输入,并能读取图表和 PDF。我没有在主示例中使用视觉能力,因为仓库计划不需要它。如果要扩展该代理以规划 UI 变更,我会将当前截图与功能需求一并发送。如果细小的视觉细节不影响任务,可先下采样。
为何从 Fable 5 切换后我的代理变慢了?
在指责模型之前先检查工具结果。如果此前的批量请求指令已存在,请对比它们的数量和大小。当前读取器将每个文件限制为 40,000 字节。如果仍然过大,请添加行范围或搜索参数,让工具仅返回相关片段。
为何 Claude Fable 5.1 返回 400 invalid_request_error?
不要先重试。invalid_request_error 通常指向需要更改的请求结构或账户设置。在本项目中,可能的原因包括强制 tool_choice、不兼容的数据保留设置、在保留思考的前提下编辑了前缀,或发送了没有配套头信息的测试版字段。修复所述原因后再发送请求。
我应该缓存源文件还是摘要?
我的做法是:当后续多个回合都需要精确代码时缓存源文件。如果后续步骤只需要架构或文件地图,就缓存摘要。摘要成本更低,但可能遗漏最终计划所需的一行。
Batch API 能运行这个代理吗?
本身不行。Batch API 提交的是独立的 Messages 请求;它不会运行此客户端工具循环。我会将其用于无需实时进度的自包含仓库审查。要批量运行完整循环,需要您自己的代码在提交下一批前处理上一批的工具请求。