PtcRunner
View SourceA BEAM-native Elixir library for Programmatic Tool Calling (PTC). Execute LLM-generated programs that orchestrate tools and transform data safely inside sandboxed processes.
What is PTC?
Programmatic Tool Calling is an execution model where an LLM writes small programs to process data, rather than making individual tool calls. Instead of returning large datasets to the model (which bloats context), the model generates a program that calls tools, filters/transforms results, and returns only the final answer.
The pattern was introduced by Anthropic in their blog posts on advanced tool use and code execution with MCP.
Quick Example
iex> tools = %{
...> "get_expenses" => fn _args ->
...> [
...> %{"category" => "travel", "amount" => 500},
...> %{"category" => "food", "amount" => 50},
...> %{"category" => "travel", "amount" => 200}
...> ]
...> end
...> }
iex> program = ~S|{"program": {"op": "pipe", "steps": [
...> {"op": "call", "tool": "get_expenses"},
...> {"op": "filter", "where": {"op": "eq", "field": "category", "value": "travel"}},
...> {"op": "sum", "field": "amount"}
...> ]}}|
iex> {:ok, result, _memory_delta, _new_memory} = PtcRunner.Json.run(program, tools: tools)
iex> result
700PTC-Lisp (same result, more compact):
iex> tools = %{"get-expenses" => fn _args ->
...> [%{"category" => "travel", "amount" => 500},
...> %{"category" => "food", "amount" => 50},
...> %{"category" => "travel", "amount" => 200}]
...> end}
iex> program = ~S|(->> (call "get-expenses" {}) (filter (where :category = "travel")) (sum-by :amount))|
iex> {:ok, result, _, _} = PtcRunner.Lisp.run(program, tools: tools)
iex> result
700Why PTC?
Traditional tool calling requires multiple LLM round-trips:
LLM → get_employees() → LLM → filter(dept=eng) → LLM → avg(salary) → LLMWith PTC, the LLM writes one program executed locally:
(->> (call "get-employees" {}) (filter (where :department = "engineering")) (avg-by :salary))Benchmark snapshot (DeepSeek V3.2, 15 queries, 2500 records):
| DSL | Passed | Avg Attempts | Cost |
|---|---|---|---|
| PTC-JSON | 15/15 | 1.3 | $0.002 |
| PTC-Lisp | 14/15 | 1.2 | $0.002 |
Small sample—see Performance and Use Cases for methodology.
Two DSLs — same engine, same operations:
- PTC-JSON — Easy to generate and validate
- PTC-Lisp — 8x fewer output tokens, Clojure-compatible
Installation
def deps do
[{:ptc_runner, "~> 0.3"}]
endFeatures
- Two DSLs: JSON (verbose, universal) and Lisp (compact, LLM-friendly)
- Safe: Fixed operations, no arbitrary code execution
- Fast: Isolated BEAM processes with configurable timeout (1s) and memory (10MB) limits
- Simple: No external dependencies (Python, containers, etc.)
- Cost-efficient: Tested with budget models (DeepSeek 3.2, Gemini 2.5 Flash)
- Retry-friendly: Structured errors with actionable messages for LLM retry loops
- Stateful: Context refs enable persistent memory across agentic loop iterations
Documentation
- Guide - Architecture, API reference, detailed examples
- Performance and Use Cases - Benchmarks, cost analysis, when to use PTC
- PTC-JSON Specification - Complete JSON DSL reference
- PTC-Lisp Overview - Lisp DSL introduction
License
MIT