Tool calling
Las cuatro ops devuelven texto. Para que el modelo elija una tool (una decisión estructurada {tool, args}), Synsema agrega llm_step + call_tool. El loop de seguridad se escribe in-language, así el modelo nunca gana poderes nuevos.
-- Doc example: tool-calling. A "tool" is just a task; call() invokes it with named
-- args. The llm_step loop that lets the MODEL pick a tool is shown in the prose.
intent: "doc example: tool-calling"
require llm
task get_weather(city)
give "weather in " + city + ": sunny"
print(call(get_weather, {"city": "Lima"})) -- run shows: weather in Lima: sunny
test "a tool is a normal task; call() invokes it with named args from a map"
assert_eq(get_weather("Paris"), "weather in Paris: sunny")
assert_eq(call(get_weather, {"city": "Lima"}), "weather in Lima: sunny")
Las primitivas
llm_step(prompt, catalog, context)→ la decisión del modelo:{kind: "final", text, tokens}o{kind: "tool", name, args, tokens}.cataloges data plana;tokenste deja imponer un presupuesto.call(task, args_map)→ llama una task con args nombrados desde un map.call_tool(task, args_map)→ despacha una tool elegida con mínimo privilegio: corre solo con las capacidades que declaró (∩ las del agente), aunque el agente tenga más.
El loop seguro
require llm
task get_weather(city)
require net("api.weather.com") -- la declaración propia de la tool
give "weather in " + city + ": sunny"
let tools be {"get_weather": get_weather} -- ALLOW-LIST (nombre → task)
let catalog be [{"name": "get_weather", "describe": "Weather for a city", "params": ["city"]}]
task run_agent(question, max_steps, budget)
let spent be 0
let n be 0
let ctx be ""
while (n < max_steps) and (spent <= budget) -- loop acotado (guardia de presupuesto en la condición)
set n to n + 1
let step be llm_step(question, catalog, ctx)
set spent to spent + step["tokens"]
when step["kind"] == "final"
give step["text"]
otherwise when contains(tools, step["name"]) -- solo nombres en la allow-list despachan
let result be call_tool(tools[step["name"]], step["args"])
set ctx to ctx + " [" + step["name"] + " => " + text(result) + "]"
otherwise
set ctx to ctx + " [" + step["name"] + " => not allowed]" -- inyectado/alucinado → rechazado
give "out of steps"
El modelo solo devuelve un nombre de tool; tu programa decide si la corre. Seguridad = allow-list + gate de capacidad por task + intent congelado + loop acotado. Offline, llm_step devuelve {kind: "final", text: "[no llm provider]", tokens: 0}.