Spring Boot 4 – komplett guide · Lektion

Embeddings och hämtning från vektorlagring

Generera embeddings och fråga vektorlagringar för att möjliggöra semantisk sökning i dina data.

Lektion 2 av 413 steg

Embeddings och hämtning från vektorlagring är en gratis lektion i Spring Boot 4 – komplett guide på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Spring Boot 4 – komplett guide, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Spring Boot 4 – komplett guide innehåller totalt 4 lektioner.

Varför embeddings möjliggör semantisk sökning

Nyckelordssökning matchar bokstavliga token. Semantisk sökning matchar betydelse. Bryggan mellan de två är en embedding: en vektor med flyttal och fast längd som fångar det semantiska innehållet i en text.

  • Texter med liknande betydelse ger vektorer som ligger nära varandra i vektorrummet.
  • Närheten mäts med cosine similarity eller euklidiskt avstånd.
  • En fråga som "how do I reset my password" kan matcha ett dokument med rubriken "account recovery steps" även om de inte har ett enda gemensamt nyckelord.

I Spring AI genererar ni embeddings med en EmbeddingModel och lagrar och frågar efter dem med en VectorStore. Den här lektionen kopplar ihop båda i en Retrieval-Augmented-pipeline.

Abstraktionen EmbeddingModel

Spring AI tillhandahåller EmbeddingModel som ett leverantörsoberoende gränssnitt. Lägg till starter-paketet (till exempel spring-ai-starter-model-openai), så autokonfigurerar Spring Boot en bean som ni kan injicera.

  • embed(String) returnerar en enskild float[].
  • embed(List<String>) batchar flera texter effektivt.
  • dimensions() anger vektorns storlek (till exempel 1536 för text-embedding-3-small).

Konfigurera modellen i application.yml så att beanen är klar för injicering.

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      embedding:
        options:
          model: text-embedding-3-small

Generera er första embedding

Injicera EmbeddingModel och anropa embed. Resultatet är en tät vektor som ni kan inspektera eller spara. Det mer omfattande anropet embedForResponse returnerar även metadata om tokenanvändningen.

  • Logga alltid dimensions() en gång vid uppstart så att ett modellbyte som ändrar vektorns storlek upptäcks tidigt.
  • En kolumn i ett vektorlager måste ha exakt samma dimension, annars misslyckas infogningen.
@RestController
class EmbeddingController {

    private final EmbeddingModel embeddingModel;

    EmbeddingController(EmbeddingModel embeddingModel) {
        this.embeddingModel = embeddingModel;
    }

    @GetMapping("/embed")
    Map<String, Object> embed(@RequestParam String text) {
        float[] vector = embeddingModel.embed(text);
        return Map.of(
            "dimensions", vector.length,
            "preview", List.of(vector[0], vector[1], vector[2])
        );
    }
}

Cosine similarity för hand

För att bygga upp en intuitiv förståelse ser ni här den matematik som ett vektorlager utför åt er. Cosine similarity är skalärprodukten av två vektorer dividerad med produkten av deras magnituder. Värdet sträcker sig från -1 (motsatt riktning) till 1 (identisk riktning).

  • Ett värde nära 1.0 betyder att texterna är semantiskt mycket lika.
  • Vektorlager visar vanligtvis ett likhetspoäng som härleds från detta mått.

Det här fristående programmet beräknar cosine similarity för två exempelvektorer.

public class CosineSimilarity {
    static double cosine(double[] a, double[] b) {
        double dot = 0, normA = 0, normB = 0;
        for (int i = 0; i < a.length; i++) {
            dot += a[i] * b[i];
            normA += a[i] * a[i];
            normB += b[i] * b[i];
        }
        return dot / (Math.sqrt(normA) * Math.sqrt(normB));
    }

    public static void main(String[] args) {
        double[] query = {0.9, 0.1, 0.2};
        double[] docA  = {0.8, 0.2, 0.1};
        double[] docB  = {-0.5, 0.9, -0.4};
        System.out.printf("query vs A: %.4f%n", cosine(query, docA));
        System.out.printf("query vs B: %.4f%n", cosine(query, docB));
    }
}

Abstraktionen VectorStore

En VectorStore lagrar Document-objekt tillsammans med deras embeddings och stöder likhetsfrågor. Spring AI levereras med implementationer för PGVector, Redis, Chroma, Milvus och Qdrant samt en SimpleVectorStore för tester.

  • add(List<Document>) skapar embeddings för och lagrar dokument.
  • similaritySearch(SearchRequest) skapar en embedding av frågan och returnerar de närmaste dokumenten.
  • delete(...) tar bort dokument efter id eller filter.

Viktigt är att add anropar EmbeddingModel åt er, så vid användning av ett lager behöver ni sällan skapa embeddings manuellt.

Konfigurera PGVector

För produktion är PGVector (Postgres-tillägget) ett vanligt val eftersom vektorerna lagras tillsammans med era relationsdata. Lägg till spring-ai-starter-vector-store-pgvector och konfigurera initieringen av schemat.

  • index-type: HNSW ger snabb approximerad sökning efter närmaste grannar.
  • dimensions måste motsvara utmatningsstorleken hos er embeddingmodell.
  • initialize-schema: true låter Spring skapa tabellen vector_store vid uppstart.
spring:
  ai:
    vectorstore:
      pgvector:
        initialize-schema: true
        index-type: HNSW
        distance-type: COSINE_DISTANCE
        dimensions: 1536
  datasource:
    url: jdbc:postgresql://localhost:5432/appdb
    username: app
    password: ${DB_PASSWORD}

Läsa in dokument

Omslut varje del av källtexten i ett Document. Ni kan lägga till valfria metadata (källa, författare, klient-id) som kan användas i filter vid frågor. Anropet vectorStore.add(...) skapar embeddings och lagrar dem i ett enda steg.

  • Använd ett stabilt id när ni vill uppdatera eller infoga på nytt i stället för att skapa dubbletter.
  • Håll delarna små (några hundra token) så att den hämtade kontexten förblir fokuserad.
@Service
class IngestionService {

    private final VectorStore vectorStore;

    IngestionService(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    void ingest() {
        List<Document> docs = List.of(
            new Document("Spring Boot 4 requires Java 17 or later.",
                Map.of("source", "release-notes", "version", "4.0")),
            new Document("Reset your password from the account recovery page.",
                Map.of("source", "help-center", "topic", "auth"))
        );
        vectorStore.add(docs);
    }
}

Dela upp med ETL-läsare

Verkliga textkorpusar kommer som PDF, Markdown eller JSON. Spring AI:s ETL-pipeline tillhandahåller källor av typen DocumentReader och uppdelare av typen DocumentTransformer. TokenTextSplitter delar upp stora dokument i delar anpassade för embeddings samtidigt som metadata bevaras.

  • TikaDocumentReader läser PDF, Word och HTML.
  • TokenTextSplitter delar upp efter tokenantal med konfigurerbart överlapp.
  • Resultatet skickas direkt vidare till vectorStore.add(...).
@Service
class PdfIngestion {

    private final VectorStore vectorStore;

    PdfIngestion(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    void load(Resource pdf) {
        var reader = new TikaDocumentReader(pdf);
        var splitter = new TokenTextSplitter();
        List<Document> chunks = splitter.apply(reader.get());
        vectorStore.add(chunks);
    }
}

Likhetssökning med SearchRequest

SearchRequest är en builder som styr hämtningen. De viktigaste reglagen är topK (antal resultat) och similarityThreshold (svaga matchningar under en gräns tas bort).

  • Ett högre similarityThreshold (till exempel 0.75) minskar brus men kan ge färre träffar.
  • topK begränsar hur mycket kontext ni skickar till LLM:en, vilket styr tokenkostnaden.
@Service
class SearchService {

    private final VectorStore vectorStore;

    SearchService(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    List<Document> search(String query) {
        return vectorStore.similaritySearch(
            SearchRequest.builder()
                .query(query)
                .topK(4)
                .similarityThreshold(0.7)
                .build()
        );
    }
}

Metadatafiltrering

Semantisk relevans räcker inte för data som är uppdelad mellan flera klienter eller avgränsad på annat sätt. SearchRequest accepterar ett filteruttryck som utvärderas mot dokumentens metadata och kombinerar vektorsimilaritet med strukturerade begränsningar.

  • Använd FilterExpressionBuilder eller ett portabelt stränguttryck.
  • Lagret tillämpar filtret och likhetsrankningen tillsammans.
  • Så här upprätthåller ni klientisolering: tenantId == 'acme'.
List<Document> scoped = vectorStore.similaritySearch(
    SearchRequest.builder()
        .query("how do I reset my password")
        .topK(3)
        .similarityThreshold(0.7)
        .filterExpression("source == 'help-center' && topic == 'auth'")
        .build()
);

Från hämtning till RAG

Hämtning är den första halvan av RAG (Retrieval-Augmented Generation). Den andra halvan lägger in de hämtade delarna i prompten så att LLM:en svarar med stöd i era data. Spring AI:s QuestionAnswerAdvisor kopplar vektorlagret direkt till ett ChatClient-anrop.

  • Advisorn kör en likhetssökning per begäran och lägger in resultaten som kontext.
  • Ni skickar in samma inställningar för SearchRequest (topK, threshold) till advisorn.
  • Det gör svaret troget era indexerade dokument och minskar hallucinationer.
@Service
class RagService {

    private final ChatClient chatClient;

    RagService(ChatClient.Builder builder, VectorStore vectorStore) {
        this.chatClient = builder
            .defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
                .searchRequest(SearchRequest.builder().topK(4).similarityThreshold(0.7).build())
                .build())
            .build();
    }

    String ask(String question) {
        return chatClient.prompt().user(question).call().content();
    }
}

Snabbkontroll: justera hämtningen

En kollega rapporterar att deras RAG-endpoint ofta lägger in irrelevanta dokument i prompten, vilket ökar tokenkostnaden och förvirrar modellen. De vill bara behålla starkt relevanta matchningar utan att ändra embeddingmodellen. Vilken enskild justering av SearchRequest åtgärdar detta mest direkt?

Sammanfattning och viktiga slutsatser

Ni har byggt en komplett pipeline för semantisk hämtning i Spring AI:

  • EmbeddingModel omvandlar text till täta vektorer; dimensions() måste matcha storleken på kolumnen i lagret.
  • VectorStore (PGVector, Redis, Qdrant med flera) lagrar Document-objekt och skapar automatiskt embeddings för dem vid add().
  • Använd ETL-pipelinen (TikaDocumentReader + TokenTextSplitter) för att dela upp verkliga filer före inläsningen.
  • SearchRequest styr hämtningen via topK, similarityThreshold och metadatafältet filterExpression för avgränsning och klientisolering.
  • QuestionAnswerAdvisor omvandlar hämtningen till fullständig RAG genom att lägga in matchande kontext i en ChatClient-prompt.

Justera topK för kostnad och similarityThreshold för precision för att balansera förankring mot tokenbudgeten.

Gratis att börja

Lär dig Java med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
21
Lektioner
84

Vanliga frågor

Är lektionen ”Embeddings och hämtning från vektorlagring” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Spring Boot 4 – komplett guide, inklusive ”Embeddings och hämtning från vektorlagring”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Spring Boot 4 – komplett guide innehåller totalt 4 lektioner.

Vad lär jag mig i ”Embeddings och hämtning från vektorlagring”?

Generera embeddings och fråga vektorlagringar för att möjliggöra semantisk sökning i dina data. Ni övar på Spring Boot 4 – komplett guide med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Spring Boot 4 – komplett guide?

Du behöver inga förkunskaper. Utbildningen i Spring Boot 4 – komplett guide på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Embeddings och hämtning från vektorlagring”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Spring Boot 4 – komplett guide-lektionen?

Ja. Varje Spring Boot 4 – komplett guide-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. ChatClient, promptar och strukturerade utdata
  2. Embeddings och hämtning från vektorlagring
  3. Pipelines för Retrieval-Augmented Generation
  4. Verktygsanrop och agentrådgivare
← Tillbaka till Spring Boot 4 – komplett guide