Spring AI · RAG ilə sənədlərə əsaslanan cavablar · Dərs 1
RAG yanaşması və sənədlərin rolu
Spring AI advisor mexanizmi ilə axtarışla zənginləşdirilmiş generasiya (RAG) qur. Modelin təlim məlumatlarında olmayan sənədləri cavablar üçün mənbə kimi istifadə et.
RAG yanaşması və sənədlərin rolu
Java 21, Spring Boot 3.5.9 və Spring AI 1.1.8 ilə mövcud Study Coach layihəsində davam et. Əvvəlki modul bir istinadı birbaşa Java-da seçirdi. Axtarışla zənginləşdirilmiş generasiya (RAG) hər sual üçün generasiyadan əvvəl uyğun kontekst namizədlərini seçən axtarış addımı əlavə edir.
1. Sənədlərin hazırlanmasını sual-cavabdan ayır
Hazırlanma mərhələsi istifadəsinə icazə verilmiş mənbələri oxuyur, lazım olduqda hissələrə bölür, embedding hesablayır və mətni, vektorları, mənbə məlumatını saxlayır. Sual gələndə tətbiq sualın embedding-ini hesablayır, yaxın sənəd vektorlarını axtarır və seçilmiş parçaları chat modelinə verir. Sonra generasiya namizəd cavab yaradır.
Embedding mətni ədədlərlə təsvir edir. Oxşarlıq əlaqəli materialı tapmağa kömək edə bilər, amma həqiqət balı və ya giriş hüququ qərarı deyil. Axtarış lazım olan parçanı tapa bilməyə və ya əlaqəsiz parça qaytara bilər. RAG modelin təlimində olmayan materialı təqdim edə bilər; düzgün cavaba zəmanət vermir. Spring AI RAG icmalı
2. Vektor saxlancları üçün advisor modulunu əlavə et
Bu asılılığı POM-un əsas <dependencies> elementinə əlavə et. Mövcud Spring AI BOM 1.1.8 versiyasını təyin edir. Əvvəlki asılılıqları saxla:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>Nümunələr yaddaşda işləyən SimpleVectorStore ilə qurulur. O, saxlanmış vektorları kosinus oxşarlığı ilə axtarır. Bu kiçik məşqə uyğundur, amma production vektor bazası və ya davamlı indeks deyil. Dərslərdə onun save/load metodları çağırılmır, ona görə yeni saxlanc boş başlayır. SimpleVectorStore implementasiyası
3. Kiçik, icazəli sənəd dəsti müəyyən et
src/main/java/academy/study/RagCorpus.java faylını saxla:
package academy.study;
import java.util.List;
import java.util.Map;
import org.springframework.ai.document.Document;
public final class RagCorpus {
public static final String RESOURCES = """
A try-with-resources statement closes its resources when control leaves the try block.
A resource must implement AutoCloseable.
""";
private RagCorpus() {}
public static List<Document> documents() {
return List.of(document("inheritance-v1", InheritanceReference.CONTEXT),
document("resources-v1", RESOURCES));
}
private static Document document(String id, String text) {
return Document.builder().id(id).text("[source:" + id + "]\n" + text)
.metadata(Map.of("source_id", id, "visibility", "public")).build();
}
}İrsilik qeydi qiymətləndirmə modulundakı yoxlanılmış istinadı təkrar istifadə edir. Resurs qeydi Java-nın try-with-resources qaydasını qısa izah edir. Başlanğıc xətaları, bağlanma sırası və suppressed exception-ları tam əhatə etmir. Java 21 try-with-resources
Bütün mətn istifadəsinə icazə verilmiş açıq tədris materialıdır. source_id mənbəni qeyd edir, mətnin daxilindəki açıq işarə isə modelə görünəcək. Metadata sənəd mətnindən ayrıdır; advisor-un hər metadata sahəsini avtomatik prompt-a yerləşdirdiyini düşünmə. Mənbənin mənası dəyişəndə versiyasını da dəyiş.
4. Test üçün aydın embedding əvəzedicisi istifadə et
Bu faylı production mənbəsində deyil, src/test/java/academy/study/KeywordEmbeddingModel.java yolunda saxla:
package academy.study;
import java.util.ArrayList;
import java.util.Locale;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.Embedding;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.EmbeddingRequest;
import org.springframework.ai.embedding.EmbeddingResponse;
final class KeywordEmbeddingModel implements EmbeddingModel {
private float[] vector(String text) {
String lower = text.toLowerCase(Locale.ROOT);
float inheritance = lower.matches("(?s).*\\b(class|classes|superclass|inheritance)\\b.*") ? 1 : 0;
float resources = lower.matches("(?s).*\\b(resource|resources|autocloseable)\\b.*") ? 1 : 0;
return new float[] { inheritance, resources,
inheritance == 0 && resources == 0 ? 1 : 0 };
}
@Override
public float[] embed(Document document) {
return vector(document.getText());
}
@Override
public EmbeddingResponse call(EmbeddingRequest request) {
var results = new ArrayList<Embedding>();
for (int i = 0; i < request.getInstructions().size(); i++) {
results.add(new Embedding(vector(request.getInstructions().get(i)), i));
}
return new EmbeddingResponse(results);
}
@Override
public int dimensions() {
return 3;
}
}Bu, açar sözləri üç ədədə çevirən mexanizmdir, təlim keçmiş semantik model deyil. İrsilik və resurslarla bağlı bir neçə ingilis sözünü tanıyır, qalanları üçün üçüncü ölçünü istifadə edir. Azərbaycan dilini, sinonimləri və mənanı başa düşmür. Provayder çağırışı olmadan axtarış testlərini təkrarlana bilən etmək üçün istifadə edilir.
Real embedding modeli dillərinə və sənədlərinə uyğun olmalıdır. Həm indekslənmiş sənədlər, həm də suallar üçün uyğun model/versiya/ölçü parametrləri istifadə et. Embedding modeli dəyişəndə adətən indeksi yenidən qurmaq lazımdır; müxtəlif embedding fəzalarının vektorları təkcə uzunluqları eyni olduğuna görə bir-birini əvəz etmir.
5. Seçimi və sabit metadata filtrini yoxla
src/test/java/academy/study/RagRetrievalTest.java faylını saxla:
package academy.study;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import static org.junit.jupiter.api.Assertions.*;
class RagRetrievalTest {
private SimpleVectorStore store() {
var store = SimpleVectorStore.builder(new KeywordEmbeddingModel()).build();
store.add(RagCorpus.documents());
return store;
}
private SearchRequest query(String text) {
return SearchRequest.builder().query(text).topK(2).similarityThreshold(0.75)
.filterExpression("visibility == 'public'").build();
}
@Test
void retrievesTheMatchingTeachingNote() {
var store = store();
assertEquals(List.of("inheritance-v1"), store.similaritySearch(query("class inheritance"))
.stream().map(Document::getId).toList());
assertEquals(List.of("resources-v1"), store.similaritySearch(query("AutoCloseable resources"))
.stream().map(Document::getId).toList());
}
@Test
void filtersTheSyntheticPrivateMarker() {
var store = store();
store.add(List.of(Document.builder().id("private-marker")
.text("Synthetic inheritance marker")
.metadata(Map.of("visibility", "private")).build()));
var hits = store.similaritySearch(query("inheritance"));
assertEquals(1, hits.size());
assertEquals("inheritance-v1", hits.getFirst().getId());
}
@Test
void returnsNoHitsForAnUnrepresentedTopic() {
assertTrue(store().similaritySearch(query("galaxies")).isEmpty());
}
}topK(2) qaytarılan nəticələrin yuxarı həddidir, iki uyğun sənəd vədi deyil. 0.75 həddi bu deterministik məşq üçün seçilib. Universal tövsiyə və ya 75% əminlik deyil. Real axtarışı qiymətləndirilmiş nümunələrlə yoxla, tapılmayan və əlaqəsiz nəticələri ölç.
Private marker süni test məlumatıdır. Sabit visibility filtri bu axtarışın həmin markeri çıxardığını sübut edir; autentifikasiya və ya tenant icazə yoxlaması qurmur. Real tətbiqdə etibarlı server qaydası axtarışdan əvvəl icazəli sənəd dəstini/filtri seçməlidir. Giriş hüquqlarını müəyyən edən filtri birbaşa öyrənəndən və ya modeldən qəbul etmə.
6. Əl ilə kontekstdən fərqi yoxla
İcra et:
env -u RUN_LIVE_EVALUATION mvn clean verifyOtuz altı offline metod keçməli, bir real qiymətləndirmə metodu buraxılmalıdır. Bu testlər xarici embedding və ya chat modeli çağırmır.
Tapşırıq: sabit InheritanceReference prompt-unu “AutoCloseable resources” axtarışı ilə müqayisə et. Sonra dəstəklənən açar sözü olmayan sorğu sına və nəticənin niyə boş olduğunu izah et.
Cavabın yoxlanması: əvvəlki factory həmişə irsilik qeydini seçirdi. İndi axtarış sual vektoruna əsasən resurs qeydini seçir. Dəstəklənməyən sual test əvəzedicisinin üçüncü ölçüsünə düşür; açıq qeydlərin heç biri həddən yuxarı uyğunluq göstərmir. Bu, axtarış bağlantısının işlədiyini göstərir, real semantik axtarış keyfiyyətini deyil. Sonra axtarışı və prompt-a kontekst əlavə etməyi advisor-a verəcəyik.