Cách xây chatbot bằng Grok API
Bùi Tấn Lực
- 104
- 08/10/2026
Xây chatbot bằng Grok API không đơn giản chỉ là gửi một câu hỏi đến mô hình rồi hiển thị câu trả lời. Một chatbot thực tế cần xử lý được toàn bộ vòng đời của cuộc hội thoại: nhận tin nhắn, xác định phiên trò chuyện, gửi ngữ cảnh phù hợp đến Grok, nhận kết quả, trả lời người dùng và kiểm soát các vấn đề như bảo mật API key, giới hạn chi phí, độ dài lịch sử và lỗi kết nối.
Ở thời điểm hiện tại, xAI cung cấp Responses API làm giao diện chính cho việc tạo nội dung, suy luận và sử dụng công cụ; Chat Completions vẫn được hỗ trợ nhưng được xem là giao diện kế thừa và các tính năng mới ưu tiên Responses API.
Trong bài viết này, Web Mới sẽ hướng dẫn theo hướng có thể áp dụng vào một website thực tế, đặc biệt phù hợp với mô hình website PHP: từ kiến trúc tổng thể, tạo API key, xây backend trung gian, quản lý lịch sử hội thoại cho đến hoàn thiện giao diện chatbot.

Chatbot dùng Grok API hoạt động như thế nào?
Điểm quan trọng nhất cần hiểu là trình duyệt không nên gọi trực tiếp Grok API bằng API key bí mật. Thay vào đó, chatbot nên được xây thành một luồng gồm giao diện người dùng, backend của website và máy chủ API của xAI.
Có thể hình dung luồng xử lý cơ bản như sau:
- Người dùng nhập câu hỏi trên giao diện chatbot.
- JavaScript gửi nội dung câu hỏi đến backend của website.
- Backend kiểm tra dữ liệu đầu vào và xác định phiên trò chuyện.
- Backend lấy lịch sử hội thoại cần thiết.
- Backend gửi yêu cầu đến Grok API kèm prompt và ngữ cảnh.
- Grok xử lý yêu cầu và trả về kết quả.
- Backend lưu câu hỏi cùng câu trả lời nếu hệ thống cần duy trì lịch sử.
- Backend trả dữ liệu về trình duyệt để hiển thị cho người dùng.
Kiến trúc này tạo ra một lớp trung gian rất quan trọng. Website có thể kiểm soát API key, giới hạn số lần gọi, lọc dữ liệu đầu vào, ghi log, xử lý lỗi và áp dụng các quy tắc riêng trước khi yêu cầu được chuyển đến Grok.
Thay vì thiết kế theo kiểu:
Người dùng → Grok API
nên thiết kế:
Người dùng
↓
Giao diện chatbot
↓
Backend website
↓
Quản lý phiên + lịch sử + kiểm soát dữ liệu
↓
Grok API
↓
Backend
↓
Giao diện chatbot
Đây là nền tảng giúp chatbot có thể mở rộng về sau. Khi cần thêm đăng nhập, giới hạn người dùng, lưu cuộc trò chuyện, streaming, công cụ tìm kiếm hoặc hệ thống quản trị, bạn vẫn có thể phát triển trên cùng kiến trúc.
Cần chuẩn bị gì trước khi lập trình?
Để xây một chatbot cơ bản, bạn cần một tài khoản xAI có quyền sử dụng API, API key, một backend có khả năng gửi HTTP request và giao diện để người dùng nhập tin nhắn.
xAI hiện cung cấp API key thông qua xAI Console. API sử dụng cơ chế xác thực bằng header Authorization: Bearer và endpoint API toàn cầu là https://api.x.ai.
Nếu website Web Mới sử dụng PHP, không nhất thiết phải cài một hệ thống quá phức tạp. PHP có thể gửi request HTTP đến API bằng cURL, sau đó trả JSON về cho JavaScript.
Một cấu trúc đơn giản có thể tổ chức như sau:
chatbot/
├── index.php
├── api/
│ └── chat.php
├── config/
│ └── config.php
├── assets/
│ ├── chatbot.css
│ └── chatbot.js
└── logs/
└──
Trong đó, index.php chịu trách nhiệm tạo giao diện, chatbot.js xử lý tương tác phía trình duyệt, còn chat.php là điểm nhận câu hỏi và giao tiếp với Grok API.
API key nên được đặt ở phía máy chủ. Tuyệt đối không đưa khóa bí mật vào JavaScript chạy trên trình duyệt, HTML hoặc bất kỳ đoạn mã nào mà khách truy cập có thể xem được.
Tạo API key cho ứng dụng
Sau khi tạo tài khoản xAI, bạn tạo API key trong khu vực quản lý API rồi đưa khóa vào biến môi trường hoặc cấu hình bảo mật của máy chủ. Tài liệu chính thức của xAI cũng hướng dẫn sử dụng biến môi trường XAI_API_KEY để lưu khóa thay vì hard-code trực tiếp trong mã nguồn.
Ví dụ trên môi trường máy chủ Linux:
export XAI_API_KEY="your_api_key"
Nếu sử dụng file môi trường, có thể lưu:
XAI_API_KEY=your_api_key
Không nên đưa giá trị thật của API key vào Git repository. Nếu website được triển khai trên hosting, VPS hoặc máy chủ riêng, hãy ưu tiên cơ chế biến môi trường mà hệ thống hosting cung cấp.
Trong trường hợp bắt buộc phải sử dụng file cấu hình, file chứa API key cũng cần được đặt ngoài thư mục public nếu cấu hình máy chủ cho phép. Mục tiêu là người truy cập không thể mở URL và đọc được khóa.
Chọn API và model phù hợp cho chatbot
Với một dự án mới, nên ưu tiên Responses API thay vì xây kiến trúc mới dựa hoàn toàn trên Chat Completions. xAI mô tả Responses API là giao diện chính cho text generation, reasoning và tool use, trong khi Chat Completions được xem là API kế thừa.
Ví dụ dưới đây sử dụng model grok-4.7. Tài liệu xAI hiện liệt kê đây là model flagship cho code và nhiều tác vụ khác, đồng thời hỗ trợ các khả năng như function calling, web search và X search.
Tuy nhiên, không nên hiểu rằng mọi chatbot đều phải cố định một model vĩnh viễn. Khi triển khai thật, nên đặt tên model trong cấu hình để có thể thay đổi mà không phải sửa nhiều vị trí trong mã nguồn.
<?php
return [
'api_key' => getenv('XAI_API_KEY'),
'model' => 'grok-4.7',
'api_url' => 'https://api.x.ai/v1/responses'
];
Cách tổ chức này có lợi khi cần chuyển model, thay đổi endpoint hoặc xây nhiều chatbot với cấu hình khác nhau.
Thiết kế prompt để chatbot trả lời đúng mục đích
Một chatbot tốt không chỉ phụ thuộc vào model. Prompt quyết định rất lớn đến cách chatbot giao tiếp, phạm vi thông tin được phép trả lời và phong cách phản hồi.
Ví dụ, nếu xây chatbot tư vấn dịch vụ website cho Web Mới, không nên chỉ gửi câu hỏi của khách hàng như:
Khách hàng: Tôi muốn làm website bán hàng
Thay vào đó, backend có thể xác định vai trò của chatbot trước:
Vai trò:
Bạn là nhân viên tư vấn website.
Mục tiêu:
- Hiểu nhu cầu của khách hàng.
- Trả lời ngắn gọn, dễ hiểu.
- Không tự bịa thông tin về dịch vụ.
- Nếu chưa đủ dữ liệu để tư vấn, hãy hỏi lại.
- Không khẳng định giá hoặc tính năng nếu dữ liệu hệ thống chưa cung cấp.
Phong cách:
- Lịch sự.
- Tự nhiên.
- Ưu tiên câu trả lời trực tiếp.
- Không lặp lại câu hỏi của khách hàng một cách máy móc.
Sau đó mới truyền câu hỏi thực tế của người dùng vào ngữ cảnh phù hợp.
Điểm cần tránh là nhồi toàn bộ nội dung website vào mỗi request. Cách làm này khiến request dài, tăng chi phí và có thể làm chatbot khó tập trung vào thông tin quan trọng.
Gửi yêu cầu đầu tiên đến Grok API bằng PHP
Sau khi có API key và cấu hình model, bước tiếp theo là tạo endpoint PHP làm nhiệm vụ nhận câu hỏi rồi gọi API.
Với Responses API, request được gửi tới endpoint /v1/responses. Tài liệu xAI hiện sử dụng trường input để truyền nội dung đầu vào và trường model để xác định model xử lý.
Một request PHP tối giản có thể triển khai như sau:
<?php
header('Content-Type: application/json; charset=utf-8');
$apiKey = getenv('XAI_API_KEY');
$input = trim($_POST['message'] ?? '');
if ($input === '') {
http_response_code(400);
echo json_encode([
'error' => 'Vui lòng nhập nội dung cần hỏi.'
], JSON_UNESCAPED_UNICODE);
exit;
}
$data = [
'model' => 'grok-4.7',
'input' => $input
];
$ch = curl_init('https://api.x.ai/v1/responses');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey
],
CURLOPT_POSTFIELDS => json_encode($data, JSON_UNESCAPED_UNICODE)
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) {
curl_close($ch);
http_response_code(500);
echo json_encode([
'error' => 'Không thể kết nối đến dịch vụ AI.'
], JSON_UNESCAPED_UNICODE);
exit;
}
curl_close($ch);
http_response_code($httpCode);
echo $response;
Đây mới là phần lõi của kết nối API. Một chatbot hoàn chỉnh vẫn cần xử lý kết quả trả về, quản lý hội thoại, xác thực người dùng nếu cần và kiểm soát lỗi trước khi đưa vào môi trường thực tế.
Không nên để trình duyệt gọi trực tiếp API
Một lỗi kiến trúc phổ biến là viết JavaScript chứa API key rồi gửi request trực tiếp từ trình duyệt. Cách này có thể khiến khóa bị lộ thông qua mã nguồn trang, Developer Tools hoặc các request mà trình duyệt thực hiện.
Ví dụ không nên triển khai theo kiểu:
const apiKey = "API_KEY_THAT";
fetch("https://api.x.ai/v1/responses", {
method: "POST",
headers: {
"Authorization": "Bearer " + apiKey,
"Content-Type": "application/json"
}
});
Thay vào đó, JavaScript chỉ giao tiếp với endpoint của chính website:
fetch("/api/chat.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams({
message: message
})
});
Như vậy, API key chỉ tồn tại ở backend. Đây là ranh giới quan trọng giữa phần giao diện mà người dùng có thể kiểm soát và phần bí mật mà website cần bảo vệ.
Đưa lịch sử hội thoại vào mỗi lần gọi API
Nếu chỉ gửi câu hỏi mới nhất, chatbot sẽ không thực sự hiểu được mạch hội thoại. Người dùng có thể hỏi “Giá bao nhiêu?”, sau khi trước đó đã nói về một loại website cụ thể. Nếu request thứ hai không có ngữ cảnh của request trước, model không biết từ “giá” đang đề cập đến vấn đề nào.
Vì vậy, chatbot cần duy trì lịch sử hội thoại và gửi phần ngữ cảnh cần thiết trong các request tiếp theo. Với ứng dụng đơn giản, lịch sử có thể được lưu trong session PHP. Với hệ thống có nhiều người dùng hoặc cần đồng bộ trên nhiều thiết bị, nên lưu vào cơ sở dữ liệu.
Một cấu trúc lịch sử đơn giản có thể gồm vai trò và nội dung:
[
[
'role' => 'user',
'content' => 'Tôi muốn làm website bán hàng'
],
[
'role' => 'assistant',
'content' => 'Bạn có thể cho tôi biết sản phẩm và quy mô dự kiến không?'
],
[
'role' => 'user',
'content' => 'Tôi bán khoảng 100 sản phẩm'
]
]
Khi người dùng gửi câu hỏi mới, backend lấy lịch sử phù hợp, thêm câu hỏi hiện tại rồi gửi toàn bộ ngữ cảnh đến model.
Tuy nhiên, không nên lưu và gửi lịch sử vô hạn. Một cuộc trò chuyện kéo dài hàng trăm lượt có thể khiến request ngày càng lớn, làm tăng lượng token sử dụng và khiến chatbot phải xử lý nhiều thông tin không còn cần thiết.
Quản lý bộ nhớ hội thoại để chatbot không bị quá tải
Đây là một trong những phần quan trọng nhất khi chuyển chatbot từ bản thử nghiệm sang hệ thống thực tế. Lịch sử hội thoại cần được xem như một tài nguyên có giới hạn thay vì một kho dữ liệu gửi nguyên vẹn trong mọi request.
Có thể áp dụng một số chiến lược:
- Chỉ giữ lại một số lượt trò chuyện gần nhất.
- Tóm tắt các đoạn hội thoại cũ.
- Loại bỏ những câu hỏi không còn liên quan.
- Lưu dữ liệu dài hạn trong cơ sở dữ liệu nhưng chỉ đưa phần cần thiết vào prompt.
- Tách thông tin hồ sơ người dùng khỏi lịch sử trò chuyện.
Ví dụ, thay vì gửi 50 lượt hội thoại cũ, backend có thể giữ lại vài lượt gần nhất và một bản tóm tắt:
Thông tin cuộc trò chuyện trước:
- Khách hàng đang tìm giải pháp website bán hàng.
- Khách hàng dự kiến có khoảng 100 sản phẩm.
- Khách hàng muốn website có thể quản lý sản phẩm và đơn hàng.
Các tin nhắn gần nhất:
- Khách hàng: Tôi muốn biết chi phí.
- Tư vấn viên: Bạn muốn ưu tiên những tính năng nào?
- Khách hàng: Tôi cần quản lý đơn hàng.
Cách này giúp model nhận được phần thông tin có giá trị thay vì phải đọc lại toàn bộ lịch sử.
Trích xuất câu trả lời từ phản hồi API
Không nên mặc định rằng response từ API chỉ là một chuỗi văn bản nằm ở một vị trí cố định. Cấu trúc phản hồi cần được xử lý dựa trên API mà ứng dụng đang sử dụng và nên kiểm tra trường dữ liệu trước khi hiển thị.
Backend có thể giải mã JSON rồi tìm nội dung phù hợp trước khi trả về cho frontend.
<?php
$result = json_decode($response, true);
if (!is_array($result)) {
http_response_code(502);
echo json_encode([
'error' => 'Phản hồi từ máy chủ AI không hợp lệ.'
], JSON_UNESCAPED_UNICODE);
exit;
}
echo json_encode([
'success' => true,
'data' => $result
], JSON_UNESCAPED_UNICODE);
Ở lớp giao diện, không nên đưa toàn bộ response thô vào màn hình. Backend nên chuẩn hóa dữ liệu thành một cấu trúc riêng của website. Nhờ vậy, nếu xAI thay đổi cách tổ chức response hoặc website chuyển sang một model khác, phần giao diện không cần phụ thuộc trực tiếp vào cấu trúc nội bộ của API.
Xử lý lỗi khi chatbot không nhận được câu trả lời
API có thể thất bại vì nhiều nguyên nhân: API key không hợp lệ, tài khoản không có quyền sử dụng model, request sai cấu trúc, vượt giới hạn, lỗi mạng hoặc dịch vụ tạm thời gặp vấn đề.
Do đó, chatbot không nên hiển thị nguyên văn thông báo lỗi kỹ thuật cho khách hàng. Backend cần phân biệt lỗi dành cho người dùng và thông tin dành cho log quản trị.
Ví dụ:
<?php
if ($httpCode < 200 || $httpCode >= 300) {
error_log('Grok API error: ' . $response);
http_response_code(502);
echo json_encode([
'success' => false,
'error' => 'Hệ thống AI đang tạm thời không phản hồi. Vui lòng thử lại sau.'
], JSON_UNESCAPED_UNICODE);
exit;
}
Người dùng chỉ cần nhận được thông báo dễ hiểu. Chi tiết kỹ thuật nên nằm trong log để quản trị viên kiểm tra.
Tạo giao diện chat bằng JavaScript
Sau khi backend hoạt động, frontend có thể gửi tin nhắn bằng fetch mà không cần tải lại toàn bộ trang.
Phần HTML có thể tạo vùng hiển thị cuộc trò chuyện và ô nhập nội dung:
<div id="chat-box"></div>
<input type="text" id="message" placeholder="Nhập câu hỏi...">
<button type="button" id="send-button">Gửi</button>
JavaScript chịu trách nhiệm lấy nội dung, gửi đến backend và hiển thị kết quả:
const input = document.getElementById('message');
const button = document.getElementById('send-button');
const chatBox = document.getElementById('chat-box');
button.addEventListener('click', async () => {
const message = input.value.trim();
if (!message) {
return;
}
chatBox.innerHTML += '<p><strong>Bạn:</strong> ' +
escapeHtml(message) + '</p>';
input.value = '';
button.disabled = true;
try {
const response = await fetch('/api/chat.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
message: message
})
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new Error(data.error || 'Không thể nhận phản hồi.');
}
chatBox.innerHTML += '<p><strong>Grok:</strong> ' +
escapeHtml(data.message) + '</p>';
} catch (error) {
chatBox.innerHTML += '<p>' +
escapeHtml(error.message) +
'</p>';
} finally {
button.disabled = false;
input.focus();
}
});
function escapeHtml(value) {
const div = document.createElement('div');
div.textContent = value;
return div.innerHTML;
}
Hàm escapeHtml() có vai trò quan trọng. Nội dung do người dùng nhập không nên được đưa thẳng vào HTML bằng innerHTML, bởi dữ liệu đầu vào có thể chứa mã HTML hoặc JavaScript độc hại.
Bảo vệ chatbot khỏi nội dung đầu vào nguy hiểm
Việc gọi AI API không có nghĩa là backend có thể tin tưởng mọi dữ liệu gửi từ trình duyệt. Người dùng có thể tự tạo request bằng công cụ khác thay vì sử dụng giao diện chatbot.
Backend nên kiểm tra tối thiểu:
- Request có đúng phương thức HTTP hay không.
- Nội dung có tồn tại hay không.
- Độ dài tin nhắn có vượt giới hạn cho phép hay không.
- Dữ liệu có đúng encoding hay không.
- Người dùng có được phép sử dụng chatbot hay không.
- Số lần gọi trong một khoảng thời gian có vượt giới hạn hay không.
Ví dụ giới hạn độ dài câu hỏi:
<?php
$input = trim($_POST['message'] ?? '');
if ($input === '') {
http_response_code(400);
echo json_encode([
'error' => 'Nội dung không được để trống.'
], JSON_UNESCAPED_UNICODE);
exit;
}
if (mb_strlen($input, 'UTF-8') > 4000) {
http_response_code(400);
echo json_encode([
'error' => 'Nội dung gửi lên quá dài.'
], JSON_UNESCAPED_UNICODE);
exit;
}
Con số giới hạn cụ thể nên được điều chỉnh theo mục đích của chatbot. Không nên đặt giới hạn quá thấp khiến người dùng không thể mô tả vấn đề, nhưng cũng không nên cho phép request tùy ý vì có thể ảnh hưởng đến chi phí và tài nguyên máy chủ.
Giới hạn số lần gọi để kiểm soát chi phí
Một chatbot công khai trên website có thể bị gọi liên tục nếu không có cơ chế kiểm soát. Một người dùng bình thường có thể gửi nhiều câu hỏi, nhưng bot tự động hoặc request giả mạo có thể tạo ra lượng request lớn hơn rất nhiều.
Vì vậy, backend nên áp dụng rate limit theo IP, tài khoản, session hoặc kết hợp nhiều yếu tố.
Ví dụ, có thể quy định một session chỉ được gửi một số lượng request nhất định trong một khoảng thời gian. Khi hệ thống lớn hơn, nên đưa cơ chế rate limit vào tầng chuyên dụng thay vì chỉ dựa vào PHP session.
Ngoài giới hạn số request, cũng nên theo dõi:
- Số token sử dụng.
- Số request theo từng người dùng.
- Model được gọi.
- Thời gian phản hồi.
- Tỷ lệ request lỗi.
- Chi phí phát sinh theo ngày hoặc theo tháng.
Những số liệu này giúp phát hiện sớm trường hợp chatbot bị lạm dụng hoặc một thay đổi trong prompt khiến lượng dữ liệu gửi đến API tăng bất thường.
Streaming giúp chatbot phản hồi tự nhiên hơn
Ở chatbot thông thường, người dùng phải chờ model xử lý xong rồi mới nhìn thấy toàn bộ câu trả lời. Với câu trả lời dài, cảm giác chờ đợi có thể khá rõ rệt.
Streaming giải quyết vấn đề này bằng cách cho phép dữ liệu được truyền dần về trong quá trình model tạo nội dung. xAI cung cấp tùy chọn streaming cho các API inference, giúp ứng dụng có thể xử lý dữ liệu theo từng phần thay vì chờ toàn bộ response hoàn tất.
Luồng xử lý khi đó sẽ gần giống:
Người dùng gửi câu hỏi
↓
Backend gửi request
↓
Grok bắt đầu tạo nội dung
↓
Backend nhận từng phần dữ liệu
↓
Frontend hiển thị dần
↓
Kết thúc câu trả lời
Streaming đặc biệt phù hợp với chatbot tư vấn, trợ lý lập trình và các hệ thống thường tạo câu trả lời dài. Tuy nhiên, cách triển khai phức tạp hơn request thông thường vì backend và frontend phải xử lý dữ liệu theo luồng, đồng thời cần có cơ chế nhận biết thời điểm response kết thúc.
Kết nối chatbot với dữ liệu riêng của website
Một chatbot chỉ sử dụng kiến thức sẵn có của model sẽ phù hợp với các câu hỏi chung, nhưng chưa đủ tốt nếu mục tiêu là tư vấn thông tin riêng của một doanh nghiệp. Chẳng hạn, chatbot của Web Mới có thể cần biết dịch vụ đang cung cấp, quy trình làm website, các tính năng hỗ trợ hoặc thông tin liên hệ.
Không nên đưa toàn bộ dữ liệu doanh nghiệp vào prompt ở mọi request. Cách phù hợp hơn là xác định thông tin nào thực sự liên quan đến câu hỏi rồi mới cung cấp cho model.
Với hệ thống nhỏ, dữ liệu có thể được tổ chức thành các nhóm:
- Thông tin doanh nghiệp.
- Dịch vụ và sản phẩm.
- Bảng thông tin hoặc chính sách.
- Câu hỏi thường gặp.
- Quy trình làm việc.
- Thông tin liên hệ.
Ví dụ, khi khách hàng hỏi về dịch vụ thiết kế website, backend có thể lấy phần dữ liệu liên quan rồi đưa vào ngữ cảnh:
Thông tin được phép sử dụng:
- Doanh nghiệp cung cấp dịch vụ lập trình website theo yêu cầu.
- Website có thể được phát triển bằng PHP theo nhu cầu thực tế.
- Chi phí cần được tư vấn dựa trên phạm vi chức năng.
- Không tự đưa ra báo giá ngoài dữ liệu được cung cấp.
Câu hỏi khách hàng:
Tôi muốn làm website bán hàng thì cần những chức năng nào?
Cách làm này giúp chatbot trả lời sát dữ liệu của doanh nghiệp hơn và giảm nguy cơ tự suy diễn những thông tin mà hệ thống không cung cấp.
Khi nào nên dùng tìm kiếm dữ liệu thay vì nhồi prompt?
Nếu dữ liệu của website ngày càng lớn, việc đưa trực tiếp hàng nghìn đoạn văn bản vào request sẽ không còn hiệu quả. Khi đó, hệ thống nên có một lớp tìm kiếm để xác định những tài liệu liên quan trước khi gửi chúng cho model.
Ví dụ, website có 5.000 bài viết. Người dùng hỏi về một chính sách cụ thể. Backend không cần gửi cả 5.000 bài viết cho Grok. Thay vào đó, hệ thống có thể:
- Nhận câu hỏi của người dùng.
- Tìm kiếm những tài liệu có liên quan.
- Chọn một số kết quả phù hợp nhất.
- Đưa nội dung đó vào ngữ cảnh.
- Gửi câu hỏi cùng ngữ cảnh cho Grok.
Đây là hướng tiếp cận thường được gọi là Retrieval-Augmented Generation, hay RAG. Nó đặc biệt hữu ích khi chatbot cần trả lời dựa trên dữ liệu riêng thường xuyên thay đổi.
Với website PHP nhỏ, chưa nhất thiết phải xây hệ thống RAG ngay từ đầu. Có thể bắt đầu bằng MySQL hoặc công cụ tìm kiếm hiện có, sau đó nâng cấp khi lượng dữ liệu và nhu cầu sử dụng tăng lên.
Cho chatbot sử dụng công cụ khi cần thiết
Chatbot có thể được mở rộng từ việc chỉ sinh văn bản sang thực hiện hành động thông qua tool hoặc function calling. Đây là hướng phù hợp khi chatbot cần tương tác với dữ liệu hoặc chức năng của chính website.
Ví dụ, thay vì yêu cầu Grok tự đoán giá dịch vụ, chatbot có thể được phép gọi một hàm lấy thông tin từ cơ sở dữ liệu. Tương tự, một chatbot bán hàng có thể gọi chức năng kiểm tra sản phẩm, một chatbot đặt lịch có thể kiểm tra lịch trống và một trợ lý nội bộ có thể truy vấn dữ liệu doanh nghiệp.
Luồng xử lý khi sử dụng công cụ sẽ phức tạp hơn:
Người dùng
↓
Gửi câu hỏi
↓
Grok phân tích yêu cầu
↓
Yêu cầu sử dụng công cụ
↓
Backend kiểm tra quyền
↓
Backend thực thi hàm
↓
Kết quả được trả lại cho Grok
↓
Grok tạo câu trả lời cuối cùng
↓
Người dùng nhận kết quả
Điểm rất quan trọng là backend phải kiểm soát việc thực thi. Không nên cho model toàn quyền chạy bất kỳ câu lệnh nào trên máy chủ hoặc cơ sở dữ liệu.
Mỗi tool nên có phạm vi rõ ràng. Ví dụ, một hàm getProduct() chỉ được phép lấy dữ liệu sản phẩm và không có quyền thực hiện thao tác xóa hoặc cập nhật dữ liệu.
Không cho AI toàn quyền truy cập cơ sở dữ liệu
Đây là nguyên tắc bảo mật cần đặc biệt chú ý khi xây chatbot có khả năng sử dụng dữ liệu nội bộ.
Không nên thiết kế chatbot theo kiểu nhận câu hỏi rồi chuyển nguyên văn yêu cầu đó thành SQL để thực thi. Nếu không có lớp kiểm soát, người dùng có thể tìm cách khiến hệ thống thực hiện truy vấn ngoài phạm vi dự kiến.
Thay vào đó, hãy tạo các hàm có mục đích cụ thể:
getProductById()
searchProducts()
getOrderStatus()
getCustomerProfile()
Backend tự quyết định cách những hàm này truy vấn dữ liệu. Model chỉ có thể yêu cầu sử dụng một chức năng nằm trong danh sách được cho phép.
Cách tiếp cận này làm tăng một chút công sức lập trình nhưng giúp ranh giới bảo mật rõ ràng hơn rất nhiều.
Thiết kế database để lưu cuộc trò chuyện
Nếu chatbot cần lưu lịch sử cho từng tài khoản, nên sử dụng cơ sở dữ liệu thay vì phụ thuộc hoàn toàn vào session.
Một thiết kế đơn giản có thể gồm hai bảng:
chat_conversations
-------------------
id
user_id
title
created_at
updated_at
chat_messages
-------------
id
conversation_id
role
content
created_at
Bảng chat_conversations đại diện cho một cuộc trò chuyện. Bảng chat_messages lưu từng tin nhắn thuộc cuộc trò chuyện đó.
Quan hệ giữa hai bảng:
chat_conversations
|
| 1
|
| n
↓
chat_messages
Cấu trúc này cho phép một người dùng có nhiều cuộc trò chuyện và mỗi cuộc trò chuyện có nhiều tin nhắn.
Khi người dùng mở lại cuộc trò chuyện, backend có thể lấy các tin nhắn cần thiết theo conversation_id. Đây là cách phù hợp hơn so với việc lưu toàn bộ lịch sử trong cookie hoặc gửi dữ liệu trực tiếp từ trình duyệt.
Kiểm soát quyền truy cập lịch sử
Nếu chatbot có đăng nhập, việc kiểm tra quyền truy cập cuộc trò chuyện phải được thực hiện ở backend. Không được coi conversation_id do trình duyệt gửi lên là bằng chứng cho thấy người dùng có quyền xem cuộc trò chuyện đó.
Ví dụ, nếu request chứa:
conversation_id=125
backend phải kiểm tra cuộc trò chuyện 125 có thực sự thuộc về tài khoản hiện tại hay không.
Không nên chỉ thực hiện truy vấn theo kiểu:
SELECT * FROM chat_messages
WHERE conversation_id = 125
Mà nên gắn cuộc trò chuyện với người dùng đang đăng nhập và kiểm tra quyền trước khi lấy dữ liệu.
SELECT m.*
FROM chat_messages AS m
INNER JOIN chat_conversations AS c
ON c.id = m.conversation_id
WHERE c.id = ?
AND c.user_id = ?
Nguyên tắc này rất quan trọng vì chatbot có thể chứa dữ liệu riêng tư, thông tin khách hàng hoặc nội dung nội bộ.
Hiển thị Markdown mà không tạo lỗ hổng XSS
Câu trả lời từ AI thường có thể chứa tiêu đề, danh sách, đoạn mã hoặc định dạng Markdown. Nếu muốn giao diện chatbot hiển thị đẹp, website có thể chuyển Markdown thành HTML.
Tuy nhiên, không nên lấy nội dung AI trả về rồi đưa thẳng vào innerHTML. Nội dung sinh ra từ model vẫn phải được xem là dữ liệu không đáng tin cậy.
Nếu sử dụng thư viện chuyển Markdown sang HTML, cần có thêm bước sanitize HTML để loại bỏ những nội dung nguy hiểm trước khi hiển thị.
Đặc biệt cần kiểm soát các nội dung như:
- Thẻ script.
- Thuộc tính sự kiện JavaScript.
- URL nguy hiểm.
- HTML nhúng không cần thiết.
- Các đoạn mã có khả năng thực thi trong trình duyệt.
Nếu chatbot chỉ cần trả lời văn bản thuần túy, cách an toàn và đơn giản nhất là hiển thị nội dung bằng textContent thay vì cho phép HTML trực tiếp.
Thêm trạng thái đang xử lý cho giao diện
Chatbot không nên khiến người dùng nghĩ rằng nút gửi không hoạt động trong lúc API đang xử lý. Giao diện nên có trạng thái rõ ràng như “Đang trả lời...” và vô hiệu hóa nút gửi trong khoảng thời gian phù hợp.
Ví dụ:
button.disabled = true;
const loading = document.createElement('p');
loading.textContent = 'Grok đang trả lời...';
chatBox.appendChild(loading);
Khi response hoàn tất, trạng thái chờ được thay bằng câu trả lời thật.
Nếu triển khai streaming, trạng thái này có thể được thay bằng hiệu ứng hiển thị nội dung từng phần. Người dùng sẽ cảm nhận chatbot phản hồi nhanh hơn ngay cả khi tổng thời gian model hoàn thành câu trả lời không thay đổi.
Những lỗi thường gặp khi xây chatbot bằng Grok API
Có một số lỗi xuất hiện khá thường xuyên ở các chatbot tự xây dựng.
- Đưa API key vào JavaScript: khóa có thể bị lộ cho người truy cập.
- Không giới hạn request: chatbot có thể bị spam và làm chi phí tăng nhanh.
- Gửi toàn bộ lịch sử mãi mãi: request ngày càng lớn và chứa nhiều dữ liệu không cần thiết.
- Tin tưởng dữ liệu từ frontend: người dùng có thể tự sửa request gửi đến backend.
- Hiển thị HTML từ AI trực tiếp: có thể tạo rủi ro XSS nếu không sanitize.
- Không ghi log lỗi: khi API gặp sự cố sẽ rất khó tìm nguyên nhân.
- Cho model quyền quá rộng: chatbot có thể thực hiện những hành động ngoài phạm vi dự kiến.
- Hard-code model ở nhiều vị trí: khó thay đổi cấu hình khi model hoặc yêu cầu hệ thống thay đổi.
Những lỗi này không nhất thiết xuất hiện khi chatbot mới chạy thử, nhưng có thể trở thành vấn đề lớn khi website có lượng người dùng thực tế.
Quy trình hoàn thiện chatbot trước khi đưa lên website
Một chatbot nên được kiểm tra theo từng lớp thay vì chỉ thử vài câu hỏi rồi đưa vào sử dụng.
- Kiểm tra API key và quyền truy cập model.
- Kiểm tra request cơ bản từ backend.
- Kiểm tra câu trả lời khi nội dung đầu vào rỗng.
- Kiểm tra câu hỏi rất dài.
- Kiểm tra nhiều lượt hội thoại liên tiếp.
- Kiểm tra lỗi mạng và API timeout.
- Kiểm tra rate limit.
- Kiểm tra quyền truy cập lịch sử.
- Kiểm tra nội dung HTML và JavaScript trong câu hỏi.
- Kiểm tra chi phí và số lượng request.
- Kiểm tra giao diện trên điện thoại.
- Kiểm tra trường hợp nhiều người dùng truy cập đồng thời.
Nếu chatbot có tool hoặc kết nối dữ liệu nội bộ, cần bổ sung các bài kiểm thử riêng cho từng chức năng. Đặc biệt phải kiểm tra những tình huống người dùng cố tình yêu cầu chatbot thực hiện hành động ngoài phạm vi quyền hạn.
Cách mở rộng chatbot sau phiên bản cơ bản
Sau khi phiên bản đầu tiên hoạt động ổn định, chatbot có thể được phát triển thành một hệ thống trợ lý hoàn chỉnh thay vì chỉ là hộp hỏi đáp.
Một số hướng mở rộng đáng cân nhắc gồm:
- Đăng nhập và đồng bộ lịch sử giữa các thiết bị.
- Streaming câu trả lời.
- Tìm kiếm dữ liệu riêng của website.
- Function calling để truy cập các chức năng được cấp phép.
- Upload tài liệu để chatbot hỗ trợ phân tích.
- Phân quyền theo tài khoản.
- Thống kê số lượt sử dụng.
- Giới hạn request theo người dùng.
- Trang quản trị theo dõi hội thoại.
- Hệ thống đánh giá câu trả lời.
Không nên triển khai tất cả ngay từ phiên bản đầu tiên. Một chatbot tốt nên được xây theo từng lớp: trước hết đảm bảo request và response hoạt động ổn định, sau đó mới bổ sung bộ nhớ, dữ liệu riêng, công cụ và các tính năng nâng cao.
Kết luận
Xây chatbot bằng Grok API với PHP không quá phức tạp nếu chia hệ thống thành những phần rõ ràng. Backend giữ vai trò trung gian giữa người dùng và Grok, API key được bảo vệ ở máy chủ, lịch sử hội thoại được quản lý có giới hạn và dữ liệu đầu vào được kiểm soát trước khi gửi đi.
Với một chatbot cơ bản, chỉ cần bắt đầu từ giao diện nhập câu hỏi, endpoint PHP và request đến Responses API. Khi nhu cầu tăng lên, có thể tiếp tục bổ sung lưu lịch sử, streaming, tìm kiếm dữ liệu riêng, function calling và hệ thống phân quyền.
Quan trọng nhất không phải là làm cho chatbot có thật nhiều tính năng ngay từ đầu mà là xây đúng kiến trúc. Khi lớp backend được thiết kế tốt, website có thể thay đổi model, bổ sung dữ liệu hoặc thêm công cụ mà không phải viết lại toàn bộ hệ thống.
- 0 Bình luận
Email, Điện thoại của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *