<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged algolia</title>
<link>https://coderlegion.com/tag/algolia</link>
<description>Powered by Question2Answer</description>
<item>
<title>Autopilot - Assistant : Building an AI Copilot with Algolia Agent Studio</title>
<link>https://coderlegion.com/12341/autopilot-assistant-building-an-ai-copilot-with-algolia-agent-studio</link>
<description>&lt;h3 id=&quot;what-i-built&quot;&gt;What I Built&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ops Engineering Copilot&lt;/strong&gt; is an AI-powered chat widget that lives on my Hugo blog and answers visitor questions using conversational AI grounded in my actual content.&lt;/p&gt;
&lt;p&gt;Ask it &quot;What is Datalaria?&quot; and instead of a generic response, you get:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Datalaria is a technical blog focused on data engineering, artificial intelligence, and business operations. As the Ops Engineering Copilot, I specialize in providing actionable, engineering-focused advice across four key pillars: S&amp;amp;OP, Product, Projects, and People...&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is NOT just search&lt;/strong&gt; – it's a conversational agent that synthesizes knowledge from 72 indexed posts to provide contextual, helpful answers.&lt;/p&gt;
&lt;h3 id=&quot;demo&quot;&gt;Demo&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://datalaria.com/en/posts/ai_agents_part8/demo_algoria.gif&quot; alt=&quot;Chat widget showing AI-generated response about Datalaria&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://datalaria.com&quot;&gt;datalaria.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Try these questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;What is the Autopilot project?&quot;&lt;/li&gt;
&lt;li&gt;&quot;How do I detect outliers in S&amp;amp;OP data?&quot;&lt;/li&gt;
&lt;li&gt;&quot;Tell me about GitHub Actions automation&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-architecture&quot;&gt;The Architecture&lt;/h3&gt;
&lt;p&gt;This project extends my existing Autopilot system (documented in Parts 1-7) with a new capability: &lt;strong&gt;consumer-facing AI search&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://datalaria.com/es/posts/ia_agents_part8/arquitectura.png&quot; alt=&quot;arquitectura&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;key-components&quot;&gt;Key Components&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Component &lt;/th&gt;&lt;th&gt;Technology &lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Content Source &lt;/td&gt;&lt;td&gt;Hugo + Markdown &lt;/td&gt;&lt;td&gt;72 bilingual posts (EN/ES)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Indexing &lt;/td&gt;&lt;td&gt;Python + Algolia API &lt;/td&gt;&lt;td&gt;Extract, clean, classify, push&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Agent &lt;/td&gt;&lt;td&gt;Algolia Agent Studio &lt;/td&gt;&lt;td&gt;RAG + LLM orchestration&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;LLM &lt;/td&gt;&lt;td&gt;Google Gemini &lt;/td&gt;&lt;td&gt;Response generation&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Proxy &lt;/td&gt;&lt;td&gt;Netlify Functions &lt;/td&gt;&lt;td&gt;CORS bypass + error handling&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Widget &lt;/td&gt;&lt;td&gt;Vanilla JS &lt;/td&gt;&lt;td&gt;Embedded chat interface&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;how-i-used-algolia-agent-studio&quot;&gt;How I Used Algolia Agent Studio&lt;/h3&gt;
&lt;h4 id=&quot;step-1-indexing-the-knowledge-base&quot;&gt;Step 1: Indexing the Knowledge Base&lt;/h4&gt;
&lt;p&gt;The foundation is a Python script that processes all Hugo posts and pushes them to Algolia:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# algolia_sync.py - Key extraction logic
def extract_post_data(filepath: Path, lang: str) -&amp;gt; Optional[Dict]:
    &quot;&quot;&quot;Extract and clean post data for Algolia indexing.&quot;&quot;&quot;
    post = frontmatter.load(filepath)
    
    # Clean markdown to plain text
    html = markdown.markdown(post.content)
    soup = BeautifulSoup(html, 'html.parser')
    clean_text = soup.get_text(separator=' ', strip=True)
    
    # Classify into operational domains
    domain = classify_domain(post.metadata.get('categories', []))
    
    return {
        'objectID': generate_object_id(filepath),
        'title': post.metadata.get('title', ''),
        'content': clean_text[:8000],  # Algolia limit
        'description': post.metadata.get('description', ''),
        'url': build_url(filepath, lang),
        'lang': lang,
        'domain': domain,  # S&amp;amp;OP, Product, Projects, People
        'date': post.metadata.get('date'),
        'tags': post.metadata.get('tags', []),
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 72 records indexed (36 English + 36 Spanish) across 4 operational domains.&lt;/p&gt;
&lt;h4 id=&quot;step-2-configuring-the-agent-in-agent-studio&quot;&gt;Step 2: Configuring the Agent in Agent Studio&lt;/h4&gt;
&lt;p&gt;In the Algolia dashboard, I created an agent with a specialized system prompt:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;You are the &quot;Ops Engineering Copilot&quot; for the Datalaria technical blog.

Your expertise spans four pillars:
-  S&amp;amp;OP: Demand forecasting, data hygiene, outlier detection
-  Product: Lifecycle management, data-driven decisions
-  Projects: CI/CD, GitHub Actions, automation, DevOps
-  People: Team dynamics, onboarding, collaboration

Guidelines:
1. Ground all answers in the indexed blog content
2. Be conversational but technically precise
3. When relevant, suggest specific articles to read
4. Support both English and Spanish queries
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;LLM Provider:&lt;/strong&gt; Google Gemini (excellent free tier for prototyping)&lt;/p&gt;
&lt;h4 id=&quot;step-3-the-cors-problem-and-solution-&quot;&gt;Step 3: The CORS Problem (and Solution)&lt;/h4&gt;
&lt;p&gt;Here's where it got interesting. The Agent Studio API doesn't allow direct browser calls due to CORS restrictions. My first attempt:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// ❌ This fails with CORS error
fetch('[https://agent-studio.eu.algolia.com/1/agents/](https://agent-studio.eu.algolia.com/1/agents/){id}/completions', {
    method: 'POST',
    headers: { 'X-Algolia-API-Key': key }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; A Netlify Function as a proxy:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// netlify/functions/agent-chat.js
exports.handler = async (event) =&amp;gt; {
    const { query, conversationId } = JSON.parse(event.body);
    
    // Call Agent Studio from server-side (no CORS!)
    const response = await fetch(
        `https://agent-studio.eu.algolia.com/1/agents/${AGENT_ID}/completions?compatibilityMode=ai-sdk-4&amp;amp;stream=false`,
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-Algolia-API-Key': SEARCH_KEY,
                'X-Algolia-Application-Id': APP_ID
            },
            body: JSON.stringify({
                messages: [{ role: 'user', content: query }],
                id: conversationId
            })
        }
    );
    
    const data = await response.json();
    
    return {
        statusCode: 200,
        body: JSON.stringify({
            answer: data.content,
            conversationId: data.id
        })
    };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Key discoveries:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;compatibilityMode=ai-sdk-4&lt;/code&gt; is required&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stream=false&lt;/code&gt; returns JSON instead of SSE&lt;/li&gt;
&lt;li&gt;Response format is &lt;code&gt;{id, role, content}&lt;/code&gt;, not OpenAI-style&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;step-4-the-chat-widget&quot;&gt;Step 4: The Chat Widget&lt;/h4&gt;
&lt;p&gt;A lightweight vanilla JS widget that integrates with Hugo:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&quot;[https://cdn.jsdelivr.net/npm/algoliasearch@4/dist/algoliasearch-lite.umd.js](https://cdn.jsdelivr.net/npm/algoliasearch@4/dist/algoliasearch-lite.umd.js)&quot;&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;script&amp;gt;
async function sendMessage() {
    const query = input.value.trim();
    
    try {
        // Try Agent Studio via Netlify proxy
        const response = await fetch('/.netlify/functions/agent-chat', {
            method: 'POST',
            body: JSON.stringify({ query, conversationId, language })
        });
        
        const data = await response.json();
        addMessage(data.answer, 'agent');
        
    } catch (error) {
        // Fallback to direct Algolia search
        await fallbackSearch(query);
    }
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dark mode support&lt;/li&gt;
&lt;li&gt;Mobile responsive&lt;/li&gt;
&lt;li&gt;Bilingual (ES/EN)&lt;/li&gt;
&lt;li&gt;⚡ Graceful fallback to search if Agent fails&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;technical-challenges&quot;&gt;Technical Challenges&lt;/h3&gt;
&lt;h4 id=&quot;challenge-1-the-422-validation-error&quot;&gt;Challenge 1: The 422 Validation Error&lt;/h4&gt;
&lt;p&gt;My first API calls returned &lt;code&gt;422: Field required: compatibilityMode&lt;/code&gt;. The documentation mentioned it, but I missed it initially.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Added query parameters &lt;code&gt;?compatibilityMode=ai-sdk-4&amp;amp;stream=false&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&quot;challenge-2-streaming-response-format&quot;&gt;Challenge 2: Streaming Response Format&lt;/h4&gt;
&lt;p&gt;Even after fixing the 422 error, I got &lt;code&gt;SyntaxError: Unexpected token 'f:'&lt;/code&gt;. The API was returning SSE format (&lt;code&gt;f:{&quot;message&quot;...&lt;/code&gt;) instead of JSON.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Added &lt;code&gt;stream=false&lt;/code&gt; to disable streaming.&lt;/p&gt;
&lt;h4 id=&quot;challenge-3-response-parsing&quot;&gt;Challenge 3: Response Parsing&lt;/h4&gt;
&lt;p&gt;The response structure wasn't OpenAI-compatible. I expected &lt;code&gt;choices[0].message.content&lt;/code&gt; but got &lt;code&gt;{id, role, content}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Updated parsing logic to check &lt;code&gt;data.content&lt;/code&gt; first.&lt;/p&gt;
&lt;h3 id=&quot;results&quot;&gt;Results&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Metric &lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Posts Indexed &lt;/td&gt;&lt;td&gt;72 (36 EN + 36 ES)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Avg Response Time &lt;/td&gt;&lt;td&gt;~3-4 seconds&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fallback Coverage &lt;/td&gt;&lt;td&gt;100% (graceful degradation)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Languages Supported &lt;/td&gt;&lt;td&gt;English &amp;amp; Spanish&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Deployment &lt;/td&gt;&lt;td&gt;Fully automated via Netlify&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;source-code&quot;&gt;Source Code&lt;/h3&gt;
&lt;p&gt;The complete implementation is open source:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Indexing Script:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Dalaez/datalaria-website/blob/main/scripts/algolia_sync.py&quot;&gt;algolia_sync.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Netlify Function:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Dalaez/datalaria-website/blob/main/netlify/functions/agent-chat.js&quot;&gt;agent-chat.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chat Widget:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Dalaez/datalaria-website/blob/main/layouts/partials/algolia-agent.html&quot;&gt;algolia-agent.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;what-s-next&quot;&gt;What's Next&lt;/h3&gt;
&lt;p&gt;This is Part 8 of my Autopilot series. The system now has:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ &lt;strong&gt;Content Generation&lt;/strong&gt; (Parts 1-3): AI creates social posts&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Distribution&lt;/strong&gt; (Parts 4-5): Automated publishing to Twitter/LinkedIn&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Newsletter&lt;/strong&gt; (Part 6): Automated email campaigns&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Quality Control&lt;/strong&gt; (Part 7): Copilot CLI auditing&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Consumer Search&lt;/strong&gt; (Part 8): AI-powered Q&amp;amp;A - &lt;em&gt;You are here&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-fast-retrieval-matters&quot;&gt;Why Fast Retrieval Matters&lt;/h3&gt;
&lt;p&gt;In Operations Engineering, decisions happen fast. When a supply chain analyst asks &quot;What's the best practice for detecting demand outliers?&quot;, they need answers in seconds, not minutes of browsing through archives.&lt;/p&gt;
&lt;p&gt;Algolia's millisecond retrieval makes this possible:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Scenario &lt;/th&gt;&lt;th&gt;Without Fast Retrieval &lt;/th&gt;&lt;th&gt;With Algolia&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Find relevant article &lt;/td&gt;&lt;td&gt;~30-60s browsing &lt;/td&gt;&lt;td&gt;~100ms retrieval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Get synthesized answer &lt;/td&gt;&lt;td&gt;Manual reading required &lt;/td&gt;&lt;td&gt;~3-4s AI response&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Context accuracy &lt;/td&gt;&lt;td&gt;May miss related content &lt;/td&gt;&lt;td&gt;RAG ensures completeness&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The RAG pipeline works because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fast vector search&lt;/strong&gt; finds semantically similar content across 72 posts instantly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faceted filtering&lt;/strong&gt; narrows results by language and domain (S&amp;amp;OP, Projects, Product, People)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Snippet extraction&lt;/strong&gt; provides the LLM with precisely relevant context&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Without sub-second retrieval, the conversation would feel sluggish and users would abandon the chat. Algolia enables the &quot;instant&quot; in instant knowledge discovery – transforming a static blog into an interactive, AI-powered knowledge base.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Algolia Agent Studio transforms a static blog into an interactive knowledge base. Instead of forcing visitors to search and read multiple posts, they can simply &lt;em&gt;ask&lt;/em&gt; and get synthesized answers.&lt;/p&gt;
&lt;p&gt;The combination of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Algolia Search&lt;/strong&gt; (fast, relevant retrieval)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent Studio&lt;/strong&gt; (RAG orchestration)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Gemini&lt;/strong&gt; (natural language generation)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Netlify Functions&lt;/strong&gt; (serverless proxy)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;...creates a powerful, cost-effective AI assistant that runs 24/7.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Questions?&lt;/strong&gt; Try the live demo at &lt;a rel=&quot;nofollow&quot; href=&quot;https://datalaria.com&quot;&gt;datalaria.com&lt;/a&gt; or drop a comment below &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This post is part of the &lt;a rel=&quot;nofollow&quot; href=&quot;https://datalaria.com/en/tags/agents/&quot;&gt;Autopilot Project&lt;/a&gt; series, where I document how I automate content creation, distribution, and now consumer interaction using AI.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/12341/autopilot-assistant-building-an-ai-copilot-with-algolia-agent-studio</guid>
<pubDate>Tue, 03 Mar 2026 07:05:29 +0000</pubDate>
</item>
</channel>
</rss>