iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0

1. 前言:同事或主管不會只問一個問題

昨天把查資料的工具交給 Gemini 之後,一問一答已經不會亂編數字了,可是實際上沒有人是這樣問問題的,行銷同事問完「上週哪支廣告花最多錢」,下一句一定是「那它的點擊率呢」,再下一句是「前一週也是它嗎」,每一句都省略了主詞,「它」是哪一支、「前一週」是從哪一週往前算,都要靠前面的對話才聽得懂。

Day 21 的程式每一題都是從零開始,第二句丟進去模型根本不知道「它」是誰,另外昨天的三個工具查得到功勞、異常和素材特徵,就是查不到廣告花了多少錢,還有一個更基本的問題,模型不知道今天是幾月幾號,「上週」對它來說沒有意義。

今天把這三個洞補起來,做出一個可以連續聊的行銷助理,不用寫 SQL,用聊天的方式一路問下去。

今日核心目標:

  1. 把每一輪的問題、工具結果和回答都留在同一份對話紀錄裡,讓模型聽得懂「它」和「前一週」
  2. 在系統指示裡告訴模型今天的日期和一週怎麼算,讓「上週」換算成正確的起訖日
  3. 新增查廣告花費的工具,並且實測對話越長每一輪要多付多少錢

2. 系統架構全景與設計理念

圖一:每一輪的問題、模型的工具要求、工具結果與回答都接在同一份對話紀錄後面,下一輪整份重新送給 Gemini,所以輸入 Token 逐輪增加

和 Day 21 比 Day 21 Day 22
對話紀錄 每一題用完就丟 整場對話共用一份,一輪一輪往後接
日期 題目裡自己寫明起訖日 系統指示給今天的日期,模型自己換算
工具 歸因、異常診斷、素材特徵 多一個查廣告花費,共四個
上限 每題最多呼叫模型 4 次、輸入最多 4,000 個 Token 輸入放寬到 8,000 個 Token,再加上每場最多 8 輪

💡 核心工程理念:

  1. 模型沒有記憶,記憶是我們每次重送的那份紀錄:這裡用的 generate_content 每一次呼叫都是獨立的,不會記得上一次說過什麼,所謂的連續對話是程式把前面每一輪的內容全部再送一次,模型每次都是把整場對話從頭讀完才回答
  2. 留下來的要是完整的來回:一輪裡有使用者的問題、模型的工具要求、工具查到的結果和模型最後的回答,四樣都要留,少了工具結果下一輪就沒有數字可以引用,少了回答它就不記得自己講過什麼
  3. 記得越多付得越多:紀錄越長每一輪的輸入越多,這筆錢是逐輪往上疊的,所以輪數和輸入量都要有上限,到了就請使用者開一場新的對話

3. 核心技術深度拆解

3.1 對話紀錄就是一份越接越長的清單

Day 21 的程式裡已經有 contents 這份清單,只是每一題都重新建一份,今天改成整場對話共用同一份,一輪問答結束之後裡面會多出這些東西:

順序 誰說的 內容
1 使用者 這一輪的問題
2 模型 要呼叫哪些工具、參數是什麼(不需要查的時候沒有這一項)
3 程式 每個工具查到的結果(同上)
4 模型 這一輪的回答

下一輪把新的問題接在後面,整份再送一次,程式的主體只有這幾行:

contents = []                                  # 整場對話共用
for turn, question in enumerate(questions, 1):
    row, calls, tool_rows = one_turn(client, bq, contents, config, question, session_id, turn)

one_turn 裡面和 Day 21 幾乎一樣,多做的事只有兩件,成功的時候把模型的回答也接進 contents,失敗的時候把這一輪接上去的東西全部拿掉,還原到這一輪開始之前的樣子,如果不還原,紀錄裡會留下一段有問題、有工具結果卻沒有回答的半套來回,下一輪接著問的時候模型讀到的對話就是亂的。

3.2 模型不知道今天幾號,「上週」要靠我們給

模型的知識停在訓練資料的那個時間點,它不知道現在是哪一天,問它「上週」它沒有依據可以換算,解法是在系統指示裡直接講清楚:

今天是 2026-09-17(星期四),資料最新到 2026-09-16。
使用者說的今天、上週、上個月都以這個日期換算,
一週從星期一算到星期日,上週指的是今天所在那一週的前一週。

這一段有三個細節,第一是合成資料只到 9 月 16 日,所以把隔天當成今天,真的上線時這裡要換成程式執行當下的日期,第二是連星期幾都一起給,模型自己推算星期幾不一定可靠,直接給它比較保險,第三是把「一週」和「上週」的定義寫死,有人的一週從星期日開始,有人說的上週是過去七天,不寫清楚模型每次選的可能不一樣。

有了這一段,「上週」被換算成 9 月 7 日到 9 月 13 日,「再前一週」是 8 月 31 日到 9 月 6 日,實測兩次都算對了。

3.3 新增查花費的工具:一個工具四種看法

查花費的工具讀的是 Day 07 的廣告成效表 fct_ad_daily,同一筆花費可以依單支廣告、廣告群組、活動或通路來看,我沒有寫成四個工具,而是用一個 group_by 參數切換:

參數 可以填什麼
start_date、end_date 起訖日,YYYY-MM-DD
group_by creative(單支廣告)、ad_group、campaign、channel
channel 只看某個通路時填 meta、line 或 google_cpc

回傳的每一列有花費、曝光、點擊、點擊率和每次點擊成本,依花費由高到低排,寫這個工具的時候有兩件事要先處理,都是在呼叫模型之前的審查階段發現的。

第一件是結果被截斷,上週有 30 支廣告有花費,工具一次最多回 20 列,如果只回 20 列什麼都不說,模型沒有辦法知道後面還有 10 支,所以回傳裡多了 total_groups(全部有幾個)和 truncated(有沒有被截掉)兩個欄位,工具說明也寫明最多回傳花費前 20 名。

第二件是兩個工具的通路名稱不一樣,花費表叫 meta,歸因表叫 meta / paid_social,歸因表裡還同時有 google / cpc 和 google / organic,要把花費和功勞放在一起看,模型得知道誰對到誰,所以在花費工具的說明裡加了一句對照,meta、line、google_cpc 分別對應歸因工具裡的 meta / paid_social、line / display、google / cpc。

3.4 實測:五句話,一路問下去

圖二:五輪對話每一輪查了哪些工具,以及每一次呼叫模型的輸入 Token,從第一次的 1,056 個增加到最後一次的 6,195 個

固定用五句話問一輪,後面四句都要靠前面的對話才聽得懂:

輪 我問的 模型查了什麼 回答重點
1 上週哪支廣告花最多錢? 花費,依單支廣告,9/7 到 9/13 cr-meta-trn-p1,12,938 元,點擊 877 次
2 它的點擊率跟其他幾支比起來怎麼樣? 沒有查 2.46%,比同通路另外兩支高,但比再行銷素材的 3.14% 和最高的 3.22% 低
3 那再前一週呢?一樣是它最貴嗎? 花費,依單支廣告,8/31 到 9/6 一樣是它,14,328 元,比上週還高
4 上週花最多錢的通路是哪個?它在同一週用時間衰減算的訂單功勞排第幾? 花費依通路,加上歸因的時間衰減,同一輪一起查 Meta 花了 59,902 元,功勞 79.9 筆排第 3,付費通路裡排第 1
5 幫我把剛剛聊到的整理成三點,我要貼給主管 沒有查 把前四輪的數字整理成三點

五輪回答裡的數字我都另外寫 SQL 直接查表對過,全部正確,有幾個地方值得看:

  • 第 2 輪沒有重查:系統指示裡有一句「同一場對話裡已經查過的資料可以直接用,不用重查」,第 1 輪查回來的 20 支廣告裡本來就有點擊率,模型直接從紀錄裡拿來比,點擊率最高的 3.22% 是花費排第 8 的那一支,它也找到了
  • 第 3 輪聽懂了兩個省略:「再前一週」被換算成 8 月 31 日到 9 月 6 日,「它」被認出是 cr-meta-trn-p1,而且回答時主動拿上週的 12,938 元來比
  • 第 4 輪把兩個工具的通路對起來了:照著工具說明裡的對照,花費表的 meta 和歸因表的 meta / paid_social 被當成同一個通路,功勞排在電子報和直接流量後面,它還補了一句只看付費通路的話是第 1
  • 第 5 輪完全靠紀錄:沒有查任何工具,三點裡的每個數字都來自前四輪

花最多錢的那支廣告點擊率並不是最高的,花最多錢的通路功勞排第 3,這兩件事如果只問第一句是看不到的,都是接著問才問出來。

數字都對不代表每一句話都對,第 2 輪它說點擊率比「Google/LINE 的高點擊素材」低,可是它手上那 20 支裡的 LINE 素材點擊率全部低於 2.46%,高的只有 Google 那幾支,LINE 是它順手多帶的,這種沒有數字的形容最容易被放過。

這一輪只問了一次,溫度設 0,換一種問法結果可能不一樣,第 4 輪「排第幾」我也沒有說是在哪些通路裡排,模型兩種都給了,換一次不一定會這麼周到。


4. FinOps 成本防護實踐:三道防線體系

  1. 第一道防線:善用 Google Cloud 每月免費額度:四個工具查的都是小表,在 BigQuery 每月 1 TiB 的查詢免費額度內,開始之前的預檢(確認上週查得到資料、數一次輸入 Token)也都不收費
  2. 第二道防線:架構層被動成本防護:每輪最多呼叫模型 4 次、每場最多 8 輪,每次送出前先數輸入 Token,超過 8,000 就不送並請使用者開新對話,chat.sh 一樣先印最壞金額,輸入 yes 才開始
  3. 第三道防線:Cloud Billing 預算警報:沿用 Day 03 由 Terraform 建立的預算警報,50%、80%、100% 三段通知,今天的操作不會觸發

五輪對話一共呼叫模型 8 次,每一次的輸入 Token 是這樣長大的:

輪 第幾次呼叫 輸入 Token 這時候紀錄裡多了什麼
1 1 1,056 只有系統指示、四個工具定義和第一句話
1 2 2,433 上週 20 支廣告的花費
2 1 2,627 第 1 輪的回答
3 1 3,261 第 2 輪的問答
3 2 4,584 前一週 20 支廣告的花費
4 1 4,927 第 3 輪的回答
4 2 5,678 通路花費與歸因兩份結果
5 1 6,195 第 4 輪的回答

最後一次呼叫的輸入是第一次的 5.9 倍,第 5 輪那句「整理成三點」本身只有二十個字左右,卻要付 6,195 個 Token 的輸入,因為前面四輪的東西全部重送了一次,五輪合計輸入 30,761 個 Token、輸出加思考 2,920 個,費用約新台幣 1.09 元,其中輸入佔了 68%,事前印出的最壞金額是 8.76 元。

最佔空間的是工具結果,一份 20 支廣告的花費大約 1,300 個 Token,查一次之後每一輪都要跟著重送,所以回傳的列數和欄位要節制,另外如果接下來每一輪都再查一份 20 列的結果,再聊兩三輪就會碰到 8,000 的上限,上限到了的處理方式今天是最簡單的請使用者重開,把舊的對話壓縮成摘要再繼續是另一種做法,這個系列沒有做,費用依 gemini-3.6-flash 在 global 端點的單價(每百萬 Token 輸入 0.75、輸出 3.75 美元)與實際 Token 數算出,匯率以 1 美元約 32 元計,實際以帳單為準。


5. Cloud Shell 實戰演練:跟助理聊五句

5.1 事前準備

  • 先在 ~/ai-driven-martech-pipeline 執行 git pull,取得 Day 22 新增的 agent/chat.py、agent/chat.sh、兩支檢查與報表的 SQL,以及加了查花費工具的 agent/tools.py
  • gcloud config get-value project 要印出你的專案 ID
  • 已完成 Day 07(廣告成效表)、Day 08、Day 09 與 Day 17,四個工具讀的是這幾天建好的表,Token 用量表 ops_llm_usage 是 Day 16 建的,也要先有
  • 需要 google-genai 與 google-cloud-bigquery 兩個 Python 套件,缺少時 chat.sh 會提示安裝指令

5.2 路線 A|懶人包:照固定的五句話問一輪

cd ~/ai-driven-martech-pipeline && git pull && bash agent/chat.sh

chat.sh 先確認前幾天的表都在,接著做不花錢的預檢,確認上週查得到廣告花費、四份工具結果放得進輸入上限,印出最壞金額之後停下來,輸入 yes 才開始,五輪的問題、查了哪些工具、回答和每一輪用掉的 Token 都會印在畫面上,全部跑完大約兩分鐘,約新台幣 1 元,完整跑過一次之後再執行不會重複收費。

5.3 路線 B|自己打字聊

步驟 1:只看查花費的工具,不呼叫模型

cd ~/ai-driven-martech-pipeline/agent
python3 -c "
import json, tools
from google.cloud import bigquery
bq = bigquery.Client(location='US')
r = tools.run_tool(bq, 'get_ad_spend', {'start_date': '2026-09-07', 'end_date': '2026-09-13', 'group_by': 'channel'})[0]
print(json.dumps(r, ensure_ascii=False, indent=1))
"

會印出上週三個通路的花費,把 group_by 換成 creative 可以看到 total_groups 是 30、truncated 是 true,這一步不花錢。

步驟 2:開始聊

cd ~/ai-driven-martech-pipeline && bash agent/chat.sh --talk

一樣會先印估價,輸入 yes 之後就可以自己打字問,每一輪結束會印出這一輪用掉的 Token 和累計金額,輸入空白行結束,一場最多 8 輪,八輪的最壞金額是新台幣 14.01 元。

步驟 3:檢查與報表

cd ~/ai-driven-martech-pipeline
bq query --nouse_legacy_sql --format=pretty < agent/chat_check.sql
bq query --nouse_legacy_sql --format=pretty --max_rows=100 < agent/chat_report.sql

5.4 驗證成果

  • chat_check.sql 的 9 項都是 OK,包含五輪都有回答、沒有工具錯誤、每次輸入都在 8,000 以內、最後一輪的輸入比第一次多
  • 報表第 2 段是每一次呼叫模型的輸入 Token,應該和上面那張表一樣逐次增加
  • 自己聊的時候試試只講「那 line 呢」這種沒頭沒尾的句子,看它接不接得上

5.5 用完後怎麼處理

chat_turns、chat_calls_log、chat_tool_log 三張表都只有幾列,留著不會產生費用,明天要拿這個助理來測惡意指令,Day 25 的成本儀表板也會用到 ops_llm_usage 裡今天寫進去的用量(只跑固定腳本是 8 列),都不要刪。


6. 工程實務避坑指南

  1. 失敗的那一輪要整輪拿掉:一輪做到一半失敗就把這一輪接上去的全部還原,紀錄裡只留完整的來回,不然下一輪模型讀到的是一段沒有結尾的對話
  2. 日期要連星期幾和一週的算法一起給:只給日期不給定義,「上週」可能被算成過去七天,差一兩天報表的數字就全部不一樣,而且看起來完全合理
  3. 結果被截斷要讓模型知道:只回前 20 名沒問題,但要告訴它全部有幾個,否則問「最便宜的是哪支」它手上最後一名其實是第 20 名,今天的工具只照花費由高到低排,問最便宜的還是答不了,這種問題要另外加排序方向的參數
  4. 不同工具的名稱對不起來要在說明裡對照:同一個通路在兩張表叫不同的名字是很常見的事,不寫對照就是讓模型自己猜,尤其是 google 在歸因表裡有付費和自然兩種
  5. 輸入上限是一定會碰到的:連續對話的輸入只增不減,沒有上限的話聊得越久每一句越貴,使用者不會感覺到,帳單會

7. 總結與明日預告

今天把 Day 21 的工具接成可以連續對話的行銷助理,對話紀錄整場共用、系統指示給了今天的日期、多了一個查廣告花費的工具,五句話一路問下去,上週花最多錢的是 cr-meta-trn-p1 的 12,938 元,前一週也是它,花最多錢的通路 Meta 在時間衰減下的功勞排第 3,五輪的數字全部和資料表對得上,其中兩輪沒有重查、直接用紀錄裡的資料回答,合計呼叫模型 8 次約新台幣 1.09 元。

回到篇名,不用寫 SQL 這件事做到了,但它不是免費的,模型的記憶其實是我們每一輪重送的那份紀錄,最後一次呼叫的輸入是第一次的 5.9 倍,聊得越久每一句越貴,所以這種助理一定要有輪數和輸入的上限。

明日預告:Day 23《別讓你的 AI 助理被惡意指令套出個資或亂講話》,助理能聊天也能查資料之後,接下來要面對的是有人故意叫它做不該做的事,明天拿今天的助理來測 Prompt Injection、個資遮蔽和安全設定。


上一篇
Day 21 | 教 AI 自己去查資料庫免得它在那邊瞎猜
系列文
AI-Driven MarTech:用 Google Cloud + Vertex AI 打造全自動廣告歸因與多模態素材分析系統 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言