他讓 Claude 串接客戶公司裡的請假系統、報帳系統、客戶名單查詢等好幾百支工具,想做出一個什麼都能問、什麼都能辦的 AI 助理
「Claude Cookbook」是 Anthropic 官方自己整理的一本『食譜書』,裡面收錄了他們實際測試過、真的有效的開發技巧,不是隨便寫寫的入門教學。這次上熱門的重點,是教開發者怎麼解決一個很實際的麻煩:當你想讓 Claude(一款AI)幫你操作很多不同系統的功能,也就是常說的工具呼叫讓AI不只回答文字,還能實際去操作外部系統,例如查資料庫、送出表單、開機器手臂,工具一多,AI 就開始變笨重、變貴、變慢。
第一招叫『程式化工具呼叫』(Programmatic Tool Calling,簡稱 PTC)。以前的做法是:AI 每次只能決定呼叫『一支』工具,呼叫完要把結果傳回去,AI 才能決定下一步該呼叫哪一支,像接力賽一樣一棒接一棒、每一棒都要停下來交接。PTC 讓 Claude 直接『寫一段程式碼』,程式碼裡可以連續呼叫好幾支工具、自己做判斷、自己整理資料,整段丟進一個程式碼執行環境一個讓AI寫的程式碼可以真的被執行、跑出結果的隔離小房間,AI寫完程式碼馬上能看到執行結果裡跑一次就好,不用一直來回交接,省下時間也省下權杖AI處理文字的最小計價單位,白話講就是AI界的字數計費單位,用越多字通常花越多錢、跑越慢。
第二招叫『用嵌入搜尋找工具』。如果一個 AI 系統串接了成千上萬支工具(大公司內部系統真的可能這麼多),沒辦法每次都把全部工具的說明塞進對話裡——那樣上下文AI這次對話記得的所有內容,包括你打的字、AI回的字、還有它讀過的工具說明會直接爆掉。解法是先用語意搜尋不是比對關鍵字,而是讓電腦理解意思相近的東西,例如你打怎麼退貨也能找到寫著退款流程的說明,只挑出跟這次任務最相關的幾支工具說明,才交給 Claude。
食譜書裡還有其他幾招:長時間掛著跑的 AI 任務,系統會自動幫對話『瘦身』(context compaction);分析圖表或文件時,給 Claude 一支『裁圖工具』讓它自己放大看細節;還有怎麼下指令讓 Claude 做出來的網頁設計不要千篇一律。這些都是 Anthropic 自己內部驗證過、開發者可以直接抄的實戰做法,不是空泛的『AI 概念課』。
| 比較項目 | 傳統工具呼叫 | 程式化工具呼叫(PTC) |
|---|---|---|
| 每一步怎麼決定 | AI 每次只能決定呼叫『一支』工具,呼叫完要等結果回來才能決定下一步 | AI 一次寫出一整段程式碼,裡面可以連續呼叫好幾支工具、自己做判斷 |
| 對話要來回幾輪 | 工具串越多步驟,對話來回的輪數越多 | 常常一輪就能把多步驟的任務做完 |
| 速度與費用 | 每一輪都要重新讀一次完整上下文,延遲跟權杖花費會一直疊加 | 省下中間反覆的來回,延遲跟花費明顯降低 |
這段程式在幹嘛:模擬公司裡有 10 支工具(真實情況可能有幾千支),先用陽春版的『語意搜尋』從裡面篩出跟使用者問題最相關的 3 支,再把這 3 支交給 Claude 決定要呼叫哪一支,而不是把全部工具塞給它。
def text_to_vector(text):bigrams = [text[i:i + 2] for i in range(len(text) - 1)]def cosine_similarity(vec_a, vec_b):def find_relevant_tools(user_query, top_k=3):scored.sort(key=lambda pair: pair[0], reverse=True)tools=[to_claude_tool_schema(tool) for tool in relevant_tools],elif block.type == "tool_use":這是一個真實小專案:下載(或複製)檔案,照步驟在你電腦上跑起來。
"""
smart_tool_picker.py
教學示範:當公司裡的工具多到爆炸(想像 200 支工具),
怎麼用「語意搜尋」先篩出跟使用者需求最相關的幾支工具,
再交給 Claude 決定要呼叫哪一支,而不是把 200 支工具說明全部塞進提示詞。
執行前準備:
1. pip install anthropic
2. 設定環境變數 ANTHROPIC_API_KEY(去 Anthropic Console 申請)
"""
import math
import os
from collections import Counter
import anthropic
# 模擬公司裡「多到爆炸」的工具清單(實務上可能有幾百到幾千支)
TOOL_LIBRARY = [
{"name": "query_leave_balance", "description": "查詢員工目前剩餘的特休天數"},
{"name": "submit_leave_request", "description": "送出請假申請,需要開始日期、結束日期、假別"},
{"name": "query_expense_status", "description": "查詢一筆報帳單目前審核到哪個階段"},
{"name": "submit_expense_report", "description": "送出新的報帳單,附上金額與收據描述"},
{"name": "search_customer_profile", "description": "用客戶姓名或編號查詢客戶基本資料"},
{"name": "query_order_status", "description": "查詢一筆訂單目前的出貨進度"},
{"name": "book_meeting_room", "description": "預約會議室,需要日期、時段、人數"},
{"name": "query_payroll_date", "description": "查詢這個月薪水什麼時候會入帳"},
{"name": "reset_employee_password", "description": "重設員工的內部系統登入密碼"},
{"name": "query_warehouse_stock", "description": "查詢某項商品在倉庫的目前庫存數量"},
]
def text_to_vector(text):
"""把一段文字轉成「字元二連字」的出現次數向量,
這是最陽春版的語意搜尋:用重疊的字元組合,粗略估計兩段文字像不像。
正式產品通常會改用像 Voyage AI 這種訓練過的嵌入模型,準確度高很多。"""
text = text.replace(" ", "")
bigrams = [text[i:i + 2] for i in range(len(text) - 1)]
return Counter(bigrams)
def cosine_similarity(vec_a, vec_b):
common = set(vec_a) & set(vec_b)
dot = sum(vec_a[k] * vec_b[k] for k in common)
norm_a = math.sqrt(sum(v * v for v in vec_a.values()))
norm_b = math.sqrt(sum(v * v for v in vec_b.values()))
if norm_a == 0 or norm_b == 0:
return 0.0
return dot / (norm_a * norm_b)
def find_relevant_tools(user_query, top_k=3):
"""從整個工具庫裡,只挑出跟使用者需求最相關的 top_k 支工具。"""
query_vec = text_to_vector(user_query)
scored = []
for tool in TOOL_LIBRARY:
tool_vec = text_to_vector(tool["description"])
score = cosine_similarity(query_vec, tool_vec)
scored.append((score, tool))
scored.sort(key=lambda pair: pair[0], reverse=True)
return [tool for score, tool in scored[:top_k]]
def to_claude_tool_schema(tool):
return {
"name": tool["name"],
"description": tool["description"],
"input_schema": {"type": "object", "properties": {}},
}
def main():
user_query = "我想請下週三的特休,要怎麼申請"
relevant_tools = find_relevant_tools(user_query, top_k=3)
print("=== 語意搜尋篩出的候選工具 ===")
for tool in relevant_tools:
print(f"- {tool['name']}:{tool['description']}")
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=[to_claude_tool_schema(tool) for tool in relevant_tools],
messages=[{"role": "user", "content": user_query}],
)
print("\n=== Claude 的回應 ===")
for block in response.content:
if block.type == "text":
print("文字回覆:", block.text)
elif block.type == "tool_use":
print(f"Claude 決定呼叫工具:{block.name},參數:{block.input}")
if __name__ == "__main__":
main()