# `Nous.Agent.Behaviour`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/agent/behaviour.ex#L1)

Behaviour for agent implementations.

Defines the interface that different agent types must implement,
allowing for customized execution logic while sharing common infrastructure.

## Implementing an Agent

To create a custom agent type, implement this behaviour:

    defmodule MyApp.CustomAgent do
      @behaviour Nous.Agent.Behaviour

      @impl true
      def init_context(agent, ctx) do
        # Add custom initialization
        ctx
      end

      @impl true
      def build_messages(agent, ctx) do
        # Build messages for the LLM
        ctx.messages
      end

      @impl true
      def process_response(agent, message, ctx) do
        # Process LLM response, handle tool calls
        Context.add_message(ctx, message)
      end

      @impl true
      def extract_output(agent, ctx) do
        # Extract final output from context
        {:ok, Context.last_message(ctx).content}
      end

      @impl true
      def get_tools(agent) do
        agent.tools
      end
    end

## Built-in Implementations

- `Nous.Agents.BasicAgent` - Standard tool-calling agent
- `Nous.Agents.ReActAgent` - Structured reasoning with plan/note/final_answer

## Using with Agent.new

    agent = Agent.new("openai:gpt-4",
      behaviour_module: MyApp.CustomAgent,
      tools: [&search/2]
    )

# `after_tool`
*optional* 

```elixir
@callback after_tool(
  agent :: Nous.Agent.t(),
  tool_call :: map(),
  result :: any(),
  ctx :: Nous.Agent.Context.t()
) :: Nous.Agent.Context.t()
```

Called after each tool execution.

Optional callback for tool result processing.

## Parameters

  * `agent` - The agent configuration
  * `tool_call` - The tool call that was executed
  * `result` - The tool result
  * `ctx` - Current context

## Returns

Modified context.

# `before_request`
*optional* 

```elixir
@callback before_request(
  agent :: Nous.Agent.t(),
  ctx :: Nous.Agent.Context.t(),
  request_opts :: keyword()
) :: keyword()
```

Called before each LLM request.

Optional callback for request-level customization.

## Parameters

  * `agent` - The agent configuration
  * `ctx` - Current context
  * `request_opts` - Options for the LLM request

## Returns

Modified request options.

# `build_messages`

```elixir
@callback build_messages(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) :: [
  Nous.Message.t()
]
```

Build messages to send to the LLM.

Called at the start of each iteration. Use this to:
- Add system prompts
- Format messages for the provider
- Include tool schemas

## Parameters

  * `agent` - The agent configuration
  * `ctx` - Current context with messages

## Returns

List of messages to send to the model.

## Example

    @impl true
    def build_messages(agent, ctx) do
      system_msg = Message.system(agent.instructions)
      [system_msg | ctx.messages]
    end

# `extract_output`

```elixir
@callback extract_output(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) ::
  {:ok, any()} | {:error, term()}
```

Extract the final output from the context.

Called after the execution loop completes. Use this to:
- Extract the final answer
- Format structured output
- Validate the result

## Parameters

  * `agent` - The agent configuration
  * `ctx` - Final context after execution

## Returns

  * `{:ok, output}` - Successfully extracted output
  * `{:error, reason}` - Failed to extract valid output

## Example

    @impl true
    def extract_output(_agent, ctx) do
      case Context.last_message(ctx) do
        %Message{role: :assistant, content: content} ->
          {:ok, content}
        _ ->
          {:error, :no_output}
      end
    end

# `get_tools`

```elixir
@callback get_tools(agent :: Nous.Agent.t()) :: [Nous.Tool.t()]
```

Get tools available for this agent run.

Called to determine which tools to advertise to the model.
Implementations may add agent-specific tools (e.g., ReAct tools).

## Parameters

  * `agent` - The agent configuration

## Returns

List of Tool structs.

## Example

    @impl true
    def get_tools(agent) do
      react_tools = [plan_tool(), note_tool(), final_answer_tool()]
      react_tools ++ agent.tools
    end

# `handle_error`
*optional* 

```elixir
@callback handle_error(
  agent :: Nous.Agent.t(),
  error :: term(),
  ctx :: Nous.Agent.Context.t()
) ::
  {:retry, Nous.Agent.Context.t()}
  | {:continue, Nous.Agent.Context.t()}
  | {:error, term()}
```

Called when the agent encounters an error.

Optional callback for custom error handling. Default behavior
is to return the error.

## Parameters

  * `agent` - The agent configuration
  * `error` - The error that occurred
  * `ctx` - Current context when error occurred

## Returns

  * `{:retry, ctx}` - Retry the current iteration
  * `{:continue, ctx}` - Continue with modified context
  * `{:error, reason}` - Propagate error

# `init_context`
*optional* 

```elixir
@callback init_context(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) ::
  Nous.Agent.Context.t()
```

Initialize context before execution begins.

Called once at the start of `Nous.run/3`. Use this to:
- Add agent-specific tools (e.g., ReAct tools)
- Set up initial state in deps
- Configure callbacks

## Parameters

  * `agent` - The agent configuration
  * `ctx` - Initial context from run options

## Returns

Modified context for execution.

## Example

    @impl true
    def init_context(agent, ctx) do
      # Add ReAct-specific tools
      ctx
      |> Context.merge_deps(%{tool_history: []})
    end

# `process_response`

```elixir
@callback process_response(
  agent :: Nous.Agent.t(),
  message :: Nous.Message.t(),
  ctx :: Nous.Agent.Context.t()
) :: Nous.Agent.Context.t()
```

Process a response from the LLM.

Called after each model response. Use this to:
- Handle tool calls
- Update context state
- Set `needs_response` based on whether more work is needed

## Parameters

  * `agent` - The agent configuration
  * `message` - The LLM's response message
  * `ctx` - Current context

## Returns

Updated context. The runner will check `ctx.needs_response` to decide
whether to continue the loop.

## Example

    @impl true
    def process_response(agent, message, ctx) do
      ctx = Context.add_message(ctx, message)

      if Message.has_tool_calls?(message) do
        # Execute tools and add results
        execute_tools_and_continue(ctx, message.tool_calls)
      else
        # No tool calls - we're done
        Context.set_needs_response(ctx, false)
      end
    end

# `call`

```elixir
@spec call(module(), atom(), list(), any()) :: any()
```

Call a behaviour callback, using default if not implemented.

## Examples

When the module exports the callback, it is invoked with `args`:

    iex> agent = Agent.new("openai:gpt-4", tools: [%Nous.Tool{name: "search", function: &Function.identity/1}])
    iex> [tool] = Behaviour.call(Nous.Agents.BasicAgent, :get_tools, [agent], [])
    iex> tool.name
    "search"

When it does not, `default` is returned untouched — this is how the
optional callbacks (`c:init_context/2`, `c:handle_error/3`,
`c:before_request/3`, `c:after_tool/4`) degrade to no-ops:

    iex> ctx = Nous.Agent.Context.new()
    iex> agent = Agent.new("openai:gpt-4")
    iex> Behaviour.call(Nous.Agents.BasicAgent, :init_context, [agent, ctx], ctx) === ctx
    true

# `default_module`

```elixir
@spec default_module() :: module()
```

Get the default behaviour module.

## Examples

    iex> Behaviour.default_module()
    Nous.Agents.BasicAgent

# `get_module`

```elixir
@spec get_module(Nous.Agent.t()) :: module()
```

Get the behaviour module for an agent.

Returns the configured behaviour module, or the default BasicAgent.

## Examples

    iex> agent = Agent.new("openai:gpt-4")
    iex> Behaviour.get_module(agent)
    Nous.Agents.BasicAgent

    iex> agent = Agent.new("openai:gpt-4", behaviour_module: MyAgent)
    iex> Behaviour.get_module(agent)
    MyAgent

---

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