Packages
nous
0.12.14
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/agents/basic_agent.ex
defmodule Nous.Agents.BasicAgent do
@moduledoc """
Default agent implementation with standard tool-calling behavior.
BasicAgent implements the `Nous.Agent.Behaviour` and provides:
- Standard message building with system prompts
- Simple tool call detection and execution
- Text extraction from assistant responses
This is the default behaviour used when no `behaviour_module` is specified.
## Example
agent = Agent.new("openai:gpt-4",
instructions: "Be helpful",
tools: [&search/2]
)
# Uses BasicAgent by default
{:ok, result} = Agent.run(agent, "Search for Elixir tutorials")
"""
@behaviour Nous.Agent.Behaviour
alias Nous.{Message, Messages, OutputSchema}
alias Nous.Agent.Context
@doc """
Build messages to send to the LLM.
Combines system prompt (if any) with conversation messages.
"""
@impl true
def build_messages(_agent, ctx) do
# Start with messages from context
messages = ctx.messages
# If system prompt not already in messages, add it
has_system = Enum.any?(messages, &Message.is_system?/1)
if not has_system and ctx.system_prompt do
[Message.system(ctx.system_prompt) | messages]
else
messages
end
end
@doc """
Process a response from the LLM.
Adds the response to context and updates `needs_response` based on
whether there are tool calls to process.
"""
@impl true
def process_response(_agent, response, ctx) do
# Add message to context - this also updates needs_response
Context.add_message(ctx, response)
end
@doc """
Extract the final output from the context.
Returns the text content of the last assistant message.
When `output_type` is set, parses and validates the response.
"""
@impl true
def extract_output(agent, ctx) do
case find_output_message(ctx) do
nil ->
{:error, :no_output}
msg ->
extract_with_output_type(agent, msg)
end
end
defp find_output_message(ctx) do
case Context.last_message(ctx) do
%Message{role: :assistant} = msg -> msg
%Message{role: :tool} -> find_last_assistant_message(ctx.messages)
_ -> nil
end
end
defp extract_with_output_type(%{output_type: :string}, msg) do
{:ok, Messages.extract_text(msg)}
end
defp extract_with_output_type(%{output_type: {:one_of, schemas}}, msg) do
case OutputSchema.extract_response_for_one_of(msg, schemas) do
{text, schema} when not is_nil(schema) ->
OutputSchema.parse_and_validate(text, schema)
{text, nil} ->
OutputSchema.parse_and_validate(text, {:one_of, schemas})
end
end
defp extract_with_output_type(%{output_type: output_type, model: model}, msg) do
text = OutputSchema.extract_response_text(msg, model.provider)
OutputSchema.parse_and_validate(text, output_type)
end
@doc """
Get tools available for this agent.
Returns the tools configured on the agent.
"""
@impl true
def get_tools(agent) do
agent.tools
end
# Private helpers
defp find_last_assistant_message(messages) do
messages
|> Enum.reverse()
|> Enum.find(&(&1.role == :assistant))
end
end