iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0
生成式 AI

AI 產品與架構設計之旅:從 0 到 1,再到 Day 2系列 第 12

Day 12: 成本透明化 - 問一個問題多少錢?

  • 分享至 

  • xImage
  •  

嗨大家,我是 Debuguy。

經過前面的努力,我們的 ChatBot 已經能思考、能協作、能使用工具。但今天要來面對一個很現實的問題:「解決一個 issue 要多少錢?」

成本與效益的考量

我們來複習一下年代久遠的梗

我使用 AI ChatBot 的原則是:「MDFK 用爆」。

沒錯,就是 MDFK 用爆,老子才不管甚麼 token 成本的,每次有什麼事都 @ bot 就對了。簡單問題也要 ultra think,普通 query 也懶得寫直接 @bot 然後 ultra think 用爆。Context window 塞好塞滿,thinking tokens 是什麼我不知道。

我還記得,那天上班剛滿一個月,主管跑來跟我說:「這個月 Gemini API 帳單爆了,你有頭緒嗎?」

我 TMD 怎麼會知道。

作為一名負責任的工程師,是不應該讓這種事發生的
但如果沒有從使用者體驗的角度思考
很有可能拿到帳單才發現問題大條了

問題的本質

當 LLM 對話結束後,使用者看到的只有結果:

🤖 Bot: @Debuguy 根據人事總處網站的最新資訊,今天台北市因颱風宣布停班停課...

但背後實際發生的成本消耗:

  • Input tokens:處理對話歷史和 system prompt
  • Thinking tokens:LLM 內心獨白的運算成本
  • Output tokens:最終回覆的生成成本
  • 工具調用:額外成本(如 MCP 的 Playwright)

「使用者爽爽問,但這都是需要支付費用的...」

解決方案:讓成本現形

第一步:Token 使用量追蹤

讓我們看看 GenKit Flow 的關鍵改動:

   async ({ messages }, { sendChunk }) => {
     const { newMessages, history } = organizeMessages(messages);
     const tools = await host.getActiveTools(ai);
     const resources = await host.getActiveResources(ai);
+    
+    // 關鍵:設置 token 計算變數
+    let inputTokens = 0;
+    let thoughtsTokens = 0;
+    let outputTokens = 0;
     
     const { stream, response } = ai.prompt('chatbot').stream({
       botUserId: process.env['SLACK_BOT_USER_ID']!,
       prompt: newMessages,
     }, {
       messages: history,
       tools,
       resources,
       toolChoice: 'auto',
+      // middleware 攔截 token 使用量
+      use: [
+        async (req, next) => {
+          const result = await next(req);
+          inputTokens += result.usage?.inputTokens ?? 0;
+          thoughtsTokens += result.usage?.thoughtsTokens ?? 0;
+          outputTokens += result.usage?.outputTokens ?? 0;
+          return result;
+        }
+      ],
     });

     const result = await response;

-    return (await response).text;
+    return {
+      text: result.text,
+      usage: {
+        inputTokens,
+        thoughtsTokens,
+        outputTokens,
+      },
+    };
   }
 );

需要這樣設計的原因:

  1. Middleware 攔截:用 use 攔截每次 API 調用,累計 token 使用量
  2. 三種 Token 分離:Input、Thinking、Output 分別計算
  3. 回傳包裝:不只回傳文字,還包含完整的使用量資訊

第二步:美化 Slack 顯示

原本簡陋的回覆升級成專業的 Slack Block Kit 格式:

- await say({ text: response, thread_ts: event.thread_ts || event.ts });
+ await say({ 
+   text: response.text, 
+   blocks: [
+     {
+       "type": "section",
+       "text": {
+         "type": "mrkdwn",
+         "text": response.text
+       }
+     },
+     {
+       "type": "divider"  // 分隔線
+     },
+     {
+       "type": "rich_text",
+       "elements": [
+         {
+           "type": "rich_text_section",
+           "elements": [
+             {
+               "type": "text",
+               "text": "usage:\n"
+             }
+           ]
+         },
+         {
+           "type": "rich_text_list",
+           "style": "bullet",
+           "indent": 0,
+           "elements": [
+             {
+               "type": "rich_text_section",
+               "elements": [
+                 {
+                   "type": "text",
+                   "text": "input tokens: " + response.usage.inputTokens
+                 }
+               ]
+             },
+             {
+               "type": "rich_text_section", 
+               "elements": [
+                 {
+                   "type": "text",
+                   "text": "thoughts tokens: " + response.usage.thoughtsTokens
+                 }
+               ]
+             },
+             {
+               "type": "rich_text_section",
+               "elements": [
+                 {
+                   "type": "text",
+                   "text": "output tokens: " + response.usage.outputTokens
+                 }
+               ]
+             }
+           ]
+         }
+       ]
+     }
+   ], 
+   thread_ts: event.thread_ts || event.ts 
+ });

Slack Block Kit 的好處:

  • 視覺分離:主回覆和成本資訊清楚區分
  • 結構化顯示:用 bullet list 整齊呈現各項數據

如果想要格式化的更美
可以先用 Slack Block Kit Builder 測試

實際效果:數據帶來的意識轉變

Before(沒有成本顯示)

👤 Debuguy: @bot 幫我查一下今天天氣怎樣?
🤖 Bot: 今天台北多雲,溫度 25-28 度,降雨機率 30%。
👤 Debuguy: 那明天呢?
🤖 Bot: 明天晴朗,溫度 26-30 度...
👤 Debuguy: 後天呢?
🤖 Bot: 後天小雨...

使用者體驗:😐 想問就問,沒感覺

After(有成本顯示)

👤 Debuguy: @bot 幫我查一下今天天氣怎樣?

🤖 Bot: 今天台北多雲,溫度 25-28 度,降雨機率 30%。

───────────────────
usage:
• input tokens: 1,240
• thoughts tokens: 890  
• output tokens: 156

👤 Debuguy: 能不能一次給我未來三天的預報?

🤖 Bot: 未來三天天氣預報:
今天:多雲 25-28°C...
明天:晴朗 26-30°C...
後天:小雨 23-26°C...

───────────────────
usage:
• input tokens: 1,380
• thoughts tokens: 650
• output tokens: 245

使用者體驗:🤔 開始學會問更有效率的問題

這樣顯示的意義

1. 成本意識的建立

行為改變:

  • 開始問「更好的問題」
  • 不再隨便丟一句話就期待 AI 猜測意圖

2. 成本透明化帶來的管理效益

現在我們能清楚知道:

  • 哪一種 issue 大概需要花多少費用
  • 是否是一個值得投資的事情
  • 不同使用模式的成本差異

技術細節:GenKit Middleware 的奧秘

為什麼用 Middleware 而不是直接讀取?

這是一個很關鍵的技術決策。你可能會想:「response 裡面不是本來就有 usage 嗎?」

// 看起來很合理的做法(但有大坑)
const response = await ai.generate({...});
- const tokens = response.usage; // 看起來很正常?
+ const tokens = response.usage; // ❌ 只包含最後一輪!

但這裡有個陷阱!

還記得前面幾篇文章的 trace 畫面嗎?當 ChatBot 使用 MCP 工具時,實際上會發生這樣的流程:

第1輪:LLM 分析使用者請求
第2輪:LLM 決定要用 Playwright 工具
第3輪:執行工具調用
第4輪:LLM 整合工具結果,生成最終回覆

問題來了: response.usage 只會包含最後一輪的 token 使用量!

實際測試一下:

// 當使用者問「幫我查颱風假」時
console.log(response.usage);
- // 期望:完整的 token 使用量
+ // 實際:{ inputTokens: 245, outputTokens: 156, thoughtsTokens: 89 }
+ // ❌ 但前面還有分析階段、工具選擇階段的 token 消耗!

所以如果只看 response.usage,你會發現:

「奇怪,查個颱風假怎麼只花這麼少 token?感覺不對啊...」

Middleware 的救援

- // 錯誤的做法:只拿最後一輪
- const response = await ai.generate({...});
- const tokens = response.usage;

+ // 正確的做法:攔截每一輪的 API 調用
+ use: [
+   async (req, next) => {
+     const result = await next(req);
+     // 每一輪都累計!
+     inputTokens += result.usage?.inputTokens ?? 0;
+     thoughtsTokens += result.usage?.thoughtsTokens ?? 0;
+     outputTokens += result.usage?.outputTokens ?? 0;
+     return result;
+   }
+ ]

這樣做的好處:

  • 完整追蹤:捕獲 LLM 的每次 API 調用(包含所有工具調用輪次)
  • 真實成本:累計所有輪次的實際 token 消耗

Slack Block Kit vs 純文字

- // 純文字版本:
- 回覆內容...
- 
- Token usage: Input 1240, Thinking 890, Output 156

+ // Block Kit 版本:
+ 🤖 Bot: 回覆內容...
+ ───────────────────
+ usage:
+ • input tokens: 1240
+ • thoughts tokens: 890  
+ • output tokens: 156

Block Kit 版本的優勢:

  • 視覺層次更清楚:主內容和成本資訊有明顯區分
  • 可以加入更豐富的互動元素:未來可以加按鈕、圖表等
  • 未來擴展空間大:可以加入成本計算、使用趨勢等功能

小結:透明化的力量

今天我們成功實現了成本透明化

技術層面:

  • GenKit Middleware:優雅的 token 追蹤機制
  • Slack Block Kit:專業的資訊呈現方式

產品層面:

  • **使用者習慣:自然形成更好的使用習慣
  • 成本控制:管理層有了明確的預算依據
  • 行為引導:透過資料引導更有效率的互動模式

文化層面:

  • 意識轉變:從「免費」心態到「成本意識」
  • 技能提升:同事們自然會開始思考 prompt engineering
  • 信任建立:透明的成本讓管理層更有信心投資

成本透明化不是為了限制使用,而是為了讓使用變得更有效益。

當使用者能看到每次對話的真實成本時,他們不是變得吝嗇,而是變得更有策略。這種「數據驅動的使用者體驗設計」,可能是 AI 產品設計的一個重要方向。

明天我們要來處理下一個的挑戰:部署策略。


完整的原始碼在這裡,現在每次對話都能看到確切的成本消耗!


AI 的發展變化很快,目前這個想法以及專案也還在實驗中。但也許透過這個過程大家可以有一些經驗和想法互相交流,歡迎大家追蹤這個系列。

也歡迎追蹤我的 Threads @debuguy.dev


上一篇
Day 11: 從原型到產品 - 檢視原型缺陷規劃生產環境
系列文
AI 產品與架構設計之旅:從 0 到 1,再到 Day 212
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言