### Basic RAG Setup with Rig Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rag.md Sets up a RAG system by creating embeddings for documents and storing them in an in-memory vector store. It then performs a semantic search to retrieve relevant documents. ```rust use rig:: client::{EmbeddingsClient, ProviderClient}, embeddings::EmbeddingsBuilder, providers::openai::{Client, TEXT_EMBEDDING_ADA_002}, vector_store::{VectorSearchRequest, VectorStoreIndex, in_memory_store::InMemoryVectorStore}, }; #[tokio::main] async fn main() -> Result<(), Box> { let openai_client = Client::from_env(); let mut vector_store = InMemoryVectorStore::default(); // Define documents to index let documents = vec![ "Rig is a Rust library for building LLM-powered applications.", "RAG combines retrieval and generation for better accuracy.", "Vector stores enable semantic search over documents.", ]; let model = openai_client.embedding_model(TEXT_EMBEDDING_ADA_002); // Create embeddings and add to vector store let embeddings = EmbeddingsBuilder::new(model.clone()) .documents(documents)? .build() .await?; vector_store.add_documents(embeddings); // Create a vector index from the in-memory vector store let vector_idx = vector_store.index(model); let query = VectorSearchRequest::builder() .query("What is Rig?") .samples(2) .build()?; // Query the vector store let results = vector_idx.top_n::(query).await?; for (score, doc_id, doc) in results { println!("Score: {}, ID: {}, Content: {}", score, doc_id, doc); } Ok(()) } ``` -------------------------------- ### Example Usage of Provider Registry Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/dynamic-model-creation.md Demonstrates how to use the `ProviderRegistry` to dynamically create and interact with different AI agents (OpenAI and Anthropic) based on string identifiers and configurations. ```rust #[tokio::main] async fn main() { let registry = ProviderRegistry::new(); let prompt = "How much does 4oz of parmesan cheese weigh?"; println!("Prompt: {prompt}"); let helpful_cfg = AgentConfig { name: "Assistant", preamble: "You are a helpful assistant", }; let openai_agent = registry.agent("openai", &helpful_cfg).unwrap(); let oai_response = openai_agent.prompt(prompt).await.unwrap(); println!("Helpful response (OpenAI): {oai_response}"); let unhelpful_cfg = AgentConfig { name: "Assistant", preamble: "You are an unhelpful assistant", }; let anthropic_agent = registry.agent("anthropic", &unhelpful_cfg).unwrap(); let anthropic_response = anthropic_agent.prompt(prompt).await.unwrap(); println!("Unhelpful response (Anthropic): {anthropic_response}"); } ``` -------------------------------- ### Query JSON Logs with jq Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/observability.md Example command to tail a log file and parse its JSON output using `jq` for easier querying. ```bash tail -f | jq . ``` -------------------------------- ### Complete Multi-Provider Routing Example Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/model-routing.md Demonstrates a complete application flow using both a typed router for specific agents and a semantic router for dynamic query routing, integrating multiple OpenAI agents. ```rust #[tokio::main] async fn main() -> Result<(), Box> { let openai_client = openai::Client::from_env(); let coding_agent = openai_client .agent("gpt-5") .preamble("You are an expert coding assistant specializing in Rust programming.") .build(); let math_agent = openai_client .agent("gpt-5") .preamble("You are a mathematics expert who excels at solving complex problems.") .build(); let rtr = TypedRouter::new() .add_route("rust", coding_agent) .add_route("maths", math_agent); let semantic_router = create_semantic_router(&openai_client).await?; let prompt = "How do I use async with Rust?"; println!("Prompt: {prompt}"); let route_name = semantic_route_query(prompt, &semantic_router, &openai_client).await?; println!("Route name selected: {route_name}"); let response = rtr .fetch_agent(&route_name) .unwrap() .prompt(prompt) .await .unwrap(); println!("Response: {response}"); Ok(()) } ``` -------------------------------- ### Example Usage of ConversationMemory Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/memory.md Demonstrates the practical usage of `ConversationMemory`, including initialization, adding messages, manual compaction, and building the next request with a summary. ```rust let mut memory = ConversationMemory::new(10); // Add messages as the conversation progresses memory.add_message("user", "Tell me about Rust"); memory.add_message("assistant", "Rust is a systems programming language..."); // When the conversation grows too long, compact it if memory.get_messages().len() > memory.max_messages { memory.compact(&client).await?; } // Build your next request with the summary included let messages = memory.get_messages_with_summary(); ``` -------------------------------- ### Serve Rig Playbook with Live Reload Source: https://github.com/0xplaygrounds/rig-book/blob/main/README.md Run this command to generate and hot-reload the HTML for the Rig playbook during development. Requires mdbook and mdbook-mermaid to be installed. ```bash mdbook serve ``` -------------------------------- ### Export Traces to OpenTelemetry Collector Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/observability.md Configure Rig to export traces to an OpenTelemetry collector using `opentelemetry-otlp`. This example sets up an HTTP/Binary exporter and a multi-layer subscriber for filtering, pretty-printing, and exporting traces. ```rust use opentelemetry::trace::TracerProvider; use opentelemetry_sdk::{runtime, trace as sdktrace, Resource}; use opentelemetry_otlp::WithExportConfig; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() -> Result<(), Box> { let exporter = opentelemetry_otlp::SpanExporter::builder() .with_http() .with_protocol(opentelemetry_otlp::Protocol::HttpBinary) .build()?; // Create a new OpenTelemetry trace pipeline that prints to stdout let provider = SdkTracerProvider::builder() .with_batch_exporter(exporter) .with_resource(Resource::builder().with_service_name("rig-service").build()) .build(); let tracer = provider.tracer("example"); // Create a tracing layer with the configured tracer let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer); let filter_layer = tracing_subscriber::filter::EnvFilter::builder() .with_default_directive(Level::INFO.into()) .from_env_lossy(); // add a `fmt` layer that prettifies the logs/spans that get outputted to `stdout` let fmt_layer = tracing_subscriber::fmt::layer().pretty(); // Create a multi-layer subscriber that filters for given traces, // prettifies the logs/spans and then sends them to the OTel collector. tracing_subscriber::registry() .with(filter_layer) .with(fmt_layer) .with(otel_layer) .init(); let response = process_query("Hello world!").await.unwrap(); println!("Response: {response}"); // Shutdown tracer provider on exit opentelemetry::global::shutdown_tracer_provider(); Ok(()) } ``` -------------------------------- ### Embed and Index Documents for RAG Source: https://context7.com/0xplaygrounds/rig-book/llms.txt This example demonstrates embedding a list of documents using `EmbeddingsBuilder` and storing them in an `InMemoryVectorStore`. It then sets up a search index and performs a semantic search. ```rust use rig::{ client::{EmbeddingsClient, ProviderClient}, embeddings::EmbeddingsBuilder, providers::openai::{Client, TEXT_EMBEDDING_ADA_002}, vector_store::{VectorSearchRequest, VectorStoreIndex, in_memory_store::InMemoryVectorStore}, }; #[tokio::main] async fn main() -> Result<(), Box> { let openai_client = Client::from_env(); let mut vector_store = InMemoryVectorStore::default(); let documents = vec![ "Rig is a Rust library for building LLM-powered applications.", "RAG combines retrieval and generation for better accuracy.", "Vector stores enable semantic search over documents.", ]; let model = openai_client.embedding_model(TEXT_EMBEDDING_ADA_002); let embeddings = EmbeddingsBuilder::new(model.clone()) .documents(documents)? .build() .await?; vector_store.add_documents(embeddings); let index = vector_store.index(model); let query = VectorSearchRequest::builder() .query("What is Rig?") .samples(2) .build()?; let results = index.top_n::(query).await?; for (score, doc_id, doc) in results { println!("Score: {score:.4} ID: {doc_id} Content: {doc}"); } Ok(()) } ``` -------------------------------- ### Dockerfile for OTEL Collector Image Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/observability.md This Dockerfile sets up an OpenTelemetry Collector environment by starting from an official contrib image and copying a local configuration file into the container. Ensure the config filename matches. ```bash # Start from the official OpenTelemetry Collector Contrib image FROM otel/opentelemetry-collector-contrib:0.135.0 # Copy your local config into the container # Replace `config.yaml` with your actual filename if different COPY ./config.yaml /etc/otelcol-contrib/config.yaml ``` -------------------------------- ### Configure OpenTelemetry Tracing with Rust Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Sets up OpenTelemetry tracing with HTTP/Binary protocol export and integrates with tracing-subscriber for logging and OpenTelemetry span emission. Ensure your application code is placed after the tracing setup and before the tracer provider shutdown. ```rust use opentelemetry::trace::TracerProvider; use opentelemetry_sdk::{trace::SdkTracerProvider, Resource}; use opentelemetry_otlp::WithExportConfig; use tracing::Level; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() -> Result<(), Box> { let exporter = opentelemetry_otlp::SpanExporter::builder() .with_http() .with_protocol(opentelemetry_otlp::Protocol::HttpBinary) .build()?; let provider = SdkTracerProvider::builder() .with_batch_exporter(exporter) .with_resource(Resource::builder().with_service_name("rig-service").build()) .build(); let tracer = provider.tracer("example"); tracing_subscriber::registry() .with(tracing_subscriber::filter::EnvFilter::builder() .with_default_directive(Level::INFO.into()) .from_env_lossy()) .with(tracing_subscriber::fmt::layer().pretty()) .with(tracing_opentelemetry::layer().with_tracer(tracer)) .init(); // … your application code … opentelemetry::global::shutdown_tracer_provider(); Ok(()) } ``` -------------------------------- ### Get Summary as User Message Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/memory.md Generates a `Vec` containing the conversation summary as a user message, if a summary is available. ```rust use rig::completion::Message; use rig::content::{OneOrMany, UserContent}; impl ConversationMemory { pub fn get_message_summary(&self) -> Vec { let mut messages = Vec::new(); if let Some(summary) = &self.summary { messages.push( Message::User { content: OneOrMany::one( UserContent::text(format!("Context from previous conversation:\n{}", summary).as_ref())) } ); } messages } } ``` -------------------------------- ### Initialize Rig Project Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Sets up a new Rust project and adds necessary Rig and Tokio dependencies. ```bash cargo init my-first-project cd my-first-project cargo add rig-core@0.25.0 tokio -F tokio/macros,rt-multi-thread ``` -------------------------------- ### Build System Prompt with Summary Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/memory.md Constructs a system prompt by prepending a previous conversation summary to a base prompt, if a summary exists. ```rust impl ConversationMemory { pub fn build_system_prompt(&self, base_prompt: &str) -> String { match &self.summary { Some(summary) => { format!( "{}\n\nPrevious conversation summary:\n{}", base_prompt, summary ) } None => base_prompt.to_string(), } } } ``` -------------------------------- ### Import and Initialize CompletionsClient Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Import the `CompletionsClient` trait and initialize a client from environment variables to interact with completion models. ```rust /// the completion model trait is provided through the CompletionsClient trait! use rig::client::CompletionsClient; let openai_client = Client::from_env(); let openai_completions_model = openai_client.completion_model("gpt-5"); ``` -------------------------------- ### Initialize CompletionsClient Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Import the CompletionsClient trait and initialize a client to interact with completion models. ```APIDOC ## Initialize CompletionsClient Import the `CompletionsClient` trait from the `rig::client` module and initialize a client instance. ```rust use rig::client::CompletionsClient; let openai_client = Client::from_env(); let openai_completions_model = openai_client.completion_model("gpt-5"); ``` ``` -------------------------------- ### Create OpenAI Provider Client Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Instantiates an OpenAI client using the `Client::from_env()` method, which reads the API key from the `OPENAI_API_KEY` environment variable. ```rust use rig::providers::openai::Client; #[tokio::main] async fn main() { /// uses `OPENAI_API_KEY` environment variable let openai_client = Client::from_env(); } ``` -------------------------------- ### Define a Rust Tool with the `Tool` Trait Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Implement the `rig::tool::Tool` trait to define a tool's name, arguments schema, and execution logic. This example shows an 'add' tool. ```rust use rig::tool::{Tool, ToolDefinition}; use serde::{Deserialize, Serialize}; use serde_json::json; #[derive(Deserialize)] struct OperationArgs { x: i32, y: i32 } #[derive(Debug, thiserror::Error)] #[error("Math error")] struct MathError; #[derive(Deserialize, Serialize)] struct Adder; impl Tool for Adder { const NAME: &'static str = "add"; type Error = MathError; type Args = OperationArgs; type Output = i32; async fn definition(&self, _prompt: String) -> ToolDefinition { ToolDefinition { name: "add".to_string(), description: "Add x and y together".to_string(), parameters: json!({ "type": "object", "properties": { "x": { "type": "number", "description": "First number" }, "y": { "type": "number", "description": "Second number" } }, "required": ["x", "y"] }), } } async fn call(&self, args: Self::Args) -> Result { Ok(args.x + args.y) } } ``` -------------------------------- ### Build and Prompt an Agent in Rust Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Agents wrap a completion model, system preamble, and optional tools. The `.prompt()` method drives the agentic loop, including any tool calls requested by the model. Ensure the `rig` and `tokio` crates are included. ```rust use rig::client::{CompletionClient, ProviderClient}; use rig::completion::Prompt; use rig::providers::openai::Client; #[tokio::main] async fn main() -> Result<(), Box> { let openai_client = Client::from_env(); let agent = openai_client .agent("gpt-5") .preamble("You are a helpful assistant.") .name("Bob") // used in tracing logs .build(); let prompt = "What is the Rust programming language?"; let response = agent.prompt(prompt).await?; println!("Response: {response}"); // Response: Rust is a modern, statically typed systems programming language… Ok(()) } ``` -------------------------------- ### Simple LLM Routing System in Rust Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/model-routing.md Initializes two specialized agents (coding and math) and a decision layer agent. The decision layer determines the topic of a prompt and routes it to the appropriate specialized agent. Returns an error if no suitable topic is found. ```rust use rig::{completion::Prompt, providers::openai}; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize the OpenAI client let openai_client = openai::Client::from_env(); // Create specialized agents let coding_agent = openai_client .agent("gpt-5") .preamble("You are an expert coding assistant specializing in Rust programming.") .build(); let math_agent = openai_client .agent("gpt-5") .preamble("You are a mathematics expert who excels at solving complex problems.") .build(); let router = openai_client .agent("gpt-5-mini") // we can afford to use a less expensive model here as the computation required is significantly less .preamble("Please return a word from the allowed options list, depending on which word the user's question is more closely related to. Skip all prose. Options: [\'rust', 'maths'] ") .build(); let prompt = "How do I use async with Rust?"; let topic = router.prompt(prompt).await?; println!("Topic selected: {topic}"); let res = if topic.contains("math") { math_agent.prompt(prompt).await? } else if topic.contains("rust") { coding_agent.prompt(prompt).await? } else { return Err(format!("No route found in text: {topic}").into()); }; println!("Response: {res}"); Ok(()) } ``` -------------------------------- ### Create and Use an Agent Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Defines an agent named 'Bob' with a specific model and preamble, then uses it to prompt the model and print the response. Requires `OPENAI_API_KEY` to be set. ```rust use rig::client::{CompletionClient, ProviderClient}; use rig::completion::Prompt; use rig::providers::openai::Client; #[tokio::main] async fn main() -> Result<(), Box> { let openai_client = Client::from_env(); // method provided by the ProviderClient trait let agent = openai_client .agent("gpt-5") // method provided by CompletionClient trait .preamble("You are a helpful assistant.") .name("Bob") // used in logging .build(); let prompt = "What is the Rust programming language?"; println!("{prompt}"); let response_text = agent.prompt(prompt).await?; // prompt method provided by Prompt trait println!("Response: {response_text}"); Ok(()) } ``` -------------------------------- ### Dynamic Agent Creation with Provider Registry Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Use a HashMap of function pointers to dynamically instantiate agents at runtime based on a provider name. This is useful when the provider is selected by user configuration. ```rust use std::collections::HashMap; use rig::providers::{anthropic, openai}; struct AgentConfig<'a> { name: &'a str, preamble: &'a str } enum Agents { OpenAI(rig::agent::Agent), Anthropic(rig::agent::Agent), } struct ProviderRegistry(HashMap<&'static str, fn(&AgentConfig) -> Agents>); impl ProviderRegistry { pub fn new() -> Self { Self(HashMap::from_iter([ ("openai", openai_agent as fn(&AgentConfig) -> Agents), ("anthropic", anthropic_agent as fn(&AgentConfig) -> Agents), ])) } pub fn agent(&self, provider: &str, cfg: &AgentConfig) -> Option { self.0.get(provider).map(|f| f(cfg)) } } fn openai_agent(cfg: &AgentConfig) -> Agents { let agent = openai::Client::from_env() .agent("gpt-4o").name(cfg.name).preamble(cfg.preamble).build(); Agents::OpenAI(agent) } fn anthropic_agent(cfg: &AgentConfig) -> Agents { let agent = anthropic::Client::from_env() .agent("claude-3-7-sonnet").name(cfg.name).preamble(cfg.preamble).build(); Agents::Anthropic(agent) } #[tokio::main] async fn main() { let registry = ProviderRegistry::new(); let cfg = AgentConfig { name: "Assistant", preamble: "You are a helpful assistant" }; let agent = registry.agent("openai", &cfg).unwrap(); // agent.prompt(…).await } ``` -------------------------------- ### Initialize Provider Client in Rust Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Create a typed client for a model provider using environment variables or an explicit API key. The `from_env()` constructor reads the provider-specific environment variable and panics if absent. `Client::new()` accepts the key directly. ```rust use rig::providers::openai::Client; use rig::client::ProviderClient; #[tokio::main] async fn main() { // Reads OPENAI_API_KEY from environment; panics if missing let openai_client = Client::from_env(); // Or pass the key explicitly let openai_client = Client::new("sk-..."); } ``` -------------------------------- ### Instrumented Function with Logging Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/observability.md Demonstrates how to use the `#[tracing::instrument]` macro to create a custom span named 'process_user_query' and emit 'INFO' level logs within an async function. It also shows how to integrate Rig's client for LLM interactions. ```rust use rig::client::{CompletionClient, ProviderClient}; use rig::completion::Prompt; use rig::providers::openai; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use tracing::{info, instrument}; #[tokio::main] async fn main() -> Result<(), Box> { tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "info,rig=trace".into()) ) .with(tracing_subscriber::fmt::layer()) .init(); let response = process_query("Hello world!").await.unwrap(); println!("Response: {response}"); Ok(()) } #[instrument(name = "process_user_query")] async fn process_query(user_input: &str) -> Result> { info!("Processing user query"); let openai_client = openai::Client::from_env(); let agent = openai_client .agent("gpt-4") .preamble("You are a helpful assistant.") .build(); // This completion call will emit spans automatically let response = agent.prompt(user_input).await?; info!("Query processed successfully"); Ok(response) } ``` -------------------------------- ### Implement Dynamic Tool RAG Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Use `.dynamic_tools(n, store, tools)` to dynamically select and expose contextually relevant tools to the agent. This reduces context-window bloat by sending only the top `n` tools retrieved from the vector store. ```rust let tools = some_toolset(); // your full toolset let in_memory = InMemoryVectorStore::default(); let agent = openai_client .agent("gpt-5") .dynamic_tools(2, in_memory, tools) // at most 2 tools retrieved per query .build(); ``` -------------------------------- ### Main Function for Agent Simulation Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/multi-agent-systems.md Sets up and runs multiple autonomous agents, demonstrating inter-agent communication and task execution. Requires `tokio` for the async runtime and `mpsc` channels for communication. Ensure the OPENAI_API_KEY environment variable is set. ```rust #[tokio::main] async fn main() -> Result<(), Box> { let api_key = std::env::var("OPENAI_API_KEY") .expect("OPENAI_API_KEY must be set"); // Create channels for agent communication let (tx1, rx1) = mpsc::channel(100); let (tx2, rx2) = mpsc::channel(100); let (tx3, rx3) = mpsc::channel(100); // Create agents let agent1 = AutonomousAgent::new("Tom".to_string(), api_key.clone(), rx1, tx1.clone()); let agent2 = AutonomousAgent::new("Richard".to_string(), api_key.clone(), rx2, tx2.clone()); let agent3 = AutonomousAgent::new("Harry".to_string(), api_key, rx3, tx3.clone()); // Register peers (each agent knows about the others) agent1.register_peer(tx2.clone()).await; agent1.register_peer(tx3.clone()).await; agent2.register_peer(tx1.clone()).await; agent2.register_peer(tx3.clone()).await; agent3.register_peer(tx1.clone()).await; agent3.register_peer(tx2.clone()).await; // Spawn agent actors let handle1 = tokio::spawn(agent1.run()); let handle2 = tokio::spawn(agent2.run()); let handle3 = tokio::spawn(agent3.run()); // Send initial task to Agent-Alpha tx1.send(AgentMessage::Task("Analyze the benefits of autonomous agent systems".to_string())).await?; // External trigger example tokio::time::sleep(Duration::from_secs(5)).await; tx2.send(AgentMessage::Trigger("Check system status and report findings".to_string())).await?; // Let agents run for demonstration tokio::time::sleep(Duration::from_secs(30)).await; // Shutdown tx1.send(AgentMessage::Shutdown).await?; tx2.send(AgentMessage::Shutdown).await?; tx3.send(AgentMessage::Shutdown).await?; handle1.await?; handle2.await?; handle3.await?; Ok(()) } ``` -------------------------------- ### Basic Observability with `tracing` Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Configures `tracing-subscriber` with an `EnvFilter` to capture Rig's internal traces at `TRACE` level while defaulting user code to `INFO`. Requires `tracing` and `tracing-subscriber` dependencies. ```rust use rig::{client::{CompletionClient, ProviderClient}, completion::Prompt, providers::openai}; use tracing::{info, instrument}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() -> Result<(), Box> { tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "info,rig=trace".into()) ) .with(tracing_subscriber::fmt::layer()) .init(); let response = process_query("Hello world!").await?; println!("Response: {response}"); Ok(()) } #[instrument(name = "process_user_query")] async fn process_query(user_input: &str) -> Result> { info!("Processing user query"); let agent = openai::Client::from_env() .agent("gpt-4") .preamble("You are a helpful assistant.") .build(); let response = agent.prompt(user_input).await?; info!("Query processed successfully"); Ok(response) } ``` -------------------------------- ### Stream Agent Responses Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Demonstrates how to receive streaming responses from an agent by using `stream_prompt()` instead of `prompt()`. This is useful for real-time feedback. ```rust use futures::Stream; let mut stream = agent.stream_prompt(prompt).await; while let Some(item) = stream.next().await { // .. do some work here } ``` -------------------------------- ### Initialize Basic Tracing Subscriber Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/observability.md Initializes the `tracing-subscriber` formatting layer. This requires setting the `RUSTLOG` environment variable manually to view logs. ```rust tracing_subscriber::fmt().init(); ``` -------------------------------- ### Provider Registry for Dynamic Agent Creation Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/dynamic-model-creation.md Implements a registry using a HashMap to map string names to functions that create specific agent types. This allows for dynamic instantiation of agents based on configuration. ```rust struct AgentConfig<'a> { name: &'a str, preamble: &'a str, } struct ProviderRegistry(HashMap<&'static str, fn(&AgentConfig) -> Agents>); impl ProviderRegistry { pub fn new() -> Self { Self(HashMap::from_iter([ ("anthropic", anthropic_agent as fn(&AgentConfig) -> Agents), ("openai", openai_agent as fn(&AgentConfig) -> Agents), ])) } pub fn agent(&self, provider: &str, agent_config: &AgentConfig) -> Option { self.0.get(provider).map(|p| p(agent_config)) } } /// A function that creates an instance of `Agents` (using the Anthropic variant) fn anthropic_agent(AgentConfig { name, preamble }: &AgentConfig) -> Agents { let agent = anthropic::Client::from_env() .agent(CLAUDE_3_7_SONNET) .name(name) .preamble(preamble) .build(); Agents::Anthropic(agent) } /// A function that creates an instance of `Agents` (using the OpenAI variant) fn openai_agent(AgentConfig { name, preamble }: &AgentConfig) -> Agents { let agent = openai::Client::from_env() .completions_api() .agent(GPT_4O) .name(name) .preamble(preamble) .build(); Agents::OpenAI(agent) } ``` -------------------------------- ### Rust LLM-Based Model Routing with Keyword Classification Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Route prompts to specialized agents based on a one-word classification from a lightweight router model. Ensure the router model is inexpensive (e.g., gpt-5-mini). ```rust use rig::{ client::{CompletionClient, ProviderClient}, completion::Prompt, providers::openai, }; #[tokio::main] async fn main() -> Result<(), Box> { let client = openai::Client::from_env(); let coding_agent = client.agent("gpt-5") .preamble("You are an expert Rust coding assistant.").build(); let math_agent = client.agent("gpt-5") .preamble("You are a mathematics expert.").build(); let router = client .agent("gpt-5-mini") .preamble("Return one word from [rust, maths] matching the user's topic. No prose.") .build(); let prompt = "How do I use async with Rust?"; let topic = router.prompt(prompt).await?; println!("Topic: {topic}"); let response = if topic.contains("rust") { coding_agent.prompt(prompt).await? } else if topic.contains("math") { math_agent.prompt(prompt).await? } else { return Err(format!("No route found: {topic}").into()); }; println!("Response: {response}"); Ok(()) } ``` -------------------------------- ### Implement Manager-Worker Pattern with Agents in Rig Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/multi-agent-systems.md Define a manager agent (Alice) that uses a worker agent (Bob) as a tool. Ensure agents have names and descriptions for proper tool implementation. The manager prompts the worker, and the worker's response is relayed back to the manager and then to the user. ```rust use rig:: client::{CompletionClient, ProviderClient}, completion::Prompt, }; let prompt = "Ask Bob to write an email for you and let me know what he has written."; let bob = openai_client.agent("gpt-5") .name("Bob") .description("An employee who works in admin at FooBar Inc.") .preamble("You are Bob, an employee working in admin at FooBar Inc. Alice, your manager, may ask you to do things. You need to do them.") .build(); let alice = openai_client.agent("gpt-5") .name("Alice") .description("A manager at FooBar Inc.") .preamble("You are a manager in the admin department at FooBar Inc. You manage Bob.") .tool(bob) .build(); let res = alice.prompt("Ask Bob to write an email for you and let me know what he has written.").await?; println!("{res:?}"); ``` -------------------------------- ### Prepare Documents for LLM Completion Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rag.md Converts search results from a vector store into a format suitable for LLM completion requests. Each document includes its ID, text content, and a placeholder for additional properties. ```rust let documents: Vec = results.into_iter().map(|(score, id, doc)| { Document { id, text: doc, additional_props: std::collections::HashMap::new() // whatever extra properties you want here } }).collect(); let req = CompletionRequest { documents, // fill in the rest of the fields here! } ``` -------------------------------- ### Create a Semantic Router with Embeddings Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/model-routing.md Build a router that uses embeddings to semantically match user queries to predefined routes. This requires an OpenAI client and a vector store. ```rust use rig::completion::Prompt; use rig::embeddings::EmbeddingsBuilder; use rig::providers::openai; use rig::vector_store::in_memory_store::InMemoryVectorStore; use serde::{Deserialize, Serialize}; #[derive(Clone, Default, Serialize, Deserialize, Eq, PartialEq)] struct RouteDefinition { name: String, description: String, examples: Vec, } async fn create_semantic_router( openai_client: &openai::Client, ) -> Result, Box> { let routes = vec![ RouteDefinition { name: "coding".to_string(), description: "Programming, code, and software development".to_string(), examples: vec![ "How do I write a function?".to_string(), "Debug this code".to_string(), "Implement a sorting algorithm".to_string(), ], }, RouteDefinition { name: "math".to_string(), description: "Mathematics, calculations, and equations".to_string(), examples: vec![ "Solve this equation".to_string(), "Calculate the derivative".to_string(), "What is 15% of 200?".to_string(), ], }, ]; let mut vector_store = InMemoryVectorStore::default(); for route in routes { let embedding_text = format!( "{}: {}. Examples: {}", route.name, route.description, route.examples.join(", ") ); let embedding = openai_client .embeddings("text-embedding-ada-002") .simple_document(&embedding_text) .await?; vector_store.add_document(route, embedding); } Ok(vector_store) } ``` -------------------------------- ### Call Completion with Builder Pattern Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Use the `completion_request()` method followed by builder methods like `preamble()` and `send()` for a more fluent API to call completion models. ```rust let response = openai_completions_model .completion_request("What is the Rust programming language?") .preamble("You are a helpful assistant") .send() .await?; ``` -------------------------------- ### Dynamic Tool Retrieval with RAG Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rag.md Configure an agent to dynamically fetch tool definitions using RAG. This is useful for agents managing a large number of tools, saving tokens and reducing hallucinations. ```rust let tools = some_toolset(); // this function is pseudo-code and simply represents the toolset let in_memory = InMemoryVectorStore::default(); let agent = openai_client.agent("gpt-5") .dynamic_tools(2, in_memory, some_toolset) .build(); ``` -------------------------------- ### Enable JSON Logging with Tracing Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/observability.md Configure the tracing subscriber to output logs in JSON format. This is useful for production environments where logs can be easily queried. ```rust use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, fmt}; fn main() { tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "info,rig=trace".into()) ) .with(fmt::layer().json()) .init(); // Your application code } ``` -------------------------------- ### Implement Agent Message Handling and Run Loop Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/multi-agent-systems.md Handles incoming messages and implements the agent's main run loop using `tokio::select!` for concurrent task processing and message reception. Includes logic for processing tasks, storing results, and broadcasting responses. ```rust use tokio::time::{interval, Duration}; impl AutonomousAgent { async fn handle_message(&self, task: AgentMessage) { match task { AgentMessage::Task(task) => { println!("[{}] Received task: {}", self.id, task); match self.process_autonomous_task(&task).await { Ok(result) => { println!("[{}] Completed task: {}", self.id, result); // Store in history let mut state = self.state.write().await; state.conversation_history.push(format!("Task: {} | Result: {}", task, result)); // Broadcast result to peers self.broadcast_to_peers( AgentMessage::Response(self.id.clone(), result) ).await; } Err(e) => eprintln!("[{}] Error processing task: {}", self.id, e), } } AgentMessage::Response(from_id, content) => { println!("[{}] Received response from {}: {}", self.id, from_id, content); let mut state = self.state.write().await; state.conversation_history.push(format!("From {}: {}", from_id, content)); ``` -------------------------------- ### Enable Dynamic Context for RAG with Agents Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Attach a vector store directly to an agent using `.dynamic_context(n, store)`. The agent automatically retrieves the top `n` relevant document chunks and injects them as context before each prompt. ```rust use rig::{ client::{CompletionClient, ProviderClient}, completion::Prompt, providers::openai::{Client, GPT_4}, vector_store::in_memory_store::InMemoryVectorStore, }; #[tokio::main] async fn main() -> Result<(), Box> { let openai_client = Client::from_env(); // (populate vector_store with embeddings as shown above) let vector_store = InMemoryVectorStore::default(); let agent = openai_client .agent(GPT_4) .preamble("Answer questions using the provided context.") .dynamic_context(2, vector_store) // retrieve top-2 docs per query .build(); let response = agent .prompt("What is Rig and how does it help with LLM applications?") .await?; println!("Agent response: {response}"); Ok(()) } ``` -------------------------------- ### Initialize Tracing with EnvFilter Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/observability.md Configures `tracing-subscriber` with an `EnvFilter` to dynamically set logging levels based on the `RUSTLOG` environment variable. It defaults to 'info' level for general logging and 'trace' level specifically for the 'rig' crate. ```rust tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "info,rig=trace".into()) ) .with(tracing_subscriber::fmt::layer()) .init(); ``` -------------------------------- ### Rust Conversation Memory Management with Summarization Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Implement conversation memory that stores messages and compacts them into a summary when exceeding a threshold. Requires a CompletionModel client for summarization. ```rust use rig::completion::{CompletionModel, Message}; use rig::message::{AssistantContent, OneOrMany, UserContent}; pub struct ConversationMemory { messages: Vec, max_messages: usize, summary: Option, } impl ConversationMemory { pub fn new(max_messages: usize) -> Self { Self { messages: Vec::new(), max_messages, summary: None } } pub fn add_user_message(&mut self, input: &str) { self.messages.push(Message::User { content: OneOrMany::one(UserContent::text(input)), }); } pub fn add_assistant_message(&mut self, input: &str) { self.messages.push(Message::Assistant { content: OneOrMany::one(AssistantContent::text(input)), }); } pub fn get_messages(&self) -> &[Message] { &self.messages } /// Compact history to a summary when it exceeds max_messages pub async fn compact(&mut self, client: &T) -> Result<(), Box> { if self.messages.len() <= self.max_messages { return Ok(()); } let summary_prompt = format!( "Provide a concise summary of this conversation:\n\n{:?}", self.messages ); let response = client.completion_request(&summary_prompt).send().await?; self.summary = Some(format!("{response:?}")); self.messages.clear(); Ok(()) } /// Inject past-summary into a system preamble pub fn build_system_prompt(&self, base_prompt: &str) -> String { match &self.summary { Some(s) => format!("{base_prompt}\n\nPrevious conversation summary:\n{s}"), None => base_prompt.to_string(), } } } // Usage: let mut memory = ConversationMemory::new(10); memory.add_user_message("Tell me about Rust"); memory.add_assistant_message("Rust is a systems programming language…"); // Compact when needed if memory.get_messages().len() > 10 { memory.compact(&completion_model).await?; } ``` -------------------------------- ### Completion Request using CompletionRequest struct Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Send a completion request by creating a `CompletionRequest` struct and calling the `completion` method. ```APIDOC ## Completion Request using CompletionRequest struct Create a `CompletionRequest` object with messages and optional preamble, then call the `completion` method on the model. ```rust use rig::client::CompletionsClient; use rig::core::{Message, UserContent, OneOrMany}; // Assuming openai_completions_model is already initialized let message = Message::User { content: OneOrMany::one(UserContent::text("What is the Rust programming language?")) }; let req = CompletionRequest { messages: OneOrMany::one(message), premamble: Some("You are a helpful assistant.".to_string()), ..Default::default() }; let response = openai_completions_model.completion(req).await?; ``` ``` -------------------------------- ### Define User Profile Structure Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/memory.md Defines the structure for a user profile, including preferences, context, communication style, and last updated timestamp. Preferences are stored in a HashMap. ```rust pub struct UserProfile { pub preferences: HashMap, pub context: Vec, pub communication_style: Option, pub last_updated: DateTime, } ``` -------------------------------- ### Completion Request using Builder Pattern Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/rig-api-call.md Send a completion request using a more fluent builder pattern by calling `completion_request` and chaining methods. ```APIDOC ## Completion Request using Builder Pattern Alternatively, use the `completion_request` method followed by builder methods like `preamble` and `send` for a more concise request. ```rust use rig::client::CompletionsClient; // Assuming openai_completions_model is already initialized let response = openai_completions_model .completion_request("What is the Rust programming language?") .preamble("You are a helpful assistant") .send() .await?; ``` ``` -------------------------------- ### Define a Typed Router for Specific Providers Source: https://github.com/0xplaygrounds/rig-book/blob/main/src/playbook/model-routing.md Use a HashMap to route requests to agents of a specific provider type. This is suitable for simpler use cases where agents are from a single provider. ```rust type OpenAIAgent = Agent; struct TypedRouter { routes: HashMap, } impl TypedRouter { pub fn new() -> Self { Self { routes: HashMap::new(), } } pub fn add_route(mut self, route_loc: &str, agent: OpenAIAgent) -> Self { self.routes.insert(route_loc.to_string(), agent); self } pub fn fetch_agent(&self, route: &str) -> Option<&OpenAIAgent> { self.routes.get(route) } } ``` -------------------------------- ### Rust Semantic Model Routing with Embeddings and Vector Store Source: https://context7.com/0xplaygrounds/rig-book/llms.txt Implement deterministic model routing by embedding route definitions into a vector store and matching queries via cosine similarity. Uses an OpenAI embedding model and an in-memory vector store. ```rust use rig::{ providers::openai, vector_store::{VectorSearchRequest, VectorStoreIndex, in_memory_store::InMemoryVectorStore}, }; use serde::{Deserialize, Serialize}; #[derive(Clone, Default, Serialize, Deserialize, Eq, PartialEq)] struct RouteDefinition { name: String, description: String, examples: Vec, } async fn semantic_route_query( query: &str, router: &InMemoryVectorStore, client: &openai::Client, ) -> Result> { let model = client.embedding_model("text-embedding-ada-002"); let index = router.clone().index(model); let req = VectorSearchRequest::builder() .query(query) .samples(1) .build()?; let results = index.top_n::(req).await?; let route = results .first() .map(|(_, _, r)| r.name.clone()) .unwrap_or_default(); Ok(route) } ```