跳至內容

在持久核心中探索資料

本指南說明如何讓代理只載入一次昂貴的內容,然後就它提出多個問題,而不必為每個問題重複付出載入成本。

eval 工具會執行一個持久直譯器。變數、匯入的模組與已載入的資料,都會在呼叫之間保留下來。

一次性的 python -c 會為每個問題重複付出設定成本。載入一個資料框要花上數秒與上百 MB;把它畫成圖表卻只需數毫秒。當每個問題都要付一次載入成本,代理問的問題就會比它應該問的少——而每個答案都以文字形式回傳,它還得重新解析一遍。

載入 data/runs.csv,並告訴我每個代理的 p95 持續時間。

import pandas as pd
runs = pd.read_csv("data/runs.csv")
len(runs)

最後一個運算式會像筆記本那樣被回顯出來,因此 len(runs) 不須 print 就能報出列數。

然後,在另一次呼叫中:

runs.groupby("agent")["duration_ms"].quantile(0.95).sort_values()

runs 仍然處於已載入狀態。pandas 資料框回傳時會渲染成表格,而不是以 repr 呈現。

import matplotlib.pyplot as plt
runs.groupby("agent")["duration_ms"].mean().plot.bar()

圖表會在儲存格執行完之後被擷取,並以影像形式回傳,由對話紀錄負責渲染。你看得到圖表,代理也看得到。

圖表會在擷取後關閉。未被收回的圖表會連下一個儲存格都重畫並重送一次,因此一段五個儲存格的工作階段,就會把第一張圖表送出五次。

這些寫法會被改寫成一般的程式碼,而不是直接報錯:

你輸入的 會發生的事
%pip install x 執行 pip,然後清除模組快取,讓下一次 import 看得到
%cd path os.chdir
!command 一個被擷取的子行程
%%bash 儲存格其餘部分當作 shell 腳本執行
%%timeit 1000 次計時執行

真正關鍵的是 %pip 的快取逐出:對一個已在運作中的直譯器安裝套件,對已經匯入的模組毫無作用,因此少了它,實際在跑的根本不是你剛剛安裝的那個版本。

paths = tool("find", {"pattern": "**/*.dart", "path": "lib"})
for path in paths.splitlines()[:100]:
contents = tool("read", {"path": path})
...

tool(name, args) 會重新進入代理自己的工具註冊表。也就是說,一個儲存格可以對上百個檔案扇出作業,或透過 task 委派,而不必讓模型為每一項各花一個回合。

橋接呼叫在尚未完成期間,會暫停儲存格的逾時計算。這個預算量度的是閒置,而不是實際流逝的時間——一個扇出到子代理、等了四分鐘的儲存格並沒有卡住,而在扇出途中把它殺掉,會令整個核心的狀態付諸流水。

eval(code: "…", reset: true, description: "Start a clean interpreter")

丟棄目前的直譯器,另起一個乾淨的。所有已載入的東西都會消失,而這正是重點。

對話有隔離環境時,就在其中執行;否則在主機上。

由模型讀取不受信任的程式庫所驅動的持久直譯器,正是「僅限隔離環境執行」存在的理由:一次性的 bash 呼叫至少會結束,而核心卻是一個會記住東西的 shell。

它不會為了這樣做而啟動隔離環境——這與阻止測試機分頁自動啟動的是同一條規則。若你想讓核心處於封閉環境中,請先開啟對話的 shell。詳見給代理一部測試用機器。

  • 每個核心一次只能執行一個儲存格。 兩個儲存格在同一個直譯器裏互相搶跑,就是兩個程式各自的一半交錯在同一個命名空間之中。
  • 輸出有上限。 陷入 print 迴圈的儲存格會被截斷,而不會放任它填滿上下文窗口。
  • 每個儲存格四張影像。 繪圖迴圈可能每迭代就輸出一張圖表,而每張影像在承載它的請求中,都是一筆可觀的固定成本。
  • 核心會隨執行結束而消亡。 對話結束後仍握着資料框的核心,就是一種洩漏。