ai newsDo., 15. Okt.
EilmeldungMeta and Apple team up to make Muse AI models run better on Macs

Sechs typische Fehlerquellen beim Claude Tool Use im Produktivbetrieb

Tool use macht Claude zum arbeitenden Assistenten, erzeugt aber vor allem unter echten Bedingungen viele Bugs. Eine Entwicklerin beschreibt die häufigsten Fehlermodi und ihre Gegenmassnahmen aus der Praxis.

dev.toDonnerstag 04:32 · 15.10.DE5 Min. Lesezeit
Bild: dev.to

Tool-Nutzung ist das Feature, das Claude von einem Chat-Modell zu etwas macht, das tatsächlich Arbeit erledigen kann. Sie ist auch die Quelle der meisten Produktionsfehler, die ich erlebe.

Keiner dieser Fehler tritt in einem Notebook auf. Sie zeigen sich erst, wenn echte Nutzer, echte APIs und echte Latenz aufeinandertreffen. Hier sind die sechs, die ich am häufigsten sehe, und was ich jeweils dagegen tue.

Das Schema sagt, dass customer_id ein String ist. Das Modell sendet "CUST-00123 " mit einem Leerzeichen am Ende oder einen Namen statt einer ID.

Was ich tue: Ich validiere jede Tool-Eingabe vor der Ausführung und gebe eine klare Fehlermeldung zurück, mit der das Modell arbeiten kann.

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 ) Enter fullscreen mode Exit fullscreen mode Wenn man die Validierungsmeldung als Tool-Ergebnis zurückgibt, kann sich das Modell im nächsten Durchlauf selbst korrigieren, statt die gesamte Aufgabe scheitern zu lassen.

Eine Schleife, die immer wieder search_orders mit leicht abweichenden Abfragen aufruft, verbrennt Tokens und Rate-Limits.

Was ich tue: Ich führe ein Budget pro Aufgabe für jedes Tool und teile dem Modell mit, wenn es aufgebraucht ist.

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 ) Enter fullscreen mode Exit fullscreen mode 3. Langsame Tools bringen das Gespräch durcheinander Manche interne APIs brauchen 20 Sekunden. Die Nutzer halten den Assistenten für kaputt und laden die Seite neu.

Was ich tue: Ich setze explizite Timeouts, sende dem Nutzer eine kurze Statusmeldung als Stream und gebe dem Modell ein Timeout-Ergebnis zurück, damit es die Verzögerung erklären oder ein leichteres Tool ausprobieren kann.

Jemand ändert eine API, sodass status jetzt "closed" statt "resolved" zurückgibt. Die Tool-Beschreibung nennt weiterhin "resolved", und das Modell trifft falsche Entscheidungen, ohne dass ein Fehler auftritt.

Was ich tue: Ich behandle Tool-Beschreibungen wie Code. Sie liegen neben der Integration, werden im selben Pull Request geprüft, und eine kleine Testsuite spielt echte Tool-Ausgaben gegen das erwartete Modellverhalten ab.

Gibt man dem Modell 40 Tools, sinkt die Genauigkeit, die Latenz steigt und die Kosten gehen nach oben.

Was ich tue: Zuerst routen, dann eine kleine Tool-Auswahl freigeben. Ein günstiger Klassifikationsschritt entscheidet, ob es bei der Anfrage um Rechnungen, Bestellungen oder Kontoänderungen geht, und nur die fünf oder sechs relevanten Tools werden an den Hauptaufruf übergeben.

Erstattungen, Stornierungen und ausgehende E-Mails sollten nicht einfach deshalb passieren, weil das Modell es beschlossen hat.

Was ich tue: Ich markiere diese Tools als genehmigungspflichtig. Das Tool gibt ein pending_approval-Ergebnis mit einer Zusammenfassung zurück, eine Person bestätigt, und erst dann läuft der echte Aufruf. Nutzern fällt bei Aktionen, die ihnen wichtig sind, ein zusätzlicher Klick selten negativ auf.

Unit-Tests für die Tools selbst sind notwendig, aber nicht ausreichend. Der riskante Teil ist die Interaktion zwischen Modell und Tools, daher pflege ich eine kleine Verhaltenstestsuite, die gegen das echte Modell läuft.

Jeder Testfall ist ein kurzes Gespräch plus eine Erwartung darüber, welche Tools in welcher Reihenfolge aufgerufen werden sollen und welche niemals aufgerufen werden dürfen.

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 " ] Enter fullscreen mode Exit fullscreen mode Ich beginne mit etwa zwanzig Fällen aus echten Gesprächen und füge jedes Mal einen neuen hinzu, wenn ein Bug bis in die Produktion durchrutscht. Die Suite läuft bei jeder Prompt-Änderung, jeder Änderung an Tool-Beschreibungen und jedem Modell-Upgrade.

Weil Modellausgaben variieren, führe ich jeden Fall einige Male aus und alarmiere bei der Fehlerquote statt bei einem einzelnen Durchlauf.

Das findet mehr Regressionen als alles andere, was ich tue. Es ist besonders gut darin, Fehlermodus 4 zu entdecken, weil eine veraltete Tool-Beschreibung sich fast immer darin zeigt, dass das Modell in einem dieser Fälle das falsche Tool aufruft.

Die Tool-Zuverlässigkeit unterscheidet sich je nach Modellgröße. Nach meiner Erfahrung kommen kleinere Modelle damit zurecht, ein Tool aus einer kurzen Liste auszuwählen, während mehrstufige Pläne mit mehreren abhängigen Tool-Aufrufen von einem größeren Modell profitieren. Ich route entsprechend: Ein schnelles Modell übernimmt Klassifikation und einfache Nachschlageabfragen, und ein stärkeres springt ein, wenn die Aufgabe Planung erfordert. Testen Sie das allerdings mit Ihren eigenen Fällen. Die richtige Aufteilung hängt stark davon ab, wie Ihre Tools beschrieben sind.

Wenn Sie das größere Bild sehen möchten, auch wie sich function calling in mehrstufige Agent-Loops einfügt, habe ich einen ausführlicheren Leitfaden zu function calling und agentischen Workflows mit Claude geschrieben.

Ich baue und deploye Enterprise-Agents bei AI Hive. Welcher Fehlermodus setzt Ihnen am meisten zu? Ich würde auf Nummer 4 wetten.

Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.

Original (Englisch) anzeigen

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.

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 ) Enter fullscreen mode Exit fullscreen mode Returning the validation message as the tool result lets the model correct itself on the next turn instead of failing the whole task.

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 ) Enter fullscreen mode Exit fullscreen mode 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.

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.

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.

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.

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 " ] Enter fullscreen mode Exit fullscreen mode 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.

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.

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.

Originaltitel: Claude Tool Use in Production: 6 Failure Modes and How I Handle ThemQuelle: dev.to (dev.to). Original öffnen