DOTNET EXPERT BLOG

Connect AI to a Database in C# with ASP.NET Core and EF Core | Day 8

10/3/2026 4:02:43 AM Noor All Safaet Loading... 0


Our AI assistant can now call approved C# functions.

In Day 7, we created a get_product_stock tool that allowed the model to request current inventory information.

But our inventory data came from an in-memory list:

private static readonly List<ProductStockInfo>
    Products = [...];

That was useful for learning function calling. Real applications, however, usually store inventory, orders, customers, invoices, and other business data in a database.

Today we will replace that temporary data source with Entity Framework Core.

Our new flow will be:

User
 ↓
AI Model
 ↓
get_product_stock
 ↓
C# Tool
 ↓
Inventory Service
 ↓
Entity Framework Core
 ↓
Database
 ↓
Verified Data
 ↓
AI Model
 ↓
Final Answer

The most important part is the boundary.

The AI model will not receive a database connection. It will not generate arbitrary SQL that our application blindly executes. It will not receive unrestricted access to database tables.

Instead, the model requests a narrow application capability, while normal C# code controls how data is retrieved.

In this tutorial, we will cover:

  • adding Entity Framework Core

  • creating a Product entity

  • configuring AppDbContext

  • creating and applying a migration

  • replacing in-memory inventory data

  • querying asynchronously with EF Core

  • using AsNoTracking() for read-only queries

  • protecting tenant boundaries

  • limiting data exposed to AI

  • avoiding AI-generated SQL

  • keeping deterministic business rules in C#

  • designing database-backed AI tools for production

By the end, our C# AI assistant will answer inventory questions using data retrieved from a real database.

Previously in This Series

We are building one practical AI application with C# and ASP.NET Core.

Day 1: Build Your First AI Application with C# and .NET
Connected C# to an AI model.

Day 2: Understanding IChatClient
Introduced a provider-independent AI abstraction.

Day 3: Build a Reusable AI Chat Service
Moved AI integration into an ASP.NET Core architecture.

Day 4: Add Conversation Memory
Added multi-turn conversation history.

Day 5: Stream AI Responses
Streamed generated responses to the client.

Day 6: Structured AI Output
Converted AI responses into strongly typed C# results.

Day 7: Function Calling
Allowed the model to request approved C# application capabilities.

Today we keep the Day 7 tool architecture but replace:

In-Memory List

with:

Entity Framework Core
        ↓
Database

So Day 8 focuses on real data access rather than repeating function-calling concepts.

What Are We Building?

Imagine our database contains:

ProductCurrent StockReorder Level
Wireless Mouse410
Mechanical Keyboard188
USB-C Hub05

The user asks:

How many Wireless Mouse units do we currently have?

The model should not know the answer in advance.

Instead:

AI
 ↓
get_product_stock
 ↓
InventoryService
 ↓
AppDbContext
 ↓
Products Table

EF Core retrieves:

ProductName = Wireless Mouse
CurrentStock = 4
ReorderLevel = 10

The model can then answer:

Wireless Mouse currently has 4 units in stock.
Its reorder level is 10, so stock is below
the configured threshold.

The model provides the language.

The database provides the facts.

Why Keep the Database Behind Application Services?

A direct architecture such as:

AI
 ↓
Database

gives the model too much responsibility.

A better design is:

AI Model
    ↓
Approved Tool
    ↓
Application Service
    ↓
EF Core
    ↓
Database

Each layer has a clear responsibility.

AI Model understands the natural-language request.

Tool exposes a narrow capability.

Application Service controls the operation and applies application rules.

EF Core performs controlled data access.

Database remains the authoritative source of business data.

This separation also means the database implementation can change without redesigning the AI layer.

Step 1: Install Entity Framework Core

If your application does not already use EF Core, install the required packages.

For SQL Server:

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design

For PostgreSQL:

dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL

The application architecture remains the same. Only provider-specific configuration changes.

The main examples below use SQL Server.

Step 2: Create the Product Entity

Create:

Entities/Product.cs

Add:

using System.ComponentModel.DataAnnotations;

public sealed class Product
{
    public int Id { get; set; }

    [Required]
    [MaxLength(200)]
    public string Name { get; set; }
        = string.Empty;

    [MaxLength(100)]
    public string? Sku { get; set; }

    public int CurrentStock { get; set; }

    public int ReorderLevel { get; set; }

    public bool IsActive { get; set; } = true;
}

This is a persistence entity.

It represents application data rather than an AI-specific model. That separation is useful because your database design should not depend on what one AI prompt happens to need.

Step 3: Create AppDbContext

Create:

Data/AppDbContext.cs

Add:

using Microsoft.EntityFrameworkCore;

public sealed class AppDbContext
    : DbContext
{
    public AppDbContext(
        DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }

    public DbSet<Product> Products =>
        Set<Product>();

    protected override void OnModelCreating(
        ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Product>(
            entity =>
            {
                entity.HasKey(x => x.Id);

                entity.Property(x => x.Name)
                    .HasMaxLength(200)
                    .IsRequired();

                entity.Property(x => x.Sku)
                    .HasMaxLength(100);

                entity.HasIndex(x => x.Name);
                entity.HasIndex(x => x.Sku);
            });
    }
}

Our AI layer does not need to know how this context is configured.

It only communicates through the application service.

Step 4: Configure the Database

Add a connection string to appsettings.json.

For example:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=YOUR_SERVER;Database=CSharpAiDb;Trusted_Connection=True;TrustServerCertificate=True"
  }
}

Do not commit production database credentials to source control.

Production secrets should come from an appropriate secure configuration mechanism for your hosting environment.

Now register EF Core in Program.cs:

using Microsoft.EntityFrameworkCore;

string connectionString =
    builder.Configuration
        .GetConnectionString(
            "DefaultConnection")
    ?? throw new InvalidOperationException(
        "Connection string 'DefaultConnection' was not found.");

builder.Services.AddDbContext<AppDbContext>(
    options =>
        options.UseSqlServer(
            connectionString));

For PostgreSQL:

builder.Services.AddDbContext<AppDbContext>(
    options =>
        options.UseNpgsql(
            connectionString));

In a typical ASP.NET Core request, a scoped AppDbContext works naturally with our current flow:

HTTP Request
     ↓
Inventory Assistant
     ↓
Inventory Tool
     ↓
Inventory Service
     ↓
AppDbContext

Do not store a DbContext in a static field or treat it as a permanent application-wide connection.

Step 5: Create the Database Migration

If the EF Core command-line tool is not installed:

dotnet tool install --global dotnet-ef

Create the migration:

dotnet ef migrations add CreateProducts

Apply it:

dotnet ef database update

EF Core will create the database schema represented by the model and migration.

Step 6: Add Sample Data

For development, add a few products so we can test the assistant.

For example:

if (app.Environment.IsDevelopment())
{
    using IServiceScope scope =
        app.Services.CreateScope();

    AppDbContext dbContext =
        scope.ServiceProvider
            .GetRequiredService<AppDbContext>();

    await dbContext.Database.MigrateAsync();

    if (!await dbContext.Products.AnyAsync())
    {
        dbContext.Products.AddRange(
            new Product
            {
                Name = "Wireless Mouse",
                Sku = "WM-001",
                CurrentStock = 4,
                ReorderLevel = 10
            },
            new Product
            {
                Name = "Mechanical Keyboard",
                Sku = "MK-001",
                CurrentStock = 18,
                ReorderLevel = 8
            },
            new Product
            {
                Name = "USB-C Hub",
                Sku = "UCH-001",
                CurrentStock = 0,
                ReorderLevel = 5
            });

        await dbContext.SaveChangesAsync();
    }
}

This is development sample data.

Production migrations and seeding should follow your application's deployment strategy rather than automatically copying this startup approach.

Step 7: Replace the In-Memory Inventory Service

Our existing interface can remain unchanged:

public interface IInventoryService
{
    Task<ProductStockInfo?>
        GetProductStockAsync(
            string productName,
            CancellationToken cancellationToken = default);
}

Now replace the Day 7 in-memory implementation:

using Microsoft.EntityFrameworkCore;

public sealed class InventoryService
    : IInventoryService
{
    private readonly AppDbContext _dbContext;

    public InventoryService(
        AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<ProductStockInfo?>
        GetProductStockAsync(
            string productName,
            CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(productName))
        {
            return null;
        }

        string normalizedName =
            productName.Trim();

        return await _dbContext.Products
            .AsNoTracking()
            .Where(x =>
                x.IsActive &&
                x.Name == normalizedName)
            .Select(x =>
                new ProductStockInfo
                {
                    ProductName = x.Name,
                    CurrentStock = x.CurrentStock,
                    ReorderLevel = x.ReorderLevel
                })
            .FirstOrDefaultAsync(
                cancellationToken);
    }
}

This is the main change in Day 8.

Before:

InventoryService
      ↓
List<ProductStockInfo>

Now:

InventoryService
      ↓
AppDbContext
      ↓
Database

The AI-facing architecture did not need to change.

Why Use AsNoTracking()?

Our stock lookup only reads data.

It does not update the returned product.

Therefore:

.AsNoTracking()

is appropriate for this query.

We also project directly into:

ProductStockInfo

instead of loading the entire database entity.

Our tool only receives:

ProductName
CurrentStock
ReorderLevel

It does not receive unrelated fields simply because they exist in the database.

A useful rule is:

Retrieve and expose only the data required for the tool's job.

This keeps queries focused and reduces unnecessary data exposure.

Step 8: Keep the Existing AI Tool

The Day 7 tool can continue using IInventoryService:

using System.ComponentModel;

public sealed class InventoryTools
{
    private readonly IInventoryService
        _inventoryService;

    public InventoryTools(
        IInventoryService inventoryService)
    {
        _inventoryService =
            inventoryService;
    }

    [Description(
        "Gets the current stock and reorder level for a product.")]
    public async Task<ProductStockInfo?>
        GetProductStockAsync(
            [Description(
                "The exact product name to look up.")]
            string productName,
            CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(productName))
        {
            throw new ArgumentException(
                "Product name is required.",
                nameof(productName));
        }

        if (productName.Length > 200)
        {
            throw new ArgumentException(
                "Product name is too long.",
                nameof(productName));
        }

        return await _inventoryService
            .GetProductStockAsync(
                productName.Trim(),
                cancellationToken);
    }
}

The tool does not care whether inventory comes from:

Memory
SQL Server
PostgreSQL
API
ERP

It depends on:

IInventoryService

That is the boundary we want.

Step 9: Keep the Function Registration

The function created in Day 7 can also remain:

AIFunction getProductStock =
    AIFunctionFactory.Create(
        _inventoryTools.GetProductStockAsync,
        name: "get_product_stock",
        description:
            "Gets current stock and reorder level for a product.");

And:

var options = new ChatOptions
{
    Tools =
    [
        getProductStock
    ]
};

The model sees:

get_product_stock

It does not need to see:

Products table
DbContext
SQL query
Connection string
Database password

Database implementation details stay inside the application.

Step 10: Test the Database-Backed Assistant

Send:

POST /api/inventory-assistant/ask
Content-Type: application/json

Request:

{
  "message": "How many Wireless Mouse units do we currently have?"
}

The runtime flow is now:

HTTP Request
     ↓
InventoryAssistantService
     ↓
IChatClient
     ↓
AI requests get_product_stock
     ↓
InventoryTools
     ↓
IInventoryService
     ↓
InventoryService
     ↓
AppDbContext
     ↓
Database
     ↓
Tool Result
     ↓
AI
     ↓
Final Response

A possible response:

{
  "answer": "Wireless Mouse currently has 4 units in stock. Its reorder level is 10, so it is below the configured reorder threshold."
}

The wording is generated by the model.

The stock values come from the database.

During development, verify the underlying database record as well. This helps distinguish database problems from tool or AI-response problems.

Handle Products That Do Not Exist

Suppose the user asks:

What is the stock of Gaming Coffee Machine?

Our current query returns null if no matching product exists.

For production applications, an explicit lookup result can make this clearer:

public sealed class ProductStockLookupResult
{
    public bool Found { get; init; }

    public string? ProductName { get; init; }

    public int? CurrentStock { get; init; }

    public int? ReorderLevel { get; init; }
}

A missing product could then return:

{
  "found": false,
  "productName": null,
  "currentStock": null,
  "reorderLevel": null
}

This is easier for both application code and the model to interpret than an ambiguous result.

Use Stable Identifiers as the Application Grows

Our tutorial uses an exact product name because it is easy to understand.

Real users may type:

mouse
wireless mice
WirelessMouse
the wireless mouse

Rather than giving the model flexible database access, a larger application can expose a separate capability:

search_products

That tool might return:

[
  {
    "id": 17,
    "name": "Wireless Mouse",
    "sku": "WM-001"
  }
]

Then the workflow becomes:

User's Product Name
        ↓
search_products
        ↓
ProductId = 17
        ↓
get_product_stock
        ↓
Verified Stock

Stable identifiers such as ProductId, SKU, or WarehouseId reduce ambiguity in business operations.

Do Not Let the AI Generate Arbitrary SQL

Avoid exposing a generic capability such as:

ExecuteSqlAsync(
    string sql)

The safer architecture is:

Natural-Language Request
        ↓
AI Selects Approved Tool
        ↓
Application Chooses Query
        ↓
EF Core Executes Controlled LINQ

The model can decide:

I need current product stock.

The application decides exactly how that information is retrieved.

EF Core query parameterization is useful, but SQL injection is not the only security concern.

Even a technically safe query must still answer:

Is the user authenticated?
Is the user authorized?
Does this record belong to the user's tenant?
Should this field be exposed?

Application authorization remains essential.

Protect Tenant Boundaries

Multi-tenant applications need an additional boundary.

Suppose Product includes:

public int TenantId { get; set; }

A tenant-aware query might include:

return await _dbContext.Products
    .AsNoTracking()
    .Where(x =>
        x.TenantId == tenantId &&
        x.IsActive &&
        x.Name == normalizedName)
    .Select(x =>
        new ProductStockInfo
        {
            ProductName = x.Name,
            CurrentStock = x.CurrentStock,
            ReorderLevel = x.ReorderLevel
        })
    .FirstOrDefaultAsync(
        cancellationToken);

But tenantId should come from trusted application context—not from a model-generated argument.

The secure flow is:

Authenticated Request
      ↓
Trusted Tenant Context
      ↓
InventoryService
      ↓
Tenant-Filtered Query

The AI can choose which approved capability it needs.

It should not decide which tenant it belongs to.

Keep Deterministic Business Rules in C#

Suppose the business rule is:

CurrentStock <= 0
    → OutOfStock

CurrentStock <= ReorderLevel
    → LowStock

Otherwise
    → Healthy

We do not need AI to calculate this.

C# can do it reliably:

public static string GetStockStatus(
    int currentStock,
    int reorderLevel)
{
    if (currentStock <= 0)
    {
        return "OutOfStock";
    }

    if (currentStock <= reorderLevel)
    {
        return "LowStock";
    }

    return "Healthy";
}

The application can provide:

{
  "productName": "Wireless Mouse",
  "currentStock": 4,
  "reorderLevel": 10,
  "stockStatus": "LowStock"
}

Then the model explains verified business state instead of inventing the rule.

A useful architecture is:

Database
   ↓
Deterministic C# Logic
   ↓
Verified Facts
   ↓
AI Explanation

Use AI where language understanding or flexible reasoning adds value.

Keep deterministic application rules in C#.

Propagate Cancellation

Our database method accepts:

CancellationToken cancellationToken

and passes it to:

FirstOrDefaultAsync(
    cancellationToken)

That allows cancellation to flow through the application:

HTTP Request
     ↓
AI Service
     ↓
Tool
     ↓
Inventory Service
     ↓
EF Core

If the request is cancelled, unnecessary work can stop sooner.

When You May Need IDbContextFactory

Our current HTTP request works naturally with a scoped AppDbContext.

Some applications have different lifetime requirements, including:

  • Blazor Server

  • background processing

  • long-running workflows

  • multiple independent units of work

For these scenarios, IDbContextFactory<AppDbContext> may be useful.

For example:

builder.Services
    .AddDbContextFactory<AppDbContext>(
        options =>
            options.UseSqlServer(
                connectionString));

Then a service can create a context for a specific operation:

await using AppDbContext dbContext =
    await _dbContextFactory
        .CreateDbContextAsync(
            cancellationToken);

Do not switch to a factory automatically.

Choose the context strategy that matches the application's lifetime and unit-of-work requirements.

Also avoid concurrent operations on the same DbContext instance.

Design Tools with Database Performance in Mind

Adding AI does not remove normal database engineering concerns.

Consider:

  • indexes

  • projections

  • bounded result sizes

  • query frequency

  • database round trips

  • pagination

  • cancellation

  • caching where appropriate

Tool design also affects query performance.

Suppose the user asks:

Show stock for 50 products.

Calling:

get_product_stock

50 times may create unnecessary database round trips.

A bounded batch capability can be better:

Task<IReadOnlyList<ProductStockInfo>>
    GetProductStocksAsync(
        IReadOnlyCollection<int> productIds,
        CancellationToken cancellationToken);

Similarly, a search tool should not return an unlimited product table.

For example:

return await _dbContext.Products
    .AsNoTracking()
    .Where(x =>
        x.IsActive &&
        x.Name.Contains(searchTerm))
    .OrderBy(x => x.Name)
    .Take(20)
    .Select(x =>
        new ProductSearchResult
        {
            Id = x.Id,
            Name = x.Name,
            Sku = x.Sku
        })
    .ToListAsync(
        cancellationToken);

The application decides:

Take(20)

not the model.

Do Not Expose Unnecessary Data

A database entity may contain much more information than an AI tool needs.

For example:

Name
Stock
Supplier Cost
Margin
Internal Notes
Audit Fields
Security Metadata

If the tool only needs:

Name
CurrentStock
ReorderLevel

project only those fields.

Do not rely on a prompt saying:

Don't mention private fields.

A stronger approach is not to expose those fields to the model at all.

Handle Database Errors Safely

If the database becomes unavailable, avoid exposing raw infrastructure errors such as:

Server=...
Database=...
Login failed...

to the model or end user.

Return controlled application behavior such as:

Inventory information is temporarily unavailable.
Please try again later.

Technical details can be logged internally according to your logging and privacy policy.

Useful operational information may include:

Tool Name
Duration
Success / Failure
Conversation ID
Tenant ID

Avoid unnecessarily logging sensitive prompts, full tool results, credentials, or connection strings.

Repository Pattern Is Optional

Some applications use:

Tool
 ↓
Application Service
 ↓
Repository
 ↓
EF Core

Others use:

Tool
 ↓
Application Service
 ↓
EF Core

Both can be reasonable.

A repository should provide a meaningful persistence boundary, not exist simply because AI has been added.

The AI integration should fit the architecture of your application rather than forcing every project into the same pattern.

Updated Project Structure

After Day 8, our project may look like:

CSharpAiApi
│
├── AI
│   └── InventoryTools.cs
│
├── Controllers
│   ├── AiController.cs
│   ├── InventoryAiController.cs
│   └── InventoryAssistantController.cs
│
├── Data
│   └── AppDbContext.cs
│
├── Entities
│   └── Product.cs
│
├── Migrations
│   └── ...
│
├── Models
│   ├── AiChatResult.cs
│   ├── AiConversation.cs
│   ├── AiStreamContext.cs
│   ├── ChatRequest.cs
│   ├── InventoryAnalysisResult.cs
│   ├── InventoryAssistantRequest.cs
│   └── ProductStockInfo.cs
│
├── Services
│   ├── IAiChatService.cs
│   ├── AiChatService.cs
│   ├── IConversationStore.cs
│   ├── InMemoryConversationStore.cs
│   ├── IInventoryAssistantService.cs
│   ├── InventoryAssistantService.cs
│   ├── IInventoryService.cs
│   └── InventoryService.cs
│
├── Program.cs
└── appsettings.json

The important path is:

Natural Language
      ↓
AI
      ↓
Approved Function
      ↓
Application Service
      ↓
Controlled EF Core Query
      ↓
Database

Common Problems When Connecting AI to EF Core

Unable to Resolve AppDbContext

Check that it is registered:

builder.Services.AddDbContext<AppDbContext>(
    options =>
        options.UseSqlServer(
            connectionString));

Also verify the consuming service registration.

No Database Provider Has Been Configured

Make sure the appropriate provider package is installed and configuration contains:

options.UseSqlServer(...)

or:

options.UseNpgsql(...)

The Table Does Not Exist

Check that migrations were created and applied:

dotnet ef migrations add CreateProducts
dotnet ef database update

The AI Returns Incorrect Stock

Trace the data through each layer:

Database Record
      ↓
EF Core Query
      ↓
Projection
      ↓
Tool Result
      ↓
AI Response

This helps identify whether the problem is data access, tool behavior, or model interpretation.

Concurrent DbContext Errors

Do not run overlapping operations on the same context instance.

Await EF Core operations properly and use a context strategy appropriate for the application's unit of work.

Sensitive Fields Reach the Model

Fix the projection.

Do not depend only on prompt instructions to hide data that should never have been provided to the model.

Production Checklist

Before allowing AI tools to query production data, verify:

  • authentication is enforced

  • authorization happens in application code

  • tenant filtering is enforced

  • tenant identity comes from trusted context

  • database credentials are never exposed to the model

  • arbitrary SQL execution is unavailable

  • tools expose narrow business capabilities

  • queries return only required fields

  • read-only queries use an appropriate tracking strategy

  • result sizes are bounded

  • cancellation is propagated

  • DbContext is not used concurrently

  • sensitive fields are excluded

  • infrastructure errors are sanitized

  • logging follows privacy requirements

  • important query patterns are efficient

  • deterministic business rules remain in C#

  • write operations have stronger controls

What We Built Today

At the end of Day 7:

User
 ↓
AI
 ↓
C# Tool
 ↓
In-Memory Data

After Day 8:

User
 ↓
AI
 ↓
Approved C# Tool
 ↓
Application Service
 ↓
Entity Framework Core
 ↓
Database
 ↓
Verified Business Data
 ↓
AI
 ↓
Final Answer

The important change is not simply that we added a database.

We preserved the application boundary.

The model sees a capability such as:

get_product_stock

It does not need to know the table name, SQL query, connection string, database password, or database server.

That separation improves security, testing, maintainability, and flexibility.

Day 8 Checklist

Before moving to Day 9, make sure you understand:

  • how EF Core fits behind an AI tool

  • how to create and register AppDbContext

  • how to create and apply migrations

  • why application services should control database access

  • why AsNoTracking() is useful for read-only queries

  • why tool results should use focused projections

  • why AI should not execute arbitrary SQL

  • why authorization is separate from SQL injection protection

  • why tenant identity must come from trusted context

  • why deterministic business rules belong in C#

  • why one DbContext should not be used concurrently

  • when IDbContextFactory<TContext> may be useful

  • why AI tool design affects database performance

  • why result sizes should be bounded

  • why unnecessary database fields should stay outside the AI context

What's Next: Day 9

Our AI assistant can now retrieve structured business data from a database.

But important business knowledge also lives in:

PDF files
Documentation
Policies
Manuals
Knowledge-base articles
Internal documents

Users may ask:

What is our return policy?

What does the employee handbook say about annual leave?

Which part of the manual explains calibration?

Sending every document to the model for every question would be inefficient.

In Day 9: Build a RAG System in C# with .NET, we will introduce Retrieval-Augmented Generation.

The architecture will evolve into:

User Question
      ↓
Retrieve Relevant Knowledge
      ↓
Selected Context
      ↓
AI Model
      ↓
Grounded Answer

We will focus on how RAG complements the structured database tools we built today without repeating the database or function-calling concepts.

Final Thoughts

Connecting AI to a database should not mean:

Give AI unrestricted SQL access.

A stronger approach is:

Give AI carefully designed
application capabilities.

The model understands the user's natural-language request.

The tool exposes an approved capability.

The C# application controls how that capability works.

EF Core performs the database query.

The database remains the authoritative source of business facts.

The model then communicates those verified facts naturally.

Our final boundary is:

AI Understanding
      ↓
Controlled Tool
      ↓
Application Logic
      ↓
EF Core
      ↓
Database Facts

The model should not become your database layer.

It should work through your application layer.

Day 8 connected our AI assistant to real business data.

Day 9 will connect it to a different type of information: unstructured knowledge through Retrieval-Augmented Generation (RAG).

Comments 0