Hướng Dẫn Xây Dựng Chatbot với Python và Streamlit
Chào mừng bạn đến với hướng dẫn xây dựng một chatbot sử dụng Python và Streamlit. Trong bài viết này, chúng ta sẽ khám phá cách tạo giao diện người dùng cho chatbot RAG (Retrieval-Augmented Generation), xử lý các truy vấn, và tạo câu trả lời bằng API Groq LLM. Hãy cùng bắt đầu!
Mục Đích Chính
- Tải chỉ mục FAISS và các khối tài liệu đã được xử lý.
- Nhận đầu vào từ người dùng (câu hỏi).
- Lấy các khối tài liệu liên quan nhất bằng tìm kiếm ngữ nghĩa.
- Chuyển ngữ cảnh đã lấy vào Groq LLM để tạo câu trả lời.
- Hiển thị lịch sử trò chuyện trong một giao diện trò chuyện tùy chỉnh được định dạng bằng CSS.
- Đảm bảo tính minh bạch bằng cách hiển thị các khối đã lấy.
Cấu Trúc Bài Viết
1. Nhập Khẩu và Cấu Hình
python
import streamlit as st
import pickle
import faiss
from sentence_transformers import SentenceTransformer
from groq import Groq
import os
from dotenv import load_dotenv
load_dotenv()
streamlit→ khung UI.pickle→ tải các khối đã lưu.faiss→ tìm kiếm tương tự nhanh trên các embedding.SentenceTransformer→ mô hình embedding giống như backend.Groq→ client cho LLM..envloader → tảiGROQ_API_KEYmột cách an toàn.
Lưu ý: API key được lưu trong các biến môi trường (hoặc
Streamlit secretstrong quá trình triển khai).
2. Tải Chỉ Mục và Khối Tài Liệu
python
INDEX_FILE = "faiss_index.bin"
CHUNKS_FILE = "chunks.pkl"
embedder = SentenceTransformer("all-MiniLM-L6-v2")
index = faiss.read_index(INDEX_FILE)
with open(CHUNKS_FILE, "rb") as f:
chunks = pickle.load(f)
- Đọc chỉ mục FAISS đã được xây dựng trước đó.
- Tải các khối tài liệu tương ứng.
- Đảm bảo mô hình embedding phù hợp với backend.
3. Hàm Tìm Kiếm Ngữ Nghĩa
python
def search_index(query, k=10):
q_vec = embedder.encode([query])
D, I = index.search(q_vec, k)
return [chunks[i] for i in I[0]]
- Mã hóa truy vấn thành một vector.
- Tìm kiếm FAISS để lấy
kkhối tài liệu liên quan nhất. - Trả về các khối tài liệu đó để tạo câu trả lời.
4. Tạo Câu Trả Lời Bằng LLM
python
def generate_answer(question, context_chunks):
context = "\n\n".join(context_chunks)
prompt = (
f"Trả lời câu hỏi dựa trên ngữ cảnh đã cho. "
"Nếu câu hỏi không liên quan đến ngữ cảnh, vui lòng không cố gắng trả lời. "
"Thay vào đó, hãy trả lời: 'Cơ sở kiến thức của tôi không có thông tin về điều này.'\n\n"
f"Ngữ cảnh: {context}\n\nCâu hỏi: {question}\nTrả lời:"
)
response = client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model="llama-3.3-70b-versatile",
)
return response.choices[0].message.content.strip()
- Xây dựng một prompt RAG với:
- Ngữ cảnh đã lấy.
- Câu hỏi.
- Gửi tới mô hình LLaMA-3.3-70B của Groq.
- Trả về một câu trả lời sạch.
- Đảm bảo rằng nếu không có ngữ cảnh, chatbot sẽ nói:
“Cơ sở kiến thức của tôi không có thông tin về điều này.”
5. Giao Diện Trò Chuyện Tùy Chỉnh (CSS)
python
st.markdown(
"""
<style>
...
</style>
""",
unsafe_allow_html=True,
)
- Định nghĩa hai kiểu:
- Tin nhắn người dùng (màu xanh, căn phải).
- Tin nhắn bot (màu xám, căn trái).
- Tạo hiệu ứng bong bóng trò chuyện bên trong một khung cuộn.
6. Giao Diện Ứng Dụng Streamlit
python
st.title("📚 Chatbot RAG")
st.write("Hãy hỏi các câu hỏi dựa trên tài liệu đã được lập chỉ mục.")
- Thêm tiêu đề và mô tả ngắn gọn.
- Khởi tạo lịch sử trò chuyện (
st.session_state.messages). - Hiển thị tất cả các tin nhắn trò chuyện trước đó trong các bong bóng được định dạng.
7. Nhập và Xử Lý Câu Hỏi
python
with st.form(key="chat_form", clear_on_submit=True):
question = st.text_input("Câu hỏi của bạn:", key="question_input")
submit_button = st.form_submit_button("Gửi")
- Nhập liệu cho câu hỏi của người dùng.
- Khi được gửi:
- Thêm tin nhắn người dùng vào lịch sử.
- Lấy các khối qua
search_index(). - Gọi
generate_answer()để nhận phản hồi từ LLM. - Thêm phản hồi của bot vào lịch sử trò chuyện.
8. Minh Bạch: Các Khối Đã Lấy
python
st.markdown("### 🔍 Các Khối Đã Lấy")
for i, chunk in enumerate(retrieved, 1):
st.write(f"**Khối {i}:** {chunk[:300]}...")
- Hiển thị các khối đã lấy thực tế.
- Giúp debug nếu tài liệu không đúng đang được lấy.
- Cũng có thể xem trong một expander cho truy vấn cuối cùng.
9. Nút Xóa Trò Chuyện
python
if st.button("Xóa Trò Chuyện"):
st.session_state.messages = []
st.rerun()
- Đặt lại lịch sử trò chuyện.
- Cho phép bắt đầu một phiên mới.
Quy Trình Làm Việc (Frontend)
- Người dùng đặt câu hỏi trong giao diện Streamlit.
- Hệ thống mã hóa nó → tìm kiếm FAISS → lấy các khối hàng đầu.
- Các khối + câu hỏi được chuyển vào Groq LLM.
- Câu trả lời được tạo → hiển thị trong giao diện trò chuyện.
- Các khối đã lấy được hiển thị để đảm bảo tính minh bạch.
Những Lưu Ý Quan Trọng
- Frontend không xây dựng lại chỉ mục. Nó phụ thuộc vào backend (
index_docs.py) đã chạy trước đó. - Bộ nhớ trò chuyện chỉ dựa trên phiên (xóa khi làm mới).
- Biến môi trường
GROQ_API_KEYphải được thiết lập trong:- Tệp
.envcục bộ. - Bí mật Streamlit (
st.secrets["GROQ_API_KEY"]) trong quá trình triển khai.
- Tệp
Các Thực Hành Tốt Nhất
- Luôn kiểm tra các biến môi trường để đảm bảo an toàn cho API key.
- Giữ cho mã nguồn rõ ràng và dễ bảo trì bằng cách sử dụng các hàm nhỏ.
- Thực hiện xử lý lỗi cho các trường hợp không tìm thấy khối tài liệu.
Những Cạm Bẫy Thường Gặp
- Không xử lý ngữ cảnh dẫn đến câu trả lời không chính xác.
- Không làm sạch lịch sử trò chuyện có thể dẫn đến thông tin sai lệch.
Mẹo Tối Ưu Hiệu Suất
- Sử dụng các phiên bản mới nhất của thư viện để cải thiện tốc độ và hiệu suất.
- Tối ưu hóa mô hình để giảm thời gian phản hồi.
Câu Hỏi Thường Gặp (FAQ)
Q: Làm thế nào để triển khai chatbot trên một máy chủ?
A: Bạn có thể sử dụng Heroku hoặc AWS để triển khai ứng dụng Streamlit của mình.
Q: Có cách nào để cải thiện độ chính xác của câu trả lời không?
A: Có thể cải thiện bằng cách sử dụng mô hình LLM mạnh hơn hoặc đào tạo lại với dữ liệu tốt hơn.
Kết Luận
Trong bài viết này, chúng ta đã tìm hiểu cách xây dựng một chatbot sử dụng Python và Streamlit. Hy vọng rằng bạn đã có thể theo kịp và thực hành thành công. Hãy thử ngay và cho chúng tôi biết trải nghiệm của bạn nhé!