跳到正文
DEV Community· HaoPeng Zhang·· 2 小時前AI 評分63

n8n 成本帳本:我如何為每次工作流程執行標記成本

The Cost Ledger: How I Gave Every n8n Workflow Run a Price Tag

AI 導讀

作者為每次 n8n 工作流程中的 LLM 呼叫記錄 token 用量和美元成本,並每日按工作流程彙總支出。昨日支出達該工作流程自身 7 日中位數 2 倍時,系統會向 Slack 發出警報;一次重試政策變更觸發 3.1 倍警報,作者當天收緊重試條件。成本按 API 回傳的 token 用量和價格表計算,價格表可能隨供應商調價而過時;帳本可定位異常工作流程,但原因仍需人工排查。

正文

Eleven workflows calling LLMs, one monthly API bill, and nothing in between. When a prompt change tripled my model spend overnight, I found out thirty days later -- from the invoice. So I built a cost ledger: every LLM call in every workflow logs its tokens and its dollar cost, and a daily query tells me exactly which workflow is burning money. Here's the setup, the alert rule, and the incident that paid for it.

The shadow-run post covered how I test a new workflow version against live traffic. It mentioned a cost bound in passing -- fifty shadowed events, roughly two dollars of extra model spend. Two readers asked the obvious follow-up: how do you know it's two dollars? What's your actual LLM spend, and per what?

Honest answer at the time: I didn't know. I had a per-provider invoice and a gut feeling. That's not an answer, that's a shrug.

The missing layer

n8n executions log token usage for some AI nodes, buried in execution data you have to open one run at a time. There's no aggregation: no "workflow X spent $Y this week," no per-node attribution, no trend. The invoice tells you the total. Nothing tells you the distribution.

And distribution is where the surprises live. My worst surprise wasn't the total going up -- it was one workflow, the inbox triage agent, quietly going from $0.40/day to $1.30/day after I "improved" its classification prompt. The improvement made the prompt 2.4x longer and added a second model call per email for "edge cases." Nobody noticed for a month. The workflow worked fine. It just worked fine expensively.

The ledger: one Code node after every LLM call

The design is boring on purpose. Right after every node that calls a model -- OpenAI node, Anthropic node, an HTTP call to an inference endpoint -- sits a Cost node. One Code node, same shape everywhere, writes one row per call:

// Cost node: append one row per LLM call
const call = $input.first().json;
const PRICE = {
  'gpt-4o-mini':        { in: 0.15,  out: 0.60  },   // USD per 1M tokens
  'gpt-4o':             { in: 2.50,  out: 10.00 },
  'claude-3-5-sonnet':  { in: 3.00,  out: 15.00 },
  'claude-3-5-haiku':   { in: 0.80,  out: 4.00  },
};
const p = PRICE[call.model];
const row = {
  run_id:     $execution.id,          // n8n execution id
  event_id:   call.event_id,          // my own correlation key
  workflow:   $workflow.name,
  node:       call.node_name,
  model:      call.model,
  in_tokens:  call.usage.prompt_tokens,
  out_tokens: call.usage.completion_tokens,
  usd:        (call.usage.prompt_tokens * p.in +
                call.usage.completion_tokens * p.out) / 1e6,
  ts:         new Date().toISOString()
};
// insert into cost_log (run_id, event_id, workflow, node, model, in_tokens, out_tokens, usd, ts)

Three details that matter:

  1. The price table lives in the node, not in config. Prices change rarely; when they do I edit one shared Code snippet (it's a shared sub-workflow, imported everywhere). I don't want a database lookup between the model call and the log row -- the ledger must never slow down or break the workflow it observes.
  2. It logs the intended model call, not a guess. Token counts come from the actual API response (usage), not from counting words in my prompt. If a provider doesn't return usage (some proxy endpoints don't), the node logs in_tokens: null and a usd_estimated flag -- estimated rows never feed the alert query, only the dashboard.
  3. Every AI agent workflow gets one. No exceptions for "this one barely calls the model." The workflows that barely call the model are exactly the ones where a prompt edit quietly doubles the token count.

The morning query

A scheduled workflow runs this once a day at 07:00 and posts the result to Slack:

-- daily spend per workflow, vs its own 7-day median
WITH daily AS (
  SELECT workflow, DATE(ts) AS d, SUM(usd) AS spend
  FROM cost_log WHERE ts >= NOW() - INTERVAL 14 DAY
  GROUP BY workflow, DATE(ts)
),
medians AS (
  SELECT workflow, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY spend) AS med
  FROM daily WHERE d < CURRENT_DATE GROUP BY workflow
)
SELECT d.workflow, d.spend, m.med,
       ROUND(d.spend / NULLIF(m.med, 0), 2) AS spike_ratio
FROM daily d JOIN medians m ON d.workflow = m.workflow
WHERE d.d = CURRENT_DATE - 1;

The alert rule is one line: spike_ratio >= 2.0 fires a Slack alert with the workflow name and yesterday's spend. Not total spend -- ratio against its own median. A $0.30 workflow that suddenly costs $0.90 is the same class of problem as a $30 workflow that suddenly costs $90. Both mean something changed and nobody reviewed it.

The morning digest itself is a table, not a chart: workflow, yesterday, 7-day median, ratio. I read it in thirty seconds with coffee. Charts are for quarterly reviews; tables are for Tuesday mornings.

The incident that paid for it

Two weeks after wiring the ledger, the alert fired on my repurposing workflow -- spike_ratio 3.1, $2.10 vs a $0.68 median. The cause: a retry policy change I'd made the day before (retry on a broader class of failures, from the retry-budget post). Broader retries meant the workflow re-ran its two most expensive model calls on failures that used to be terminal. The retry change was correct for reliability. It was also, quietly, a 3x cost change. Without the ledger I'd have learned this from next month's invoice. With the ledger I learned it over coffee and tightened the retry condition the same morning.

That's the whole value proposition: the ledger doesn't reduce cost. It converts "the invoice went up" (monthly, unactionable) into "this workflow changed its behavior yesterday" (daily, actionable). Cost control follows from visibility; visibility is all the ledger does.

What the ledger is not

It's not precise accounting. Token counts from usage are exact; my price table is a snapshot that drifts when providers change prices. The ledger answers "which workflow, roughly how much, trending which way" -- it's an instrument panel, not a tax return.

It's not a prompt optimizer. It will never tell you why a workflow got expensive -- the retry incident needed me to go read the diff. The ledger narrows the search to one workflow and one day. Diagnosis is still human work.

And it's not free: every model call now writes a database row. At my volume that's a few hundred rows a day, trivial. If you're doing millions of calls a day, sample (log 1 in 100, multiply). The pattern survives sampling; the alert rule just needs the sampling rate in the denominator.


I'm building AI Automation Lab -- n8n workflow templates for AI automation -- and keeping a free lab on GitHub. I also send occasional notes on n8n reliability patterns: get them here.

來源:DEV Community · dev.to