這篇是給「每次 CI 掛掉都要翻 300 行 log」的人。你很可能就是那個人。
先講結論
我用 Claude API 的 Tool Use 做了一個小管線:讓 Claude 先判斷要讀哪一段 log、呼叫本地 tool 抓出重點、再輸出可以直接貼到 GitHub issue 的錯誤報告。這套流程我直接塞進 OpenClaw 的內容產生工作流,CI 失敗時不用再手動翻 log。
這篇只講三個重點:
- Tool Use 怎麼設計才不會一直回 JSON parse error
- 失敗 log 太長時的切片策略
- 我踩過的坑(permission prompt / log encoding)
背景:為什麼不用「直接丟整段 log」
你一定做過這件事:把整段 log 貼進 Claude,請它找錯誤。效果當然有,但缺點很明顯:
- 300 行 log 輕鬆爆 token
- Claude 常常抓錯重點(尤其是 build 前的 warn)
- 還是得手動整理成 issue 模板
我想要的是一個能「自動縮小範圍 + 有結構輸出」的流程,所以最後變成:Claude 先做判斷、需要時再叫 tool 讀檔。
實作步驟
Step 1:準備 Node.js + Claude SDK
npm init -y
npm install @anthropic-ai/sdk我用 Node.js 是因為我們 OpenClaw 的自動化本來就跑在 Node 上。下面的範例也可以直接塞到你自己的 CI script。
Step 2:定義一個「可讀 log」的 Tool
重點:tool 的輸入要乾淨。我一開始把 filePath、startLine、endLine 都放一起,結果 Claude 有時候會輸出字串數字(“120”)導致 parse 失敗。
後來我改成:只傳 path 和 lineRange(字串),再自己 parse。比較穩。
// log-tool.ts
import fs from "fs";
export function readLogSlice(path: string, lineRange: string) {
const [startStr, endStr] = lineRange.split("-");
const start = Number(startStr || 1);
const end = Number(endStr || start + 200);
const lines = fs.readFileSync(path, "utf8").split("\n");
return lines.slice(start - 1, end).join("\n");
}Tool schema:
const tools = [
{
name: "read_log_slice",
description: "Read a slice of a CI log file by line range.",
input_schema: {
type: "object",
properties: {
path: { type: "string" },
lineRange: { type: "string", description: "e.g. 120-220" }
},
required: ["path", "lineRange"]
}
}
];Step 3:讓 Claude 自己決定要讀哪段
我把 prompt 寫得很「工具導向」。Claude 先用摘要判斷,再叫 tool。這樣 log 太長也不怕。
import Anthropic from "@anthropic-ai/sdk";
import { readLogSlice } from "./log-tool.js";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });
const logSummary = fs.readFileSync("./ci.log", "utf8").split("\n").slice(0, 80).join("\n");
const response = await client.messages.create({
model: "claude-3-5-sonnet",
max_tokens: 800,
tools,
messages: [
{
role: "user",
content: `你是 CI log 分析助手。這是 log 前 80 行:\n\n${logSummary}\n\n請判斷錯誤可能在哪一段,並呼叫 read_log_slice 取得那段完整內容,再輸出 JSON 報告。`
}
]
});Tool call 進來後,把結果丟回 Claude:
const toolCall = response.content.find(c => c.type === "tool_use");
if (toolCall?.name === "read_log_slice") {
const { path, lineRange } = toolCall.input;
const slice = readLogSlice(path, lineRange);
const followUp = await client.messages.create({
model: "claude-3-5-sonnet",
max_tokens: 800,
tools,
messages: [
...response.messages,
{
role: "tool",
tool_use_id: toolCall.id,
content: slice
}
]
});
console.log(followUp.content);
}Step 4:輸出 issue-ready 的報告格式
我讓 Claude 直接輸出這個 schema:
{
"title": "Build failed: missing env var",
"root_cause": "CI missing OPENAI_API_KEY in secrets",
"suspected_commit": "abc123",
"log_excerpt": "...",
"next_action": "Add secret to GitHub Actions and rerun"
}然後我在 OpenClaw 的 pipeline 中把 JSON 包成 GitHub issue 模板。終於不用每次手動整理了。
實際成果
- 失敗 log 平均分析時間從 8-10 分鐘變成 30 秒內
- issue 內容更一致,別人接手也比較快懂
- log 太長時,Claude 會自動要求更多切片,不用我手動挑
踩坑紀錄(真的不要小看這些)
1) tool input 不要太多欄位
Claude 會亂填,尤其是數字欄位。我試過 startLine: "120" 就直接炸。最後用 lineRange: "120-220",自己 parse。
2) log encoding 不是 UTF-8 就會亂
我們有一個舊 pipeline 產生的 log 是 Big5,Claude 讀起來全是亂碼。最後用 iconv-lite 在 tool 端先轉成 UTF-8。
3) Claude Code 權限提示會更嚴格
我用 Claude Code v2.1.98 之後,Bash tool 權限提示變多了。原本偷懶寫 cat ./logs/*.log,現在會被擋。乾脆寫清楚路徑 + allowlist,結果更安全也更可控。
我對 Tool Use 的心得(短版)
- Tool Use 的價值不是讓 Claude 幫你「做事」,而是讓它幫你決定要做哪件事
- 如果你讓 Claude 一次拿到全部資料,反而會丟掉最有用的信號
- 最好的 tool 是「窄而明確」的,不要做一個超級 tool
延伸閱讀
- Claude Cookbooks(tool use/integration):https://github.com/anthropics/claude-cookbooks
- Claude Code release notes(權限與工具更新):https://github.com/anthropics/claude-code/releases
基於實際專案經驗撰寫。程式碼可直接執行,但請先準備 ANTHROPIC_API_KEY。