# `PhoenixAI.Team`
[🔗](https://github.com/franciscpd/phoenix-ai/blob/main/lib/phoenix_ai/team.ex#L1)

Parallel fan-out/fan-in execution.

Agent specs run concurrently via `Task.async_stream`. Results are collected
and passed to a merge function. Crashed specs produce `{:error, {:task_failed, reason}}`
instead of crashing the caller.

## Ad-hoc usage

    Team.run([
      fn -> AI.chat([msg("Search X")], provider: :openai) end,
      fn -> AI.chat([msg("Search Y")], provider: :anthropic) end
    ], fn results -> merge(results) end, max_concurrency: 5)

## DSL usage

    defmodule MyTeam do
      use PhoenixAI.Team

      agent :researcher do
        fn -> AI.chat([msg("Search")], provider: :openai) end
      end

      merge do
        fn results -> Enum.map(results, fn {:ok, r} -> r.content end) end
      end
    end

    MyTeam.run()

# `agent_spec`

```elixir
@type agent_spec() :: (-&gt; {:ok, term()} | {:error, term()} | term())
```

# `merge_fn`

```elixir
@type merge_fn() :: ([ok: term(), error: term()] -&gt; term())
```

# `agent`
*macro* 

Defines a named agent spec in a team module.

The block must return a zero-arity function `fn -> {:ok, result} | {:error, reason} end`.

# `merge`
*macro* 

Defines the merge function for a team module.

The block must return a function that accepts a list of result tuples.

# `run`

```elixir
@spec run([agent_spec()], merge_fn(), keyword()) :: {:ok, term()} | {:error, term()}
```

Executes agent specs in parallel and merges results.

Each spec is a zero-arity function. Results are collected in input order
and passed to `merge_fn`. Crashed specs produce `{:error, {:task_failed, reason}}`.

## Options

- `:max_concurrency` — maximum parallel tasks (default: 5)
- `:timeout` — per-task timeout in ms (default: `:infinity`)
- `:ordered` — preserve input order in results (default: `true`)

---

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