上一篇先做 Agent Behavior Evaluation。
確認 Agent 的結果、grounding、instruction following 和 execution strategy 都合理之後,下一步才是把這次分析保存下來。
目前為止,一次 analysis 大概是:
User asks a question
↓
Agent queries data
↓
Agent writes code
↓
Sandbox executes
↓
HTML report
但如果下次還想做同樣的分析,不應該每次都重新叫 Agent 從頭推理一次。
所以這次要把一次已經確認過的分析,正式保存成一個可以重新執行的 Analysis。
Analysis 不是保存最後那張 chart,也不是把整段 conversation 存起來 replay。
真正需要留下來的是:
Inputs
+
Script
+
Outputs
目前的 model 是:
class Analysis(BaseModel):
id: str
title: str
description: str = ""
question: str = ""
conversation: str | None = None
created_at: str = Field(default_factory=now)
version: int = 1
updated_at: str | None = None
inputs: list[Input]
script: str
outputs: list[Output]
其中:
inputs:資料從哪裡來script:怎麼分析outputs:最後要產生哪些檔案其他 metadata 則保留它回答什麼問題、從哪個 conversation 保存、目前是哪個 version。
所以 Analysis 比較像是一份 executable recipe。
為了之後可規模化做準備,應盡可能保持彈性而不是寫死在程式裡。
假設 Agent 已經得到出:
2017-11 → 904
2017-12 → 1365
2018-01 → 1433
...
然後直接把結果寫進 Python:
data = {
"month": ["2017-11", "2017-12", "2018-01"],
"distinct_videos": [904, 1365, 1433],
}
這段 code 當下可以畫出正確的 chart,但它不是 reusable Analysis。
因為下次再執行的時候如果換了資料源結果就會是錯的:
Data changed
↓
Script still contains old numbers
↓
Same chart again
真正要保存的 flow 應該是:
Datasource
↓
Input file
↓
Script
↓
Output
所以保存 Analysis 時,不只是把 Agent 最後那段 Python 收起來而已。
User 在 chat 裡說:
save this analysis
Agent 會呼叫 save_analysis。
但不是呼叫完就直接寫進 storage。
目前會先檢查:
Validate Recipe
↓
Fetch Inputs
↓
Check Hard-coded Results
↓
Test Run
↓
Save
例如會確認:
最後還會在一個空的 work folder 裡跑一次確認有沒有滿足要求,盡可能降低依賴。
成功保存後,就會出現在 Analyses page。

例如:
Views per Video by YouTube Category
可以看到:
這時它已經不再只是某個 conversation 裡的一次回答,而是一個可以重新執行的 artifact。
點進 Analysis 後,可以直接按 Run。


這次跟 conversation execution 有一個很大的不同:
Run 不需要 LLM。
流程是:
Saved Analysis
↓
Fetch Inputs
↓
Fresh Sandbox
↓
Run Saved Script
↓
Collect Outputs
↓
Delete Sandbox
不需要再讓 Agent:
read question
→ decide tools
→ write code again
因為這些 decision 已經在第一次 conversation 裡完成了。
Run 做的只是重新執行已經保存的 recipe。
所以真正被保存的也不是之前產生的 report.html。
如果只是:
Analysis
→ old report.html
那它只是 snapshot。
資料變了,內容也不會跟著變。
現在保存的是:
Input Definition
+
Script
+
Output Definition
每次 Run 都重新從 datasource 拿資料,再產生新的 HTML。
例如原本的 Analysis 跑在 US trending videos:

當時結果是:
Music
→ 6.0M average views per video
這個 Music 和 6.0M 並不是 Analysis 本身的一部分。
它們只是這次 execution 的結果。
既然 Analysis 保存的是 recipe,下一個問題就是:
同樣的分析邏輯,可以直接套到另一份 data 嗎?
在 Analysis page 的 Data picker,可以選擇另一個 compatible table。
例如原本使用:
videos
可以切換成:
videos_ca
系統會先檢查新的 table 是否具有 Analysis 需要的 columns,以及對應的 data types 是否 compatible。
如果可以替換,原本 query:
SELECT videos.views
FROM videos
執行時可以變成:
SELECT videos.views
FROM videos_ca AS videos
也就是 datasource 換了,但 Analysis 裡原本使用的 table reference 還可以繼續工作。
接著按下 Run。
同一份 Analysis 跑在另一份 datasource 上:

結果變成:
Gaming
→ 7.0M average views per video
但 Analysis logic 沒有變:
Peak views per video
↓
Average by category
↓
Sort
↓
Generate bar chart
↓
Highlight top category
變的是 data,所以 finding 也跟著變。
這其實才開始接近一開始想做的:
Define Once
→ Analyze Another Dataset
Saved Analysis 也不是存下來之後就不能再修改。
例如 user 後來說:
sort the bars from smallest to largest
Agent 可以讀出原本的 Analysis,修改 recipe,再用 replaces 保存回同一個 Analysis。
這時:
version 1
→ version 2
id 不變,run history 也還在,只是目前使用的是新的 recipe。
所以 version 在這裡主要回答:
現在執行的是哪一版 Analysis?
做到這裡,Conversation 和 Analysis 的角色開始很不一樣。
Conversation 是:
Explore
↓
Clarify
↓
Query
↓
Write Code
↓
Fix
↓
Confirm
這是用戶創造分析的過程。
Analysis 則是:
Save
↓
Run
↓
Switch Data
↓
Run Again
所以 Analysis 是從 conversation 裡抽出一個已經可以獨立執行的 recipe:
Conversation
↓
User + Agent iterate
↓
Confirmed Analysis
↓
Save
↓
Inputs + Script + Outputs
到了這一段, project 從一次性的分析變成可重複執行且有辦法規模化。