Несколько месяцев назад мы запустили LlamaIndex 0.6.0, которая включала масштабную переработку нашей кодовой базы, чтобы сделать нашу библиотеку более модульной, настраиваемой и доступной как для начинающих, так и для опытных пользователей:

  • Мы создали модульные абстракции хранения (данные, индексы) и абстракции вычислений (извлекатели, механизмы запросов).
  • Мы создали низкоуровневый API, в котором пользователи могли использовать наши модули (извлекатели, механизмы запросов) независимо друг от друга и настраивать его как часть более крупной системы.

Сегодня мы рады представить LlamaIndex 0.7.0. Наш последний выпуск продолжает тему улучшения модульности/настраиваемости на более низком уровне, чтобы обеспечить разработку приложений LLM снизу вверх для ваших данных. Теперь у вас есть еще больший контроль над использованием ключевых абстракций: LLM, наша синтезатор ответов и наши объекты Document и Node.

  • Мы создали автономные абстракции LLM (OpenAI, HuggingFace, PaLM).
  • Мы сделали наш модуль синтеза ответов независимым модулем, который вы можете использовать совершенно независимо от остальных наших абстракций — избавьтесь от подсказок, связанных с попытками выяснить, как разместить контекст в окне контекста.
  • Мы добавили обширные возможности управления метаданными в наши объекты Document/Node — теперь у вас есть полный контроль над контекстом, который вы решите внедрить в свои документы.

Ниже мы опишем каждый раздел более подробно. Мы также приводим полный список критических изменений внизу.

Автономные абстракции LLM

Мы создали автономные абстракции LLM для OpenAI, HuggingFace и PaLM. Эти абстракции могут использоваться сами по себе или как часть существующей системы LlamaIndex (механизмы запросов, средства извлечения).

Мотивация высокого уровня

Мы сделали это по нескольким причинам:

  • Более чистые абстракции в кодовой базе. Раньше наш класс LLMPredictor имел массу дырявых абстракций с базовым классом LangChain LLM. Это сделало наши абстракции LLM трудными для понимания и трудными для настройки.
  • Чуть более чистый UX для разработчиков. Раньше, если вы хотели настроить LLM по умолчанию (например, использовать «text-davinci-003»), вам нужно было импортировать правильный класс LangChain, обернуть его в наш LLMPredictor, а затем передать в ServiceContext. Теперь легко просто импортируйте нашу абстракцию LLM (которая изначально задокументирована в нашей документации) и подключите ее к ServiceContext.Конечно, вы все равно можете использовать LLM LangChain, если хотите.
  • Способствует развитию снизу вверх: имеет смысл поэкспериментировать с этими модулями LLM независимо, прежде чем подключать их как часть более крупной системы. Это отражает наш больший толчок в 0.7.0, чтобы позволить пользователям создавать свои собственные рабочие процессы.

Использование самостоятельно

Наши абстракции LLM поддерживают как конечные точки complete, так и chat. Основное отличие состоит в том, что complete предназначен для ввода простой строки и вывода CompletionResponse (содержащего текстовый вывод + дополнительные поля). chat принимает ChatMessage и выводит ChatResponse (содержащее сообщение чата + дополнительные поля).

Эти конечные точки LLM также изначально поддерживают потоковую передачу через stream_complete и stream_chat.

Вот как вы можете использовать абстракции LLM сами по себе:

from llama_index.llms import OpenAI

# using complete endpoint
resp = OpenAI().complete('Paul Graham is ')
print(resp)
# get raw object
resp_raw = resp.raw
# using chat endpoint
from llama_index.llms import ChatMessage, OpenAI
messages = [
    ChatMessage(role='system', content='You are a pirate with a colorful personality'),
    ChatMessage(role='user', content='What is your name')
]
resp = OpenAI().chat(messages)
print(resp)
# get raw object
resp_raw = resp.raw
# using streaming endpoint
from llama_index.llms import OpenAI
llm = OpenAI()
resp = llm.stream_complete('Paul Graham is ')
for delta in resp:
    print(delta, end='')

Вот как вы можете использовать абстракции LLM как часть общей системы LlamaIndex.

from llama_index.llms import OpenAI
from llama_index.indices.service_context import ServiceContext
from llama_index import VectorStoreIndex

llm = OpenAI(model='gpt-3.5-turbo', temperature=0)
service_context = ServiceContext.from_defaults(llm=llm)
index = VectorStoreIndex.from_documents(docs, service_context=service_context)
response = index.as_query_engine().query("<question>")

Примечание. Наш верхний уровень LLMPredictor все еще существует, но менее удобен для пользователя (и мы можем отказаться от его поддержки в будущем). Кроме того, вы все еще можете использовать LLM LangChain через наш класс LangChainLLM.

Ресурсы

Все наши блокноты по умолчанию обновлены для использования нашей встроенной интеграции OpenAI LLM. Вот некоторые ресурсы, чтобы показать как абстракцию LLM саму по себе, так и то, как ее можно использовать в системе в целом:

Автономные модули синтеза ответов

Контекст

В любой системе RAG есть поиск и синтез. Компонент синтеза должен принимать входящий контекст в качестве входных данных и синтезировать ответ с использованием LLM.

По сути, модуль синтеза должен синтезировать ответ по любому списку контекстов, независимо от его длины. По сути, это шаблон, который должен написать разработчик LLM / инженер искусственного интеллекта.

Раньше это было внутренней абстракцией в LlamaIndex (как ResponseSynthesizer), но внешний UX был недружественным для пользователей. Фактическую часть, которая собирала ответы (ResponseBuilder), было сложно настроить, а сам ResponseSynthesizer добавлял дополнительный ненужный слой.

Теперь у нас есть набор автономных модулей, которые вы можете легко импортировать. Раньше, когда вы устанавливали response_mode в механизме запросов, они настраивались для вас. Теперь они более доступны и ориентированы на пользователя.

Вот список всех новых Response Synthesiszer модулей, доступных от llama_index.response_synthesizer:

  • Refine - Запросить LLM, отправляя каждый фрагмент текста по отдельности. После первого вызова LLM существующий ответ также отправляется в LLM для обновления и уточнения с использованием следующего фрагмента текста.
  • Accumulate — Запросить LLM с одним и тем же приглашением в нескольких фрагментах текста и вернуть отформатированный список ответов.
  • Compact — то же, что и Refine, но в каждый вызов LLM помещается как можно больше текста.
  • CompactAndAccumulate — то же, что и Accumulate, но содержит как можно больше текста.
  • TreeSummarize — создать восходящую сводку из предоставленных фрагментов текста и вернуть корневую сводку.
  • SimpleSummarize — объединить и обрезать все фрагменты текста и суммировать их в одном вызове LLM.

Использование

Как подробно описано выше, вы можете напрямую установить синтезатор ответов в механизме запросов или позволить response_mode получить соответствующий синтезатор ответов.

Кроме того, вы можете напрямую вызывать и использовать эти синтезаторы как низкоуровневые модули. Вот небольшой пример:

from llama_index import ServiceContext
from llama_index.response_synthesizers import CompactAndRefine

# you can also configure the text_qa_template, refine_template, 
# and streaming toggle from here
response_synthesizer = CompactAndRefine(
  service_context=service_context.from_defaults()
)
response = response_synthesizer.get_response(
 "What skills does Bob have?",
  text_chunks=[" ..."]  # here would be text, hopefully about Bob's skills
)

Ресурсы

Вот несколько дополнительных блокнотов, показывающих, как использовать get_response_synthesizer :

Возможности управления метаданными

Если вы хотите иметь хорошую производительность в любом приложении LLM с вашими данными (включая конвейер RAG), вам необходимо убедиться, что ваши документы действительно содержат соответствующий контекст для запроса. Один из способов сделать это — добавить правильные метаданные как на уровне документа, так и после того, как документы были проанализированы на текстовые фрагменты (в узлы).

Мы позволяем вам определять поля метаданных в документе, настраивать идентификатор, а также настраивать текст/формат метаданных для использования LLM и встраивания.

Определение полей метаданных

document = Document(
    text='text', 
    metadata={
        'filename': '<doc_file_name>', 
        'category': '<category>'
    }
)

Настройка идентификатора

Идентификатор каждого документа может быть установлен несколькими способами

  • В конструкторе: document = Document(text="text", doc_id_="id")
  • После постройки объекта: document.doc_id = "id"
  • Автоматически используя SimpleDirectoryReader : SimpleDirectoryReader(filename_as_id=True).load_data()

Настройка текста метаданных для LLM и вложений

Как показано выше, вы можете установить метаданные, содержащие полезную информацию. По умолчанию все метаданные будут видны модели внедрения и LLM. Однако иногда вы можете захотеть включить данные только для смещения встраивания или включить данные только в качестве дополнительной информации для LLM!

С новыми объектами Document вы можете настроить, для чего используется каждое поле метаданных:

document = Document(
    text='text', 
    metadata={
        'filename': '<doc_file_name>', 
        'category': '<category>'
    },
    excluded_llm_metadata_keys=['filename', 'category'],
    excluded_embed_metadata_keys=['filename']
)

Настройка шаблона формата метаданных

Когда метаданные вставляются в текст, они имеют очень специфический формат. Этот формат настраивается на нескольких уровнях:

from llama_index.schema import MetadataMode

document = Document(
  text='text',
  metadata={"key": "val"},
  metadata_seperator="::",
    metadata_template="{key}=>{value}",
    text_template="Metadata: {metadata_str}\\n-----\\nContent: {content}"
)
# available modes are ALL, NONE, LLM, and EMBED
print(document.get_content(metadata_mode=MetadataMode.ALL))
# output:
# Metadata: key=>val
# -----
# text

Пожалуйста, ознакомьтесь с этим руководством для более подробной информации!

Полный список критических изменений

Синтез ответов + постпроцессоры узла

Класс объектов ResponseSynthesizer был удален и заменен на get_response_synthesizer . В дополнение к этому, постпроцессоры узла теперь обрабатываются механизмом запросов напрямую, а старый SentenceEmbeddingOptimizer был переключен на сам экземпляр постпроцессора узла.

Вот пример необходимой миграции для использования всех перемещенных функций.

Старый

from llama_index import (
    VectorStoreIndex,
    ResponseSynthesizer,
)
from llama_index.indices.postprocessor import SimilarityPostprocessor
from llama_index.optimizers import SentenceEmbeddingOptimizer
from llama_index.query_engine import RetrieverQueryEngine

documents = ...
# build index
index = VectorStoreIndex.from_documents(documents)
# configure retriever
retriever = index.as_retriever(
   similarity_top_k=3
)
# configure response synthesizer
response_synthesizer = ResponseSynthesizer.from_args(
   response_mode="tree_summarize",
    node_postprocessors=[
        SimilarityPostprocessor(similarity_cutoff=0.7),
        SentenceEmbeddingOptimizer(percentile_cutoff=0.5)
    ]
)
# assemble query engine
query_engine = RetrieverQueryEngine(
    retriever=retriever,
    response_synthesizer=response_synthesizer,
)

Новое

from llama_index import (
    VectorStoreIndex,
    get_response_synthesizer,
)
from llama_index.indices.postprocessor import (
    SimilarityPostprocessor,
    SentenceEmbeddingOptimizer
)

documents = ...
# build index
index = VectorStoreIndex.from_documents(documents)
# configure response synthesizer
response_synthesizer = get_response_synthesizer(
   response_mode="tree_summarize",
)
# assemble query engine
query_engine = index.as_query_engine(
  similarity_top_k=3,
    response_synthesizer=response_synthesizer,
    node_postprocessors=[
        SimilarityPostprocessor(similarity_cutoff=0.7),
        SentenceEmbeddingOptimizer(percentile_cutoff=0.5)
    ]
)

Предиктор LLM

Представляя новую абстракцию LLM, мы очистили предиктор LLM и удалили несколько устаревших функций:

  1. Удалите ChatGPTLLMPredictor и HuggingFaceLLMPredictor (вместо них используйте OpenAI и HuggingFaceLLM, см. Руководство по миграции)
  2. Удалена поддержка установки cache через конструктор LLMPredictor.
  3. Удален модуль llama_index.token_counter.token_counter (см. Руководство по миграции).

Теперь класс LLM Predictor в основном представляет собой облегченную оболочку над абстракцией LLM, которая обрабатывает:

  • преобразование подсказок в формат ввода строки или сообщения чата, ожидаемый LLM
  • регистрация подсказок и ответов на callback-менеджер

Мы советуем пользователям настраивать аргумент llm напрямую в ServiceContext (вместо создания LLM Predictor).

Чат Движок

Мы обновили интерфейс BaseChatEngine, чтобы использовать List[ChatMessage]] для chat_history вместо кортежа строк. Это делает модель данных согласованной с вводом/выводом LLM , а также обеспечивает большую гибкость для указания последовательных сообщений с одной и той же ролью.

Старый

engine = SimpleChatEngine.from_defaults(
	chat_history=[("human message", "assistant message")],
)
response = engine.chat("new human message")

Новое

engine = SimpleChatEngine.from_defaults(
    service_context=mock_service_context,
    chat_history=[
        ChatMessage(role=MessageRole.USER, content="human message"),
        ChatMessage(role=MessageRole.ASSISTANT, content="assistant message"),
    ],
)
response = engine.chat("new human message")

Мы также предоставили состояние chat_history как свойство и поддержали переопределение chat_history в конечных точках chat и achat.

Подсказка помощника

Мы удалили некоторые ранее устаревшие аргументы: max_input_size, embedding_limit, max_chunk_overlap.

Заключение

На высоком уровне мы надеемся, что эти изменения по-прежнему позволят разрабатывать приложения LLM по принципу «снизу вверх» для ваших данных. Сначала мы рекомендуем вам поиграть с нашими новыми модулями сами по себе, чтобы понять, что они делают и где их можно использовать. Когда вы будете готовы использовать их в более сложных рабочих процессах, вы сможете понять, как использовать наши внешние компоненты для настройки сложного конвейера RAG.

Как всегда, наше репозиторий здесь и наши документы здесь. Если у вас есть мысли/комментарии, не стесняйтесь зайти в наш Дискорд!