|
| 1 | +# Ollama Tool-Call Retry Adapter |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +When using Ollama models with OpenKB's `query` and `chat` commands, the |
| 6 | +openai-agents SDK tool-calling loop frequently fails because small local |
| 7 | +models hallucinate tool names instead of calling the registered tools. |
| 8 | + |
| 9 | +For example, when the only registered tool is `read_file`, a model may |
| 10 | +attempt to call `get_topics`, `search_strategy`, or `system` — none of |
| 11 | +which exist. The SDK then raises: |
| 12 | + |
| 13 | +``` |
| 14 | +ModelBehaviorError: Tool search_strategy not found in agent wiki-query |
| 15 | +``` |
| 16 | + |
| 17 | +and the entire query aborts with no recovery. |
| 18 | + |
| 19 | +This affects all Ollama models tested (issue #205): |
| 20 | +| Model | Symptom | |
| 21 | +|---|---| |
| 22 | +| `llama3.1:8b` | raw tool-call JSON; no final answer | |
| 23 | +| `qwen3:14b` | `{}` | |
| 24 | +| `qwen3.5:9b` | empty output | |
| 25 | +| `gemma4:12b` | timed out | |
| 26 | +| `deepseek-r1:14b` | `{}` | |
| 27 | +| `qwen2.5-coder:14b` | raw/incomplete response | |
| 28 | +| `llama3.2:1b` | raw function-call JSON | |
| 29 | + |
| 30 | +## Solution |
| 31 | + |
| 32 | +OpenKB now includes a **retry adapter** (`openkb/agent/ollama_adapter.py`) |
| 33 | +that intercepts `ModelBehaviorError` and retries the query with a |
| 34 | +corrective system message: |
| 35 | + |
| 36 | +``` |
| 37 | +[SYSTEM CORRECTION 1] You tried to call a tool named 'get_topics', |
| 38 | +but that tool does not exist. The only available tools are: |
| 39 | +read_file, get_page_content, get_image. Please answer the original |
| 40 | +question using ONLY these tools. Do not invent tool names. |
| 41 | +``` |
| 42 | + |
| 43 | +The retry adapter is **Ollama-only** — all other providers (OpenAI, |
| 44 | +Anthropic, Gemini, etc.) keep the original bare-Runner behaviour |
| 45 | +unchanged. |
| 46 | + |
| 47 | +## How It Works |
| 48 | + |
| 49 | +### Detection |
| 50 | + |
| 51 | +`is_ollama_backend(model)` returns `True` for model strings matching: |
| 52 | +- `ollama/llama3.2:1b` (LiteLLM prefix) |
| 53 | +- `litellm/ollama/llama3.2:1b` (Agent-layer prefix used by OpenKB) |
| 54 | + |
| 55 | +### Retry Flow |
| 56 | + |
| 57 | +1. The agent runs normally via `Runner.run()` / `Runner.run_streamed()`. |
| 58 | +2. If `ModelBehaviorError` is raised, the bad tool name is extracted |
| 59 | + from the error message. |
| 60 | +3. A corrective user message is appended to the input. |
| 61 | +4. The run is retried (up to `max_retries` times, default 3). |
| 62 | +5. If all retries are exhausted, the original error is re-raised. |
| 63 | + |
| 64 | +### Streaming |
| 65 | + |
| 66 | +For streamed queries, `_RetryableStreamResult` wraps the `RunResult` and |
| 67 | +intercepts `stream_events()`. If a `ModelBehaviorError` occurs mid-stream, |
| 68 | +the wrapper re-creates the stream with a corrective message and continues |
| 69 | +yielding events seamlessly. |
| 70 | + |
| 71 | +## Configuration |
| 72 | + |
| 73 | +The retry behaviour is automatic for Ollama models. No configuration is |
| 74 | +required. The default retry count is 3. |
| 75 | + |
| 76 | +Future config options (not yet implemented): |
| 77 | + |
| 78 | +```yaml |
| 79 | +ollama: |
| 80 | + tool_call_retries: 3 # max retry attempts (default 3) |
| 81 | + correct_tool_names: true # enable tool-name correction (default true) |
| 82 | +``` |
| 83 | +
|
| 84 | +## Files |
| 85 | +
|
| 86 | +| File | Description | |
| 87 | +|---|---| |
| 88 | +| `openkb/agent/ollama_adapter.py` | Retry adapter module | |
| 89 | +| `openkb/agent/query.py` | Patched to use adapter for Ollama models | |
| 90 | +| `tests/test_ollama_adapter.py` | 26 unit tests | |
| 91 | + |
| 92 | +## Testing |
| 93 | + |
| 94 | +```bash |
| 95 | +# Run adapter tests |
| 96 | +python -m pytest tests/test_ollama_adapter.py -v |
| 97 | +
|
| 98 | +# Run full test suite (non-Ollama path unchanged) |
| 99 | +python -m pytest tests/ -q |
| 100 | +``` |
| 101 | + |
| 102 | +## Limitations |
| 103 | + |
| 104 | +- Very small models (e.g. `llama3.2:1b`, 1B params) may fail even after |
| 105 | + retries — they are simply too small to follow tool-use instructions |
| 106 | + reliably. The adapter recovers the crash but cannot fix the model's |
| 107 | + fundamental inability. |
| 108 | +- The adapter does not modify the model's output format. If a model |
| 109 | + returns tool-call JSON in the `content` field instead of `tool_calls`, |
| 110 | + the SDK will still not execute the tool. A future enhancement could |
| 111 | + add content-to-tool-call extraction. |
| 112 | +- Timeout handling is not modified. Models that time out will still |
| 113 | + time out; the retry adapter only handles `ModelBehaviorError`. |
0 commit comments