Công nghệ · Code, kiến trúc, AI, và những thứ giữa các tầng abstraction.

Prompt caching: kiến trúc 4 tầng cho ứng dụng AI

Tiết kiệm 93% token không phải bằng cách viết ít, mà bằng cách viết đúng tầng. Phân tích cách tổ chức prompt cho hệ thống multi-tenant.

Khi build MKT Content AI — một SaaS multi-tenant generate marketing content cho doanh nghiệp Việt — tôi gặp một vấn đề điển hình: cost của Anthropic API tăng tuyến tính với traffic, trong khi nhiều phần của prompt là static.

Vấn đề

Mỗi request đến Claude API có cấu trúc:

{
  system: "Bạn là chuyên gia content marketing...", // ~2000 tokens, static
  tools: [/* 12 tools với schema dài */],           // ~1500 tokens, static per tenant
  messages: [
    { role: "user", content: "Viết bài Facebook về..." }
  ]
}

Trong mỗi tenant, system prompt và tools schema gần như không đổi. Nhưng mỗi request lại gửi toàn bộ — Anthropic charge full token mỗi lần.

Với 1000 requests/ngày, system + tools chiếm ~3500 token × 1000 = 3.5M tokens/ngày chỉ để gửi cùng một nội dung.

Giải pháp: 4 tầng cache

Anthropic API có cache_control cho phép mark một phần prompt là cacheable. Cache TTL 5 phút (default) hoặc 1 giờ (extended). Cache hit chỉ tính 10% cost so với cache miss.

Tôi tổ chức thành 4 tầng theo mức độ ổn định:

Tầng 1: Global system (TTL: 1 giờ)

Nội dung không bao giờ thay đổi giữa tenants:

{
  type: "text",
  text: SYSTEM_PROMPT_BASE, // "Bạn là Claude, trợ lý..."
  cache_control: { type: "ephemeral", ttl: "1h" }
}

Tầng 2: Tenant config (TTL: 1 giờ)

Mỗi tenant có cấu hình riêng (brand voice, style guide, audience profile):

{
  type: "text",
  text: `Tenant: ${tenant.name}\nBrand voice: ${tenant.voice}...`,
  cache_control: { type: "ephemeral", ttl: "1h" }
}

Tầng 3: Tools schema (TTL: 5 phút)

Tools schema cũng cacheable. Đặt sau system prompt:

{
  tools: [
    ,
    ,
    /* tool 1 */ /* tool 2 */ {
      /* tool cuối */
      cache_control: { type: 'ephemeral', ttl: '5m' },
    },
  ];
}

Lưu ý: chỉ cần đặt cache_control ở tool CUỐI CÙNG — Anthropic cache mọi thứ TRƯỚC marker đó.

Tầng 4: Conversation history (no cache)

Phần này thay đổi mọi request, không cache.

Kết quả thực tế

Sau khi triển khai trên production:

MetricTrướcSauTiết kiệm
Input tokens/request3,84728792.5%
Cost/request$0.0115$0.000893%
Latency p502.1s1.4s33%

Pitfalls đã gặp

1. Cache key sensitive với whitespace

Một thay đổi nhỏ trong template string — thêm space, đổi newline — = cache miss. Phải hash cache key trước deploy.

2. Cache cold start tốn 25% phụ phí

Lần đầu cache miss, Anthropic charge 1.25× normal token cost (để build cache). Sau đó hit chỉ 0.1×. Vẫn lãi lớn nhưng phải tính vào ROI.

3. Multi-tenant cache pollution

Nếu mày dùng chung cache key giữa tenants → leak data. Phải include tenant_id vào cache namespace.

Code mẫu

Đây là buildCachedPrompt final:

async function buildCachedPrompt(tenant: Tenant, userMessage: string): Promise<MessagesRequest> {
  return {
    model: 'claude-sonnet-4-5',
    max_tokens: 4096,
    system: [
      {
        type: 'text',
        text: SYSTEM_BASE,
        cache_control: { type: 'ephemeral', ttl: '1h' },
      },
      {
        type: 'text',
        text: buildTenantContext(tenant),
        cache_control: { type: 'ephemeral', ttl: '1h' },
      },
    ],
    tools: [
      ...TOOLS.slice(0, -1),
      {
        ...TOOLS.at(-1)!,
        cache_control: { type: 'ephemeral', ttl: '5m' },
      },
    ],
    messages: [{ role: 'user', content: userMessage }],
  };
}

Kết

Prompt caching là một “free lunch” hiếm có trong tech — không có trade-off thực sự nào ngoài việc phải hiểu rõ thứ tự cache hierarchy. Nếu ứng dụng AI của mày chưa dùng nó, mày đang đốt tiền.

Bài tiếp theo tôi sẽ viết về multi-tenant RLS với PostgreSQL — vấn đề bảo mật data isolation song hành với prompt caching trong kiến trúc SaaS.


Tham khảo: Anthropic Prompt Caching docs