|
|
|
@@ -0,0 +1,239 @@
|
|
|
|
|
# How to Improve Ollama Tool-Calling Prompt Cache Reuse in nanobot
|
|
|
|
|
|
|
|
|
|
Some Ollama model templates move or remove tool definitions as a conversation
|
|
|
|
|
switches between user, assistant, and tool messages. nanobot can send a correct
|
|
|
|
|
append-only chat request while the model template still renders a different token
|
|
|
|
|
prefix. On slower local hardware, re-evaluating that prefix can add tens of seconds
|
|
|
|
|
to an otherwise simple tool-using turn.
|
|
|
|
|
|
|
|
|
|
This guide shows how to diagnose that specific pattern and create a derived
|
|
|
|
|
`llama3.1:8b` tag with a prefix-stable tool template. It does not modify nanobot or
|
|
|
|
|
overwrite the original Ollama model.
|
|
|
|
|
|
|
|
|
|
## What you will build
|
|
|
|
|
|
|
|
|
|
- a repeatable two-turn cache check
|
|
|
|
|
- an optional derived `llama3.1:8b-prefix-stable-v1` Ollama tag
|
|
|
|
|
- a nanobot model preset that uses the derived tag
|
|
|
|
|
|
|
|
|
|
## When to use this
|
|
|
|
|
|
|
|
|
|
Use this guide when all of the following are true:
|
|
|
|
|
|
|
|
|
|
- direct Ollama responses are reasonably fast;
|
|
|
|
|
- nanobot becomes slow after the model calls a tool;
|
|
|
|
|
- Ollama logs show a long main prompt, a much shorter tool follow-up, and low
|
|
|
|
|
initial cache reuse on the next main prompt;
|
|
|
|
|
- the model is `llama3.1:8b` with a template that renders concrete tools only for
|
|
|
|
|
the final user message.
|
|
|
|
|
|
|
|
|
|
Do not apply this template to another model family without checking that model's
|
|
|
|
|
tool-call format first.
|
|
|
|
|
|
|
|
|
|
## Diagnose the rendered prompt
|
|
|
|
|
|
|
|
|
|
Stop any existing Ollama process, then start a single-slot debug server. A single
|
|
|
|
|
slot makes the cache sequence easier to read.
|
|
|
|
|
|
|
|
|
|
**macOS or Linux**
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
OLLAMA_CONTEXT_LENGTH=16384 \
|
|
|
|
|
OLLAMA_NUM_PARALLEL=1 \
|
|
|
|
|
OLLAMA_DEBUG=1 \
|
|
|
|
|
ollama serve
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Windows PowerShell**
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
$env:OLLAMA_CONTEXT_LENGTH = "16384"
|
|
|
|
|
$env:OLLAMA_NUM_PARALLEL = "1"
|
|
|
|
|
$env:OLLAMA_DEBUG = "1"
|
|
|
|
|
ollama serve
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In another terminal, use a fresh session and explicitly request a tool so both
|
|
|
|
|
turns exercise the agent loop:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
nanobot agent --session cli:ollama-cache-check \
|
|
|
|
|
--message "Use the exec tool to calculate 2+2, then answer"
|
|
|
|
|
nanobot agent --session cli:ollama-cache-check \
|
|
|
|
|
--message "Use the exec tool to calculate 4+7, then answer"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In the Ollama output, find each `new prompt` line and the first
|
|
|
|
|
`cached n_tokens` line that follows it. Later increasing `cached n_tokens` lines
|
|
|
|
|
are prompt-evaluation progress, not additional initial cache hits.
|
|
|
|
|
|
|
|
|
|
A cache-unfriendly tool template may produce a pattern like this:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
turn 1 main: 2 / 8460 initially cached
|
|
|
|
|
turn 1 tool follow-up: 3713 / 3758 initially cached
|
|
|
|
|
turn 2 main: 3767 / 8519 initially cached
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The cache is working, but the next main request can reuse only the shorter prompt.
|
|
|
|
|
Hardware throughput determines how expensive the remaining evaluation is.
|
|
|
|
|
|
|
|
|
|
To inspect the API request bodies as well, add
|
|
|
|
|
`OLLAMA_DEBUG_LOG_REQUESTS=1` before starting Ollama. These logs can contain system
|
|
|
|
|
prompts, workspace context, and user messages. Keep them local and disable request
|
|
|
|
|
logging after diagnosis.
|
|
|
|
|
|
|
|
|
|
## Why this happens with the stock template
|
|
|
|
|
|
|
|
|
|
The tested `llama3.1:8b` template conditionally expands the tool definitions inside
|
|
|
|
|
a user message:
|
|
|
|
|
|
|
|
|
|
```gotemplate
|
|
|
|
|
{{- if and $.Tools $last }}
|
|
|
|
|
... render tool definitions ...
|
|
|
|
|
{{- end }}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The first request ends with a user message, so the tools are rendered there. After
|
|
|
|
|
nanobot appends an assistant tool call and its result, that user message is no
|
|
|
|
|
longer last, so the same API request history renders without the concrete tool
|
|
|
|
|
block. On the next user turn, the tools reappear at a new position.
|
|
|
|
|
|
|
|
|
|
This is a model-template behavior. At the API boundary, nanobot continues to append
|
|
|
|
|
the assistant tool call and tool result and sends the same tool definitions.
|
|
|
|
|
|
|
|
|
|
## Create a prefix-stable derived model
|
|
|
|
|
|
|
|
|
|
Create `PrefixStable.Modelfile` with the content below. The template keeps concrete
|
|
|
|
|
tool definitions in the system block, where they remain in the same position across
|
|
|
|
|
user and tool messages.
|
|
|
|
|
|
|
|
|
|
```dockerfile
|
|
|
|
|
FROM llama3.1:8b
|
|
|
|
|
|
|
|
|
|
TEMPLATE """{{- if or .System .Tools }}<|start_header_id|>system<|end_header_id|>
|
|
|
|
|
{{- if .System }}
|
|
|
|
|
|
|
|
|
|
{{ .System }}
|
|
|
|
|
{{- end }}
|
|
|
|
|
{{- if .Tools }}
|
|
|
|
|
|
|
|
|
|
Cutting Knowledge Date: December 2023
|
|
|
|
|
|
|
|
|
|
When you receive a tool call response, use the output to format an answer to the original user question.
|
|
|
|
|
|
|
|
|
|
You are a helpful assistant with tool calling capabilities.
|
|
|
|
|
|
|
|
|
|
Given the following functions, respond with a JSON function call with the proper arguments when a tool is needed.
|
|
|
|
|
|
|
|
|
|
Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}. Do not use variables.
|
|
|
|
|
|
|
|
|
|
{{ range .Tools }}
|
|
|
|
|
{{- . }}
|
|
|
|
|
{{ end }}
|
|
|
|
|
{{- end }}<|eot_id|>
|
|
|
|
|
{{- end }}
|
|
|
|
|
{{- range $i, $_ := .Messages }}
|
|
|
|
|
{{- $last := eq (len (slice $.Messages $i)) 1 }}
|
|
|
|
|
{{- if eq .Role "user" }}<|start_header_id|>user<|end_header_id|>
|
|
|
|
|
|
|
|
|
|
{{ .Content }}<|eot_id|>{{ if $last }}<|start_header_id|>assistant<|end_header_id|>
|
|
|
|
|
|
|
|
|
|
{{ end }}
|
|
|
|
|
{{- else if eq .Role "assistant" }}<|start_header_id|>assistant<|end_header_id|>
|
|
|
|
|
{{- if .ToolCalls }}
|
|
|
|
|
{{ range .ToolCalls }}
|
|
|
|
|
{"name": "{{ .Function.Name }}", "parameters": {{ .Function.Arguments }}}{{ end }}
|
|
|
|
|
{{- else }}
|
|
|
|
|
|
|
|
|
|
{{ .Content }}
|
|
|
|
|
{{- end }}{{ if not $last }}<|eot_id|>{{ end }}
|
|
|
|
|
{{- else if eq .Role "tool" }}<|start_header_id|>ipython<|end_header_id|>
|
|
|
|
|
|
|
|
|
|
{{ .Content }}<|eot_id|>{{ if $last }}<|start_header_id|>assistant<|end_header_id|>
|
|
|
|
|
|
|
|
|
|
{{ end }}
|
|
|
|
|
{{- end }}
|
|
|
|
|
{{- end }}"""
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Create the new tag:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
ollama create llama3.1:8b-prefix-stable-v1 -f PrefixStable.Modelfile
|
|
|
|
|
ollama list
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Ollama reuses the existing model layers. The new tag adds a small template and
|
|
|
|
|
manifest instead of copying the base weights.
|
|
|
|
|
|
|
|
|
|
## Select the derived model in nanobot
|
|
|
|
|
|
|
|
|
|
Merge this preset into `~/.nanobot/config.json` and select it:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"providers": {
|
|
|
|
|
"ollama": {
|
|
|
|
|
"apiBase": "http://localhost:11434/v1"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"modelPresets": {
|
|
|
|
|
"ollamaPrefixStable": {
|
|
|
|
|
"label": "Ollama Llama 3.1 prefix-stable",
|
|
|
|
|
"provider": "ollama",
|
|
|
|
|
"model": "llama3.1:8b-prefix-stable-v1",
|
|
|
|
|
"maxTokens": 2048,
|
|
|
|
|
"contextWindowTokens": 16384,
|
|
|
|
|
"temperature": 0.1
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"agents": {
|
|
|
|
|
"defaults": {
|
|
|
|
|
"modelPreset": "ollamaPrefixStable"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Verify the selected model and repeat the two-turn check:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
nanobot status
|
|
|
|
|
nanobot agent --session cli:ollama-stable-check \
|
|
|
|
|
--message "Use the exec tool to calculate 2+2, then answer"
|
|
|
|
|
nanobot agent --session cli:ollama-stable-check \
|
|
|
|
|
--message "Use the exec tool to calculate 4+7, then answer"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In one controlled test with Ollama 0.32.1, `llama3.1:8b`, and one slot, the second
|
|
|
|
|
main request improved from `3767 / 8519` initially cached (44.22%) to
|
|
|
|
|
`8505 / 8520` (99.82%). The number of re-evaluated tokens fell from 4752 to 15.
|
|
|
|
|
Treat these numbers as a diagnostic example, not a performance guarantee.
|
|
|
|
|
|
|
|
|
|
## Roll back
|
|
|
|
|
|
|
|
|
|
Switch `agents.defaults.modelPreset` back to the original preset. When no config
|
|
|
|
|
uses the derived tag, remove it with:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
ollama rm llama3.1:8b-prefix-stable-v1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Removing the derived tag does not remove `llama3.1:8b`.
|
|
|
|
|
|
|
|
|
|
## Limitations
|
|
|
|
|
|
|
|
|
|
- The template above is specific to the tested `llama3.1:8b` tool-call format.
|
|
|
|
|
- Ollama or the model publisher may update the stock template in a later release.
|
|
|
|
|
- Validate multiple tool calls, tool errors, parallel calls, and long conversations
|
|
|
|
|
before using a custom template for unattended workloads.
|
|
|
|
|
- A higher cache ratio reduces prompt evaluation, but model generation, tool
|
|
|
|
|
execution, process startup, and storage can still dominate end-to-end latency.
|
|
|
|
|
- Multiple Ollama slots change cache scheduling and may produce different results.
|
|
|
|
|
|
|
|
|
|
## Related nanobot docs
|
|
|
|
|
|
|
|
|
|
- [Provider Cookbook: Ollama Local Model](../provider-cookbook.md#recipe-ollama-local-model)
|
|
|
|
|
- [Providers and Models: Ollama](../providers.md#ollama)
|
|
|
|
|
- [Troubleshooting](../troubleshooting.md)
|