PtcRunner.SubAgent.Compiler (PtcRunner v0.7.0)

Copy Markdown View Source

Compilation logic for SubAgents.

This module provides the compile/2 function that transforms a SubAgent into a CompiledAgent by running it once with an LLM to derive the PTC-Lisp program. The resulting CompiledAgent can then be executed many times without further LLM calls.

SubAgentTools Support

Compiled agents can include SubAgentTools. The orchestrator's PTC-Lisp code is deterministic (sequencing tool calls), while SubAgentTools execute their child agents with an LLM at runtime.

When executing a compiled agent with SubAgentTools, pass the LLM at runtime:

{:ok, compiled} = SubAgent.compile(orchestrator, llm: compile_llm)
compiled.execute.(%{topic: "cats"}, llm: runtime_llm)

See PtcRunner.SubAgent.compile/2 for the public API.

Summary

Functions

Compiles a SubAgent into a reusable PTC-Lisp function.

Functions

compile(agent, opts)

@spec compile(
  PtcRunner.SubAgent.t(),
  keyword()
) :: {:ok, PtcRunner.SubAgent.CompiledAgent.t()} | {:error, PtcRunner.Step.t()}

Compiles a SubAgent into a reusable PTC-Lisp function.

The LLM is called once during compilation to derive the logic. The resulting CompiledAgent can then be executed many times without further LLM calls, making it efficient for processing many items with deterministic logic.

Requirements

  • max_turns: 1 - Only single-shot agents can be compiled
  • output: :ptc_lisp - Only PTC-Lisp output mode (not :json)

Tool Support

  • Pure Elixir tools - Supported, executed directly
  • LLMTool - Supported (requires LLM at runtime)
  • SubAgentTool - Supported if child agent has no mission_timeout

When SubAgentTools are present, the compiled agent requires an llm option at execute time for the child agents.

Options

  • llm - Required. LLM callback used once during compilation. Can be a function or atom.
  • llm_registry - Required if llm is an atom. Maps atoms to LLM callbacks.
  • sample - Optional sample data to help LLM understand the input structure (default: %{})

Returns

  • {:ok, CompiledAgent.t()} - Successfully compiled agent
  • {:error, Step.t()} - Compilation failed (agent execution failed)

Examples

iex> tools = %{"double" => fn %{"n" => n} -> n * 2 end}
iex> agent = PtcRunner.SubAgent.new(
...>   prompt: "Double the input number {{n}}",
...>   signature: "(n :int) -> {result :int}",
...>   tools: tools,
...>   max_turns: 1
...> )
iex> mock_llm = fn _ -> {:ok, ~S|(return {:result (tool/double {:n data/n})})|} end
iex> {:ok, compiled} = PtcRunner.SubAgent.Compiler.compile(agent, llm: mock_llm, sample: %{n: 5})
iex> compiled.signature
"(n :int) -> {result :int}"
iex> is_binary(compiled.source)
true
iex> is_function(compiled.execute, 2)
true
iex> result = compiled.execute.(%{n: 10}, [])
iex> result.return.result
20

Agents with LLMTool compile successfully (LLMTool requires LLM at runtime):

iex> alias PtcRunner.SubAgent.LLMTool
iex> tools = %{"classify" => LLMTool.new(prompt: "Classify {{x}}", signature: "(x :string) -> :string")}
iex> agent = PtcRunner.SubAgent.new(prompt: "Process {{item}}", signature: "(item :string) -> {category :string}", tools: tools, max_turns: 1)
iex> {:ok, compiled} = PtcRunner.SubAgent.Compiler.compile(agent, llm: fn _ -> {:ok, ~S|(return {:category "test"})|} end)
iex> compiled.llm_required?
true