Write schemas for decisions, not decoration
Give every function a specific name, a decision-useful description, and a JSON Schema that rejects ambiguous input. Descriptions should state when to call the function and when not to.
Keep a versioned mapping from exposed tool name to implementation. Never use model-provided text as a module name, command, URL, or database query without independent validation.
Dispatch every requested call
A response can request more than one function. Validate each request, apply authorization, execute within a timeout, and return a result tied to the original call identifier. Process parallel calls only when their side effects are independent.
Return compact, typed results. If a tool fails, return a bounded error the model can reason about; do not paste stack traces or credentials back into the conversation.
- Unknown tool: reject.
- Invalid arguments: report schema failure.
- Duplicate request: use an idempotency record.
- Timeout: stop and expose a recoverable state.
Bound the continuation loop
After tool results are supplied, the model may request another call or produce a final answer. Cap total turns, tool invocations, elapsed time, and cost. A loop that reaches a limit should return a useful partial result with an explicit reason.
Test the dispatcher independently from model behavior. Unit tests should prove authorization, schema rejection, duplicate handling, timeouts, and redaction.