Skip to content

Soul Files

Soul files are standalone YAML documents stored in custom/souls/. Each file defines one agent identity. There is no envelope or wrapper structure — the soul fields sit at the top level of the file.

  • Directorycustom/
    • Directorysouls/
      • researcher.yaml
      • editor.yaml
      • fetcher.yaml

The filename stem (the part before .yaml) is the soul’s lookup key. When a workflow block sets soul_ref: researcher, the parser resolves it to custom/souls/researcher.yaml.

Only .yaml files are discovered. Files with the .yml extension are ignored. Files whose names start with _ are not excluded — all .yaml files in the directory are loaded.

A soul file is flat YAML with no version field and no wrapping key. The file content maps directly to the soul definition:

custom/souls/researcher.yaml
id: researcher_v1
role: Senior Researcher
system_prompt: |
You are a senior research analyst. Given a topic, you produce
a structured report with findings, sources, and confidence levels.
Always cite your sources and flag low-confidence claims.
provider: openai
model_name: gpt-4o
temperature: 0.3
max_tokens: 4096
tools:
- http
max_tool_iterations: 5
avatar_color: accent

These fields must be present in every soul file. The parser raises a ValidationError if any are missing.

FieldTypeDescription
idstrUnique identifier for this soul. Can differ from the filename stem.
rolestrThe agent’s role (e.g., “Senior Researcher”). Displayed as the soul name in the UI.
system_promptstrSystem instructions defining the agent’s behavior and constraints.
FieldTypeDefaultDescription
model_namestrNoneModel identifier (e.g., gpt-4o, claude-sonnet-4). Falls back to the runner’s default if not set.
providerstrNoneProvider for the model (e.g., openai, anthropic). Falls back to the runner’s default if not set.
temperaturefloatNoneSampling temperature override. When None, uses the model’s default.
max_tokensintNoneOutput token limit override. When None, uses the model’s default.
toolslist[str]NoneTool IDs this soul can use. Each must also be declared in the workflow’s top-level tools: section.
required_tool_callslist[str]NoneLLM-facing tool function names that must be called before the agent completes.
max_tool_iterationsint5Maximum number of tool-use iterations per execution.
avatar_colorstrNoneUI color for displaying the soul. Accepts one of six preset tokens: accent, info, success, warning, danger, neutral.

The id field and the filename stem are independent. A file named researcher.yaml can contain id: researcher_v1. Workflow blocks reference the filename stem (soul_ref: researcher), not the internal id.

The id field is used by the engine at runtime to identify the soul object. The filename stem is the lookup key during YAML parsing and discovery.

Soul files are validated using Pydantic’s model_validate. The schema model (SoulDef in schema.py) enforces:

  • extra="forbid" — unknown fields raise a validation error. Only the fields listed above are accepted.
  • Required fieldsid, role, and system_prompt must be present. A missing field produces a Pydantic ValidationError.
  • Type checking — each field must match its declared type. A string where an integer is expected raises a validation error.

There are no ge, le, or min_length constraints on soul fields. The max_tool_iterations field accepts any integer.

When the parser encounters a soul_ref on a block, it resolves the reference through these steps:

  1. Discover external souls. The parser calls SoulScanner(base_dir).scan(), which scans custom/souls/ for .yaml files. Each file is loaded, validated against the Soul model, and stored in a dictionary keyed by filename stem.

  2. Merge inline souls. If the workflow YAML contains a souls: section, inline definitions are merged over the external map. When keys overlap, the inline soul wins and a warning is logged: "Inline soul 'X' overrides external soul file".

  3. Look up soul_ref. The block’s soul_ref value is looked up in the merged map. If not found, the parser raises a ValueError listing available souls.

A soul’s tools list declares which tools the agent needs, but every listed tool must also appear in the workflow’s top-level tools: section. If a soul references a tool not declared in the workflow, the parser raises a ValueError:

Soul 'fetcher' (custom/souls/fetcher.yaml) references undeclared tool 'http'.
Declared tools: []

This two-layer design gives workflow authors explicit control over which tools are available in a given workflow, regardless of what individual souls request.

The modified_at field is set by the API server when a soul is created or updated through the GUI. It stores a Unix timestamp (float). This field is not part of the engine’s runtime Soul model — it is metadata tracked by the API layer for display in the Soul Library’s “Modified” column.