AIO2024 LLamaIndex
AIO2024 LLamaIndex
2 Phương pháp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1 Mô tả bài toán . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Dữ liệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Quy trình xử lí dữ liệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4 Quy trình tạo Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.5 Xây dựng ứng dụng . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.6 Quy trình đánh giá hệ thống . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
8 Kỹ thuật prompt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.1 Prompts cho Metadata Extractors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.2 Prompts cho cuộc trò chuyện . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
9 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
9.1 Thực hành 4: Đánh giá hệ thống Mental Health . . . . . . . . . . . . . . . . . . . . . . . 60
10 Kết luận . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
1
AIO 2024 aivietnam.edu.vn
1 Giới thiệu
Tại Việt Nam, sức khỏe tinh thần từ lâu đã bị coi là một vấn đề thứ yếu, chỉ dành cho những người
có điều kiện kinh tế khá giả, hoặc thậm chí bị gán nhãn là vấn đề chỉ của những kẻ "điên dại". Sự kỳ
thị xã hội này đã dẫn đến việc xem nhẹ nhu cầu chăm sóc sức khỏe tinh thần, gây ra những hậu quả
nghiêm trọng về cả thể chất lẫn tinh thần cho hàng triệu người.
Theo thông tin từ báo suckhoedoisong.vn năm 2023, Việt Nam có khoảng 14 triệu người bị rối loạn
tâm thần. Con số này thể hiện một tỷ lệ đáng báo động, đặc biệt là khi so sánh với số lượng chuyên gia
tâm lý lâm sàng và tâm lý trị liệu chỉ dừng lại ở con số 143 người. Tỷ lệ này tương đương với khoảng
1 chuyên gia cho mỗi 100.000 người dân bị rối loạn tâm thần, cho thấy một sự thiếu hụt trầm trọng
trong việc cung cấp dịch vụ chăm sóc sức khỏe tâm thần tại Việt Nam.
Trong khi dịch vụ chăm sóc sức khỏe tâm thần chủ yếu là điều trị bằng thuốc, thì những phương pháp
hỗ trợ tâm lý chuyên sâu lại chưa được phổ biến rộng rãi. Hơn nữa, dịch vụ tâm lý lâm sàng chưa được
bảo hiểm y tế chi trả, khiến nhiều người bệnh không tiếp cận được dịch vụ cần thiết. Điều này càng
làm trầm trọng thêm khoảng cách giữa nhu cầu và khả năng cung cấp dịch vụ chăm sóc sức khỏe tâm
thần.
Ngoài ra, nước ta đang phải đối mặt với nhiều thách thức mới, bao gồm già hóa dân số, tỷ lệ người
khuyết tật thần kinh cao, và những ảnh hưởng sâu rộng từ đại dịch COVID-19. Đại dịch đã làm gia
tăng tỷ lệ lo âu và trầm cảm lên hơn 25% chỉ trong năm đầu tiên, điều này cho thấy sức khỏe tâm thần
đang trở thành một vấn đề ngày càng nghiêm trọng.
Trước tình hình này, bài viết hướng đến mục tiêu ứng dụng công nghệ tiên tiến với chi phí thấp để xây
dựng hệ thống chăm sóc sức khỏe tinh thần thông minh. Hệ thống này đóng vai trò như một chuyên
gia tâm lý, có thể trò chuyện với con người một cách tự nhiên và có khả năng đánh giá, theo dõi sức
khỏe tinh thần của người dùng qua kiến thức chuyên môn từ DSM5 -Sổ tay chẩn đoán và thống kê các
rối loạn tâm thần.
Việc xây dựng một hệ thống chăm sóc sức khỏe tinh thần thông minh không chỉ là một bước đi cần
thiết mà còn là một giải pháp kịp thời để giảm bớt gánh nặng cho hệ thống y tế. Nó có thể hỗ trợ
những người không có khả năng tiếp cận với các chuyên gia tâm lý, cung cấp hỗ trợ tâm lý kịp thời
và phổ biến thông tin về sức khỏe tâm thần, giúp giảm bớt sự kỳ thị và nâng cao nhận thức của cộng
đồng.
Và tất nhiên rồi, bài viết này dành cho các bạn AIO và cộng đồng AI VIET NAM, vì vậy hướng dẫn
sử dụng công nghệ và xây dựng hệ thống là một trong những mục tiêu hàng đầu. Nội dung của bài
viết này sẽ đan xen giữa lý thuyết và thực hành, đủ cho bạn có thể phát triển các hệ thống của riêng
mình để giải quyết các vấn đề khác, mà không bị giới hạn trong dự án chăm sóc sức khỏe tinh thần.
Nội dung chính của các phần như sau:
• Phần 1: Như đã trình bày, phần này nhằm giới thiệu bài toán, đặt vấn đề và nêu rõ những mục
tiêu của bài viết.
• Phần 2: Trình bày phương pháp thực hiện, mô tả hệ thống và các thành phần cơ bản.
• Phần 3: Hướng dẫn sử dụng thư viện Llamaindex cơ bản, giới thiệu các thành phần của một hệ
thống RAG cơ bản.
• Phần 4: Phần này sẽ đi sâu vào chi tiết các kỹ thuật load dữ liệu, tạo nodes và khai thác tiềm năng
của metadata. Sau đó hướng dẫn sử dụng Ingest pipeline để tạo quy trình hoàn chỉnh cho quá
trình xử lí dữ liệu. Cuối phần này là phần thực hành, chúng ta sẽ tiến hành tạo Ingest Pipeline
cho dự án chăm sóc sức khỏe tinh thần.
• Phần 5 Kỹ thuật Indexing Data: Phần này sẽ đi sâu vào chi tiết các kỹ thuật tạo Index, bao gồm
việc tạo Vector Store Index và Vector Database index. Cuối cùng là phần thực hành xây dựng
Index cho dự án chăm sóc sức khỏe tinh thần.
2
AIO 2024 aivietnam.edu.vn
• Phần 6 Kỹ thuật truy xuất dữ liệu: Phần này trình bày cơ chế của hoạt động và hướng dẫn tinh
chỉnh các thành phần trong hệ thống truy xuất dữ liệu.
• Phần 7 Kỹ thuật xây dựng Chatbot, Agent: Trong phần này đi sâu vào các loại công cụ và cơ chế
quản lí cuộc hội thoại. Hướng dẫn tạo Agent thông minh và khai thác tiềm năng của chúng. Cuối
phần này, chúng ta sẽ thực hành tạo Agent cho hệ thống chăm sóc sức khỏe tinh thần.
• Phần 8 Kỹ thuật prompt: Phần này giới thiệu và giải thích cấu trúc của các loại prompt sử dụng
trong llamaindex, sau phần này bạn có thể tạo được prompt cho riêng mình.
• Phần 9 Đánh giá hệ thống - Evaluate RAG: Trong phần này sẽ trình bày kỹ thuật để đánh giá
hệ thống RAG, các độ đo cơ bản và tập trung vào đánh giá thành phần query engine. Cuối phần
này chúng ta sẽ thực hành đánh giá hệ thống chăm sóc sức khỏe tinh thần.
• Phần 10 Kết Luận: Tóm lại một số ý chính, nhận xét, hướng phát triển đề xuất.
2 Phương pháp
Với sự phát triển mạnh mẽ của công nghệ trí tuệ nhân tạo, các mô hình ngôn ngữ lớn (Large Language
Models - LLM) hiện nay có khả năng hiểu và tạo ra văn bản với mức độ tự nhiên như con người. Tuy
nhiên, một vấn đề lớn khi sử dụng các LLM là hiện tượng ảo giác (hallucination), khi mô hình đưa ra
thông tin không chính xác hoặc không có trong dữ liệu huấn luyện. Hơn nữa, các LLM thường không
được cập nhật dữ liệu thường xuyên, dẫn đến việc phản hồi dựa trên thông tin lỗi thời.
Để khắc phục các vấn đề trên, giải pháp Retrieval-Augmented Generation (RAG) đã được phát triển.
RAG kết hợp sức mạnh của các LLM với hệ thống truy xuất thông tin từ một cơ sở dữ liệu hoặc tài
liệu bên ngoài. Khi nhận được câu hỏi, hệ thống RAG sẽ tìm kiếm và lấy thông tin liên quan từ các
nguồn dữ liệu đáng tin cậy, sau đó sử dụng LLM để tạo ra câu trả lời dựa trên thông tin được truy
xuất. Phương pháp này giúp giảm thiểu ảo giác và tăng độ chính xác của phản hồi.
Có nhiều thư viện hỗ trợ xây dựng hệ thống RAG, trong bài viết này, chúng ta sẽ sử dụng LlamaIndex
- là một thư viện giúp xây dựng các LLM có khả năng thích ứng với các trường hợp cụ thể. Thay vì
chỉ dựa vào kiến thức chung chung đã được mô hình đào tạo trước, LlamaIndex cho phép chúng ta kết
hợp LLM với dữ liệu bên ngoài theo nhu cầu cụ thể của từng ứng dụng. Thư viện này hỗ trợ tích hợp
dữ liệu từ nhiều nguồn khác nhau, giúp mô hình đưa ra các phản hồi chính xác và phù hợp hơn với ngữ
cảnh cụ thể.
• Trò chuyện và tư vấn tâm lý: Hệ thống phải có khả năng thực hiện các cuộc trò chuyện tự nhiên,
thân thiện và đồng cảm với người dùng. Nó có thể cung cấp các tư vấn tâm lý cơ bản dựa trên
thông tin được thu thập từ người dùng, giúp họ giải tỏa cảm xúc và định hướng giải quyết các
vấn đề tâm lý.
3
AIO 2024 aivietnam.edu.vn
• Phân tích và chẩn đoán: Hệ thống cần có khả năng phân tích các tương tác với người dùng để
đánh giá và chẩn đoán sơ bộ về tình trạng sức khỏe tâm thần của họ, bao gồm các bệnh lý tâm
thần phổ biến (như lo âu, trầm cảm) và phân loại mức độ tình trạng (tốt, khá, trung bình, kém),
dựa trên dữ liệu DSM5.
• Theo dõi tiến trình sức khỏe: Hệ thống cần theo dõi tiến trình sức khỏe tâm thần của người dùng
theo thời gian, lưu trữ lịch sử tương tác và các chỉ số liên quan. Dựa trên dữ liệu này, Hệ thống
cung cấp các khuyến nghị điều chỉnh hành vi hoặc nhắc nhở người dùng duy trì thói quen tốt cho
sức khỏe tâm thần.
• Tính bảo mật và quyền riêng tư: Dữ liệu sức khỏe tâm thần của người dùng phải được bảo vệ
nghiêm ngặt, tuân thủ các quy định về bảo mật thông tin cá nhân.
• Tính khả dụng: Hệ thống cần hoạt động liên tục, sẵn sàng hỗ trợ người dùng 24/7.
• Tính dễ sử dụng: Giao diện người dùng phải đơn giản, thân thiện và dễ dàng truy cập, phù hợp
với mọi đối tượng người dùng.
Dựa vào đặc tả bài toán, chúng ta mô phỏng hệ thống ban đầu như sơ đồ dưới đây:
Hình 1: Hệ thống thực hiện trò chuyện với người dùng (1)(2), phân tích và chẩn đoán (3), theo dõi tiến
trình sức khỏe (4)
4
AIO 2024 aivietnam.edu.vn
2.2 Dữ liệu
Trong lĩnh vực chẩn đoán và phân loại các rối loạn tâm thần, DSM-5 (Diagnostic and Statistical Manual
of Mental Disorders, 5th Edition) là tài liệu chuẩn mực được sử dụng rộng rãi trên toàn thế giới. Với
hơn 300 loại rối loạn tâm thần được liệt kê và mô tả chi tiết, DSM-5 cung cấp các tiêu chí chẩn đoán cụ
thể. Tuy nhiên tài liệu nayf lên đến hơn 1000 trang, do muốn giảm chi phí và tối ưu hóa quá trình thử
nghiệm, chúng ta sẽ chọn sử dụng một giáo trình rút gọn, là bản dịch tiếng Việt của DSM-5, mang tên
"Tiêu chuẩn chẩn đoán các rối loạn tâm thần theo DSM-5."Tài liệu này gồm 106 trang và được biên
soạn bởi Bệnh viện 103, bộ môn Tâm thần và Tâm lý học.
Giáo trình rút gọn này bao gồm 15 nhóm rối loạn tâm thần chính, chẳng hạn như rối loạn phát triển
thần kinh, rối loạn trầm cảm, và nhiều loại rối loạn khác. Cấu trúc chung mỗi loại bệnh cụ thể được
viết theo hai phần chính là: các tiêu chí chẩn đoán và hướng dẫn chẩn đoán. Các tiêu chí được trình
bày một cách cụ thể, giúp xác định tình trạng sức khỏe tâm thần của bệnh nhân. Dựa trên những tiêu
chí này, tài liệu cung cấp hướng dẫn cụ thể để thực hiện chẩn đoán và phân loại các rối loạn tâm thần
một cách chính xác và hiệu quả.
Hình 2: Giáo trình "Tiêu chuẩn chẩn đoán các rối loạn tâm thần theo DSM-5"và một mẫu dữ liệu về
Rối loạn điều hòa khí sắc.
5
AIO 2024 aivietnam.edu.vn
Hình 3: Quy trình xử lí dữ liệu, dữ liệu thô được xử lí bởi Ingest Pipeline(1), quy trình xử lí được lưu
lại tại bộ nhớ cache(2), sau đó quá trình tạo index và lưu trữ tại kho index(3)
Sau khi đã có metadata, chúng ta thực hiện embedding cho node hiện tại thông qua mô hình embedding
được cung cấp bởi OPENAI API. Việc embedding này giúp chuyển đổi nội dung văn bản thành dạng
vector để dễ dàng tìm kiếm và so sánh trong các bước tiếp theo.
Toàn bộ quy trình tạo nodes này được lưu lại trong bộ nhớ cache để có thể tải lại nhanh chóng mà
không cần phải thực hiện toàn bộ quy trình Ingest Pipeline từ đầu mỗi khi cần. Cuối cùng, từ các nodes
đã được embedding, chúng ta tạo ra index và lưu trữ index này vào kho dữ liệu Index. Index này sẽ
được sử dụng để truy xuất thông tin một cách hiệu quả và nhanh chóng trong các truy vấn sau này.
• Đóng vai là chuyên gia tâm lí, nói chuyện với người dùng một cách tự nhiên
• Khi người dùng yêu cầu chẩn đoán, hoặc kết thúc trò chuyện hoặc khi Agent đã có đủ thông tin
để chẩn đoán. Nó phải tiến hành chẩn đoán bằng cách tổng hợp lại cuộc trò chuyện của người
dùng và tìm kiếm thông tin về các bệnh liên quan trong kho dữ liệu. Dựa vào thông tin tìm kiếm
được và nội dung cuộc trò chuyện hiện tại, đưa ra tổng đoán và điểm số sức khỏe của người dùng
theo các mức độ (tốt, khá, trung bình, kém)
• Kết quả chẩn đoán sẽ được lưu lại để phục vụ cho quá trình phân tích và theo dõi.
6
AIO 2024 aivietnam.edu.vn
Hình 4: Agent quản lí lịch sử trò chuyện và sử dụng các công cụ truy vấn DSM5 và công cụ lưu trữ kết
quả chẩn đoán.
7
AIO 2024 aivietnam.edu.vn
Hình 6: Quy trình đánh giá hệ thống, tạo tập câu hỏi đánh giá (1), đánh giá trên 3 độ đo (2)
Đầu tiên, chúng ta tạo tập dữ liệu đánh giá bằng công cụ RagDatasetGenerator, công cụ này sẽ sử dụng
LLM để sinh câu hỏi từ nội dung của từng node, tạo thành một tập dữ liệu các câu hỏi để đánh giá
khả năng truy xuất của DSM5 engine. Sau đó sử dụng gói evaluation để thực hiện đánh giá trên ba độ
đo Correctness, Faithfulness, Relevancy.
8
AIO 2024 aivietnam.edu.vn
Trước khi bắt đầu xây dựng hệ thống phức tạp, chúng ta sẽ tìm hiểu kiến trúc của một hệ thống RAG
cơ bản được xây dựng bởi Llama index sẽ gồm các thành phần nào. Sau phần này ta có thể tạo một
ứng dụng RAG cơ bản. Code phần này có tại: docs/tutorial/1.Basicl lamaindex.ipynb
2. Tạo OpenAI API Key tại https://platform.openai.com/api-keys, sau khi tạo API chúng ta cần lưu
lại để sử dụng sau.
3. Tạo Llamaindex Cloud API Key tại https://cloud.llamaindex.ai/login
4. Cài đặt git và tạo tài khoản github
3.2 Documents
Trong các ứng dụng RAG, chúng ta phải làm việc với nhiều loại dữ liệu khác nhau, từ các tệp văn bản
như PDF, DOCX, đến dữ liệu từ cơ sở dữ liệu hay kết quả từ API. Những loại dữ liệu này thường có
định dạng khác nhau, và điều này có thể gây khó khăn trong việc quản lý và xử lý.
Để giải quyết vấn đề này, LlamaIndex sử dụng một khái niệm gọi là Document. Document đóng vai trò
như một "container"(bộ chứa), giúp chúng ta lưu trữ các loại dữ liệu khác nhau trong một cấu trúc
chung. Điều này giúp cho việc quản lý và khai thác dữ liệu trở nên dễ dàng và hiệu quả hơn.
Một trong những điểm đặc biệt của Document là nó có thể chứa không chỉ dữ liệu chính (thường là
văn bản), mà còn các thông tin bổ sung gọi là siêu dữ liệu (metadata). Siêu dữ liệu này có thể là các
mô tả, tóm tắt, tiêu đề, hoặc bất kỳ thông tin nào khác mà chúng ta muốn gắn kèm với dữ liệu, giúp
việc tìm kiếm và phân tích dữ liệu chính xác hơn.
Để tạo một Document trong LlamaIndex, chúng ta sử dụng cú pháp sau:
1 Document ( text , metadata , id_ )
Đây là những thành phần cơ bản nhất của Document, trong đó:
9
AIO 2024 aivietnam.edu.vn
• metadata: Siêu dữ liệu, được lưu trữ dưới dạng từ điển (dictionary), có thể được tạo tự động bởi
thư viện hoặc do chúng ta cung cấp.
• id_: ID của Document, thường được tự động tạo để định danh duy nhất mỗi Document.
Trong ví dụ này, chúng ta sẽ tạo một Document đơn giản trong LlamaIndex để lưu trữ một đoạn văn
bản cùng với một số thông tin bổ sung.
Giải thích:
• Khai báo thư viện: Dòng đầu tiên from llama_index.core import Document nhập lớp Document
từ thư viện LlamaIndex. Lớp này sẽ được sử dụng để tạo một Document mới.
• Tạo dữ liệu: Dòng text = "AI VIET NAM"khởi tạo một chuỗi văn bản đơn giản là "AI VIET
NAM". Đây chính là nội dung chính mà chúng ta muốn lưu trữ trong Document. Thông thường
dữ liệu thô thường nằm trong các định dạng file như pdf, csv... chúng ta cần sử dụng các công cụ
phù hợp để đọc các file này, chúng ta sẽ được tìm hiểu các kỹ thuật này ở phần sau.
• Tạo Document: Tiếp theo, chúng ta tạo một Document mới bằng cách sử dụng Document(text=text,
metadata={"fb": "fb/aivietnam.edu.vn"}, id_="1"). Ở đây, chúng ta truyền vào ba đối số:
– text: Nội dung chính của Document, trong trường hợp này là "AI VIET NAM".
– metadata: Một từ điển chứa các thông tin bổ sung, trong ví dụ này là một liên kết Facebook
với khóa "fb"và giá trị "fb/aivietnam.edu.vn".
– id_: Mã định danh duy nhất của Document, chúng ta đặt nó là 1.
• Cuối cùng, print(doc) in ra thông tin của Document mà chúng ta vừa tạo. Kết quả này sẽ hiển
thị đoạn văn bản, siêu dữ liệu, và mã định danh mà chúng ta đã đặt cho Document.
Ngoài ra, chúng ta có thể xem toàn bộ cấu trúc của document bằng cách in mọi thông tin của nó ra
như sau:
1 from llama_index . core import Document
2
3 text = " AI VIET NAM "
4 doc = Document (
5 text = text ,
6 metadata ={ " fb " : " fb / aivietnam . edu . vn " } ,
7 id_ = " 1 "
8 )
9 for i in doc :
10 print ( i )
10
AIO 2024 aivietnam.edu.vn
3.3 Nodes
Việc tạo Documents là bước đầu tiên, nhưng để dữ liệu thô này có thể được mô hình ngôn ngữ lớn
(LLM) xử lý hiệu quả, chúng ta cần chia nhỏ nó ra. Đây là lúc Nodes phát huy vai trò. Nodes là các
"mảnh"nhỏ hơn của một Document, như một đoạn văn bản hoặc hình ảnh, giúp chia tài liệu thành các
phần dễ quản lý hơn.
Nodes giúp bạn tránh vượt quá giới hạn đầu vào của mô hình và giảm chi phí xử lý. Ví dụ, thay vì đưa
cả cuốn ebook dài 100 trang vào một prompt, bạn có thể chia nó thành nhiều Nodes nhỏ hơn để mô
hình có thể xử lý từng phần một cách chính xác và hiệu quả. Hơn nữa, các Nodes có thể được liên kết
với nhau, giúp tổ chức và quản lý thông tin một cách logic.
Trong LlamaIndex, bạn có thể tạo Nodes từ dữ liệu văn bản bằng cách sử dụng lớp TextNode. Cú pháp
sử dụng như sau:
1 from llama_index . core . schema import TextNode
2 TextNode ( text )
Ví dụ trên minh họa cách chia nhỏ một Document thành các phần nhỏ hơn gọi là Nodes. Đầu tiên,
chúng ta tạo một Document từ chuỗi văn bản "AI VIET NAM". Sau đó, chúng ta chia văn bản này
thành hai phần: "AI"và "VIET NAM", mỗi phần được lưu trữ trong một TextNode riêng biệt. Các
Nodes này giúp mô hình xử lý dữ liệu từng phần một cách dễ dàng và hiệu quả hơn.
11
AIO 2024 aivietnam.edu.vn
Trong ví dụ trước, chúng ta đã tạo các Nodes một cách thủ công bằng cách chia nhỏ văn bản trong
Document. Tuy nhiên, quá trình này có thể được thực hiện tự động bằng cách sử dụng TokenTextSplitter.
Cú pháp sử dụng:
1 from llama_index . core . node_parser import TokenTextSplitter
2
3 Token TextSplitter (
4 chunk_size ,
5 chunk_overlap ,
6 separator ,
7 )
12
AIO 2024 aivietnam.edu.vn
Trong phần output, một cảnh báo xuất hiện nói rằng chúng ta sẽ gặp vấn đề khi mà chunk_size của
chúng ta quá nhỏ, nhỏ hơn hoặc chênh lệch không quá nhiều so với kích cỡ của metadata. Vì khi đó
metadata sẽ chiếm phần lớn lượng dữ liệu của node trong khi dữ liệu thực tế thì rất ít.
Việc tạo node không chỉ dừng lại ở việc chia nhỏ document, chúng ta có thể thiết lập mối quan hệ giữa
các node, ngoài ra còn có một số phương pháp biến đổi node nâng cao hơn mà chúng ta sẽ tìm hiểu
ở phần sau. Dưới đây là cách mà chúng ta tạo mối quan hệ giữa hai node, có 5 mối quan hệ giữa các
node là: Trong hệ thống phân chia văn bản thành các đoạn nhỏ (nodes) như được sử dụng trong thư
viện ‘llama_index‘, các mối quan hệ giữa các nodes có thể được mô tả như sau:
• 1. SOURCE:
– Node này là tài liệu gốc (source document). Đây là toàn bộ văn bản ban đầu mà từ đó các
nodes khác được tách ra.
– Ví dụ: Nếu văn bản gốc là "Hôm nay trời nắng, tôi đi ăn kem, lạnh buốt cả răng!", thì node
‘SOURCE‘ sẽ chứa toàn bộ văn bản này.
• 2. PREVIOUS:
– Node này là node trước đó trong tài liệu. Nó đại diện cho đoạn văn bản ngay trước node
hiện tại.
– Ví dụ: Nếu có hai nodes liên tiếp, node thứ hai sẽ có một liên kết ‘PREVIOUS‘ trỏ đến node
thứ nhất.
• 3. NEXT:
– Node này là node kế tiếp trong tài liệu. Nó đại diện cho đoạn văn bản ngay sau node hiện
tại.
– Nếu có hai nodes liên tiếp, node thứ nhất sẽ có một liên kết ‘NEXT‘ trỏ đến node thứ hai
• 4. PARENT:
– Node này là node cha trong tài liệu. Nó đại diện cho một đoạn văn bản lớn hơn chứa node
hiện tại.
– Ví dụ: Nếu một tài liệu lớn được chia thành các đoạn nhỏ hơn và mỗi đoạn nhỏ này lại được
chia thành các đoạn nhỏ hơn nữa, thì một node có thể có một node cha chứa nó.
• 5. CHILD:
– Node này là node con trong tài liệu. Nó đại diện cho một đoạn văn bản nhỏ hơn nằm trong
node hiện tại.
– Ví dụ: Nếu một đoạn văn bản lớn được chia thành các đoạn nhỏ hơn, thì mỗi đoạn nhỏ sẽ là
một node con của node lớn.
13
AIO 2024 aivietnam.edu.vn
Mặc dù có thể thiết lập các mối quan hệ này một cách thủ công, nhưng mình nhận thấy rằng khi triển
khai dự án thực tế, mình chẳng có đủ hơi sức để làm điều này. Dù sao thì khi tạo node thì các mối
quan hệ này cũng được tạo tự động rồi, trừ khi mình biết chắc việc tạo thủ công cho kết quả tốt hơn
đáng kể thì có thể xem xét lại vấn đề này.
Chúng ta có thể xem các mối quan hệ nào đã được tạo giữa các node trong ví dụ trên bằng cách in ra
từng node hoặc đơn giản chỉ in thuộc tính relationships.
1 print ( nodes [0]. relationships )
2 print ( nodes [1]. relationships )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
{ < NodeRelationship . SOURCE : ’1 ’ >: RelatedNodeInfo ( node_id = ’7425 c6ab - e5c9 -4 b1b - b12f -2
dcbd2ec946f ’ , node_type = < ObjectType . DOCUMENT : ’4 ’ > , metadata ={} , hash = ’0
a f 2 3 2 5 b c 3 9 7 6 e f 8 5 0 2 4 e 3 5 7 6 9 3 4 4 a c 7 1 3 8 f e 4 c 2 1 7 0 0 c 2 b f 5 5 d 2 1 0 9 0 3 9 8 8 2 b b 4 ’) , <
NodeRelationship . NEXT : ’3 ’ >: RelatedNodeInfo ( node_id = ’ a075758c -6291 -4 a5b -975 d -8
f89850fcfc7 ’ , node_type = < ObjectType . TEXT : ’1 ’ > , metadata ={} , hash = ’524
e 3 4 2 3 7 4 5 5 e e 8 5 f e c f 2 7 2 4 2 3 7 a 8 e 8 2 5 e 1 3 6 e 9 d f f 3 5 9 b b d e 4 4 c 4 c 3 a 6 5 6 c 1 d 9 5 ’) }
{ < NodeRelationship . SOURCE : ’1 ’ >: RelatedNodeInfo ( node_id = ’7425 c6ab - e5c9 -4 b1b - b12f -2
dcbd2ec946f ’ , node_type = < ObjectType . DOCUMENT : ’4 ’ > , metadata ={} , hash = ’0
a f 2 3 2 5 b c 3 9 7 6 e f 8 5 0 2 4 e 3 5 7 6 9 3 4 4 a c 7 1 3 8 f e 4 c 2 1 7 0 0 c 2 b f 5 5 d 2 1 0 9 0 3 9 8 8 2 b b 4 ’) , <
NodeRelationship . PREVIOUS : ’2 ’ >: RelatedNodeInfo ( node_id = ’3 a186da6 -92 d1 -40 e0 -823 b -0
d6f901f426e ’ , node_type = < ObjectType . TEXT : ’1 ’ > , metadata ={} , hash = ’
e d c d 2 c 0 0 a 1 3 2 f f 9 c 5 b 9 c 1 5 9 f 7 b 6 b c b 2 6 b 0 0 e 8 f c 1 6 d 6 d f 4 5 4 d 4 f 1 c 3 6 3 9 e 0 1 c 2 9 4 ’) }
======================================================================================
3.4 Indexes
Sau khi bạn nhập dữ liệu vào hệ thống, LlamaIndex sẽ giúp bạn lập chỉ mục dữ liệu vào một cấu trúc dễ
dàng truy xuất. Quá trình này thường bao gồm việc tạo ra các vector embeddings và lưu trữ chúng trong
một cơ sở dữ liệu chuyên biệt gọi là vector store. Việc lập chỉ mục giúp tổ chức dữ liệu sao cho dễ dàng
tìm kiếm và truy xuất sau này. Llama Index hỗ trợ nhiều loại index khác nhau như SummaryIndex,
VectorStoreIndex, TreeIndex, KnowledgeGraphIndex, tùy vào từng trường hợp mà chúng ta sẽ chọn loại
phù hợp. Nhìn chung, các loại index này đều thực hiện các chức năng tạo index, thêm node mới, và truy
vấn. Trong phần này, chúng ta sẽ tìm hiểu cách sử dụng SumaryIndex, theo định nghĩa thì Summary
Index là một cấu trúc dữ liệu đơn giản, nơi các Nodes (các đoạn văn bản đã được chia nhỏ) được lưu
trữ theo một chuỗi liên tiếp. Đọc tên, có thể chúng ta sẽ nghĩ rằng nó sẽ tóm tắt thông tin các node
nhưng mình nghĩ ở đây nó mang nghĩa tổng hợp, vì thực chất, nó chỉ tập hợp lại các node, giống một
danh sách trong Python. Phương pháp này rất cơ bản và nó cũng không cả sử dụng đến các mô hình
embedding.
Dưới đây là cú pháp sử dụng:
1 from llama_index . core import SummaryIndex
2 # T ạ o index t ừ node
3 index = SummaryIndex ( nodes )
4 # T ạ o index t ừ document
5 index2 = SummaryIndex . from_documents ([ doc ])
Trên đây là 2 cách để chúng ta tạo index, cách 1 tạo index từ node, cách 2 tạo index từ document.
Điểm khác biệt là khi tạo index từ node, chúng ta sẽ cần thiết lập thủ công các tham số về kích thước
mỗi node,... trong khi cách 2 tạo index từ document, quá trình tạo node diễn ra hoàn toàn tự động.
Ví dụ sau đây sẽ thực hiệu tạo index theo cả hai cách:
14
AIO 2024 aivietnam.edu.vn
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
index 1 IndexList ( index_id = ’ fde51551 - b82f -4 be0 - a7c7 - af11cbd790d1 ’ , summary = None , nodes
=[ ’37 d4af84 -6 deb -4 b5a -8906 -73 eff559ce10 ’ , ’9 fa671af -9 be6 -43 ea -8 fd0 -5 d603c6cc0c0 ’ ,
’0 b75eed6 - fd63 -4 b7a - bbd2 - b8ed9962a3c6 ’])
index 2 IndexList ( index_id = ’ ab9305a7 -9268 -4 ad1 -8 edf -368 aef1d8c8d ’ , summary = None , nodes
=[ ’796 e0d8c -8 e7e -4 ee2 -91 aa - a74df80207a6 ’])
======================================================================================
• Đầu tiên chúng ta cần khai báo các gói cần thiết, trong đó SumaryIndex nằm trong llama_index.core
• Tiếp theo chúng ta tạo document và nodes như cách mà chúng ta đã tìm hiểu ở hai phần trước
đó.
• Việc tạo index thực hiện theo hai phương pháp, đầu tiên index sẽ được tạo bằng cách sử dụng
các Nodes đã chia nhỏ. Các Nodes này được lưu trữ theo trình tự trong index. Trong khi đó cách
2 index được tạo trực tiếp từ Document, tức là văn bản ban đầu sẽ tự động được chia nhỏ và lưu
trữ dưới dạng Nodes trong index2.
• Chúng ta có thể xem cấu trúc của các index đã tạo qua thuộc tính index_struct của nó.
15
AIO 2024 aivietnam.edu.vn
Bùm, và lỗi xuất hiện, lỗi này là do trình truy xuất dữ liệu của chúng ta cần sử dụng đến API của
OPEN AI. Để sửa lỗi thì chúng ta cần thiết lập khóa API này. Để thiết lập có nhiều cách, chúng ta sẽ
làm theo cách dưới đây:
1 from llama_index . core import Settings
2 from llama_index . llms . openai import OpenAI
3 import openai
4
5 openai . api_key = " sk - your - openai - api - key "
6 Settings . llm = OpenAI ( model = " gpt -4 o - mini " , temperature =0.2)
• Đầu tiên là thiết lập API key: Đây là bước đầu tiên và cần thiết để kết nối với OpenAI API,
cho phép chúng ta sử dụng các dịch vụ của OpenAI như các mô hình ngôn ngữ lớn, các mô hình
embedding.
• Tiếp theo chúng ta cấu hình mô hình ngôn ngữ lớn chúng ta sẽ sử dụng:
– Settings.llm: Đây là thuộc tính giúp chúng ta thiết lập mô hình ngôn ngữ mà LlamaIndex sẽ
sử dụng cho việc truy vấn thông tin.
– OpenAI(model="gpt-4o-mini", temperature=0.2): Trong đoạn mã này, chúng ta chọn sử
dụng mô hình "gpt-4o-mini"từ OpenAI với tham số temperature=0.2.
∗ model: Mô hình mà bạn muốn sử dụng, ví dụ như "gpt-4o-mini". Đây là phiên bản nhỏ
hơn và nhẹ hơn của mô hình GPT-4.
∗ temperature: Tham số này có giá trị từ 0 đến 2, nó kiểm soát sự sáng tạo của mô hình
khi đưa ra câu trả lời. Giá trị thấp hơn (như 0.2) sẽ khiến mô hình trả lời cụ thể và ít
ngẫu nhiên hơn, giúp kết quả trở nên chính xác hơn.
Sau khi hoàn tất các thiết lập, hệ thống RAG đơn giản của chúng ta đã sẵn sàng để sử dụng.
• Đầu tiên trình truy xuất sẽ tìm kiếm và lấy ra các phần (Nodes) liên quan từ chỉ mục dựa trên
câu hỏi của bạn.
• Sau đó các phần này được sắp xếp lại hoặc lọc để đảm bảo thông tin cần thiết được chuẩn bị tốt
nhất.
16
AIO 2024 aivietnam.edu.vn
• Cuối cùng, nó kết hợp các phần đó và đưa vào mô hình ngôn ngữ (LLM) để tạo ra câu trả lời
hoàn chỉnh cho bạn.
Tóm tắt
Vậy là chúng ta đã tìm hiểu về các khái niệm cơ bản trong llama index và xây dựng thành công
mô trình truy vấn RAG đơn giản. Dưới đây là tóm tắt các bước xây dựng mô hình RAG cơ bản:
• 4. Truy vấn
17
AIO 2024 aivietnam.edu.vn
a) SimpleDirectoryReader
SimpleDirectoryReader cực kỳ tiện dụng bởi khả năng đọc được hầu hết các loại tài liệu phổ biến
như PDF, Word, CSV, TXT. Việc của chúng ta là tạo một thư mục chứa các loại dữ liệu và đưa cho
SimpleDirectoryReader, nó sẽ tự xử lý, nó tự động đến mức mà chúng ta không cần phải chỉ rõ từng
loại file, nó tự động lựa chọn công cụ phù hợp với từng file mà nó nhận dạng bởi đuôi file. Với các file
PDF, CSV nó sử dụng các thư viện như PyPDF... để tải các dữ liệu này.
Cú pháp:
1 from llama_index . core import Simple Direct oryRe ader
2 documents = Simp leDir ectory Reade r ( input_dir : str , input_files : List , recursive : bool )
Trong đó:
• input_dir: str: Đường dẫn đến thư mục chứa các tệp tin mà bạn muốn nạp dữ liệu từ đó.
• input_files: List: Danh sách các tên tệp cụ thể , mặc định là None.
• recursive: bool: Nếu đặt True, SimpleDirectoryReader sẽ tìm kiếm và nạp dữ liệu từ tất cả các
thư mục con bên trong input_dir. Nếu đặt False, chỉ các tệp trong thư mục chính mới được nạp.
Ví dụ dưới đây sẽ sử dụng SimpleDirectoryReader để đọc file trong một thư mục chứa nhiều file:
1 from llama_index . core import Simple Direct oryRe ader
2
3 STORAGE_PATH = " docx "
4 documents = Simp leDir ectory Reade r (
5 STORAGE_PATH ,
6 recursive = True
7 ) . load_data ()
8 for i in documents [0]:
9 print ( i )
Kết quả trả về là danh sách các đối tượng Document được tạo từ các tài liệu, mỗi đối tượng có các
thuộc tính như id, metadata, text...
18
AIO 2024 aivietnam.edu.vn
b) SimpleWebPageReader
SimpleWebPageReader là một công cụ giúp tải nội dung từ các trang web trực tiếp vào LlamaIndex.
Công cụ này tự động chuyển đổi HTML thành văn bản để dễ dàng xử lý và phân tích.
Cú Pháp:
1 reader = SimpleWebPageReader ( html_to_text = True )
2 pages = reader . load_data ( urls =[ " URL1 " , " URL2 " , ...])
Trong đó:
• html_to_text: bool : Nếu đặt ‘True‘, nội dung HTML sẽ được chuyển đổi thành văn bản thuần
túy.
• urls: List[str]: Danh sách các URL của các trang web mà ta muốn tải nội dung.
Ví dụ dưới đây chungs ta sẽ tải nội dung một bài viết trên trang web suckhoevadoisong.vn:
1 from llama_index . readers . web import SimpleWebPageReader
2
3 reader = SimpleWebPageReader ( html_to_text = True )
4 pages = reader . load_data ( urls =[ " https :// suckhoedoisong . vn / viet - nam - co - khoang -14 - trieu -
nguoi - roi - loan - tam - than -169230803214300404. htm " ])
5 print ( pages [0]. text )
• SimpleWebPageReader được khởi tạo với html_to_text=True, nghĩa là trang web sẽ được chuyển
đổi từ HTML sang văn bản.
• load_data được sử dụng để tải nội dung từ URL được chỉ định. Trong ví dụ này, nội dung từ
trang báo sức khỏe sẽ được tải về.
c) LlamaParse
LlamaParse là nền tảng phân tích tài liệu genAI đầu tiên trên thế giới, được xây dựng đặc biệt cho các
trường hợp sử dụng liên quan đến LLMs (Large Language Models). Mục tiêu chính của LlamaParse là
giúp phân tích và làm sạch dữ liệu để đảm bảo chất lượng tốt nhất trước khi sử dụng trong các ứng
dụng liên quan đến LLMs, như Retrieval-Augmented Generation (RAG).
Chức Năng Chính:
• Trích xuất bảng hiện đại: LlamaParse có khả năng trích xuất dữ liệu từ các bảng một cách hiệu
quả.
• Hỗ trợ nhiều loại tệp: Hỗ trợ hơn 10 loại tệp khác nhau như .pdf, .pptx, .docx, .html, .xml, và
nhiều hơn nữa.
• Hỗ trợ ngôn ngữ nước ngoài: Có khả năng làm việc với nhiều ngôn ngữ khác nhau.
Ví Dụ: Ví dụ sau đây, chúng ta sử dụng LlamaParse để phân tích một tài liệu PDF. Chúng ta thiết lập
môi trường, tạo một đối tượng parser với result_type="text"để chỉ định rằng chúng ta muốn đầu ra là
văn bản. Sau đó, chúng ta sử dụng SimpleDirectoryReader để đọc dữ liệu từ thư mục "files/"và phân
tích nội dung của các tệp PDF bằng LlamaParse.
19
AIO 2024 aivietnam.edu.vn
5 # Th ê m key v à o m ô i tr ư ờ ng
6 os . environ [ " LLAMA_CLOUD_API_KEY " ] = " llx - your - llama - cloud - api - key "
7
8 # Kh ở i t ạ o đ ố i t ư ợ ng LlamaParse
9 parser = LlamaParse ( result_type = " text " )
10
11 # Ch ỉ đ ị nh tr ì nh ph â n t í ch t ệ p cho . pdf
12 file_extractor = { " . pdf " : parser }
13
Để thực hiện ví dụ trên, chúng ta cần tạo Llama Cloud API Key tại trang web cloud.llamaindex.ai.
Hiện tại Llamacloud cho phép chúng ta sử dụng miễn phí với giới hạn sử dụng xử lí 1000 trang tài liệu
một ngày. Mình đánh giá rất cao công cụ này bởi khả năng xử lí các định dạng tài liệu mạnh mẽ. Trong
ví dụ trên, file pdf có cấu trúc phức tạp gồm cả văn bản và bảng, nhưng LlamaParse cho kết quả đọc
file rất xuất sắc, các bạn có thể so sánh với việc đọc file chỉ dùng SimpleDirectoryReader thông thường
để thấy sự khác biệt này. Nhưng nói chung không có miếng bánh nào miễn phí cả, đối với các ứng dụng
thương mại, chúng ta cần phát triển một công cụ riêng sẽ tối ưu hơn.
a) SimpleFileNodeParser
SimpleFileNodeParser là một công cụ trong LlamaIndex giúp phân tích tài liệu từ các tệp và tự động
chọn phương pháp phân tích nút (node) tốt nhất cho từng loại nội dung. Nó đặc biệt hữu ích khi làm
việc với nhiều loại tệp khác nhau.
Trong ví dụ dưới đây, chúng ta sử dụng FlatReader để tải dữ liệu từ tệp Markdown (.md), sau đó sử
dụng SimpleFileNodeParser để phân tích dữ liệu và tạo ra các nút từ tài liệu.
1 from llama_index . core . node_parser import SimpleFileNodeParser
2 from llama_index . readers . file import FlatReader
3 from pathlib import Path
4
5 md_docs = FlatReader () . load_data ( Path ( " files / md_test . md " ) )
6
7 parser = SimpleFileNodeParser ()
8 md_nodes = parser . g e t _n o d es _ f ro m _ do c u me n t s ( md_docs )
9 print ( md_nodes [0])
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Node ID : 87571 ec5 -00 ab -40 cc - b2df -63 edcc34309d
Text : Documents M ộ t th à nh ph ầ n kh ô ng th ể thi ế u trong c á c ứ ng d ụ ng RAG
l à d ữ li ệu , ch ú ng ta c ó nhi ề u lo ạ i d ữ li ệ u kh á c nhau nh ư : pdf , docx ,...
======================================================================================
20
AIO 2024 aivietnam.edu.vn
Ngoài ra chúng ta có thể sử dụng các trình phân tích cú pháp cho từng loại dữ liệu cụ thể như:
HTMLNodeParser, JSONNodeParser, MarkdownNodeParser. Nhưng mình cho rằng chỉ cần dùng Sim-
pleFileNodeParser là đủ, nó sẽ lựa chọn các công cụ phù hợp nhất với dữ liệu.
• CodeSplitter: Tạo node từ việc phân tách dữ liệu code. Ví dụ như các chương trình python.
• SentenceSplitter: phân tách văn bản trong khi vẫn tôn trọng ranh giới của câu.
• SentenceWindowNodeParser: Mỗi node được tạo từ 1 câu, ngoài ra nó còn sử dụng các câu phía
trước vầ phía sau câu hiện tại để làm ngữ cảnh.
• SemanticSplitterNodeParser: Thay vì tạo node từ các câu theo độ dài cố định, nó lấy các câu theo
ngữ nghĩa.
• TokenTextSplitter: Phương pháp tạo node bằng cách chia Document thành những phần nhỏ hơn
theo chunk_size mà chúng ta đã được giới thiệu trong phần trước.
• HierarchicalNodeParser: Thực hiện tạo node theo phương pháp phân cấp.
Trong các phương pháp trên, tùy trường hợp mà chúng ta sẽ lựa chọn phương pháp phù hợp, cách ví dụ
cho từng phương pháp các bạn xem thêm ở node_parsers. Phương pháp SemanticSplitterNodeParser
khá hay nên chúng ta hãy cùng tìm hiểu phương pháp này.
"Semantic Chunking"là một phương pháp mới được đề xuất bởi Greg Kamradt, thay vì chia văn bản
theo kích thước cố định, phương pháp này sử dụng sự tương đồng ngữ nghĩa để chọn điểm ngắt giữa
các câu. Điều này đảm bảo rằng mỗi "chunk"chứa các câu có liên quan về mặt ngữ nghĩa, giúp tạo ra
các khối văn bản có ý nghĩa hơn khi làm việc với các mô hình ngôn ngữ lớn (LLMs).
Phương pháp này đã được tích hợp vào module LlamaIndex với tên gọi SemanticSplitterNodeParser.
Trong ví dụ dưới đây, chúng ta sử dụng SemanticSplitterNodeParser để phân chia văn bản dựa trên
ngữ nghĩa, thay vì sử dụng kích thước cố định. Điều này được thực hiện bằng cách tính toán độ tương
đồng giữa các câu và chọn điểm ngắt phù hợp.
21
AIO 2024 aivietnam.edu.vn
5 text = """ M è o r ấ t đ á ng y ê u .
6 Acid hydrochloric đ ậ m đ ặ c nh ấ t c ó n ồ ng đ ộ t ố i đ a l à 40%.
7 Ở d ạ ng đ ậ m đ ặc , acid n à y c ó th ể t ạ o th à nh c á c s ư ơ ng m ù acid ,
8 ch ú ng đ ề u c ó kh ả n ă ng ă n m ò n c á c m ô con ng ư ời , g â y t ổ n th ư ơ ng c ơ quan h ô h ấp ,
9 m ắt , da v à ru ộ t . """
10
11 doc = Document ( text = text )
12
13 # S ử d ụ ng m ô h ì nh OpenAI đ ể t í nh to á n embedding
14 embed_model = OpenAIEmbedding ()
15
16 # Kh ở i t ạ o S e m a n t i c S p l i t t e r N o d e P a r s e r v ớ i c á c tham s ố t ù y ch ỉ nh
17 splitter = S e m a n t i c S p l i t t e r N o d e P a r s e r (
18 buffer_size =1 ,
19 b r e a k p o i n t _ p e r c e n t i l e _ t h r e s h o l d =95 ,
20 embed_model = embed_model
21 )
22
23 nodes = splitter . g e t _n o d es _ f ro m _ do c u me n t s ([ doc ])
24 for node in nodes :
25 print ( node )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Node ID : 6 d15126c -007 d -4 dc2 -9123 -7287 da01afc1
Text : M è o r ấ t đ á ng y ê u .
Node ID : 5971 de1f -8369 -4 fc6 - b7ec -478 a5697bb54
Text : Acid hydrochloric đ ậ m đ ặ c nh ấ t c ó n ồ ng đ ộ t ố i đ a l à 40%.
Ở d ạ ng đ ậ m đ ặc , acid n à y c ó th ể t ạ o th à nh c á c s ư ơ ng m ù acid ,
ch ú ng đ ề u c ó kh ả n ă ng ă n m ò n c á c m ô con ng ư ời , g â y t ổ n th ư ơ ng c ơ quan
h ô h ấp , m ắt , da v à ru ộ t .
======================================================================================
Trong ví dụ trên:
• buffer_size=1: Tham số này xác định số lượng câu được xem xét trước và sau câu hiện tại để xác
định điểm ngắt. Với buffer_size=1, splitter sẽ xem xét câu trước và câu sau câu hiện tại khi đánh
giá mức độ tương đồng ngữ nghĩa.
• breakpoint_percentile_threshold=95: Đây là ngưỡng phần trăm được sử dụng để xác định điểm
ngắt giữa các câu. Nếu điểm tương đồng ngữ nghĩa giữa hai câu thấp hơn ngưỡng 95%, thì đó là
một điểm ngắt tiềm năng. Ngưỡng càng cao, việc ngắt câu sẽ càng ít xảy ra, và các chunk sẽ lớn
hơn.
Kết quả chương trình đã tạo cho chúng ta 2 node, mỗi node chứa nội dung riêng biệt theo ngữ nghĩa
câu chứng tỏ phương pháp này rất tiềm năng. Tuy nhiên việc sử dụng embedding từ API của OPENAI
sẽ tốn phí. Chúng ta có thể thay thế bằng các mô hình tương đương trên huggingface.
22
AIO 2024 aivietnam.edu.vn
• TitleExtractor(): Trích xuất tiêu đề hoặc nội dung chính của tài liệu để dễ dàng định vị thông tin
quan trọng.
• QuestionsAnsweredExtractor(): Trích xuất các câu hỏi mà đoạn văn bản trả lời, giúp xác định
nhanh nội dung chính.
• SummaryExtractor(): Tạo tóm tắt cho đoạn văn bản hiện tại và cả các đoạn trước đó, giúp duy
trì ngữ cảnh.
• KeywordExtractor(): Trích xuất các từ khóa quan trọng từ văn bản để định vị nội dung cốt lõi.
• EntityExtractor(): Trích xuất các thực thể (như tên người, địa điểm) với độ chính xác cao, giúp
định danh thông tin quan trọng trong đoạn văn bản.
Trước khi đi vào phần hướng dẫn sử dụng từng công cụ, chúng ta cùng xem lại cấu trúc của Document,
Node.
1 from llama_index . core . node_parser import S e m a n t i c S p l i t t e r N o d e P a r s e r
2 from llama_index . embeddings . openai import OpenAIEmbedding
3 from llama_index . core import Simple Direct oryRe ader
4
5 # Đ ọ c d ữ li ệ u t ừ th ư m ụ c
6 reader = Simple Direc toryRe ader ( input_files =[ " files / test_metadata_ex . txt " ])
7 docs = reader . load_data ()
8
9 # S ử d ụ ng m ô h ì nh OpenAI đ ể t í nh to á n embedding
10 embed_model = OpenAIEmbedding ()
11
12 # Kh ở i t ạ o S e m a n t i c S p l i t t e r N o d e P a r s e r v ớ i c á c tham s ố t ù y ch ỉ nh
13 splitter = S e m a n t i c S p l i t t e r N o d e P a r s e r (
14 buffer_size =1 ,
15 b r e a k p o i n t _ p e r c e n t i l e _ t h r e s h o l d =95 ,
16 embed_model = embed_model
17 )
18
19 nodes = splitter . g e t _n o d es _ f ro m _ do c u me n t s ( docs )
20
21 print ( " document metadata " , docs [0]. metadata )
22 print ( " node metadata " , nodes [0]. metadata )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
document metadata { ’ file_path ’: ’ files / test_metadata_ex . txt ’ , ’ file_name ’: ’
test_metadata_ex . txt ’ , ’ file_type ’: ’ text / plain ’ , ’ file_size ’: 308 , ’ creation_date
’: ’2024 -08 -12 ’ , ’ last_modified_date ’: ’2024 -08 -12 ’}
23
AIO 2024 aivietnam.edu.vn
Thành phần metadata trong Document được tạo tự động, chứa thông tin về file dữ liệu gốc như vị
trí.... Khi tạo node, dữ liệu này tự động được gán cho node nên chúng ta có thể thấy kết quả đầu ra
metadata của node con giống hệt trong document.
Quay trở lại với nội dung chính của phần này, nhiệm vụ của chúng ta là khai thác thuộc tính metadata
trong node để hỗ trợ việc tìm kiếm hiệu quả hơn. Theo như kết quả của ví dụ trên thì chúng ta chưa
thấy được lợi ích nào của metadata trong node. Chính vì vậy, chúng ta sẽ sử dụng các phương pháp
trích xuất metadata từ node để tạo ra những thông tin có giá trị.
a) TitleExtractor
TitleExtractor là công cụ giúp chúng ta trích xuất tiêu đề cho các node. Nó giúp chúng ta phân loại
các node theo các chủ đề khác nhau, từ đó nâng cao hiệu quả khi tìm kiếm. Việc này giống như chúng
ta có một cuốn sách dày cộp, chúng ta muốn tìm kiếm nội dung thuộc một chủ đề cụ thể, nhưng các
nội dung này nằm rải rác khắp cuốn sách, nếu đọc hết cuốn sách để tìm từng đoạn thì mất rất nhiều
công sức phải không nào? Trong trường hợp này, nếu sử dụng TitleEtractor thì sẽ hiệu quả hơn nhiều.
Hãy cùng xem ví dụ dưới đây:
1 import nest_asyncio
2 nest_asyncio . apply ()
3 from llama_index . core . extractors import TitleExtractor
4 title_extractor = TitleExtractor ()
5 metadata_list = title_extractor . extract ( nodes )
6 for metadata in metadata_list :
7 print ( metadata )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
{ ’ document_title ’: ’" Kh á m Ph á Th ế Gi ớ i Đ ộ ng V ậ t v à H ó a H ọ c : T ừ S ự Đ á ng Y ê u C ủ a M è o Đ ế n
T á c Đ ộ ng C ủ a Axit Hydrochloric " ’}
{ ’ document_title ’: ’" Kh á m Ph á Th ế Gi ớ i Đ ộ ng V ậ t v à H ó a H ọ c : T ừ S ự Đ á ng Y ê u C ủ a M è o Đ ế n
T á c Đ ộ ng C ủ a Axit Hydrochloric " ’}
======================================================================================
Trong ví dụ trên chúng ta thực hiện trích xuất title cho các node, các tham số trong TitleExtractor
chúng ta sử dụng mặc định. Tuy nhiên, có một số tham số có thể điều chỉnh là:
• node_template: Prompt dùng trích xuất tiêu đề, mặc định dùng mẫu có sẵn của thư viện.
Đây là nội dung của title template mặc định, chúng ta hoàn toàn có thể dựa vào đây để thiết kế
một mẫu mới:
1 D E F A U L T _ T I T L E _ N O D E _ T E M P L A T E = """\
2 Context : { context_str }. Give a title that summarizes all of \
3 the unique entities , titles or themes found in the context . Title : """
24
AIO 2024 aivietnam.edu.vn
b) QuestionsAnsweredExtractor
QuestionsAnsweredExtractor giúp xác định các câu hỏi mà văn bản có thể trả lời, giúp quá trình truy
xuất tập trung vào những nút có thông tin chính xác cho các câu hỏi cụ thể. Ví dụ, trong hệ thống
FAQ, công cụ này giúp nhận diện các câu hỏi duy nhất mà bài viết có thể trả lời, từ đó dễ dàng tìm
kiếm câu trả lời chính xác cho các truy vấn của người dùng.
Ví dụ:
1 from llama_index . core . extractors import Q u e s t i o n s A n s w e r e d E x t r a c t o r
2 qa_extractor = Q u e s t i o n s A n s w e r e d E x t r a c t o r ( questions =3)
3 metadata_list = qa_extractor . extract ( nodes )
4 print ( metadata_list )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
[{ ’ questions_this_excerpt_can_answer ’: ’ Based on the provided context , here are three
specific questions that can be answered using the information given :\ n \ n1 . ** What
is the file size of the document named " test_metadata_ex . txt "?**\ n - Answer : The
file size is 308 bytes .\ n \ n2 . ** On what date was the file " test_metadata_ex . txt "
created ?**\ n - Answer : The file was created on August 12 , 2024.\ n \ n3 . ** What is
the MIME type of the file " test_metadata_ex . txt "?**\ n - Answer : The file type is
text / plain . ’} , { ’ questions_this_excerpt_can_answer ’: " Based on the provided context
about hydrochloric acid and its properties , here are three specific questions that
can be answered :\ n \ n1 . ** What is the maximum concentration of concentrated
hydrochloric acid ?**\ n ..."}]
======================================================================================
Theo kinh nghiệm của mình, với các dự án sử dụng ngôn ngữ khác như Tiếng Việt thì chúng ta cần
thiết kế , hay đơn giản là chỉnh sửa Propmt mặc định sang tiếng việt. Ví dụ như trường hợp trên, kết
quả trả về là tiếng Anh, mình nghĩ nó sẽ có vấn đề mặc dù các mô hình LLM hiện tại hỗ trợ đa ngôn
ngữ.
Sau đây, chúng ta sẽ chỉnh sửa prompt để kết quả đầu ra là tiếng việt. Chúng ta sẽ làm theo cách đơn
giản nhất là lấy propmt mặc định rồi viết thêm yêu cầu trả về kết quả tiếng Việt là xong.
1 C U S T O R M _ Q U E S T I O N _ GE N _ T M P L = """\
2 Here is the context :
3 { context_str }
4
5 Given the contextual information , \
6 generate { num_questions } questions this context can provide \
7 specific answers to which are unlikely to be found elsewhere .
8
9 Higher - level summaries of surrounding context may be provided \
10 as well . Try using these summaries to generate better questions \
11 that this context can answer .
12
13 L ư u ý : H ã y tr ả v ề k ế t qu ả b ằ ng ti ế ng Vi ệ t .
14 """
Đối với chương trình thì vẫn giữ như cũ, chỉ thêm tham số prompt_template khi khởi tạo Question-
sAnsweredExtractor.
1 from llama_index . core . extractors import Q u e s t i o n s A n s w e r e d E x t r a c t o r
2 qa_extractor = Q u e s t i o n s A n s w e r e d E x t r a c t o r ( questions =3 , prompt_template =
C U S T O R M_ Q U E S T I O N _ GE N _ T M P L )
3 metadata_list = qa_extractor . extract ( nodes )
4 print ( metadata_list )
25
AIO 2024 aivietnam.edu.vn
c) SummaryExtractor
SummaryExtractor tạo ra các tóm tắt ngắn gọn cho từng nút và các nút lân cận, giúp tăng cường hiệu
quả truy xuất thông tin.
1 from llama_index . core . extractors import SummaryExtractor
2
3 C U S T O R M _ S U M M A R Y _ E X T R A C T _ T E M P L A T E = """ \
4 Here is the content of the section :
5 { context_str }
6
7 Summarize the key topics and entities of the section . \
8 L ư u ý : H ã y tr ả v ề k ế t qu ả b ằ ng ti ế ng Vi ệ t .\
9 Summary : """
10 summa ry_extractor = SummaryExtractor ( summaries =[ " prev " , " self " , " next " ] ,
prompt_template = C U S T O R M _ S U M M A R Y _ E X T R A C T _ T E M P L A T E )
11 metadata_list = summary_extractor . extract ( nodes )
12 print ( metadata_list )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
[{ ’ next_section_summary ’: ’T ó m t ắ t :\ n \n - ** Ch ủ đ ề ch í nh **: Acid hydrochloric ( HCl ) v à
t á c đ ộ ng c ủ a n ó .\ n - ** N ồ ng đ ộ **: Acid hydrochloric đ ậ m đ ặ c nh ấ t c ó n ồ ng đ ộ t ố i đ a
l à 40%.\ n - ** T á c h ạ i **: Acid hydrochloric c ó th ể t ạ o th à nh s ư ơ ng m ù acid , g â y ă n m ò n
m ô con ng ư ờ i v à t ổ n th ư ơ ng cho c á c c ơ quan nh ư h ô h ấp , m ắt , da v à ru ộ t . ’ , ’
section_summary ’: ’T ó m t ắ t : \ n \ nN ộ i dung c ủ a ph ầ n n à y ch ứ a th ô ng tin v ề m ộ t t ệ p tin
c ó t ê n " test_metadata_ex . txt ". T ệ p n à y thu ộ c lo ạ i v ă n b ả n ( text / plain ) ,
c ó k í ch th ư ớ c 308 byte , v à đ ư ợ c t ạ o ra c ũ ng nh ư ch ỉ nh s ử a v à o ng à y 12 th á ng 8 n ă m
2024.
Ngo à i ra , ph ầ n n ộ i dung c ủ a t ệ p tin đ ề c ậ p đ ế n m èo , m ô t ả ch ú ng l à r ấ t đ á ng y ê u .
\ n \ nC á c ch ủ đ ề v à th ự c th ể ch í nh bao g ồ m :\ n - T ê n t ệ p : test_metadata_ex . txt \n - Lo ạ i t ệ p
: v ă n b ả n \n - K í ch th ư ớ c t ệ p : 308 byte \n - Ng à y t ạ o v à ch ỉ nh s ử a : 12/08/2024\ n - N ộ i
dung : M è o đ á ng y ê u . ’} , { ’ prev_section_summary ’: ’T ó m t ắ t : \ n \ nN ộ i dung c ủ a ph ầ n n à y
ch ứ a th ô ng tin v ề m ộ t t ệ p tin c ó t ê n " test_metadata_ex . txt ".
T ệ p n à y thu ộ c lo ạ i v ă n b ả n ( text / plain ) , c ó k í ch th ư ớ c 308 byte , v à đ ư ợ c t ạ o ra c ũ ng
nh ư ch ỉ nh s ử a v à o ng à y 12 th á ng 8 n ă m 2024. Ngo à i ra , ph ầ n n ộ i dung c ủ a t ệ p tin đ ề
c ậ p đ ế n m èo , m ô t ả ch ú ng l à r ấ t đ á ng y ê u . \ n \ nC á c ch ủ đ ề v à th ự c th ể ch í nh bao g ồ m
:\ n - T ê n t ệ p : test_metadata_ex . txt \n - Lo ạ i t ệ p : v ă n b ả n \n - K í ch th ư ớ c t ệ p : 308 byte
\n - Ng à y t ạ o v à ch ỉ nh s ử a : 12/08/2024\ n - N ộ i dung : M è o đ á ng y ê u . ’ , ’ section_summary
’: ’T ó m t ắ t :\ n \n - ** Ch ủ đ ề ch í nh **: Acid hydrochloric ( HCl ) v à t á c đ ộ ng c ủ a n ó .\ n -
** N ồ ng đ ộ **: Acid hydrochloric đ ậ m đ ặ c nh ấ t c ó n ồ ng đ ộ t ố i đ a l à 40%.\ n - ** T á c h ạ i
**: Acid hydrochloric c ó th ể t ạ o th à nh s ư ơ ng m ù acid , g â y ă n m ò n m ô con ng ư ờ i v à
t ổ n th ư ơ ng cho c á c c ơ quan nh ư h ô h ấp , m ắt , da v à ru ộ t . ’}]
======================================================================================
Trong ví dụ trên chúng ta cần chú ý đến tham số summaries: Danh sách các loại tóm tắt mà ta muốn
tạo cho các nút. Các giá trị có thể là:
26
AIO 2024 aivietnam.edu.vn
d) KeywordExtractor
KeywordExtractor giúp trích xuất các từ khóa quan trọng từ nội dung văn bản. Những từ khóa này rất
hữu ích trong việc tìm kiếm và truy xuất các nút liên quan dựa trên truy vấn của người dùng
1 from llama_index . core . extractors import KeywordExtractor
2
3 key_extractor = KeywordExtractor ( keywords =3)
4 metadata_list = key_extractor . extract ( nodes )
5 print ( metadata_list )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
[{ ’ excerpt_keywords ’: ’ Keywords : m èo , đ á ng y êu , th ú c ư ng ’} , { ’ excerpt_keywords ’: ’
Keywords : acid hydrochloric , n ồ ng đ ộ , ă n m òn ’}]
======================================================================================
Trong ví dụ trên, chúng ta tiến hành trích xuất 3 keyword từ mỗi node, các tham số tương tự như các
trình trích xuất khác.
e) EntityExtractor
EntityExtractor giúp nhận diện và trích xuất các thực thể được đặt tên từ văn bản, chẳng hạn như
tên người, địa điểm, tổ chức, và nhiều loại thực thể khác. Việc này giúp hệ thống truy xuất có thể tập
trung vào các nút có chứa thông tin cụ thể, cung cấp ngữ cảnh cần thiết để cải thiện độ chính xác của
kết quả tìm kiếm.
Đối với công cụ này, yêu cầu chúng ta cài đặt thêm gói bổ xung sau:
1 pip install llama - index - extractors - entity
2 pip install transformers ==4.40.2
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
[{} , { ’ entities ’: [ ’ h ô h ấp ’]}]
======================================================================================
• label_entities: Nếu đặt thành True, trình trích xuất sẽ gắn nhãn (label) cho mỗi tên thực thể với
một loại thực thể tương ứng, ví dụ như người, địa điểm, hoặc tổ chức. Điều này rất hữu ích trong
giai đoạn truy xuất và truy vấn sau này. Mặc định, tham số này được đặt thành False.
27
AIO 2024 aivietnam.edu.vn
• device: Xác định thiết bị mà mô hình sẽ chạy trên đó. Mặc định là "cpu", nhưng nếu hệ thống
của chúng ta hỗ trợ, ta có thể đặt thành "cuda"để sử dụng GPU, giúp tăng tốc quá trình xử lý.
• entity_map: Cho phép ta tùy chỉnh nhãn cho từng loại thực thể. Trình trích xuất đi kèm với một
bộ nhãn thực thể được định nghĩa trước, nhưng cũng có thể điều chỉnh chúng theo nhu cầu cụ
thể của mình.
13 # t ạ o pipeline v ớ i c á c b ư ớ c chuy ể n đ ổ i
14 pipeline = IngestionPipeline (
15 transformations =[
16 SentenceSplitter ( chunk_size =50 , chunk_overlap =0) ,
17 TitleExtractor () ,
18 OpenAIEmbedding () ,
19 ]
20 )
21
22 # ch ạ y pipeline
23 nodes = pipeline . run ( documents =[ doc ])
24 for node in nodes :
25 print ( node )
26 print ( node . metadata )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Node ID : ca641c7a -3279 -4 c65 - b1cc -58 b48f478c1a
Text : H ô m nay S à i G ò n m ư a qu á , th è m m ộ t chi ế c kem l ạ nh th ấ u t â m can .
{ ’ document_title ’: ’" Nh ữ ng C ơ n M ư a S à i G ò n v à N ỗ i Nh ớ Qu ê : H à nh Tr ì nh T ì m Ki ế m K ý Ứ c
Qua Nh ữ ng Gi ọ t M ư a " ’}
Node ID : b1b07eb7 -99 c0 -44 bb -959 e -02370 ddbb314
Text : Ngo à i tr ờ i m ư a v ẫ n r ơ i nh ư tr út , t ô i th ì c ạ n kh ô !
{ ’ document_title ’: ’" Nh ữ ng C ơ n M ư a S à i G ò n v à N ỗ i Nh ớ Qu ê : H à nh Tr ì nh T ì m Ki ế m K ý Ứ c
Qua Nh ữ ng Gi ọ t M ư a " ’}
======================================================================================
28
AIO 2024 aivietnam.edu.vn
• Document: Đầu tiên, đoạn văn bản được gói gọn trong một đối tượng Document.
• SentenceSplitter: Bước đầu tiên trong pipeline là chia đoạn văn bản thành các câu nhỏ hơn (kích
thước tối đa là 25 ký tự). Điều này giúp xử lý từng phần của văn bản một cách chi tiết.
• TitleExtractor: Tiếp theo, công cụ này sẽ trích xuất một tiêu đề cho từng phần văn bản. Trong
ví dụ này, tiêu đề "Những Cảm Xúc Trong Mưa: Hành Trình Tìm Kiếm Vị Ngọt và Ý Nghĩa"đã
được tạo ra cho tất cả các đoạn.
• OpenAIEmbedding: Cuối cùng, embeddings được tạo ra từ các đoạn văn bản. Embeddings là các
biểu diễn toán học của văn bản, giúp mô hình AI hiểu và làm việc với chúng.
Chúng ta có thể lưu lại cache và load lại theo cách sau:
1 # L ư u tr ữ k ế t qu ả v à o cache
2 pipeline . persist ( " pipeline_cache " )
3 # Load k ế t qu ả t ừ cache
4 new_pipeline = IngestionPipeline (
5 transformations =[
6 SentenceSplitter ( chunk_size =50 , chunk_overlap =0) ,
7 TitleExtractor () ,
8 OpenAIEmbedding () ,
9 ]
10 )
11 new_pipeline . load ( " pipeline_cache " )
12 nodes = new_pipeline . run ( documents =[ doc ])
13 for node in nodes :
14 print ( node )
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Node ID : 08 b538e8 - c8a9 -4 ff8 - a1ae -5 f3fec8da531
Text : H ô m nay S à i G ò n m ư a qu á , th è m m ộ t chi ế c kem l ạ nh th ấ u t â m can .
Node ID : 671 fe15f -0613 -4 b91 -923 f - bb491b081d59
Text : Ngo à i tr ờ i m ư a v ẫ n r ơ i nh ư tr út , t ô i th ì c ạ n kh ô !
======================================================================================
Vậy là chúng ta đã lưu và load lại pipeline thành công, điều này rất hữu ích khi triển khai ứng dụng
thực tế và giảm chi phí tạo node.
– authenticate.py: Module xử lí xác thực người dùng như đăng nhập, đăng ký.
– conversation_engine.py: Module tạo Agent, quản lí cuộc trò chuyện.
29
AIO 2024 aivietnam.edu.vn
• ṡtreamlit: Thư mục chứa các khóa API,tùy chỉnh cấu hình streamlit.
Sau khi chúng ta tạo dự án với cấu trúc trên, hoặc có thể clone từ github tại đây, sau đó tiến hành tạo
môi trường và cài đặt các gói cần thiết, tuy nhiên bạn có thể sử dụng môi trường từ phần hướng dẫn
các ví dụ trên.
Để tạo môi trường mới, chúng ta sử dụng lệnh sau:
1 conda create -n aio_mental_health python =3.11
Hoặc có thể cài đặt nhanh bằng cách sử dụng file requirements.txt được cung cấp kèm theo mã nguồn:
1 pip install -r requirements . txt
Sau khi thiết lập môi trường, chúng ta nhập thông tin khóa OpenAI API vào file secrets.toml với cấu
trúc sau:
1 [ openai ]
2 OPENAI_API_KEY = " sk - Your - API - KEY "
Chúng ta tiến hành làm việc với module global_settings.py, thiết lập tên file, các đường dẫn cần thiết
để phục vụ cho hoạt động lưu trữ, truy xuất dữ liệu của toàn bộ hệ thống.
1 CACHE_FILE = " data / cache / pipeline_cache . json "
2 CONVE RSATION_FILE = " data / cache / chat_history . json "
3 STORAGE_PATH = " data / ingestion_storage / "
4 FILES_PATH = [ " data / ingestion_storage / dsm -5 - cac - tieu - chuan - chan - doan . docx " ]
5 INDEX_STORAGE = " data / index_storage "
6 SCORES_FILE = " data / user_storage / scores . json "
7 USERS_FILE = " data / user_storage / users . yaml "
Trong đó:
• CACHE_FILE: Đường dẫn file cache cho pipeline, lưu trữ dữ liệu tạm thời sau khi xử lý.
30
AIO 2024 aivietnam.edu.vn
• CONVERSATION_FILE: Đường dẫn file lưu lịch sử hội thoại của người dùng.
• FILES_PATH: Danh sách file dữ liệu đầu vào cụ thể, ví dụ tài liệu DSM-5.
31
AIO 2024 aivietnam.edu.vn
Tiếp theo, chúng ta sẽ làm việc với module ingest_pipeline.py. Module này hỗ trợ chúng ta xây dựng
một pipeline để xử lý dữ liệu thô, tạo node và lưu trữ cache của pipeline vào CACHE_FILE để có thể
tải lại sau này.
1 from llama_index . core import Simple Direct oryRe ader
2 from llama_index . core . ingestion import IngestionPipeline , IngestionCache
3 from llama_index . core . node_parser import TokenTextSplitter
4 from llama_index . core . extractors import SummaryExtractor
5 from llama_index . embeddings . openai import OpenAIEmbedding
6 from llama_index . core import Settings
7 from llama_index . llms . openai import OpenAI
8 import openai
9 import streamlit as st
10 from src . global_settings import STORAGE_PATH , FILES_PATH , CACHE_FILE
11 from src . prompts import C U S T O R M _ S U M M A R Y _ E X T R A C T _ T E M P L A T E
12
13 openai . api_key = st . secrets . openai . OPENAI_API_KEY
14 Settings . llm = OpenAI ( model = " gpt -4 o - mini " , temperature =0.2)
15
16 def ingest_documents () :
17 # Load documents , easy but we can ’t move data or share for another device .
18 # Because document id is root file name when our input is a folder .
19 # documents = Simpl eDirec toryR eader (
20 # STORAGE_PATH ,
21 # filename_as_id = True
22 # ) . load_data ()
23
24 documents = Simp leDir ectory Reade r (
25 input_files = FILES_PATH ,
26 filename_as_id = True
27 ) . load_data ()
28 for doc in documents :
29 print ( doc . id_ )
30
31 try :
32 cached_hashes = IngestionCache . from_persist_path (
33 CACHE_FILE
34 )
35 print ( " Cache file found . Running using cache ... " )
36 except :
37 cached_hashes = " "
38 print ( " No cache file found . Running without cache ... " )
39 pipeline = IngestionPipeline (
40 transformations =[
41 TokenTextSplitter (
42 chunk_size =512 ,
43 chunk_overlap =20
44 ),
45 SummaryExtractor ( summaries =[ ’ self ’] , prompt_template =
CUSTORM_SUMMARY_EXTRACT_TEMPLATE ),
46 OpenAIEmbedding ()
47 ],
48 cache = cached_hashes
49 )
50
51 nodes = pipeline . run ( documents = documents )
52 pipeline . cache . persist ( CACHE_FILE )
53
54 return nodes
32
AIO 2024 aivietnam.edu.vn
• Đầu tiên là lấy API KEY từ file secrets bằng streamlit và thiết lập llm, mô hình chúng ta sử dụng
là gpt-4o-mini với mức độ ngẫu nhiên 0.2
• Tiếp theo hàm ingest_documents() thực hiện đọc dữ liệu thô bằng công cụ SimpleDirecto-
ryReader, trong trường hợp này, đầu vào chúng ta sử dụng là danh sách files dữ liệu cụ thể,
vì nếu dùng đầu vào là đường dẫn thư mục như các ví dụ trước, id của documents được tạo ra sẽ
có giá trị là đường dẫn tuyệt đối đến file, chính vì vậy khi chúng ta muốn chia sẻ dữ liệu sau khi
xử lí cho người khác hoặc muốn dùng lại trong dự án khác sẽ không sử dụng được.
• Sau đó thì đến phần pipeline để xử lí dữ liệu, đầu tiên chúng ta kiểm tra xem đã có dữ liệu
cache pipeline nào trước đó chưa, nếu có rồi thì load lại, nếu chưa có thì tạo cache mới. Sau đó
tiến hành tạo pipeline sử dụng kỹ thuật chia nhỏ dữ liệu TokenTextSplitter với chunk_size=512,
chunk_overlap=20. Một số thí nghiệm trên llamaindex chỉ ra rằng giá trị chunk_size=512 hoặc
1024 sẽ cho kết quả tốt.
• Tiếp theo chúng ta sử dụng kỹ thuật khai thác metadata với SummaryExtractor, trong dự án này
chúng ta chỉ tạo tóm tắt tại chính node hiện tại vì vậy chúng ta sử dụng tham số self, ngoài ra
vấn đề đã đề cập trong ví dụ trước là do prompt mặc định của SummaryExtractor viết bằng tiếng
anh, trong khi chúng ta đang làm dự án với dữ liệu tiếng Việt, vì vậy chúng ta cần thiết lập lại
prompt là tiếng Việt. Phần thiết lập promt bạn mở module prompts.py và thiết lập prompt như
sau:
1 C U S T O R M _ S U M M A R Y _ E X T R A C T _ T E M P L A T E = """ \
2 D ư ớ i đ â y l à n ộ i dung c ủ a ph ầ n :
3 { context_str }
4
5 H ã y t ó m t ắ t c á c ch ủ đ ề v à th ự c th ể ch í nh c ủ a ph ầ n n à y .
6
7 T ó m t ắ t : """
8
• Embedding: Thật ra đối với phương pháp không sử dụng pipeline thì phần này thuộc giai đoạn
tạo index, tuy nhiên khi sử dụng pipeline thì bắt buộc phải tạo embedding trong này. Theo mặc
định thì mô hình chúng ta sử dụng từ API là model – text-embedding-ada-002
• Cuối cùng chúng ta tạo node bằng cách chạy pipeline vừa tạo và lưu lại bộ nhớ đệm của pipeline
trong CACHE_FILE.
33
AIO 2024 aivietnam.edu.vn
• Lưu trữ Dữ liệu: Chỉ mục lưu trữ dữ liệu dưới dạng các đối tượng Node, đại diện cho các phần
nhỏ của tài liệu gốc. Điều này giúp truy xuất thông tin nhanh chóng và hiệu quả.
Có nhiều loại chỉ mục khác nhau như: Summary Index, Vector Store Index, Tree Index, Keyword Table
Index, Property Graph Index. Trong phần này chúng ta sẽ tập trung vào Vector Store Index, lưu trữ
và sử dụng nó.
4 text = """ M è o r ấ t đ á ng y ê u .
5 Acid hydrochloric đ ậ m đ ặ c nh ấ t c ó n ồ ng đ ộ t ố i đ a l à 40%.
6 Ở d ạ ng đ ậ m đ ặc , acid n à y c ó th ể t ạ o th à nh c á c s ư ơ ng m ù acid ,
7 ch ú ng đ ề u c ó kh ả n ă ng ă n m ò n c á c m ô con ng ư ời , g â y t ổ n th ư ơ ng c ơ quan h ô h ấp ,
8 m ắt , da v à ru ộ t . """
9 doc = Document ( text = text )
10 index = VectorStoreIndex . from_documents ([ doc ])
8 text = """ M è o r ấ t đ á ng y ê u .
9 Acid hydrochloric đ ậ m đ ặ c nh ấ t c ó n ồ ng đ ộ t ố i đ a l à 40%.
10 Ở d ạ ng đ ậ m đ ặc , acid n à y c ó th ể t ạ o th à nh c á c s ư ơ ng m ù acid ,
11 ch ú ng đ ề u c ó kh ả n ă ng ă n m ò n c á c m ô con ng ư ời , g â y t ổ n th ư ơ ng c ơ quan h ô h ấp ,
12 m ắt , da v à ru ộ t . """
13
14 doc = Document ( text = text )
34
AIO 2024 aivietnam.edu.vn
3 # L ư u tr ữ index v à o th ư m ụ c
4 index . storage_context . persist ( persist_dir = " index_cache " )
5
6 # Load index t ừ th ư m ụ c
7 storage_context = StorageContext . from_defaults (
8 persist_dir = " index_cache " )
9 reload_index = lo ad _ in de x _f ro m_ s to ra ge ( storage_context )
Trong chương trình trên, đầu tiên chúng ta sẽ lưu lại dữ liệu index vào thư mục index_cache. Thông
tin mặc định được lưu bao gồm vector index, doc store, tree index(file này sẽ trống vì chúng ta không
dùng tree, nó được taọ mặc đinh).
• index.storage_context: Đây là một đối tượng liên quan đến ngữ cảnh lưu trữ (storage context)
của chỉ mục. Nó lưu trữ thông tin về cách dữ liệu của chỉ mục được quản lý và lưu trữ.
• persist(persist_dir="index_cache"): Phương thức persist được gọi để lưu trữ toàn bộ chỉ mục
vào một thư mục được chỉ định, trong trường hợp này là "index_cache". Tất cả các thông tin cần
thiết cho chỉ mục, bao gồm dữ liệu vector và các siêu dữ liệu (metadata), được lưu trữ để có thể
tái sử dụng.
Sau đó để tải lại index thì chúng ta thực hiện tạo storage context từ kho lưu trữ index_cache. Cuối cùng
sử dụng hàm load_index_from_storage(storage_context) để tải lại index từ storage context. Hàm này
sử dụng thông tin từ storage_context để xác định vị trí và cách tải lại chỉ mục đã được lưu. Kết quả
là chỉ mục đã được khôi phục.
35
AIO 2024 aivietnam.edu.vn
b) Vector Database
Trong phần này, chúng ta sẽ sử dụng Chroma Database để lưu trữ vector index.
1 import chromadb
2 from llama_index . vector_stores . chroma import ChromaVectorStore
3 from llama_index . core import VectorStoreIndex , StorageContext , Document
4
5
6 db = chromadb . PersistentClient ( path = " database " )
7 chrom a_collection = db . g e t _o r _ cr e a te _ c ol l e ct i o n ( " my_chroma_store " )
8
9 vector_store = ChromaVectorStore (
10 chrom a_collection = chroma_collection
11 )
12 storage_context = StorageContext . from_defaults (
13 vector_store = vector_store
14 )
15
16 text = " I love my cat ! "
17 doc = Document ( text = text )
18 index = VectorStoreIndex . from_documents (
19 documents =[ doc ] ,
20 storage_context = storage_context ,
21 )
• Đầu tiên chúng ta tạo hoặc kết nối tới cơ sở dữ liệu vector (Chroma) tại đường dẫn "database"
• Tiếp theo ta tạo hoặc lấy một collection có tên "my_chroma_store"trong cơ sở dữ liệu để lưu trữ
vector.
• Ta tiếp tục tạo vector store dựa trên collection trong Chroma, nơi các vector sẽ được lưu trữ
• Sau đó tạo storage contex mặc định từ vector store, quản lý việc lưu trữ và truy xuất dữ liệu
vector.
• Cuối cùng, ta tạo index với đối số là danh sách doc và storage context
Để tải lại index thì chúng ta thực hiện kết nối, load vector store, storage context như bước trên. Sau
đó sử dụng VectorStoreIndex.from_vector_store để tạo lại index.
1 reload_index = VectorStoreIndex . from_vector_store (
2 vector_store = vector_store ,
3 storage_context = storage_context
4 )
36
AIO 2024 aivietnam.edu.vn
• INDEX_STORAGE từ module global_settings, chứa đường dẫn đến thư mục lưu trữ index.
• Tải lại index từ store: Hàm cố gắng tải index từ storage_context với index_id="vector". Nếu
việc tải thành công, nghĩa là các index đã tồn tại và được tải từ thư mục lưu trữ, thông báo "All
indices loaded from storage."sẽ được in ra.
Xử lý ngoại lệ:
• Ngoại lệ: Nếu có lỗi xảy ra trong quá trình tải index (chẳng hạn như không có index nào tồn tại
trong lưu trữ), thì ngoại lệ sẽ được bắt và một thông báo lỗi sẽ được in ra.
• Tạo mới các index: Khi không thể tải các index từ kho lưu trữ, một storage_context mới sẽ được
tạo. Sau đó, một VectorStoreIndex mới sẽ được tạo từ các nodes đầu vào và được liên kết với
storage_context này.
• index_id: Chúng ta chỉ định là vector vì trong kho dữ liệu chứa nhiều loại index khác nhau, chẳng
hạn như tree index.
• Lưu trữ index mới: Cuối cùng, các index mới sẽ được lưu trữ vào thư mục được chỉ định (IN-
DEX_STORAGE), và thông báo "New indexes created and persisted."sẽ được in ra để xác nhận
quá trình này.
• Trả về: Hàm build_indexes() sẽ trả về vector_index, index được tải hoặc tạo mới, để sử dụng
trong các phần tiếp theo của chương trình.
37
AIO 2024 aivietnam.edu.vn
• postprocessing: Hậu xử lý
Chúng ta sẽ tìm hiểu qua ví dụ xây dựng công cụ truy vấn dưới đây. Code phần này có tại: docs/tuto-
rial/4.Retriever.ipynb
Đầu tiên, bạn cần nhập các module cần thiết để thực hiện việc truy xuất, xử lý và tổng hợp câu trả
lời. Ở đây có một số công cụ mà chúng ta cần chú ý:
• VectorIndexRetriever, là thành phần chịu trách nhiệm tìm kiếm và truy xuất các tài liệu dựa trên
vector embedding đã tạo trước đó.
• SimilarityPostprocessor dùng để xử lý kết quả sau khi truy vấn, đảm bảo rằng các kết quả trả về
có độ tương đồng cao với truy vấn ban đầu.
• RetrieverQueryEngine là công cụ chính được sử dụng để thực hiện truy vấn, kết hợp các thành
phần trên để tìm kiếm, xử lý, và tổng hợp kết quả.
Tiếp theo, chúng ta sẽ tạo document và index. Ở đây các buớc đều giống ở phần hướng dẫn tạo Ingest
Pipeline, chỉ có khác là chúng ta sử dụng TitleExtractor để trích xuất tiêu đề từ tài liệu. Tiêu đề có
thể giúp cải thiện khả năng truy vấn và sắp xếp thông tin.
1 text = """ M è o r ấ t đ á ng y ê u .
2 Acid hydrochloric đ ậ m đ ặ c nh ấ t c ó n ồ ng đ ộ t ố i đ a l à 40%.
3 Ở d ạ ng đ ậ m đ ặc , acid n à y c ó th ể t ạ o th à nh c á c s ư ơ ng m ù acid ,
4 ch ú ng đ ề u c ó kh ả n ă ng ă n m ò n c á c m ô con ng ư ời , g â y t ổ n th ư ơ ng c ơ quan h ô h ấp ,
5 m ắt , da v à ru ộ t . """
6
7 doc = Document ( text = text )
38
AIO 2024 aivietnam.edu.vn
Trong hệ thống truy vấn, việc thiết lập các thành phần như bộ truy xuất (retriever), bộ tổng hợp phản
hồi (response synthesizer), và bộ xử lý hậu kỳ (postprocessor) là bước tiếp theo quan trọng. Mỗi thành
phần này đóng vai trò cụ thể trong quá trình tìm kiếm và trả về kết quả. Chúng ta sẽ tạo công cụ truy
xuất với VectorIndexRetriever, đây là bộ truy xuất sử dụng chỉ mục (index) đã tạo để tìm kiếm các
đoạn văn bản liên quan. Và chúng ta chỉ định rằng bộ truy xuất sẽ trả về 2 kết quả có độ tương đồng
cao nhất với truy vấn. Số lượng này có thể thay đổi tùy theo nhu cầu.
1 retriever = VectorIndexRetriever (
2 index = index ,
3 similarity_top_k =2 ,
4 )
Tiếp theo ta thiết lập Bộ Tổng hợp Phản hồi (Response Synthesizer). Ta sử dụng get_response_synthesizer
là hàm được sử dụng để tạo ra một bộ tổng hợp phản hồi, chịu trách nhiệm kết hợp các kết quả tìm
kiếm thành một phản hồi cuối cùng. Với tham số response_mode="tree_summarize", là tham số chỉ
định phản hồi sẽ được tổng hợp dưới dạng tóm tắt theo cấu trúc cây. Tham số verbose=True được sử
dụng để hiển thị thêm chi tiết quá trình thực hiện truy vấn của hệ thống, giúp theo dõi và hiểu rõ hơn
về cách phản hồi được tạo ra.
1 r e s p o n s e _ synthesizer = ge t _ re s p on s e _s y n th e s iz e r (
2 response_mode = " tree_summarize " ,
3 verbose = True
4 )
Cuối cùng chúng ta thiết lập bộ Xử lý hậu kỳ (Postprocessor). Chúng ta sử dungj SimilarityPostpro-
cessor, là bộ xử lý hậu kỳ giúp lọc và tinh chỉnh kết quả sau khi đã được tìm kiếm và tổng hợp. Với
tham số similarity_cutoff=0.5 là giá trị ngưỡng (cutoff) giúp xác định mức độ tương đồng tối thiểu mà
một kết quả phải đạt được. Nếu độ tương đồng của một kết quả thấp hơn 0.5, kết quả đó sẽ bị loại bỏ
khỏi danh sách phản hồi cuối cùng.
1 pp = S i m i l a ri ty Po s tp ro c es so r ( similarity_cutoff =0.5)
Kết hợp các thành phần đã tạo, chúng ta có được công cụ truy vấn query_engine.
1 query_engine = RetrieverQueryEngine (
2 retriever = retriever ,
3 r e s p o nse_synthesizer = response_synthesizer ,
4 n od e _ postprocessors =[ pp ]
5 )
Bây giờ, chúng ta có thể thực hiện truy vấn bằng công cụ vừa xây dựng và xem kết quả.
1 response = query_engine . query (
2 " M è o r ấ t đ á ng y ê u ph ả i kh ô ng ? "
3 )
4 print ( response )
39
AIO 2024 aivietnam.edu.vn
1 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2 1 text chunks after repacking
3 Đ ú ng v ậy , m è o r ấ t đ á ng y ê u .
4 ======================================================================================
Trên đây là hướng dẫn về cách xây dựng và tinh chỉnh một hệ thống truy vấn dữ liệu. Đối với các dự
án thông thường, chỉ cần query_engine mặc định là đủ, tuy nhiên khi cần tinh chỉnh, chúng ta cần bóc
tách nó ra như hướng dẫn trên để thử nghiệm các kỹ thuật khác.
40
AIO 2024 aivietnam.edu.vn
Cú pháp trên thực chất là để chuyển đổi chỉ mục (index) thành một công cụ trò chuyện.
Cụ thể, nó hoạt động như sau:
• Tạo công cụ truy vấn (query engine): Đầu tiên, chỉ mục (index) được sử dụng để tạo ra công cụ
truy vấn, cho phép tra cứu thông tin.
• Đóng gói: Sau đó, công cụ truy vấn này được bọc trong một công cụ trò chuyện, dựa trên chế độ
trò chuyện (chat_mode). Điều này cho phép ta tương tác với hệ thống như thể đang trò chuyện
với một người thật, trong đó các câu trả lời sẽ dựa trên thông tin từ chỉ mục.
– ChatMode.BEST (mặc định): Công cụ trò chuyện sử dụng tác nhân (react hoặc openai) với
công cụ công cụ truy vấn
– ChatMode.CONTEXT: Công cụ trò chuyện sử dụng ngữ cảnh từ index storage
– ChatMode.CONDENSE_QUESTION: Công cụ trò chuyện cô đọng các câu hỏi
41
AIO 2024 aivietnam.edu.vn
• chat(): Phương thức này dùng để bắt đâù một phiên trò chuyện và sẽ trả về kết quả ngay lập tức.
• achat(): Giống như chat() nhưng tích hợp truy vấn bất đồng bộ.
• stream_chat(): Tương tự chat() nhưng kết quả trả về sẽ hiển thị thông output trực tiếp từ API.
Tức là nó giống giao diện chatGPT, kết quả không hiển thị toàn bộ mà nó sẽ xuất hiện từ từ(các
chữ như đang chạy ra từ màn hình). Điều này có thể tăng trải nghiệm tương tác với người dùng.
• chat_repl(): Tạo một cuộc trò chuyện liên tục trả lời câu hỏi của người dùng cho đến khi người
dùng kết thúc cuộc trò chuyện.
Dưới đây là ví dụ về cách sử dụng chat(), astream_chat(), và chat_repl(). Đầu tiên chúng ta sẽ tạo
chat_engine từ index đơn giản sau:
1 from llama_index . core import Document , VectorStoreIndex
2
Tiếp theo, chúng ta sẽ sử dụng phương thức chat() để bắt đầu cuộc trò chuyện.
Như đã giới thiệu, chat_engine sử dụng dữ liệu truy vấn và cả lịch sử trò chuyện trong cuộc hội thoại.
Điều đó thể hiện rõ qua ví dụ sau:
Bạn có thể thấy rằng, tên mình không có trong dữ liệu ban đầu, nhưng khi trò chuyện mình cố tình
nói tên mình trong lần trò chuyện trước. Vậy nên khi tiếp tục trò chuyện, mình đã hỏi tên mình là gì,
và chat engine đã trả lời đúng, điều này là do chat engine đã sử dụng nội dung lịch sử trò chuyện trước
đó. Chúng ta có thể xem chi tiết lịch sử cuộc trò chuyện trên bằng cách sử dụng hàm chat_history như
sau:
1 chat_engine . chat_history
42
AIO 2024 aivietnam.edu.vn
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
[ ChatMessage ( role = < MessageRole . USER : ’ user ’ > , content = ’ T ô i l à Ti ềm , m è o Ú c ủ a t ô i đ ang
l à m g ì nh ỉ ? ’ , additional_kwargs ={}) ,
ChatMessage ( role = < MessageRole . ASSISTANT : ’ assistant ’ > , content = None ,
addi tional_kwargs ={ ’ tool_calls ’: [ C h a t C o m p l e t i o n M e s s a g e T o o l C a l l ( id = ’
call_SztjRMkbEtABahzyhVb5CSGU ’ , function = Function ( arguments = ’{" input ":" M è o Ú c ủ a
Ti ề m đ ang l à m g ì ?"} ’ , name = ’ query_engine_tool ’) , type = ’ function ’) ]}) ,
ChatMessage ( role = < MessageRole . TOOL : ’ tool ’ > , content = ’ M è o Ú đ ang ng ủ b ê n c ử a s ổ . ’ ,
addi tional_kwargs ={ ’ name ’: ’ query_engine_tool ’ , ’ tool_call_id ’: ’
call_SztjRMkbEtABahzyhVb5CSGU ’}) ,
ChatMessage ( role = < MessageRole . ASSISTANT : ’ assistant ’ > , content = ’ M è o Ú c ủ a b ạ n
đ ang ng ủ b ê n c ử a s ổ . ’ , additional_kwargs ={}) ,
ChatMessage ( role = < MessageRole . USER : ’ user ’ > , content = ’ T ô i t ê n l à g ì ? ’ ,
addi tional_kwargs ={}) ,
ChatMessage ( role = < MessageRole . ASSISTANT : ’ assistant ’ > , content = ’ B ạ n t ê n l à Ti ề m . ’ ,
addi tional_kwargs ={}) ]
======================================================================================
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
C ó v ẻ nh ư b ạ n đ ang mu ố n bi ế t v ề t ì nh c ả m c ủ a m ì nh đ ố i v ớ i M è o Ú . D ư ớ i đ â y l à m ộ t b à i
th ơ th ể hi ệ n t ì nh y ê u d à nh cho M è o Ú :
M è o Ú nh ỏ b é , đ á ng y ê u v ô c ù ng ,
Trong l ò ng t ôi , c ậ u l à á nh tr ă ng r ằ m .
M ỗ i bu ổ i s á ng , c ậ u nh ả y nh ó t vui ,
Mang đ ế n n ụ c ư ời , xua tan u á m .
L ô ng m ề m m ại , nh ư m â y tr ờ i bay ,
Á nh m ắ t trong veo , nh ư n ư ớ c h ồ đ ầ y .
C ậ u l à b ạ n th ân , l à ni ề m an ủi ,
M ỗ i ph ú t gi â y b ê n nhau , th ậ t tuy ệ t v ờ i .
Khi c ậ u ng ủ say , t ô i ng ắ m nh ìn ,
T ì nh y ê u d à nh cho c ậu , m ã i kh ô ng phai .
M è o Ú ơi , c ậ u l à m ó n qu à ,
Trong tr á i tim t ôi , c ậ u lu ô n l à nh ấ t .
======================================================================================
Cuối cùng, hãy đến ví dụ về cách sử dụng chat_repl(), ngoài việc khác biệt so với chat() hay stream_chat()
bởi giao diện hoạt động như một vòng lặp, nó còn khác biệt bởi cơ chế lưu trữ lịch sử chat, nó khởi tạo
mới lịch sử chat khi bắt đầu một phiên chat mới. Chính vì vậy phiên chat mới không sử dụng lại lịch
sử của phiên chat cũ.
1 response = chat_engine . chat_repl ()
43
AIO 2024 aivietnam.edu.vn
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
===== Entering Chat REPL =====
Type " exit " to exit .
User : Ch à o b ạ n
Assistant : Ch à o b ạ n ! B ạ n c ầ n gi ú p g ì h ô m nay ?
User : B ạ n c ó bi ế t m è o ú kh ô ng ?
Assistant : M è o ú th ư ờ ng đ ư ợ c bi ế t đ ế n v ớ i v ẻ ngo à i d ễ th ư ơ ng v à t í nh c á ch th â n thi ệ n .
B ạ n c ó k ỷ ni ệ m n à o đ ặ c bi ệ t v ớ i m è o ú kh ô ng ? Ho ặ c b ạ n mu ố n bi ế t th ê m th ô ng tin g ì
v ề ch ú ng ?
User : C ậ u ấ y đ ang l à m g ì ?
Assistant : M è o ú đ ang ng ủ b ê n c ử a s ổ . C ó v ẻ nh ư c ậ u ấ y đ ang t ậ n h ư ở ng m ộ t gi ấ c ng ủ
ngon ! B ạ n c ó mu ố n bi ế t th ê m đ i ề u g ì v ề m è o ú kh ô ng ?
User : exit
======================================================================================
Để xóa lịch sử trò chuyện hoặc làm mới lại chat_engine, chúng ta sử dụng chat_engine.reset().
Tiếp theo, chúng ta tạo chat_store bằng cách tải lại file chứa lịch sử trò chuyện trước đó. Nếu đây là
lần đầu tiên trò chuyện, chat_store sẽ được khởi tạo mới bởi SipleChatStore, và chat_store lúc này sẽ
trống rỗng.
44
AIO 2024 aivietnam.edu.vn
1 try :
2 chat_store = SimpleChatStore . from_persist_path (
3 persist_path = " chat_memory . json "
4 )
5 except FileNotFoundError :
6 chat_store = SimpleChatStore ()
Tiếp theo, chúng ta sẽ thiết lập bộ nhớ trong cuộc trò chuyện, theo mặc định thì nó đã được thiết lập
sẵn trong chế độ chat. Nhưng ở đây, chúng ta sẽ thử tinh chỉnh bằng cách thiết lập một số tham số
theo ý của mình. Khi tinh chỉnh ChatMemoryBuffer, có 3 tham số mà chúng ta cần chú ý:
• token_limit: Số lượng token tối đa cho phép trong lịch sử trò chuyện, khi vượt quá số lượng, cơ
chế trượt window sẽ cắt bỏ những phần lịch sử cũ hơn.
• chat_store_key: Khóa định danh xác định cuộc trò chuyện này với cuộc trò chuyện khác. Nó hữu
ích trong trường hợp ứng dụng cung cấp cho nhiều người dùng, cuộc trò chuyện của mỗi người sẽ
được gán một khóa riêng để phân biệt với nhau.
Cuối cùng, chúng ta tạo chat engine với tham số memory và bắt đầu cuộc trò chuyện.
1 chat_engine = index . as_chat_engine ( memory = memory )
2 while True :
3 user_input = input ( " User : " )
4 if user_input == " exit " :
5 break
6 response = chat_engine . chat ( user_input )
7 print ( " Bot : " , response )
Chúng ta sử dụng chat_history để kiểm tra thông tin lịch sử cuả cuộc trò chuyện.
1 chat_engine . chat_history
Cuối cùng, để lưu lại lịch sử chat, chúng ta sử dụng persist để lưu thông tin đến file "chat_memory.json"
1 chat_store . persist ( persist_path = " chat_memory . json " )
45
AIO 2024 aivietnam.edu.vn
Chế độ trò chuyện này có ưu điểm là mọi câu trả lời đều được tạo từ thông tin trong kho dữ liệu của
chúng ta. Tuy nhiên, đây cũng chính là nhược điểm, ví dụ khi chúng ta chỉ nói "xin chào", nó cũng
thực hiện các bước phức tạp nên có thể gây lãng phí tài nguyên, và cơ chế tạo lại câu hỏi có thể tạo ra
câu hỏi chung chung thì cũng khó mà tìm được câu trả lời.
46
AIO 2024 aivietnam.edu.vn
7.4 Agent
Nếu chúng ta hỏi chatbot "Mấy giờ rồi nhỉ?", hay ta muốn chatbot viết một bức thư rồi gửi nó qua địa
chỉ email của khách hàng, hoặc ta có một cơ sở dữ liệu muốn chatbot có thể kết nối và truy xuất thông
tin dựa trên yêu cầu, sau đó viết thành file báo cáo rồi gửi cho ta, giống như một nhân viên thật sự?...
nói chung là chúng ta muốn chatbot có thể thực hiện nhiều công việc hơn thì làm như thế nào?
Các phương pháp trước đây, khi kết hợp LLM với dữ liệu(RAG cơ bản), chỉ cho phép chatbot thực hiện
chức năng trò chuyện. Điều này hạn chế khả năng của chatbot, khiến nó chỉ có thể trả lời câu hỏi hoặc
cung cấp thông tin dựa trên dữ liệu tĩnh mà nó được cung cấp. Chúng ta không thể yêu cầu chatbot
tìm kiếm thông tin mới, thay đổi dữ liệu hiện có, hoặc thực hiện các tác vụ phức tạp khác.
Đây là lúc mà Agent xuất hiện—một thành phần có thể thay đổi cách chúng ta nghĩ về khả năng của
chatbot. Với Agent trong LlamaIndex, chatbot không chỉ có khả năng trò chuyện mà còn có thể tự động
tìm kiếm thông tin, kết nối với các dịch vụ bên ngoài, và thậm chí cập nhật hoặc thay đổi dữ liệu một
cách thông minh.
Agents như những người trợ lý ảo có thể thực hiện cả chức năng "đọc"và "ghi,"nghĩa là chúng có thể
tìm kiếm và truy xuất thông tin từ nhiều loại dữ liệu khác nhau, cũng như thay đổi hoặc cập nhật dữ
liệu khi cần thiết. Điều này giúp mở rộng đáng kể những gì mà chatbot có thể làm, từ việc chỉ trả lời
câu hỏi trở thành một công cụ thông minh có khả năng thực hiện nhiều tác vụ khác nhau.
Để tạo một Agent dữ liệu, ta cần các thành phần chính sau:
• Vòng lặp suy luận: Đây là cách mà agent suy nghĩ và quyết định bước tiếp theo sẽ làm gì.
• Các công cụ (Tool Abstractions): Đây là các công cụ hoặc API mà agent có thể sử dụng để lấy
hoặc thay đổi thông tin. Khi ta giao một nhiệm vụ cho agent, nó sẽ sử dụng vòng lặp suy luận để
quyết định sử dụng công cụ nào, theo thứ tự nào, và cách sử dụng từng công cụ.
a) Tools
Công cụ (Tools) là phần quan trọng trong việc xây dựng agent. Chúng giống như các API nhưng dành
cho agent sử dụng thay vì con người. Ví dụ một tool có thể chứa query engine để truy xuất dữ liệu,
hoặc chứa các hàm python để thực hiện các chức năng cụ thể, ví dụ như một hàm đọc, ghi file.
Việc chọn đúng công cụ và cách sử dụng chúng rất quan trọng vì ảnh hưởng đến cách LLM tương tác
với công cụ đó. Chúng ta cần định nghĩa tên và phần mô tả khi tạo công cụ vì sẽ ảnh hưởng lớn đến
việc LLM gọi và sử dụng các công cụ đó. Điều này cũng dễ hiểu vì Agent cũng giống như con người,
khi đưa cho nó công cụ thì phải kèm hướng dẫn sử dụng như mô tả công cụ này là gì, chức năng như
thế nào. Đối với dân lập trình Python như chúng ta không còn xa lạ gì khi mà chúng ta có những quy
định đặt tên biến, hàm, viết doc-mô tả cho hàm để đọc code dễ hiểu hơn. Thông thường khi lập trình
chúng ta có thể không cần viết mô tả, nhưng các hàm cho agent sử dụng bắt buộc phải viết để Agent
có thể hiểu và sử dụng công cụ chính xác.
Có một số loại tool sau:
• FunctionTool: Chuyển đổi bất kỳ hàm nào của người dùng thành một Tool.
• QueryEngineTool: Gói gọn một công cụ truy vấn hiện có thành Tool.
• ToolSpecs từ cộng đồng: Công cụ do cộng đồng đóng góp, bao gồm nhiều dịch vụ như Gmail.
• Utility Tools: Công cụ tiện ích, hỗ trợ quản lý và xử lý lượng dữ liệu lớn từ các công cụ khác.
47
AIO 2024 aivietnam.edu.vn
Đầu tiên, chúng ta hãy cùng xem một ví dụ về công cụ trò chuyện cơ bản, chỉ sử dụng LLM.
Mô hình trò chuyện trên không thể trả lời về thời gian hiện tại, vì nó không được học và cũng không
biết bây giờ là mấy giờ để trả lời. Vậy thì, hãy xây dựng một công cụ để Agent có thể xem giờ.
1 import datetime
2
3 def get_date_time () -> str :
4 """ Get current date and time
5
6 Returns :
7 str : Current date and time
8 """
9 now = datetime . datetime . now ()
10 return now . strftime ( " %Y -% m -% d % H :% M :% S " )
Chúng ta sử dụng thư viện datetime để lấy thời gian hiện tại. Bằng việc tạo một hàm get_date_time()
hàm này không có tham số và được gợi ý là sẽ trả về str. Bên trong hàm chúng ta cần viết doc cho nó,
mô tả chức năng của hàm, các tham số nếu có và giá trị trả về. Như đã đề cập, việc ghi chú thích như
vậy rất quan trọng, để Agent có thể lựa chọn chính xác công cụ.
Tiếp theo chúng ta biến hàm này thành tool, bằng cách sử dụng FunctionTool.
1 from llama_index . core . tools import FunctionTool
2
3 ge t_ da te_ time_tool = FunctionTool . from_defaults ( fn = get_date_time )
Cuối cùng chúng ta tạo agent và thử hỏi giờ xem agent đã trả lời thời gian được chưa.
Vậy là bây giờ Agent đã có trả lời thời gian chính xác, điều đơn giản mà LLM không làm được.
48
AIO 2024 aivietnam.edu.vn
Tiếp theo, chúng ta sẽ tìm hiểu query engine tool. Trong Agent chúng ta có thể sử dụng nhiều công cụ
truy xuất dữ liệu khác nhau. Chính vì vậy mà chúng ta sẽ goí các công cụ truy xuất này lại thành công
cụ cho Agent sử dụng.
1 from llama_index . core . tools import QueryEngineTool
2 from llama_index . core import Document , VectorStoreIndex
3 from llama_index . agent . openai import OpenAIAgent
4
5
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
B â y gi ờ l à 23 gi ờ 56 ph ú t .
M è o Ú c ủ a anh đ ã b ị s ậ p b ẫ y khi đ i ch ơ i v ề mu ộ n .
======================================================================================
Trong chương trình trên, chúng ta bắt đầu bằng việc khai báo các gói cần thiết, ở đây chúng ta sẽ sử
dụng QueryEngineTool để gói lại các công cụ loại query engine. Tiếp theo chúng ta tạo query engine,
sau đó tạo query engine tool. Cuối cùng chúng ta sẽ tạo agent từ các công cụ. Đầu ra cho thấy agent
đã sử dụng công cụ khác nhau để trả lời cho mỗi câu hỏi.
b) Resoning Loop
Vòng lặp lí luận(Resoning Loop) là một thành phần quan trọng của Agent, nó là khả năng tư duy của
Agent trong việc sử dụng các công cụ để thực hiện công việc được giao. Trong phần trước, chúng ta đã
tạo tool và agent, khi chúng ta trò chuyện, agent có thể chọn công cụ để trả lời câu hỏi của chúng ta
một cách tự động.
Vòng lặp lý luận khi nhận được câu hoỉ sẽ thực hiện đánh giá bối cảnh, lựa chọn công cụ phù hợp để
sử dụng. Đối với trường hợp công việc cần phải sử dụng nhiều công cụ, Agent cũng xác định trình tự
sử dụng các công cụ một cách hợp lý.
c) OpenAI Agent
OpenAI Agent được thiết kế để tương tác và thực hiện các tác vụ cụ thể thông qua giao tiếp. Nó được
xây dựng dựa trên API của OpenAI và có khả năng sử dụng các công cụ (tools) để thực hiện các phép
tính hoặc xử lý dữ liệu theo yêu cầu. Khả năng này đã được tích hợp sẵn trong API nên sử dụng khá
đơn giản.
Việc xử lí logic để chọn và sử dụng công cụ phù hợp nằm ở phía API, khi người dùng yêu cầu thực hiện
nhiệm vụ nào đó, API sẽ thực hiện phân tích ngữ cảnh yêu cầu, cùng với lịch sử trò chuyện để quyết
định nên sử dụng công cụ hay đưa ra câu trả lời cuối cùng.
49
AIO 2024 aivietnam.edu.vn
Trong ví dụ dưới đây, chúng ta tạo Agent với thiết lập là một người bạn thân của mình. Cậu ấy có thể
thực hiện phép nhân hai số rất chính xác và có thể trả lời thông tin về mèo Ú.
1 from typing import Optional
2 from llama_index . agent . openai import OpenAIAgent
3 from llama_index . core . tools import FunctionTool
4 from llama_index . core import Document , VectorStoreIndex
5 from llama_index . core . tools import QueryEngineTool
6
7 def multiply ( a : int , b : int ) -> int :
8 """ Tr ả v ề k ế t qu ả ph é p nh â n a v ớ i b """
9 return a * b
10
11 def save_result ( result : int ) -> str :
12 """ L ư u k ế t qu ả v à o file result . txt """
13 with open ( " result . txt " , " w " ) as f :
14 f . write ( str ( result ) )
15 return " Result saved ! "
16
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Added user message to memory : Thu ơi , h ã y nh â n 5 v ớ i 3 gi ú p t ớ nh é . M à c ậ u bi ế t m è o Ú
c ủ a t ớ đ ã th ế n à o kh ô ng ?
=== Calling Function ===
Calling function : multiply with args : {" a ": 5 , " b ": 3}
Got output : 15
= = = = = = = = = == = = == = = == = = = ==
K ế t qu ả c ủ a ph é p nh â n 5 v ớ i 3 l à 15.
C ò n v ề m è o Ú c ủ a c ậu , n ó đ ã tr ở v ề v à o s á ng ng à y 4 th á ng 9 n ă m 2024 , d ư ớ i c ơ n m ư a t ầ m
t ã . Hy v ọ ng m è o Ú c ủ a c ậ u đ ã an to à n v à kh ỏ e m ạ nh !
======================================================================================
50
AIO 2024 aivietnam.edu.vn
• Đầu tiên chúng ta import các thư viện cần thiết: Đoạn code sử dụng một số thư viện từ LlamaIndex
để tạo ra một agent có thể thực hiện các tác vụ cụ thể.
• Định nghĩa hàm multiply: Hàm này nhận hai số nguyên và trả về kết quả phép nhân của chúng.
• Định nghĩa hàm save_result: Hàm này nhận một số nguyên, lưu kết quả vào tệp result.txt, và trả
về thông báo xác nhận đã lưu kết quả.
• Tạo tài liệu và index: Đoạn văn bản về "mèo Ú"được chuyển thành một đối tượng Document, sau
đó được đưa vào VectorStoreIndex để có thể thực hiện tìm kiếm thông tin.
• Tạo công cụ QueryEngineTool: Công cụ này cho phép agent tìm kiếm thông tin liên quan đến
"mèo Ú"trong documents đã được index.
• Tạo công cụ FunctionTool: Hai công cụ khác là multiply_tool để thực hiện phép nhân và save_result_tool
để lưu kết quả vào tệp.
• Tạo OpenAIAgent: Agent này được cấu hình với các công cụ đã tạo, cùng với lời nhắc hệ thống
rằng nó là "Thu, người bạn tri kỷ". Agent có khả năng thực hiện các tác vụ như nhân số, lưu kết
quả và tìm kiếm thông tin khi được yêu cầu.
• Cuối cùng thực hiện trò chuyện với agent: Agent nhận yêu cầu từ người dùng (nhân 5 với 3 và
tìm thông tin về mèo Ú) và trả về kết quả tương ứng.
d) React Agent
Khác với OpenAI Agent được tích hợp vòng lặp lý luận vào model, React Agent hoạt động dựa trên
prompt, việc chọn lựa và tạo quy trình làm việc dựa trên hướng dẫn của prompt, chính vì vậy có thể
áp dụng phương pháp này cho các loại llms khác nhau. Sự phức tạp đã có người khác giải quyết, chúng
ta hãy xem cách sử dụng React Agent như thế nào qua ví dụ sau:
1 from llama_index . core . agent . react import ReActAgent
2
3 react_agent = ReActAgent . from_tools (
4 tools =[ query_tool , multiply_tool , save_result_tool ] ,
5 system_prompt = " B ạ n l à Thu , ng ư ờ i b ạ n tri k ỷ , hi ể u chuy ệ n v à th à nh th ậ t c ủ a t ô i . " ,
6 verbose = True
7 )
8 response = react_agent . chat ( " Thu ơi , h ã y nh â n 5 v ớ i 3 gi ú p t ớ nh é . M à c ậ u bi ế t m è o Ú
9 c ủ a t ớ đ ã th ế n à o kh ô ng ? " )
10 print ( response )
51
AIO 2024 aivietnam.edu.vn
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
> Running step d2be793c -827 f -45 c3 -8 bd8 -4799 bbd24722 . Step input : Thu ơi , h ã y nh â n 5
v ớ i 3 gi ú p t ớ nh é . M à c ậ u bi ế t m è o Ú c ủ a t ớ đ ã th ế n à o kh ô ng ?
Thought : Ng ư ờ i d ù ng đ ang y ê u c ầ u t ô i th ự c hi ệ n ph é p nh â n 5 v ớ i 3 v à c ũ ng h ỏ i v ề m è o Ú
c ủ a h ọ . T ô i s ẽ b ắ t đ ầ u b ằ ng c á ch th ự c hi ệ n ph é p nh â n tr ư ớ c .
Action : multiply
Action Input : { ’a ’: 5 , ’b ’: 3}
Observation : 15
> Running step 90 dd37e4 -1 fc6 -47 d1 - b8ca -9 e7fd8f685e5 . Step input : None
Thought : T ô i đ ã th ự c hi ệ n ph é p nh â n v à k ế t qu ả l à 15. B â y gi ờ t ô i s ẽ t ì m ki ế m th ô ng
tin v ề m è o Ú c ủ a ng ư ờ i d ù ng .
Action : query_engine_tool
Action Input : { ’ input ’: ’m è o Ú ’}
Observation : M è o Ú l à m ộ t con m è o đ ã tr ở v ề v à o s á ng ng à y 4 th á ng 9 n ă m 2024 ,
dưới cơn mưa tầm tã.
> Running step 113586 e5 - c588 -4533 - b7e8 - aee2323ee90a . Step input : None
Thought : T ô i đ ã t ì m th ấ y th ô ng tin v ề m è o Ú c ủ a ng ư ờ i d ù ng . B â y gi ờ t ô i c ó th ể tr ả l ờ i
câu hỏi của họ.
Answer : K ế t qu ả ph é p nh â n 5 v ớ i 3 l à 15. M è o Ú c ủ a b ạ n đ ã tr ở v ề v à o s á ng ng à y 4 th á ng
9 n ă m 2024 , d ư ớ i c ơ n m ư a t ầ m t ã .
K ế t qu ả ph é p nh â n 5 v ớ i 3 l à 15. M è o Ú c ủ a b ạ n đ ã tr ở v ề v à o s á ng ng à y 4 th á ng 9 n ă m
2024 , d ư ớ i c ơ n m ư a t ầ m t ã .
======================================================================================
Đầu tiên chúng ta import các thư viện cần thiết, ở đây hầu hết là các thư viện, gói mà chúng ta đã
được giới thiệu trong phần ví dụ về xây dựng Agent.
Chúng ta xây dựng hàm load_chat_store() để khởi tạo hoặc load lại lịch sử trò chuyện.
1 def load_chat_store () :
2 if os . path . exists ( CONVERSATION_FILE ) and os . path . getsize ( CONVERSATION_FILE ) > 0:
3 try :
4 chat_store = SimpleChatStore . from_persist_path ( CONVERSATION_FILE )
5 except json . JSONDecodeError :
6 chat_store = SimpleChatStore ()
7 else :
8 chat_store = SimpleChatStore ()
9 return chat_store
52
AIO 2024 aivietnam.edu.vn
Hàm load_chat_store() có chức năng tải lịch sử hội thoại từ file CONVERSATION_FILE. Cụ thể:
• Nếu file tồn tại và không rỗng, hàm sẽ cố gắng tải dữ liệu hội thoại từ file này bằng cách sử dụng
SimpleChatStore.from_persist_path(CONVERSATION_FILE).
• Nếu có lỗi trong quá trình đọc file (ví dụ như lỗi định dạng JSON), hoặc nếu file không tồn tại
hoặc rỗng, hàm sẽ tạo một SimpleChatStore mới.
• Cuối cùng, hàm trả về đối tượng chat_store, chứa lịch sử hội thoại đã tải hoặc mới được tạo.
Tiếp theo chúng ta tạo hàm save_score để lưu lại kết quả chẩn đoán.
1
2 def save_score ( score , content , total_guess , username ) :
3 """ Write score and content to a file .
4
5 Args :
6 score ( string ) : Score of the user ’s mental health .
7 content ( string ) : Content of the user ’s mental health .
8 total_guess ( string ) : Total guess of the user ’s mental health .
9 """
10 current_time = datetime . now () . strftime ( " %Y -% m -% d % H :% M :% S " )
11 new_entry = {
12 " username " : username ,
13 " Time " : current_time ,
14 " Score " : score ,
15 " Content " : content ,
16 " Total guess " : total_guess
17 }
18
19 # Đ ọ c d ữ li ệ u t ừ file n ế u t ồ n t ạ i
20 try :
21 with open ( SCORES_FILE , " r " ) as f :
22 data = json . load ( f )
23 except FileNotFoundError :
24 data = []
25
26 # Th ê m d ữ li ệ u m ớ i v à o danh s á ch
27 data . append ( new_entry )
28
29 # Ghi d ữ li ệ u tr ở l ạ i file
30 with open ( SCORES_FILE , " w " ) as f :
31 json . dump ( data , f , indent =4)
53
AIO 2024 aivietnam.edu.vn
54
AIO 2024 aivietnam.edu.vn
8 Kỹ thuật prompt
Prompt là các câu lệnh hoặc chỉ dẫn để các mô hình ngôn ngữ lớn hiểu và tạo ra câu trả lời. Prompt
đóng vai trò quan trọng trong việc khai thác sức mạnh của LLMs, giúp chúng có thể tạo ra những phản
hồi chất lượng.
Trong LlamaIndex, prompt được sử dụng trong nhiều bước quan trọng như xây dựng index, truy vấn
và tổng hợp câu trả lời cuối cùng. Trong hướng dẫn ở các phần trước, việc tạo nodes, query engine...
chủ yếu dùng các prompts mặc định trong thư viện llamaindex. Chính vì vậy, trong phần này, chúng ta
sẽ tìm hiểu một vài prompts được thiết kế sẵn, và dựa vào các mẫu prompts này, chúng ta hoàn toàn
có thể biến tấu chúng thành prompts của riêng chúng ta.
Prompt này được sử dụng để tạo ra một mẫu tóm tắt nội dung cho một đoạn văn bản cụ thể. Trong
đó:
• context_str: Đây là nơi mà nội dung của đoạn văn bản sẽ được chèn vào. Nội dung này có thể là
một đoạn văn bản dài hoặc một phần của một tài liệu.
• Yêu cầu tóm tắt: Sau khi cung cấp nội dung, prompt yêu cầu hệ thống tóm tắt lại các chủ đề
chính và các thực thể (entities) quan trọng được đề cập trong đoạn văn bản đó.
• Summary: Đây là nơi mà tóm tắt cuối cùng sẽ được tạo ra, dựa trên nội dung và các chủ đề, thực
thể quan trọng đã được hệ thống xác định.
Prompt này giúp tự động hóa quá trình tóm tắt thông tin, đặc biệt hữu ích trong việc xử lý văn bản
dài hoặc tài liệu lớn.
Ví dụ prompt cho Keyword Extractor:
1 D E F A U L T _ K E Y W O R D _ E X T R A C T _ T E M P L A T E = """ \
2 { context_str }. Give { keywords } unique keywords for this \
3 document . Format as comma separated . Keywords : """
55
AIO 2024 aivietnam.edu.vn
Đó là mẫu prompt cho câu hỏi đầu vào của người dùng, ví dụ chúng ta có đầu vào là :"Mèo Ú của Tiềm
đang làm gì". Thì prompt sẽ thành như sau:
1 Context information is below .
2 ---------------------
3 T ô i c ó m ộ t b é m èo , c ậ u ấ y t ê n l à M è o Ú . C ậ u ấ y đ ang ng ủ b ê n c ử a s ổ . T ô i r ấ t qu ý c ậ u ấ y
.
4 ---------------------
5 Given the context information and not prior knowledge , answer the query .
6 Query : M è o Ú c ủ a Ti ề m đ ang l à m g ì ?
7 Answer :
Ngoài prompt để thiết lập đầu vào của người dùng, chúng ta còn có prompt cho hệ thống LLM. Theo
mặc định, với công cụ chat thông thường, prompt thiết lập cho hệ thống sẽ là:
1 You are an expert Q & A system that is trusted around the world .
2 Always answer the query using the provided context information , and not prior
knowledge .
3 Some rules to follow :
4 1. Never directly reference the given context in your answer .
5 2. Avoid statements like ’ Based on the context , ... ’ or ’ The context information ... ’
or anything along those lines .
Ngoài ra các công cụ như React Agent cũng được thiết kế bởi các prompt phức tạp, tuy nhiên chúng
ta sẽ dừng lại ở đây và chấp nhận sử dụng những prompt mặc định này. Dù sao chúng đã được công
bố trong các nghiên cứu rồi, chỉ khi nào cần đào sâu hơn nữa thì có thể chúng ta sẽ tinh chỉnh nó. Bạn
có thể đọc thêm bài viết về các kỹ thuật prompt rất chi tiết của nhóm bạn Hùng An và Bách Ngô ở
đây:Foundation Of Prompt Engineering
56
AIO 2024 aivietnam.edu.vn
9 Evaluation
Một trong những thách thức lớn nhất khi đánh giá hệ thống Retrieval-Augmented Generation (RAG)
là việc thiếu nhãn (labels) chuẩn để so sánh. Không giống như các mô hình học máy truyền thống,
trong đó dữ liệu được gắn nhãn đầy đủ để làm thước đo cho kết quả đầu ra, hệ thống RAG kết hợp
dữ liệu truy xuất và mô hình ngôn ngữ, làm cho việc tạo ra một tiêu chuẩn đánh giá chính xác trở nên
khó khăn.
Không có nhãn cụ thể để xác định đúng hay sai, chúng ta không thể dễ dàng xác định chất lượng của
các câu trả lời mà hệ thống RAG đưa ra. Điều này đòi hỏi một phương pháp đánh giá khác biệt và linh
hoạt hơn.
Để giải quyết vấn đề thiếu nhãn này, chúng ta có thể sử dụng các mô hình LLM của APENAI để tự
động đánh giá các đầu ra của hệ thống RAG. OpenAI API cung cấp khả năng đánh giá các phản hồi
dựa trên một số tiêu chí nhất định, giúp chúng ta có thể đánh giá một cách khách quan và hệ thống
hơn.
Cụ thể, chúng ta sẽ đánh giá các phản hồi của hệ thống RAG theo ba tiêu chí chính: Độ chính xác
(Correctness), Tính trung thực (Faithfulness), và Tính liên quan (Relevancy). Mỗi tiêu chí sẽ đo lường
một khía cạnh cụ thể của chất lượng phản hồi, từ đó giúp ta hiểu rõ hơn về hiệu suất của hệ thống.
Trong phần này, chúng ta sẽ làm quen với 3 độ đo đánh giá:
• Độ chính xác (Correctness): Đo lường xem câu trả lời có đúng và khớp với câu trả lời tham chiếu
không. Đây là tiêu chí quan trọng để đảm bảo rằng hệ thống RAG đang cung cấp thông tin đúng
cho người dùng. Giá trị của độ đo này nằm trong khoảng từ 1 đến 5.
• Tính trung thực (Faithfulness): Đánh giá mức độ mà câu trả lời phản ánh chính xác nội dung
trong nguồn dữ liệu truy xuất mà không có những thông tin sai lệch hay được bịa ra. Giá trị từ
0 dến 1.
• Tính liên quan (Relevancy): Đánh giá xem câu trả lời có thực sự trả lời câu hỏi đã được đưa ra
hay không. Một câu trả lời có thể đúng nhưng không liên quan thì vẫn bị coi là không đạt yêu
cầu. Giá trị từ 0 đến 1.
Bây giờ, chúng ta sẽ đi vào từng bước cụ thể để đánh giá hệ thống RAG qua ví dụ dưới đây. Code phần
này có tại: docs/tutorial/6.Evaluate.ipynb
Đầu tiên, chúng ta sẽ nhập các gói cần thiết cho chương trình, ở đây có hai gói mới như evaluate cung
cấp các công cụ để đánh giá mô hình, và gói llama_dataset giúp tạo bộ dữ liệu phục vụ cho việc đánh
giá.
1 import openai
2 from llama_index . core import Settings , Document , VectorStoreIndex
3 from llama_index . llms . openai import OpenAI
4 from llama_index . core . node_parser import TokenTextSplitter
5 from llama_index . core . evaluation import (
6 BatchEvalRunner ,
7 CorrectnessEvaluator ,
8 FaithfulnessEvaluator ,
9 Re le vancyEvaluator
10 )
11 from llama_index . core . llama_dataset . generator import RagDatasetGenerator
12 import asyncio
13 import pandas as pd
14 import nest_asyncio
15 from tqdm . asyncio import tqdm_asyncio
57
AIO 2024 aivietnam.edu.vn
Chúng ta cần thiết lập API key và cấu hình mô hình OpenAI.
1 def setup_openai ( api_key : str , model : str = " gpt -4 o - mini " , temperature : float = 0.2) :
2 openai . api_key = api_key
3 Settings . llm = OpenAI ( model = model , temperature = temperature )
Chúng ta sẽ tạo nodes từ dữ liệu test là một đoạn văn bản đơn giản. Sau đó tiến hành tạo query engine.
1 # Split text into smaller chunks for processing
2 def c r e a t e _ d o c u m e n t _ a n d _ s p l i t t e r ( text : str , chunk_size : int = 20 ,
3 chunk_overlap : int = 5 , separator : str = " " ) :
4 doc = Document ( text = text )
5 splitter = TokenTextSplitter (
6 chunk_size = chunk_size ,
7 chunk_overlap = chunk_overlap ,
8 separator = separator
9 )
10 nodes = splitter . g e t _n o d es _ f ro m _ do c u me n t s ([ doc ])
11 return nodes
12
13 # Create a vector store index and a query engine
14 def c r e a t e _ v e c t o r _ s t or e _ i n d e x ( nodes ) :
15 vector_index = VectorStoreIndex ( nodes )
16 query_engine = vector_index . as_query_engine ()
17 return query_engine
Tiếp theo, chúng ta sử dụng các nodes đã tạo để sinh ra các câu hỏi mà ta sẽ dùng để đánh giá hệ
thống. Các câu hỏi này được tạo tự động và sẽ đóng vai trò như là bộ dữ liệu đánh giá hệ thống RAG.
1 def g en er ate_questions ( nodes , n um _ qu es ti o ns _p er _ ch un k : int = 1) :
2 datas et_generator = RagDatasetGenerator ( nodes , nu m _q ue s ti on s_ p er _c hu n k =
n u m _ q u es ti on s _p er _c h un k )
3 eval_questions = dataset_generator . g e n e r a t e _ q u e s t i o n s _ f r o m _ n o d e s ()
4 return eval_questions . to_pandas ()
Chúng ta sẽ chạy quá trình đánh giá hệ thống RAG bằng cách sử dụng các độ đo về độ Correctness,
Faithfulness, Relevancy. Đây là bước quan trọng để thu thập dữ liệu về hiệu suất của hệ thống.
1 async def evaluate_async ( query_engine , df ) :
2 c o r r e ctne ss_ev aluato r = CorrectnessEvaluator () # Evaluate correctness against a
reference answer
3 f a i t h fu ln ess _ev al uat or = F aithfu lness Evalua tor () # Evaluate hallucination of the
response
4 r el e v ancy_evaluator = RelevancyEvaluator () # Evaluate if the response actually
answers the query
5
6 # Initialize the BatchEvalRunner
7 runner = BatchEvalRunner (
8 {
9 " correctness " : correctness_evaluator ,
10 " faithfulness " : faithfulness_evaluator ,
11 " relevancy " : relevancy_evaluator
12 },
13 show_progress = True
14 )
15
16 # Run the asynchronous evaluation
17 eval_result = await runner . aevaluate_queries (
18 query_engine = query_engine ,
19 queries =[ question for question in df [ ’ query ’]] ,
20 )
21
22 return eval_result
58
AIO 2024 aivietnam.edu.vn
Sau đó ta sẽ tập hợp kết quả đánh giá vào một bảng dữ liệu và tính toán các điểm số trung bình cho
từng tiêu chí. Điều này giúp chúng ta có cái nhìn tổng quan về chất lượng của hệ thống và biết được
điểm mạnh, điểm yếu cụ thể ở đâu.
1 def aggr egate_results ( df , eval_result ) :
2 data = []
3 for i , question in enumerate ( df [ ’ query ’ ]) :
4 correctness_result = eval_result [ ’ correctness ’ ][ i ]
5 f aithfulness_result = eval_result [ ’ faithfulness ’ ][ i ]
6 relevancy_result = eval_result [ ’ relevancy ’ ][ i ]
7 data . append ({
8 ’ Query ’: question ,
9 ’ Correctness response ’: correctness_result . response ,
10 ’ Correctness passing ’: correctness_result . passing ,
11 ’ Correctness feedback ’: correctness_result . feedback ,
12 ’ Correctness score ’: correctness_result . score ,
13 ’ Faithfulness response ’: faithfulness_result . response ,
14 ’ Faithfulness passing ’: faithfulness_result . passing ,
15 ’ Faithfulness feedback ’: faithfulness_result . feedback ,
16 ’ Faithfulness score ’: faithfulness_result . score ,
17 ’ Relevancy response ’: relevancy_result . response ,
18 ’ Relevancy passing ’: relevancy_result . passing ,
19 ’ Relevancy feedback ’: relevancy_result . feedback ,
20 ’ Relevancy score ’: relevancy_result . score ,
21 })
22
23 # Create a pandas DataFrame from the data
24 df_result = pd . DataFrame ( data )
25 return df_result
26
27 def p r i n t _average_scores ( df ) :
28 co rr ectness_scores = df [ ’ Correctness score ’ ]. mean ()
29 f ai t h fulness_scores = df [ ’ Faithfulness score ’ ]. mean ()
30 relevancy_scores = df [ ’ Relevancy score ’ ]. mean ()
31 print ( f " Correctness scores : { correctness_scores } " )
32 print ( f " Faithfulness scores : { faithfulness_scores } " )
33 print ( f " Relevancy scores : { relevancy_scores } " )
59
AIO 2024 aivietnam.edu.vn
Cuối cùng, tổng hợp các hàm đã tạo, chúng ta sẽ có một chương trình đánh giá RAG hoàn thiện.
1 def main () :
2 # Apply nested asyncio
3 nest_asyncio . apply ()
4
5 # Setup OpenAI
6 setup_openai ( api_key = " your_api_key " )
7
8 # Create document and split into nodes
9 text = " H ô m nay tr ờ i n ắ ng , t ô i đ i ă n kem , l ạ nh bu ố t c ả r ă ng ! "
10 nodes = c r e a t e _ d o c u m e n t _ a n d _ s p l i t t e r ( text )
11
12 # Create vector store index and query engine
13 query_engine = c r e a t e _v e c t o r _ s t o r e_ i n d e x ( nodes )
14
15 # Generate evaluation questions
16 df = generate_questions ( nodes )
17
18 # Evaluate and aggregate results
19 eval_result = asyncio . run ( evaluate_async ( query_engine , df ) )
20 df_result = aggregate_results ( df , eval_result )
21
22 # Print average scores
23 p r i n t _average_scores ( df_result )
Sau khi thực hiện chạy hàm main, chúng ta sẽ thu được kết quả về các độ đo:
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
100%| - - - - - - - - - - - -| 2/2 [00:02 <00:00 , 1.13 s / it ]
100%| - - - - - - - - - - - -| 6/6 [00:03 <00:00 , 1.88 it / s ]
Correctness scores : 4.5
Faithfulness scores : 0.5
Relevancy scores : 1.0
======================================================================================
Trong quá trình thực hiện, bạn cứ thoải mái in ra thông tin về các data frames hay bất cứ biến nào
bạn muốn kiểm tra, điều này có thể giúp bạn hiểu hơn quy trình thực hiện.
60
AIO 2024 aivietnam.edu.vn
1 import pandas as pd
2 import nest_asyncio
3 from tqdm . asyncio import tqdm_asyncio
4 import streamlit as st
5 from src import ingest_pipeline , index_builder
6 import os
Tiếp theo, các hàm sẽ giống phần ví dụ, chỉ khác hàm main chúng ta sẽ chỉnh sửa lại phần node và
index như sau:
1 def main () :
2 # Apply nested asyncio
3 nest_asyncio . apply ()
4
5 # Setup OpenAI
6 api_key = st . secrets . openai . OPENAI_API_KEY
7 setup_openai ( api_key = api_key )
8
9 # Create document and split into nodes
10 nodes = ingest_pipeline . ingest_documents ()
11
12 # Create vector store index and query engine
13 index = index_builder . build_indexes ( nodes )
14 dsm5_engine = index . as_query_engine (
15 similarity_top_k =3 ,
16 )
17
18 # Generate evaluation questions
19 df = generate_questions ( nodes )
20
21 # Evaluate and aggregate results
22 eval_result = asyncio . run ( evaluate_async ( query_engine = dsm5_engine , df = df ) )
23 df_result = aggregate_results ( df , eval_result )
24
25 # Print average scores
26 correctness_scores , faithfulness_scores , relevancy_scores = print_average_sc o r e s (
df_result )
27
28 # Save results
29 os . makedirs ( " eval_results " , exist_ok = True )
30 df_result . to_csv ( " eval_results / evaluation_results . csv " , index = False )
31 df . to_csv ( " eval_results / evaluation_questions . csv " , index = False )
32 with open ( " eval_results / average_scores . txt " , " w " ) as f :
33 f . write ( f " Correctness scores : { correctness_scores }\ n " )
34 f . write ( f " Faithfulness scores : { faithfulness_scores }\ n " )
35 f . write ( f " Relevancy scores : { relevancy_scores }\ n " )
Chúng ta load lại nodes và index trong kho dữ liệu. Sau đó tạo DSM5 engine với thiết lập tham số
giống với thiết lập khi tạo DSM5 engine khi xây dựng Agent. Sau khi chạy chương trình, chúng ta sẽ
thu được kết quả đánh giá như sau:
1 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Output = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2 data / ingestion_storage / dsm -5 - cac - tieu - chuan - chan - doan . docx_part_0
3 Cache file found . Running using cache ...
4 All indices loaded from storage .
5 100%| - - -| 227/227 [05:51 <00:00 , 1.55 s / it ]
6 100%| - - -| 681/681 [04:37 <00:00 , 2.46 it / s ]
7 Correctness scores : 4.334801762114537
8 Faithfulness scores : 0.788546255506608
9 Relevancy scores : 0.775330396475771
10 ======================================================================================
61
AIO 2024 aivietnam.edu.vn
Kết quả đánh giá cho thấy hệ thống có độ chính xác cao với Correctness Score là 4.3348. Điều này cho
thấy hệ thống thường cung cấp câu trả lời chính xác. Tuy nhiên, hai chỉ số khác là Faithfulness Score
(0.7885) và Relevancy Score (0.7753) đều ở mức khá, cho thấy các vấn đề về tính trung thực và mức độ
liên quan của thông tin mà hệ thống đưa ra. Cụ thể, điểm Faithfulness cho thấy hệ thống đôi khi thêm
thắt hoặc thay đổi thông tin so với nguồn gốc, làm giảm độ tin cậy của câu trả lời. Điểm Relevancy
cho thấy hệ thống chưa hoàn toàn tập trung vào cung cấp thông tin phù hợp với ngữ cảnh hoặc yêu
cầu cụ thể.
10 Kết luận
Trong bối cảnh sức khỏe tinh thần của người Việt Nam đang gặp những thách thức lớn, việc xây dựng
một hệ thống chăm sóc sức khỏe tinh thần thông minh có thể đóng góp đáng kể vào việc giải quyết
vấn đề này. Qua bài viết, chúng ta đã đi từng bước xác định bài toán, đến các kỹ thuật và công nghệ
RAG, LLM để phát triển hệ thống. Với các kết quả đo lường thu được, Correctness Score đạt 4.3348
cho thấy hệ thống có độ chính xác tương đối cao trong việc cung cấp thông tin chính xác, trong khi
diểm số Faithfulness và Relevancy lần lượt là 0.7885 và 0.7753 ở mức khá. Điều này cho thấy tiềm năng
của hệ thống trong việc chăm sóc sức khỏe tinh thần người Việt.
Trong tương lai, hệ thống này cần sự đánh giá từ các chuyên gia và người dùng thực tế. Việc cải thiện
điểm số đánh giá có thể được xem xét thực hiện bằng cách thử nghiệm trên nhiều loại mô hình LLMs
hơn nữa.
Tóm lại, bài viết đã hoàn thành mục tiêu là đưa ra giải pháp hệ thống chăm sóc sức khỏe tinh thần
thông minh và cung cấp hướng dẫn chi tiết về cách sử dụng LLamaindex trong quá trình xây dựng hệ
thống.
62
AIO 2024 aivietnam.edu.vn
63
AIO 2024 aivietnam.edu.vn
Hình 11: Theo dõi, thống kê tình trạng sức khỏe tinh thần.
64