Cheat sheets

Agent Tool Use and Skill Libraries

Define, validate, dispatch and secure tool calls. Store skills as code, retrieve, compose and refine them.

  • Concepts and terms
  • Tasks and commands
  • Annotated code
  • Diagrams
  • Comparisons
  • Defaults and numbers
  • Troubleshooting
  • Questions and tips

Source

The function calling loop#

messagemessages + tool definitionstool_callexecuteresultschemasclientUsersends a messageprocessApplicationexecutes toolsmodelModeldecides the callprocessToolget_weather("Tokyo")storeTool definitionsJSON Schemamessagemessages + tool definitionstool_callexecuteresultschemasclientUsersends a messageprocessApplicationexecutes toolsmodelModeldecides the callprocessToolget_weather("Tokyo")storeTool definitionsJSON Schema

Tool schema#

{"type": "function", "function": {
"name": "get_weather",
"description": "Get current weather for a city.",a prompt for tool selection
"parameters": {"type": "object",Anthropic: input_schema
"properties": {"city": {"type": "string"},
"units": {"enum": ["celsius", "fahrenheit"]}},
"required": ["city"]}}}missing field: error observation

Providers side by side#

FactorOpenAIAnthropicGoogle
Tools parametertoolstoolsfunction_declarations
Call formattool_calls[].functioncontent[].type="tool_use"functionCall
Schema fieldfunction.parametersinput_schemaJSON Schema
Forced calltool_choice="required"tool_choice={"type":"any"}function_calling_config
Result goes back as{"role": "tool", "tool_call_id": ...}type: "tool_result" in a user messageNot in the lesson table

Tasks in code#

TaskCode
Register a toolreg.register(ToolDef(...))
Show the catalog to the modelreg.catalog()
Run calls of one turnreg.dispatch_many(calls)
Check args against a schemavalidate(args, schema)
Run the loop with a caprun_function_calling_loop(user_message, max_iterations=5)
Find skills for a tasklib.search("I need a pickaxe")
Run a skill with its depslib.execute("craft_iron_pickaxe")
Run the demopython3 code/main.py

Registry with structured errors#

reg = ToolRegistry()
reg.register(ToolDef(name="classify", description="<when to use>",
input_schema={"type": "object", "required": ["status"],
"properties": {"status": {"type": "string",
"enum": ["open", "closed", "pending"]}}},
executor=classify))timeout_s defaults to 5.0
calls = [ToolCall("u02", "multiply", {"a": "4", "b": 5}),"4" coerces
ToolCall("u03", "classify", {"status": "in_progress"})]not in enum
for result in reg.dispatch_many(calls):
result.ok, result.contenterrors are strings, never raised

Skill library loop#

lib = SkillLibrary()
lib.register(Skill(name="craft_iron_pickaxe", description="<what it does>",
code="mine_ore(); place_table(); craft('iron_pickaxe')",
fn=_craft_iron_pick_v1, tags=("craft", "tool"),
depends_on=("mine_ore", "place_crafting_table")))v1
lib.search("I need a pickaxe")token overlap, top_k=3
ctx = lib.execute("craft_iron_pickaxe")deps first, topo_order
ctx["failed"]True: need 2 stick, have 0
lib.register(Skill(..., fn=_craft_iron_pick_v2))same name: v2

Numbers that matter#

BFCL V4
40% agentic, 30% multi-turn, 10% live, 10% non-live, 10% hallucination.
Parallel calls
cut round trips from 2 to 1.
Parallel latency
60-80% less for 5-10 tool calls per query.
Call budget
10-20 calls per conversation is reasonable.
Voyager
3.3x more unique items, 8.5x faster stone tools, 6.4x faster iron tools.
Toolformer
keep a tool call only when its result reduces next-token loss.

Voyager refinement loop#

1Curriculumproposes a task just above current capability.2Libraryreturns top-k skills by query-to-descriptionsimilarity.3Agentwrites or composes a skill as executable code.4Environmentruns the skill and returns success, error orself-verification failure.5Agentrewrites the skill with the signal as context.6Librarystores the new version. The loop stops atsuccess or max rounds.

Validation failures and fixes#

SymptomCauseFix
error: unknown tool 'subtract'Model calls a tool that does not existReturn the error. Add a no-op tool to refuse
missing required: <name>Required field absentSend the error back. The model retries
'in_progress' not in [...]Value outside the enumReject with a descriptive error
Wrong tool pickedBad tool descriptionSay what the tool does and when to use it
Results routed to wrong callsSwapped tool_use_id valuesCorrelate each result by its ID
Retrieval gets worseLibrary past a few hundred skillsAdd tag filters and hard constraints

Defaults in the lesson code#

SettingDefaultNote
timeout_s5.0Per tool in ToolDef
version1New Skill
dedupTrueSame name refines the skill
top_k3Skill search results
tag_filterNoneSet it to limit search to one tag
max_iterations5run_function_calling_loop

Security rules#

  • Never pass model-generated SQL directly to a database. Parameterize it.
  • Allowlist functions. Expose only the tools the user needs.
  • Validate every argument for type, range and format.
  • Sanitize tool results. Remove API keys, PII and internal errors.
  • run_code parses the code with ast.parse and blocks imports and dunder access.
  • Run untrusted code in a separate process or container.

Questions#

Who executes the tool?
Your code. The model only decides what to call.
Structured outputs or function calling?
Structured outputs for data extraction. Function calling to act on external systems.
Why return errors as results?
Models self-correct from structured errors. They fail on empty or generic errors.
What is a skill?
A named chunk of code plus a description, retrievable by similarity.
Why version skills?
A parent pinned to v1 does not pick up v3 of a child.