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.
File location and naming
Section titled “File location and naming”Directorycustom/
Directorysouls/
- researcher.yaml
- editor.yaml
- fetcher.yaml
The embedded id is the soul’s lookup key. When a workflow block sets soul_ref: researcher, the parser resolves the soul whose embedded id is researcher; external soul files must use the same filename stem, such as 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.
File format
Section titled “File format”A soul file is flat YAML with no version field and no wrapping key. The file content maps directly to the soul definition:
id: researcherkind: soulname: Researcherrole: Senior Researchersystem_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: openaimodel_name: gpt-4otemperature: 0.3max_tokens: 4096tools: - httpmax_tool_iterations: 5avatar_color: accentField reference
Section titled “Field reference”Required fields
Section titled “Required fields”These fields must be present in every soul file. The parser raises a ValidationError if any are missing.
| Field | Type | Description |
|---|---|---|
id | str | Embedded soul id. Must match the filename stem for external soul files. |
kind | "soul" | Entity kind. Must be "soul". |
name | str | Display name for this soul. |
role | str | The agent’s role (e.g., “Senior Researcher”). Displayed as the soul name in the UI. |
system_prompt | str | System instructions defining the agent’s behavior and constraints. |
Optional fields
Section titled “Optional fields”| Field | Type | Default | Description |
|---|---|---|---|
model_name | str | None | Model identifier (e.g., gpt-4o, claude-sonnet-4). Falls back to the runner’s default if not set. |
provider | str | None | Provider for the model (e.g., openai, anthropic). Falls back to the runner’s default if not set. |
temperature | float | None | Sampling temperature override. When None, uses the model’s default. |
max_tokens | int | None | Output token limit override. When None, uses the model’s default. |
tools | list[str] | None | Tool IDs this soul can use. Each must also be declared in the workflow’s top-level tools: section. |
required_tool_calls | list[str] | None | LLM-facing tool function names that must be called before the agent completes. |
max_tool_iterations | int | 5 | Maximum number of tool-use iterations per execution. |
avatar_color | str | None | UI color for displaying the soul. Accepts one of six preset tokens: accent, info, success, warning, danger, neutral. |
id and filename stem
Section titled “id and filename stem”The id field is the identity. For external soul files, it must match the filename stem exactly. A file named researcher.yaml must contain id: researcher; a mismatch is rejected during discovery.
Workflow blocks reference the embedded id with soul_ref: researcher.
Validation rules
Section titled “Validation rules”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 fields —
id,kind,name,role, andsystem_promptmust be present. A missing field produces a PydanticValidationError. - 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.
soul_ref resolution
Section titled “soul_ref resolution”When the parser encounters a soul_ref on a block, it resolves the reference through these steps:
-
Discover external souls. The parser scans
custom/souls/for.yamlfiles. Each file is loaded, validated against the Soul schema, checked forid == filename stem, and stored in a dictionary keyed by embedded id. -
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". -
Look up
soul_ref. The block’ssoul_refvalue is looked up in the merged map. If not found, the parser raises aValueErrorlisting available souls.
Tool governance
Section titled “Tool governance”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.
modified_at field
Section titled “modified_at field”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.
What’s next
Section titled “What’s next”- Souls Overview — concepts, architecture, and design rationale
- Inline Souls — defining souls directly inside workflow YAML
- Soul Library — managing souls through the GUI