Google Search

Use Google Search for real-time information and research.

Overview

Google Search allows agents to:

  • Search the web for current information

  • Find documentation and tutorials

  • Research game mechanics

  • Look up technical information

  • Verify facts and data

Basic Setup

agent.AddGoogleTool(GoogleToolType.Search);

Configure API Key

agent.Settings.GoogleTools = new GoogleToolsSettings
{
    ApiKey = "your-google-api-key",
    SearchEngineId = "your-search-engine-id"
};

Perform Searches

await agent.SendAsync("Search Google for Unity best practices");
await agent.SendAsync("Find information about C# async/await");

With Specific Query

await agent.SendAsync(@"
Search Google for:
'Unity ML-Agents tutorial 2024'
and summarize the key points
");

Search Results

Access Results

agent.onGoogleSearchCompleted.AddListener(results =>
{
    Debug.Log($"Found {results.Count} results");
    
    foreach (var result in results)
    {
        Debug.Log($"Title: {result.Title}");
        Debug.Log($"URL: {result.Url}");
        Debug.Log($"Snippet: {result.Snippet}");
    }
});

Display Results in UI

public class SearchResultsUI : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    [SerializeField] private GameObject resultPrefab;
    [SerializeField] private Transform resultsContainer;
    
    void Start()
    {
        agent.onGoogleSearchCompleted.AddListener(DisplayResults);
    }
    
    void DisplayResults(List<SearchResult> results)
    {
        // Clear previous results
        foreach (Transform child in resultsContainer)
        {
            Destroy(child.gameObject);
        }
        
        // Display new results
        foreach (var result in results)
        {
            var resultObj = Instantiate(resultPrefab, resultsContainer);
            
            var title = resultObj.transform.Find("Title").GetComponent<TMP_Text>();
            var snippet = resultObj.transform.Find("Snippet").GetComponent<TMP_Text>();
            var button = resultObj.GetComponent<Button>();
            
            title.text = result.Title;
            snippet.text = result.Snippet;
            
            button.onClick.AddListener(() => Application.OpenURL(result.Url));
        }
    }
}

Game Development Use Cases

public async void SearchUnityAssets(string query)
{
    await agent.SendAsync($@"
Search Google for Unity Asset Store assets related to: '{query}'
Focus on:
- Asset name and price
- Rating and reviews
- Compatible Unity versions
- Publisher
");
}

// Usage
SearchUnityAssets("particle effects");
SearchUnityAssets("inventory system");

Documentation Lookup

public async void FindDocumentation(string topic)
{
    await agent.SendAsync($@"
Search for Unity documentation on: '{topic}'
Include:
- Official Unity docs
- API reference
- Code examples
- Best practices
");
}

// Usage
FindDocumentation("NavMesh Agent");
FindDocumentation("Scriptable Objects");

Error Research

public async void ResearchError(string errorMessage)
{
    await agent.SendAsync($@"
Search for solutions to this Unity error:
'{errorMessage}'

Find:
- Common causes
- Solutions
- Related forum discussions
- Code fixes
");
}

// Usage
ResearchError("NullReferenceException at line 42");

Package Compatibility

public async void CheckCompatibility(string packageName, string unityVersion)
{
    await agent.SendAsync($@"
Search if '{packageName}' is compatible with Unity {unityVersion}
Look for:
- Official compatibility information
- Known issues
- User experiences
- Alternative packages if incompatible
");
}

// Usage
CheckCompatibility("UniTask", "2022.3");

Custom Search Parameters

public async void AdvancedSearch(string query, SearchOptions options)
{
    string searchQuery = query;
    
    if (options.DateRestrict != null)
    {
        searchQuery += $" after:{options.DateRestrict}";
    }
    
    if (options.SiteSearch != null)
    {
        searchQuery += $" site:{options.SiteSearch}";
    }
    
    if (options.ExcludeTerms != null)
    {
        searchQuery += $" -{options.ExcludeTerms}";
    }
    
    await agent.SendAsync($"Search Google for: {searchQuery}");
}

[System.Serializable]
public class SearchOptions
{
    public string DateRestrict;  // e.g., "2024-01-01"
    public string SiteSearch;    // e.g., "unity.com"
    public string ExcludeTerms;  // e.g., "deprecated"
}

// Usage
AdvancedSearch("Unity networking", new SearchOptions
{
    DateRestrict = "2024-01-01",
    SiteSearch = "docs.unity3d.com"
});
public async void SearchImages(string query)
{
    await agent.SendAsync($@"
Search Google Images for: '{query}'
Find high-quality reference images suitable for game development
");
}

// Usage
SearchImages("medieval armor reference");
SearchImages("cyberpunk environment concept art");

Research Assistant

Game Mechanics Research

public class GameResearchAssistant : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    
    public async void ResearchMechanic(string mechanic)
    {
        await agent.SendAsync($@"
Research the '{mechanic}' game mechanic:

1. Search for examples in popular games
2. Find implementation tutorials
3. Look for best practices
4. Identify common pitfalls
5. Suggest Unity assets or packages

Provide a comprehensive summary with links
");
    }
}

// Usage
assistant.ResearchMechanic("inventory system");
assistant.ResearchMechanic("dialogue system");
assistant.ResearchMechanic("skill tree");

Technology Comparison

public async void CompareTechnologies(string tech1, string tech2)
{
    await agent.SendAsync($@"
Compare '{tech1}' vs '{tech2}' for Unity game development:

Search for:
- Performance differences
- Ease of use
- Community support
- Documentation quality
- Use cases
- Pros and cons

Provide a detailed comparison
");
}

// Usage
CompareTechnologies("UniTask", "Coroutines");
CompareTechnologies("Addressables", "AssetBundles");

Caching and Rate Limiting

Cache Search Results

public class SearchCache : MonoBehaviour
{
    private Dictionary<string, CachedSearch> cache = new();
    private TimeSpan cacheExpiry = TimeSpan.FromHours(1);
    
    public async UniTask<List<SearchResult>> GetOrSearch(AgentBehaviour agent, string query)
    {
        if (cache.TryGetValue(query, out var cached))
        {
            if (DateTime.Now - cached.Timestamp < cacheExpiry)
            {
                Debug.Log("Using cached search results");
                return cached.Results;
            }
        }
        
        // Perform new search
        var results = await PerformSearch(agent, query);
        
        cache[query] = new CachedSearch
        {
            Results = results,
            Timestamp = DateTime.Now
        };
        
        return results;
    }
    
    async UniTask<List<SearchResult>> PerformSearch(AgentBehaviour agent, string query)
    {
        // Implementation
        return new List<SearchResult>();
    }
    
    struct CachedSearch
    {
        public List<SearchResult> Results;
        public DateTime Timestamp;
    }
}

Rate Limiting

public class SearchRateLimiter : MonoBehaviour
{
    [SerializeField] private int maxSearchesPerMinute = 10;
    
    private Queue<DateTime> searchTimestamps = new();
    
    public async UniTask<bool> CanSearch()
    {
        // Remove old timestamps
        while (searchTimestamps.Count > 0 &&
               DateTime.Now - searchTimestamps.Peek() > TimeSpan.FromMinutes(1))
        {
            searchTimestamps.Dequeue();
        }
        
        // Check limit
        if (searchTimestamps.Count >= maxSearchesPerMinute)
        {
            Debug.LogWarning("Search rate limit reached");
            return false;
        }
        
        searchTimestamps.Enqueue(DateTime.Now);
        return true;
    }
}

Error Handling

Handle Search Errors

agent.onGoogleSearchError.AddListener(error =>
{
    Debug.LogError($"Search failed: {error}");
    
    if (error.Contains("quota"))
    {
        ShowMessage("Search quota exceeded. Try again later.");
    }
    else if (error.Contains("invalid_key"))
    {
        ShowMessage("Invalid Google API key. Check settings.");
    }
    else if (error.Contains("rate_limit"))
    {
        ShowMessage("Too many searches. Please wait.");
    }
    else
    {
        ShowMessage("Search failed. Please try again.");
    }
});

Complete Example

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

public class GoogleSearchManager : MonoBehaviour
{
    [SerializeField] private AgentBehaviour agent;
    [SerializeField] private TMP_InputField searchInput;
    [SerializeField] private Button searchButton;
    [SerializeField] private Transform resultsContainer;
    [SerializeField] private GameObject resultPrefab;
    
    private Dictionary<string, List<SearchResult>> cache = new();
    private Queue<DateTime> searchTimestamps = new();
    private const int MAX_SEARCHES_PER_MINUTE = 10;
    
    async void Start()
    {
        await SetupGoogleSearch();
        
        searchButton.onClick.AddListener(OnSearchClicked);
    }
    
    async UniTask SetupGoogleSearch()
    {
        // Configure Google Tools
        agent.Settings.GoogleTools = new GoogleToolsSettings
        {
            ApiKey = GetGoogleApiKey(),
            SearchEngineId = GetSearchEngineId()
        };
        
        // Add Google Search tool
        agent.AddGoogleTool(GoogleToolType.Search);
        
        // Listen for results
        agent.onGoogleSearchCompleted.AddListener(DisplayResults);
        agent.onGoogleSearchError.AddListener(OnSearchError);
        
        Debug.Log("✓ Google Search ready");
    }
    
    async void OnSearchClicked()
    {
        string query = searchInput.text.Trim();
        
        if (string.IsNullOrEmpty(query))
        {
            ShowMessage("Please enter a search query");
            return;
        }
        
        // Check rate limit
        if (!await CanSearch())
        {
            ShowMessage("Too many searches. Please wait.");
            return;
        }
        
        // Check cache
        if (cache.TryGetValue(query, out var cachedResults))
        {
            Debug.Log("Using cached results");
            DisplayResults(cachedResults);
            return;
        }
        
        // Perform search
        Debug.Log($"🔍 Searching: {query}");
        searchButton.interactable = false;
        
        await agent.SendAsync($"Search Google for: {query}");
    }
    
    async UniTask<bool> CanSearch()
    {
        // Clean old timestamps
        while (searchTimestamps.Count > 0 &&
               DateTime.Now - searchTimestamps.Peek() > TimeSpan.FromMinutes(1))
        {
            searchTimestamps.Dequeue();
        }
        
        // Check limit
        if (searchTimestamps.Count >= MAX_SEARCHES_PER_MINUTE)
        {
            return false;
        }
        
        searchTimestamps.Enqueue(DateTime.Now);
        return true;
    }
    
    void DisplayResults(List<SearchResult> results)
    {
        Debug.Log($"✓ Found {results.Count} results");
        
        // Clear previous
        foreach (Transform child in resultsContainer)
        {
            Destroy(child.gameObject);
        }
        
        // Display new results
        foreach (var result in results)
        {
            CreateResultItem(result);
        }
        
        // Cache results
        string query = searchInput.text.Trim();
        cache[query] = results;
        
        searchButton.interactable = true;
    }
    
    void CreateResultItem(SearchResult result)
    {
        var resultObj = Instantiate(resultPrefab, resultsContainer);
        
        var titleText = resultObj.transform.Find("Title").GetComponent<TMP_Text>();
        var urlText = resultObj.transform.Find("URL").GetComponent<TMP_Text>();
        var snippetText = resultObj.transform.Find("Snippet").GetComponent<TMP_Text>();
        var button = resultObj.GetComponent<Button>();
        
        titleText.text = result.Title;
        urlText.text = result.Url;
        snippetText.text = result.Snippet;
        
        button.onClick.AddListener(() =>
        {
            Debug.Log($"Opening: {result.Url}");
            Application.OpenURL(result.Url);
        });
    }
    
    void OnSearchError(string error)
    {
        Debug.LogError($"Search error: {error}");
        ShowMessage($"Search failed: {error}");
        searchButton.interactable = true;
    }
    
    void ShowMessage(string message)
    {
        Debug.Log(message);
        // Show in UI
    }
    
    string GetGoogleApiKey()
    {
        return PlayerPrefs.GetString("GoogleApiKey", "");
    }
    
    string GetSearchEngineId()
    {
        return PlayerPrefs.GetString("GoogleSearchEngineId", "");
    }
}

Next Steps

Last updated