# Your first agent

> Run a complete graph without credentials, then add a model call.

Start in the `hello-polaris` project from [Installation](/docs/polaris/getting-started/installation.md). First check the execution path without a model or network request.

## 1. Run one system

Replace `src/main.rs` with this complete program:

```rust
use polaris_ai::graph::{Graph, GraphExecutor};
use polaris_ai::system::{server::Server, system};


#[system]
async fn greet() -> String {
    "Hello from Polaris!".to_owned()
}


#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut server = Server::new();
    server.finish().await?;
    let mut graph = Graph::new();
    graph.add_system(greet);
    assert!(graph.validate().is_ok(), "graph must be valid");
    let mut ctx = server.create_context();
    let result = GraphExecutor::new()
        .execute(&graph, &mut ctx, None, None)
        .await?;
    println!(
        "{}",
        result.output::<String>().expect("greet returns a String")
    );
    Ok(())
}
```

```sh
cargo run
```

After compilation, the program prints:

```text
Hello from Polaris!
```

`#[system]` adapts `greet` into a graph system. `graph.add_system(greet)` adds one step. The executor runs it and returns its typed output. `graph.validate()` checks the graph structure before execution.

`server.finish().await?` completes plugin startup. It is asynchronous: calling `finish()` without awaiting it does not initialise your server.

## 2. Add a model call

Enable the `anthropic` feature as shown in [Installation](/docs/polaris/getting-started/installation.md#model-provider-features). Replace `src/main.rs` with this complete program:

```rust
use polaris_ai::graph::{Graph, GraphExecutor};
use polaris_ai::models::{AnthropicPlugin, ModelRegistry, ModelsPlugin};
use polaris_ai::system::param::Res;
use polaris_ai::system::prelude::SystemError;
use polaris_ai::system::{server::Server, system};


#[system]
async fn ask_llm(registry: Res<ModelRegistry>) -> Result<String, SystemError> {
    let model_id = std::env::var("POLARIS_MODEL")
        .unwrap_or_else(|_| "anthropic/claude-sonnet-5-5".to_owned());
    let llm = registry
        .llm(&model_id)
        .map_err(|err| SystemError::ExecutionError(err.to_string()))?;
    let response = llm
        .builder()
        .system("Answer in one sentence.")
        .user("What is Rust's ownership model?")
        .generate()
        .await
        .map_err(|err| SystemError::ExecutionError(err.to_string()))?;
    Ok(response.text())
}


#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut server = Server::new();
    server
        .add_plugins(ModelsPlugin)
        .add_plugins(AnthropicPlugin::from_env("ANTHROPIC_API_KEY"));
    server.finish().await?;
    let mut graph = Graph::new();
    graph.add_system(ask_llm);
    assert!(graph.validate().is_ok(), "graph must be valid");
    let mut ctx = server.create_context();
    let result = GraphExecutor::new()
        .execute(&graph, &mut ctx, None, None)
        .await?;
    println!(
        "{}",
        result.output::<String>().expect("ask_llm returns a String")
    );
    Ok(())
}
```

Set `ANTHROPIC_API_KEY` in the environment using your normal secret-management method, then run `cargo run` again. This makes a billable model request. The result should be a one-sentence answer; wording will vary.

The default is Claude Sonnet 5.5. Choose another model available to your account without editing the code:

```sh
POLARIS_MODEL=anthropic/claude-opus-5-5 cargo run
```

`anthropic/` selects the registered provider; the rest is the provider’s model ID. See [Anthropic’s current model list](https://platform.claude.com/docs/en/models/overview/). To use a different provider, enable its Cargo feature, register its plugin and configure its credentials too.

Authentication errors indicate a missing or invalid key; a model-access error is a separate issue. The pinned Polaris version may not have pricing entries for newer models, so missing cost estimates do not mean a request is free.

## What to change next

* Add a second system that consumes the previous output with `Out<String>`: [Systems](/docs/polaris/core-concepts/systems.md).
* Run a complete tool-using agent: [ReAct CLI](/docs/polaris/examples/react-cli.md).
* Test your graph without paid model calls: [Testing agents](/docs/polaris/guides/testing-agents.md).

This first example is intentionally a single model invocation. The graph becomes a multi-step agent when you add tool use, branching, memory and a stopping condition.
