Nhiều lập trình viên lầm tưởng rằng làm việc với Mô hình Ngôn ngữ Lớn (LLM) thông qua API chỉ đơn giản là gửi một chuỗi văn bản tự do và nhận lại câu trả lời. Thực tế, để xây dựng các ứng dụng AI ổn định và kiểm soát được hành vi của mô hình, bạn bắt buộc phải hiểu rõ kiến trúc cấu trúc dữ liệu của API và toán học đằng sau các tham số suy luận.
Bài học này sẽ hướng dẫn bạn làm chủ giao diện lập trình LLM: phân tích 3 vai trò hội thoại cốt lõi (System, User, Assistant), giải mã toán học của phân phối xác suất từ thông qua tham số Temperature và Top-p, tìm hiểu nghệ thuật Prompt Engineering cấp cao, và thực hành xây dựng một Chatbot có bộ nhớ ngữ cảnh trượt (Sliding Window Context Buffer) bằng PyTorch/Python.
11.1 Cấu trúc hội thoại LLM: System, User, và Assistant Roles
Các API hội thoại hiện đại (như OpenAI Chat Completion hay Google Gemini API) không nhận vào một dòng chat thô duy nhất. Chúng nhận đầu vào là một mảng có cấu trúc chứa danh sách các tin nhắn, trong đó mỗi tin nhắn bắt buộc phải được định vị một trong ba vai trò (Roles) cụ thể:
- System (Hệ thống): Đây là vai trò tối thượng dùng để thiết lập cấu hình nền tảng cho LLM. Lời nhắc System thiết lập tính cách, quy tắc hành xử, định dạng đầu ra bắt buộc (ví dụ: "chỉ trả về JSON"), và các giới hạn cấm kỵ cho AI. Người dùng cuối bình thường sẽ không nhìn thấy và không thể can thiệp trực tiếp vào lời nhắc này trong cuộc trò chuyện.
- User (Người dùng): Đại diện cho các yêu cầu, câu hỏi hoặc dữ liệu đầu vào thực tế từ người dùng cuối gửi lên mô hình.
- Assistant (Trợ lý): Ghi lại các câu trả lời mà chính mô hình LLM đã phản hồi trước đó. Để tạo ra trải nghiệm chatbot ghi nhớ lịch sử chat, lập trình viên phải chủ động lưu trữ các câu trả lời này và gửi ngược lại lên API ở lượt tiếp theo.
User. Việc này khiến mô hình cực
kỳ dễ bị lẫn lộn giữa lời của khách hàng và lời của hệ thống, dẫn đến hiện tượng
ảo tưởng (Hallucination) tự đóng vai người dùng tự hỏi tự trả lời hoặc phá vỡ các ranh
giới bảo mật (Prompt Injection). Hãy luôn tuân thủ cấu trúc mảng JSON phân vai rõ ràng.
11.2 Các tham số cấu hình suy luận LLM
Khi LLM suy luận để đoán từ tiếp theo, nó không chọn ngẫu nhiên hoàn toàn mà tính toán phân phối xác suất trên toàn bộ từ vựng $V$. Các tham số cấu hình suy luận cho phép chúng ta can thiệp vào phân phối xác suất này:
1. Temperature (Nhiệt độ - $T$)
Temperature là tham số kiểm soát mức độ sáng tạo hoặc tính ngẫu nhiên của câu trả lời. Về mặt toán học, trước khi nạp ma trận điểm số thô của các từ (logits - ký hiệu là $z_i$) vào hàm Softmax để tính xác suất, hệ thống sẽ chia toàn bộ logits cho trị số $T$:
- Khi $T \to 0$ (Nhiệt độ cực thấp): Các từ có điểm số logits $z_i$ cao nhất sẽ bị phóng đại xác suất tiệm cận về 1, trong khi các từ khác bị ép xác suất về 0. Mô hình rơi vào trạng thái sinh từ đơn điệu, lặp lại và mang tính quyết định tuyệt đối (Greedy Decoding). Thích hợp cho các tác vụ cần độ chính xác cao như viết mã nguồn, giải toán.
- Khi $T \to 1$: Phân phối xác suất trở về trạng thái tự nhiên được huấn luyện của mô hình.
- Khi $T \to \infty$ (Nhiệt độ cực cao): Số chia $T$ làm cho thương số $z_i/T$ tiệm cận về 0. Hàm số mũ $\exp(0) = 1$, khiến phân phối xác suất bị san phẳng đều như nhau cho mọi từ. Mô hình sẽ chọn ngẫu nhiên các từ lạ lẫm, dẫn đến câu văn bay bổng sáng tạo nhưng cực kỳ hỗn loạn, mất kiểm soát cấu trúc câu ngữ pháp.
- $T = 0.1$: $P = (1.0000,\ 0.0000,\ 0.0000,\ 0.0000,\ 0.0000)$ — từ có điểm cao nhất chiếm gần như toàn bộ xác suất, các từ còn lại bị triệt tiêu hoàn toàn.
- $T = 1.0$: $P = (0.6316,\ 0.2324,\ 0.0855,\ 0.0314,\ 0.0191)$ — phân phối tự nhiên của mô hình, vẫn còn cơ hội cho các từ xếp sau.
- $T = 5.0$: $P = (0.2829,\ 0.2316,\ 0.1897,\ 0.1553,\ 0.1405)$ — gần như san phẳng đều giữa 5 từ, đúng như dự đoán lý thuyết ở trên.
2. Top-p (Nucleus Sampling - Lấy mẫu hạt nhân)
Thay vì chọn lọc trên toàn bộ từ vựng, Top-p giới hạn việc chọn từ trong một nhóm nhỏ các từ có tổng xác suất tích lũy vượt qua ngưỡng $p$ (ví dụ: $p=0.9$). Hệ thống sắp xếp các từ theo xác suất từ cao xuống thấp, cộng dồn xác suất lại và cắt bỏ hoàn toàn các từ nằm ngoài ngưỡng $p$. Điều này giúp loại bỏ hoàn toàn các từ rác có xác suất cực thấp đứng ở đuôi phân phối, giữ cho câu văn mạch lạc, tránh lỗi chính tả lạ.
11.3 Nghệ thuật thiết kế Prompt (Prompt Engineering)
Prompt Engineering là nghệ thuật định hình câu lệnh đầu vào để dẫn dắt mô hình LLM suy luận chính xác nhất. Hai kỹ thuật kinh điển nhất bao gồm:
- Few-shot Prompting (Lời nhắc kèm mẫu): Thay vì chỉ mô tả yêu cầu bằng văn bản lý thuyết (Zero-shot), chúng ta cung cấp trực tiếp 2-3 ví dụ minh họa có cấu trúc đầu vào và đầu ra mẫu ngay trong prompt. Điều này giúp mô hình hiểu được định dạng và văn phong mong muốn một cách trực quan nhất.
- Chain-of-Thought (CoT - Chuỗi suy luận): Đối với các tác vụ giải toán hoặc logic phức tạp, nếu ta ép LLM đưa ra ngay đáp án cuối cùng, nó thường tính toán sai do cơ chế sinh từ tự hồi quy không có thời gian lập kế hoạch. Bằng cách thêm câu lệnh dẫn dắt: "Hãy suy nghĩ từng bước một" (Let's think step by step), ta bắt buộc mô hình phải viết ra từng bước suy luận trung gian. Việc viết ra các ký tự logic trung gian này đóng vai trò như bộ nhớ nháp (scratchpad) giúp LLM tính toán chuẩn xác kết quả cuối cùng.
11.4 Dự án thực hành bài 11: Xây dựng Chatbot ghi nhớ ngữ cảnh hội thoại
Dự án thực hành của bài học này là xây dựng một chương trình chatbot Python tương tác trực tiếp qua màn hình Terminal.
Điểm đặc biệt của dự án là việc tự thiết kế cơ chế Sliding Window Context Buffer (Bộ đệm ngữ cảnh trượt): Chương trình tự động ước lượng độ dài token của cuộc hội thoại, và khi lịch sử chat vượt quá giới hạn tài nguyên cấu hình, nó sẽ chủ động dọn dẹp các tin nhắn cũ nhất ở đầu cuộc trò chuyện nhưng vẫn giữ lại lời nhắc System cố định để chatbot không bị mất đi tính cách ban đầu.
import time
# Giả lập một LLM API Client cục bộ để chương trình chạy độc lập không cần Internet/API Key thực tế
class MockLLMClient:
def __init__(self):
# Định nghĩa một số mẫu câu trả lời thông minh dựa trên từ khóa trong cuộc hội thoại
self.responses = {
"hello": "Xin chào! Tôi là trợ lý AI thực chiến. Tôi có thể giúp gì cho bạn?",
"chào": "Xin chào! Tôi là trợ lý AI thực chiến. Tôi có thể giúp gì cho bạn?",
"toán": "Toán học là ngôn ngữ của vũ trụ. Bạn cần tôi hỗ trợ giải bài toán nào?",
"code": "Lập trình là cách chúng ta giao tiếp với máy tính. Bạn muốn viết code bằng ngôn ngữ gì?",
"ai": "Trí tuệ nhân tạo đang thay đổi thế giới thông qua kiến trúc mạng Transformer và LLMs."
}
def generate_response(self, messages, temperature=0.7):
# Trích xuất tin nhắn cuối cùng của người dùng
user_message = messages[-1]["content"].lower()
# Tìm kiếm từ khóa để phản hồi
reply = "Tôi đã tiếp nhận thông tin của bạn. Lịch sử hội thoại hiện tại vẫn đang được lưu vết trong bộ đệm ngữ cảnh."
for key, resp in self.responses.items():
if key in user_message:
reply = resp
break
# Giả lập độ trễ suy luận của mô hình
time.sleep(0.5)
return reply
class ContextChatbot:
def __init__(self, system_prompt, max_token_limit=150):
self.client = MockLLMClient()
self.max_token_limit = max_token_limit
# Lời nhắc hệ thống cố định luôn nằm ở đầu mảng tin nhắn
self.system_message = {"role": "system", "content": system_prompt}
self.history = [] # Lưu trữ lịch sử chat của User và Assistant
def estimate_tokens(self, text):
# Ước lượng thô: 1 từ tiếng Việt ~ 1.3 tokens (phương pháp thô thực tế khi không có tokenizer thực)
return int(len(text.split()) * 1.3)
def get_total_tokens(self):
# Tính tổng số tokens của toàn bộ cuộc hội thoại hiện tại (gồm cả System Prompt)
total = self.estimate_tokens(self.system_message["content"])
for msg in self.history:
total += self.estimate_tokens(msg["content"])
return total
def manage_context_window(self):
# Cơ chế Sliding Window: Nếu tổng số token vượt quá giới hạn, tiến hành loại bỏ các tin nhắn cũ
# Nhưng bắt buộc phải giữ lại phần tử System Message ở đầu
while self.get_total_tokens() > self.max_token_limit and len(self.history) > 0:
removed_msg = self.history.pop(0) # Loại bỏ tin nhắn cũ nhất ở đầu lịch sử chat
print(f"\n[Hệ thống] Đã dọn dẹp tin nhắn cũ để giải phóng bộ nhớ: '{removed_msg['content'][:30]}...'")
def chat(self, user_input):
# 1. Thêm tin nhắn của User vào lịch sử
self.history.append({"role": "user", "content": user_input})
# 2. Quản lý bộ đệm ngữ cảnh để tránh tràn bộ nhớ trước khi gọi API
self.manage_context_window()
# 3. Lắp ráp mảng hội thoại hoàn chỉnh gửi lên API
api_payload = [self.system_message] + self.history
# 4. Gọi API giả lập nhận phản hồi
assistant_reply = self.client.generate_response(api_payload, temperature=0.7)
# 5. Lưu phản hồi của Assistant vào lịch sử
self.history.append({"role": "assistant", "content": assistant_reply})
return assistant_reply
if __name__ == "__main__":
print("=== Khởi tạo Chatbot ghi nhớ ngữ cảnh ===")
system_prompt = "Bạn là một trợ lý AI chuyên nghiệp có tính cách vui vẻ và súc tích."
# Khởi tạo chatbot với giới hạn dung lượng bộ đệm nhỏ (150 tokens) để dễ quan sát cơ chế trượt
chatbot = ContextChatbot(system_prompt=system_prompt, max_token_limit=80)
print(f"Giới hạn bộ đệm ngữ cảnh: {chatbot.max_token_limit} tokens.\n")
# Giả lập chuỗi hội thoại liên tục của người dùng
user_inputs = [
"Xin chào trợ lý, bạn khỏe không?",
"Tôi muốn hỏi một chút kiến thức về Toán học AI.",
"Tôi cũng cần viết một số đoạn code Python.",
"Trí tuệ nhân tạo AI là gì?",
"Cảm ơn bạn rất nhiều nhé."
]
for idx, inp in enumerate(user_inputs):
print(f"User: {inp}")
reply = chatbot.chat(inp)
print(f"Assistant: {reply}")
print(f"-> Tổng token hiện tại trong bộ nhớ chat: {chatbot.get_total_tokens()}\n")
print("=== Toàn bộ lịch sử hội thoại còn lưu giữ trong RAM: ===")
for msg in chatbot.history:
print(f"[{msg['role'].upper()}]: {msg['content']}")
Tóm tắt bài học & Cầu nối kiến thức
- Đạt được: Làm chủ cách tùy biến tham số Temperature, Top-P của LLM và thiết kế prompt hệ thống (System Prompt).
- Đạt được: Xây dựng thành công chatbot ghi nhớ ngữ cảnh hội thoại sử dụng API của các mô hình thương mại.
Cầu nối bài tiếp theo: Để kết quả trả về của LLM không chỉ là văn bản tự do mà có cấu trúc chuẩn hóa cho ứng dụng Web đọc được, chúng ta cần tìm hiểu chế độ JSON Mode và Function Calling ở Bài số 12.
Tải file code thực hành minh họa bài học
File Python chatbot_context.py — mã nguồn xây dựng chatbot Terminal có cơ chế trượt bộ đệm
ngữ cảnh Sliding Window giải phóng tài nguyên (chạy python chatbot_context.py):
📖 Tài liệu tham khảo
- OpenAI Prompt Engineering Guide — Tài liệu chính thức về các chiến lược thiết kế prompt tối ưu hóa cho mô hình GPT (OpenAI Docs)
- Large Language Models are Zero-Shot Reasoners — Bài báo khoa học kinh điển chứng minh sức mạnh của Chain-of-Thought Prompting (Kojima et al., 2022)
- Generation strategies in Transformers — Hướng dẫn chi tiết thuật toán suy luận Temperature, Top-p, Top-k (Hugging Face Docs)
Bình luận