Web Search

Search the web and retrieve real-time information using Google Search.

Overview

Web Search allows agents to:

  • Search Google for current information

  • Access real-time data

  • Verify facts

  • Find URLs and resources

  • Get latest news and updates

Basic Setup

// Add web search tool (Google AI only)
agent.AddTool(ToolType.WebSearch);

// Or for Google Gemini
agent.AddTool(ToolType.GoogleSearch);

Simple Searches

Ask Questions

await agent.SendAsync("What's the current weather in Tokyo?");
await agent.SendAsync("What are the latest Unity releases?");
await agent.SendAsync("Who won the game awards 2024?");

Get Current Information

await agent.SendAsync("What's the current price of Bitcoin?");
await agent.SendAsync("What are today's trending topics on Twitter?");
await agent.SendAsync("Find the latest news about AI in gaming");

Search Results

Access Search Results

agent.onResponseCompleted.AddListener(response =>
{
    if (response.SearchResults != null)
    {
        Debug.Log($"Found {response.SearchResults.Count} search results");
        
        foreach (var result in response.SearchResults)
        {
            Debug.Log($"Title: {result.Title}");
            Debug.Log($"URL: {result.Url}");
            Debug.Log($"Snippet: {result.Snippet}");
        }
    }
});

Display Search Results

public class SearchResultsDisplay : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    [SerializeField] private GameObject resultPrefab;
    [SerializeField] private Transform resultsContainer;
    
    void Start()
    {
        agent.onResponseCompleted.AddListener(DisplayResults);
    }
    
    void DisplayResults(Response response)
    {
        // Clear previous results
        foreach (Transform child in resultsContainer)
        {
            Destroy(child.gameObject);
        }
        
        // Display new results
        if (response.SearchResults != null)
        {
            foreach (var result in response.SearchResults)
            {
                var resultObj = Instantiate(resultPrefab, resultsContainer);
                var display = resultObj.GetComponent<SearchResultItem>();
                display.SetResult(result);
            }
        }
    }
}

public class SearchResultItem : MonoBehaviour
{
    [SerializeField] private TMP_Text titleText;
    [SerializeField] private TMP_Text urlText;
    [SerializeField] private TMP_Text snippetText;
    [SerializeField] private Button openButton;
    
    private SearchResult result;
    
    public void SetResult(SearchResult result)
    {
        this.result = result;
        
        titleText.text = result.Title;
        urlText.text = result.Url;
        snippetText.text = result.Snippet;
        
        openButton.onClick.AddListener(() => Application.OpenURL(result.Url));
    }
}

Use Cases

News and Updates

public class NewsAssistant : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    
    void Start()
    {
        agent.AddTool(ToolType.WebSearch);
    }
    
    public async void GetLatestNews(string topic)
    {
        await agent.SendAsync($"What's the latest news about {topic}?");
    }
    
    public async void CheckUpdates(string software)
    {
        await agent.SendAsync($"What's the latest version of {software} and what's new?");
    }
}

Fact Checking

public async void VerifyFact(string claim)
{
    await agent.SendAsync($"Is this true: {claim}? Please verify with current information.");
}

Research Assistant

public class ResearchAssistant : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    
    void Start()
    {
        agent.AddTool(ToolType.WebSearch);
    }
    
    public async void Research(string topic)
    {
        await agent.SendAsync($@"
Research {topic} and provide:
1. Key facts and statistics
2. Recent developments
3. Expert opinions
4. Relevant resources and links
");
    }
    
    public async void CompareOptions(string option1, string option2)
    {
        await agent.SendAsync($@"
Compare {option1} vs {option2}:
1. Pros and cons of each
2. Current market prices
3. User reviews and ratings
4. Which is better for what use case
");
    }
}

Game Development Use Cases

Check Asset Store

public async void FindAssets(string assetType)
{
    await agent.SendAsync($@"
Find Unity Asset Store packages for {assetType}:
1. Top rated options
2. Price range
3. Key features
4. User reviews
Provide direct links.
");
}

Look Up Documentation

public async void FindDocumentation(string topic)
{
    await agent.SendAsync($@"
Find documentation for {topic}:
1. Official Unity docs link
2. Tutorial links
3. Community resources
4. Example projects
");
}

Check Compatibility

public async void CheckCompatibility(string plugin, string unityVersion)
{
    await agent.SendAsync($@"
Is {plugin} compatible with Unity {unityVersion}?
Check the official page and recent reports.
");
}

Advanced Usage

Custom Search Queries

public class AdvancedSearchAgent : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    
    public async void SearchWithFilters(string query, SearchFilters filters)
    {
        string searchQuery = BuildSearchQuery(query, filters);
        await agent.SendAsync($"Search for: {searchQuery}");
    }
    
    string BuildSearchQuery(string query, SearchFilters filters)
    {
        var parts = new List<string> { query };
        
        if (filters.Site != null)
            parts.Add($"site:{filters.Site}");
        
        if (filters.FileType != null)
            parts.Add($"filetype:{filters.FileType}");
        
        if (filters.DateRange != null)
            parts.Add($"after:{filters.DateRange}");
        
        return string.Join(" ", parts);
    }
}

[System.Serializable]
public class SearchFilters
{
    public string Site;      // e.g., "unity3d.com"
    public string FileType;  // e.g., "pdf"
    public string DateRange; // e.g., "2024-01-01"
}

Search Multiple Sources

public async void MultiSourceSearch(string topic)
{
    await agent.SendAsync($@"
Search for information about {topic} from:
1. Official documentation
2. Stack Overflow discussions
3. Reddit community posts
4. Recent blog articles
5. Video tutorials

Summarize findings from each source.
");
}

Rate Limiting

Handle Rate Limits

public class SearchRateLimiter : MonoBehaviour
{
    [SerializeField] private int maxSearchesPerMinute = 10;
    
    private Queue<DateTime> searchTimes = new();
    
    public bool CanSearch()
    {
        // Remove old entries
        var cutoff = DateTime.Now.AddMinutes(-1);
        while (searchTimes.Count > 0 && searchTimes.Peek() < cutoff)
        {
            searchTimes.Dequeue();
        }
        
        return searchTimes.Count < maxSearchesPerMinute;
    }
    
    public void RecordSearch()
    {
        searchTimes.Enqueue(DateTime.Now);
    }
    
    public async UniTask<bool> SearchWithRateLimit(string query)
    {
        if (!CanSearch())
        {
            Debug.LogWarning("Search rate limit reached");
            return false;
        }
        
        RecordSearch();
        await agent.SendAsync(query);
        return true;
    }
}

Caching

Cache Search Results

public class SearchCache : MonoBehaviour
{
    private Dictionary<string, CachedResult> cache = new();
    private TimeSpan cacheExpiry = TimeSpan.FromHours(1);
    
    public async UniTask<Response> SearchWithCache(string query)
    {
        // Check cache
        if (cache.TryGetValue(query, out var cached))
        {
            if (DateTime.Now - cached.Timestamp < cacheExpiry)
            {
                Debug.Log("📦 Using cached result");
                return cached.Response;
            }
        }
        
        // Perform search
        var response = await agent.SendAsync(query);
        
        // Cache result
        cache[query] = new CachedResult
        {
            Response = response,
            Timestamp = DateTime.Now
        };
        
        return response;
    }
    
    class CachedResult
    {
        public Response Response;
        public DateTime Timestamp;
    }
}

Error Handling

Handle Search Errors

agent.onToolCallError.AddListener((toolCall, error) =>
{
    if (toolCall.Function.Name.Contains("search"))
    {
        Debug.LogError($"Search failed: {error}");
        
        if (error.Contains("rate limit"))
        {
            ShowMessage("Too many searches. Please wait a moment.");
        }
        else if (error.Contains("timeout"))
        {
            ShowMessage("Search timed out. Please try again.");
        }
        else
        {
            ShowMessage("Search temporarily unavailable.");
        }
    }
});

Best Practices

1. Be Specific

// ❌ Vague
await agent.SendAsync("Find Unity stuff");

// ✅ Specific
await agent.SendAsync("Find Unity 2023 LTS features and changes");

2. Request Sources

await agent.SendAsync(@"
What's the latest on Unity 6?
Please provide official sources and links.
");

3. Set Context

await agent.SendAsync(@"
I'm developing a mobile game with Unity.
Find the best practices for mobile optimization in 2024.
Include official Unity recommendations.
");

Complete Example

using UnityEngine;
using Glitch9.AIDevKit.Agents;
using Cysharp.Threading.Tasks;

public class WebSearchManager : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    [SerializeField] private int maxSearchesPerMinute = 10;
    
    private Queue<DateTime> searchHistory = new();
    private Dictionary<string, CachedResult> cache = new();
    
    void Start()
    {
        SetupWebSearch();
    }
    
    void SetupWebSearch()
    {
        agent.AddTool(ToolType.WebSearch);
        agent.onResponseCompleted.AddListener(OnSearchCompleted);
        agent.onToolCallError.AddListener(OnSearchError);
        
        Debug.Log("✓ Web search ready");
    }
    
    public async void Search(string query)
    {
        Debug.Log($"🔍 Searching: {query}");
        
        // Check rate limit
        if (!CanSearch())
        {
            ShowMessage("Too many searches. Please wait.");
            return;
        }
        
        // Check cache
        if (TryGetCached(query, out var cached))
        {
            Debug.Log("📦 Using cached result");
            DisplayResults(cached);
            return;
        }
        
        try
        {
            // Perform search
            RecordSearch();
            await agent.SendAsync(query);
        }
        catch (Exception ex)
        {
            Debug.LogError($"Search failed: {ex.Message}");
            ShowMessage("Search failed. Please try again.");
        }
    }
    
    bool CanSearch()
    {
        var cutoff = DateTime.Now.AddMinutes(-1);
        
        while (searchHistory.Count > 0 && searchHistory.Peek() < cutoff)
        {
            searchHistory.Dequeue();
        }
        
        return searchHistory.Count < maxSearchesPerMinute;
    }
    
    void RecordSearch()
    {
        searchHistory.Enqueue(DateTime.Now);
    }
    
    bool TryGetCached(string query, out Response cached)
    {
        if (cache.TryGetValue(query, out var result))
        {
            if (DateTime.Now - result.Timestamp < TimeSpan.FromHours(1))
            {
                cached = result.Response;
                return true;
            }
        }
        
        cached = null;
        return false;
    }
    
    void OnSearchCompleted(Response response)
    {
        Debug.Log("✓ Search completed");
        
        // Cache result
        string query = GetLastQuery();
        if (!string.IsNullOrEmpty(query))
        {
            cache[query] = new CachedResult
            {
                Response = response,
                Timestamp = DateTime.Now
            };
        }
        
        // Display results
        DisplayResults(response);
    }
    
    void DisplayResults(Response response)
    {
        if (response.SearchResults != null)
        {
            Debug.Log($"📋 {response.SearchResults.Count} result(s):");
            
            foreach (var result in response.SearchResults)
            {
                Debug.Log($"  • {result.Title}");
                Debug.Log($"    {result.Url}");
            }
        }
    }
    
    void OnSearchError(ToolCall toolCall, string error)
    {
        if (toolCall.Function.Name.Contains("search"))
        {
            Debug.LogError($"Search error: {error}");
            
            if (error.Contains("rate limit"))
            {
                ShowMessage("Rate limit exceeded. Please wait.");
            }
            else
            {
                ShowMessage("Search failed. Please try again.");
            }
        }
    }
    
    string GetLastQuery()
    {
        // Get last user message
        var messages = agent.Conversation.Messages;
        var lastMessage = messages.LastOrDefault(m => m.Role == "user");
        return lastMessage?.Content ?? "";
    }
    
    void ShowMessage(string message)
    {
        Debug.Log($"ℹ️ {message}");
        // Show UI notification
    }
    
    class CachedResult
    {
        public Response Response;
        public DateTime Timestamp;
    }
}

Limitations

What's Available

  • ✅ Current information (news, prices, etc.)

  • ✅ Public websites and pages

  • ✅ Recent updates and changes

  • ✅ Factual information

  • ❌ Content behind paywalls

  • ❌ Private or restricted sites

  • ❌ Real-time chat/social media streams

Rate Limits

// Google Search has rate limits
// - Implement request throttling
// - Cache results when possible
// - Handle 429 errors gracefully

Next Steps

Last updated