Planck.Agent.AgentSpec (Planck.Agent v0.1.0)

Copy Markdown View Source

Static, serializable agent definition.

An AgentSpec is the shape used both by team members loaded from disk (via Planck.Agent.Team) and by the orchestrator's spawn_agent tool when it creates workers at runtime. It contains only serializable fields — no execute_fn. Tool wiring is merged in programmatically before starting the agent.

Member-entry JSON schema

This is the format for a single entry in a team's members list:

{
  "type":          "builder",
  "name":          "Builder Joe",
  "description":   "Writes and edits code.",
  "provider":      "anthropic",
  "model_id":      "claude-sonnet-4-6",
  "system_prompt": "You are an expert builder.",
  "opts":          { "temperature": 0.7 },
  "tools":         ["read", "write", "edit", "bash"],
  "skills":        ["code_review", "refactor"]
}

Required fields are type, provider, model_id. Valid providers are derived from Planck.AI.Model.providers/0.

system_prompt is either inline text or a path to a .md/.txt file resolved relative to a caller-provided base_dir. tools and skills are lists of names resolved against caller-provided pools at start time (see to_start_opts/2). When skills is non-empty, their descriptions are appended to the system prompt via Planck.Agent.Skill.system_prompt_section/1.

Construction

iex> AgentSpec.from_map(%{
...>   "type" => "builder",
...>   "provider" => "ollama",
...>   "model_id" => "llama3.2",
...>   "system_prompt" => "Build things."
...> })
{:ok, %AgentSpec{...}}

iex> AgentSpec.from_list(list_of_maps, base_dir: "/path/to/team")
[%AgentSpec{...}, ...]

Summary

Functions

Convert a list of maps (as decoded from JSON) into a list of AgentSpec structs.

Convert a single map into an AgentSpec struct.

Build an AgentSpec from a keyword list of validated fields.

Convert an AgentSpec to keyword options suitable for Planck.Agent.start_link/1.

Types

t()

@type t() :: %Planck.Agent.AgentSpec{
  base_url: String.t() | nil,
  compactor: String.t() | nil,
  description: String.t() | nil,
  model_id: String.t(),
  name: String.t(),
  opts: keyword(),
  provider: atom(),
  skills: [String.t()],
  system_prompt: String.t(),
  tools: [String.t()],
  type: String.t()
}
  • :type — role identifier used for registry lookups and tool targeting (e.g. "builder")
  • :name — human-readable label shown to other agents via list_team; defaults to type when not provided or empty
  • :description — one-line purpose shown to other agents via list_team
  • :provider — LLM provider atom (e.g. :anthropic, :ollama)
  • :model_id — model identifier within the provider (e.g. "claude-sonnet-4-6")
  • :system_prompt — system prompt text sent to the model at the start of every turn
  • :opts — provider-specific options forwarded to the LLM call (e.g. temperature:)
  • :tools — tool names to resolve from a tool_pool: at start time (e.g. ["read", "bash"])
  • :skills — skill names to resolve from a skill_pool: at start time; when non-empty, their descriptions are appended to system_prompt in to_start_opts/2
  • :base_url — base URL of the model server for local providers that run multiple instances (e.g. "http://localhost:11434" for a specific Ollama server). When nil, the provider's default URL is used.
  • :compactor — fully-qualified module name of a sidecar compactor for this agent, e.g. "MySidecar.Compactors.Builder". The module must implement compact/2. planck_headless resolves this via Planck.Agent.Sidecar.compactor_for/1 when materialising the agent. nil means the default compactor is used.

Functions

from_list(entries, opts \\ [])

@spec from_list(
  [map()],
  keyword()
) :: [t()]

Convert a list of maps (as decoded from JSON) into a list of AgentSpec structs.

Invalid entries are skipped with a warning; the rest are returned. Accepts base_dir: for resolving relative system_prompt file paths. Defaults to File.cwd!().

from_map(entry, base_dir \\ ".")

@spec from_map(map(), Path.t()) :: {:ok, t()} | {:error, String.t()}

Convert a single map into an AgentSpec struct.

Returns {:ok, spec} or {:error, reason}. system_prompt values ending in .md or .txt are treated as file paths and read from disk relative to base_dir.

new(fields)

@spec new(keyword()) :: t()

Build an AgentSpec from a keyword list of validated fields.

name defaults to type when not provided or empty — every agent has a human-readable label, and teams with multiple members of the same type are forced to assign explicit names (via Team.load/1's name-uniqueness check).

to_start_opts(spec, overrides \\ [])

@spec to_start_opts(
  t(),
  keyword()
) :: keyword()

Convert an AgentSpec to keyword options suitable for Planck.Agent.start_link/1.

Accepts optional overrides: tools:, tool_pool:, skill_pool:, team_id:, session_id:, available_models:, on_compact:.

Tool resolution

When spec.tools is non-empty, tool names are resolved against tool_pool: (a list of Tool.t() structs). Unknown names are silently ignored. Any tools passed via tools: are appended after the resolved ones. When spec.tools is empty, tools: is used directly.

Skill resolution

When spec.skills is non-empty, skill names are resolved against skill_pool: (a list of Skill.t() structs). The resolved skills' descriptions are appended to spec.system_prompt via Planck.Agent.Skill.system_prompt_section/1. Unknown names are silently ignored. When spec.skills is empty, system_prompt passes through unchanged.

Examples

iex> AgentSpec.to_start_opts(spec, tool_pool: [read_tool, bash_tool], team_id: "team-1")
[id: "...", type: "builder", tools: [read_tool], ...]