Hướng Dẫn Xây Dựng Tính Năng AI Đúng Cách Trong .NET

Trong bài viết trước, chúng ta tập trung vào nền tảng: LLM là gì (tokens, embeddings, transformer layers), và tại sao việc trừu tượng hóa nhà cung cấp quan trọng.

Trong bài viết này, chúng ta chuyển từ hiểu biết sang xây dựng. Ở đây, tôi sẽ chỉ cho bạn cách biến Microsoft.Extensions.AI thành một lớp sẵn sàng cho production, bao gồm các hợp đồng rõ ràng, pipeline middleware có thể kết hợp, caching, OpenTelemetry, function (tool) invocation, streaming, evaluation và các mẫu thực tế so với anti-patterns. Đến cuối bài, bạn có thể chuyển từ “tôi có thể gọi một model” sang “tôi có thể triển khai một bộ tính năng AI kiên cường, có thể quan sát được, tương thích tương lai” mà không ràng buộc mình với một SDK duy nhất.

Microsoft.Extensions.AI: Tổng Quan Kiến Trúc

Trước khi đi sâu vào các interface hoặc mẫu cụ thể, điều quan trọng là phải hiểu nơi Microsoft.Extensions.AI nằm trong stack AI .NET hiện đại.

Stack này được phân lớp theo trách nhiệm:

  • Ứng dụng .NET của bạn điều phối logic nghiệp vụ và tương tác người dùng.
  • Semantic Kernel tùy chọn cung cấp prompt orchestration, planning và vector memory, nhưng ủy quyền các thao tác ngôn ngữ mô hình thực tế cho lớp dưới.
  • Microsoft.Extensions.AI cung cấp các hợp đồng chính (IChatClient, IEmbeddingGenerator) và pipeline có thể kết hợp (middleware, DI, telemetry).
  • Provider SDKs triển khai các hợp đồng đó, bao bọc các API model cụ thể (OpenAI, Azure OpenAI, Ollama, v.v.).
  • Models là các LLM hoặc embedding models thực tế chạy trên cloud hoặc on-prem.

Kiến trúc này có nghĩa là tất cả mã tính năng AI trong ứng dụng của bạn có thể nhắm đến một tập hợp interface và mẫu duy nhất, thúc đẩy khả năng tương tác, kiểm thử và bảo trì trên cảnh quan AI đang phát triển nhanh chóng.

IChatClient Interface

Nền tảng của Microsoft.Extensions.AI là interface IChatClient, một sự trừu tượng hóa cho bất kỳ dịch vụ nào cung cấp khả năng chat completion từ cloud LLMs đến các model endpoint tự host. Bằng cách triển khai hợp đồng này, một provider cho phép tích hợp AI chat liền mạch, có thể kết hợp và an toàn kiểu trong ứng dụng .NET.

Các Phương Thức Chính

  • Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ...)
    Đồng bộ lấy phản hồi đầy đủ từ model cho một tập hợp tin nhắn chat.
  • IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ...)
    Tạo đầu ra tăng dần khi nó stream từ model – quan trọng cho UI thời gian thực, hội thoại.
  • object GetService(Type serviceType, object serviceKey)/T GetService<T>(object serviceKey)
    Cho phép truy xuất các dịch vụ “side-car” cụ thể của provider – ví dụ: metrics, extension APIs.

Giải Phẫu Một Cuộc Trò Chuyện

Một tương tác chat có cấu trúc đa phương thức. Mỗi tin nhắn là một ChatMessage với:

  • Role: (User, Assistant, Tool, v.v.)
  • Contents: một tập hợp AIContent đa hình (text, images, audio, function calls, v.v.)

Ví dụ (chat đa tin nhắn, đa phương thức):


var history = new List<ChatMessage><br>
{<br>
    new(ChatRole.User, new TextContent("Describe this image, please."))<br>
    {<br>
        Contents = { new ImageContent(new Uri("https://example.com/myImage.jpg")) }<br>
    },<br>
    new(ChatRole.Assistant, new TextContent("That looks like a mountain landscape."))<br>
};

Các Loại Nội Dung Được Hỗ Trợ (Hierarchy AIContent)

TextContent: Văn bản đơn giản
ImageContent: Hình ảnh qua URL hoặc byte[]
AudioContent: Tải audio
UsageContent: Báo cáo sử dụng token và chi phí
FunctionCallContent: Function calls được model gọi
FunctionResultContent: Kết quả trả về từ function calls

Sử dụng Contents để kích hoạt cuộc trò chuyện đa phương thức và có cấu trúc, bao gồm tool invocation và chaining. Điều này mở khóa nhiều tiềm năng hơn so với chat dựa trên văn bản thuần túy.

Ví Dụ Triển Khai


public sealed class SampleChatClient(Uri endpoint, string modelId) : IChatClient<br>
{<br>
    public ChatClientMetadata Metadata { get; } = new(...);<br><br>
    public async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ...)<br>
    {<br>
        var result = await CallYourModelAsync(messages);<br>
        return new ChatResponse(new ChatMessage(ChatRole.Assistant, result));<br>
    }<br><br>
    public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(<br>
        IEnumerable<ChatMessage> messages, ...)<br>
    {<br>
        foreach (var token in StreamFromModelAPI(messages))<br>
            yield return new ChatResponseUpdate(ChatRole.Assistant, token);<br>
    }<br><br>
    public object? GetService(Type serviceType, object? serviceKey) => this;<br>
}

Thread Safety và Best Practices

  • Thread-Safe: Tất cả triển khai IChatClient phải an toàn cho các thao tác đồng thời.
  • Options Mutation: Vì các đối số (như ChatOptions) có thể bị thay đổi, không bao giờ chia sẻ các instance options qua các lệnh gọi phương thức đồng thời.
  • Disposal: Không bao giờ dispose một IChatClient đang sử dụng (vì nó kế thừa IDisposable).

IEmbeddingGenerator Interface

Embeddings là biểu diễn vector của văn bản (hoặc nội dung khác), rất quan trọng cho semantic search, RAG, clustering và các ứng dụng khác. Trong Microsoft.Extensions.AI, interface IEmbeddingGenerator<TInput, TEmbedding> cung cấp một sự trừu tượng hóa mạnh mẽ cho các dịch vụ embedding.


public interface IEmbeddingGenerator<in TInput, TEmbedding> : IDisposable<br>
    where TEmbedding : Embedding<br>
{<br>
    Task<IEnumerable<TEmbedding>> GenerateAsync(<br>
        IEnumerable<TInput> texts,<br>
        EmbeddingGenerationOptions options,<br>
        CancellationToken cancellationToken = default<br>
    );<br>
}

Dependency Injection Patterns trong Microsoft.Extensions.AI

Giống như các thư viện khác trong họ Microsoft.Extensions, Microsoft.Extensions.AI được xây dựng cho tích hợp DI mạnh mẽ, phản ánh các mẫu từ ASP.NET Core và Entity Framework Core.

Đăng Ký Tiêu Chuẩn


services.AddOpenAIChatClient("gpt-4o", openAiApiKey);<br>
services.AddAzureOpenAIEmbeddingGenerator("text-embed", endpoint, azureKey);

Với việc đăng ký trong IServiceCollection, bạn có thể yêu cầu các instance được định kiểu ở bất kỳ đâu thông qua constructor injection:


public class MyService<br>
{<br>
    private readonly IChatClient _chat;<br><br>
    public MyService(IChatClient chat)<br>
    {<br>
        _chat = chat;<br>
    }<br>
}

Key Best Practices

  • Sử dụng DI ở mọi nơi: Tránh các factory tĩnh hoặc singleton bên ngoài ngữ cảnh mà DI cung cấp.
  • Testability: Bằng cách nhắm đến các abstraction, hoán đổi mocks hoặc triển khai provider thay thế cho integration testing.
  • Quản lý multi-client: Đăng ký nhiều client cho các provider hoặc model khác nhau dưới các key khác nhau để scaling mô-đun.

Middleware và Pipeline Customization

Composable Middleware: Mẫu UseXxx

Một trong những tiến bộ kiến trúc chính với Microsoft.Extensions.AI là giới thiệu các pipeline middleware cho các thao tác AI, lấy cảm hứng mạnh mẽ từ mô hình UseXxx() của ASP.NET Core.

Các Middleware Extension Phổ Biến

.UseFunctionInvocation(): Tool/function invocation tự động
.UseOpenTelemetry(…): Đính kèm tracing, metrics (OpenTelemetry)
.UseCaching(…): Response caching (in-memory hoặc distributed)
.UseCustomMiddleware(handler): Cắm logic pre/post-invocation tùy chỉnh


IChatClient client = new ChatClientBuilder(baseClient)<br>
    .UseFunctionInvocation()<br>
    .UseCaching()<br>
    .UseOpenTelemetry(loggerFactory, sourceName, configure: c => ...)<br>
    .Build();

Middleware có thể được sử dụng để tiêm các cross-cutting concerns (logging, caching, security, fallback handling) mà không làm nhiễm bẩn logic nghiệp vụ hoặc mã provider model. Giữ pipeline rõ ràng và dễ đọc để triển khai AI đáng tin cậy, có thể bảo trì.

Telemetry Integration với OpenTelemetry

Telemetry là không thể thiếu cho các AI workloads hiện đại. Không có nó, bạn phải đoán về hiệu suất, chi phí và các mẫu lỗi.

Microsoft.Extensions.AI hỗ trợ tích hợp hạng nhất với OpenTelemetry cho tracing, metrics và logging. Bạn instrument pipeline của mình với .UseOpenTelemetry(), và telemetry sẽ theo dõi các spans liên quan đến chat completions, function calls và các bước pipeline:


var tracerProvider = Sdk.CreateTracerProviderBuilder()<br>
    .AddSource(sourceName)<br>
    .AddConsoleExporter()<br>
    .Build();<br><br>
IChatClient client = new ChatClientBuilder(openaiClient)<br>
    .UseFunctionInvocation()<br>
    .UseOpenTelemetry(loggerFactory, sourceName, configure: c => c.EnableSensitiveData = false)<br>
    .Build();

Caching Strategies cho AI Responses

AI requests có thể tốn kém và chậm, vì vậy caching là cần thiết cho hiệu quả, kiểm soát chi phí và hiệu suất dự đoán được.

Built-In Caching

Microsoft.Extensions.AI cung cấp response caching có thể cắm được ở nhiều lớp:

  • In-Memory Caching: Nhanh nhất, tạm thời – tốt nhất cho các triển khai single-instance.
  • Distributed Caching: Sử dụng Redis, Azure Storage, v.v., cho các kịch bản multi-instance hoặc cloud.


var client = new ChatClientBuilder(baseClient)<br>
    .UseCaching()<br>
    .Build();

Cache các phản hồi AI bất cứ khi nào có thể, nhưng luôn cân nhắc sự lỗi thời của cache (đặc biệt với các prompt động hoặc nhạy cảm danh tính). Các chiến lược cache invalidation cũng quan trọng như cache insertion.

Automatic Function Tool Invocation

Tool calling (hoặc function calling) cho phép các AI model gọi mã ứng dụng có cấu trúc và mở khóa các kịch bản tự động hóa và điều phối nâng cao.

Cách Hoạt Động

  • Định nghĩa các function trong .NET mà bạn muốn AI gọi.
  • Sử dụng AIFunctionFactory để expose những function đó dưới dạng tool definitions với JSON schemas cho xác thực tham số và tài liệu.
  • Đăng ký các function đó trong pipeline chat của bạn.
  • Model bây giờ có thể phát ra một FunctionCallContent trong chat để marshall, gọi và trả về kết quả cho ngữ cảnh chat.

Ví Dụ: Đăng Ký .NET Functions cho Tool Calling


// Function để expose<br>
int GetWeatherForCity(string cityName) => WeatherService.Get(cityName);<br><br>
// Expose như AI Function<br>
var tool = AIFunctionFactory.Create((Func<string, int>)GetWeatherForCity);<br><br>
// Thêm vào chat options<br>
var chatOptions = new ChatOptions<br>
{<br>
    Tools = { tool }<br>
};

Luôn sử dụng JSONSchema cho các tham số và triển khai logic xác thực. Xác thực các yêu cầu model và không bao giờ thực thi các phương thức không tin cậy với tải trọng tùy ý được hướng dẫn duy nhất bởi LLM.

Stateless vs. Stateful Client Design

Sự Khác Biệt Cốt Lõi

  • Stateless Client: Không giữ lại ngữ cảnh cụ thể người dùng hoặc phiên. Mỗi yêu cầu độc lập.
    Ưu điểm: Đơn giản, throughput cao, scaling dễ dàng.
    Nhược điểm: Không cá nhân hóa/ngữ cảnh, tính liên tục hội thoại hạn chế.
  • Stateful Client: Giữ lại ngữ cảnh qua các tương tác, ví dụ: lịch sử trò chuyện, logic phiên theo người dùng.
    Ưu điểm: Phản hồi được cá nhân hóa, có liên quan ngữ cảnh; UX tốt hơn cho chatbot/agents.
    Nhược điểm: Yêu cầu quản lý ngữ cảnh mạnh mẽ và các chiến lược scaling phức tạp hơn.

Microsoft.Extensions.AI hỗ trợ cả hai loại client. Chọn dựa trên độ phức tạp ứng dụng và mục tiêu trải nghiệm người dùng.

Streaming Chat Response Handling

Đối với các ứng dụng tương tác phong phú, streaming chat responses biến đổi nhận thức của người dùng về tốc độ và sự tham gia. Thay vì chờ câu trả lời đầy đủ, tạo ra tokens/messages ngay khi có sẵn.


await foreach (var update in chatClient.GetStreamingResponseAsync(messages, chatOptions))<br>
{<br>
    Console.Write(update.Content?.Text);<br>
}

Mỗi ChatResponseUpdate cung cấp một phần của đầu ra model đầy đủ. Hoàn hảo cho UI thời gian thực, công cụ CLI và trải nghiệm agent đáp ứng.

Luôn stream các phản hồi AI hướng đến người dùng hoặc độ trễ cao trừ khi một câu trả lời blocking, full-batch là quan trọng về mặt nghiệp vụ. Điều này cho phép các tương tác người dùng mượt mà hơn và khả năng phản hồi được nhận thức.

Telemetry, Evaluation và Reporting

Đánh giá các phản hồi được tạo bởi AI là một khía cạnh quan trọng của các ứng dụng AI hiện đại. Các thư viện Microsoft.Extensions.AI.Evaluation cung cấp một tập hợp các abstraction và công cụ để tự động hóa việc đo lường mức độ liên quan, độ chính xác, tính đầy đủ và tính an toàn của đầu ra.

  • Tích hợp kiểm thử: Chạy đánh giá như một phần của CI/CD hoặc test pipelines với MSTest, xUnit, v.v.
  • Số liệu toàn diện: Đánh giá không chỉ xem một câu trả lời có được tạo ra hay không, mà còn xem liệu nó có đáp ứng chất lượng, bảo mật và kỳ vọng người dùng.
  • Caching & báo cáo: Đánh giá, lưu trữ và báo cáo về các phản hồi AI được cache để kiểm tra và cải tiến liên tục.
  • Khả năng mở rộng: Thêm các đánh giá tùy chỉnh cụ thể cho miền của bạn.

Đối với môi trường production, tích hợp đánh giá tự động để đánh giá cả model và pipeline regression, và theo dõi số liệu theo thời gian.

Patterns vs. Anti-Patterns: Bảng Thực Hành

Pattern: Luôn sử dụng hợp đồng IChatClient/IEmbeddingGenerator
Anti-Pattern: Hard-coding provider-specific clients

Pattern: Kết hợp middleware qua UseXxx()
Anti-Pattern: Nhúng logic cho caching/logging/telemetry bên trong logic nghiệp vụ

Pattern: Streaming responses cho UI thời gian thực
Anti-Pattern: Full-batch blocking calls cho UI hội thoại

Pattern: Tham số hóa tất cả tool (function) calls với JSON Schema
Anti-Pattern: Chấp nhận các tham số đầu vào model/tool không được xác thực

Pattern: Tham gia vào span correlation OpenTelemetry cho tất cả các lớp
Anti-Pattern: Không có telemetry, hoặc chỉ sử dụng logging ad-hoc

Kết Luận

Microsoft.Extensions.AI cung cấp một sự trừu tượng hóa có thể kết hợp, nhất quán và sẵn sàng cho tương lai về cách các nhà phát triển .NET sử dụng AI. Các abstraction của nó cho phép thử nghiệm nhanh chóng với các model và dịch vụ mới, và các mẫu pipeline của nó cung cấp các guardrails để scaling các tính năng một cách đáng tin cậy.

Bằng cách áp dụng các best practices và patterns chi tiết trong hướng dẫn này, bạn có thể tự tin xây dựng các tính năng AI có thể bảo trì, kiểm thử và quan sát được như phần còn lại của các giải pháp .NET của bạn. Khi cảnh quan generative AI tiếp tục tăng tốc, đầu tư vào các abstraction này ngay bây giờ sẽ mang lại lợi nhuận lớn cho team kỹ thuật và người dùng của bạn.

Hãy Xây Dựng Ngay Bây Giờ

  1. Nhắm vào các Abstractions: Viết tất cả logic điều phối cross-component chống lại IChatClientIEmbeddingGenerator.
  2. Kết hợp Middleware Pipelines: Sử dụng middleware UseXxx() cho telemetry, caching và function/tool invocation.
  3. Instrument Mọi Thứ: Kích hoạt OpenTelemetry spans cho các thao tác AI.
  4. Đi Stateless Trước, Stateful Khi Cần: Hầu hết các use cases tập trung API hoạt động tốt nhất với các client stateless.
  5. Ưu Tiên Streaming APIs cho Kịch Bản Hướng Đến Người Dùng: Tạo các cập nhật gia tăng ở bất cứ nơi nào được hỗ trợ.
  6. Tích Hợp Tool Calling Cẩn Thận: Tận dụng function invocation, nhưng luôn khóa chặt JSON schemas và xác thực tham số.
  7. Cache Chiến Lược: Sử dụng caching để giảm tải chi phí và độ trễ model.
  8. Kiểm Thử và Đánh Giá Liên Tục: Áp dụng các thư viện đánh giá cho các kiểm tra chất lượng, an toàn và tính trung thực.
Chỉ mục