AI Hero

repository·main·Indexed 23 days ago

https://github.com/ai-hero-dev/ai-hero

An educational resource and codebase designed to transition developers from traditional software engineering to AI engineering. It provides runnable examples, exercises, and libraries focusing on concepts such as connecting Vercel's AI SDK to various models, implementing text streaming, managing conversation history with CoreMessage arrays, and generating structured outputs using Zod and OpenAI-compatible APIs.

Tokens
107.1K
Snippets
232
Records
420
Agent score
82%

What's inside ai-hero

  1. What is Retrieval Augmented Generation (RAG)?

    main

    Retrieval Augmented Generation (RAG) is a technique used to provide Large Language Models (LLMs) with specific, relevant context that was not part of their original training dataset.

    Instead of feeding an entire massive document into a prompt (which is token-intensive and may exceed context windows), RAG follows a three-step workflow:

    1. Question: The user submits a query.
    2. Document Search: The system searches a data source for information relevant to that query.
    3. Relevant Sections: Only the specific, retrieved sections are injected into the prompt.
    4. LLM Generation: The LLM uses the retrieved context to generate an accurate answer.

    Why use RAG?

    • Access Private Data: Allows LLMs to answer questions about internal company documents or non-public information.
    • Specialist Knowledge: Provides context for niche or highly specific topics not prevalent in general training data.
    • Grounding & Hallucination Reduction: By forcing the LLM to focus on retrieved facts rather than its internal weights, you reduce the likelihood of hallucinations.
  2. What is the Model Context Protocol (MCP)?

    main

    The Model Context Protocol (MCP) is an open standard that acts as a standardized interface layer between Large Language Models (LLMs) and external data sources or APIs (like Slack, GitHub, or local filesystems).

    Instead of writing custom 'glue code' for every unique API to connect it to an LLM, developers can use MCP to create a reusable connection. An MCP server contains the logic to interact with specific APIs and exposes a set of 'tools' in an LLM-friendly format. This allows any MCP-compliant client (such as Cursor, Windsurf, or Claude Code) to interact with those tools without needing to know the underlying API implementation.

  3. Implement 'Hard' Human In The Loop for destructive actions

    main

    A 'Hard' Human In The Loop implementation is used to prevent an LLM from performing irreversible or destructive actions (like deleting files or sending emails) without explicit authorization.

    Instead of allowing the model to execute a tool call directly, the workflow should:

    1. Intercept the tool call request.
    2. Wait for user feedback/confirmation.
    3. Execute the tool only after permission is granted.
    4. Feed the tool's result back to the LLM.

    This pattern ensures the probabilistic nature of the LLM cannot result in unintended side effects.

  4. Define the Action schema for LLM decision making

    main

    When building an agentic loop, you need to define a schema that allows an LLM to choose between different tasks. While z.union is a common approach in Zod, it translates to a JSON Schema oneOf structure which can be difficult for LLMs to parse reliably.

    Instead, use a single z.object with a type field defined as an enum, and make the specific payload fields (like query or urls) .optional(). This approach is more robust for Structured Outputs.

    import { z } from "zod";
    
    export const actionSchema = z.object({
      type:
        z
          .enum(["search", "scrape", "answer"])
          .describe(
            `The type of action to take.
          - 'search': Search the web for more information.
          - 'scrape': Scrape a URL.
          - 'answer': Answer the user's question and complete the loop.`,
          ),
      query:
        z
          .string()
          .describe(
            "The query to search for. Required if type is 'search'.",
          )
          .optional(),
      urls:
        z
          .array(z.string())
          .describe(
            "The URLs to scrape. Required if type is 'scrape'.",
          )
          .optional(),
    });
  5. Choose a model for LLM-as-a-judge

    main

    When selecting a model to act as a judge, consider two primary strategies:

    1. High-capability models: Use powerful models (e.g., reasoning models) if the task is complex and requires high precision. This follows the "go big or go home" approach.
    2. Efficient models: Use small to mid-range models (e.g., gemini-1.5-flash) if the task is primarily classification. This is faster and more cost-effective.

    Example using Google's Gemini via the AI SDK:

    import { google } from "@ai-sdk/google";
    
    export const factualityModel = google(
      "gemini-1.5-flash",
    );
  6. Use XML Tags for input and output delimitation

    main

    XML tags are used to provide clear delimiters within prompts, helping the LLM distinguish between different types of information or instructions.

    XML Tags on Input

    Use tags to wrap data, instructions, or examples to prevent the model from confusing them. For example, wrapping spreadsheet data in <data> tags and instructions in <instructions> tags.

    XML Tags on Output

    Instruct the LLM to wrap specific parts of its response in XML tags. This allows you to programmatically parse multiple distinct outputs (like a <summary>, <critique>, and <suggested-improvements>) from a single response.

    Review the article below and provide a summary, critique, and suggested improvements.
    
    Wrap the summary in <summary> tags.
    Wrap the critique in <critique> tags.
    Wrap the suggested improvements in <suggested-improvements> tags.
    
    <article>
    {{ARTICLE}}
    </article>
  7. Implement global rate limiting using Redis

    main

    When deploying to serverless platforms (like Next.js on Vercel), in-memory tracking (e.g., using a Map) fails because multiple Node.js processes are spun up. Use a centralized Redis instance to sync rate limits across all processes.

    This is achieved by using a Redis key based on the current time window (e.g., prefix:windowStart) and utilizing Redis pipelines to atomically increment the counter (INCR) and set an expiration (EXPIRE) for the window.

  8. Define RateLimitConfig for window-based limiting

    main

    To implement rate limiting, define a configuration object that specifies the maximum number of requests allowed within a specific time window (in milliseconds).

    interface RateLimitConfig {
      // Maximum number of requests
      maxRequests: number;
      // Time window in milliseconds
      windowMs: number;
      keyPrefix?: string;
      // Maximum number of retries before failing
      maxRetries?: number;
    }
  9. Choose an observability platform for LLM applications

    main

    When building LLM applications, you need observability to monitor long conversational threads, tool calls, and database queries. This is critical for both local development and production to manage costs (LLM usage) and user experience.

    Key considerations for choosing a platform:

    • OpenTelemetry Support: Since Vercel's AI SDK supports OpenTelemetry, any platform capable of collecting OpenTelemetry data is a viable option.
    • Deployment Options: Consider if the tool can be run locally (e.g., via Docker) to avoid vendor lock-in.
    • Integration: Look for seamless integration with your existing stack (e.g., Vercel's AI SDK).
    • Cost: Evaluate free tiers for development and pricing models for production scaling.
  10. MCP vs. Tool Calling

    main

    While both involve LLMs invoking functions, they differ in architecture and decoupling:

    • Tool Calling: Typically occurs within the same process. The LLM and the tool executor reside within the same server or application.
    • Model Context Protocol (MCP): Decouples the client from the tools. The MCP server can run as a completely separate process, either locally or on a remote server. The MCP protocol facilitates communication between the MCP Client (containing the LLM) and the MCP Server (containing the Tool Executor).

    MCP Architecture Flow:

    1. MCP Client (LLM) describes a tool to be called via the MCP Protocol.
    2. The MCP Protocol sends the call to the MCP Server.
    3. The MCP Server's Tool Executor runs the tool and sends the result back through the MCP Protocol.
    4. The MCP Protocol returns the result to the MCP Client (LLM).
  11. Understand the concept of Evals for AI systems

    main
    In probabilistic AI systems (LLM-powered apps), traditional deterministic testing (input A always equals output B) is insufficient because LLM outputs are unpredictable. Evals (evaluations) act as the 'unit tests' for AI engineers. They provide a quantitative score to measure how changes to prompts, models, or system design affect performance, allowing you to determine if a change makes the system better or worse.