這篇是給「每次 CI 掛掉都要翻 300 行 log」的人。你很可能就是那個人。

先講結論

我用 Claude API 的 Tool Use 做了一個小管線:讓 Claude 先判斷要讀哪一段 log、呼叫本地 tool 抓出重點、再輸出可以直接貼到 GitHub issue 的錯誤報告。這套流程我直接塞進 OpenClaw 的內容產生工作流,CI 失敗時不用再手動翻 log。

這篇只講三個重點:

  1. Tool Use 怎麼設計才不會一直回 JSON parse error
  2. 失敗 log 太長時的切片策略
  3. 我踩過的坑(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 的輸入要乾淨。我一開始把 filePathstartLineendLine 都放一起,結果 Claude 有時候會輸出字串數字(“120”)導致 parse 失敗。

後來我改成:只傳 pathlineRange(字串),再自己 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

延伸閱讀


基於實際專案經驗撰寫。程式碼可直接執行,但請先準備 ANTHROPIC_API_KEY。