# Anonymous Workflows
Anonymous workflows are one-call agent runs. They are useful for scripts,
notebooks, jobs, CI tasks, and callbacks where defining a named agent module
would add ceremony without adding useful state.
`Condukt.run/2` accepts a prompt as the first argument:
```elixir
{:ok, text} =
Condukt.run("Summarize README.md in three bullets.",
model: "anthropic:claude-sonnet-4-20250514",
tools: [Condukt.Tools.Read]
)
```
Condukt starts a transient `Condukt.Session`, runs the prompt, returns the
final text, and stops the session. No conversation history is kept across
calls.
If you already have an agent module and want its callbacks without managing a
long-lived process, pass the module as the first argument instead:
```elixir
{:ok, text} =
Condukt.run(MyApp.ReviewAgent, "Review the current branch.",
timeout: 120_000
)
```
This module-defined one-shot form uses the agent's `system_prompt/0`,
`tools/0`, `model/0`, sandbox, secrets, and sub-agent defaults. It also
supports typed input and structured output through the same options described
below.
## Runtime options
Anonymous workflows accept the same run options as agent runs:
* `:timeout` caps the synchronous call timeout in milliseconds
* `:max_turns` caps tool-use loops
* `:images` attaches images to the user message
They also accept the same session options you would pass to an agent's
`start_link/1`, including `:model`, `:api_key`, `:base_url`,
`:system_prompt`, `:thinking_level`, `:tools`, `:sandbox`, `:cwd`,
`:subagents`, `:session_store`, `:compactor`, `:redactor`, and
`:load_project_instructions`.
Anonymous workflows default `:load_project_instructions` to `false`. Pass
`load_project_instructions: true` when you want `AGENTS.md`, `CLAUDE.md`, and
local skills to shape the transient run.
## Typed input
Use `:input` when you want the prompt to be instructions and the arguments to
be a separate JSON payload:
```elixir
{:ok, text} =
Condukt.run("Review the supplied pull request metadata.",
input: %{repo: "tuist/condukt", pr_number: 42},
input_schema: %{
type: "object",
properties: %{
repo: %{type: "string"},
pr_number: %{type: "integer"}
},
required: ["repo", "pr_number"]
}
)
```
When `:input_schema` is present, Condukt validates the input with JSV before
making an LLM request. Input must be a map.
## Structured output
Use `:output` to require a JSON Schema-shaped result:
```elixir
{:ok, %{verdict: "approve", summary: summary}} =
Condukt.run("Decide a review verdict.",
input: %{repo: "tuist/condukt", pr_number: 42},
output: %{
type: "object",
properties: %{
verdict: %{type: "string", enum: ["approve", "request_changes", "comment"]},
summary: %{type: "string"}
},
required: ["verdict", "summary"]
}
)
```
Structured mode appends a synthetic `submit_result` tool. The model calls that
tool with the final result, Condukt validates the submitted map with JSV, and
the validated value is returned.
If the schema's top-level `properties` keys are atoms, Condukt atomizes the
matching top-level result keys after validation.
## Inline tools
For small workflow-specific tools, use `Condukt.tool/1`:
```elixir
ls =
Condukt.tool(
name: "ls",
description: "Lists files under a glob.",
parameters: %{
type: "object",
properties: %{pattern: %{type: "string"}},
required: ["pattern"]
},
call: fn %{"pattern" => pattern}, context ->
Condukt.Sandbox.glob(context.sandbox, pattern)
end
)
{:ok, text} =
Condukt.run("List Elixir files under lib/.",
tools: [ls]
)
```
Inline tool callbacks receive the same context map as module tools:
`:agent`, `:sandbox`, `:cwd`, and `:opts`. `:opts` is always `[]` for inline
tools.
## Anonymous sub-agents
Anonymous workflows can register sub-agents inline with `:subagents`. Use
`role: [opts]` when the child does not need a named module:
```elixir
{:ok, text} =
Condukt.run("Plan the release notes.",
subagents: [
researcher: [
model: "anthropic:claude-haiku-4-5",
system_prompt: "Find facts and return concise notes.",
tools: [Condukt.Tools.Read]
]
]
)
```
Inline sub-agent opts are normal session opts plus the optional `:input` and
`:output` schemas documented in the sub-agents guide. Anonymous sub-agents
default `:load_project_instructions` to `false`; set it to `true` in the role
opts when the child should load project instructions.
## Errors
Anonymous workflows return `{:error, reason}` for validation failures, LLM
errors, and session startup failures.
Common structured workflow reasons include:
* `{:invalid_input, %JSV.ValidationError{}}`
* `{:invalid_output, %JSV.ValidationError{}}`
* `{:invalid_input, :input_must_be_a_map}`
* `:no_result_submitted`
## Choosing an API
Use anonymous workflows when the task fits in one call and no module-level
agent identity is useful.
Use `Condukt.run(MyApp.Agent, prompt, opts)` when the task fits in one call
but the agent module's callbacks are useful.
Use `operation/2` when you want a named compile-time entrypoint on an agent
module.
Use a supervised agent process when you need long-lived state, conversation
history, streaming interaction, or OTP supervision.