Section: Core Features · URL: https://hermesbible.com/docs/user-guide/features/code-execution
程式碼執行(Programmatic Tool Calling)
execute_code 工具讓 agent 撰寫 Python 腳本,以程式化方式呼叫 Hermes 工具,將多步驟工作流程壓縮為單一 LLM 回合。腳本在 agent 主機上的子程序中運行,透過 Unix domain socket RPC 與 Hermes 通訊。
運作原理
- Agent 使用
from hermes_tools import ...撰寫 Python 腳本 - Hermes 生成包含 RPC 函數的
hermes_tools.py模組存根 - 開啟 Unix domain socket 並啟動 RPC 監聽執行緒
- 腳本在子程序中運行——工具呼叫透過 socket 回傳至 Hermes
- 只有腳本的
print()輸出會回傳給 LLM;中間的工具結果不會進入 context window
# Agent 可以撰寫這樣的腳本:
from hermes_tools import web_search, web_extract
results = web_search("Python 3.13 features", limit=5)
for r in results["data"]["web"]:
content = web_extract([r["url"]])
# ... 篩選與處理 ...
print(summary)
腳本中可用的工具: web_search、web_extract、read_file、write_file、search_files、patch、terminal(僅前台模式)。
使用時機
Agent 會在以下情況使用 execute_code:
- 需要 3 次以上工具呼叫且呼叫之間有處理邏輯
- 需要大量資料篩選或條件分支
- 需要迴圈遍歷結果
關鍵優勢:中間的工具結果不會進入 context window——只有最終的 print() 輸出會回傳,大幅降低 token 使用量。
實際範例
資料處理管線
from hermes_tools import search_files, read_file
import json
# 找出所有設定檔並擷取資料庫設定
matches = search_files("database", path=".", file_glob="*.yaml", limit=20)
configs = []
for match in matches.get("matches", []):
content = read_file(match["path"])
configs.append({"file": match["path"], "preview": content["content"][:200]})
print(json.dumps(configs, indent=2))
多步驟網路研究
from hermes_tools import web_search, web_extract
import json
# 在一個回合中搜尋、擷取並摘要
results = web_search("Rust async runtime comparison 2025", limit=5)
summaries = []
for r in results["data"]["web"]:
page = web_extract([r["url"]])
for p in page.get("results", []):
if p.get("content"):
summaries.append({
"title": r["title"],
"url": r["url"],
"excerpt": p["content"][:500]
})
print(json.dumps(summaries, indent=2))
大量檔案重構
from hermes_tools import search_files, read_file, patch
# 找出所有使用已棄用 API 的 Python 檔案並修復它們
matches = search_files("old_api_call", path="src/", file_glob="*.py")
fixed = 0
for match in matches.get("matches", []):
result = patch(
path=match["path"],
old_string="old_api_call(",
new_string="new_api_call(",
replace_all=True
)
if "error" not in str(result):
fixed += 1
print(f"Fixed {fixed} files out of {len(matches.get('matches', []))} matches")
建構與測試管線
from hermes_tools import terminal, read_file
import json
# 執行測試、解析結果並報告
result = terminal("cd /project && python -m pytest --tb=short -q 2>&1", timeout=120)
output = result.get("output", "")
# 解析測試輸出
passed = output.count(" passed")
failed = output.count(" failed")
errors = output.count(" error")
report = {
"passed": passed,
"failed": failed,
"errors": errors,
"exit_code": result.get("exit_code", -1),
"summary": output[-500:] if len(output) > 500 else output
}
print(json.dumps(report, indent=2))
執行模式
execute_code 有兩種執行模式,透過 ~/.hermes/config.yaml 中的 code_execution.mode 控制:
| 模式 | 工作目錄 | Python 解釋器 |
|---|---|---|
project(預設) | Session 的工作目錄(與 terminal() 相同) | 使用啟用中的 VIRTUAL_ENV / CONDA_PREFIX python,若無法使用則退回 Hermes 自身的 python |
strict | 與使用者專案隔離的暫存目錄 | sys.executable(Hermes 自身的 python) |
何時保持 project: 當你希望 import pandas、from my_project import foo 或相對路徑如 open(".env") 的運作方式與 terminal() 中相同。這幾乎總是符合需求的選擇。
何時切換至 strict: 當你需要最大程度的可重現性——每次 session 都使用相同的解釋器,不論使用者啟用了哪個 venv,且腳本與專案目錄樹隔離(不會因相對路徑而意外讀取專案檔案)。
# ~/.hermes/config.yaml
code_execution:
mode: project # 或 "strict"
project 模式的退回行為:若 VIRTUAL_ENV / CONDA_PREFIX 未設定、損壞或指向低於 3.8 的 Python 版本,解析器會平順地退回至 sys.executable——永遠不會讓 agent 沒有可用的解釋器。
兩種模式的安全關鍵不變量完全相同:
- 環境變數清理(移除 API key、token、憑證)
- 工具白名單(腳本不能遞迴呼叫
execute_code、delegate_task或 MCP 工具) - 資源限制(逾時、stdout 上限、工具呼叫上限)
切換模式只會改變腳本的運行位置和使用的解釋器,不會改變它們能看到的憑證或能呼叫的工具。
資源限制
| 資源 | 限制 | 說明 |
|---|---|---|
| 逾時 | 5 分鐘(300 秒) | 腳本先收到 SIGTERM,5 秒緩衝後收到 SIGKILL |
| Stdout | 50 KB | 輸出被截斷並附上 [output truncated at 50KB] 提示 |
| Stderr | 10 KB | 非零退出碼時包含在輸出中,方便除錯 |
| 工具呼叫 | 每次執行最多 50 次 | 達到上限時回傳錯誤 |
所有限制均可透過 config.yaml 設定:
# In ~/.hermes/config.yaml
code_execution:
mode: project # project(預設)| strict
timeout: 300 # 每個腳本最大秒數(預設:300)
max_tool_calls: 50 # 每次執行最大工具呼叫次數(預設:50)
腳本內工具呼叫的運作方式
當你的腳本呼叫如 web_search("query") 的函數時:
- 呼叫被序列化為 JSON,透過 Unix domain socket 傳送至父程序
- 父程序透過標準的
handle_function_call處理器進行分派 - 結果透過 socket 傳回
- 函數回傳解析後的結果
這表示腳本內的工具呼叫與正常工具呼叫的行為完全相同——相同的速率限制、相同的錯誤處理、相同的能力。唯一的限制是 terminal() 只支援前台模式(不支援 background 或 pty 參數)。
錯誤處理
當腳本失敗時,agent 會收到結構化的錯誤資訊:
- 非零退出碼:stderr 包含在輸出中,讓 agent 看到完整的堆疊追蹤
- 逾時:腳本被終止,agent 看到
"Script timed out after 300s and was killed." - 中斷:若使用者在執行期間發送新訊息,腳本會被終止,agent 看到
[execution interrupted — user sent a new message] - 工具呼叫上限:達到 50 次呼叫上限時,後續的工具呼叫會回傳錯誤訊息
回應一律包含 status(success/error/timeout/interrupted)、output、tool_calls_made 和 duration_seconds。
安全性
警告——安全模型
子程序在最小化環境中運行。API key、token 和憑證預設會被移除。腳本透過 RPC 通道存取工具——除非明確允許,否則無法從環境變數中讀取機密。
名稱中包含 KEY、TOKEN、SECRET、PASSWORD、CREDENTIAL、PASSWD 或 AUTH 的環境變數會被排除。只有安全的系統變數(PATH、HOME、LANG、SHELL、PYTHONPATH、VIRTUAL_ENV 等)會被傳入。
Skill 環境變數透傳
當 skill 在 frontmatter 中宣告 required_environment_variables 時,那些變數會在 skill 載入後自動透傳至 execute_code 和 terminal 子程序。這讓 skill 能使用其宣告的 API key,而無需削弱任意程式碼的安全性。
對於非 skill 的使用情境,你可以在 config.yaml 中明確允許清單變數:
terminal:
env_passthrough:
- MY_CUSTOM_KEY
- ANOTHER_TOKEN
詳見安全性指南。
子程序中的 HERMES_* 變數
子程序只會接收一小組固定的運作用 HERMES_* 變數(依名稱精確匹配):
HERMES_HOMEHERMES_PROFILEHERMES_CONFIGHERMES_ENV
(以及 HERMES_RPC_DIR / HERMES_RPC_SOCKET / TZ / HOME,由 Hermes 明確注入以確保 RPC 通道正常運作。)
注意——行為變更
早期版本會將所有名稱以
HERMES_開頭的變數傳入子程序。該寬泛的前綴已因安全性強化而移除:它可能洩漏不符合機密子字串的HERMES_*命名設定(例如HERMES_BASE_URL、HERMES_KANBAN_DB或HERMES_*_WEBHOOK端點)至任意沙箱化程式碼中。如果
execute_code腳本——或其在匯入時載入的 repo/plug-in 模組——依賴上述四個運作名稱之外的HERMES_*變數,現在會發現該變數在子程序中未設定。這是刻意的移除,不是 Bug。
解決方法——將變數重新加入允許清單。 兩種途徑都會將變數透傳至 execute_code 和 terminal 子程序,且都不會削弱機密移除保證(Hermes 管理的 provider 憑證永遠無法透過此方式重新允許):
-
每台機器,在
config.yaml中——將確切的變數名稱加入透傳允許清單:terminal: env_passthrough: - HERMES_KANBAN_DB - HERMES_BASE_URL -
每個 skill,在 skill 的 frontmatter 中——宣告它,以便在該 skill 載入時自動註冊:
required_environment_variables: - HERMES_KANBAN_DB
診斷方式。 當子程序移除一個或多個未加入允許清單的 HERMES_* 變數時,Hermes 會發出一行 debug 日誌,列出變數名稱並指向 env_passthrough 逃脫機制。以偵錯日誌運行(hermes logs --level DEBUG,或檢查 ~/.hermes/logs/agent.log),搜尋 execute_code: dropped N non-allowlisted HERMES_* var(s) 即可判斷腳本是否因 HERMES_* 變數缺失而異常。
Hermes 會將腳本和自動生成的 hermes_tools.py RPC 存根寫入暫存目錄,執行完畢後清理。在 strict 模式下,腳本也在該目錄中執行;在 project 模式下,腳本在 session 的工作目錄中執行(暫存目錄會保留在 PYTHONPATH 上,因此 import 仍可正常解析)。子程序在自己的程序群組中運行,以便在逾時或中斷時能被乾淨地終止。
execute_code 與 terminal 的比較
| 使用情境 | execute_code | terminal |
|---|---|---|
| 需要多步驟工作流程且工具呼叫之間有處理邏輯 | ✅ | ❌ |
| 簡單的 shell 指令 | ❌ | ✅ |
| 篩選或處理大量工具輸出 | ✅ | ❌ |
| 執行建構或測試套件 | ❌ | ✅ |
| 迴圈遍歷搜尋結果 | ✅ | ❌ |
| 互動式/背景程序 | ❌ | ✅ |
| 需要環境變數中的 API key | ⚠️ 僅透過透傳 | ✅(大多數會透傳) |
經驗法則: 當你需要以程式化方式呼叫 Hermes 工具且呼叫之間有處理邏輯時,使用 execute_code。執行 shell 指令、建構和程序時,使用 terminal。
平台支援
程式碼執行需要 Unix domain socket,僅支援 Linux 和 macOS。在 Windows 上會自動停用——agent 會退回至一般的順序工具呼叫。