| name | golem-add-agent-rust |
| description | Adding a new Rust agent to a Golem component. Use when the user asks to create, add, or define a new agent type, implement an agent trait, or add agent methods in a Rust Golem project. |
Adding a New Agent to a Rust Golem Component
Overview
An agent is a durable, stateful unit of computation in Golem. Each agent type is defined as a trait annotated with #[agent_definition] and implemented on a struct annotated with #[agent_implementation].
Steps
- Create the agent module — add a new file
src/<agent_name>.rs
- Define the agent trait — annotate with
#[agent_definition]
- Implement the agent — annotate with
#[agent_implementation]
- Re-export from
lib.rs — add mod <agent_name>; and pub use <agent_name>::*;
- Build — run
golem build to verify
Agent Definition
use golem_rust::{agent_definition, agent_implementation};
#[agent_definition]
pub trait MyAgent {
fn new(name: String) -> Self;
fn get_count(&self) -> u32;
fn increment(&mut self) -> u32;
async fn fetch_data(&self, url: String) -> String;
}
struct MyAgentImpl {
name: String,
count: u32,
}
#[agent_implementation]
impl MyAgent for MyAgentImpl {
fn new(name: String) -> Self {
Self { name, count: 0 }
}
fn get_count(&self) -> u32 {
self.count
}
fn increment(&mut self) -> u32 {
self.count += 1;
self.count
}
async fn fetch_data(&self, url: String) -> String {
todo!()
}
}
Custom Types
All parameter and return types must implement the Schema trait. For custom types, derive it along with IntoValue and FromValueAndType:
use golem_rust::Schema;
use serde::{Serialize, Deserialize};
#[derive(Clone, Schema, Serialize, Deserialize)]
pub struct MyData {
pub field1: String,
pub field2: u32,
}
Returning Failures
Agent methods should distinguish between domain errors (expected failure outcomes) and uncaught errors:
- Uncaught errors (panics, unwrapped
Err, etc.) are not returned to the caller as a failed invocation. Golem treats them as crashes: the invocation is retried according to the agent's retry policy, and if the retries are exhausted the agent itself becomes failed.
- Domain errors that the caller should observe as a normal failure result must be expressed in the method's return type using
Result<T, E>. The error type E must derive Schema like any other custom type.
use golem_rust::{agent_definition, agent_implementation, Schema};
use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, Schema, Serialize, Deserialize)]
pub enum WithdrawError {
InsufficientFunds { available: u64 },
AccountClosed,
}
#[agent_definition]
pub trait Wallet {
fn new(owner: String) -> Self;
fn withdraw(&mut self, amount: u64) -> Result<u64, WithdrawError>;
}
struct WalletImpl {
owner: String,
balance: u64,
closed: bool,
}
#[agent_implementation]
impl Wallet for WalletImpl {
fn new(owner: String) -> Self {
Self { owner, balance: 0, closed: false }
}
fn withdraw(&mut self, amount: u64) -> Result<u64, WithdrawError> {
if .closed {
(WithdrawError::AccountClosed);
}
amount > .balance {
(WithdrawError::InsufficientFunds { available: .balance });
}
.balance -= amount;
(.balance)
}
}
Returning Err(WithdrawError::...) completes the invocation successfully — the caller receives the error as a value. Panicking with unwrap(), expect(), panic!(), or returning from ? on an unrelated error type will instead trigger a retry and eventually fail the whole agent.
Key Constraints
- All agent method parameters are passed by value (no references)
- All custom types need
Schema derive (plus IntoValue and FromValueAndType, which Schema implies)
- Constructor parameters form the agent identity — two agents with the same parameters are the same agent
- Agents are created implicitly on first invocation — no separate creation step
- Invocations are processed sequentially in a single thread — no concurrency within a single agent
- Never use
block_on — all agent methods run in an async context. Use async methods and .await instead of blocking on futures