以 MonkeyCode 為例,如何把免費推理服務拆分為三個平面
Draw Your Free Tier as Three Planes
Harper Xu 提出免費推理服務的三平面設計方法,把狀態、模型推理,以及跨越單次請求的工作分置處理。文章建議自有帳本先預留估算用量,超出上限便將工作排隊,批次前以 canary 檢查可達性,並令工作具冪等性,以便伺服器重啟後重播。MonkeyCode 是文中案例,提供免費模型存取及免費伺服器選項;文中稱撰寫時宣傳額度為 10M tokens,並提醒重新查看現行條款。
Free inference is not a socket you plug in. It is a plane you place in a diagram.
Sketch three planes before you write a client. One plane holds your state. One plane answers prompts. One plane runs work that outlives a single request.
If you cannot name which plane failed, you are not ready to ship on a free tier. Here is the review I would run on that sketch.
Constraints set the shape
Three constraints decide the design. The model behind a free endpoint can change without telling you. Throughput is shared, so latency is a distribution and not a number. The machine that runs your jobs is not yours to patch at 2 a.m.
A wallet mental model fails here. Budgets feel finite. Shared capacity is not yours to reason about. You need a pipeline view, not a balance.
None of those constraints are defects. They are inputs. Design against them, not around them.
MonkeyCode is the concrete case in this review. It offers free model access and a free server option, which map to the middle and last planes. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The advertised allowance is 10M tokens at the time of writing. Re-read the current terms before you size a batch.
One rule keeps the flow one-way
Your ledger owns state. The model plane owns nothing. The compute plane owns nothing you cannot rebuild.
That rule turns data flow into six steps. Create a job row, assemble the prompt, hold a token estimate, capture the response, store the artifact, trigger the run. Every step writes to your side of the fence. None of them read state back from the provider.
Now the part that saves you a night. Refuse work before you spend, not after.
# budget.py — starter, no dependencies, single-flight only
from dataclasses import dataclass
@dataclass
class Ledger:
ceiling: int
spent: int = 0
held: int = 0
def hold(self, estimate: int) -> bool:
if self.spent + self.held + estimate > self.ceiling:
return False
self.held = estimate
return True
def settle(self, actual: int) -> None:
self.held = 0
self.spent += actual
Call hold with a rough estimate from your prompt length. Call settle with the usage the response reports. If hold returns false, the job waits in a queue you can see.
That queue is the whole point. A refusal you can inspect beats a 429 you cannot.
Run it once with a tiny ceiling to prove the refusal path fires. You want that failure in a test, not in a demo.
Check the lane before the batch
Free endpoints move. Treat reachability as a measurement, not an assumption. Run a canary before a batch, never during one.
#!/usr/bin/env bash
set -euo pipefail
: "${MC_BASE_URL:?set MC_BASE_URL}"
: "${MC_API_KEY:?set MC_API_KEY}"
curl -sS --max-time 20 "$MC_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $MC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"reply with ok"}],"max_tokens":8}' \
| grep -q '"content"' && echo "canary: up" || { echo "canary: down"; exit 1; }
Adjust the path to your provider's shape. The check is not the point. The habit is. Gate the batch on the canary, and a bad afternoon becomes a delayed job instead of a half-written artifact.
Three blast radii, not one
Naming failure domains is cheap. Bounding them is the work. Draw a circle around each plane and ask what leaks out of it.
The ingress radius covers prompt assembly. A bad template corrupts every job downstream. Version the template and stamp the version on each row.
The model radius covers inference. A timeout here costs one held estimate, nothing more. Keep it that way by never letting a response write directly to your system of record.
The compute radius covers the free server. Restarts are weather, not incidents. Make every job idempotent so a restart replays cleanly and the artifact lands once.
| Plane | What breaks first | Blast radius | Next change |
|---|---|---|---|
| Ingress | Prompt template edit | Every queued job | Version and stamp |
| Model | Timeout or shape change | One held estimate | Fallback behind one interface |
| Compute | Restart mid-job | One artifact | Idempotent replay |
| Ledger | Concurrent writes | Silent overspend | Single-flight lock |
Read the table top to bottom. The last row is the one people skip. It is also the one that quietly spends your allowance while you sleep.
What I would change next
Move prompt assembly behind a versioned template first. Your ledger is only as honest as the thing that produced the estimate.
Then make the ledger durable and single-flight. The starter above assumes one writer. Real batch runners need a lock or a queue. Until then, keep concurrency at one and say so in the README.
Finally, put a fallback model behind the same interface. Do not build a router with five branches. Build one seam and change what sits behind it.
Measure queue depth weekly. If it grows, your estimate is wrong, not the model.
Who should not use this
Do not build on a free tier if you need pinned model versions in a contract. Do not send regulated data you cannot share with a third party. Do not accept cold starts if you sold an availability number. Pay for the guarantee instead.
Everyone else should start here. Sketch the planes, prove the refusal path, gate on a canary. Then move real work through it.
Try the free model access and the free server with one canary job, not your backlog. If the diagram survives that, it survives Tuesday.
來源:DEV Community · dev.to