Claude 工具調用在生產環境中的 6 種故障模式及處理方法
Claude Tool Use in Production: 6 Failure Modes and How I Handle Them
作者整理 Claude 工具調用在生產環境中的 6 種常見故障,並逐一説明相應的處理方法。建議包括校驗工具輸入、設定呼叫預算和超時、只提供相關工具,以及為不可逆操作設定人工審批。作者以約 20 個源自真實對話的行為測試,檢查工具呼叫次序及不應呼叫的工具,並在提示詞、工具描述或模型更新時執行測試。
Tool use is the feature that turns Claude from a chat model into something that can actually do work. It is also where most of my production bugs come from.
None of these failures show up in a notebook. They show up when real users, real APIs, and real latency meet. Here are the six I see most often and what I do about each.
1. The model calls a tool with arguments that are almost right
The schema says customer_id is a string. The model sends "CUST-00123 " with a trailing space, or a name instead of an ID.
What I do: validate every tool input before execution and return a clear error the model can act on.
from pydantic import BaseModel, ValidationError, field_validator
class LookupCustomer(BaseModel):
customer_id: str
@field_validator("customer_id")
@classmethod
def clean(cls, v):
v = v.strip().upper()
if not v.startswith("CUST-"):
raise ValueError("customer_id must look like CUST-12345")
return v
def run_lookup(raw_args):
try:
args = LookupCustomer(**raw_args)
except ValidationError as e:
return {"error": "invalid_arguments", "detail": str(e)}
return crm.get_customer(args.customer_id)
Returning the validation message as the tool result lets the model correct itself on the next turn instead of failing the whole task.
2. The model calls the right tool too many times
A loop that keeps calling search_orders with slightly different queries burns tokens and rate limits.
What I do: keep a per-task budget for each tool and tell the model when it runs out.
from collections import Counter
MAX_CALLS = {"search_orders": 3, "send_email": 1}
def guarded_call(task_state, name, args):
used = task_state.setdefault("tool_counts", Counter())
if used[name] >= MAX_CALLS.get(name, 5):
return {"error": "budget_exceeded",
"detail": f"{name} already called {used[name]} times. Summarize what you have."}
used[name] += 1
return TOOLS[name](**args)
3. Slow tools break the conversation
Some internal APIs take 20 seconds. Users assume the assistant is broken and refresh the page.
What I do: set explicit timeouts, stream a short status message to the user, and return a timeout result to the model so it can explain the delay or try a lighter tool.
4. Tool descriptions drift from reality
Someone changes an API so status now returns "closed" instead of "resolved". The tool description still says "resolved", and the model starts making wrong decisions without any error.
What I do: treat tool descriptions as code. They live next to the integration, they are reviewed in the same pull request, and a small test suite replays real tool outputs against expected model behaviour.
5. Too many tools in one prompt
Give the model 40 tools and accuracy drops, latency rises, and costs go up.
What I do: route first, then expose a small toolset. A cheap classification step decides whether the request is about billing, orders, or account changes, and only the five or six relevant tools are passed to the main call.
6. Irreversible actions with no checkpoint
Refunds, cancellations, and outbound emails should not happen just because the model decided they should.
What I do: mark those tools as requiring approval. The tool returns a pending_approval result with a summary, a person confirms, and only then does the real call run. Users rarely mind one extra click on actions they care about.
How I test tool use before a release
Unit tests on the tools themselves are necessary but not enough. The risky part is the interaction between the model and the tools, so I keep a small behavioural test suite that runs against the real model.
Each test case is a short conversation plus an expectation about which tools should be called, in what order, and which should never be called.
CASES = [
{
"messages": [{"role": "user", "content": "Where is my order 88412?"}],
"must_call": ["lookup_order"],
"must_not_call": ["create_refund", "send_email"],
},
{
"messages": [{"role": "user", "content": "Refund order 88412, it arrived broken"}],
"must_call": ["lookup_order", "create_refund"],
"expect_status": "pending_approval",
},
]
def run_case(case):
calls = []
result = agent.run(case["messages"], on_tool_call=lambda name, args: calls.append(name))
for name in case.get("must_call", []):
assert name in calls, f"expected {name}, got {calls}"
for name in case.get("must_not_call", []):
assert name not in calls, f"{name} should not be called"
if "expect_status" in case:
assert result.status == case["expect_status"]
I start with around twenty cases taken from real conversations and add one every time a bug reaches production. The suite runs on every prompt change, every tool description change, and every model upgrade. Because model outputs vary, I run each case a few times and alert on the failure rate rather than on a single run.
This catches more regressions than anything else I do. It is especially good at catching failure mode 4, because a stale tool description almost always shows up as the model calling the wrong tool in one of these cases.
Choosing which model to call for tool-heavy work
Tool reliability differs between model sizes. In my experience, smaller models are fine for picking one tool from a short list, while multi-step plans with several dependent tool calls benefit from a larger model. I route accordingly: a fast model handles classification and simple lookups, and a stronger one takes over when the task needs planning. Test this on your own cases, though. The right split depends heavily on how your tools are described.
A small checklist before going live
- Every tool validates its inputs and returns machine-readable errors.
- Every tool has a timeout and a per-task call budget.
- Tool descriptions are versioned and tested alongside the code.
- The model sees only the tools relevant to the current request.
- Irreversible actions require explicit approval.
- Every call is logged with inputs, outputs, latency, and a trace ID.
If you want the broader picture, including how function calling fits into multi-step agent loops, I wrote a longer guide on function calling and agentic workflows with Claude.
I build and deploy enterprise agents at AI Hive. Which failure mode bites you most often? I'd bet on number 4.
來源:DEV Community · dev.to
