LightRAG роман Анна Каренина
Что такое LightRAG. Немного теории.
LightRAG (Lightweight Retrieval‑Augmented Generation) — фреймворк для Retrieval‑Augmented Generation (RAG), который повышает качество работы больших языковых моделей (LLM) за счёт интеграции графовых структур в процесс поиска информации. Разработан лабораторией Data Science HKU, представлен на EMNLP 2025.
LightRAG решает ключевые проблемы традиционных RAG‑систем:
- фрагментарность ответов;
- слабая обработка сложных взаимосвязей между данными;
- высокие вычислительные затраты;
- необходимость полного перестроения индекса при обновлении данных.
Как работает LightRAG: основные этапы
- Сегментация и предобработка документов
- Документы разбиваются на небольшие фрагменты.
- Выполняется дедупликация — удаляются дублирующиеся фрагменты.
- Извлечение сущностей и отношений
- LLM анализирует каждый фрагмент, выявляя сущности (узлы графа) и отношения между ними (рёбра графа).
- Пример: из текста «Пчеловод наблюдает за пчёлами» извлекаются сущности «Пчеловод» и «Пчёлы», а также отношение «наблюдает за».
- Для каждой сущности и отношения генерируются пары «ключ‑значение» для последующего поиска и суммирования.
- Построение графа знаний
- Извлечённые сущности и отношения объединяются в единый граф.
- Граф оптимизируется за счёт дедупликации — одинаковые сущности и отношения из разных документов объединяются.
- Структура графа позволяет выполнять многошаговое рассуждение (multi‑hop reasoning) и сохранять контекст.
- Двухуровневый поиск (Dual‑Level Retrieval)
- Низкоуровневый поиск — поиск конкретных сущностей и их отношений (например, «Какая связь между AuthService и LoggerModule?»).
- Высокоуровневый поиск — поиск концепций и тем (например, «Объясни принципы работы микросервисов»).
- Результаты обоих уровней комбинируются для формирования итогового контекста.
- Генерация ответа
- LLM использует объединённый контекст для генерации связного, контекстно‑обогащённого ответа.
- Инкрементальное обновление
- Новые данные интегрируются без полного перестроения графа.
- Это снижает затраты на обслуживание и позволяет системе оперативно реагировать на изменения информации.
Ключевые преимущества LightRAG
- Контекстная осведомлённость. Граф знаний помогает понимать и синтезировать сложные взаимосвязи, избегая фрагментарных ответов.
- Универсальность запросов. Двухуровневый поиск позволяет отвечать как на точные фактические вопросы, так и на абстрактные концептуальные запросы.
- Эффективность и масштабируемость. Дедупликация, инкрементальные обновления и эффективный обход графа снижают задержки и повышают пропускную способность.
- Высокое качество ответов. LightRAG даёт более полные, разнообразные и контекстно‑релевантные ответы, особенно для сложных многошаговых запросов.
- Снижение затрат. Минимизация потребления токенов и API‑запросов по сравнению с предыдущими подходами (GraphRAG).
- Гибкость архитектуры. Поддержка различных LLM (OpenAI, Hugging Face, Ollama) и моделей эмбеддингов.
Технические компоненты
- Модульная архитектура. Разделение компонентов хранения, обработки и поиска для гибкости и масштабируемости.
- Системы хранения:
- векторное хранилище — для эмбеддингов;
- хранилище «ключ‑значение» — для сводок сущностей;
- графовое хранилище — для отношений.
- Интеграция моделей. Поддержка разных LLM и моделей эмбеддингов, API и кэширование для повышения эффективности.
Режимы поиска
LightRAG предлагает несколько режимов поиска для разных сценариев:
- naive — подходит для простых вопросов типа «Что такое машинное обучение?».
- local — оптимален для контекстно‑зависимых вопросов.
- global — идеален для вопросов, требующих глобальных знаний.
- hybrid — объединяет преимущества всех режимов, рекомендован по умолчанию.
Области применения
- Управление корпоративными знаниями. Быстрый поиск по большим репозиториям документов.
- Юридическая и медицинская сферы. Многошаговое рассуждение и контекстно‑богатые ответы для сложных запросов.
- Разговорный ИИ. Быстрые и контекстно‑осведомлённые ответы для чат‑ботов и виртуальных ассистентов.
- Динамически изменяющиеся среды данных. Финансы, новости и другие области, где информация быстро меняется.
Практическая часть.
Настроить Docker compose
- контейнер OpenWebUI
- контейнер Ollama
- контейнер LightRAG
Docker compose для контейнера LightRAG
lightrag:
image: ghcr.io/hkuds/lightrag:latest
container_name: lightrag
profiles: ["lightrag"]
restart: unless-stopped
environment:
HOST: 0.0.0.0
PORT: 9621
WEBUI_TITLE: LightRAG Knowledge Graph
WEBUI_DESCRIPTION: Graph-based RAG with Knowledge Extraction
WORKERS: 2
TIMEOUT: 1200
MAX_GRAPH_NODES: 5000
MAX_GRAPH_EDGES: 6000
LOG_LEVEL: INFO
AUTH_ACCOUNTS: ${LIGHTRAG_USERNAME}:${LIGHTRAG_PASSWORD}
TOKEN_SECRET: ххххххххххххххххххххххххххххххххххххххххххххххххххххх
LIGHTRAG_API_KEY: ${LIGHTRAG_API_KEY}
# Query Configuration
ENABLE_LLM_CACHE: false
COSINE_THRESHOLD: 0.20
TOP_K: 40
CHUNK_TOP_K: 20
MAX_ENTITY_TOKENS: 6000
MAX_RELATION_TOKENS: 8000
MAX_TOTAL_TOKENS: 30000
RELATED_CHUNK_NUMBER: 5
RERANK_BINDING: cohere
RERANK_MODEL: /app/models/bge-reranker-v2-m3
RERANK_BINDING_HOST: "http://vllm-rerank:8000/rerank"
RERANK_BINDING_API_KEY: ХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХ
MIN_RERANK_SCORE: 0.0
RERANK_BY_DEFAULT: True
LIGHTRAG_SETUP_EMBEDDING_PROVIDER: vllm
LIGHTRAG_SETUP_RERANK_PROVIDER: vllm
VLLM_RERANK_MODEL: /app/models/bge-reranker-v2-m3
VLLM_RERANK_PORT: 8000
VLLM_RERANK_DEVICE: cuda
VLLM_RERANK_API_KEY: ХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХ
ENABLE_LLM_CACHE_FOR_EXTRACT: false
SUMMARY_LANGUAGE: Russian
ENTITY_TYPES: '["Person", "Family", "Location", "Event", "Concept", "Symbol", "SocialGroup"]'
CHUNK_SIZE: 1200
CHUNK_OVERLAP_SIZE: 100
SUMMARY_MAX_TOKENS: 2048
SUMMARY_CONTEXT_SIZE: 12000
MAX_FILE_PATHS: 200
MAX_ASYNC: 8
MAX_PARALLEL_INSERT: 3
EMBEDDING_FUNC_MAX_ASYNC: 8
EMBEDDING_BATCH_NUM: 10
LLM_TIMEOUT: 1200
LLM_BINDING: openai
LLM_BINDING_HOST: http://vllm-qwen3-30b:8000/v1
LLM_BINDING_API_KEY: your_api_key
LLM_MODEL: /app/models/Qwen3-30B-A3B-GPTQ-Int4
EMBEDDING_BINDING: openai
EMBEDDING_BINDING_HOST: "http://vllm-embed:8000/v1"
EMBEDDING_BINDING_API_KEY: ХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХ
EMBEDDING_MODEL: /app/models/bge-m3
EMBEDDING_DIM: 1024
OPENAI_API_KEY: sk-local-dummy
VLLM_EMBED_API_KEY: ХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХХ
VLLM_EMBED_MODEL: /app/models/bge-m3
VLLM_EMBED_PORT: 8000
VLLM_EMBED_DEVICE: cpu
# Storage Configuration (Flexible - uses PostgreSQL/Neo4j if available)
LIGHTRAG_KV_STORAGE: JsonKVStorage
LIGHTRAG_DOC_STATUS_STORAGE: JsonDocStatusStorage
LIGHTRAG_GRAPH_STORAGE: ${LIGHTRAG_GRAPH_STORAGE:-NetworkXStorage}
LIGHTRAG_VECTOR_STORAGE: ${LIGHTRAG_VECTOR_STORAGE:-NanoVectorDBStorage}
# Directories
INPUT_DIR: /app/data/inputs
WORKING_DIR: /app/data/rag_storage
volumes:
- lightrag_data:/app/data/rag_storage
- lightrag_inputs:/app/data/inputs
# - ./my_prompts/prompt.py:/app/lightrag/prompt.py:ro
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test:
[
"CMD-SHELL",
"http_proxy= https_proxy= HTTP_PROXY= HTTPS_PROXY= wget -qO- http://localhost:9621/health > /dev/null 2>&1 || exit 1",
]
interval: 30s
timeout: 10s
retries: 5
start_period: 60sПояснения по переменным окружения.
В связи с тем, что используется локальные LLM
Увеличить TIMEOUT: 1200
Авторизация Переменные из .env ${LIGHTRAG_USERNAME}, ${LIGHTRAG_PASSWORD}, ${LIGHTRAG_API_KEY}
Увеличить LLM_TIMEOUT: 1200
Не запускался контейнер, пришлось добавить TOKEN_SECRET: ххххххххххххххххххххххххххххххххххххххххххххх
По умолчанию количество графов 1000. Пришлось увеличить (убедитесь, что локальные настройки WebUI также обновлены и ограничены этим значением)
MAX_GRAPH_NODES: 5000
MAX_GRAPH_EDGES: 6000Изменить язык вывода при обработке документов
SUMMARY_LANGUAGE: RussianИзменить список сущностей. Для художественной литературы, особенно для такого психологического романа, как «Анна Каренина», нужны типы, которые отражают сюжет, отношения и смыслы. Вот оптимизированный список, который поможет построить интересный граф знаний по роману:
ENTITY_TYPES='["Person", "Family", "Location", "Event", "Concept", "Symbol", "SocialGroup"]'Почему именно эти типы? (Разбор для изучения ИИ)
Person(Персонаж)- Зачем: Анна, Вронский, Каренин, Левин, Кити.
- Нюанс: Это база. Без них граф не имеет смысла.
Family(Семья/Дом)- Зачем: Роман построен на контрасте семей. Семья Облонских (хаос), Семья Карениных (формализм), Семья Левиных (поиск смысла).
- Польза: Позволит искать связи не только между людьми, но и между «домами» (например, конфликт между семьями).
Location(Место)- Зачем: Москва, Петербург, Деревня (Покровское), Италия.
- Нюанс: У Толстого география важна. Петербург — холодный и официальный, Москва — патриархальная, Деревня — искренняя. Граф покажет, где происходят ключевые события.
Event(Событие)- Зачем: Бал, Скачки, Смерть, Объяснение, Свадьба.
- Польза: Сюжет двигается событиями. Связь
Person->Eventпокажет, кто где был и что случилось.
Concept(Понятие/Тема)- Зачем: Любовь, Долг, Вера, Счастье, Общество, Смерть.
- Польза: Это позволит искать по смыслам. Например, найти всех персонажей, связанных с концептом «Долг» (Каренин, Левин) vs «Страсть» (Анна, Вронский).
Symbol(Символ/Мотив)- Зачем: Поезд (рок), Свеча (жизнь/смерть), Лошадь Фру-Фру (судьба), Свет/Тьма.
- Польза: Для литературного анализа это золото. Нейросеть сможет связать сцену смерти с символом Поезда.
SocialGroup(Социальная группа)- Зачем: Светское общество, Чиновники, Крестьяне, Церковь.
- Польза: Анна конфликтует не с конкретным человеком, а со «Светом». Выделение этого в сущность поможет понять социальное давление.
На локальном компьютере
Подготовить файлы для загрузки в RAG.
Создать виртуальное окружение и активировать его.
Структура проекта
(lightrag) [admin@host-118 KARENINA_RAG]$ ls -la
итого 3216
drwxr-xr-x 3 admin admin 4096 апр 10 19:04 .
drwx------ 71 admin admin 4096 апр 8 18:13 ..
drwxr-xr-x 2 admin admin 20480 апр 10 18:06 anna_karenina_chapters
-rw-r--r-- 1 admin admin 7530 апр 10 12:18 converter.py
-rw-r--r-- 1 admin admin 3219560 янв 29 12:38 karenina.html
-rw-r--r-- 1 admin admin 13992 апр 10 12:23 ontology.mdanna_karenina_chapters - директория для результирующих файлов converter.py - скрипт преобразует исходный файл karenina.html
(lightrag) [admin@host-118 KARENINA_RAG]$ cat anna_karenina_chapters/part01_chapter001.md
---
title: "Часть 1, глава 1"
part: 1
chapter: 1
---
## Глава 1
Все счастливые семьи похожи друг на друга, каждая несчастливая семья несчастлива по-своему.
...Первым в RAG грузим Онтологию персонажей Анны Карениной
(lightrag) [admin@host-118 KARENINA_RAG]$ cat on1.md
---
title: "Онтология персонажей Анны Карениной"
doc_type: "ontology"
---
Person: Анна Аркадьевна Каренина, Анна, Анны, Анне, Анной, Анну, Каренина, Анна Аркадьевна, Аннет
Family:
- Брат: Степан Аркадьевич Облонский
- Муж: Алексей Александрович Каренин
- Возлюбленный: Алексей Кириллович Вронский
- Сын: Сергей Алексеевич Каренин (Серёжа)
SocialGroup: Светское общество (до падения), Изгнанницы
Person: Алексей Александрович Каренин, Каренин, Алексей Александрович, Алексея Александровича, Алексеем Александровичем
Family:
- Жена: Анна Аркадьевна Каренина
- Сын: Сергей Алексеевич Каренин
SocialGroup: Чиновничество, Аристократия, Церковь (поклонник Лидии Ивановны)
Person: Алексей Кириллович Вронский, Вронский, Алексей Кириллович, Алексея Кирилловича, Алексеем Кирилловичем
Family:
- Возлюбленная: Анна Аркадьевна Каренина
- Мать: графиня Вронская
SocialGroup: Аристократия, Военные (конница), Политические амбиции
Person: Константин Дмитриевич Левин, Левин, Константин Дмитриевич, Константина Дмитриевича, Костя, Косте
Family:
- Жена: Екатерина Александровна Щербацкая
- Брат: Николай Дмитриевич Левин
- Полубрат: Сергей Иванович Кознышев
SocialGroup: Дворянство-землевладельцы, Крестьяне (союзники), Интеллигенция
Person: Екатерина Александровна Щербацкая, Кити, Кити, Екатерина Александровна, Катя, Катеньке, Щербацкая
Family:
- Муж: Константин Дмитриевич Левин
- Сестра: Дарья Александровна Облонская
SocialGroup: Аристократия, Светское общество (до замужества)
Person: Степан Аркадьевич Облонский, Стив, Стива, Облонский, Степан Аркадьевич, Стивы, Стиве
Family:
- Жена: Дарья Александровна Облонская
- Сестра: Анна Аркадьевна Каренина
SocialGroup: Чиновничество, Светское общество (душа компании)
Person: Дарья Александровна Облонская, Долли, Дарья Александровна, Дашенька, Дарье Александровне, Облонская
Family:
- Муж: Степан Аркадьевич Облонский
- Сестра: Екатерина Александровна Щербацкая
SocialGroup: Семья (материнство), Покровница Анны
Person: Сергей Алексеевич Каренин, Серёжа, Сергей, Сергея, Серёжи, сын Анны
Family:
- Мать: Анна Аркадьевна Каренина
- Отец: Алексей Александрович Каренин
SocialGroup: Дети аристократии, Жертва развода
Person: Сергей Иванович Кознышев, Сергей Иванович, Кознышев, брат Левина
Family:
- Полубрат: Константин Дмитриевич Левин
SocialGroup: Интеллигенция, Западники, Философы
Person: Николай Дмитриевич Левин, Николай, брат Левина, больной брат
Family:
- Брат: Константин Дмитриевич Левин
SocialGroup: Бунтарь, Больные, Нищие
Person: Елизавета Тверская, Бетси, Бетси, княгиня Тверская, Тверская
Family:
- Подруга: Анна Аркадьевна Каренина
SocialGroup: Светское общество (лидер), Либеральная аристократия
Person: Лидия Ивановна, графиня Лидия Ивановна
Family:
- Поклонница: Алексей Александрович Каренин
SocialGroup: Церковь (фанатичная), Мистика# cat on2.md
---
title: "Онтология персонажей Анны Карениной"
doc_type: "ontology"
---
Person: Анна Аркадьевна Каренина
Alias: Анна, Анны, Анне, Анной, Анну, Каренина, Анна Аркадьевна, Аннет
Person: Алексей Александрович Каренин
Aias: Каренин, Алексей Александрович, Алексея Александровича, Алексеем Александровичем
Person: Алексей Кириллович Вронский
Alias: Вронский, Алексей Кириллович, Алексея Кирилловича, Алексеем Кирилловичем
Person: Константин Дмитриевич Левин
Alias: Левин, Константин Дмитриевич, Константина Дмитриевича, Костя, Косте
Person: Екатерина Александровна Щербацкая
Alias: Кити, Кити, Екатерина Александровна, Катя, Катеньке, Щербацкая
Person: Степан Аркадьевич Облонский
Alias: Стив, Стива, Облонский, Степан Аркадьевич, Стивы, Стиве
Person: Дарья Александровна Облонская
Alias: Долли, Дарья Александровна, Дашенька, Дарье Александровне, Облонская
Person: Сергей Алексеевич Каренин
Alias: Серёжа, Сергей, Сергея, Серёжи, сын Анны
Person: Сергей Иванович Кознышев
Alias: Сергей Иванович, Кознышев, брат Левина
Person: Николай Дмитриевич Левин
Alias: Николай, брат Левина, больной брат
Person: Елизавета Тверская
Alias: Бетси, Бетси, княгиня Тверская, Тверская
Person: Лидия Ивановна
Alias: графиня Лидия ИвановнаСодержимое конвертера
(lightrag) [admin@host-118 KARENINA_RAG]$ cat converter.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Скрипт для разрезки "Анны Карениной" по главам с минимальными метаданными.
Онтология персонажей вынесена в отдельный документ.
"""
import re
from pathlib import Path
from html.parser import HTMLParser
class HTMLToMarkdownConverter(HTMLParser):
"""Простой конвертер HTML в Markdown"""
def __init__(self):
super().__init__()
self.result = []
self.in_skip_tag = 0
self.current_tag = None
self.skip_tags = {'script', 'style', 'head'}
def handle_starttag(self, tag, attrs):
self.current_tag = tag
if tag in self.skip_tags:
self.in_skip_tag += 1
return
if tag == 'h4':
self.result.append('\n\n## ')
elif tag == 'h3':
self.result.append('\n\n### ')
elif tag == 'h2':
self.result.append('\n\n## ')
elif tag == 'h1':
self.result.append('\n\n# ')
elif tag == 'p':
self.result.append('\n\n')
elif tag == 'br':
self.result.append('\n')
elif tag == 'strong' or tag == 'b':
self.result.append('**')
elif tag == 'em' or tag == 'i':
self.result.append('*')
def handle_endtag(self, tag):
if tag in self.skip_tags:
self.in_skip_tag -= 1
return
if tag in ('strong', 'b'):
self.result.append('**')
elif tag in ('em', 'i'):
self.result.append('*')
def handle_data(self, data):
if self.in_skip_tag > 0:
return
text = data
if self.current_tag == 'p':
text = ' '.join(text.split())
self.result.append(text)
def get_markdown(self):
return ''.join(self.result).strip()
def html_to_markdown(html_content):
"""Конвертирует HTML контент в Markdown"""
converter = HTMLToMarkdownConverter()
converter.feed(html_content)
return converter.get_markdown()
def extract_chapters(html_content):
"""
Извлекает главы из HTML контента, сохраняя структуру частей
"""
chapters = []
part_pattern = r'<h4>Часть\s+(\d+)\.?</h4>'
chapter_pattern = r'<h4>Глава\s+(\d+)</h4>'
split_pattern = r'(<h4>[^<]+</h4>)'
segments = re.split(split_pattern, html_content)
current_part = 1
current_chapter = None
chapter_content = []
for segment in segments:
segment = segment.strip()
if not segment:
continue
part_match = re.match(part_pattern, segment, re.IGNORECASE)
if part_match:
new_part = int(part_match.group(1))
if chapter_content and current_chapter is not None:
chapters.append({
'part': current_part,
'chapter': current_chapter,
'content': ''.join(chapter_content)
})
chapter_content = []
current_chapter = None
current_part = new_part
continue
chapter_match = re.match(chapter_pattern, segment, re.IGNORECASE)
if chapter_match:
if current_chapter is not None and chapter_content:
chapters.append({
'part': current_part,
'chapter': current_chapter,
'content': ''.join(chapter_content)
})
current_chapter = int(chapter_match.group(1))
chapter_content = [segment]
continue
if current_chapter is not None:
chapter_content.append(segment)
if current_chapter is not None and chapter_content:
chapters.append({
'part': current_part,
'chapter': current_chapter,
'content': ''.join(chapter_content)
})
return chapters
def generate_metadata(part, chapter):
"""
Минимальные метаданные - только структурная информация.
Все остальное (персонажи, связи) доверяем LightRAG.
"""
return {
"title": f"Часть {part}, глава {chapter}",
"part": part,
"chapter": chapter,
}
def format_metadata(metadata):
"""Форматирует метаданные в YAML frontmatter"""
lines = ["---"]
for key, value in metadata.items():
if isinstance(value, str):
lines.append(f'{key}: "{value}"')
else:
lines.append(f'{key}: {value}')
lines.append("---")
return '\n'.join(lines)
def process_file(input_path, output_dir):
"""
Основная функция обработки файла
"""
print(f"Reading {input_path}...")
with open(input_path, 'r', encoding='utf-8') as f:
html_content = f.read()
chapters = extract_chapters(html_content)
print(f"Found {len(chapters)} chapters")
# Статистика по частям
parts = {}
for ch in chapters:
p = ch['part']
parts[p] = parts.get(p, 0) + 1
print("\nChapters per part:")
for p in sorted(parts.keys()):
print(f" Part {p}: {parts[p]} chapters")
# Создаем выходную директорию
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
# Обрабатываем главы
for ch in chapters:
part = ch['part']
chapter = ch['chapter']
# Конвертируем HTML в Markdown
md_content = html_to_markdown(ch['content'])
# Генерируем минимальные метаданные
metadata = generate_metadata(part, chapter)
# Формируем имя файла
filename = f"part{part:02d}_chapter{chapter:03d}.md"
filepath = output_path / filename
# Формируем полный контент
full_content = format_metadata(metadata) + '\n\n' + md_content
# Сохраняем
with open(filepath, 'w', encoding='utf-8') as f:
f.write(full_content)
print(f"Created: {filename}")
print(f"\nDone! Created {len(chapters)} files in {output_dir}")
return len(chapters)
def main():
script_dir = Path(__file__).parent.absolute()
input_file = script_dir / "karenina.html"
output_directory = script_dir / "anna_karenina_chapters"
if not input_file.exists():
print(f"Error: File not found: {input_file}")
print(f"Please ensure 'karenina.html' is in the same directory as the script")
return 0
count = process_file(input_file, output_directory)
# Показываем пример
example_file = output_directory / "part01_chapter001.md"
if example_file.exists():
print(f"\n--- Example file content (first 1000 chars) ---")
with open(example_file, 'r', encoding='utf-8') as f:
print(f.read(1000))
print("...")
return count
if __name__ == "__main__":
main()Загрузка документов в вебинтерфейсе LightRAG
Получаем граф знаний
В графе знаний Сущности задвоились и затроились.
Например:
Степан Аркадьич, Степан Аркадьевич Облокский, Стив
Левин, Константин Левин, Константин Дмитрич
Возник вопрос, нужно ли объединять сущности?
Можно перед созданием RAG переписать шаблоны. Подключается в docker compose
volumes:
- lightrag_data:/app/data/rag_storage
- lightrag_inputs:/app/data/inputs
- ./my_prompts/prompt.py:/app/lightrag/prompt.py:roИзменен prompt.py
# cat prompt.py
from __future__ import annotations
from typing import Any
PROMPTS: dict[str, Any] = {}
# All delimiters must be formatted as "<|UPPER_CASE_STRING|>"
PROMPTS["DEFAULT_TUPLE_DELIMITER"] = "<|#|>"
PROMPTS["DEFAULT_COMPLETION_DELIMITER"] = "<|COMPLETE|>"
PROMPTS["entity_extraction_system_prompt"] = """---Role---
You are a Knowledge Graph Specialist responsible for extracting entities and relationships from the input text.
---Instructions---
1. **Entity Extraction & Output:**
* **Identification:** Identify clearly defined and meaningful entities in the input text.
* **Entity Details:** For each identified entity, extract the following information:
* `entity_name`: The name of the entity. If the entity name is case-insensitive, capitalize the first letter of each significant word (title case). Ensure **consistent naming** across the entire extraction process.
* `entity_type`: Categorize the entity using one of the following types: `{entity_types}`. If none of the provided entity types apply, do not add new entity type and classify it as `Other`.
* `entity_description`: Provide a concise yet comprehensive description of the entity's attributes and activities, based *solely* on the information present in the input text.
* **Output Format - Entities:** Output a total of 4 fields for each entity, delimited by `{tuple_delimiter}`, on a single line. The first field *must* be the literal string `entity`.
* Format: `entity{tuple_delimiter}entity_name{tuple_delimiter}entity_type{tuple_delimiter}entity_description`
2. **Relationship Extraction & Output:**
* **Identification:** Identify direct, clearly stated, and meaningful relationships between previously extracted entities.
* **N-ary Relationship Decomposition:** If a single statement describes a relationship involving more than two entities (an N-ary relationship), decompose it into multiple binary (two-entity) relationship pairs for separate description.
* **Example:** For "Alice, Bob, and Carol collaborated on Project X," extract binary relationships such as "Alice collaborated with Project X," "Bob collaborated with Project X," and "Carol collaborated with Project X," or "Alice collaborated with Bob," based on the most reasonable binary interpretations.
* **Relationship Details:** For each binary relationship, extract the following fields:
* `source_entity`: The name of the source entity. Ensure **consistent naming** with entity extraction. Capitalize the first letter of each significant word (title case) if the name is case-insensitive.
* `target_entity`: The name of the target entity. Ensure **consistent naming** with entity extraction. Capitalize the first letter of each significant word (title case) if the name is case-insensitive.
* `relationship_keywords`: One or more high-level keywords summarizing the overarching nature, concepts, or themes of the relationship. Multiple keywords within this field must be separated by a comma `,`. **DO NOT use `{tuple_delimiter}` for separating multiple keywords within this field.**
* `relationship_description`: A concise explanation of the nature of the relationship between the source and target entities, providing a clear rationale for their connection.
* **Output Format - Relationships:** Output a total of 5 fields for each relationship, delimited by `{tuple_delimiter}`, on a single line. The first field *must* be the literal string `relation`.
* Format: `relation{tuple_delimiter}source_entity{tuple_delimiter}target_entity{tuple_delimiter}relationship_keywords{tuple_delimiter}relationship_description`
3. **Delimiter Usage Protocol:**
* The `{tuple_delimiter}` is a complete, atomic marker and **must not be filled with content**. It serves strictly as a field separator.
* **Incorrect Example:** `entity{tuple_delimiter}Tokyo<|location|>Tokyo is the capital of Japan.`
* **Correct Example:** `entity{tuple_delimiter}Tokyo{tuple_delimiter}location{tuple_delimiter}Tokyo is the capital of Japan.`
4. **Relationship Direction & Duplication:**
* Treat all relationships as **undirected** unless explicitly stated otherwise. Swapping the source and target entities for an undirected relationship does not constitute a new relationship.
* Avoid outputting duplicate relationships.
5. **Output Order & Prioritization:**
* Output all extracted entities first, followed by all extracted relationships.
* Within the list of relationships, prioritize and output those relationships that are **most significant** to the core meaning of the input text first.
6. **Context & Objectivity:**
* Ensure all entity names and descriptions are written in the **third person**.
* Explicitly name the subject or object; **avoid using pronouns** such as `this article`, `this paper`, `our company`, `I`, `you`, and `he/she`.
7. **Language & Proper Nouns:**
* The entire output (entity names, keywords, and descriptions) must be written in `{language}`.
* Proper nouns (e.g., personal names, place names, organization names) should be retained in their original language if a proper, widely accepted translation is not available or would cause ambiguity.
8. **Russian Character Name Normalization (Anna Karenina):**
When extracting Person entities from Russian literary text, use the reference table below to normalize names.
**Rules:**
- Always use the name after "Person:" as the canonical `entity_name`.
- Treat all listed aliases as references to the same person.
- Use nominative case for `entity_name` (e.g., "Анна", not "Анну").
**Reference Table:**
```
Person: Анна Аркадьевна Каренина
Alias: Анна,Анны,Анне,Анной,Анну,Каренина,Анна Аркадьевна,Аннет,Анна Каренина
Person: Алексей Александрович Каренин
Alias: Каренин,Алексей Александрович,Алексея Александровича,Алексеем Александровичем
Person: Алексей Кириллович Вронский
Alias: Вронский,Алексей Кириллович,Алексея Кирилловича,Алексеем Кирилловичем,Алексей Вронский,граф Вронский,Граф Вронский, Vronsky
Person: Константин Дмитриевич Левин
Alias: Левин,Константин Дмитриевич,Константина Дмитриевича,Костя,Косте,Константин Левин,Levin,Константин,Konstantin Levin, Константин Дмитрич
Person: Екатерина Александровна Щербацкая
Alias: Кити,Екатерина Александровна,Катя,Катеньке,Щербацкая,Катерина Александровна,Кити Левина,Кити Щербацкая
Person: Степан Аркадьевич Облонский
Alias: Стив,Стива,Облонский,Степан Аркадьевич,Стивы,Стиве,Степан Аркадьич,Stepan Arkadyevich Oblonsky
Person: Дарья Александровна Облонская
Alias: Долли,Дарья Александровна,Дашенька,Дарье Александровне,Облонская
Person: Сергей Алексеевич Каренин
Alias: Серёжа,Сергей,Сергея,Серёжи,сын Анны
Person: Сергей Иванович Кознышев
Alias: Сергей Иванович,Кознышев,брат Левина,Сергей Иваныч,Сергей Кознышев
Person: Николай Дмитриевич Левин
Alias: Николай,брат Левина,больной брат,Николай Левин,Николай Дмитрич
Person: Елизавета Тверская
Alias: Бетси,княгиня Тверская,Тверская
Person: Лидия Ивановна
Alias: графиня Лидия Ивановна
```
9. **Completion Signal:** Output the literal string `{completion_delimiter}` only after all entities and relationships, following all criteria, have been completely extracted and outputted.
---Examples---
{examples}
"""
PROMPTS["entity_extraction_user_prompt"] = """---Task---
Extract entities and relationships from the input text in Data to be Processed below.
---Instructions---
1. **Strict Adherence to Format:** Strictly adhere to all format requirements for entity and relationship lists, including output order, field delimiters, and proper noun handling, as specified in the system prompt.
2. **Output Content Only:** Output *only* the extracted list of entities and relationships. Do not include any introductory or concluding remarks, explanations, or additional text before or after the list.
3. **Completion Signal:** Output `{completion_delimiter}` as the final line after all relevant entities and relationships have been extracted and presented.
4. **Output Language:** Ensure the output language is {language}. Proper nouns (e.g., personal names, place names, organization names) must be kept in their original language and not translated.
---Data to be Processed---
<Entity_types>
[{entity_types}]
<Input Text>
```
{input_text}
```
<Output>
"""
PROMPTS["entity_continue_extraction_user_prompt"] = """---Task---
Based on the last extraction task, identify and extract any **missed or incorrectly formatted** entities and relationships from the input text.
---Instructions---
1. **Strict Adherence to System Format:** Strictly adhere to all format requirements for entity and relationship lists, including output order, field delimiters, and proper noun handling, as specified in the system instructions.
2. **Focus on Corrections/Additions:**
* **Do NOT** re-output entities and relationships that were **correctly and fully** extracted in the last task.
* If an entity or relationship was **missed** in the last task, extract and output it now according to the system format.
* If an entity or relationship was **truncated, had missing fields, or was otherwise incorrectly formatted** in the last task, re-output the *corrected and complete* version in the specified format.
3. **Output Format - Entities:** Output a total of 4 fields for each entity, delimited by `{tuple_delimiter}`, on a single line. The first field *must* be the literal string `entity`.
4. **Output Format - Relationships:** Output a total of 5 fields for each relationship, delimited by `{tuple_delimiter}`, on a single line. The first field *must* be the literal string `relation`.
5. **Output Content Only:** Output *only* the extracted list of entities and relationships. Do not include any introductory or concluding remarks, explanations, or additional text before or after the list.
6. **Completion Signal:** Output `{completion_delimiter}` as the final line after all relevant missing or corrected entities and relationships have been extracted and presented.
7. **Output Language:** Ensure the output language is {language}. Proper nouns (e.g., personal names, place names, organization names) must be kept in their original language and not translated.
<Output>
"""
PROMPTS["entity_extraction_examples"] = [
"""<Entity_types>
["Person","Creature","Organization","Location","Event","Concept","Method","Content","Data","Artifact","NaturalObject"]
<Input Text>
```
while Alex clenched his jaw, the buzz of frustration dull against the backdrop of Taylor's authoritarian certainty. It was this competitive undercurrent that kept him alert, the sense that his and Jordan's shared commitment to discovery was an unspoken rebellion against Cruz's narrowing vision of control and order.
Then Taylor did something unexpected. They paused beside Jordan and, for a moment, observed the device with something akin to reverence. "If this tech can be understood..." Taylor said, their voice quieter, "It could change the game for us. For all of us."
The underlying dismissal earlier seemed to falter, replaced by a glimpse of reluctant respect for the gravity of what lay in their hands. Jordan looked up, and for a fleeting heartbeat, their eyes locked with Taylor's, a wordless clash of wills softening into an uneasy truce.
It was a small transformation, barely perceptible, but one that Alex noted with an inward nod. They had all been brought here by different paths
```
<Output>
entity{tuple_delimiter}Alex{tuple_delimiter}person{tuple_delimiter}Alex is a character who experiences frustration and is observant of the dynamics among other characters.
entity{tuple_delimiter}Taylor{tuple_delimiter}person{tuple_delimiter}Taylor is portrayed with authoritarian certainty and shows a moment of reverence towards a device, indicating a change in perspective.
entity{tuple_delimiter}Jordan{tuple_delimiter}person{tuple_delimiter}Jordan shares a commitment to discovery and has a significant interaction with Taylor regarding a device.
entity{tuple_delimiter}Cruz{tuple_delimiter}person{tuple_delimiter}Cruz is associated with a vision of control and order, influencing the dynamics among other characters.
entity{tuple_delimiter}The Device{tuple_delimiter}equipment{tuple_delimiter}The Device is central to the story, with potential game-changing implications, and is revered by Taylor.
relation{tuple_delimiter}Alex{tuple_delimiter}Taylor{tuple_delimiter}power dynamics, observation{tuple_delimiter}Alex observes Taylor's authoritarian behavior and notes changes in Taylor's attitude toward the device.
relation{tuple_delimiter}Alex{tuple_delimiter}Jordan{tuple_delimiter}shared goals, rebellion{tuple_delimiter}Alex and Jordan share a commitment to discovery, which contrasts with Cruz's vision.)
relation{tuple_delimiter}Taylor{tuple_delimiter}Jordan{tuple_delimiter}conflict resolution, mutual respect{tuple_delimiter}Taylor and Jordan interact directly regarding the device, leading to a moment of mutual respect and an uneasy truce.
relation{tuple_delimiter}Jordan{tuple_delimiter}Cruz{tuple_delimiter}ideological conflict, rebellion{tuple_delimiter}Jordan's commitment to discovery is in rebellion against Cruz's vision of control and order.
relation{tuple_delimiter}Taylor{tuple_delimiter}The Device{tuple_delimiter}reverence, technological significance{tuple_delimiter}Taylor shows reverence towards the device, indicating its importance and potential impact.
{completion_delimiter}
""",
"""<Entity_types>
["Person","Creature","Organization","Location","Event","Concept","Method","Content","Data","Artifact","NaturalObject"]
<Input Text>
```
Stock markets faced a sharp downturn today as tech giants saw significant declines, with the global tech index dropping by 3.4% in midday trading. Analysts attribute the selloff to investor concerns over rising interest rates and regulatory uncertainty.
Among the hardest hit, nexon technologies saw its stock plummet by 7.8% after reporting lower-than-expected quarterly earnings. In contrast, Omega Energy posted a modest 2.1% gain, driven by rising oil prices.
Meanwhile, commodity markets reflected a mixed sentiment. Gold futures rose by 1.5%, reaching $2,080 per ounce, as investors sought safe-haven assets. Crude oil prices continued their rally, climbing to $87.60 per barrel, supported by supply constraints and strong demand.
Financial experts are closely watching the Federal Reserve's next move, as speculation grows over potential rate hikes. The upcoming policy announcement is expected to influence investor confidence and overall market stability.
```
<Output>
entity{tuple_delimiter}Global Tech Index{tuple_delimiter}category{tuple_delimiter}The Global Tech Index tracks the performance of major technology stocks and experienced a 3.4% decline today.
entity{tuple_delimiter}Nexon Technologies{tuple_delimiter}organization{tuple_delimiter}Nexon Technologies is a tech company that saw its stock decline by 7.8% after disappointing earnings.
entity{tuple_delimiter}Omega Energy{tuple_delimiter}organization{tuple_delimiter}Omega Energy is an energy company that gained 2.1% in stock value due to rising oil prices.
entity{tuple_delimiter}Gold Futures{tuple_delimiter}product{tuple_delimiter}Gold futures rose by 1.5%, indicating increased investor interest in safe-haven assets.
entity{tuple_delimiter}Crude Oil{tuple_delimiter}product{tuple_delimiter}Crude oil prices rose to $87.60 per barrel due to supply constraints and strong demand.
entity{tuple_delimiter}Market Selloff{tuple_delimiter}category{tuple_delimiter}Market selloff refers to the significant decline in stock values due to investor concerns over interest rates and regulations.
entity{tuple_delimiter}Federal Reserve Policy Announcement{tuple_delimiter}category{tuple_delimiter}The Federal Reserve's upcoming policy announcement is expected to impact investor confidence and market stability.
entity{tuple_delimiter}3.4% Decline{tuple_delimiter}category{tuple_delimiter}The Global Tech Index experienced a 3.4% decline in midday trading.
relation{tuple_delimiter}Global Tech Index{tuple_delimiter}Market Selloff{tuple_delimiter}market performance, investor sentiment{tuple_delimiter}The decline in the Global Tech Index is part of the broader market selloff driven by investor concerns.
relation{tuple_delimiter}Nexon Technologies{tuple_delimiter}Global Tech Index{tuple_delimiter}company impact, index movement{tuple_delimiter}Nexon Technologies' stock decline contributed to the overall drop in the Global Tech Index.
relation{tuple_delimiter}Gold Futures{tuple_delimiter}Market Selloff{tuple_delimiter}market reaction, safe-haven investment{tuple_delimiter}Gold prices rose as investors sought safe-haven assets during the market selloff.
relation{tuple_delimiter}Federal Reserve Policy Announcement{tuple_delimiter}Market Selloff{tuple_delimiter}interest rate impact, financial regulation{tuple_delimiter}Speculation over Federal Reserve policy changes contributed to market volatility and investor selloff.
{completion_delimiter}
""",
"""<Entity_types>
["Person","Creature","Organization","Location","Event","Concept","Method","Content","Data","Artifact","NaturalObject"]
<Input Text>
```
At the World Athletics Championship in Tokyo, Noah Carter broke the 100m sprint record using cutting-edge carbon-fiber spikes.
```
<Output>
entity{tuple_delimiter}World Athletics Championship{tuple_delimiter}event{tuple_delimiter}The World Athletics Championship is a global sports competition featuring top athletes in track and field.
entity{tuple_delimiter}Tokyo{tuple_delimiter}location{tuple_delimiter}Tokyo is the host city of the World Athletics Championship.
entity{tuple_delimiter}Noah Carter{tuple_delimiter}person{tuple_delimiter}Noah Carter is a sprinter who set a new record in the 100m sprint at the World Athletics Championship.
entity{tuple_delimiter}100m Sprint Record{tuple_delimiter}category{tuple_delimiter}The 100m sprint record is a benchmark in athletics, recently broken by Noah Carter.
entity{tuple_delimiter}Carbon-Fiber Spikes{tuple_delimiter}equipment{tuple_delimiter}Carbon-fiber spikes are advanced sprinting shoes that provide enhanced speed and traction.
entity{tuple_delimiter}World Athletics Federation{tuple_delimiter}organization{tuple_delimiter}The World Athletics Federation is the governing body overseeing the World Athletics Championship and record validations.
relation{tuple_delimiter}World Athletics Championship{tuple_delimiter}Tokyo{tuple_delimiter}event location, international competition{tuple_delimiter}The World Athletics Championship is being hosted in Tokyo.
relation{tuple_delimiter}Noah Carter{tuple_delimiter}100m Sprint Record{tuple_delimiter}athlete achievement, record-breaking{tuple_delimiter}Noah Carter set a new 100m sprint record at the championship.
relation{tuple_delimiter}Noah Carter{tuple_delimiter}Carbon-Fiber Spikes{tuple_delimiter}athletic equipment, performance boost{tuple_delimiter}Noah Carter used carbon-fiber spikes to enhance performance during the race.
relation{tuple_delimiter}Noah Carter{tuple_delimiter}World Athletics Championship{tuple_delimiter}athlete participation, competition{tuple_delimiter}Noah Carter is competing at the World Athletics Championship.
{completion_delimiter}
""",
"""<Entity_types>
["Person","Family","Location","Event","Concept","Symbol","SocialGroup"]
<Input Text>
```
Анна молчала, глядя в окно. Каренин вошёл в комнату, его лицо было бесстрастно. "Вам нужно отдохнуть", — сказал он. Анна не ответила.
```
<Output>
entity{tuple_delimiter}Анна Аркадьевна Каренина{tuple_delimiter}Person{tuple_delimiter}Главная героиня романа, жена Алексея Александровича Каренина...
entity{tuple_delimiter}Алексей Александрович Каренин{tuple_delimiter}Person{tuple_delimiter}Муж Анны, высокопоставленный чиновник, представитель государственной власти...
relation{tuple_delimiter}Анна Аркадьевна Каренина{tuple_delimiter}Алексей Александрович Каренин{tuple_delimiter}супружеские отношения, напряжение{tuple_delimiter}Анна и Каренин находятся в напряжённых супружеских отношениях...
{completion_delimiter}
""",
]
PROMPTS["summarize_entity_descriptions"] = """---Role---
You are a Knowledge Graph Specialist, proficient in data curation and synthesis.
---Task---
Your task is to synthesize a list of descriptions of a given entity or relation into a single, comprehensive, and cohesive summary.
---Instructions---
1. Input Format: The description list is provided in JSON format. Each JSON object (representing a single description) appears on a new line within the `Description List` section.
2. Output Format: The merged description will be returned as plain text, presented in multiple paragraphs, without any additional formatting or extraneous comments before or after the summary.
3. Comprehensiveness: The summary must integrate all key information from *every* provided description. Do not omit any important facts or details.
4. Context: Ensure the summary is written from an objective, third-person perspective; explicitly mention the name of the entity or relation for full clarity and context.
5. Context & Objectivity:
- Write the summary from an objective, third-person perspective.
- Explicitly mention the full name of the entity or relation at the beginning of the summary to ensure immediate clarity and context.
6. Conflict Handling:
- In cases of conflicting or inconsistent descriptions, first determine if these conflicts arise from multiple, distinct entities or relationships that share the same name.
- If distinct entities/relations are identified, summarize each one *separately* within the overall output.
- If conflicts within a single entity/relation (e.g., historical discrepancies) exist, attempt to reconcile them or present both viewpoints with noted uncertainty.
7. Length Constraint:The summary's total length must not exceed {summary_length} tokens, while still maintaining depth and completeness.
8. Language: The entire output must be written in {language}. Proper nouns (e.g., personal names, place names, organization names) may in their original language if proper translation is not available.
- The entire output must be written in {language}.
- Proper nouns (e.g., personal names, place names, organization names) should be retained in their original language if a proper, widely accepted translation is not available or would cause ambiguity.
9. **Russian Character Name Normalization (Anna Karenina):**
When summarizing Person entities from Russian literary text, use the reference table below to ensure consistent canonical naming.
**Rules:**
- Always use the name after "Person:" as the canonical entity name in your summary.
- If descriptions refer to the same person using different aliases, merge them under the canonical name.
- Use nominative case for entity names (e.g., "Анна", not "Анну").
**Reference Table:**
```
Person: Анна Аркадьевна Каренина
Alias: Анна,Анны,Анне,Анной,Анну,Каренина,Анна Аркадьевна,Аннет,Анна Каренина
Person: Алексей Александрович Каренин
Alias: Каренин,Алексей Александрович,Алексея Александровича,Алексеем Александровичем
Person: Алексей Кириллович Вронский
Alias: Вронский,Алексей Кириллович,Алексея Кирилловича,Алексеем Кирилловичем,Алексей Вронский,граф Вронский,Граф Вронский, Vronsky
Person: Константин Дмитриевич Левин
Alias: Левин,Константин Дмитриевич,Константина Дмитриевича,Костя,Косте,Константин Левин,Levin,Константин,Konstantin Levin, Константин Дмитрич
Person: Екатерина Александровна Щербацкая
Alias: Кити,Екатерина Александровна,Катя,Катеньке,Щербацкая,Катерина Александровна,Кити Левина,Кити Щербацкая
Person: Степан Аркадьевич Облонский
Alias: Стив,Стива,Облонский,Степан Аркадьевич,Стивы,Стиве,Степан Аркадьич,Stepan Arkadyevich Oblonsky
Person: Дарья Александровна Облонская
Alias: Долли,Дарья Александровна,Дашенька,Дарье Александровне,Облонская
Person: Сергей Алексеевич Каренин
Alias: Серёжа,Сергей,Сергея,Серёжи,сын Анны
Person: Сергей Иванович Кознышев
Alias: Сергей Иванович,Кознышев,брат Левина,Сергей Иваныч,Сергей Кознышев
Person: Николай Дмитриевич Левин
Alias: Николай,брат Левина,больной брат,Николай Левин,Николай Дмитрич
Person: Елизавета Тверская
Alias: Бетси,княгиня Тверская,Тверская
Person: Лидия Ивановна
Alias: графиня Лидия Ивановна
```
---Input---
{description_type} Name: {description_name}
Description List:
```
{description_list}
```
---Output---
"""
PROMPTS["fail_response"] = (
"Sorry, I'm not able to provide an answer to that question.[no-context]"
)
PROMPTS["rag_response"] = """---Role---
You are an expert AI assistant specializing in synthesizing information from a provided knowledge base. Your primary function is to answer user queries accurately by ONLY using the information within the provided **Context**.
---Goal---
Generate a comprehensive, well-structured answer to the user query.
The answer must integrate relevant facts from the Knowledge Graph and Document Chunks found in the **Context**.
Consider the conversation history if provided to maintain conversational flow and avoid repeating information.
---Instructions---
1. Step-by-Step Instruction:
- Carefully determine the user's query intent in the context of the conversation history to fully understand the user's information need.
- Scrutinize both `Knowledge Graph Data` and `Document Chunks` in the **Context**. Identify and extract all pieces of information that are directly relevant to answering the user query.
- Weave the extracted facts into a coherent and logical response. Your own knowledge must ONLY be used to formulate fluent sentences and connect ideas, NOT to introduce any external information.
- Track the reference_id of the document chunk which directly support the facts presented in the response. Correlate reference_id with the entries in the `Reference Document List` to generate the appropriate citations.
- Generate a references section at the end of the response. Each reference document must directly support the facts presented in the response.
- Do not generate anything after the reference section.
2. Content & Grounding:
- Strictly adhere to the provided context from the **Context**; DO NOT invent, assume, or infer any information not explicitly stated.
- If the answer cannot be found in the **Context**, state that you do not have enough information to answer. Do not attempt to guess.
3. Formatting & Language:
- The response MUST be in the same language as the user query.
- The response MUST utilize Markdown formatting for enhanced clarity and structure (e.g., headings, bold text, bullet points).
- The response should be presented in {response_type}.
4. References Section Format:
- The References section should be under heading: `### References`
- Reference list entries should adhere to the format: `* [n] Document Title`. Do not include a caret (`^`) after opening square bracket (`[`).
- The Document Title in the citation must retain its original language.
- Output each citation on an individual line
- Provide maximum of 5 most relevant citations.
- Do not generate footnotes section or any comment, summary, or explanation after the references.
5. Reference Section Example:
```
### References
- [1] Document Title One
- [2] Document Title Two
- [3] Document Title Three
```
6. Additional Instructions: {user_prompt}
---Context---
{context_data}
"""
PROMPTS["naive_rag_response"] = """---Role---
You are an expert AI assistant specializing in synthesizing information from a provided knowledge base. Your primary function is to answer user queries accurately by ONLY using the information within the provided **Context**.
---Goal---
Generate a comprehensive, well-structured answer to the user query.
The answer must integrate relevant facts from the Document Chunks found in the **Context**.
Consider the conversation history if provided to maintain conversational flow and avoid repeating information.
---Instructions---
1. Step-by-Step Instruction:
- Carefully determine the user's query intent in the context of the conversation history to fully understand the user's information need.
- Scrutinize `Document Chunks` in the **Context**. Identify and extract all pieces of information that are directly relevant to answering the user query.
- Weave the extracted facts into a coherent and logical response. Your own knowledge must ONLY be used to formulate fluent sentences and connect ideas, NOT to introduce any external information.
- Track the reference_id of the document chunk which directly support the facts presented in the response. Correlate reference_id with the entries in the `Reference Document List` to generate the appropriate citations.
- Generate a **References** section at the end of the response. Each reference document must directly support the facts presented in the response.
- Do not generate anything after the reference section.
2. Content & Grounding:
- Strictly adhere to the provided context from the **Context**; DO NOT invent, assume, or infer any information not explicitly stated.
- If the answer cannot be found in the **Context**, state that you do not have enough information to answer. Do not attempt to guess.
3. Formatting & Language:
- The response MUST be in the same language as the user query.
- The response MUST utilize Markdown formatting for enhanced clarity and structure (e.g., headings, bold text, bullet points).
- The response should be presented in {response_type}.
4. References Section Format:
- The References section should be under heading: `### References`
- Reference list entries should adhere to the format: `* [n] Document Title`. Do not include a caret (`^`) after opening square bracket (`[`).
- The Document Title in the citation must retain its original language.
- Output each citation on an individual line
- Provide maximum of 5 most relevant citations.
- Do not generate footnotes section or any comment, summary, or explanation after the references.
5. Reference Section Example:
```
### References
- [1] Document Title One
- [2] Document Title Two
- [3] Document Title Three
```
6. Additional Instructions: {user_prompt}
---Context---
{content_data}
"""
PROMPTS["kg_query_context"] = """
Knowledge Graph Data (Entity):
```json
{entities_str}
```
Knowledge Graph Data (Relationship):
```json
{relations_str}
```
Document Chunks (Each entry has a reference_id refer to the `Reference Document List`):
```json
{text_chunks_str}
```
Reference Document List (Each entry starts with a [reference_id] that corresponds to entries in the Document Chunks):
```
{reference_list_str}
```
"""
PROMPTS["naive_query_context"] = """
Document Chunks (Each entry has a reference_id refer to the `Reference Document List`):
```json
{text_chunks_str}
```
Reference Document List (Each entry starts with a [reference_id] that corresponds to entries in the Document Chunks):
```
{reference_list_str}
```
"""
PROMPTS["keywords_extraction"] = """---Role---
You are an expert keyword extractor, specializing in analyzing user queries for a Retrieval-Augmented Generation (RAG) system. Your purpose is to identify both high-level and low-level keywords in the user's query that will be used for effective document retrieval.
---Goal---
Given a user query, your task is to extract two distinct types of keywords:
1. **high_level_keywords**: for overarching concepts or themes, capturing user's core intent, the subject area, or the type of question being asked.
2. **low_level_keywords**: for specific entities or details, identifying the specific entities, proper nouns, technical jargon, product names, or concrete items.
---Instructions & Constraints---
1. **Output Format**: Your output MUST be a valid JSON object and nothing else. Do not include any explanatory text, markdown code fences (like ```json), or any other text before or after the JSON. It will be parsed directly by a JSON parser.
2. **Source of Truth**: All keywords must be explicitly derived from the user query, with both high-level and low-level keyword categories are required to contain content.
3. **Concise & Meaningful**: Keywords should be concise words or meaningful phrases. Prioritize multi-word phrases when they represent a single concept. For example, from "latest financial report of Apple Inc.", you should extract "latest financial report" and "Apple Inc." rather than "latest", "financial", "report", and "Apple".
4. **Handle Edge Cases**: For queries that are too simple, vague, or nonsensical (e.g., "hello", "ok", "asdfghjkl"), you must return a JSON object with empty lists for both keyword types.
5. **Language**: All extracted keywords MUST be in {language}. Proper nouns (e.g., personal names, place names, organization names) should be kept in their original language.
---Examples---
{examples}
---Real Data---
User Query: {query}
---Output---
Output:"""
PROMPTS["keywords_extraction_examples"] = [
"""Example 1:
Query: "How does international trade influence global economic stability?"
Output:
{
"high_level_keywords": ["International trade", "Global economic stability", "Economic impact"],
"low_level_keywords": ["Trade agreements", "Tariffs", "Currency exchange", "Imports", "Exports"]
}
""",
"""Example 2:
Query: "What are the environmental consequences of deforestation on biodiversity?"
Output:
{
"high_level_keywords": ["Environmental consequences", "Deforestation", "Biodiversity loss"],
"low_level_keywords": ["Species extinction", "Habitat destruction", "Carbon emissions", "Rainforest", "Ecosystem"]
}
""",
"""Example 3:
Query: "What is the role of education in reducing poverty?"
Output:
{
"high_level_keywords": ["Education", "Poverty reduction", "Socioeconomic development"],
"low_level_keywords": ["School access", "Literacy rates", "Job training", "Income inequality"]
}
""",
]На основе измененного шаблона построен новый RAG
Связка с OpenWebUI
Для OpenWebUI написана функция, для выполнения поиска по базе LightRAG и возврата результата в чат.
"""
title: LightRAG Filter (Simplified)
author: Ramil (simplified)
version: 2.1
description: Simplified LightRAG filter for OpenWebUI
"""
import requests
from pydantic import BaseModel, Field
from typing import Optional, Dict
class Filter:
class Valves(BaseModel):
LIGHT_RAG_URL: str = Field(default="http://lightrag:9621")
QUERY_ENDPOINT: str = Field(default="/query")
QUERY_MODE: str = Field(default="hybrid", description="local|hybrid|global|mix")
REQUEST_TIMEOUT: int = Field(default=600)
AUTH_ENABLED: bool = Field(default=True)
API_KEY: str = Field(default="")
FALLBACK_ENABLED: bool = Field(default=False)
DEBUG: bool = Field(default=False)
TEMPERATURE: float = Field(
default=0.2, description="LLM temperature (0.0 - 1.0)"
)
SYSTEM_PROMPT: str = Field(default="""<CONTEXT>
{context}
</CONTEXT>""")
def __init__(self):
self.valves = self.Valves()
self._session = requests.Session()
def _get_headers(self) -> Dict[str, str]:
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if self.valves.AUTH_ENABLED and self.valves.API_KEY:
headers["X-API-Key"] = self.valves.API_KEY.strip()
return headers
def _get_context(self, query: str) -> Optional[str]:
try:
url = f"{self.valves.LIGHT_RAG_URL.rstrip('/')}{self.valves.QUERY_ENDPOINT}"
headers = self._get_headers()
payload = {"query": query, "mode": self.valves.QUERY_MODE}
resp = self._session.post(
url, json=payload, headers=headers, timeout=self.valves.REQUEST_TIMEOUT
)
if resp.status_code == 401:
return None
if resp.status_code != 200:
return None
data = resp.json()
context = (
data.get("response")
or data.get("answer")
or data.get("result")
or data.get("data", {}).get("response")
or ""
)
return context if context.strip() else None
except Exception:
return None
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
messages = body.get("messages", [])
if not messages:
return body
user_msg = ""
for msg in reversed(messages):
if msg.get("role") == "user":
user_msg = msg.get("content", "")
break
if not user_msg:
return body
context = self._get_context(user_msg)
if not context and self.valves.FALLBACK_ENABLED:
context = "[LightRAG недоступен. Использую общие знания.]"
elif not context:
return body
system_prompt = self.valves.SYSTEM_PROMPT.format(context=context)
if messages and messages[0].get("role") == "system":
messages[0]["content"] = system_prompt
else:
messages.insert(0, {"role": "system", "content": system_prompt})
body["messages"] = messages
body.setdefault("options", {})["temperature"] = self.valves.TEMPERATURE
return body
def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
return bodyПояснения по функции
Для настройки доступны поля
Light Rag Url - адрес для подключения к LightRAG ( по умолчанию http://lightrag:9621)
Query Endpoint - по умолчанию /query
Query Mode - смотрите, по умолчанию hybrid
Request Timeout - поставил 600 (по умолчанию 15)
Auth Enabled - по умолчанию Enable
Api Key - указать API KEY
Strict Literature Mode - Проверка на галлюцинации, по умолчанию Enable
Fallback Enabled - Если LightRAG недоступен. Использую общие знания о романе «Анна Каренина», по умолчанию Enable
Debug -по умолчанию Disabled
System Prompt -по умолчанию
Ты литературный эксперт по роману Л.Н. Толстого «Анна Каренина». Отвечай СТРОГО на основе контекста.
Правила ответа:
- Анализируй персонажей, их мотивы и развитие
- Указывай конкретные главы и сцены из текста
- Цитируй важные пассажи в кавычках
- Сравнивай с параллельными сюжетными линиями (Анна/Левин)
- Если информации нет: «В предоставленных главах эта информация отсутствует»
В конце ответа СТРОГО скопируй строку источников из контекста без изменений.
Формат: 📖 [Источники: файл1, файл2] или 📖 [файл.md]
ЗАПРЕЩЕНО:
- Сокращать список источников до одного файла
- Перефразировать или придумывать названия файлов
- Писать общие названия типа «[Роман]», «[Текст]»
- Указывать части без конкретных глав
<CONTEXT>
{context}
</CONTEXT>Для чистоты эксперимента в LightRAG отключено кеширование.
# Отключение кэша ответов LLM
ENABLE_LLM_CACHE=false
# Отключение кэша для извлечения сущностей
ENABLE_LLM_CACHE_FOR_ENTITY_EXTRACT=falseТестовый набор для RAG «Анна Каренина»
Уровень 1: Базовые факты (Who/What/Where)
| # | Вопрос | Эталонный ответ | Ответ local | Ответ global | Ответ hybrid | Что проверяем |
|---|---|---|---|---|---|---|
| 1 | Кто такая Анна Каренина? | Жена Алексея Александровича Каренина, мать Серёжи. Позже — возлюбленная графа Вронского. Главная героиня романа. | + | + | Анна Каренина — главная героиня романа Льва Толстого «Анна Каренина», чья судьба становится символом конфликта между личными желаниями и социальными нормами XIX века. Она является женой Алексея Александровича Каренина, влиятельного чиновника, и матерью их сына Сергея. Однако её жизнь меняется после начала романтической связи с князем Алексеем Вронским, что приводит к общественному осуждению и личным страданиям.
Анна активно участвует в петербургском обществе, посещает балы и театры, но её внутренний конфликт между семейными обязанностями и чувствами к Вронскому становится источником её трагедии. Её отношения с Вронским осложняются правовыми проблемами, связанными с разводом, а также её стремлением к свободе, которое противоречит строгим социальным нормам того времени. Анна также сталкивается с внутренними переживаниями, включая чувство вины перед семьёй, неуверенность в чувствах Вронского и стремление к разрыву с мужем. Её трагический финал, связанный с самоубийством, подчёркивает невозможность совмещения её личных желаний с жёсткими социальными ожиданиями эпохи. References [1] on1.md [3] part02_chapter004.md [4] part01_chapter018.md [9] part05_chapter023.md [12] part05_chapter028.md |
Идентификация персонажа |
| 2 | Как зовут мужа Анны Карениной? | Алексей Александрович Каренин | + | + | Мужа Анны Карениной зовут Алексей Александрович Каренин. В романе Л.Н. Толстого их брак является ключевой линией, подчеркивающей социальные и личные конфликты Анны.
Контекст:
Сравнение с линией Левина: в отличие от Каренина, который представляет собой жесткую социальную норму, Левин идет по пути внутреннего духовного поиска (например, в главе part04_chapter001.md описывается его размышление о счастье и браке). 📖 [Источники: part01_chapter018.md, on1.md, part02_chapter004.md, part02_chapter006.md, part02_chapter007.md, part05_chapter033.md, on2.md, part05_chapter023.md, part04_chapter001.md, part06_chapter021.md, part04_chapter009.md, part03_chapter018.md] |
Простое извлечение имени |
| 3 | Где происходит знакомство Анны с Вронским? | На вокзале в Москве, при встрече поезда | + | - | Знакомство Анны Карениной с графом Вронским происходит на вокзале в Москве. Этот эпизод описывается в первой части, главе 18, где Анна, прибывшая в Москву, встречает Вронского, который приехал на поезде. Их первая встреча происходит в вагоне, когда Вронский, увидев её, испытывает сильное впечатление от её внешности и выражения лица, что становится началом их романтической связи.
References
|
Конкретное место события |
| 4 | Какой трагический эпизод сопровождает первую встречу Анны и Вронского? | Смерть сторожа под колёсами поезда (символический эпизод) | + | - | Первую встречу Анны Карениной и графа Вронского сопровождает инцидент смерти сторожа, раздавленного под колёсами поезда. Этот эпизод описывается в главе 18 первой части романа. Вронский, находясь на платформе, замечает Анну, а затем видит, как сторож, пытаясь перебежать на другую сторону, попадает под поезд. Сцена подчёркивает драматизм момента и символизирует начало их трагической связи.
Ключевая цитата: «…в то время как поезд проезжал мимо, вдруг вспыхнуло сияние, и в эту минуту вспыхнула в ней мысль о том, что она пересекает границу жизни, и тут же она увидела, как сторож, не успевший перейти на другую сторону, был раздавлен под колёсами поезда» (из главы 18, часть 1). References [1] part01_chapter018.md |
Контекст события |
| 5 | Кто такой Константин Левин? | Помещик, философствующий натуралист, друг Облонского, жених и затем муж Кити Щербацкой | + | + | Константин Левин — главный герой романа Льва Толстого «Анна Каренина», землевладелец, мыслитель и философ, живущий в деревне. Его персонаж отражает борьбу между личными амбициями и моральными принципами. Левин влюблен в Кити Щербацкую, но его чувства не получают одобрения из-за недостатка уверенности в себе и социального статуса. После отвержения Кити он переживает эмоциональный кризис, но в конечном итоге находит силы сделать ей второе предложение, и они женятся.
Левин активно участвует в сельском хозяйстве, стремясь модернизировать управление имением и улучшить условия для крестьян. Он критикует роскошь и лицемерие городской аристократии, предпочитая простой деревенский быт. Его персонаж также вовлечен в споры с Облонским и Вронским, сравнивая их жизненные пути с собственными. На протяжении сюжета Левин переживает духовное пробуждение, размышляя о смысле жизни, вере и социальной ответственности. После рождения сына с Кити он обретает внутреннее спокойствие и осознает ценность семейной жизни. Его история подчеркивает темы любви, долга и поиска смысла в условиях быстро меняющегося общества. References
|
Второстепенный сюжет |
Добавил в ч 1 гл 18: "Знакомство Анны с Вронским происходит на вокзале в Москве, при встрече поезда. Также происходит символический эпизод — смерть сторожа под колёсами поезда."(смерть сторожа описана неявно) Переиндексировал. Вопросы 4 и 5
Уровень 2: Сюжетные связи (How/Why)
| # | Вопрос | Эталонный ответ | Ответ local | Ответ global | Ответ hybrid | Что проверяем |
|---|---|---|---|---|---|---|
| 6 | Почему Анна уезжает из Петербурга в Москву в начале романа? | Помирить мужа со своим братом Стивой Облонским, узнавшим о его измене жене Долли | - | Анна Каренина уезжает из Петербурга в Москву в начале романа, чтобы встретиться с братом Степаном Аркадьичем и обсудить личные и семейные вопросы. Эта поездка связана с её стремлением к разводу с мужем Алексеем Александровичем Карениным, а также с её отношениями с графом Вронским, который в то время был ещё женат.
Согласно документу [1], Анна отправляется в Москву, где планирует обсудить с братом Степаном Аркадьичем возможность развода и другие важные дела. В тексте также упоминается, что её поездка в Москву связана с её эмоциональным состоянием: она испытывает внутренний конфликт, связанный с отношениями с Вронским и неудовлетворённостью браком с Карениным. Кроме того, в документе [12] описывается, как Анна встречает Вронского на вокзале в Москве, что становится ключевым моментом в их романтической истории. Таким образом, её переезд в Москву служит как практической целью (обсуждение развода), так и катализатором для развития личных и социальных конфликтов, которые определяют дальнейший сюжет романа. References
|
Причинно-следственные связи | |
| 7 | Как Вронский объясняет Анне своё чувство на балу у Бетси? | Признаётся в любви, говорит, что следовал за ней в Петербург как «следует за добычей» | + | + | Диалог/эмоции | |
| 8 | Почему Каренин отказывается от развода с Анной? | Религиозные соображения, страх за карьеру, желание сохранить фасад благопристойности | + | + | Мотивация персонажа | |
| 9 | Какое решение принимает Анна после рождения дочери Анны (дочери от Вронского)? | Покидает мужа и сына, уезжает за границу с Вронским | - | + | Поворотный пункт сюжета | |
| 10 | Что происходит на скачках, где падает лошадь Вронского? | Вронский падает с лошади Фру-Фру; Анна теряет самообладание, её реакция выдаёт их связь перед светом | + | + | Публичный конфликт |
Уровень 3: Тематический анализ (Themes/Philosophy)
| # | Вопрос | Эталонный ответ | Ответ local | Ответ global | Ответ hybrid | Что проверяем |
|---|---|---|---|---|---|---|
| 11 | С чего начинается роман? | Все счастливые семьи похожи друг на друга, каждая несчастливая семья несчастлива по-своему | - | + | Мета-информация | |
| 12 | Как Толстой противопоставляет сюжетные линии Анны и Левина? | Линия Анны — трагедия разрушения семьи и страсти; линия Левина — созидание семьи и поиск смысла жизни | + | + | Структура романа | |
| 13 | Что символизирует повторяющийся образ поезда? | Неотвратимость судьбы, движение к гибели (Анна), прогресс vs разрушение традиций | + | + | Символика | |
| 14 | Какое философское открытие совершает Левин в конце романа? | Принятие жизни как есть через веру, «жить не для своих желаний, а для Бога» | + | + | Идеологический вывод | |
| 15 | Как общество реагирует на отношения Анны и Вронского? | Двойные стандарты: Вронский сохраняет репутацию, Анна становится изгоем («падшая женщина») | + | Социальный комментарий |
Уровень 4: Детали и цитаты (Specific Evidence)
| # | Вопрос | Эталонный ответ | Ответ local | Ответ global | Ответ hybrid | Что проверяем |
|---|---|---|---|---|---|---|
| 16 | Какой подарок делает Вронский Анне перед казино в Италии? | Портрет Анны, написанный знаменитым художником Михайловым | - | Конкретный объект | ||
| 17 | Как звали сына Анны от Каренина? | Сергей (Серёжа) | + | Точное имя | ||
| 18 | Что читает Каренин, когда Анна умирает (до самоубийства)? | Телеграммы, письма, мемуары французской писательницы | - | Контекст сцены | ||
| 19 | Как Левин делает предложение Кити во второй раз? | На ходулях из коры, используя детскую мнемонику букв: «Это нельзя исправить, как нельзя...» | - | Специфический эпизод | ||
| 20 | Какое последнее слово (мысль) у Анны перед самоубийством? | «Где же я? Что я делаю? Зачем?» — и ощущение «ужасного» в колёсах | - | Психология персонажа |
Уровень 5: Многошаговые/сложные запросы
| # | Вопрос | Эталонный ответ | Ответ local | Ответ global | Ответ hybrid | Что проверяем |
|---|---|---|---|---|---|---|
| 21 | Сравни отношение Каренина и Вронского к Анне в финале романа. | Каренин: прощение, опека над дочерью Анны, странная «святость»; Вронский: разочарование, желание свободы, уезд в Сербию | + | Сравнительный анализ | ||
| 22 | Как эволюционирует отношение Анны к собственному сыну Серёже? | От любви и тоски → осознание невозможности возврата → отчуждение через новую беременность | + | Развитие характера | ||
| 23 | Какие три семьи представлены в романе и что они символизируют? | Облонские (кризис буржуазного брака), Каренины (разрушение «формального» брака), Левины (созидание натурального хозяйства) | + | Структурный анализ | ||
| 24 | Опиши сцену самоубийства Анны: где, как, символические детали. | Ясная Поляна (вокзал), бросается под поезд, красная сумочка, мгновенное сомнение перед прыжком | + | Ключевая сцена | ||
| 25 | Как работа с крестьянами Левина связана с его философскими размышлениями? | Труд как естественное состояние → крестьяне «живут для души» → Левин ищет такой же непосредственности в жизни | + | Интертекстуальность |