Một prompt đơn lẻ sẽ nhanh chóng đạt giới hạn. Khi tác vụ liên quan đến nhiều bước suy luận, logic có điều kiện, hoặc cần lặp lại để cải thiện chất lượng đầu ra, bạn cần prompt chaining — chia tác vụ phức tạp thành chuỗi các prompt nhỏ, mỗi prompt tập trung vào một việc và đầu ra của bước trước trở thành đầu vào của bước sau.
Bài hướng dẫn này trình bày 5 pattern đã được kiểm chứng trong thực tế, có thể mở rộng từ script cá nhân đến hệ thống production. Mỗi pattern kèm theo code mẫu để bạn áp dụng ngay.
Prompt Chaining Là Gì?
Prompt chaining là kỹ thuật phân tách một tác vụ phức tạp thành nhiều lời gọi LLM liên tiếp (hoặc song song), trong đó đầu ra của prompt này trở thành đầu vào của prompt tiếp theo. Thay vì nhồi hết mọi thứ vào một prompt khổng lồ, bạn thiết kế một pipeline gồm nhiều prompt nhỏ, mỗi cái làm tốt một việc.
Lợi ích rõ ràng:
- Độ tin cậy — mỗi bước có phạm vi hẹp, giảm diện tích ảo giác
- Dễ debug — kiểm tra được đầu ra trung gian để tìm đúng chỗ xảy ra lỗi
- Linh hoạt — thay đổi hoặc sửa từng bước mà không cần viết lại toàn bộ pipeline
- Kiểm soát chi phí — dùng model rẻ cho bước đơn giản, model đắt chỉ khi cần thiết
Pattern 1: Tinh Chỉnh Tuần Tự (Sequential Refinement)
Pattern chuỗi đơn giản nhất. Mỗi bước tinh chỉnh đầu ra của bước trước qua vòng lặp tạo-phê bình-chỉnh sửa.
Trường hợp sử dụng: Tạo nội dung, viết code, chuyển đổi dữ liệu.
Cách hoạt động:
- Tạo bản thảo thô
- Phê bình bản thảo theo các vấn đề cụ thể
- Chỉnh sửa dựa trên danh sách phê bình
# Bước 1: Tạo bản thảo thô
draft = llm_call(
prompt=f"Viết giải thích kỹ thuật về {topic}. "
f"Kèm code mẫu. Viết đầy đủ.",
model="claude-sonnet"
)
# Bước 2: Phê bình bản thảo
critique = llm_call(
prompt=f"Đánh giá bản thảo này về độ chính xác, rõ ràng và đầy đủ. "
f"Liệt kê các vấn đề cụ thể dạng danh sách đánh số:\n\n{draft}",
model="claude-sonnet"
)
# Bước 3: Chỉnh sửa dựa trên phê bình
final = llm_call(
prompt=f"Chỉnh sửa bản thảo này, giải quyết mọi vấn đề trong phê bình.\n\n"
f"BẢN THẢO:\n{draft}\n\nPHÊ BÌNH:\n{critique}",
model="claude-sonnet"
)Tại sao hiệu quả: Bước phê bình buộc model chuyển sang chế độ đánh giá khác với chế độ tạo nội dung. Nếu không có bước này, yêu cầu model "cải thiện" đầu ra của chính nó thường chỉ tạo ra thay đổi tối thiểu. Phê bình tường minh lộ ra các vấn đề mà lượt tạo đã bỏ qua.
Pattern 2: Phân Loại và Định Tuyến (Gate-and-Route)
Một bước phân loại nhanh quyết định chuỗi xử lý nào được thực thi. Cách này giữ cho mỗi nhánh đơn giản, có mục đích rõ ràng và dễ bảo trì độc lập.
Trường hợp sử dụng: Định tuyến hỗ trợ khách hàng, kiểm duyệt nội dung, xử lý tài liệu đa định dạng.
Cách hoạt động:
- Phân loại đầu vào bằng model rẻ, nhanh
- Định tuyến đến handler chuyên biệt cho loại đó
# Bước 1: Phân loại đầu vào (dùng model rẻ nhất)
category = llm_call(
prompt=f"Phân loại yêu cầu này vào đúng một danh mục: "
f"[thanh_toan, ky_thuat, tai_khoan, khac]\n\n"
f"Chỉ trả về tên danh mục.\n\n{user_input}",
model="claude-haiku",
temperature=0 # phân loại xác định, không ngẫu nhiên
)
# Bước 2: Định tuyến đến chuỗi chuyên biệt
if category.strip() == "thanh_toan":
response = billing_chain(user_input)
elif category.strip() == "ky_thuat":
response = technical_chain(user_input)
elif category.strip() == "tai_khoan":
response = account_chain(user_input)
else:
response = general_chain(user_input)Điểm mấu chốt: Cổng phân loại chỉ cần xuất ra một từ duy nhất. Đây chính xác là tác vụ mà model lớp Haiku vượt trội — nhanh, rẻ và chính xác cho đầu ra bị ràng buộc. Dùng token Opus cho phân loại là lãng phí.
Pattern 3: Tổng Hợp Map-Reduce
Chia đầu vào lớn thành các phần, xử lý từng phần độc lập, rồi tổng hợp kết quả. Pattern này phá vỡ giới hạn context window và tự nhiên cho phép xử lý song song.
Trường hợp sử dụng: Tóm tắt tài liệu dài, phân tích codebase lớn, tổng hợp nghiên cứu đa nguồn.
Cách hoạt động:
- Chia đầu vào thành các phần vừa với context window
- Map: xử lý từng phần độc lập (chạy song song)
- Reduce: tổng hợp tất cả kết quả phần thành đầu ra cuối
import asyncio
# Bước 1: Chia đầu vào thành các phần vừa phải
chunks = split_document(long_document, max_tokens=3000)
# Bước 2: Map — xử lý từng phần độc lập (chạy đồng thời)
async def process_chunk(chunk):
return await llm_call_async(
prompt=f"Tóm tắt các điểm chính trong phần này. "
f"Giữ lại dữ liệu cụ thể, con số và kết luận:\n\n{chunk}",
model="claude-sonnet"
)
summaries = await asyncio.gather(*[process_chunk(c) for c in chunks])
# Bước 3: Reduce — tổng hợp tất cả bản tóm tắt
final_summary = llm_call(
prompt=f"Tổng hợp các bản tóm tắt này thành một bài tổng quan mạch lạc. "
f"Giải quyết mâu thuẫn. Nêu bật phát hiện quan trọng nhất:\n\n"
+ "\n\n---\n\n".join(summaries),
model="claude-opus" # model tốt nhất cho suy luận tổng hợp
)Lưu ý về hiệu suất: Bước map có thể song song hoàn toàn. Chạy 10 chunk tuần tự mất gấp 10 lần so với chạy đồng thời. Luôn dùng asyncio.gather() hoặc thread pool cho giai đoạn map. Bước tổng hợp phải chờ tất cả chunk hoàn thành.
Pattern 4: Vòng Lặp Xác Minh (Verification Loop)
Tạo đầu ra, xác minh theo tiêu chí cứng, và lặp lại cho đến khi đạt. Đây là cách bạn có được structured output tin cậy — đặc biệt là JSON — mà không cần dùng regex dễ vỡ hay hy vọng model làm đúng ngay lần đầu.
Trường hợp sử dụng: Đầu ra JSON/schema, tạo code có test, bài toán thỏa mãn ràng buộc.
Cách hoạt động:
- Tạo đầu ra
- Xác thực bằng code xác định (không phải lời gọi LLM khác)
- Nếu không hợp lệ, đưa lỗi ngược lại và tạo lại
- Lặp tối đa một số lần cố định
import json
import jsonschema
max_attempts = 3
schema = load_json_schema("output_schema.json")
last_error = None
for attempt in range(max_attempts):
# Xây dựng prompt — kèm lỗi trước khi thử lại
prompt = f"Tạo một JSON object khớp với schema này:\n{json.dumps(schema, indent=2)}\n\n"
prompt += f"Yêu cầu: {requirements}"
if last_error:
prompt += f"\n\nLần thử trước không vượt qua xác thực với lỗi sau:\n{last_error}\nHãy sửa lại."
output = llm_call(prompt=prompt, model="claude-sonnet", temperature=0)
# Xác thực bằng code xác định — không phải lời gọi LLM
try:
parsed = json.loads(output)
jsonschema.validate(parsed, schema)
break # thành công — thoát vòng lặp
except (json.JSONDecodeError, jsonschema.ValidationError) as e:
last_error = str(e)
if attempt == max_attempts - 1:
raise RuntimeError(f"Thất bại sau {max_attempts} lần: {last_error}")Quy tắc quan trọng: Luôn giới hạn số lần thử lại. Nếu model không thể tạo đầu ra hợp lệ sau 3 lần, prompt hoặc schema cần được thiết kế lại — không phải thử thêm. Vòng lặp vô hạn lãng phí tiền và che giấu vấn đề thực sự.
Pattern 5: Fan-Out Song Song với Đồng Thuận
Chạy cùng một câu hỏi qua nhiều model hoặc cấu hình cùng lúc, rồi tổng hợp các phản hồi để tìm điểm đồng thuận và bất đồng. Kết quả vững chắc hơn bất kỳ model đơn lẻ nào có thể tạo ra.
Trường hợp sử dụng: Quyết định có rủi ro cao, xác minh sự thật, review kiến trúc, đánh giá bảo mật.
Cách hoạt động:
- Fan-out: cùng prompt đến nhiều model song song
- Bao gồm một cấu hình phản biện có chủ đích để kiểm tra giả định
- Tổng hợp: xác định đồng thuận, giải quyết bất đồng, đưa ra câu trả lời cuối
import asyncio
async def get_perspectives(question: str) -> list[str]:
tasks = [
llm_call_async(prompt=question, model="claude-opus"),
llm_call_async(prompt=question, model="chatgpt-4o"),
llm_call_async(
prompt=question,
model="claude-sonnet",
system="Bạn là người phê bình. Chủ động thách thức các giả định "
"và xác định rủi ro mà các phản hồi khác có thể bỏ qua."
),
]
return await asyncio.gather(*tasks)
responses = asyncio.run(get_perspectives(question))
# Tổng hợp với kiểm tra đồng thuận tường minh
final = llm_call(
prompt=f"Ba chuyên gia độc lập trả lời cùng một câu hỏi.\n\n"
f"Chuyên gia A:\n{responses[0]}\n\n"
f"Chuyên gia B:\n{responses[1]}\n\n"
f"Chuyên gia C (phản biện):\n{responses[2]}\n\n"
f"1. Xác định các điểm cả ba đồng ý.\n"
f"2. Xác định các điểm bất đồng và giải thích tại sao khác nhau.\n"
f"3. Đưa ra câu trả lời cuối cùng kết hợp lập luận mạnh nhất từ mỗi người.",
model="claude-opus"
)Tại sao cần người phản biện: Nếu không có áp lực đối kháng có chủ đích, các phản hồi fan-out có xu hướng đồng ý với nhau — dữ liệu training giống nhau tạo ra điểm mù giống nhau. Vai trò phản biện buộc bước tổng hợp phải chủ động giải quyết phản bác thay vì chỉ lấy trung bình các ý kiến nhàm chán.
Thực Hành Cho Production
Các pattern này kết hợp tốt với nhau, nhưng hệ thống production đòi hỏi kỷ luật thêm:
1. Ghi log mọi bước trung gian. Khi chuỗi tạo ra đầu ra sai, bạn cần biết bước nào thất bại. Lưu tất cả kết quả trung gian kèm timestamp và phiên bản model — không chỉ log đầu ra cuối.
2. Dùng định dạng có cấu trúc giữa các bước. JSON hoặc XML tại ranh giới chuỗi dễ parse và xác thực hơn văn bản tự do. Việc bàn giao mơ hồ giữa các bước là nguyên nhân lớn gây ra lỗi chuỗi.
3. Đặt temperature bằng 0 cho các bước xác định. Các bước phân loại, trích xuất và xác thực phải có kết quả lặp lại được. Chỉ dùng temperature > 0 cho các bước tạo sáng tạo. Trộn lẫn chúng một cách im lặng là lỗi phổ biến.
4. Xây dựng từng bước. Triển khai bước 1 trước, xác minh hoạt động tin cậy, rồi thêm bước 2. Không nên thiết kế chuỗi 6 bước trên giấy rồi triển khai tất cả cùng lúc — debug chuỗi 6 bước bị lỗi khó hơn nhiều so với debug bước vừa thêm.
5. Phân bổ theo tầng model có ý nghĩa quan trọng. Không phải bước nào cũng cần model đắt nhất. Phân loại và trích xuất chạy tốt trên model lớp Haiku. Tổng hợp và suy luận phức tạp mới xứng đáng chi phí lớp Opus. Phân bổ tính toán như vậy cắt giảm chi phí 40-70% trong các chuỗi điển hình mà không mất chất lượng.
Sẵn sàng xây dựng prompt chain?
Cả Claude và ChatGPT đều cung cấp quyền truy cập API cần thiết cho prompt chaining. Claude vượt trội ở các bước tổng hợp context dài; hệ sinh thái tool rộng của ChatGPT phù hợp tốt với các pattern gate-and-route.
Get StartedKhi Nào Không Nên Dùng Chaining
Prompt chaining thêm độ trễ, phức tạp và chi phí. Tránh dùng khi:
- Một prompt được soạn tốt đã cho kết quả tin cậy
- Độ trễ là ràng buộc chính và các bước không thể song song
- Tác vụ thực sự nguyên tử (phân loại đơn giản, hỏi đáp trực tiếp, trích xuất một định dạng)
Mục tiêu là độ tin cậy và kiểm soát đối với tác vụ phức tạp — không phải thêm phức tạp pipeline vì bản thân nó. Nếu một prompt đơn hoạt động, hãy dùng nó.