ContentInjector là thành phần cốt lõi của RetrievalAugmentor trong LangChain4j, có nhiệm vụ đưa nội dung được truy xuất (contents) và siêu dữ liệu (metadata) vào prompt của mô hình ngôn ngữ lớn (LLM). Thành phần này định dạng kết quả truy vấn để phù hợp với yêu cầu đầu vào của LLM, đồng thời cho phép kiểm soát linh hoạt nội dung và siêu dữ liệu được đưa vào.
1. Chức năng chính
(1) Chèn nội dung
Chèn các đoạn văn bản được truy xuất (TextSegment) vào prompt, giúp LLM tạo câu trả lời dựa trên những nội dung này. Ví dụ:
// Nội dung được truy xuất
String noiDung = "Chính sách hủy phòng: 30 ngày trước ngày đến."
// Cách chèn mặc định (nối chuỗi trực tiếp)
String prompt = "Trả lời sử dụng: " + noiDung;
(2) Chèn siêu dữ liệu
Tùy chọn chèn siêu dữ liệu (tên tệp, nguồn, dấu thời gian, v.v.) để LLM hiểu ngữ cảnh. Ví dụ:
// Siêu dữ liệu
Map<String, String> metadata = Map.of(
"ten_tep", "chinh_sach_khach_san.pdf",
"trang", "5",
"ngay", "2023-01-01"
);
// Chèn siêu dữ liệu
String prompt = "Trả lời sử dụng (nguồn: " + metadata.get("ten_tep") + "): " + noiDung;
(3) Mẫu hóa prompt
Hỗ trợ tùy chỉnh PromptTemplate để kiểm soát định dạng của contents và metadata.
Ví dụ:
PromptTemplate template = PromptTemplate.from(
"Bạn là chuyên gia. Trả lời dựa trên các tài liệu sau:\n" +
"{{contents}}\n" +
"Nguồn: {{metadata}}"
);
2. Triển khai mặc định: DefaultContentInjector
LangChain4j cung cấp DefaultContentInjector với các tính năng:
- Lọc trường siêu dữ liệu (
metadataKeysToInclude) - Tùy chỉnh mẫu prompt (
promptTemplate) - Logic nối mặc định (nếu không cung cấp mẫu)
Ví dụ cấu hình
ContentContentInjector noiDungInjector = DefaultContentInjector.builder()
.promptTemplate(PromptTemplate.from(
"Trả lời dựa trên thông tin sau:\n" +
"{{contents}}\n" +
"Nguồn: {{ten_tep}} (trang {{trang}})"
))
.metadataKeysToInclude(Arrays.asList("ten_tep", "trang")) // Chỉ chèn hai trường này
.build();
3. Phương thức quan trọng
(1) inject()
Đưa contents và metadata vào prompt:
String inject(List<TextSegment> doanVan, String thongDiepNguoiDung);
- Đầu vào:
doanVan: danh sách các đoạn văn bản được truy vấn (mỗi đoạn chứatextvàmetadata).thongDiepNguoiDung: truy vấn gốc của người dùng.- Đầu ra: prompt đã được định dạng để LLM sử dụng.
Ví dụ:
List<TextSegment> doanVan = List.of(
new TextSegment("Chính sách hủy phòng 30 ngày.", Map.of("ten_tep", "khach_san.pdf", "trang", "5")),
new TextSegment("Không hoàn tiền sau khi nhận phòng.", Map.of("ten_tep", "khach_san.pdf", "trang", "6"))
);
String prompt = noiDungInjector.inject(doanVan, "Chính sách hủy phòng là gì?");
Đầu ra (phụ thuộc vào mẫu):
Trả lời dựa trên thông tin sau:
- Chính sách hủy phòng 30 ngày.
- Không hoàn tiền sau khi nhận phòng.
Nguồn: khach_san.pdf (trang 5, 6)
4. Cách sử dụng nâng cao
(1) Tùy chỉnh ContentInjector
Nếu cần logic phức tạp hơn (loại bỏ trùng lặp, sắp xếp, cắt ngắn), có thể kế thừa giao diện ContentInjector:
public class CustomContentInjector implements ContentInjector {
@Override
public String inject(List<TextSegment> doanVan, String thongDiepNguoiDung) {
// Logic tùy chỉnh: ví dụ chỉ giữ 3 đoạn đầu tiên và thêm giải thích
List<TextSegment> daLoc = doanVan.stream().limit(3).toList();
String noiDung = daLoc.stream()
.map(segment -> segment.text() + " (Nguồn: " + segment.metadata().get("ten_tep") + ")")
.collect(Collectors.joining("\n"));
return "Truy vấn của người dùng: " + thongDiepNguoiDung + "\nDữ liệu liên quan:\n" + noiDung;
}
}
(2) Kết hợp với RetrievalAugmentor
RetrievalAugmentor boTangTangCuong = DefaultRetrievalAugmentor.builder()
.contentInjector(new CustomContentInjector()) // Sử dụng bộ chèn tùy chỉnh
.contentRetriever(truyVanNoiDung)
.build();
5. Thực hành tốt nhất
- Kiểm soát việc chèn siêu dữ liệu:
- Tránh chèn quá nhiều siêu dữ liệu không liên quan (như
ngay,tac_gia) để không làm nhiễu LLM. - Chỉ giữ lại các trường quan trọng (như
ten_tep,trang,phan).
- Tối ưu hóa mẫu prompt:
- Chỉ rõ cho LLM cách sử dụng nội dung được chèn (ví dụ:
"Trả lời dựa trên các tài liệu sau:"). - Tránh mẫu quá dài để không vượt qua giới hạn ngữ cảnh (Token limit) của LLM.
- Xử lý nhiều đoạn văn:
- Nếu truy xuất được nhiều đoạn, có thể:
- Cắt ngắn nội dung quá dài (ví dụ:
segment.text().substring(0, 500)). - Thêm dấu phân cách (ví dụ:
"---\n") để phân biệt các đoạn khác nhau.
6. Tổng kết
| Chức năng | Mô tả |
|---|---|
| **Chèn nội dung** | Đưa đoạn văn bản được truy xuất vào prompt |
| **Chèn siêu dữ liệu** | Tùy chọn thêm thông tin nguồn (tên tệp, số trang) |
| **Mẫu hóa** | Tùy chỉnh định dạng thông qua PromptTemplate |
| **Mở rộng** | Hỗ trợ tùy chỉnh ContentInjector để thực hiện logic phức tạp |
ContentInjector là thành phần quan trọng trong đường ống RAG (tăng cường truy vấn), nó quyết định cách thức nội dung từ kho kiến thức bên ngoài được chuyển hiệu quả đến LLM, ảnh hưởng trực tiếp đến chất lượng kết quả tạo ra. Cấu hình hợp lý có thể cải thiện đáng kể độ chính xác và khả năng giải thích của câu trả lời.