Viết và kiểm tra câu SQL từ câu hỏi tiếng Việt bằng AI
Playbook cho analyst và BI developer: dùng AI để tạo bản nháp SQL từ câu hỏi tự nhiên, kiểm tra schema, join, metric và quyền truy cập trước khi đưa kết quả vào dashboard nội bộ.
Một câu hỏi như: “Cho tôi doanh thu quý gần nhất theo khu vực, loại trừ đơn hoàn” nghe đơn giản. Nhưng để trả lời đúng, bạn vẫn phải làm rõ định nghĩa doanh thu, trạng thái hoàn, bảng nguồn, cột ngày và cách join.
AI có thể rút ngắn bước viết truy vấn đầu tiên. Tuy nhiên, với analyst và BI developer, SQL do AI tạo ra nên được xem là bản nháp để kiểm tra — không phải kết quả mặc định đúng.
Vanna 2.0 là một hướng triển khai cho luồng hỏi dữ liệu bằng ngôn ngữ tự nhiên: người dùng đặt câu hỏi, hệ thống gọi công cụ chạy SQL rồi trả về bảng, biểu đồ và phần tóm tắt. Nếu triển khai trong portal nội bộ, Vanna cũng cung cấp giao diện chat nhúng, cơ chế nhận diện người dùng và các khả năng liên quan đến phân quyền.
Vanna trả về những gì?
Theo README của Vanna, một câu hỏi có thể trả về:
Tiến độ xử lý theo thời gian thực.
Khối mã SQL; mặc định chỉ hiển thị cho người dùng có vai trò admin.
Bảng dữ liệu tương tác.
Biểu đồ Plotly.
Phần tóm tắt bằng ngôn ngữ tự nhiên.
Luồng minh họa trong tài liệu là: người dùng hỏi qua thành phần <vanna-chat>, ứng dụng nhận request kèm thông tin xác thực, agent gọi công cụ SQL, công cụ áp dụng các kiểm soát theo người dùng, rồi giao diện nhận bảng, biểu đồ và phần diễn giải.
Với đội BI, điều này có thể phù hợp cho các câu hỏi lặp lại trong môi trường tự phục vụ. Nhưng README không trình bày chi tiết cách một dự án của bạn cung cấp định nghĩa metric, quan hệ bảng hoặc quy tắc nghiệp vụ cho agent. Vì vậy, đừng giả định rằng chỉ cần kết nối database là hệ thống đã hiểu đúng logic dashboard hiện có.
Bắt đầu bằng ngữ cảnh BI rõ ràng
Hãy chuẩn bị ngữ cảnh giống như khi giao việc cho một analyst mới: schema, định nghĩa chỉ số, quy tắc loại trừ và quan hệ bảng đã được xác minh.
Ví dụ, bắt đầu với một bảng mẫu nhỏ:
| orderid | region | status | revenue |
|---|---|---|---:|
| a | north | paid | 5 |
| b | south | refunded | 3 |
| c | north | paid | 2 |
Trước khi yêu cầu tạo SQL, cần làm rõ:
revenue là doanh thu trước hay sau hoàn tiền?
Có loại trừ status = refunded không?
Một orderid có thể xuất hiện ở nhiều dòng không?
Kỳ báo cáo dựa trên ngày tạo đơn, thanh toán hay hoàn tất?
region là thuộc tính của đơn hàng, khách hàng hay nhân viên bán?
Một prompt thực dụng cho việc review SQL:
text
Bạn là SQL reviewer cho báo cáo BI.
Database: <hệ quản trị CSDL>
Mục tiêu: <câu hỏi nghiệp vụ>
Schema:
<danh sách bảng, cột, kiểu dữ liệu>
Định nghĩa metric:
<metric>: <quy tắc tính>
Loại trừ: <trạng thái hoặc điều kiện>
Quan hệ bảng đã xác minh:
<bảng A> nối <bảng B> qua <khóa>
Cardinality: <một-một / một-nhiều>
Hãy trả về:
SQL chỉ đọc dữ liệu.
Giải thích từng join, điều kiện ngày và cách xử lý NULL.
Danh sách giả định cần tôi xác nhận.
Không dùng cột nào ngoài schema đã cung cấp.
Mục tiêu không phải là viết prompt thật dài. Mục tiêu là buộc hệ thống nêu rõ giả định để bạn kiểm tra trước khi truy vấn hoặc biểu đồ được chia sẻ rộng rãi.
Tích hợp thử với FastAPI và SQLite
README có ví dụ tích hợp Vanna vào ứng dụng FastAPI có sẵn. Ví dụ này dùng UserResolver để lấy danh tính từ request, đăng ký công cụ chạy SQL với SQLite, sau đó đăng ký các route chat.
python
from fastapi import FastAPI
from vanna import Agent
from vanna.servers.fastapi.routes import registerchatroutes
from vanna.servers.base import ChatHandler
from vanna.core.user import UserResolver, User, RequestContext
from vanna.integrations.anthropic import AnthropicLlmService
from vanna.tools import RunSqlTool
from vanna.integrations.sqlite import SqliteRunner
from vanna.core.registry import ToolRegistry
app = FastAPI()
class MyUserResolver(UserResolver):
async def resolveuser(self, requestcontext: RequestContext) -> User:
token = requestcontext.getheader('Authorization')
userdata = self.decodejwt(token)
return User(
id=userdata['id'],
email=userdata['email'],
groupmemberships=userdata['groups']
)
llm = AnthropicLlmService(model="claude-sonnet-4-5")
tools = ToolRegistry()
tools.register(RunSqlTool(sqlrunner=SqliteRunner("./data.db")))
agent = Agent(
llmservice=llm,
toolregistry=tools,
userresolver=MyUserResolver()
)
chathandler = ChatHandler(agent)
registerchatroutes(app, chathandler)
Ở frontend, thành phần chat có thể được nhúng như sau:
html
<vanna-chat sse-endpoint="/api/vanna/v2/chatsse"></vanna-chat>
Theo ví dụ trong README, route chat là POST /api/vanna/v2/chatsse. Khi người dùng hỏi một câu như “Hiển thị doanh thu theo khu vực”, giao diện có thể nhận luồng kết quả gồm bảng, biểu đồ và phần tóm tắt.
Nên thử trước với SQLite và dữ liệu mẫu. Ví dụ cấu hình trên cho thấy cách nối ứng dụng, LLM, công cụ SQL và danh tính người dùng; còn định nghĩa metric, phạm vi bảng được phép truy cập và quy tắc nghiệp vụ vẫn là phần đội dữ liệu cần chủ động thiết kế.
Đừng tin biểu đồ trước khi kiểm tra SQL
Biểu đồ đẹp không chứng minh logic đúng.
Với bảng mẫu ở trên, yêu cầu loại trừ đơn hoàn phải chỉ tính hai dòng có trạng thái paid. Đây là một đối chiếu nhỏ nhưng hiệu quả để phát hiện truy vấn bỏ sót điều kiện nghiệp vụ.
Dùng checklist này cho mọi SQL do AI đề xuất:
Đối chiếu schema
Bảng, cột, alias và kiểu dữ liệu có tồn tại không?
Kiểm tra join
Join có làm nhân bản dòng không? Đặc biệt kiểm tra các quan hệ một-nhiều.
Đối chiếu metric
Tổng doanh thu, số đơn và số khách có khớp báo cáo chuẩn hoặc truy vấn đối chứng không?
Kiểm tra NULL
NULL được loại, thay thế hay ảnh hưởng thế nào đến phép tính?
Kiểm tra thời gian
Filter dùng ngày tạo, ngày thanh toán hay ngày hoàn tất? Biên đầu-cuối kỳ cần được thống nhất.
Kiểm tra quyền
Chạy thử với người dùng thuộc các nhóm khác nhau để xác minh rule truy cập áp dụng đúng như thiết kế.
Vanna trong README được mô tả với công cụ chạy SQL. Nếu dùng kết quả để đối chiếu Power BI, không nên sao chép máy móc logic SQL sang DAX. Hãy kiểm tra thêm filter context, relationship và các measure hiện có trong mô hình Power BI.
Phân quyền là một phần của thiết kế
Vanna 2.0 nhấn mạnh kiến trúc user-aware: danh tính người dùng đi qua prompt hệ thống, quá trình gọi công cụ và bước lọc SQL. README cũng nêu các khả năng như row-level security, audit log và rate limiting theo người dùng.
Khi đưa vào ứng dụng nội bộ, đội dữ liệu vẫn nên xác định rõ:
Tài khoản chạy truy vấn được cấp những quyền nào.
Những bảng hoặc view nào có thể được truy cập.
Nhóm người dùng nào được xem SQL gốc.
Audit log được kiểm tra và lưu giữ theo quy trình nào.
Dữ liệu thử nghiệm có thể thay bằng schema và mẫu dữ liệu đã được kiểm soát hay không.
Đặc biệt, phần phân quyền chỉ đáng tin khi danh tính người dùng, group membership và rule dữ liệu của chính hệ thống được cấu hình, kiểm thử đầy đủ.
Checklist trước khi đưa vào dashboard nội bộ
[ ] Có schema, định nghĩa metric và quy tắc ngày rõ ràng.
[ ] Có dữ liệu mẫu để kiểm tra các câu hỏi phổ biến.
[ ] Có truy vấn hoặc dashboard chuẩn để đối chiếu tổng số.
[ ] Có test cho join, NULL, trạng thái loại trừ và quyền theo nhóm.
[ ] Có giới hạn phạm vi dữ liệu mà công cụ SQL được truy cập.
[ ] Có quy trình xem audit log và review SQL khi cần.
[ ] Người dùng hiểu rằng câu trả lời nhanh không đồng nghĩa với kết quả luôn đúng.
Vanna có thể rút ngắn đường từ câu hỏi tiếng Việt đến bản nháp truy vấn, bảng kết quả và phần giải thích. Giá trị bền vững vẫn đến từ năng lực BI cốt lõi: định nghĩa metric rõ ràng, kiểm tra logic truy vấn và bảo vệ dữ liệu trước khi con số đi vào quyết định.