# 任务预算

为 Claude 提供完整代理循环的建议性 token 预算，帮助模型在长时间代理任务中通过任务预算进行自我调节。

---

<Note>
此功能符合[零数据保留 (ZDR)](/docs/en/build-with-claude/api-and-data-retention) 条件。当您的组织有 ZDR 协议时，通过此功能发送的数据在 API 响应返回后不会被存储。
</Note>

任务预算让您可以告诉 Claude 在完整代理循环中有多少 token 可用，包括思考、工具调用、工具结果和输出。模型会看到一个递减的计数器，并使用它来确定工作优先级，在预算消耗完毕时优雅地完成任务。

<Note>
任务预算在 [Claude Opus 4.7](/docs/en/about-claude/models/overview) 上处于公开测试阶段。设置 `task-budgets-2026-03-13` beta 头以选择加入。
</Note>

## 何时使用任务预算

任务预算最适合 Claude 在最终输出等待下一次人类响应之前进行多次工具调用和决策的代理工作流。在以下情况下使用它们：

- 您希望 Claude 在长时间任务中自我调节 token 消耗。
- 您有可预测的每任务成本或延迟上限需要强制执行。
- 您希望模型在接近预算时优雅地完成（总结发现、报告进度），而不是在执行中途被切断。

任务预算与 [effort 参数](/docs/en/build-with-claude/effort)互补：effort 控制 Claude 对每个步骤的推理深度，而任务预算限制 Claude 在整个代理循环中可以执行的总工作量。

## 设置任务预算

将 `task_budget` 添加到 `output_config` 并包含 beta 头：

<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
    --header "x-api-key: $ANTHROPIC_API_KEY" \
    --header "anthropic-version: 2023-06-01" \
    --header "anthropic-beta: task-budgets-2026-03-13" \
    --header "content-type: application/json" \
    --data '{
        "model": "claude-opus-4-7",
        "max_tokens": 128000,
        "messages": [{
            "role": "user",
            "content": "Review the codebase and propose a refactor plan."
        }],
        "output_config": {
            "effort": "high",
            "task_budget": {"type": "tokens", "total": 64000}
        }
    }'
```

```bash CLI
ant beta:messages create --beta task-budgets-2026-03-13 <<'YAML'
model: claude-opus-4-7
max_tokens: 128000
messages:
  - role: user
    content: Review the codebase and propose a refactor plan.
output_config:
  effort: high
  task_budget:
    type: tokens
    total: 64000
YAML
```

```python Python hidelines={1..2}
import anthropic

client = anthropic.Anthropic()

response = client.beta.messages.create(
    model="claude-opus-4-7",
    max_tokens=128000,
    output_config={
        "effort": "high",
        "task_budget": {"type": "tokens", "total": 64000},
    },
    messages=[
        {"role": "user", "content": "Review the codebase and propose a refactor plan."}
    ],
    betas=["task-budgets-2026-03-13"],
)
```

```typescript TypeScript hidelines={1..2}
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const response = await client.beta.messages.create({
  model: "claude-opus-4-7",
  max_tokens: 128000,
  output_config: {
    effort: "high",
    task_budget: { type: "tokens", total: 64000 }
  },
  messages: [{ role: "user", content: "Review the codebase and propose a refactor plan." }],
  betas: ["task-budgets-2026-03-13"]
});
```

```go Go hidelines={1..10,-2..}
package main

import (
	"context"
	"fmt"

	"github.com/anthropics/anthropic-sdk-go"
)

func main() {
	client := anthropic.NewClient()

	response, _ := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
		Model:     "claude-opus-4-7",
		MaxTokens: 128000,
		Betas:     []anthropic.AnthropicBeta{"task-budgets-2026-03-13"},
		Messages: []anthropic.BetaMessageParam{{
			Role: anthropic.BetaMessageParamRoleUser,
			Content: []anthropic.BetaContentBlockParamUnion{{
				OfText: &anthropic.BetaTextBlockParam{Text: "Review the codebase and propose a refactor plan."},
			}},
		}},
		OutputConfig: anthropic.BetaOutputConfigParam{
			Effort: anthropic.BetaOutputConfigEffortHigh,
			TaskBudget: anthropic.BetaTokenTaskBudgetParam{
				Total: 64000,
			},
		},
	})
	fmt.Println(response)
}
```

```java Java hidelines={1..7,9..11,-2..}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.BetaOutputConfig;
import com.anthropic.models.beta.messages.BetaTokenTaskBudget;
import com.anthropic.models.beta.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;

public class Main {
    public static void main(String[] args) {
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        MessageCreateParams params = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_4_7)
            .maxTokens(128000L)
            .addUserMessage("Review the codebase and propose a refactor plan.")
            .outputConfig(BetaOutputConfig.builder()
                .effort(BetaOutputConfig.Effort.HIGH)
                .taskBudget(BetaTokenTaskBudget.builder().total(64000L).build())
                .build())
            .addBeta("task-budgets-2026-03-13")
            .build();

        BetaMessage response = client.beta().messages().create(params);
    }
}
```

```csharp C# hidelines={1..3}
using Anthropic;
using Anthropic.Models.Beta.Messages;
using Anthropic.Models.Messages;

var client = new AnthropicClient();

var response = await client.Beta.Messages.Create(new MessageCreateParams
{
    Model = "claude-opus-4-7",
    MaxTokens = 128000,
    Messages = [new() { Role = Role.User, Content = "Review the codebase and propose a refactor plan." }],
    OutputConfig = new BetaOutputConfig
    {
        Effort = Effort.High,
        TaskBudget = new BetaTokenTaskBudget { Total = 64000 },
    },
    Betas = ["task-budgets-2026-03-13"],
});
```

```php PHP hidelines={1..4}
<?php

use Anthropic\Client;

$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));

$response = $client->beta->messages->create(
    model: 'claude-opus-4-7',
    maxTokens: 128000,
    messages: [
        ['role' => 'user', 'content' => 'Review the codebase and propose a refactor plan.'],
    ],
    outputConfig: [
        'effort' => 'high',
        'taskBudget' => ['type' => 'tokens', 'total' => 64000],
    ],
    betas: ['task-budgets-2026-03-13'],
);
```

```ruby Ruby hidelines={1..2}
require "anthropic"

client = Anthropic::Client.new

response = client.beta.messages.create(
  model: "claude-opus-4-7",
  max_tokens: 128_000,
  messages: [
    { role: "user", content: "Review the codebase and propose a refactor plan." }
  ],
  output_config: {
    effort: :high,
    task_budget: { type: :tokens, total: 64_000 }
  },
  betas: ["task-budgets-2026-03-13"]
)

puts response
```
</CodeGroup>

`task_budget` 对象有三个字段：

- `type`：始终为 `"tokens"`。
- `total`：Claude 在整个代理循环中可以消耗的 token 数量，包括思考、工具调用、工具结果和输出。
- `remaining`（可选）：从先前请求结转的预算余数。省略时默认为 `total`。

## 预算倒计时的工作原理

Claude 会看到一个由服务器端注入到整个对话中的预算倒计时标记。该标记显示当前代理循环中剩余的 token 数量，并在模型生成思考、工具调用和输出以及处理工具结果时更新。Claude 使用此信号来调整节奏，并在预算消耗完毕时优雅地完成任务。

<Warning>
**倒计时反映的是 Claude 在当前代理循环中已处理的 token 数量，而不是您在轮次之间重新发送的 token 数量。** 如果您的客户端在每个后续请求中发送完整的对话历史记录，您的客户端侧 token 计数可能与 Claude 跟踪的预算不同。如果您在重新发送完整历史记录的同时递减 `remaining`，模型会看到一个被低报的预算，倒计时下降速度会比应有的更快，导致 Claude 比预算实际允许的更早结束。设置一个充裕的预算，让模型根据倒计时进行自我调节，而不是试图在客户端侧镜像它。
</Warning>

### 跨轮次预算计数示例

任务预算计算的是 Claude **看到的**内容（思考、工具调用和结果以及文本），而不是您的请求负载中的内容。在代理循环中，您的客户端在每个请求上重新发送完整对话，因此负载逐轮增长，但预算仅按 Claude 本轮看到的 token 递减。

考虑一个带有 `task_budget: {type: "tokens", total: 100000}` 和单个 `bash` 工具的循环。

**第 1 轮。** 您发送初始请求：

```json
{
  "messages": [
    { "role": "user", "content": "Audit this repo for security issues and report findings." }
  ]
}
```

Claude 思考，然后发出一个工具调用并以 `stop_reason: "tool_use"` 停止：

```json
{
  "role": "assistant",
  "content": [
    {
      "type": "thinking",
      "thinking": "I'll start by listing dependencies to look for known-vulnerable packages..."
    },
    {
      "type": "tool_use",
      "id": "toolu_01",
      "name": "bash",
      "input": { "command": "cat package.json && npm audit --json" }
    }
  ]
}
```

假设此助手轮次（思考加工具调用）总共生成了 5,000 个 token。生成期间 Claude 看到的倒计时结束时接近 `remaining` 约 95,000。

**第 2 轮。** 您的客户端执行工具，然后重新发送完整历史记录并附加工具结果：

```json
{
  "messages": [
    { "role": "user", "content": "Audit this repo for security issues and report findings." },
    {
      "role": "assistant",
      "content": [
        { "type": "thinking", "thinking": "I'll start by listing dependencies..." },
        {
          "type": "tool_use",
          "id": "toolu_01",
          "name": "bash",
          "input": { "command": "cat package.json && npm audit --json" }
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_01",
          "content": "<2,800 tokens of npm audit output>"
        }
      ]
    }
  ]
}
```

重新发送的第 1 轮用户和助手消息不会被重复计算，但 2,800 个 token 的工具结果是 Claude 本轮看到的新内容，会从预算中扣除。Claude 在思考和第二次工具调用（`grep -rn "eval(" src/`）上又花费了 4,000 个 token。倒计时结束时接近 `remaining` 约 88,200。

**第 3 轮。** 完整历史记录再次重新发送，并附加第二个工具结果（1,200 个 token 的 grep 输出）。Claude 撰写了一份 6,000 个 token 的最终发现报告，并以 `stop_reason: "end_turn"` 停止。`remaining` 约 81,000。

将三轮并排比较，可以清楚地区分负载大小和预算消耗：

| 轮次 | 请求负载（约发送的输入 token） | 本轮计入预算的 token | 之后预算 `remaining` |
|---|---|---|---|
| 1 | ~20 | 5,000（思考 + `tool_use`） | ~95,000 |
| 2 | ~7,800（第 1 轮历史 + 工具结果） | 6,800（2,800 工具结果 + 4,000 思考和 `tool_use`） | ~88,200 |
| 3 | ~13,000（完整历史 + 第二个工具结果） | 7,200（1,200 工具结果 + 6,000 `text`） | ~81,000 |
| **合计** | **跨请求共发送约 20,820** | **计入预算 19,000** | — |

您的客户端将第 1 轮用户消息发送了三次，第 1 轮助手消息发送了两次，但每次都只计算一次。预算消耗了 100,000 个 token 中的 19,000 个，即使您的客户端传输的累积负载更大，第 2 和第 3 轮的 prompt 缓存输入也更大。

### 使用 `remaining` 在压缩后携带预算

如果您的代理循环在请求之间压缩或重写上下文（例如，通过总结较早的轮次），服务器没有压缩前已消耗多少预算的记忆。在下一个请求中传递 `remaining`，以便倒计时从中断的地方继续，而不是重置为 `total`：

<CodeGroup>
```python Python
output_config = {
    "effort": "high",
    "task_budget": {
        "type": "tokens",
        "total": 128000,
        "remaining": 128000 - tokens_spent_so_far,
    },
}
```

```typescript TypeScript
const output_config = {
  effort: "high",
  task_budget: {
    type: "tokens",
    total: 128000,
    remaining: 128000 - tokensSpentSoFar
  }
};
```

```go Go
outputConfig := anthropic.BetaOutputConfigParam{
	Effort: anthropic.BetaOutputConfigEffortHigh,
	TaskBudget: anthropic.BetaTokenTaskBudgetParam{
		Total:     128000,
		Remaining: anthropic.Int(128000 - tokensSpentSoFar),
	},
}
```

```java Java
BetaOutputConfig outputConfig = BetaOutputConfig.builder()
    .effort(BetaOutputConfig.Effort.HIGH)
    .taskBudget(BetaTokenTaskBudget.builder()
        .total(128000L)
        .remaining(128000L - tokensSpentSoFar)
        .build())
    .build();
```

```csharp C#
var outputConfig = new BetaOutputConfig
{
    Effort = Effort.High,
    TaskBudget = new BetaTokenTaskBudget
    {
        Total = 128000,
        Remaining = 128000 - tokensSpentSoFar,
    },
};
```

```php PHP
$outputConfig = [
    'effort' => 'high',
    'taskBudget' => [
        'type' => 'tokens',
        'total' => 128000,
        'remaining' => 128000 - $tokensSpentSoFar,
    ],
];
```

```ruby Ruby
output_config = {
  effort: :high,
  task_budget: {
    type: :tokens,
    total: 128_000,
    remaining: 128_000 - tokens_spent_so_far
  }
}
```
</CodeGroup>

对于每轮重新发送完整未压缩历史记录的循环，请省略 `remaining` 并让服务器跟踪倒计时。

## 任务预算是建议性的，而非强制性的

任务预算是一种**软提示，而非硬限制**。如果 Claude 正在执行一个中断比完成更具有破坏性的操作，它可能会偶尔超出预算。对总输出 token 的强制限制仍然是 `max_tokens`，当达到时会以 `stop_reason: "max_tokens"` 截断响应。

要对成本或延迟进行硬限制，请将任务预算与合理的 `max_tokens` 值结合使用：

- 使用 `task_budget` 给 Claude 一个目标来调整节奏。
- 使用 `max_tokens` 作为防止失控生成的绝对上限。

因为 `task_budget` 跨越整个代理循环（可能包含多个请求），而 `max_tokens` 限制每个单独的请求，所以这两个值是独立的；一个不需要在另一个之下或等于另一个。

<Warning>
**预算太小可能导致类似拒绝的行为。** 当 Claude 看到一个明显不足以完成所请求工作的预算时（例如，为多小时的代理编码任务设置 20,000 个 token 的预算），它可能会完全拒绝尝试该任务、大幅缩减范围，或者提前以部分结果停止，而不是开始无法完成的工作。如果您在设置预算后观察到意外的拒绝或过早停止，请在调试其他参数之前增加预算。根据您的实际任务长度分布来确定预算大小，而不是使用固定默认值；请参阅[选择预算](#选择预算)。
</Warning>

## 选择预算

正确的预算取决于您的代理循环当前执行的工作量。与其猜测，不如先测量您现有的 token 使用量，然后从那里进行调整。

### 测量当前使用量

在**未设置** `task_budget` 的情况下运行一组有代表性的任务样本，并记录 Claude 每个任务消耗的总 token 数。对于代理循环，请对循环中每个请求的 `usage.output_tokens` 加上思考和工具结果 token 求和：

<CodeGroup>
```python Python
def run_task_and_count_tokens(messages: list) -> int:
    """运行代理循环直到完成并返回消耗的总 token 数。"""
    total_spend = 0
    while True:
        response = client.beta.messages.create(
            model="claude-opus-4-7",
            max_tokens=128000,
            messages=messages,
            tools=tools,
            betas=["task-budgets-2026-03-13"],
        )
        # 计算 Claude 本轮生成的内容（output 包括文本 + 思考 + 工具调用）。
        # 工具结果 token 也计入预算；如果您希望客户端侧跟踪与服务器端倒计时匹配，
        # 请添加您在下面追加的 tool_result 块的 token 计数。
        total_spend += response.usage.output_tokens
        if response.stop_reason == "end_turn":
            return total_spend
        # 追加助手轮次和您的工具结果，然后继续循环。
        messages += [
            {"role": "assistant", "content": response.content},
            {"role": "user", "content": run_tools(response.content)},
        ]
```

```typescript TypeScript
async function runTaskAndCountTokens(
  messages: Anthropic.Beta.BetaMessageParam[]
): Promise<number> {
  let totalSpend = 0;
  while (true) {
    const response = await client.beta.messages.create({
      model: "claude-opus-4-7",
      max_tokens: 128000,
      messages,
      tools,
      betas: ["task-budgets-2026-03-13"]
    });
    // 计算 Claude 本轮生成的内容（output 包括文本 + 工具调用；
    // 如果您选择加入，可通过同一 usage 对象添加缓存创建和思考）。
    totalSpend += response.usage.output_tokens;
    if (response.stop_reason === "end_turn") {
      return totalSpend;
    }
    // 追加助手轮次和您的工具结果，然后继续循环。
    messages = [
      ...messages,
      { role: "assistant", content: response.content },
      { role: "user", content: runTools(response.content) }
    ];
  }
}
```
</CodeGroup>

在一组有代表性的任务上运行此代码并记录分布。从每任务 token 消耗的 p99 开始，了解提供模型任务预算可能如何改变模型的行为，然后根据需要向上或向下测试。

`task_budget.total` 的最小可接受值为 **20,000 个 token**；低于最小值的值将返回 400 错误。

## 与其他参数的交互

- **`max_tokens`：** 与任务预算正交。`max_tokens` 是对生成 token 的硬性每请求限制，而 `task_budget` 是跨越整个代理循环（可能跨越多个请求）的建议性限制。在 `xhigh` 或 `max` effort 下，将 `max_tokens` 设置为至少 64k，以给 Claude 在每个请求上思考和行动的空间。
- **[Effort](/docs/en/build-with-claude/effort)：** Effort 控制 Claude 对每个步骤的推理深度。任务预算控制 Claude 在整个代理循环中执行的总工作量。两者互补：effort 调整深度，任务预算调整广度。
- **[自适应思考](/docs/en/build-with-claude/adaptive-thinking)：** 任务预算将思考 token 包含在计数中，因此自适应思考会随着预算消耗而自然减少。
- **[Prompt caching](/docs/en/build-with-claude/prompt-caching)：** 预算倒计时标记是服务器端每轮注入的，因此它不会跨请求匹配。如果您的客户端在每个后续请求中递减 `task_budget.remaining`，更改的值会使任何包含它的缓存前缀失效。要保留缓存，请在初始请求上设置一次预算，让模型根据服务器端倒计时进行自我调节，而不是在客户端侧修改预算。

## 功能支持

| 模型 | 支持 |
|-------|---------|
| Claude Opus 4.7 | 公开测试（设置 `task-budgets-2026-03-13` 头） |
| Claude Opus 4.6 | 不支持 |
| Claude Sonnet 4.6 | 不支持 |
| Claude Haiku 4.5 | 不支持 |

任务预算在 [Claude Code](https://docs.claude.com/en/docs/claude-code) 或 Cowork 界面上启动时不支持。请在 Claude Opus 4.7 上通过 Messages API 直接使用任务预算。
