Learn > Claude > Claude API nâng cao — extended thinking, vision, PDF, citations, prompt caching

Claude API nâng cao — extended thinking, vision, PDF, citations, prompt caching

Phần 5/7 ghi chú Building with the Claude API: extended thinking ("giấy nháp" của Claude), vision (đưa ảnh), PDF processing, citations trích nguồn, prompt caching giảm chi phí và Files API + code execution.

  • Extended thinking cho Claude "giấy nháp" suy nghĩ trước khi trả lời; vision và PDF processing cho Claude đọc ảnh/tài liệu trực tiếp.
  • Citations gắn trích nguồn vào từng đoạn trả lời — nền tảng chống ảo giác khi làm việc trên tài liệu.
  • Prompt caching tái dùng công preprocessing để rẻ & nhanh hơn; Files API + code execution giao việc tính toán cho Claude.

TL;DR — Các năng lực nâng cao của Claude API: extended thinking · vision · PDF processing · citations · prompt caching · Files API + code execution — mỗi thứ một mục đích, ghép được vào mọi pipeline.

Phần 5/7 của ghi chú khoá Building with the Claude API — đề thi thử 589 câu (chấm điểm + giải thích) nằm ở tab "Đề thi thử" trên trang tổng quan.

Extended thinking — "giấy nháp" của Claude

TL;DR mini. Extended thinking cho Claude thời gian suy luận trước khi trả lời — như tờ giấy nháp mà bạn nhìn thấy được. Bật lên, response đổi từ một text block thành cấu trúc 2 phần: thinking block (có thể nhiều) + final answer. Lợi: reasoning tốt hơn · chính xác hơn với bài khó · minh bạch tư duy. Đánh đổi: tốn tiền (trả cho thinking tokens) · latency cao hơn · code xử lý response phức tạp hơn. ⭐ Nguyên tắc bật: để prompt evaluation quyết định — chạy KHÔNG thinking trước, tối ưu prompt cho kỹ, chỉ bật khi accuracy vẫn chưa đạt. Config: params["thinking"] = {"type":"enabled","budget_tokens": N}; budget_tokens tối thiểu 1024max_tokens PHẢI > thinking_budget. signature = token mã hoá chống sửa nội dung thinking. redacted_thinking = block bị hệ thống an toàn cờ (mã hoá, vẫn phải gửi lại). ⚠️ KHÔNG tương thích với message prefillingtemperature.

Là gì. Extended thinking là tính năng suy luận nâng cao: model có thời gian tự làm nháp trước khi ra câu trả lời cuối. Bạn thấy được quá trình lập luận → minh bạch hơn và thường cho chất lượng tốt hơn.

Response đổi cấu trúc. Không bật thinking → response là một text block. Bật thinking → response gồm 2 phần:

Phần Là gì
Thinking block (một hoặc nhiều) Quá trình lập luận của Claude — phần "giấy nháp" bạn đọc được.
Final answer (text block) Câu trả lời cuối cùng cho người dùng.

Lợi ích vs. đánh đổi:

✅ Lợi ích ⚠️ Đánh đổi
Reasoning tốt hơn cho bài phức tạp Tốn tiền — bạn trả cho thinking tokens
Chính xác hơn với bài khó Latency cao hơn (suy nghĩ mất thời gian)
Minh bạch tư duy (thấy được cách ra đáp án) Code xử lý response phức tạp hơn (phải duyệt block)

⭐ Khi nào dùng — để eval quyết định, không cảm tính. Quy trình đúng: (1) chạy prompt KHÔNG thinking trước; (2) nếu accuracy chưa đạt → tối ưu prompt (các kỹ thuật đã học); (3) chỉ khi đã tối ưu mà vẫn chưa đủ → mới bật extended thinking. Đây là công cụ cho lúc prompting thường không đủ, không phải bật mặc định (vì tốn tiền + chậm).

Bật trong code. Thêm 2 tham số vào hàm chatthinking=False, thinking_budget=1024 — rồi:

if thinking:
    params["thinking"] = {
        "type": "enabled",
        "budget_tokens": thinking_budget,
    }
  • thinking_budget = trần token Claude được dùng để suy luận (khác max_tokens = trần token sinh câu trả lời).
  • Tối thiểu 1024 token; và max_tokens PHẢI lớn hơn thinking_budget (nếu không → lỗi).

signature — chống tamper. Mỗi thinking block đi kèm một signature = token mã hoá (cryptographic) đảm bảo bạn không sửa nội dung phần suy luận. Ngăn lập trình viên can thiệp vào lập luận của Claude — điều có thể lái model đi hướng không an toàn.

redacted_thinking — khi suy luận bị "cờ". Đôi khi thay vì thinking đọc được, bạn nhận một redacted_thinking block: xảy ra khi quá trình suy luận bị hệ thống an toàn nội bộ gắn cờ. Nội dung thật nằm ở dạng mã hoá (bạn không đọc được) — nhưng bạn VẪN phải gửi nguyên message (kèm block redacted đó) trở lại Claude ở các lượt sau để không mất ngữ cảnh. Để kiểm thử app xử lý ca này mượt mà (không crash), có thể ép Claude trả về redacted bằng một magic string đặc biệt.

⚠️ Không tương thích. Extended thinking KHÔNG dùng chung được với message prefillingtemperature (còn vài hạn chế khác trong danh sách feature compatibility của tài liệu Anthropic). Bật thinking → không mồi sẵn đầu ra, không chỉnh temperature như bình thường.

⭐ Cập nhật production (2026). Khoá dạy cú pháp budget_tokens cũ — vẫn đúng để thi. Nhưng trên model mới (Opus 4.6 trở lên, Sonnet 5, Fable 5) budget_tokens đã bị bỏ (gửi vào → lỗi 400), thay bằng adaptive thinking: thinking={"type":"adaptive"} + tham số effort (low/medium/high/max) để Claude tự quyết cần suy nghĩ bao nhiêu. Trên các model này thinking cũng thường bật sẵn/mặc định, và raw chain-of-thought có thể chỉ trả summary (display:"summarized") chứ không trả nguyên văn. → Đề thi hỏi kiểu cũ, code thật dùng adaptive + effort. (Giống hệt tình huống prefill → structured outputs đã gặp.)

Góc Delivery Manager. (1) Thinking không phải "nút làm Claude thông minh hơn" để bật vô tội vạ — nó đổi tiền + thời gian lấy độ chính xác, nên quyết định bật/tắt phải dựa trên con số eval, đúng tinh thần evaluation-first của cả khoá. Bật bừa = đốt token cho những task vốn chẳng cần. (2) signature + redacted_thinking là bài học về "AI có ranh giới an toàn": phần suy luận không phải dữ liệu tự do muốn sửa gì thì sửa — có cơ chế chống tamper và có lúc bị che. App production phải xử lý được redacted block (dùng magic string test) nếu không sẽ crash khi gặp ca thật. (3) API đổi nhanhbudget_tokensadaptive/effort chỉ trong vài phiên bản; đây là lý do note này giữ cả "kiểu thi" lẫn "kiểu production", và cũng là lời nhắc: code gọi LLM phải tra tài liệu hiện hành trước khi hardcode tham số.

Vision — đưa ảnh cho Claude "nhìn"

TL;DR mini. Claude nhìn được ảnh: đính một image block vào user message (cạnh text block) là Claude mô tả · đếm · so sánh · phân tích trực quan. Giới hạn cần thuộc: tối đa 100 ảnh/request · 5MB/ảnh · 1 ảnh → max 8000px, nhiều ảnh → max 2000px mỗi chiều · gửi bằng base64 hoặc URL · token ≈ (width × height) / 750. Message flow y hệt text: user gửi ảnh + chữ → Claude trả text block. ⭐ Ý chốt lớn nhất: prompt engineering cho ảnh giống hệt cho text — hỏi cụt hay sai; vá bằng guidelines + steps · one/multi-shot · chia nhỏ task.

Cách gửi — image block cạnh text block. Ảnh đọc ra bytes → base64 → nhét vào một image block, đặt cùng một user message với text block:

with open("image.png", "rb") as f:
    image_bytes = base64.standard_b64encode(f.read()).decode("utf-8")

add_user_message(messages, [
    {   # Image Block
        "type": "image",
        "source": {
            "type": "base64",
            "media_type": "image/png",
            "data": image_bytes,
        },
    },
    {   # Text Block
        "type": "text",
        "text": "What do you see in this image?",
    },
])
  • source.type = base64 (kèm media_type như image/png, image/jpeg) hoặc url (trỏ tới ảnh).
  • Message flow không có gì mới: server gửi user message (ảnh + chữ) → Claude trả về một text block chứa phân tích. Giống hệt hội thoại text-only.

Giới hạn phải thuộc:

Giới hạn Con số
Số ảnh mỗi request ≤ 100 (tính trên toàn bộ messages)
Dung lượng mỗi ảnh ≤ 5MB
Kích thước — gửi 1 ảnh mỗi chiều ≤ 8000px
Kích thước — gửi nhiều ảnh mỗi chiều ≤ 2000px
Cách gửi base64 hoặc URL
Chi phí token ≈ (width px × height px) / 750

⭐ Prompt engineering cho ảnh = y hệt cho text. Đây là bài học lõi. Prompt đơn giản → kết quả tồi: hỏi "How many marbles are in this image?" thường đếm sai. Ba đòn bẩy (đều đã học ở phần text):

  1. Guidelines + analysis steps — đưa Claude một methodology thay vì câu hỏi trần. Ví dụ đếm bi: (1) đánh số từng viên một; (2) verify bằng cách đếm lại theo hướng khác (từ góc dưới-trái, theo hàng, trái→phải). Ép Claude tự kiểm tra hai lần.
  2. One-shot / multi-shot — đính ảnh mẫu đã biết đáp án đúng, nêu con số đúng, rồi mới hỏi ảnh thật → cho Claude một điểm mốc.
  3. Chia nhỏ task phức tạp thành từng bước.

Ví dụ thực chiến — fire risk từ ảnh vệ tinh. Bảo hiểm nhà dùng ảnh vệ tinh + Claude để chấm rủi ro cháy thay vì cử người đi khảo sát. Prompt KHÔNG hỏi cụt "cho điểm rủi ro cháy" mà chia 5 bước: (1) xác định căn nhà chính (mái lớn nhất, có driveway, hình khối đều); (2) phân tích tán cây phủ mái (ước lượng % mái bị cành che: 0–25 / 25–50 / 50–75 / 75%+); (3) đánh giá rủi ro cháy (điểm bắt tàn lửa, đường dẫn nhiên liệu tới nhà); (4) defensible space (tán cây có nối liền thành "cầu lửa" không, fuel ladder); (5) rating 1–4 (Low → Severe) kèm mô tả từng mức. Yêu cầu mỗi mục viết một câu tóm tắt, câu cuối là con số rating. Prompt có cấu trúc này cho kết quả chính xác/hữu ích hơn hẳn.

Góc Delivery Manager. (1) Vision không phải "tính năng mới" cần học lại từ đầu — nó tái dùng đúng bộ kỹ năng prompt engineering đã có: guidelines, process steps, few-shot, chia nhỏ. Ai đã nắm phần prompt text thì vision chỉ là thêm một loại block. (2) Token ảnh = (w×h)/750 là biến chi phí ẩn: một ảnh 2000×2000 ≈ 5.300 token — gửi 10 ảnh to là input phình nhanh, thẳng vào bài toán cost/context đã bàn ở RAG. Resize/nén ảnh trước khi gửi là "prompt caching của thế giới ảnh". (3) Ca fire-risk là mẫu tự động hoá điển hình mình hay gặp: thay vì bắt Claude "cho điểm", biến nghiệp vụ thành rubric có bước + thang điểm rõ — vừa chính xác hơn, vừa auditable (giải thích được vì sao ra điểm đó), đúng tinh thần eval/định lượng xuyên suốt khoá.

PDF processing — cho Claude đọc tài liệu PDF

TL;DR mini. Claude đọc thẳng PDF — không cần bóc text trước. Code gần như y hệt Vision, chỉ đổi 4 chỗ: (1) .png.pdf, (2) tên biến image_bytesfile_bytes (chỉ cho rõ nghĩa, không bắt buộc), (3) "type": "document" thay "image", (4) media_type": "application/pdf" thay image/png. base64 encode giống hệt. ⭐ Claude không chỉ lấy text mà hiểu cả ảnh/chart nhúng · bảng & quan hệ dữ liệu · cấu trúc/định dạng tài liệu → giải pháp một-cửa trích xuất mọi loại thông tin từ PDF.

Cách gửi — document block. Đọc PDF ra bytes → base64 → nhét vào một document block, đặt cùng user message với text block:

with open("earth.pdf", "rb") as f:
    file_bytes = base64.standard_b64encode(f.read()).decode("utf-8")

messages = []

add_user_message(messages, [
    {   # Document Block
        "type": "document",
        "source": {
            "type": "base64",
            "media_type": "application/pdf",
            "data": file_bytes,
        },
    },
    {   # Text Block
        "type": "text",
        "text": "Summarize the document in one sentence",
    },
])

chat(messages)

4 chỗ đổi so với code xử lý ảnh (đặt cạnh nhau để dễ nhớ):

Vision (ảnh) PDF
open("image.png", ...) open("earth.pdf", ...)
biến image_bytes biến file_bytes (chỉ đổi tên cho rõ, không đổi logic)
"type": "image" "type": "document"
"media_type": "image/png" "media_type": "application/pdf"

Phần source.type = base64 và cách base64.standard_b64encode(...) thì không đổi. Message flow cũng y hệt: user gửi document + chữ → Claude trả text block.

Claude trích được gì từ PDF. Không dừng ở "text extraction" thô — Claude hiểu tài liệu ở 4 lớp:

  • Text content xuyên suốt tài liệu.
  • Images & charts nhúng trong PDF (đọc được cả nội dung hình ảnh, không chỉ chữ).
  • Tablesquan hệ dữ liệu giữa các ô/cột.
  • Document structure & formatting (heading, bố cục, phân cấp).

→ Vì thế PDF processing là one-stop solution: cần summary, phân tích dữ liệu, hay bóc một mục cụ thể đều làm được trong một request. Ví dụ trong bài: một bài Wikipedia về Trái Đất lưu dạng PDF → Claude tóm tắt cả tài liệu trong một câu.

Góc Delivery Manager. (1) PDF = Vision "mở rộng", không phải kỹ năng mới — cùng một message với các content block, chỉ thêm loại document. Đã nắm ảnh thì PDF chỉ là đổi 2 chuỗi (type + media_type). (2) Bỏ được cả tầng OCR/parser — trước đây pipeline hay phải cắm pdfminer/OCR để bóc text rồi mới đưa vào prompt; nay Claude nuốt thẳng PDF giữ nguyên cả bảng và chart, đỡ hẳn một mắt xích dễ hỏng. (3) ⚠️ Nhưng vẫn là biến chi phí: PDF nhiều trang = nhiều "ảnh trang" → token phình như gửi ảnh loạt; tài liệu 800 trang thì bài toán quay lại đúng chỗ RAG đã bàn (chunk + retrieve), đừng nhồi trọn cả cuốn vào một prompt chỉ vì API cho phép.

Citations — cho Claude "trích nguồn" ngay trong câu trả lời

TL;DR mini. Khi Claude trả lời dựa trên tài liệu anh cấp, người dùng khó biết nó thật sự dẫn từ tài liệu hay bịa từ training data. Citations vá bằng cách để Claude trỏ về đúng đoạn trong nguồn cho từng khẳng định → biến Claude từ "black box" thành research assistant show-its-work. Bật bằng cách thêm 2 field vào document block: title (tên dễ đọc) + citations": {"enabled": True}. Response khi đó thành structured data: mỗi claim kèm 1 citation gồm cited_text · document_index · document_title · start_page_number · end_page_number. ⭐ Dùng được cả với plain text (source.type="text", media_type="text/plain") — nhưng thay vì page number thì trả về character position.

Bật citations — thêm 2 field vào document block. Vẫn là document block như PDF, chỉ bổ sung titlecitations:

{
    "type": "document",
    "source": {
        "type": "base64",
        "media_type": "application/pdf",
        "data": file_bytes,
    },
    "title": "earth.pdf",                 # tên dễ đọc cho tài liệu
    "citations": { "enabled": True }      # bảo Claude theo dõi chỗ tìm thấy info
}

Cấu trúc một citation. Bật citations → response không còn là text đơn giản mà thành structured data, mỗi khẳng định (claim) đính kèm citation gồm:

Field Ý nghĩa
cited_text Đúng đoạn text trong tài liệu làm bằng chứng cho câu Claude nói
document_index Tài liệu thứ mấy (hữu ích khi gửi nhiều tài liệu)
document_title Tên anh đã đặt ở field title
start_page_number Trang bắt đầu của đoạn trích
end_page_number Trang kết thúc của đoạn trích

Citations với plain text. Không giới hạn ở PDF — nguồn text thuần cũng dùng được, chỉ đổi source:

{
    "type": "document",
    "source": {
        "type": "text",
        "media_type": "text/plain",
        "data": article_text,
    },
    "title": "earth_article",
    "citations": { "enabled": True }
}

⭐ Khác biệt cốt lõi: với text thuần, thay vì page number anh nhận về character position (vị trí ký tự) chỉ đúng chỗ trong chuỗi.

Dùng UI để phát huy. Sức mạnh thật của citations nằm ở giao diện: gắn citation marker cho từng câu, người dùng hover là thấy đoạn nguồn — họ (1) thấy câu trả lời grounded trong tài liệu thật, (2) verify được bằng cách mở tài liệu gốc, (3) hiểu ngữ cảnh quanh mỗi dữ kiện.

Khi nào nên bật citations: user cần verify accuracy · làm việc với tài liệu authoritative người dùng cần tham chiếu được · transparency về nguồn là tối quan trọng · user muốn khám phá ngữ cảnh rộng hơn quanh một dữ kiện.

Góc Delivery Manager. (1) Citations = "anti-hallucination có kiểm chứng" cho app RAG — cả khoá dạy cách nhét đúng chunk vào prompt (RAG); citations là mảnh còn thiếu: chứng minh cho người dùng thấy câu trả lời đến từ đâu, thay vì bắt họ tin. Ghép RAG + citations là công thức chuẩn cho trợ lý tài liệu nội bộ (hợp đồng, tài liệu kỹ thuật, quy định). (2) cited_text là tài sản audit — với khách Nhật/khách khó tính, "trả lời đúng" chưa đủ, phải truy nguồn được; cited_text + số trang cho đúng thứ đó, đúng tinh thần auditable đã bàn ở fire-risk. (3) ⚠️ Nhớ phân biệt PDF → page number vs text → character position — chọn nhầm loại source là UI hiển thị sai chỗ trích; đây cũng là điểm đề hay gài.

Prompt caching — tái dùng công preprocessing để rẻ & nhanh hơn

TL;DR mini. Bình thường mỗi request Claude làm preprocessing trên input (tokenize → create embeddings → add context) rồi mới generate output — xong là vứt hết công preprocessing đó đi. Follow-up lặp lại cùng nội dung → làm lại từ đầu, phí. Prompt caching = lưu phần preprocessing vào cache (như bảng tra) thay vì vứt: request đầu WRITE cache, follow-up READ cache → rẻ hơn + nhanh hơn. ⚠️ Giới hạn: cache sống 1 giờ · chỉ lợi khi gửi lặp lại cùng nội dung · hiệu quả nhất khi nội dung đó xuất hiện cực thường xuyên.

Claude xử lý request bình thường thế nào (không cache). Gửi message → Claude không generate ngay, mà làm một đống preprocessing trên input trước:

  1. Tokenize the prompt (cắt thành token).
  2. Create embeddings cho mỗi token.
  3. Add context dựa trên các từ xung quanh.
  4. Chỉ sau đó mới Generate output text.

⭐ Then chốt: 3 bước đầu là preprocessing của INPUT, bước 4 mới là sinh câu trả lời. Trả lời xong, Claude vứt bỏ toàn bộ phần tokenization + embeddings + context — như dọn hết vào "Trash".

Vấn đề khi vứt công. Follow-up mà cùng nội dung (vd hội thoại refine bản summary của cùng đoạn text dài: "The summary needs more focus on…") → Claude phải lặp lại y hệt preprocessing trên đoạn nó vừa xử lý xong lúc nãy. Đúng kiểu "tôi vừa làm xong việc này và vứt đi mất — lẽ ra tái dùng được!"

Prompt caching vá bằng cách nào. Thay vì vứt, request đầu lưu kết quả preprocessing vào cache. Cache như một lookup table: "lần sau gặp lại message này, tôi tái dùng công đã làm." Request đầu chỉ còn Generate output (phần preprocessing lấy từ cache ra).

Lợi ích & giới hạn (phải thuộc):

✅ Benefits ⚠️ Limitations
Faster responses — request dùng cached content chạy nhanh hơn Cache duration — cached content chỉ sống 1 giờ
Lower costs — trả ít hơn cho phần cached Limited use cases — chỉ lợi khi lặp lại cùng nội dung
Automatic — initial request WRITE cache, follow-up READ cache High frequency — hiệu quả nhất khi content xuất hiện cực thường xuyên

Hợp nhất khi: document analysis (hỏi nhiều câu về cùng một tài liệu lớn) · iterative editing (base content giữ nguyên, chỉ refine từng chỗ). Đây chính là lời giải cho bài toán chi phí tích luỹ ~O(n²) của hội thoại dài đã bàn ở đầu khoá.

Góc Delivery Manager. (1) Caching là "prompt caching" đã nhắc suốt khoá — giờ mới mổ ruột. Nó không đổi chất lượng câu trả lời, chỉ đổi kinh tế: cùng một pipeline RAG/hội thoại, bật cache đúng chỗ (tài liệu nền, system prompt dài, few-shot examples) là cắt được kha khá tiền và latency. (2) ⭐ Nhớ đúng ranh giới cache: chỉ phần preprocessing input được lưu, không phải câu trả lời — nên nó tăng tốc phần đọc lại tài liệu, chứ không phải "trả lời sẵn". (3) ⚠️ Cache 1 giờ + cần lặp lại thường xuyên là điều kiện kích hoạt: batch các câu hỏi về cùng tài liệu trong cùng phiên/gần nhau thì mới ăn cache; hỏi lai rai cách nhau cả buổi thì cache hết hạn, chẳng lợi gì.

Cache breakpoints — bật cache thủ công, đặt điểm cắt

TL;DR mini. Cache không tự bật — anh phải tự đặt một cache breakpoint vào một block. Mọi thứ tính từ đầu tới VÀ BAO GỒM breakpoint được cache; phần sau breakpoint xử lý bình thường. Cache chỉ tái dùng nếu nội dung tới breakpoint giống hệt — thêm một chữ "please" cũng invalidate hết. Cách đặt: dùng dạng longhand của text block (shorthand không có chỗ) rồi thêm cache_control: {"type": "ephemeral"}. Cache được cả system prompts · tool definitions · image blocks · tool_use / tool_result — trong đó system + tools là ứng viên tốt nhất (hiếm đổi). Thứ tự xử lý: tools → system → messages; tối đa 4 breakpoints; nội dung phải ≥ 1024 token (tính tổng, không phải từng block).

1. Không tự động — phải đặt breakpoint. Công xử lý message không được cache mặc định. Anh tự gắn một cache breakpoint vào block muốn chốt. Quy tắc: cache toàn bộ từ đầu → tới và gồm cả breakpoint; content sau đó không cache.

2. Phải dùng dạng longhand + cache_control. Dạng shorthand (chuỗi text trần) không có chỗ để gắn cache; phải viết dạng expanded:

{
    "type": "text",
    "text": "<nội dung dài, ổn định...>",
    "cache_control": {"type": "ephemeral"}   # ← đây là cache breakpoint
}

3. Phải GIỐNG HỆT mới ăn cache. Ở request sau, nội dung tới breakpoint phải identical. ⚠️ Thay đổi nhỏ nhất — thêm chữ "please", đổi một dấu — cũng invalidate cache → Claude xử lý lại từ đầu. Đây là bẫy lớn nhất: cache dựa trên so khớp y hệt, không phải "gần giống".

4. Cross-message caching. Breakpoint span được nhiều message và nhiều loại. Đặt breakpoint ở message sau thì tất cả message trước (user, assistant…) đều nằm trong phần cache → tiện cache cả context hội thoại tới một điểm.

5. Cache được nhiều thứ, không chỉ text. Gắn breakpoint được vào: system prompts · tool definitions · image blocks · tool_use / tool_result blocks. ⭐ System prompt và tool definitions là ứng viên cache tốt nhấthiếm khi đổi giữa các request — thường là chỗ ăn lợi nhiều nhất.

6. Thứ tự xử lý & số breakpoint. Claude xử lý theo thứ tự cố định: tools → system prompt → messages. Hiểu thứ tự này để đặt breakpoint đúng chỗ. Tối đa 4 cache breakpoints/request — vd cache tools, rồi thêm breakpoint giữa chừng lịch sử hội thoại, linh hoạt khi các phần đổi khác nhịp.

7. Ngưỡng tối thiểu 1024 token. Nội dung phải ≥ 1024 token mới đủ điều kiện cache — và đây là TỔNG mọi message/block được cache, không phải từng block riêng. Một câu "Hi there!" không đủ; prompt thật dài (hoặc nhân bản nội dung) mới vượt ngưỡng.

Góc Delivery Manager. ⭐ Công thức thực chiến: cache cái ổn định, để breakpoint ngay trước cái hay đổi. System prompt + tool schema + tài liệu nền (RAG context) hầu như bất biến → nhét hết vào trước breakpoint; câu hỏi mới của user để sau. Nhờ thứ tự tools → system → messages, đặt breakpoint cuối phần system là cache gọn cả tools + system. ⚠️ Kỷ luật vàng: đừng để chuỗi cached bị "nhiễu" — một biến động nhỏ (timestamp, lời chào, thứ tự field) trong phần cached là mất trắng cache; giữ phần đầu prompt tĩnh tuyệt đối, mọi thứ động đẩy xuống sau breakpoint.

Triển khai cache thực tế — tools · system · đọc usage

TL;DR mini. Cache tool schemas: gắn cache_control vào tool CUỐI trong list (nên copy list + tool cuối, đừng sửa gốc — tránh vỡ nếu sau này reorder). Cache system prompt: đổi từ stringlist chứa một text blockcache_control. Đọc kết quả qua usage: request đầu ra cache_creation_input_tokens (ghi), follow-up ra cache_read_input_tokens (đọc). ⚠️ Cực nhạy — đổi 1 ký tự trong tools/system là invalidate cả component. Thứ tự tools → system → messages cho partial cache: đổi system nhưng giữ tools → read phần tools + write phần system mới (chỉ trả tiền phần thật đổi). Ứng viên vàng: system prompt lớn (~6K token) · tool schema (~1.7K token) · message lặp lại.

Cache tool schemas — gắn vào tool cuối, và COPY. Vì thứ tự xử lý là tools trước, đặt breakpoint ở tool cuối sẽ cache trọn khối tools:

if tools:
    tools_clone = tools.copy()               # copy list, không đụng gốc
    last_tool = tools_clone[-1].copy()        # copy tool cuối
    last_tool["cache_control"] = {"type": "ephemeral"}
    tools_clone[-1] = last_tool
    params["tools"] = tools_clone

Vì sao copy chứ không sửa thẳng tools[-1]? Để không đụng vào tool definitions gốc — nếu sau này anh reorder list tools, việc đã cắm cache_control vào bản gốc sẽ gây lỗi/khó lường. Sửa trực tiếp chạy được nhưng rủi ro; copy là an toàn.

Cache system prompt — đổi string thành list block. String trần không có chỗ gắn cache; bọc thành list một text block:

if system:
    params["system"] = [
        {
            "type": "text",
            "text": system,
            "cache_control": {"type": "ephemeral"}
        }
    ]

Đọc usage để biết cache có ăn không. Response trả về các field cho thấy chuyện gì xảy ra:

Tình huống Field trong usage
Request đầu (ghi cache) cache_creation_input_tokens=1772
Follow-up (đọc cache) cache_read_input_tokens=1772
Đổi nội dung cache_creation mới xuất hiện (ghi lại)

⚠️ Cache cực nhạy: đổi chỉ một ký tự trong tools hoặc system prompt là invalidate toàn bộ cache của component đó.

Partial cache nhờ thứ tự tools → system → messages. Có thể đặt nhiều breakpoint trong một request. Vì các component cache độc lập theo thứ tự, nếu anh đổi system prompt nhưng giữ nguyên tools → sẽ thấy partial cache read (phần tools tái dùng) + cache write (phần system mới). ⭐ Granular caching: chỉ trả tiền xử lý phần thật sự thay đổi, phần bất biến vẫn ăn cache.

Góc Delivery Manager. (1) usage là "đồng hồ điện" của cache — muốn biết cache có thật sự ăn không thì đừng đoán, hãy log cache_creation_input_tokens vs cache_read_input_tokens: thấy toàn creation lặp lại là cache không hit (thường do content bị đổi ngầm). (2) Copy trước khi gắn cache_control là thói quen sạch — đúng tinh thần "không mutate cấu trúc dùng chung" mà anh vốn giữ trong code; một dev vô tình reorder tools là vỡ nếu ghim cache vào bản gốc. (3) ⭐ Partial cache là chỗ tiết kiệm tinh tế nhất: tách tools (siêu ổn định) và system (đôi khi tinh chỉnh) thành 2 breakpoint → sửa system vẫn giữ được cache tools, không mất trắng cả cụm.

Files API & Code Execution — giao việc tính toán cho Claude

TL;DR mini. Files API = thay vì nhét base64 vào từng message, upload file trước (một API call riêng) → nhận file metadata + file ID → tham chiếu ID ở message sau (hợp khi dùng lại nhiều lần / file lớn). Code execution = server-based tool (không cần tự viết implementation, như web search) cho Claude chạy Python trong Docker container cô lập — ⚠️ KHÔNG có network (không gọi API ngoài), chạy được nhiều lần, kết quả Claude tự đọc & diễn giải. ⭐ Vì container không có mạng, Files API là đường chính đưa data VÀO/RA: upload CSV → đính container_upload block (file_id) → Claude viết & chạy code phân tích → sinh output (plot) → tìm block code_execution_output lấy file ID → download_file().

Files API — upload trước, tham chiếu bằng ID. Ba bước:

  1. Upload file (image/PDF/text…) qua một API call riêng.
  2. Nhận về file metadata object chứa file ID duy nhất.
  3. Tham chiếu file ID trong các message sau, thay vì nhét raw data.

→ Hợp khi: dùng lại cùng file nhiều lần, hoặc file lớn nhét vào mỗi request thì cồng kềnh.

Code execution — built-in tool chạy Python phía server. Giống web search ở chỗ không cần anh viết implementation — chỉ đưa schema, Claude tự chạy. Đặc điểm môi trường:

  • Chạy trong isolated Docker container.
  • ⚠️ KHÔNG có network access — không gọi được API/URL ngoài.
  • Claude chạy code nhiều lần trong một hội thoại (lặp, xây dần phân tích).
  • Kết quả được Claude đọc & diễn giải cho câu trả lời cuối.

Schema: {"type": "code_execution_20250522", "name": "code_execution"}.

Ghép hai cái — vì sao cần nhau. ⭐ Vì Docker không có mạng, không thể tự tải data về → Files API là đường chính để đưa data vào (upload) và lấy kết quả ra (download). Workflow điển hình:

  1. Upload file data (vd CSV) qua Files API.
  2. Đính container_upload block (kèm file ID) vào message.
  3. Bảo Claude phân tích.
  4. Claude viết & chạy code xử lý file.
  5. Claude sinh output (vd plot) → anh download về.
file_metadata = upload('streaming.csv')

add_user_message(messages, [
    {"type": "text", "text": "Run a detailed analysis to determine major drivers of churn. Your final output should include at least one detailed plot."},
    {"type": "container_upload", "file_id": file_metadata.id},
])

chat(messages, tools=[{"type": "code_execution_20250522", "name": "code_execution"}])

Đọc response — nhiều loại block. Khi Claude dùng code execution, response gồm:

  • Text blocks — phân tích & giải thích của Claude.
  • Server tool use blocks — chính đoạn code Claude quyết định chạy.
  • Code execution tool result blocksoutput khi chạy code.

Claude có thể chạy nhiều vòng trong một response, mỗi vòng gồm code + kết quả, xây dần phân tích.

Tải file Claude sinh ra. Claude tạo được file (plot/report) lưu trong container. Tìm block type: "code_execution_output" (chứa file ID của nội dung sinh ra) rồi:

download_file("file_id_from_response")

Không chỉ phân tích dữ liệu. Ghép Files API + code execution mở ra: image processing · document parsing/transformation · mathematical computation & modeling · report generation có format riêng. Cốt lõi: giao việc tính toán phức tạp cho Claude, còn anh giữ kiểm soát input/output qua Files API — Claude thành trợ lý code chạy thật & lặp được trên lời giải.

Góc Delivery Manager. (1) Đây là "code interpreter" của Claude — biến nó từ "trả lời bằng chữ" thành "chạy thật, ra file". Ca churn-analysis (upload CSV → Claude tự EDA + vẽ plot → tải về) là mẫu tự động hoá report mình gặp hoài, giờ khỏi cắm pandas/matplotlib tay. (2) ⚠️ "No network" là ranh giới bảo mật, không phải hạn chế phiền — nó đảm bảo code Claude chạy không rò dữ liệu ra ngoài / không gọi lung tung; mọi data ra-vào phải qua Files API mà anh kiểm soát, rất hợp dữ liệu nhạy cảm của khách. (3) Files API tách data khỏi prompt — file lớn không còn phình mỗi request (đỡ token, đỡ đụng context window), đúng bài toán cost/context xuyên suốt khoá; upload một lần, tham chiếu ID nhiều lần.


Phần tiếp theo: MCP trong Claude API — dựng server FastMCP, client, resources & prompts

Nguồn: Building with the Claude API (Anthropic Academy) — Copyright Anthropic. Phần đề thi thử cho khoá này nằm ở tab "Đề thi thử" trên trang tổng quan khoá.