Несколько месяцев назад мы запустили 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 саму по себе, так и то, как ее можно использовать в системе в целом:
- ОпенАй ЛЛМ
- Использование LLM в LLMPredictor
- Изменение LLM в Index/Query Engine
- Определение пользовательской модели 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 и удалили несколько устаревших функций:
- Удалите
ChatGPTLLMPredictor
иHuggingFaceLLMPredictor
(вместо них используйтеOpenAI
иHuggingFaceLLM
, см. Руководство по миграции) - Удалена поддержка установки
cache
через конструкторLLMPredictor
. - Удален модуль
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.
Как всегда, наше репозиторий здесь и наши документы здесь. Если у вас есть мысли/комментарии, не стесняйтесь зайти в наш Дискорд!