1. Tạo bảng Vector và định nghĩa chỉ mục trong Databend
Bước đầu tiên là chuẩn bị cấu trúc lưu trữ dữ liệu vector trong Databend. Databend hỗ trợ lưu trữ vector thông qua kiểu dữ liệu VARCHAR (lưu dưới dạng chuỗi JSON của mảng số) hoặc kiểu ARRAY kết hợp với index đặc biệt. Ở đây chúng ta sử dụng cách tiếp cận tối ưu cho Vector Search: lưu vector dưới dạng chuỗi và tạo chỉ mục HNSW.
Trước tiên, mở kết nối CLI hoặc DBCl để thực thi lệnh SQL tạo bảng.
Thực thi lệnh tạo bảng với cột nội dung văn bản và cột vector tương ứng.
CREATE TABLE IF NOT EXISTS knowledge_base (
id INT AUTO_INCREMENT,
content TEXT,
embedding VARCHAR,
metadata JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Kết quả mong đợi: Hệ thống trả về Query executed successfully. Bảng knowledge_base đã được tạo với cấu trúc hỗ trợ lưu trữ vector dạng chuỗi.
Tiếp theo, tạo chỉ mục Vector Search (HNSW - Hierarchical Navigable Small World) trên cột embedding. Chỉ mục này tối ưu hóa việc tìm kiếm láng giềng gần nhất (Approximate Nearest Neighbor - ANN).
CREATE INDEX idx_knowledge_base_embedding ON knowledge_base (embedding) USING HNSW (distance_metric = 'L2');
Kết quả mong đợi: Chỉ mục được xây dựng thành công. Lưu ý, quá trình build index có thể mất vài giây tùy thuộc vào số lượng dữ liệu hiện có (bây giờ là 0).
Verify kết quả bằng cách kiểm tra thông tin bảng và chỉ mục.
DESCRIBE TABLE knowledge_base;
SHOW INDEXES FROM knowledge_base;
Kết quả mong đợi: DESCRIBE hiển thị cột embedding có kiểu VARCHAR. SHOW INDEXES hiển thị chỉ mục idx_knowledge_base_embedding với engine HNSW.
2. Cài đặt và cấu hình môi trường Embedding
Databend không tự động thực hiện tính toán vector (embedding) từ văn bản. Chúng ta cần một dịch vụ bên ngoài hoặc script để chuyển đổi văn bản thành vector số học. Trong phần này, chúng ta sẽ triển khai một service đơn giản sử dụng Python và thư viện onnxruntime để chạy mô hình all-MiniLM-L6-v2 (một mô hình sentence-transformer phổ biến, nhẹ và hiệu quả).
Đầu tiên, cập nhật hệ thống và cài đặt các công cụ cần thiết trên Ubuntu 24.04.
sudo apt update && sudo apt install -y python3-pip python3-venv git curl
Kết quả mong đợi: Các gói phần mềm được cài đặt thành công, không có lỗi.
Tạo thư mục làm việc và môi trường ảo Python để cô lập các thư viện.
mkdir -p /opt/ai-embedding-service
cd /opt/ai-embedding-service
python3 -m venv venv
source venv/bin/activate
Kết quả mong đợi: Bạn đang ở trong thư mục /opt/ai-embedding-service và prompt terminal có dấu (venv).
Cài đặt các thư viện cần thiết: transformers để tải mô hình, onnxruntime để chạy inference, và fastapi + uvicorn để tạo API.
pip install transformers torch onnxruntime-gpu fastapi uvicorn numpy
Kết quả mong đợi: Quá trình cài đặt thư viện hoàn tất. Nếu không có GPU, onnxruntime-gpu sẽ tự động fallback hoặc bạn có thể thay bằng onnxruntime (CPU).
Tải mô hình all-MiniLM-L6-v2 và chuyển đổi sang định dạng ONNX để tăng tốc độ inference. Mô hình này có vector dimension là 384.
git clone https://github.com/onnx/sentence-transformers-onnx.git
cd sentence-transformers-onnx
python3 -c "
from transformers import AutoModel, AutoTokenizer
import onnxruntime as ort
import torch
import os
model_name = 'sentence-transformers/all-MiniLM-L6-v2'
output_dir = '../models'
os.makedirs(output_dir, exist_ok=True)
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# Export to ONNX
tokenizer.save_pretrained(output_dir)
torch.onnx.export(
model,
torch.randn(1, 64, 384), # Dummy input
f'{output_dir}/model.onnx',
input_names=['input_ids', 'attention_mask'],
output_names=['last_hidden_state'],
dynamic_axes={'input_ids': {0, 1}, 'attention_mask': {0, 1}, 'last_hidden_state': {0, 1}},
opset_version=12
)
print('Model exported to ONNX successfully.')
"
Kết quả mong đợi: Thông báo Model exported to ONNX successfully. Thư mục models chứa file model.onnx.
Verify kết quả bằng cách kiểm tra file model.
ls -lh /opt/ai-embedding-service/models/
Kết quả mong đợi: File model.onnx tồn tại với kích thước khoảng 90MB.
3. Xây dựng Script chuyển đổi và API Embedding
Bây giờ chúng ta viết một script Python để tạo API nhận văn bản và trả về vector embedding dưới dạng chuỗi JSON (để tương thích với cột VARCHAR trong Databend).
Tạo file embedding_api.py tại /opt/ai-embedding-service/embedding_api.py với nội dung hoàn chỉnh sau:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import onnxruntime as ort
from transformers import AutoTokenizer
import numpy as np
import json
import os
app = FastAPI()
# Đường dẫn đến model
MODEL_PATH = "/opt/ai-embedding-service/models/model.onnx"
TOKENIZER_PATH = "/opt/ai-embedding-service/models"
# Khởi tạo Session ONNX và Tokenizer
try:
session = ort.InferenceSession(MODEL_PATH)
tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH)
print("Model loaded successfully.")
except Exception as e:
print(f"Failed to load model: {e}")
raise e
class TextRequest(BaseModel):
text: str
@app.post("/embed")
async def generate_embedding(request: TextRequest):
try:
# Tokenize input text
inputs = tokenizer(
[request.text],
padding=True,
truncation=True,
max_length=512,
return_tensors="np"
)
input_names = [x.name for x in session.get_inputs()]
output_names = [x.name for x in session.get_outputs()]
# Run inference
outputs = session.run(output_names, {k: v for k, v in inputs.items()})
# Extract last_hidden_state
embedding = outputs[0][0]
# Normalize vector (L2 normalization) để chuẩn hóa khoảng cách cosine
norm = np.linalg.norm(embedding)
if norm == 0:
raise HTTPException(status_code=400, detail="Zero norm vector")
embedding_normalized = embedding / norm
# Convert to list and then to JSON string for Databend VARCHAR
vector_list = embedding_normalized.tolist()
vector_str = json.dumps(vector_list)
return {
"text": request.text,
"embedding": vector_str,
"dimension": len(vector_list)
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health_check():
return {"status": "healthy", "model": "all-MiniLM-L6-v2"}
Kết quả mong đợi: File được tạo thành công. Code này đảm bảo chuẩn hóa vector (normalize) trước khi lưu, điều này rất quan trọng cho Vector Search sử dụng metric Cosine hoặc L2.
Chạy API server trên cổng 8000.
cd /opt/ai-embedding-service
uvicorn embedding_api:app --host 0.0.0.0 --port 8000
Kết quả mong đợi: Server khởi động với thông báo Uvicorn running on http://0.0.0.0:8000. Giữ terminal này đang chạy (hoặc chạy ở background với nohup).
Verify kết quả bằng cách gọi API test.
curl -X POST "http://localhost:8000/embed" \
-H "Content-Type: application/json" \
-d '{"text": "Databend là một hệ quản trị cơ sở dữ liệu cloud-native."}'
Kết quả mong đợi: JSON trả về chứa trường embedding là một chuỗi JSON dài (danh sách số float) và dimension bằng 384.
4. Chèn dữ liệu vector vào Databend và kiểm tra lưu trữ
Bước cuối cùng là kết hợp dữ liệu văn bản, vector embedding từ API, và chèn vào bảng Databend đã tạo ở phần 1. Chúng ta sẽ viết một script Python để thực hiện quy trình: Lấy văn bản -> Gọi API -> Chèn vào DB.
Tạo file script chèn dữ liệu bulk_insert_vector.py tại /opt/ai-embedding-service/bulk_insert_vector.py:
import requests
import json
import mysql.connector
import time
# Cấu hình kết nối Databend
DB_CONFIG = {
"host": "127.0.0.1",
"port": 3660, # Default Databend MySQL port
"user": "root",
"password": "your_password_here", # Thay bằng mật khẩu thật
"database": "default"
}
# Cấu hình API Embedding
EMBEDDING_API = "http://127.0.0.1:8000/embed"
# Dữ liệu mẫu để chèn
SAMPLE_DATA = [
"Databend là một hệ quản trị cơ sở dữ liệu cloud-native.",
"Vector Search cho phép tìm kiếm ngữ nghĩa trong dữ liệu phi cấu trúc.",
"Embedding chuyển đổi văn bản thành vectơ số học trong không gian nhiều chiều.",
"Databend hỗ trợ chỉ mục HNSW để tối ưu hóa tìm kiếm láng giềng gần nhất.",
"RAG (Retrieval-Augmented Generation) kết hợp LLM với dữ liệu riêng tư của doanh nghiệp."
]
def get_embedding(text):
payload = {"text": text}
try:
response = requests.post(EMBEDDING_API, json=payload, timeout=10)
response.raise_for_status()
return response.json()["embedding"]
except Exception as e:
print(f"Error getting embedding for '{text}': {e}")
return None
def insert_into_databend(text, embedding_str, metadata):
conn = mysql.connector.connect(**DB_CONFIG)
cursor = conn.cursor()
try:
sql = """
INSERT INTO knowledge_base (content, embedding, metadata)
VALUES (%s, %s, %s)
"""
cursor.execute(sql, (text, embedding_str, json.dumps(metadata)))
conn.commit()
print(f"Inserted: {text[:30]}...")
except Exception as e:
print(f"Error inserting data: {e}")
finally:
cursor.close()
conn.close()
def main():
print("Starting bulk insert process...")
for i, text in enumerate(SAMPLE_DATA):
print(f"Processing {i+1}/{len(SAMPLE_DATA)}...")
embedding = get_embedding(text)
if embedding:
metadata = {"source": "tutorial_part4", "index": i}
insert_into_databend(text, embedding, metadata)
time.sleep(0.5) # Tránh spam request
print("Bulk insert completed.")
if __name__ == "__main__":
main()
Lưu ý: Thay thế your_password_here trong DB_CONFIG bằng mật khẩu thực tế của user Databend bạn đã tạo ở Phần 3.
Cài đặt thư viện mysql-connector-python và requests trong môi trường ảo.
pip install mysql-connector-python requests
Kết quả mong đợi: Thư viện được cài đặt thành công.
Thực thi script chèn dữ liệu.
cd /opt/ai-embedding-service
python3 bulk_insert_vector.py
Kết quả mong đợi: Script chạy qua từng dòng văn bản, gọi API, in thông báo Inserted: ... và kết thúc bằng Bulk insert completed.
Verify kết quả bằng cách truy vấn dữ liệu trong Databend.
SELECT id, content, LENGTH(embedding) as embedding_len FROM knowledge_base;
Kết quả mong đợi: Bảng hiển thị 5 dòng dữ liệu. Cột embedding_len cho thấy độ dài chuỗi JSON của vector (khoảng 1500-2000 ký tự tùy định dạng).
Verify tính năng Vector Search bằng cách thực hiện tìm kiếm ngữ nghĩa. Giả sử chúng ta tìm kiếm cụm từ "hệ quản trị dữ liệu".
-- Tạo vector query cho từ khóa tìm kiếm
-- (Trong thực tế, bạn cần gọi API trước để có vector query,
-- ở đây ta giả định vector_query là vector của từ "hệ quản trị dữ liệu")
-- Để test nhanh, ta dùng hàm vector_distance giả định hoặc chèn vector query thủ công
-- Databend syntax cho vector search:
SELECT
id,
content,
vector_distance(embedding, '[-0.023, 0.045, ...]') as score -- Thay bằng vector thật từ API
FROM knowledge_base
ORDER BY score
LIMIT 3;
Để có vector thật cho câu truy vấn trên, hãy chạy lệnh curl sau để lấy vector của từ khóa tìm kiếm:
curl -s -X POST "http://localhost:8000/embed" \
-H "Content-Type: application/json" \
-d '{"text": "hệ quản trị dữ liệu"}' | jq -r '.embedding'
Sao chép chuỗi JSON trả về từ lệnh trên và thay thế vào dấu ba chấm ... trong câu SQL vector_distance ở bước trước. Kết quả mong đợi: Databend trả về các bản ghi có nội dung liên quan nhất đến từ khóa tìm kiếm, xếp hạng theo điểm số khoảng cách (score càng nhỏ càng gần).
Điều hướng series:
Mục lục: Series: Triển khai Database AI-native với Databend và Ubuntu 24.04
« Phần 3: Cấu hình bảo mật và quản lý người dùng
Phần 5: Xây dựng ứng dụng RAG (Retrieval-Augmented Generation) với Databend »