Menu

ReAct prompting: vòng lặp suy luận và hành động

ReAct prompting khiến mô hình luân phiên giữa suy luận về nhiệm vụ và gọi công cụ, đọc từng kết quả rồi mới quyết định bước tiếp theo. Đây là vòng lặp đứng sau phần lớn các AI agent.

Mọi prompt bên dưới đều có thể chỉnh sửa: thay đổi nó, rồi mở trong ChatGPT, Claude hoặc ứng dụng AI khác.

ReAct prompting là một mẫu prompt trong đó mô hình ngôn ngữ luân phiên giữa suy luận và hành động: nó viết ra suy nghĩ về việc cần làm, thực hiện một hành động như tìm kiếm hay gọi công cụ, đọc kết quả, rồi mới quyết định bước tiếp theo. Cái tên là viết tắt của Reason + Act (suy luận và hành động), xuất phát từ bài báo của Yao và cộng sự năm 2022, "ReAct: Synergizing Reasoning and Acting in Language Models". Nó không liên quan đến React, thư viện JavaScript dùng để xây dựng giao diện người dùng.

Phần lớn các AI agent biết duyệt web, chạy code hay sửa tệp đều chạy một phiên bản nào đó của vòng lặp này. Khi bạn đã tự tay đi qua vòng lặp một lần, hành vi của agent sẽ dễ đoán và dễ gỡ lỗi hơn nhiều.

Vòng lặp Thought, Action, Observation

Một prompt ReAct yêu cầu mô hình viết theo ba loại dòng.

  • Thought: suy luận về tình huống hiện tại và thông tin nào còn thiếu.
  • Action: một lệnh gọi công cụ theo định dạng cố định, như search[query], read_file[path] hoặc finish[answer].
  • Observation: kết quả của hành động đó. Mô hình không viết dòng này. Chương trình của bạn (hoặc chính bạn) chạy hành động và thêm đầu ra thật vào bản ghi hội thoại.

Sau đó toàn bộ bản ghi được gửi lại cho mô hình, và mô hình viết Thought tiếp theo. Vòng lặp kết thúc khi mô hình chọn hành động finish. Mô hình không bao giờ tự chạy công cụ; nó chỉ yêu cầu, và phần code bao quanh quyết định có thực hiện yêu cầu đó hay không.

Vì sao kết hợp suy luận và hành động tốt hơn từng thứ riêng lẻ

Bài báo so sánh ReAct với prompt chỉ suy luận (chain of thought) và prompt chỉ hành động (gọi công cụ mà không viết ra lập luận). Ở bài toán hỏi đáp và kiểm chứng thông tin với công cụ tìm kiếm Wikipedia, chỉ suy luận thường dựng nên một lập luận đầy tự tin trên một thông tin do mô hình tự bịa, còn ReAct có thể tra cứu thông tin đó, và kết quả hỏi đáp tốt nhất đến từ việc kết hợp ReAct với chain of thought, chuyển từ cách này sang cách kia khi cách đầu không cho ra câu trả lời chắc chắn. Chỉ hành động thì gặp khó khi chia nhỏ mục tiêu và gom những gì tìm được thành câu trả lời; các suy nghĩ được viết ra giúp kế hoạch luôn hiện rõ. Ở hai nhiệm vụ tương tác, một trò chơi việc nhà dạng văn bản và một trang mua sắm mô phỏng, ReAct với một hoặc hai ví dụ trong prompt đạt kết quả tốt hơn các mô hình đối chứng được huấn luyện mà nhóm tác giả so sánh.

Bài học chung: chain of thought giúp mô hình lập kế hoạch, công cụ cho nó thông tin thật, và không thứ nào đủ một mình cho một nhiệm vụ cần cả hai.

Một agent ReAct, từng lượt một

Khối bên dưới là một agent lập trình nhỏ có hai công cụ. Mỗi tab là cùng một cuộc trò chuyện ở bước tiếp theo: các dòng Observation là thứ chương trình dán lại sau khi chạy hành động trước đó. Hãy đọc các câu trả lời theo thứ tự.

System prompt
Giải quyết nhiệm vụ bằng cách lặp lại Thought, Action và Observation. Thought: suy luận về những gì bạn đã biết và việc cần làm tiếp theo. Action: chính xác một trong các lệnh sau run_tests[path] chạy pytest trên một tệp và trả về đầu ra read_file[path] trả về nội dung của một tệp finish[answer] kết thúc nhiệm vụ với câu trả lời của bạn Viết một Thought và một Action, rồi dừng lại. Observation sẽ được đưa cho bạn.
Nhiệm vụ: tests/test_cart.py bắt đầu thất bại sau lần merge hôm qua. Tìm nguyên nhân.
Try it
Chat apps receive the system prompt as the start of your message, so this tries its wording, not its priority. To test it as standing instructions, put it in the app's custom instructions or the API's system field.
Example replyReplies vary between models and runs.

Thought: Chưa biết test nào thất bại và thất bại ra sao. Chạy test sẽ cho thấy chính xác assertion nào sai trước khi đọc code. Action: run_tests[tests/test_cart.py]

Hãy để ý hai điều. Hành động đầu tiên là chạy test, không phải đoán: mô hình thu thập bằng chứng trước khi đọc code. Và mỗi suy nghĩ đều dựa vào observation mới nhất, đó là điều chain of thought không làm được.

Thử ReAct trong ứng dụng chat

Bạn không cần code để thấy vòng lặp này. Trong ChatGPT, Claude hay Gemini, bạn có thể tự làm công cụ: mô hình yêu cầu một hành động, bạn thực hiện rồi dán kết quả lại.

Làm công cụ cho mô hình
Fill in
Mình muốn trả lời câu hỏi này: Vì sao quạt laptop của mình kêu to ngay cả khi mình không làm gì? Hãy giúp mình bằng cách làm việc theo vòng lặp. Mỗi câu trả lời có đúng một Thought (bạn đã biết gì và còn thiếu gì) và một Action: một việc cụ thể để mình kiểm tra, chạy hoặc tra cứu, kèm các bước chính xác. Rồi dừng lại và chờ. Mình sẽ trả lời bằng kết quả dưới dạng "Observation: ...". Khi đã đủ bằng chứng, hãy trả lời bằng "Trả lời:" và kết luận của bạn. Đừng đoán câu trả lời trước khi có bằng chứng.
Try it
Example replyReplies vary between models and runs.

Thought: Quạt kêu to khi máy đang rảnh thường có nghĩa là có thứ gì đó đang dùng CPU ở chế độ nền, hoặc quạt đang phải vất vả để làm mát (bụi, khe thoát khí bị chặn). Kiểm tra mức dùng CPU trước sẽ tách được hai trường hợp này.

Action: Mở Task Manager (Ctrl+Shift+Esc trên Windows) hoặc Activity Monitor (Applications > Utilities trên Mac). Sắp xếp các tiến trình theo CPU, chờ 30 giây, rồi cho mình biết ba tiến trình đứng đầu và phần trăm CPU của chúng.

Cũng những chỉ dẫn đó, với một hàm thật đứng sau mỗi hành động, chính là một agent.

Một vòng lặp ReAct tối giản bằng Python

Trong code, chương trình gửi bản ghi hội thoại, tìm dòng Action trong câu trả lời, chạy hàm tương ứng, thêm Observation vào rồi lặp lại. Bản phác thảo này dùng OpenAI Python SDK và system prompt của agent lập trình ở trên.

import re
import subprocess
from pathlib import Path
from openai import OpenAI

client = OpenAI()
MODEL = "your-model-id"  # e.g. from your provider's model list
PROJECT = Path.cwd().resolve()
READABLE = {".py", ".md", ".txt", ".toml", ".cfg"}

def inside_project(arg):
    # The model chooses arg: resolve it and refuse anything outside the project.
    path = (PROJECT / arg).resolve()
    if not path.is_relative_to(PROJECT):
        raise ValueError(f"{arg} is outside the project")
    return path

def run_tests(arg):
    path = inside_project(arg)
    if not (path.is_file() and path.name.startswith("test_") and path.suffix == ".py"):
        raise ValueError(f"{arg} is not a test file")
    result = subprocess.run(["pytest", str(path), "-q"], capture_output=True, text=True)
    return result.stdout[-2000:]

def read_file(arg):
    path = inside_project(arg)
    if not path.is_file() or path.suffix not in READABLE:
        raise ValueError(f"{arg} is not a file this agent may read")
    return path.read_text()[:20000]

TOOLS = {"run_tests": run_tests, "read_file": read_file}

def react(task, system_prompt, max_steps=8):
    transcript = f"Task: {task}\n"
    for _ in range(max_steps):
        response = client.chat.completions.create(
            model=MODEL,
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": transcript},
            ],
            temperature=0,
        )
        text = response.choices[0].message.content
        match = re.search(r"Action: (\w+)\[(.*)\]", text)
        if not match:
            return text
        transcript += text[: match.end()] + "\n"
        tool, arg = match.groups()
        if tool == "finish":
            return arg
        try:
            observation = TOOLS[tool](arg) if tool in TOOLS else f"Unknown tool: {tool}"
        except Exception as error:  # a missing file becomes an observation, not a crash
            observation = f"Error: {error}"
        transcript += f"Observation:\n{observation}\n"
    return "Stopped after max_steps without an answer."

Có bốn chi tiết quan trọng. Bản ghi hội thoại bị cắt ngay sau dòng Action, vì đôi khi mô hình viết tiếp và tự bịa ra Observation. Một công cụ bị lỗi (chẳng hạn sai tên tệp) trở thành một Observation để mô hình phản ứng, thay vì làm vòng lặp bị sập. Giới hạn số bước ngăn một mô hình lặp mãi không dừng. Và đầu ra của test được cắt bớt, vì một log test khổng lồ sẽ lấp đầy context window; quyết định những gì đưa vào cửa sổ đó chính là context engineering.

Hai công cụ cũng kiểm tra đối số trước khi làm bất cứ việc gì. Mô hình là bên chọn đối số đó, nên công cụ là nơi đặt giới hạn: inside_project từ chối mọi đường dẫn trỏ ra ngoài thư mục dự án (kể cả các mẹo ../), run_tests chỉ nhận tệp test_*.py, còn read_file chỉ mở vài loại tệp mã nguồn và văn bản, trả về tối đa 20.000 ký tự, nên tệp .env hay SSH key không bao giờ lọt vào request tiếp theo. Chạy pytest vẫn thực thi code của chính dự án, nên hãy chạy một agent như thế này trong container hoặc một bản checkout dùng một lần, đừng bao giờ chạy trên máy có chứa thông tin bí mật.

Các API hiện nay của OpenAI, Anthropic và Google cũng có tính năng gọi công cụ sẵn: bạn mô tả mỗi công cụ bằng tên và một JSON schema, và mô hình trả về một lệnh gọi công cụ có cấu trúc thay vì một dòng Action:. Vòng lặp giữ nguyên; chỉ có phần tách dữ liệu là biến mất.

An toàn trong vòng lặp ReAct

Một agent đọc những văn bản không do nó viết: trang web, tệp, đầu ra của công cụ. Bất kỳ phần nào trong đó cũng có thể chứa chỉ dẫn nhắm vào mô hình, đó là prompt injection. Chỉ cấp cho mỗi công cụ quyền tối thiểu nó cần (chỉ đọc khi có thể), yêu cầu con người xác nhận mọi thao tác xóa, gửi hoặc thanh toán, và coi các hành động mô hình yêu cầu là đầu vào không đáng tin cần kiểm tra, không phải lệnh để chạy mù quáng.

Câu hỏi thường gặp

ReAct prompting là gì?

ReAct (viết tắt của Reason + Act, suy luận và hành động) là một mẫu prompt trong đó mô hình ngôn ngữ viết một đoạn suy luận ngắn, rồi một hành động như tìm kiếm hay gọi công cụ, sau đó đọc kết quả rồi mới suy luận tiếp. Nó đến từ bài báo của Yao và cộng sự năm 2022, "ReAct: Synergizing Reasoning and Acting in Language Models". Nó không liên quan gì đến thư viện JavaScript React.

Thought, Action và Observation trong ReAct là gì?

Thought là phần suy luận của mô hình về việc cần làm tiếp theo. Action là một lệnh gọi công cụ theo định dạng cố định, ví dụ search[python 3.13 release notes]. Observation là đầu ra của công cụ, do chương trình của bạn chạy hành động đó để lấy rồi thêm vào prompt. Vòng lặp lặp lại cho đến khi mô hình chọn hành động kết thúc kèm câu trả lời.

ReAct khác chain of thought như thế nào?

Chain of thought suy luận từ những gì mô hình đã biết, nên một thông tin sai ở đầu chuỗi sẽ sai đến cuối. ReAct xen kẽ suy luận với các hành động lấy thông tin thật, nên mô hình có thể kiểm tra một sự kiện, thấy một test thất bại hoặc đọc một tệp trước khi đi tiếp. Bài báo ReAct cho thấy việc dựa phần suy luận vào kết quả tìm kiếm giúp giảm các thông tin bịa đặt mà chỉ dùng chain of thought tạo ra.

Các AI agent hiện nay còn dùng ReAct không?

Vòng lặp thì có. Phần lớn agent ngày nay suy luận, gọi công cụ, đọc kết quả rồi quyết định lại, đó chính là chu trình ReAct. Điều thay đổi là định dạng: thay vì tách các dòng Action: ra khỏi văn bản, các API hiện nay có tính năng gọi công cụ (tool calling) sẵn, trong đó mô hình trả về một yêu cầu gọi công cụ có cấu trúc và code của bạn gửi lại kết quả.

Có dùng ReAct trong ChatGPT mà không cần code được không?

Được, bằng cách tự đóng vai công cụ. Yêu cầu mô hình trả lời với một Thought và một Action rồi dừng lại. Bạn thực hiện hành động đó (tìm kiếm, mở tệp, chạy lệnh), dán kết quả vào làm Observation và lặp lại. Cách này chậm, nhưng nó cho thấy chính xác cách một agent quyết định việc cần làm tiếp theo.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu