---
title: "Tool calls that do not break the chat"
lesson_id: "03"
---

# Tool calls that do not break the chat

- **Lesson ID:** 03
- **Goal:** Build a tool loop that never saves a broken tool call into history.
- **Human lesson:** [03-tool-calls-that-do-not-break.html](03-tool-calls-that-do-not-break.html)

## Prerequisites

- Lesson 01 environment.
- A running llama-server on port 8080.

## Inputs, outputs, and artifacts

- **Inputs:** A tool list and a user question.
- **Outputs:** A final answer, or a clear failure message after retries.
- **Artifacts:** `tool_loop.py` and `safe_parse.py`.

## Agent build steps

1. Save `safe_parse.py` (lesson 02) and `tool_loop.py` from the lesson.
2. Keep tools flat: string, number, boolean fields, required fields first.
3. In `check_args`, reject unknown tool names, broken JSON, missing required fields, and extra fields.
4. On a bad reply, log the error and retry without appending the reply to history.
5. Set `max_tokens`, `max_steps`, and `--reasoning-budget` or `--reasoning off` where needed.
6. Run `uv run python tool_loop.py` and confirm a final answer.

## Constraints

Keep the language simple, friendly, and literal. No quizzes, no em dashes. Brand Edge FDE only. Use small local models. Do not invent flags, endpoints, or issue numbers. 

## Key concepts

- One bad schema can break the grammar for every tool in the request.
- A broken tool call saved in history can break every later turn.
- Check tool name, JSON, required fields, and extra fields before you run a tool.
- Cap tokens, thinking, and loop steps.

## Takeaways

- Keep tool schemas small, flat, and few.
- Log bad turns. Do not append them to history. Retry instead.
- Test your own tool list on your own llama.cpp or Ollama build.

## Acceptance checks

- `json.loads('{')` style failures are caught and retried.
- No failed turn is appended to `history`.
- Issues #22948, #25923, #30056, #30321 and Ollama #17778 are linked.
- [ ] Proceed to [lesson 04 brief](04-json-mode-and-tools.llms.md).
