Claude API 2026: Prompt Caching, Tool Use và Batch
Hướng dẫn thực chiến về ba tính năng Claude API quan trọng nhất: prompt caching, tool use, và Message Batches API — khoảng cách giữa prototype và production.
Bởi Ethan
2.611 từ · 14 phút đọc
Claude API năm 2026 là một sản phẩm khác hẳn so với một năm trước. Ba tính năng quyết định chất lượng của một integration production: prompt caching (giảm chi phí tới 90× cho context lặp lại), tool use (biến Claude thành agent gọi code của bạn), và Message Batches API (xử lý hàng nghìn request với giá một nửa). Nếu bạn vẫn đang gửi raw message mà không dùng bất kỳ tính năng nào trong số này, bạn đang lãng phí tiền.
Bài viết này dành cho ai
Backend developer đã từng gọi một LLM API nào đó — OpenAI, Cohere, hay bất kỳ provider nào — và đang onboard sang Anthropic SDK. Code mẫu dùng TypeScript làm chính, Python được bổ sung cho các pattern có sự khác biệt đáng kể.
Chọn Claude API model
Ba model hiện tại, một quy trình quyết định rõ ràng:
| Model | API ID | Context | Max output | Giá (input / output trên MTok) |
|---|---|---|---|---|
| Claude Opus 4.8 | claude-opus-4-8 | 1M tokens | 128k tokens | $5 / $25 |
| Claude Sonnet 4.6 | claude-sonnet-4-6 | 1M tokens | 64k tokens | $3 / $15 |
| Claude Haiku 4.5 | claude-haiku-4-5-20251001 | 200k tokens | 64k tokens | $1 / $5 |
Một lưu ý về cách đặt tên: từ thế hệ 4.6 trở đi, model ID không còn phần ngày tháng ở cuối. claude-sonnet-4-6 vẫn là một snapshot cố định — nó sẽ không tự thay đổi sau lưng bạn. Đây không phải là alias tự động cập nhật theo phiên bản mới nhất.
Hai alias đang bị khai tử vào 15 tháng 6 năm 2026: claude-sonnet-4-20250514 và claude-opus-4-20250514. Nếu code của bạn đang dùng một trong hai, hãy migrate sang claude-sonnet-4-6 và claude-opus-4-8 ngay bây giờ.
Nên dùng model nào:
- Haiku 4.5 cho classification, extraction, và các tác vụ gửi hàng nghìn request cần kiểm soát chi phí. Đây là model phù hợp cho các ví dụ Batch API bên dưới.
- Sonnet 4.6 cho phần lớn công việc thực tế — sinh code, phân tích, agentic flow. Cân bằng tốt giữa tốc độ và chất lượng.
- Opus 4.8 cho các bài toán multi-step phức tạp và high-autonomy agent khi chất lượng là ưu tiên hàng đầu, không phải chi phí.
Để xem benchmark chi tiết khi nào Opus đáng bỏ thêm tiền, đọc Claude Opus 4.7 cho coding — Khi nào model lớn thắng.
Cài SDK:
npm install @anthropic-ai/sdk
pip install anthropic
Prompt caching
Ý tưởng đơn giản: nếu bạn có một khối context lớn không thay đổi giữa các request — system prompt, một tài liệu, một file code — bạn có thể đánh dấu nó bằng cache_control và Anthropic sẽ lưu một snapshot đã xử lý phía server. Các request sau đó dùng cùng prefix sẽ đọc từ cache với chi phí bằng 10% giá input thông thường.
Ngưỡng token tối thiểu: Khác nhau tùy model. Opus 4.8 và Sonnet 4.6 cần 1,024 tokens; Haiku 4.5 cần 4,096 tokens. Dưới ngưỡng này, request vẫn xử lý bình thường — không có lỗi, nhưng cũng không có cache. Nếu bạn dùng Haiku 4.5 với prompt dưới 4,096 tokens, tỉ lệ cache hit sẽ bằng 0 dù có đánh dấu cache_control.
Hai tùy chọn TTL:
| Loại | cache_control | Chi phí ghi | Chi phí đọc |
|---|---|---|---|
| 5 phút (mặc định) | { "type": "ephemeral" } | 1.25× giá input cơ bản | 0.10× giá input cơ bản |
| 1 giờ | { "type": "ephemeral", "ttl": "1h" } | 2.00× giá input cơ bản | 0.10× giá input cơ bản |
Với hầu hết use case hội thoại, TTL 5 phút là đủ. Với batch workload — vốn mất nhiều thời gian xử lý hơn — hãy dùng TTL 1 giờ.
Ví dụ multi-turn chat cache một knowledge base lớn:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
// Cần ≥1,024 tokens cho Opus 4.8/Sonnet 4.6, ≥4,096 cho Haiku 4.5
const KNOWLEDGE_BASE = `
You are a senior TypeScript engineer. You have deep knowledge of:
- Node.js ecosystem and async patterns
- REST API design and OpenAPI specifications
- Database query optimization and ORM usage
... (imagine many pages of documentation, code style guides, etc.)
`.repeat(50); // kéo dài nhân tạo cho demo
async function chat(
userMessage: string,
history: Anthropic.MessageParam[] = []
): Promise<{ reply: string; cacheStats: object }> {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: [
{
type: "text",
text: KNOWLEDGE_BASE,
cache_control: { type: "ephemeral" }, // cache knowledge base lớn
},
],
messages: [...history, { role: "user", content: userMessage }],
});
const { input_tokens, cache_creation_input_tokens, cache_read_input_tokens, output_tokens } =
response.usage;
return {
reply: response.content.find((b) => b.type === "text")?.text ?? "",
cacheStats: {
uncached: input_tokens,
written: cache_creation_input_tokens, // khác 0 ở lần gọi đầu tiên
read: cache_read_input_tokens, // khác 0 ở các lần gọi sau
output: output_tokens,
},
};
}
// Lần gọi đầu — ghi cache, trả 1.25× cho phần được cache
const first = await chat("Explain async/await error handling patterns.");
console.log("First call stats:", first.cacheStats);
// Lần gọi thứ hai — đọc từ cache với 10% giá gốc
const second = await chat("Now show me a retry wrapper example.");
console.log("Second call stats:", second.cacheStats);
Bẫy mà hầu hết mọi người mắc phải: input_tokens trong response usage không có nghĩa là tổng số input token khi caching đang hoạt động. Nó chỉ đếm token sau cache breakpoint cuối cùng. Tính chi phí thực như sau:
total_input = cache_read_input_tokens + cache_creation_input_tokens + input_tokens
Bẫy thứ hai: đặt marker cache_control vào khối ổn định cuối cùng. Nếu bạn vô tình cache một khối chứa timestamp hay user ID của từng request, prefix hash thay đổi mỗi lần và cache hit rate về 0. Cache phần static; để phần dynamic ra ngoài vùng được cache.
Cache invalidation lan tỏa xuống dưới: thay đổi một tool definition sẽ làm mất hiệu lực mọi thứ phía dưới nó (system, messages). Chỉ thay đổi user message thì không ảnh hưởng đến system cache.
Tool use
Tool use biến Claude từ một text generator thành agent. Bạn định nghĩa các function theo JSON schema, Claude tự quyết định khi nào cần gọi, code của bạn thực thi chúng, rồi trả về kết quả. Vòng lặp này chạy cho đến khi Claude trả về end_turn.
Định nghĩa tool
const tools: Anthropic.Tool[] = [
{
name: "get_weather",
description:
"Fetch current weather conditions for a city. Returns temperature in Celsius and a " +
"short description. Use when the user asks about weather or needs to know current conditions.",
input_schema: {
type: "object" as const,
properties: {
city: {
type: "string",
description: "The city name, e.g. 'Tokyo', 'London', or 'San Francisco, CA'",
},
},
required: ["city"],
},
},
];
Tên tool phải khớp với ^[a-zA-Z0-9_-]{1,64}$. Trường description là quan trọng nhất — Claude đọc nó để quyết định có gọi tool hay không, vì vậy hãy mô tả rõ khi nào nên và không nên dùng.
Vòng lặp agentic
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
async function executeTool(name: string, input: Record<string, unknown>): Promise<string> {
if (name === "get_weather") {
const city = input.city as string;
// gọi weather API thực trong production
return JSON.stringify({ city, temp_celsius: 18, description: "Overcast with light rain" });
}
throw new Error(`Unknown tool: ${name}`);
}
async function runAgent(userMessage: string): Promise<string> {
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: userMessage },
];
while (true) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
tools,
messages,
});
messages.push({ role: "assistant", content: response.content });
if (response.stop_reason === "end_turn") {
const textBlock = response.content.find((b) => b.type === "text");
return textBlock?.type === "text" ? textBlock.text : "";
}
if (response.stop_reason === "tool_use") {
// QUAN TRỌNG: tool_result block phải đứng ĐẦU TIÊN trong mảng content của user message
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type !== "tool_use") continue;
let content: string;
try {
content = await executeTool(block.name, block.input as Record<string, unknown>);
} catch (err) {
content = `Error: ${(err as Error).message}`;
toolResults.push({ type: "tool_result", tool_use_id: block.id, content, is_error: true });
continue;
}
toolResults.push({ type: "tool_result", tool_use_id: block.id, content });
}
messages.push({ role: "user", content: toolResults });
}
}
}
const answer = await runAgent("What's the weather like in Tokyo right now?");
console.log(answer);
Quy tắc thứ tự gây ra lỗi 400: khi bạn push tool_result block trở lại Claude, chúng phải đứng đầu tiên trong mảng content của user message, trước bất kỳ text block nào. Vi phạm điều này sẽ nhận 400 invalid_request_error. Các LLM API khác không có ràng buộc này — đây là cái bẫy quen thuộc với những ai mới chuyển sang Claude.
Xử lý lỗi: đặt is_error: true trên tool_result và đưa thông báo lỗi vào content. Claude sẽ đọc và tự quyết định có thử lại tool call, dùng cách khác, hay thông báo cho user. Đừng throw trong agentic loop — bắt lỗi và trả về dưới dạng tool result mới là thứ làm agent trở nên bền vững.
Kiểm soát tool choice:
{ "type": "auto" }— Claude tự quyết định (mặc định){ "type": "any" }— Claude bắt buộc phải gọi một trong các tool được cung cấp{ "type": "tool", "name": "get_weather" }— ép gọi một tool cụ thể{ "type": "none" }— tool được định nghĩa nhưng không thể gọi trong lượt này
Lưu ý: tool_choice: any và tool_choice: tool không tương thích với extended thinking.
Khi agent đã chạy trên production, Chi phí thực sự khi vận hành một đội AI agent năm 2026 phân tích TCO đầy đủ — bao gồm chi phí oversight và retry thường bị bỏ qua trong hầu hết ước tính.
Message Batches API
Batches API dành cho công việc không cần response ngay lập tức — classification job, pipeline xử lý tài liệu, evaluation chạy ở quy mô lớn. Bạn submit request, polling cho đến khi hoàn thành, rồi stream kết quả. Đổi lại sự chờ đợi, bạn trả 50% giá thông thường.
| Model | Batch input | Batch output |
|---|---|---|
| Opus 4.8 | $2.50/MTok | $12.50/MTok |
| Sonnet 4.6 | $1.50/MTok | $7.50/MTok |
| Haiku 4.5 | $0.50/MTok | $2.50/MTok |
Giới hạn: tối đa 100,000 request mỗi batch, dung lượng tối đa 256 MB, thời gian xử lý tối đa 24 giờ, kết quả lưu trong 29 ngày.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
interface ReviewItem {
id: string;
text: string;
}
async function classifyReviewsBatch(reviews: ReviewItem[]): Promise<Record<string, string>> {
// 1. Tạo batch
const batch = await client.messages.batches.create({
requests: reviews.map((review) => ({
custom_id: review.id,
params: {
model: "claude-haiku-4-5",
max_tokens: 10,
messages: [
{
role: "user" as const,
content:
`Classify this product review as POSITIVE, NEGATIVE, or NEUTRAL.\n` +
`Reply with only the label.\n\nReview: "${review.text}"`,
},
],
},
})),
});
console.log(`Batch created: ${batch.id} (${reviews.length} requests)`);
// 2. Polling cho đến khi hoàn thành
let current = batch;
while (current.processing_status !== "ended") {
await new Promise((r) => setTimeout(r, 60_000)); // polling mỗi 60 giây
current = await client.messages.batches.retrieve(batch.id);
const { processing, succeeded, errored, expired } = current.request_counts;
console.log(
`Status: ${current.processing_status} — processing: ${processing}, ` +
`succeeded: ${succeeded}, errored: ${errored}, expired: ${expired}`
);
}
// 3. Stream kết quả (tiết kiệm bộ nhớ; thứ tự KHÔNG được đảm bảo)
const sentimentMap: Record<string, string> = {};
for await (const result of await client.messages.batches.results(batch.id)) {
switch (result.result.type) {
case "succeeded": {
const content = result.result.message.content[0];
sentimentMap[result.custom_id] =
content.type === "text" ? content.text.trim() : "UNKNOWN";
break;
}
case "errored":
console.error(`Request ${result.custom_id} failed:`, result.result.error);
sentimentMap[result.custom_id] = "ERROR";
break;
case "expired":
console.warn(`Request ${result.custom_id} expired (24-hour window)`);
sentimentMap[result.custom_id] = "EXPIRED";
break;
}
}
return sentimentMap;
}
const reviews: ReviewItem[] = [
{ id: "review-001", text: "Amazing product, exceeded all my expectations!" },
{ id: "review-002", text: "Stopped working after two weeks. Very disappointed." },
{ id: "review-003", text: "It does what it says on the box." },
// ... hàng nghìn review khác
];
const results = await classifyReviewsBatch(reviews);
console.log(results);
// { "review-001": "POSITIVE", "review-002": "NEGATIVE", "review-003": "NEUTRAL" }
Quy tắc thứ tự của batch: kết quả trả về theo thứ tự ngẫu nhiên, không theo thứ tự bạn submit. Luôn khớp kết quả với request bằng custom_id. custom_id phải khớp ^[a-zA-Z0-9_-]{1,64}$ và phải unique trong batch.
Các loại kết quả bạn sẽ gặp:
succeeded— response Messages API đầy đủ nằm trong.messageerrored— kiểm tra.error.error.type:invalid_request_errornghĩa là sửa lại request; server error thì nên retrycanceled— người dùng hủy; không tính phíexpired— hết 24 giờ xử lý; không tính phí
Kết hợp caching với batch: hai khoản giảm giá này cộng dồn được. Thêm cache_control vào system prompt và bạn có thể nhận cả 50% batch pricing lẫn tới 90% cache saving. Tỉ lệ cache hit trên async batch là 30–98% — best-effort, không được đảm bảo, vì các request xử lý đồng thời trên nhiều worker riêng biệt. Dùng TTL 1 giờ ({ "type": "ephemeral", "ttl": "1h" }) cho batch lớn để tối đa hóa cache hit rate.
Extended output (300k token mỗi request): thêm header anthropic-beta: output-300k-2026-03-24 vào batch request để mở khóa tới 300k output token. Tính năng này chỉ dành cho batch; giới hạn API đồng bộ vẫn là 64k–128k tùy model. Hỗ trợ trên Opus 4.8, Opus 4.7, Opus 4.6, và Sonnet 4.6. Không khả dụng trên Bedrock, Vertex AI, hay Microsoft Foundry.
Những thay đổi so với Claude 3.x
Nếu bạn đang migrate từ code Claude 3.x, đây là những thứ hay gây bất ngờ nhất:
Context window giờ rất lớn. Opus 4.8 và Sonnet 4.6 có context 1M token. Code chia nhỏ tài liệu để vừa vào cửa sổ 8k hoặc 32k không còn cần thiết nữa.
Model ID không có ngày tháng nhưng vẫn là snapshot cố định. claude-sonnet-4-6 không phải alias tự động cập nhật — nó ghim vào một snapshot cụ thể. Ngày tháng không có trong chuỗi; snapshot thì vẫn cố định.
input_tokens không phải thứ bạn nghĩ. Khi đã bật caching, input_tokens chỉ đếm token chưa được cache. Tổng input là tổng của cả ba trường trong usage.
TTL 1 giờ là tính năng mới. Claude 3.x chỉ có TTL 5 phút. Với batch workload mất hơn 5 phút để xử lý, hãy dùng "ttl": "1h" — không thì cache hết hạn trước khi batch chạy xong.
strict: true trong tool definition. Tính năng mới trong SDK 4.x. Đảm bảo tool input của Claude luôn tuân theo schema của bạn. Nên bật trong production.
Lưu ý
Ngưỡng token tối thiểu khác nhau theo model. Opus 4.8 và Sonnet 4.6 cần 1,024 tokens; Haiku 4.5 cần 4,096 tokens. Nếu muốn một ngưỡng an toàn dùng được cho mọi model Claude 4.x, hãy dùng 4,096 tokens.
Tỉ lệ cache hit của batch không thể đoán trước. 30–98% là khoảng tài liệu ghi nhận. Khi ước tính chi phí cho batch + caching kết hợp, hãy tính theo trường hợp xấu nhất.
Anthropic không có affiliate program. Đăng ký API access tại console.anthropic.com. Giá trong bài viết này là tính đến tháng 6 năm 2026 — luôn kiểm tra trang giá hiện tại trước khi xây dựng cost model.