Spring AI · Söhbət yaddaşı və kontekst · Dərs 1

Modelin kontekst məhdudiyyətləri

Əvvəlki mesajları söhbətin kontekstində saxla və növbəti sorğularda modelə ötürərək ardıcıl dialoq qur.

Modelin kontekst limitləri

Study Coach indiyədək hər sual üçün yeni prompt göndərirdi. Əl ilə əlavə etdiyimiz qeydlər və RAG fraqmentləri əlavə məlumat verirdi, amma əvvəlki dialoqu saxlamırdı. Bu modulda eyni Java 21 layihəsinə tətbiqin idarə etdiyi söhbət vəziyyətini əlavə edəcəyik. Mövcud Spring AI 1.1.8 asılılıqları kifayətdir.

1. Hər çağırışda konteksti yenidən qurun

“Nümunə göstərə bilərsən?” kimi davam sualını anlamaq üçün əvvəlki sual və cavab lazım ola bilər. Tətbiq həmin mesajları yaddaşdan oxuyub növbəti sorğuya əlavə edə bilər. Bu nümunədə model çağırışlar arasında ayrıca söhbət saxlamır; seçilmiş mesajları Java prosesi saxlayır və yenidən göndərir. Provayderin məlumat saxlama siyasəti ayrıca məsələdir.

ChatMemory söhbət ID-si üzrə add, get və clear əməliyyatlarını təqdim edir. MessageWindowChatMemory repozitori üzərində mesaj pəncərəsi siyasətini tətbiq edir. Mesajları repozitori saxlayır; model isə yalnız tətbiqin cari sorğuya daxil etdiyi məlumatı görür. Spring AI söhbət yaddaşı

2. Üç fərqli limiti ayırın

LimitNəyi idarə edirNəyi müəyyən etmir
Mesaj pəncərəsiSeçilmiş siyasətə uyğun saxlanan mesajların sayıModelin token büdcəsini və ya tam sual-cavab cütlərini
Giriş və çıxış yoxlamalarıTətbiqin qəbul etdiyi sual və cavab mətninin uzunluğuProvayder xərcini, generasiya vaxtını və ya faktların doğruluğunu
Saxlama siyasətiSöhbətin nə qədər müddət istifadə oluna bilməsini və yaddaşın nə vaxt təmizlənməsiniProvayderin silmə siyasətini

Modelin kontekst pəncərəsi tokenlərlə ölçülür. Sistem təlimatları, tarixçə, cari giriş, tapılmış fraqmentlər, alətlərlə bağlı mesajlar və cavab üçün ayrılan yer sorğunun büdcəsinə təsir edir. Tokenləşdirmə və dəstəklənən limitlər seçilmiş modeldən asılıdır. Bir simvolu bir token saymayın və bütün provayderlərin girişlə çıxışı eyni qaydada hesabladığını düşünməyin.

Mövcud sual limiti Java-da 2,000 UTF-16 kod vahididir; AnswerChecks isə ən çox 4,000 kod vahidini qəbul edir. Bunlar sadə tətbiq sərhədləridir. Real yerləşdirmədən əvvəl seçilmiş provayderin və modelin limitlərini yoxlayın, cavab üçün yer ayırın və uyğun token büdcəsi tətbiq edin. Cavab uzunluğunun yoxlanması generasiyadan sonra baş verir və onun xərcinin qarşısını ala bilmir.

3. Mesaj pəncərəsinin nəyi sildiyini müşahidə edin

Standart pəncərə 20 mesajdır; biz daha kiçik dəyəri açıq şəkildə seçəcəyik. Üç mesajlıq pəncərəyə iki istifadəçi/assistent cütü əlavə etdikdə birinci assistent cavabı öz sualı olmadan qala bilər. Çıxarma tam dialoq addımı üzrə deyil, mesaj üzrə aparılır. Kiçik pəncərə davam sualı üçün lazım olan üstünlüyü və ya faktı da silə bilər.

Sabitlənmiş implementasiyada sistem mesajları adi çıxarma zamanı qorunur. Fərqli yeni sistem mesajı əlavə etdikdə isə köhnə sistem mesajları əvəz olunur. Söhbət məlumatını saxlamaq üçün ixtiyari sistem mesajlarını təkrar əlavə etməyin. Sonrakı advisor servisimiz cari sistem təlimatını hər çağırışda verəcək və yalnız dialoq mesajlarını saxlayacaq. Mesaj pəncərəsinin implementasiyası

4. Siyasəti model olmadan test edin

src/test/java/academy/study/MemoryWindowTest.java faylını yaradın:

Java
package academy.study;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;

import static org.junit.jupiter.api.Assertions.*;

class MemoryWindowTest {
    @Test
    void evictsMessagesRatherThanWholeTurns() {
        var memory = MessageWindowChatMemory.builder().maxMessages(3).build();
        memory.add("example", List.of(new UserMessage("First question"),
                new AssistantMessage("First answer"), new UserMessage("Second question"),
                new AssistantMessage("Second answer")));
        assertEquals(List.of("First answer", "Second question", "Second answer"),
                memory.get("example").stream().map(message -> message.getText()).toList());
    }

    @Test
    void preservesSystemMessageAndReplacesItWhenANewOneArrives() {
        var memory = MessageWindowChatMemory.builder().maxMessages(3).build();
        memory.add("example", List.of(new SystemMessage("Old policy"),
                new UserMessage("First question"), new AssistantMessage("First answer")));
        memory.add("example", new UserMessage("Second question"));
        assertEquals("Old policy", memory.get("example").getFirst().getText());
        assertEquals(3, memory.get("example").size());
        memory.add("example", new SystemMessage("New policy"));
        assertEquals(List.of("New policy"), memory.get("example").stream()
                .filter(SystemMessage.class::isInstance).map(message -> message.getText()).toList());
    }

    @Test
    void messageCountDoesNotLimitTextLength() {
        var memory = MessageWindowChatMemory.builder().maxMessages(1).build();
        memory.add("example", new UserMessage("x".repeat(20_000)));
        assertEquals(1, memory.get("example").size());
        assertEquals(20_000, memory.get("example").getFirst().getText().length());
    }

    @Test
    void clearRemovesOnlyTheSelectedConversation() {
        var memory = MessageWindowChatMemory.builder().maxMessages(3).build();
        memory.add("first", new UserMessage("First"));
        memory.add("second", new UserMessage("Second"));
        memory.clear("first");
        assertTrue(memory.get("first").isEmpty());
        assertEquals("Second", memory.get("second").getFirst().getText());
    }
}

Testlərdə qəsdən sintetik mətn istifadə olunur. 20,000 simvolluq nümunə say limitinin mesaj ölçüsünü məhdudlaşdırmadığını göstərir. Bu, Study Coach-un adi giriş yoxlamasını keçmək üçün tövsiyə deyil.

5. Nəyin yadda saxlanmalı olduğunu seçin

Söhbət tarixçəsi iştirakçıların nə dediyini göstərir. O, yoxlanmış bilik bazası deyil. Əvvəlki assistent səhvi təkrarlana bilər; əvvəlki istifadəçi təlimatı yenidən göndərildikdə də prompt injection cəhdi olaraq qala bilər. Mesaj rollarını qoruyun; köhnə istifadəçi mətnini sistem təlimatına çevirməyin.

RAG sual üçün sənəd fraqmentlərini tapır. Yaddaş isə söhbətin əvvəlki dialoqunu qaytarır. Bunları birləşdirmək olar, amma bu modulda saxlama və icazə davranışı aydın görünsün deyə yeni servisi RAG müştərisindən ayrı saxlayırıq. Sonradan hər iki advisor-u əlavə etdikdə sıra, kontekstin böyüməsi və tapılmış mətnin saxlanması və ya yenidən göndərilməsi ayrıca test edilməlidir.

Xülasə giriş ölçüsünü azalda bilər, amma o da generasiya edilmiş mətndir və detalları buraxa və ya təhrif edə bilər. Burada xülasələşdirmə qurulmur. Lazım olan fakt pəncərədən çıxıbsa, onun hələ mövcud olduğunu güman etmək əvəzinə öyrənəndən yenidən soruşun və ya icazəli mənbəyə müraciət edin.

6. Fərqi yoxlayın

Layihənin bütün yığılmış testlərini başladın:

sh
env -u RUN_LIVE_EVALUATION mvn clean verify

Əvvəlki dərslər tam tətbiq edilibsə, qırx səkkiz oflayn test metodu keçməli, bir canlı qiymətləndirmə metodu isə ötürülməlidir. Bu testlər provayder çağırmır və cavab keyfiyyətini ölçmür.

Tapşırıq: birinci testdə pəncərəni üçdən dördə dəyişin və gözlənilən siyahını yeniləyin. Sonra dörd çox uzun mesajın modelin kontekst limitini aşa bildiyini, iyirmi qısa mesajın isə həmin limitə sığa bildiyini izah edin.

Cavab yoxlaması: dörd mesajlıq pəncərə bu testdə hər iki tam cütü saxlayır. Mesaj sayı nə token sayını, nə də uyğunluğu idarə edir. Tətbiq bütün sorğu üçün büdcə ayırmalı və çatışmayan tarixçə ilə necə davranacağını seçməlidir.