预算与限额
Ongrid 强制一个全局按 UTC 日的 token 上限,跨所有 provider。默认无限; 一个 env 变量打开它:
ONGRID_LLM_DAILY_TOKEN_LIMIT=2000000 # 2 million tokens per UTC day<=0 禁用上限。一个值,不是按 provider —— 这是 MVP 范围。租户落地时它移 到按组织设置;这个旋钮留作兜底全局上限。
怎么接的
三块,在 internal/pkg/llm/:
// 1. The interface
type BudgetChecker interface {
Check(ctx context.Context, userID uint64, estPromptTokens int) error
Record(ctx context.Context, userID uint64, usage Usage) error
}
// 2. The MVP implementation
budget := llm.NewInMemoryBudget(cfg.LLM.DailyTokenLimit)
// 3. The eino callback that bridges to the graph kernel
handler := llm.NewBudgetCallbackHandler(budget, userID)graph-kernel 运行时把 callback handler 装到 eino callback 链里。每次 ChatModel OnStart:
- 估 prompt token:
len(text)/4(保守)。 BudgetChecker.Check(ctx, userID, estPromptTokens)。- 拒绝时 —— 在 context 里存
ErrBudgetExceeded,下游节点可以短路;后续 代码把它露出来。
OnEnd 时,实际 Usage.TotalTokens 记到当前 UTC 日的桶里。
ErrBudgetExceeded
// internal/pkg/llm/budget.go:37
func (b *InMemoryBudget) Check(ctx context.Context, userID uint64, estPromptTokens int) error {
if b.dailyLimit <= 0 {
return nil
}
b.mu.Lock()
defer b.mu.Unlock()
key := b.dayKey()
if b.used[key]+estPromptTokens > b.dailyLimit {
return ErrBudgetExceeded
}
return nil
}错误传播到:
- chat 发送 endpoint —— 返回 HTTP 429,body 是
{ "error": "budget_exceeded", "message": "..." },chat UI 内联渲染。 - RCA investigator worker —— 报告行落成
status=failed,status_reason="budget_exceeded"。 - translate 路径 —— 回退成 "translation unavailable (budget exceeded)", 显示原文。
InMemoryBudget 注意
MVP 实现是内存里的:
type InMemoryBudget struct {
mu sync.Mutex
dailyLimit int // tokens per UTC day; <=0 means unlimited
used map[string]int // key = "YYYY-MM-DD" (UTC)
now func() time.Time
}后果:
- 不持久化 —— manager 重启会重置当日计数。如果你真要重启后还硬卡每日, 换实现。
BudgetChecker接口是缝。 - 单进程 —— 如果你在多个 manager 之间挂负载均衡器(你还不该这么干, 但如果),每个有自己的计数。
- 全局,不分用户 ——
userID流过接口,所以未来一张 MySQLusage_daily表是直接换零件,但今天上限对所有人都是同一个数。
转单租户时延后了按用户后端;接口对多用户回来时前向兼容。
token 估计
BudgetCallbackHandler.OnStart 按字符数 / 4 估 prompt token。这故意保守 —— 真实分词按 provider / model 变,预算应当宁可拒边界调用也别超。
OnEnd 时,provider 返回的实际 Usage.TotalTokens 被记下来 —— 所以即使 估算偏了,预算追踪的还是 ground truth。
如果 provider 不返 token 计数(有些自定义端点不返),callback 回退到一个 response-meta 启发式;见测试里的 OnEndUsesResponseMetaFallback。
观察预算
curl -s localhost:9100/metrics | grep llm_budget
# llm_budget_daily_limit_tokens 2000000
# llm_budget_used_tokens_today 412847
# llm_budget_rejections_total 3指标由 BudgetCallbackHandler.Stats() 接。self-obs Prom dashboard 把它们 渲染成日度花销图加一条上限 80% 的告警。
给某个工作负载关闭
没有"给 investigator 关掉预算"的旋钮。RCA 撞上限了又宁愿它继续跑而不是 chat 的话,把上限抬上去 —— 它就是干这个的。备选方案(按工作负载配额)和 多租户一起暂缓了。