# `ADK.InstructionCompiler`
[🔗](https://github.com/zeroasterisk/adk-elixir/blob/main/lib/adk/instruction_compiler.ex#L1)

Compiles the system instruction for an LLM agent by combining:

- Global instruction (from agent or parent)
- Agent's own instruction (with template variable substitution from state)
- Identity/persona instruction
- Output schema instruction
- Transfer instructions (listing available sub-agents)

This mirrors Python ADK's `BaseLlmFlow._compile_system_instruction()`.

## InstructionProvider

Both the `instruction` and `global_instruction` fields on `ADK.Agent.LlmAgent`
support dynamic providers in addition to static strings:

- `String.t()` — static instruction (existing behaviour, unchanged)
- `(ADK.Context.t() -> String.t())` — 1-arity anonymous function called at runtime
- `{module, atom}` — MFA with 1 arg (context appended): `module.atom(ctx)`
- `{module, atom, extra_args}` — MFA with extra args (context prepended): `module.atom(ctx, extra_args...)`

The provider is called once per invocation, just before template-variable
substitution, so the returned string still supports `{variable}` interpolation.

If a provider returns a non-binary value, it is coerced via `to_string/1`.
If the provider raises, the error is logged and an empty string is used so
the agent can still respond.

# `compile`

```elixir
@spec compile(map(), ADK.Context.t()) :: String.t()
```

Compile the full system instruction for an agent given the context.

Returns a single string combining all instruction components.

# `compile_split`

```elixir
@spec compile_split(map(), ADK.Context.t()) :: {String.t(), String.t()}
```

Split compiled instructions into static and dynamic portions.

- **Static**: Parts that don't change between requests — global instruction,
  identity, transfer instructions. Suitable for Gemini's context caching.
- **Dynamic**: Parts that change per request — agent instruction with state
  variable substitution, output schema instruction.

Returns `{static_instruction, dynamic_instruction}` where either may be an
empty string (but never nil).

# `resolve_provider`

```elixir
@spec resolve_provider(term(), ADK.Context.t() | nil) :: String.t() | nil
```

Resolve an instruction provider to a string.

Handles:
- `String.t()` — returned as-is
- `(ctx -> String.t())` — called with the context (may be nil for global)
- `{module, atom}` — called as `module.atom(ctx)`
- `{module, atom, extra_args}` — called as `module.atom(ctx, extra_args...)`

Non-binary return values are coerced via `to_string/1`. Errors are caught
and an empty string is returned (with a warning logged).

# `substitute_vars`

```elixir
@spec substitute_vars(String.t(), map()) :: String.t()
```

Substitute template variables in an instruction string.

Variables use the `{key}` syntax. Values are looked up from session state.

## Examples

    iex> ADK.InstructionCompiler.substitute_vars("Hello {name}!", %{"name" => "World"})
    "Hello World!"

    iex> ADK.InstructionCompiler.substitute_vars("No vars here", %{})
    "No vars here"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
