Skip to main content

Agent

Trait Agent 

Source
pub trait Agent: Send + Sync {
Show 19 methods // Required methods fn name(&self) -> &str; fn system_prompt(&self) -> Result<String>; fn available_tools(&self) -> Result<Vec<ToolSpec>>; // Provided methods fn provider(&self) -> Option<LlmProvider> { ... } fn apply_config_overrides(&self, _cfg: &mut AppConfig) { ... } fn required_completion_tools(&self) -> Option<&Vec<String>> { ... } fn max_continuations(&self) -> usize { ... } fn continuation_message(&self) -> Option<&str> { ... } fn execute_tool(&self, name: &str, _args: Value) -> Result<Value> { ... } fn execute_tool_with_context<'life0, 'life1, 'async_trait>( &'life0 self, name: &'life1 str, _ctx: ToolContext, args: Value, ) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait { ... } fn tool_concurrency(&self, _name: &str) -> ToolConcurrency { ... } fn provider_parallel_tool_calls(&self) -> bool { ... } fn max_concurrent_tool_executions(&self) -> usize { ... } fn run<'life0, 'life1, 'async_trait>( &'life0 self, user_prompt: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait { ... } fn run_streaming<'life0, 'life1, 'async_trait>( &'life0 self, user_prompt: &'life1 str, consumer: Box<dyn StreamConsumer>, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait { ... } fn run_with_consumers<'life0, 'life1, 'async_trait>( &'life0 self, user_prompt: &'life1 str, consumers: Vec<Box<dyn StreamConsumer>>, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait { ... } fn initial_messages(&self, user_prompt: &str) -> Result<Vec<ChatMessage>> { ... } fn continue_session<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, session_id: &'life1 str, user_prompt: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait { ... } fn continue_session_streaming<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, session_id: &'life1 str, user_prompt: &'life2 str, consumer: Box<dyn StreamConsumer>, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait { ... }
}
Expand description

Common interface implemented by all Appam agent types.

An Agent supplies the pieces the runtime cannot infer on its own:

  • a stable name for logging, history, and trace output
  • a system prompt or prompt-loading strategy
  • a set of tool schemas exposed to the model
  • tool execution for provider-emitted tool calls
  • optional continuation policy when a session ends prematurely

The runtime intentionally assumes tool arguments are untrusted model output. Implementations should therefore validate inputs, fail closed on missing state, and avoid side effects that depend on undocumented provider behavior.

§Examples

use appam::agent::{Agent, toml_agent::TomlAgent};
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let agent = TomlAgent::from_file("agents/assistant.toml")?;
    agent.run("Hello, how can you help me?").await?;
    Ok(())
}

Required Methods§

Source

fn name(&self) -> &str

Return the agent’s unique name.

Source

fn system_prompt(&self) -> Result<String>

Return the full system prompt for this agent.

The system prompt defines the agent’s personality, capabilities, instructions, and constraints. It is sent as the first message in every conversation.

§Errors

Returns an error if the prompt cannot be loaded (e.g., file not found).

Source

fn available_tools(&self) -> Result<Vec<ToolSpec>>

Return the set of tool specifications available to this agent.

Tools are exposed to the LLM via their JSON schemas. The LLM can decide to invoke tools based on user queries and the system prompt.

§Errors

Returns an error if tool specifications cannot be loaded.

Provided Methods§

Source

fn provider(&self) -> Option<LlmProvider>

Return the provider override for this agent.

If Some(provider), this agent will use the specified provider regardless of the global configuration. If None, the global provider config is used.

§Examples
    fn provider(&self) -> Option<LlmProvider> {
        Some(LlmProvider::Anthropic)  // Force Anthropic for this agent
    }
Source

fn apply_config_overrides(&self, _cfg: &mut AppConfig)

Apply agent-specific configuration overrides to global config.

This hook exists so agent implementations can inject configuration that was determined at construction time, such as provider selection, model overrides, retry settings, or history/tracing preferences.

The runtime applies this after loading global configuration and before constructing the provider client, so implementations should treat it as the last agent-controlled layer in the configuration precedence chain.

Source

fn required_completion_tools(&self) -> Option<&Vec<String>>

Return the list of tools required for session completion.

If Some, the runtime will automatically inject a continuation message when the session ends without calling any of these tools. This is useful for agents whose contract requires a concrete side effect before the run may be considered complete.

Default implementation returns None, meaning the runtime accepts the model’s first completed answer without any additional tool requirements.

Source

fn max_continuations(&self) -> usize

Return the maximum number of continuation attempts.

This bounds the runtime’s recovery behavior when the model stops before calling required completion tools. A low number avoids infinite loops while still giving the model a chance to recover from an early stop.

Source

fn continuation_message(&self) -> Option<&str>

Return the custom continuation message, if any.

If Some, this message will be injected when the session ends without calling required tools. Use this to explain the missing side effect in domain terms rather than relying on the runtime’s generic fallback.

Source

fn execute_tool(&self, name: &str, _args: Value) -> Result<Value>

Resolve a tool by name and execute it.

Default implementation returns an error. Agents should override this to provide tool resolution logic. New integrations should prefer Agent::execute_tool_with_context so tools receive runtime metadata and fail-closed access to managed state.

Source

fn execute_tool_with_context<'life0, 'life1, 'async_trait>( &'life0 self, name: &'life1 str, _ctx: ToolContext, args: Value, ) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Resolve a tool by name and execute it with runtime metadata.

The default implementation preserves backward compatibility by delegating to the legacy synchronous execute_tool(...) path and ignoring the provided context. Agents backed by a ToolRegistry should override this to call the registry’s async/context-aware execution entrypoint.

Source

fn tool_concurrency(&self, _name: &str) -> ToolConcurrency

Return the concurrency policy for a specific tool.

Legacy agents default every tool to serial execution. Registry-backed agents should override this to surface per-tool policies from the underlying tool registry. Declaring a tool parallel-safe only affects runtime scheduling; tool implementations remain responsible for their own synchronization and external side-effect safety.

Source

fn provider_parallel_tool_calls(&self) -> bool

Whether Appam should request provider-side parallel tool batching.

This only affects provider request wiring. Runtime execution still additionally requires max_concurrent_tool_executions() > 1 and that every returned tool in the batch be marked ParallelSafe. The default stays false because some providers emit subtly different tool-call semantics when batching is enabled.

Source

fn max_concurrent_tool_executions(&self) -> usize

Maximum number of concurrent tool executions allowed for one batch.

The runtime clamps execution to this limit after the provider has already decided which tool calls belong in a batch.

Source

fn run<'life0, 'life1, 'async_trait>( &'life0 self, user_prompt: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Run the agent with a user prompt.

Orchestrates the full conversation loop:

  1. Builds initial messages (system + user)
  2. Streams LLM response with tool calling
  3. Executes requested tools
  4. Continues until LLM stops requesting tools
  5. Returns session metadata

Output is streamed to console with default formatting.

The default implementation is provided by runtime::default_run. Agents can override this for custom orchestration.

§Errors

Returns an error if the LLM request fails, tool execution fails, or session logging fails.

Source

fn run_streaming<'life0, 'life1, 'async_trait>( &'life0 self, user_prompt: &'life1 str, consumer: Box<dyn StreamConsumer>, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Run the agent with a custom stream consumer.

Like run(), but streams events to the provided consumer instead of the console. Use this for web streaming, logging, metrics, or custom output handling.

§Examples
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let consumer = ChannelConsumer::new(tx);

agent.run_streaming("Hello!", Box::new(consumer)).await?;
Source

fn run_with_consumers<'life0, 'life1, 'async_trait>( &'life0 self, user_prompt: &'life1 str, consumers: Vec<Box<dyn StreamConsumer>>, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Run the agent with multiple stream consumers.

Events are broadcast to all consumers. If any consumer returns an error, execution stops and the error is returned.

§Examples
agent.run_with_consumers("Hello!", vec![
    Box::new(ConsoleConsumer::new()),
    Box::new(ChannelConsumer::new(tx)),
]).await?;
Source

fn initial_messages(&self, user_prompt: &str) -> Result<Vec<ChatMessage>>

Build the initial message list for a conversation.

By default, creates a system message and a user message. Override for custom message initialization (e.g., few-shot examples, context).

Source

fn continue_session<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, session_id: &'life1 str, user_prompt: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Continue an existing session with a new user prompt.

Loads the session from history and continues the conversation, preserving all previous messages and context. Streams output to console with default formatting.

§Requirements
  • Session history must be enabled in configuration
  • The session ID must exist in the database
§Examples
// First conversation
let session = agent.run("Hello!").await?;

// Continue later
agent.continue_session(&session.session_id, "How are you?").await?;
§Errors

Returns an error if:

  • Session history is not enabled
  • Session ID does not exist
  • LLM request fails
  • Tool execution fails
Source

fn continue_session_streaming<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, session_id: &'life1 str, user_prompt: &'life2 str, consumer: Box<dyn StreamConsumer>, ) -> Pin<Box<dyn Future<Output = Result<Session>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Continue an existing session with custom streaming.

Like continue_session(), but streams events to the provided consumer instead of the console.

§Examples
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let consumer = ChannelConsumer::new(tx);

agent.continue_session_streaming("session-123", "Continue...", Box::new(consumer)).await?;
§Errors

Returns an error if:

  • Session history is not enabled
  • Session ID does not exist
  • LLM request fails
  • Tool execution fails
  • Consumer returns an error

Implementors§