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§
Sourcefn system_prompt(&self) -> Result<String>
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).
Sourcefn available_tools(&self) -> Result<Vec<ToolSpec>>
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§
Sourcefn provider(&self) -> Option<LlmProvider>
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
}Sourcefn apply_config_overrides(&self, _cfg: &mut AppConfig)
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.
Sourcefn required_completion_tools(&self) -> Option<&Vec<String>>
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.
Sourcefn max_continuations(&self) -> usize
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.
Sourcefn continuation_message(&self) -> Option<&str>
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.
Sourcefn execute_tool(&self, name: &str, _args: Value) -> Result<Value>
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.
Sourcefn 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 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.
Sourcefn tool_concurrency(&self, _name: &str) -> ToolConcurrency
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.
Sourcefn provider_parallel_tool_calls(&self) -> bool
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.
Sourcefn max_concurrent_tool_executions(&self) -> usize
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.
Sourcefn 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<'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:
- Builds initial messages (system + user)
- Streams LLM response with tool calling
- Executes requested tools
- Continues until LLM stops requesting tools
- 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.
Sourcefn 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_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?;Sourcefn 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 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?;Sourcefn initial_messages(&self, user_prompt: &str) -> Result<Vec<ChatMessage>>
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).
Sourcefn 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<'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
Sourcefn 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,
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