Разработка ИИ-агентов · Модуль 2 · Урок 2.3
Память: краткосрочная vs долгосрочная, основы retrieval/RAG
Два вида памяти
У агента полезно различать два вида памяти. Краткосрочная — это текущий контекст диалога: история сообщений в окне модели. Она живёт в рамках сессии и ограничена окном (см. урок 2.2). Долгосрочная — это внешнее хранилище знаний и фактов, которое переживает сессию: документация, прошлые разговоры, профиль пользователя, база товаров.
Долгосрочную память нельзя целиком засунуть в контекст — она слишком большая и в основном нерелевантна текущему вопросу. Поэтому к ней обращаются выборочно: на каждый запрос достают только подходящие куски и подкладывают их в окно. Этот приём и называется retrieval.
RAG: retrieval-augmented generation
RAG — это паттерн «найди, потом сгенерируй». Вместо того чтобы надеяться на знания внутри модели, мы перед ответом ищем релевантные фрагменты во внешней базе и добавляем их в контекст как опору. Базовый конвейер:
- Заранее разбить документы на куски (chunks) и проиндексировать их.
- На запрос найти top-k наиболее релевантных кусков.
- Вставить найденное в промпт и попросить модель ответить, опираясь только на эти куски, со ссылками на источник.
RAG резко снижает выдумывание (галлюцинации) и позволяет работать с приватными/свежими данными, которых нет в модели. В этом курсе AI-тьютор устроен именно так: он ищет по теории и отвечает по найденному.
Лексический поиск vs эмбеддинги
Как искать релевантные куски? Два основных подхода.
- Лексический (по словам): BM25, TF-IDF. Сравнивает совпадения терминов. Прост, без внешних зависимостей, отлично работает на точных терминах и кодах. Минус — не понимает синонимы и перефразы.
- Семантический (эмбеддинги): тексты превращают в векторы, близость считают по косинусу. Ловит смысл и синонимы. Минус — нужна модель эмбеддингов и векторное хранилище.
Для старта и для небольших баз лексический поиск часто достаточен и его легко написать на Go без зависимостей. Лучшие результаты даёт гибрид: совместить лексический и семантический поиск. Начинайте с простого; усложняйте, когда упрётесь в качество.
// Retriever абстрагирует способ поиска (лексический, эмбеддинги — неважно).
type Doc struct {
Source string // откуда кусок (для ссылки в ответе)
Text string
}
type Retriever interface {
Search(query string, k int) []Doc
}
// answerWithRAG: найти контекст и попросить модель ответить только по нему.
func answerWithRAG(r Retriever, question string) (string, error) {
docs := r.Search(question, 4) // top-k релевантных кусков
var sb strings.Builder
for i, d := range docs {
fmt.Fprintf(&sb, "[Источник %d: %s]\n%s\n\n", i+1, d.Source, d.Text)
}
sys := "Отвечай ТОЛЬКО по приведённым источникам и указывай номер источника. " +
"Если ответа в них нет — скажи об этом прямо."
choice, err := callLLM([]Message{
{Role: "system", Content: sys},
{Role: "user", Content: "Источники:\n" + sb.String() + "\nВопрос: " + question},
}, nil)
if err != nil {
return "", err
}
return choice.Content, nil
}Anti-patterns
| Анти-паттерн | Почему плохо | Как правильно |
|---|---|---|
| Считать контекст диалога «долгой памятью» | Он исчезает с окном/сессией | Долгую память хранить во внешнем индексе и доставать retrieval'ом |
| Класть всю базу знаний в промпт | Не влезет, дорого, шумно | Доставать только top-k релевантных кусков на запрос |
| Отвечать «из головы» при наличии базы | Галлюцинации, устаревшие факты | RAG: грунтить ответ на найденных кусках со ссылками |
| Сразу строить векторный стек ради 50 документов | Преждевременная сложность | Начать с лексического поиска; эмбеддинги — когда нужно качество/синонимы |
Практическое задание
- Сформулируйте, что для вашего агента краткосрочная память, а что — долгосрочная.
- Соберите маленькую базу из 5–10 текстовых кусков с указанием источника.
- Реализуйте
Retrieverс простым лексическим поиском (например, по совпадению слов/BM25-lite). - Подставьте top-k найденных кусков в промпт и попросите модель отвечать только по ним, со ссылками.
- Задайте вопрос, которого нет в базе, и убедитесь, что агент честно говорит «не найдено», а не выдумывает.
Проверка знаний
Что описывает паттерн RAG?
Верный ответ: B
B верно. RAG = retrieval-augmented generation: сначала найти опорные куски, затем генерировать ответ, опираясь на них. A — это fine-tuning, другое. C и D — приёмы из урока 2.2.
Когда лексический поиск (BM25) предпочтительнее эмбеддингов для старта?
Верный ответ: B
B верно. Лексический поиск отлично подходит для точных терминов и малых баз, его легко написать без внешних сервисов. Синонимы и смысл (D) — сильная сторона эмбеддингов; лучший результат часто даёт гибрид.
Главный выигрыш RAG по сравнению с ответом «из памяти модели»?
Верный ответ: B
B верно. Грунтинг на найденных кусках снижает выдумывание и даёт доступ к данным, которых нет в модели, с прослеживаемыми источниками.