Prompt đã lỗi thời; vòng lặp muôn năm!

Trong AI, vòng lặp đang là một trong những chủ đề được quan tâm nhất. Ý tưởng phổ biến là thay vì tự viết mọi prompt, bạn để tác nhân tự tạo và cải thiện chúng. Dù đã có nhiều video giải thích khá rõ lợi ích cũng như các thành phần của mô hình này, vẫn còn thiếu những ví dụ C# cụ thể, đặc biệt là ví dụ dùng Microsoft Agent Framework.

Đây là bài đầu tiên trong một loạt bài. Tôi vẫn đang tự tìm hiểu nên một số phần có thể hơi đơn giản, nhưng theo phạm vi của bài viết thì chúng vẫn đúng. Cũng cần lưu ý rằng bản nháp đầu tiên của bài này được AI viết; tôi đã biên tập lại, nhưng kết quả thực tế của bạn có thể khác.

Thí nghiệm tư duy

Hãy hình dung một tác nhân liên tục thử các cách sửa cho đến khi một dịch vụ web hoạt động ổn định: thay đổi cấu hình, kiểm tra lại trạng thái và chỉ dừng khi kiểm tra sức khỏe trả về kết quả OK hoặc khi đạt giới hạn an toàn.

Hành vi này — lặp lại tự động nhưng có giới hạn, kèm bước đánh giá giữa các lần thử — chính là điều mà lập trình vòng lặp mang lại cho AI tác nhân hiện đại. Bài viết này trình bày mẫu thiết kế, lý do nó quan trọng và cách triển khai bằng C# dựa trên các khái niệm của Microsoft Agent Framework, viết tắt là MAF.

Tóm tắt trong một câu

Lập trình vòng lặp = tác nhân hành động → quan sát → đánh giá → lặp lại cho đến khi kết quả được chấp nhận hoặc đạt giới hạn. Hãy dùng các primitive vòng lặp của MAF, chẳng hạn lớp bao vòng lặp hoặc middleware kết hợp evaluator, để triển khai mẫu này an toàn trong .NET.

Vì sao lập trình vòng lặp quan trọng?

  • Cải tiến lặp: giảm hiện tượng bịa thông tin bằng cách yêu cầu mô hình chỉnh sửa đầu ra cho đến khi đạt tiêu chí chất lượng.
  • Tác vụ nhiều bước: chia công việc phức tạp thành các lần thử nhỏ hơn như sửa, kiểm tra và thử lại.
  • Tự động hóa và độ bền: tự động thử lại các công cụ không ổn định, dùng tính khả lặp và thời gian chờ tăng dần, hoặc điều phối nhiều công cụ cho đến khi thành công.
  • Đánh đổi chi phí và độ trễ: mỗi lần lặp đều tiêu tốn thời gian và token, vì vậy chiến lược vòng lặp cần cân bằng độ chính xác với chi phí.

Các thuật ngữ cốt lõi trong vòng lặp MAF

  • Base agent / client: mô hình được bao bọc dưới dạng tác nhân, nhận yêu cầu và tạo phản hồi.
  • Loop wrapper hoặc middleware: thành phần tự động gọi lại tác nhân cơ sở, áp dụng logic đánh giá giữa các lần lặp.
  • Evaluator: thành phần kiểm tra phản hồi trước đó và trả về Continue hoặc Stop. Một số dạng phổ biến gồm:
  • Marker-based evaluator: dừng khi đầu ra chứa một dấu hiệu cụ thể, chẳng hạn DONE.
  • Judge evaluator: yêu cầu một mô hình phụ đánh giá xem đầu ra đã đạt yêu cầu hay chưa.
  • Custom evaluator: logic do bạn tự xây dựng để kiểm tra đầu ra, kết quả của công cụ hoặc trạng thái bên ngoài.
  • Bounds: giới hạn như MaxIterations, thời gian chờ và cancellation token. Đây là các thành phần bắt buộc để tránh vòng lặp chạy vô hạn.
  • AgentRequest: dữ liệu đầu vào của tác nhân, gồm prompt, công cụ và tùy chọn.
  • AgentResponse: đầu ra của tác nhân, gồm văn bản, dữ liệu có cấu trúc hoặc kết quả công cụ.

Ví dụ C# tối thiểu có thể chạy

Ví dụ dưới đây dùng evaluator dựa trên dấu hiệu hoàn tất DONE và giới hạn MaxIterations. Mã này khá đơn giản, nhưng giúp minh họa cách kết nối các thành phần.

using System;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Evaluators;
using Microsoft.Agents.AI.OpenAI.Clients;

class Program
{
    static async Task Main(string[] args)
    {
        string? apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
        if (string.IsNullOrEmpty(apiKey))
        {
            Console.WriteLine("Set OPENAI_API_KEY environment variable and re-run.");
            return;
        }

        // 1) Tao client cho nha cung cap
        var openAiClient = new OpenAiProviderClient(
            new OpenAiOptions { ApiKey = apiKey });

        // 2) Chuyen client cua nha cung cap thanh abstraction cua agent
        AIAgent baseAgent = openAiClient.AsAIAgent();

        // 3) Tao evaluator dung marker, dung khi gap "DONE"
        var evaluator = new CompletionMarkerLoopEvaluator("DONE");

        // 4) Bao agent co hanh vi vong lap va gioi han
        var loopOptions = new LoopAgentOptions { MaxIterations = 5 };
        AIAgent loopAgent = new LoopAgent(baseAgent, evaluator, loopOptions);

        // 5) Chuan bi request
        var request = new AgentRequest
        {
            Prompt = "Summarize this paragraph and keep refining until you append DONE:\n\n" +
                     "The quick brown fox jumps over the lazy dog. " +
                     "This sentence includes all the letters of the English alphabet."
        };

        // 6) Dung timeout de dam bao an toan
        using var cts = new CancellationTokenSource(
            TimeSpan.FromSeconds(30));

        try
        {
            AgentResponse response = await loopAgent.InvokeAsync(request, cts.Token);
            Console.WriteLine("Final response:\n" + response.Text);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation timed out or was canceled.");
        }
    }
}

Các chú thích trong mã trên được viết không dấu để mã nguồn dễ dùng trong mọi môi trường. Tên lớp, phương thức và chuỗi tiếng Anh cần được giữ nguyên theo API.

Điều chỉnh ví dụ cho các gói MAF thực tế

Cài đặt các gói cần thiết:

dotnet add package Microsoft.Agents.AI
dotnet add package Microsoft.Agents.AI.OpenAI

Các bước logic gần như luôn giống nhau: tạo tác nhân cơ sở, chọn evaluator, áp dụng lớp bao vòng lặp hoặc middleware rồi gọi tác nhân.

Thiết kế evaluator tốt hơn

Evaluator dựa trên marker

  • Ưu điểm: có tính xác định và chi phí thấp.
  • Nhược điểm: dễ hỏng nếu đầu ra không tuân thủ chính xác yêu cầu.

AIJudge evaluator

Mẫu này gọi một mô hình đánh giá nhỏ sau mỗi lần lặp. Mô hình đánh giá trả lời YES hoặc NO kèm một lý do ngắn. Prompt đánh giá cần thật rõ ràng để giảm mơ hồ.

Ví dụ prompt:

You are a strict grader. Given the task: [task description]. Given the model response: [response]. Is the response acceptable? Answer EXACTLY YES or NO followed by a short reason.

Mẫu custom evaluator kiểm tra nội dung và tác động phụ

  • Kiểm tra văn bản phản hồi để tìm marker của tác vụ như DONE và các trường dữ liệu có cấu trúc bắt buộc.
  • Kiểm tra trạng thái bên ngoài, chẳng hạn cơ sở dữ liệu hoặc API, để xác định tác động phụ đã thành công chưa.
  • Chỉ gọi mô hình đánh giá khi các kiểm tra heuristic chưa thể đưa ra kết luận.

Ví dụ custom evaluator

public class KeywordOrJudgeEvaluator : ILoopEvaluator
{
    private readonly string _stopKeyword;
    private readonly AIAgent? _judgeAgent;

    public KeywordOrJudgeEvaluator(
        string stopKeyword,
        AIAgent? judgeAgent = null)
    {
        _stopKeyword = stopKeyword;
        _judgeAgent = judgeAgent;
    }

    public async Task<LoopDecision> EvaluateAsync(
        AgentResponse lastResponse,
        CancellationToken ct)
    {
        if (lastResponse.Text?.Contains(
                _stopKeyword,
                StringComparison.OrdinalIgnoreCase) == true)
        {
            return LoopDecision.Stop();
        }

        if (_judgeAgent != null)
        {
            var judgeRequest = new AgentRequest
            {
                Prompt = $"Is the following response acceptable? " +
                         $"Answer YES or NO.\n\nResponse:\n" +
                         $"{lastResponse.Text}\n\nCriteria: [list your criteria]"
            };

            var judgeResponse = await _judgeAgent.InvokeAsync(
                judgeRequest, ct);

            if (judgeResponse.Text.Trim().StartsWith(
                    "YES",
                    StringComparison.OrdinalIgnoreCase))
            {
                return LoopDecision.Stop();
            }
        }

        return LoopDecision.Continue();
    }
}

Thực hành an toàn và tốt nhất khi đưa vào sản xuất

  • Luôn đặt MaxIterations. Với tác vụ cải tiến, có thể bắt đầu từ 2–5 lần; các luồng sửa chữa dài hơn có thể cần nhiều hơn.
  • Dùng timeout theo thời gian thực bằng CancellationTokenSource để giới hạn tổng thời gian chạy.
  • Ghi nhật ký từng lần lặp: prompt đầu vào, đầu ra của mô hình, quyết định của evaluator, thời lượng và các lần gọi công cụ.
  • Đảm bảo các lời gọi công cụ tạo tác động phụ có tính khả lặp. Có thể dùng idempotency key hoặc theo dõi các bước đã hoàn tất để tránh tạo tác động trùng lặp.
  • Dùng backoff và số lần thử lại giới hạn cho các lỗi công cụ tạm thời.
  • Cân nhắc quy trình có con người phê duyệt đối với thao tác rủi ro cao.
  • Thu thập số liệu như số lần lặp mỗi tác vụ, chi phí trung bình, và tỷ lệ thành công sau N lần lặp.

Đoạn mã exponential backoff

int attempt = 0;
while (attempt < maxRetries)
{
    attempt++;
    try
    {
        callTool();
        break;
    }
    catch (TransientException)
    {
        await Task.Delay(
            TimeSpan.FromSeconds(Math.Pow(2, attempt)));
    }
}

Đánh đổi giữa chi phí và độ trễ

  • Mỗi lần lặp là một lần gọi mô hình mới, kéo theo chi phí và độ trễ. Nếu dự kiến trung bình 2–3 lần lặp, có thể ước tính chi phí bằng baseCost × expectedIterations + judgeCost nếu dùng evaluator dạng judge.
  • Dùng marker evaluator khi mỗi lần lặp rẻ và prompt có thể kiểm soát đáng tin cậy. Dùng judge evaluator khi marker dễ bị bỏ sót hoặc không ổn định.
  • Với yêu cầu độ trễ thấp, hãy giới hạn MaxIterations hoặc chuyển một phần logic đánh giá sang heuristic nhanh.

Gỡ lỗi, kiểm thử và khả năng quan sát

  • Kiểm thử evaluator bằng các đối tượng AgentResponse giả lập. Nếu có thể, hãy giữ logic evaluator thuần để dễ kiểm thử.
  • Giả lập client của nhà cung cấp trong kiểm thử tích hợp để mô phỏng chuỗi lần lặp và lỗi một cách xác định.
  • Ghi một bản ghi ngắn cho mỗi lần lặp: {iteration, promptFingerprint, outputHash, evaluatorDecision, duration, costEstimate}.
  • Thêm correlation ID xuyên suốt các lần lặp để dễ truy vết.

Các lỗi thường gặp

  • Marker không bao giờ được thêm vào: kiểm tra độ rõ của chỉ dẫn trong prompt hoặc chuyển sang judge evaluator.
  • Judge dao động giữa YES và NO: thêm hysteresis, chẳng hạn yêu cầu hai lần đánh giá YES liên tiếp, hoặc kết hợp heuristic với judge.
  • Tác động phụ bị lặp: làm cho công cụ có tính khả lặp hoặc theo dõi operation ID.
  • Vòng lặp không tiến triển: thêm số liệu tiến độ và quy tắc phát hiện kẹt; dừng nếu không có thay đổi về ngữ nghĩa sau N lần lặp.

Ví dụ trong thực tế

  • Trích xuất tài liệu và hỏi đáp: tác nhân trích xuất dữ liệu có cấu trúc, chạy validator rồi yêu cầu sửa cho đến khi dữ liệu vượt qua các validator hoặc đạt MaxIterations.
  • Tự động hóa hỗ trợ khách hàng: tác nhân soạn bản nháp trả lời và cải tiến cho đến khi mô hình judge hoặc người kiểm duyệt đánh dấu là đạt yêu cầu.

Kết luận

Lập trình vòng lặp là một mẫu mạnh và thực tế cho AI tác nhân: tác nhân hành động, kiểm tra kết quả, đánh giá rồi lặp lại — nhưng chỉ khi có giới hạn và khả năng quan sát. Microsoft Agent Framework cung cấp các mẫu lớp bao vòng lặp và evaluator để triển khai an toàn. Hãy điều chỉnh ví dụ trên theo phiên bản MAF và nhà cung cấp mà bạn đang sử dụng.

BlogWriter

Chương trình minh họa BlogWriter của tôi dùng cách tiếp cận có tính xác định cao hơn. Tác giả vẫn gửi bản nháp cho reviewer đánh giá và có tạo một vòng lặp nhỏ, nhưng một cách tiếp cận vòng lặp thực sự có thể cho kết quả tốt hơn. Đây là một hướng đáng thử nghiệm.

Sẽ còn nhiều bài viết tiếp theo.

Về Jesse Liberty

Jesse Liberty có ba thập kỷ kinh nghiệm viết và bàn giao các dự án phần mềm. Ông là tác giả của khoảng hai chục cuốn sách và vài chục khóa học trực tuyến. Liberty hiện là Senior AI Engineer tại University of Pittsburgh Medical Center. Trước đó, ông từng là trưởng nhóm và kỹ sư phần mềm cấp cao tại nhiều tập đoàn, Senior Technical Evangelist tại Microsoft, Distinguished Software Engineer tại AT&T, phó chủ tịch phụ trách dịch vụ thông tin tại Citibank và kiến trúc sư phần mềm tại PBS. Ông là Microsoft MVP trong 21 năm.