SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-23

Cách kiểm soát chi phí AI agent trên VPS chạy 24/7

Agent chạy nền có thể âm thầm tốn token qua 8.640 lần chạy mỗi tháng. Dùng hard cap, task budget, prompt cache, batching và log usage để chặn chi phí.

Cách ngăn AI agent chạy liên tục làm tăng chi phí

Kiểm soát chi phí AI agent trên VPS (virtual private server) là đặt các giới hạn trước khi agent khởi chạy, vì không có ai theo dõi đồng hồ chi phí trong lúc agent chạy. Giới hạn mỗi response bằng max_tokens, giới hạn số vòng lặp trong code của bạn, cache phần prompt không thay đổi và ghi log các số liệu usage của mọi response để biết job nào tiêu tốn nhiều nhất. Chi phí thuê server là mức cố định hằng tháng. Model API tính phí theo token, còn một vòng lặp không có người giám sát rất dễ âm thầm tiêu tốn token.

Phần này giả định bạn đã có một agent gọi Messages API từ một máy bạn sở hữu. Xây dựng AI agent với Claude trên VPS trình bày phần triển khai agent.

Tại sao agent không có giám sát có cấu trúc chi phí khác

Một session tương tác có người tham gia. Khi model đi sai hướng hoặc đọc một log dài 40,000 dòng, người đang theo dõi sẽ dừng nó lại. Agent không có giám sát không có cơ chế phanh như vậy: nó chạy đến khi vòng lặp kết thúc, sau đó timer lại khởi động nó.

Tần suất là hệ số nhân mà nhiều người bỏ qua. Một job chạy theo lịch 5 phút sẽ chạy 288 lần mỗi ngày và khoảng 8,640 lần mỗi tháng. Dù mỗi lần chạy tốn bao nhiêu, bạn phải nhân chi phí đó lên. Nhiều agent "luôn bật" thực ra không cần phải chạy liên tục. Chúng chỉ cần phản hồi trong một số phút nhất định. Đó là một schedule.

Agent cũng phát sinh chi phí cho những thành phần mà cửa sổ chat không có.

  • Tool definition được gửi kèm trong mọi request. System prompt cho việc sử dụng tool tốn 290 token trên Claude Opus 4.8 với tool_choice của auto hoặc none, và 410 với any hoặc tool. Bash tool cộng thêm 325 token. Mỗi MCP server bạn gắn vào đều thêm schema của nó vào phần này; MCP là viết tắt của model context protocol.
  • Kết quả từ tool là input token. Một command in ra 8,000 dòng sẽ đưa 8,000 dòng đó vào request tiếp theo và mọi request sau đó trong lượt chạy đó.
  • Các trang được fetch là input token. Một trang web trung bình 10 kB tương đương khoảng 2,500 token, còn một research PDF 500 kB tương đương khoảng 125,000 token. max_content_tokens chỉ truncate phần text, vì nó "áp dụng cho nội dung text, không áp dụng cho nội dung nhị phân như PDF". Thay vào đó, hãy giới hạn PDF bằng max_usesallowed_domains.
  • Web search được tính phí theo từng search, ở mức $10 cho 1,000 search, bất kể có bao nhiêu kết quả trả về. Search bị lỗi không bị tính phí.

Không khoản nào trong số này đắt nếu chỉ phát sinh một lần. Tất cả đều đắt khi lặp lại 8,640 lần.

Giới hạn cứng và giới hạn mềm giải quyết các vấn đề khác nhau

max_tokens được thực thi. Đây là giới hạn cứng cho tổng output của một request, bao gồm cả phần suy luận và văn bản phản hồi. Claude không bao giờ tạo output vượt quá giới hạn này, và model không nhìn thấy giá trị đó. Khi chạm giới hạn, request trả về stop_reason: "max_tokens" cùng một câu trả lời bị cắt ngắn. Điểm cần lưu ý với agent là mỗi request trong vòng lặp tool-use có max_tokens riêng. Vì vậy, giới hạn này chỉ áp dụng cho một response, không áp dụng cho toàn bộ task. Mười lần gọi tool với giới hạn 4,000 tạo thành mức trần 40,000 token cho lượt đó.

Task budget chỉ mang tính khuyến nghị. task_budget nằm trong output_config và cho model biết số token được phân bổ cho toàn bộ vòng lặp agentic, bao gồm phần suy luận, các lần gọi tool, kết quả tool và output.

resp = client.beta.messages.create(
    model="claude-opus-4-8",
    max_tokens=4096,
    betas=["task-budgets-2026-03-13"],
    output_config={"task_budget": {"type": "tokens", "total": 64000}},
    messages=messages,
)

"Task budget là gợi ý mềm, không phải giới hạn cứng." Claude có thể vượt qua mức này trong một hành động đang thực hiện, còn giới hạn output được thực thi vẫn là max_tokens. "Chỉ model nhìn thấy bộ đếm này", và response không có trường cho biết ngân sách còn lại. Giá trị task_budget.total tối thiểu được chấp nhận là 20,000 token; giá trị thấp hơn sẽ trả về lỗi 400. Nếu budget quá nhỏ so với công việc, model có thể có hành vi giống như từ chối. Khi đó, model sẽ thu hẹp phạm vi task hoặc dừng sớm.

Có một chi tiết làm tăng chi phí thay vì tiết kiệm. Nếu client của bạn giảm task_budget.remaining sau mỗi request follow-up, giá trị thay đổi sẽ làm mất hiệu lực của mọi cached prefix có chứa giá trị đó. Hãy đặt giá trị này một lần trong request đầu tiên.

Task budget đang ở giai đoạn beta trên Claude Fable 5, Claude Opus 4.8 và Claude Opus 4.7. Claude Sonnet 5 và Claude Haiku 4.5 được liệt kê là Not supported, còn task budget không áp dụng cho Claude Code. Vì vậy, một session Claude Code được detach trong tmux phụ thuộc vào việc quản lý session đúng cách.

Giới hạn thứ ba nằm trong Claude Console: cấp cho agent một workspace riêng, sau đó đặt giới hạn chi tiêu hàng tháng và giới hạn rate theo phút cho workspace đó. "Bạn không thể đặt giới hạn trên Default Workspace", và "giới hạn cấp Organization luôn được áp dụng, kể cả khi tổng các giới hạn của workspace cao hơn". Hãy thêm thông báo chi tiêu để nhận cảnh báo khi đạt ngưỡng, trước khi chạm giới hạn.

Chọn model theo từng job và mức effort thực sự thay đổi điều gì

Việc chọn model là quyết định riêng cho từng job. Tính đến tháng 7 năm 2026, chi phí cho mỗi triệu token, lần lượt là input rồi output: Claude Fable 5 ở mức $10 và $50, Claude Opus 4.8 và Opus 4.7 ở mức $5 và $25, Claude Sonnet 5 ở mức $3 và $15, Claude Haiku 4.5 ở mức $1 và $5. Hiện Sonnet 5 vẫn có giá thấp hơn mức niêm yết, vì “Mức giá giới thiệu $2/$10 cho mỗi triệu token input/output có hiệu lực đến hết ngày 31 tháng 8 năm 2026”. Một bước chỉ phân loại các dòng log thì không cần Opus. Cũng không có hạn mức miễn phí để hấp thụ lịch chạy dày, vì Claude API không có free tier ngoài khoản credit nhỏ được cấp khi đăng ký.

Effort là cần gạt thứ hai. output_config.effort chấp nhận low, medium, high, xhighmax, còn giá trị mặc định là high, nên đặt rõ high cũng giống như bỏ qua tham số này. Effort thấp hơn không chỉ rút ngắn quá trình reasoning: tài liệu cho biết Claude sẽ thực hiện ít tool call hơn và gộp nhiều thao tác vào một lần. Với một agent, đây mới là khoản tiết kiệm lớn hơn, vì tránh được một tool call nghĩa là tránh được toàn bộ một request.

Điểm dễ mắc là effort xung đột với cache. Thay đổi giá trị này giữa các request sẽ làm mất hiệu lực của prompt caching. Trong ví dụ của tài liệu, request 2 báo cache_read_input_tokens: 3546; request 3, với effort đổi từ high sang medium, báo cache_creation_input_tokens trên tổng 3546 và cache_read_input_tokens trên tổng 0. Vì vậy, hãy thay đổi effort giữa các workload, không thay đổi trong cùng một cached conversation. Muốn điều chỉnh độ sâu mà không phá cache, hãy làm việc đó trong prompt: một dòng như “Trả lời trực tiếp, không cần suy xét.” trong user message mới nhất sẽ giữ nguyên các breakpoint trước đó.

Thinking token được tính theo mức giá output và tính vào max_tokens, nên câu trả lời bị cắt thường có nghĩa là thinking đã dùng hết budget. Đọc usage.output_tokens_details.thinking_tokens để biết số lượng. Những gì thực sự tạo nên chi phí token của Claude sẽ phân tích chi tiết cách bộ đếm này hoạt động.

Cache phần prefix ổn định và đừng vô tình làm hỏng nó

Mỗi lần ghi cache có chi phí bằng 1.25 lần giá input cơ bản đối với cache năm phút và 2 lần đối với cache một giờ. Mỗi lần đọc cache có chi phí bằng 0.1 lần. Vì vậy, “caching có lợi sau chỉ một lần đọc cache với thời lượng 5 phút (ghi ở mức 1.25x), hoặc sau hai lần đọc cache với thời lượng 1 giờ (ghi ở mức 2x)”.

Một dòng giải thích vì sao cách này phù hợp với agent chạy liên tục: “Cache được refresh miễn phí mỗi khi nội dung đã cache được sử dụng.” Một job chạy mỗi hai phút với cache năm phút sẽ giữ prefix luôn warm cả ngày chỉ với một lần ghi.

Có ba cách làm mất cache mà không nhận ra.

Prefix thay đổi. “Các cache prefix được tạo theo thứ tự sau: tools, system, rồi messages.” Bất kỳ thay đổi nào ở một byte trước đó trong thứ tự này đều làm mất hiệu lực của mọi phần phía sau, còn việc chỉnh sửa các tool definition sẽ làm mất toàn bộ cache. Lỗi tự gây ra phổ biến nhất là đưa timestamp hoặc run id vào system prompt: khi đó mỗi request đều có prefix khác nhau, ghi một entry mới với chi phí 1.25x và không đọc lại được gì. Dấu hiệu là usage.cache_read_input_tokens luôn bằng 0 trong các lần gọi trông giống hệt nhau. Chuyển phần text thay đổi liên tục vào user message mới nhất.

Prefix quá ngắn. Mỗi model có độ dài tối thiểu có thể cache. Nếu ngắn hơn mức này, request sẽ được xử lý mà không caching và “không trả về lỗi”. Các con số gồm 1,024 token trên Claude Opus 4.8 và Claude Sonnet 5, và 4,096 token trên Claude Haiku 4.5. Vì vậy, chuyển một job từ Sonnet sang Haiku có thể âm thầm tắt caching.

Conversation vượt quá lookback. “Cửa sổ lookback là 20 block.” Hệ thống kiểm tra tối đa 20 vị trí tại mỗi breakpoint rồi dừng. Trong ví dụ được ghi rõ, một turn chứa 35 block và có breakpoint tại block 35 sẽ kiểm tra các block từ 35 đến 16. Entry của turn trước ở block 15 nằm ngoài cửa sổ, nên không có cache hit. Một agent appending nhiều block tool-use và tool-result trong mỗi turn sẽ vượt quá 20 block chỉ sau hai hoặc ba turn. Mỗi request có bốn breakpoint, vì vậy hãy dành một breakpoint cho các message gần đây.

Chuyển mọi việc có thể chờ sang Batches API

"Toàn bộ usage được tính bằng 50% giá API tiêu chuẩn", áp dụng cho cả input và output. Xử lý batch là bất đồng bộ, "phần lớn batch hoàn tất trong chưa đến 1 giờ", và có kết quả khi mọi request đã hoàn tất hoặc sau 24 giờ, tùy điều kiện nào đến trước. Đây là thời gian thông thường, không phải cam kết.

Poll processing_status cho đến khi giá trị là ended. Các request trả về errored, canceled hoặc expired không bị tính phí. Nếu bạn đặt giới hạn chi tiêu, cần lưu ý rằng "batch có thể vượt nhẹ giới hạn chi tiêu đã cấu hình cho Workspace".

Các mức giảm giá được cộng dồn. Vì một batch có thể chạy lâu hơn 5 phút, tài liệu khuyến nghị dùng cache 1 giờ cho các batch dùng chung context. Hãy tách luồng xử lý: mọi việc mà người dùng hoặc webhook phải chờ thì giữ trên live path; digest chạy hằng đêm hoặc việc phân loại log của ngày hôm qua có thể đưa vào batch với chi phí bằng một nửa.

Ghi log tất cả các trường usage của response vào kho dữ liệu riêng

Bạn không thể phân bổ khoản chi phí mà mình chưa ghi lại. Mỗi response đều cho biết nó đã tốn bao nhiêu.

u = resp.usage
row = {
    "job": job_name,
    "model": resp.model,
    "uncached_input": u.input_tokens,
    "cache_write": u.cache_creation_input_tokens,
    "cache_read": u.cache_read_input_tokens,
    "output": u.output_tokens,
    "stop_reason": resp.stop_reason,
}

Thêm một dòng cho mỗi API call vào một file JSON-lines và gắn tên job vào dòng đó. Một tuần sau, bạn có thể biết job nào thực sự phát sinh chi phí và job nào chỉ tạo nhiều hoạt động. Hãy theo dõi cache_read: một cột toàn số 0 là lỗi chi phí phổ biến nhất trong agent tự host.

Một trường dễ bị đọc sai. input_tokens chỉ đếm số token sau breakpoint cache cuối cùng, nên kích thước prompt thực tế là total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens. Agent báo input_tokens: 400 với một prompt lớn không có nghĩa là nó rẻ: phần còn lại được lấy từ cache.

Hãy đếm trước khi gửi. Việc đếm token là miễn phí và có rate limit riêng với việc tạo message, nên dùng count_tokens để từ chối attachment quá lớn thay vì phải trả phí rồi mới phát hiện. Kết quả chỉ là ước tính, vì vậy hãy đo lại theo từng model và không dùng lại kết quả đếm từ tokenizer của vendor khác. Claude Opus 4.7 và các model Opus mới hơn, Claude Fable 5 và Claude Sonnet 5 dùng tokenizer mới, tokenizer này "tạo ra nhiều hơn khoảng 30% token với cùng một đoạn văn bản". Claude Sonnet 4.6 và các phiên bản cũ hơn, trong đó có Claude Haiku 4.5, dùng tokenizer trước đây.

Để xem thông tin chính thức, Admin API báo cáo usage tại https://api.anthropic.com/v1/organizations/usage_report/messages và cost tại https://api.anthropic.com/v1/organizations/cost_report. Cả hai đều nhận admin key (sk-ant-admin01-...) dưới dạng x-api-key: $ANTHROPIC_ADMIN_KEY cùng với anthropic-version: 2023-06-01, và chấp nhận bucket_width=1d, group_by[]=modelapi_key_ids[]=. Một hạn chế là: "Admin API không khả dụng cho tài khoản cá nhân."

Tham số cuối là một cách đơn giản để phân bổ chi phí: cấp cho mỗi job một API key riêng, lọc bằng api_key_ids[], rồi chia báo cáo theo từng key bằng group_by[]=api_key_id. Bộ lọc dùng dạng số nhiều, còn chiều grouping dùng dạng số ít. Lưu các key trong environment thay vì trong code, giống cách một ứng dụng Claude API đầu tiên trên VPS xử lý chúng.

Giới hạn vòng lặp, vì sẽ không có gì khác làm việc đó

Số lần lặp có giới hạn là bắt buộc ở đây. Bạn tự viết vòng lặp, nên bạn cũng phải tự quản lý bộ đếm:

for step in range(MAX_STEPS):          # MAX_STEPS = 12, never "while True"
    resp = client.messages.create(...)
    if resp.stop_reason != "tool_use":
        break
else:
    log.warning("job %s hit MAX_STEPS=%d, giving up", job_name, MAX_STEPS)

Không giới hạn nào ở trên làm thay việc đó: max_tokens chỉ giới hạn một response, còn model chỉ được thông báo về ngân sách của task. Một sản phẩm hosted sẽ dừng tại đây, giống như giới hạn số lần gọi tool của Claude trong một lượt kết thúc session đã gọi quá nhiều lần. Nhưng một vòng lặp do bạn tự viết sẽ không có cơ chế chặn như vậy cho đến khi bạn thêm vào.

Đặt thêm một cơ chế dừng bên ngoài process. Chạy job bằng systemd timer thay vì một process chạy liên tục, rồi đặt RuntimeMaxSec= trong service unit của nó. Với RuntimeMaxSec=600, một lần chạy bị treo sẽ bị kill sau 10 phút thay vì tiếp tục chạy cho đến khi bạn phát hiện. Chạy một chương trình dưới dạng systemd service và timer trình bày các unit file tương ứng. Dùng journalctl -u triage-agent.service --since "1 hour ago" để xem một lần chạy đã thực hiện những gì.

Đồng thời cũng phải giới hạn số lần retry, vì một handler retry vô hạn sẽ tính phí cho mọi lần thử. Lỗi 429 hoặc 500 có thể retry vài lần với backoff. Lỗi 400 thì không nên retry, vì cùng một request sẽ tiếp tục fail theo cùng một cách.

Kiểm soát chi phí AI agent bắt đầu bằng việc đọc các số liệu của chính bạn

Không ai có thể cho bạn biết một agent chạy liên tục sẽ tốn bao nhiêu, vì chi phí bằng số token cho mỗi lần chạy nhân với số lần chạy mỗi ngày, và cả hai yếu tố này đều do bạn quyết định. Hãy chạy agent một lần, đọc dòng usage bạn đã ghi lại, rồi nhân với lịch chạy. Kiểm tra báo cáo chi phí sau 2 ngày và đối chiếu với phép tính đó. Khi hai con số không khớp, nguyên nhân gần như luôn là cache bị lỗi hoặc một loop chạy lâu hơn bạn dự tính.

Nội dung này giả định bạn dùng API key, vì agent là program của chính bạn gọi Messages API. Đối với công việc tương tác cá nhân, gói Claude nào phù hợp với cách bạn làm việc sẽ giải thích phần subscription. Mọi mức giá và giới hạn ở đây đã được đối chiếu với tài liệu của Anthropic vào tháng 7 năm 2026, vì vậy hãy đọc lại trang pricing trước khi lập ngân sách.

FAQ

Chi phí chạy một AI agent luôn bật trên VPS là bao nhiêu?

Có 2 khoản chi phí và chỉ 1 khoản có thể dự đoán được. Server có giá cố định hằng tháng. Model API tính phí theo token, nên chi phí bằng số token một lần chạy tiêu thụ nhân với tần suất chạy. Anthropic không công bố con số nào cho agent luôn bật được self-host, nên hãy xem mọi con số được nêu chỉ là ước tính. Ghi log usage từ một lần chạy thực tế rồi nhân với lịch chạy của bạn.

Sự khác nhau giữa max_tokens và ngân sách tác vụ là gì?

max_tokens được áp dụng bắt buộc và model không nhìn thấy giá trị này. Nó giới hạn output của một request, bao gồm cả phần suy luận, và khi chạm giới hạn này sẽ nhận stop_reason: "max_tokens". Ngân sách tác vụ thì ngược lại: model được thông báo con số này và điều chỉnh agentic loop theo đó, nhưng “Task budgets are a soft hint, not a hard cap”; giới hạn thực tế vẫn là max_tokens.

Tại sao cache_read_input_tokens của agent luôn bằng 0?

Vì prefix thay đổi giữa các lần gọi hoặc quá ngắn để cache. Nguyên nhân thường gặp là timestamp hoặc run id được chèn vào system prompt: cache dùng prefix làm key, nên bất kỳ byte nào thay đổi cũng làm mất hiệu lực của toàn bộ phần đứng sau nó. Thay đổi định nghĩa tool hoặc giá trị effort cũng gây ra kết quả tương tự. Nếu không phải các nguyên nhân trên thì là do kích thước, vì prompt ngắn hơn không được cache và cũng không trả về lỗi.

Làm cách nào để ngăn AI agent lặp vô hạn?

Đếm số vòng lặp trong code của bạn và dừng ở một giá trị tối đa cố định, vì max_tokens chỉ giới hạn một response còn agent thực hiện nhiều vòng. Thêm giới hạn thời gian thực bên ngoài process: khởi chạy job bằng systemd timer với RuntimeMaxSec= được thiết lập, để một lần chạy bị treo sẽ bị kill đúng lịch. Đồng thời giới hạn số lần retry, vì mỗi lần retry đều bị tính phí.

Tôi có thể đặt giới hạn chi tiêu cho một Claude API key riêng lẻ không?

Giới hạn chi tiêu được tài liệu hóa áp dụng theo workspace, không áp dụng theo từng key. Vì vậy, hãy cấp cho agent một workspace riêng và đặt giới hạn chi tiêu hằng tháng tại đó. “You cannot set limits on the Default Workspace”. Thêm thông báo chi tiêu để nhận cảnh báo khi đạt ngưỡng trước. Để phân bổ chi phí, cấp cho mỗi job một key riêng, rồi nhóm báo cáo sử dụng bằng group_by[]=api_key_id.