# Methodology: Implementing Tool Calls over a Plain Chat API This document describes a practical pattern for supporting tool calling when the model only exposes a plain chat API. The core idea is: 1. Convert downstream tool definitions into a prompt-level contract. 2. Ask the model to emit structured action text. 3. Parse that action text in the proxy. 4. Re-encode it back into standard protocol fields such as OpenAI `tool_calls` or Anthropic `tool_use`. ## Core Pattern When the model does not support native tool calls, do not rely on blindly forwarding `tools`. Instead: - treat the model as a text generator - define a stable action DSL - keep the proxy responsible for state, retries, parsing, and protocol mapping In this project the action DSL is a fenced block: ```text ```json action {"tool":"NAME","parameters":{"key":"value"}} ``` ``` ## What the Proxy Must Do The proxy is not a passive transport anymore. Once tool tool calling is enabled, it should: - inject tool definitions into the prompt - preserve tool history across turns - project historical tool calls back into action text - wrap tool results into a continuation prompt - detect refusal patterns such as “I don't have tools” - retry with a stronger instruction when a tool call was expected but missing - map parsed actions back into downstream protocol fields ## Multi-turn Tool Calling Single-turn tool calling is not enough. A useful agent loop looks like this: 1. model emits a tool call 2. external executor runs the tool 3. tool result is fed back into the conversation 4. model decides whether to call another tool or finish To make this stable: - do not feed tool results back as raw text only - wrap them in a continuation message that clearly asks for the next action - keep tool calling active even when later turns do not repeat the original `tools` field That last point matters. Many clients send `tools` only on the first turn. The proxy should still keep the conversation in tool calling mode when it sees tool history. ## Few-shot Guidance The minimum few-shot should teach the model the output shape. A better few-shot also teaches state transitions: - when to call a tool - when to wait for the tool result - when to call another tool - when to answer normally For complex agent loops, a multi-step example with: - user request - assistant tool call - tool result - assistant next action is usually more effective than a single static action example. ## Retry Guidance Retry is useful when: - a tool call was expected but no action block was produced - the model says tools are unavailable - the request forces tool usage A retry prompt should be explicit and procedural, for example: ```text Your last response did not include any ```json action``` block. You must respond with at least one valid action block now. Do not explain. Output the action block directly. ``` Retries should be bounded. A small retry budget plus stronger instructions per retry is usually enough. ## Protocol Mapping OpenAI side: - input may contain `tools`, `tool_choice`, `assistant.tool_calls`, and `tool` - output should map back into `message.tool_calls` and `finish_reason = "tool_calls"` Anthropic side: - input may contain `tools`, `tool_choice`, `tool_use`, and `tool_result` - output should map back into `content[].tool_use` and `stop_reason = "tool_use"` ## Common Failure Modes - only supporting the first tool turn - losing tool calling state on later turns - not projecting historical tool calls back into text - feeding back raw tool results without continuation instructions - missing refusal detection - using a parser that is too brittle for real model output ## In This Repository The implementation here follows exactly this pattern: - downstream tool schemas are rewritten into prompt instructions - the model emits `json action` blocks - the proxy parses them - the proxy re-encodes them as OpenAI or Anthropic tool protocol fields - later turns can continue from tool history even when `tools` are not repeated Implementation checklist: - [tool-tool calling-checklist.md](./tool-tool calling-checklist.md)