Документация

Интеграция с LlamaIndex

Laya предоставляет неавторегрессионные компоненты принятия решений с задержкой менее 35 мс для RAG-конвейеров LlamaIndex, RouterQueryEngine и выбора инструментов (задержка на один вопрос — 32.8 мс с laya-multilingual и 39.5 мс с laya на GPU Tesla T4; 193–464 мс на CPU):

  • LayaSingleSelector: селектор одного варианта с задержкой менее 35 мс, заменяющий LLMSingleSelector для RouterQueryEngine.
  • LayaMultiSelector: селектор нескольких вариантов, заменяющий LLMMultiSelector для составных запросов, охватывающих несколько источников данных.
  • LayaQueryRouter: автономный диспетчер запросов, направляющий входящие запросы напрямую в целевые движки запросов или callables.

Поддерживает как локальный инференс в процессе (Agent или Router), так и удалённый инференс по HTTP к вашему собственному экземпляру laya-serve, без необходимости в PyTorch на периферийных клиентах.


Установка

pip install "laya[llamaindex]"

1. Маршрутизация одного варианта с RouterQueryEngine

В LlamaIndex RouterQueryEngine использует селектор, чтобы решить, какой нижележащий движок запросов или инструмент должен ответить на вопрос. Авторегрессионные LLM-селекторы (LLMSingleSelector) тратят 1,000–2,000 мс на генерацию текста. LayaSingleSelector оценивает инструменты-кандидаты за ~33 мс, без генерации токенов:

from llama_index.core.query_engine import RouterQueryEngine
from llama_index.core.tools import QueryEngineTool, ToolMetadata
from laya.integrations.llamaindex import LayaSingleSelector

# Define query engine tools
docs_tool = QueryEngineTool(
    query_engine=vector_index.as_query_engine(),
    metadata=ToolMetadata(
        name="vector_documentation",
        description="Semantic search over technical user documentation and API guides.",
    ),
)
sql_tool = QueryEngineTool(
    query_engine=sql_index.as_query_engine(),
    metadata=ToolMetadata(
        name="sql_database",
        description="Structured SQL database containing customer accounts, billing, and orders.",
    ),
)

# Initialize Laya sub-35ms selector with confidence fallback
selector = LayaSingleSelector(
    confidence_threshold=0.80,   # If confidence < 0.80, fall back to index 0
    fallback_index=0,
)

router_engine = RouterQueryEngine(
    selector=selector,
    query_engine_tools=[docs_tool, sql_tool],
)

response = router_engine.query("What is the shipping address for order #4912?")
print(response)

2. Выбор нескольких вариантов для составных запросов

Для запросов, требующих синтеза данных из нескольких индексов (например, сравнения спецификаций документации с записями транзакционной базы данных), LayaMultiSelector оценивает релевантность кандидатов и возвращает несколько выбранных инструментов:

from laya.integrations.llamaindex import LayaMultiSelector

multi_selector = LayaMultiSelector(
    probability_threshold=0.25,  # Select all tools with probability >= 0.25
    max_outputs=2,
)

tools = [docs_tool.metadata, sql_tool.metadata, summary_tool.metadata]
result = multi_selector.select(
    tools,
    "How does the database security policy compare with our published compliance guide?"
)

for sel in result.selections:
    print(f"Tool: {tools[sel.index].name} | {sel.reason}")

3. Прямая отправка запросов с LayaQueryRouter

Для прямой маршрутизации без накладных расходов RouterQueryEngine LayaQueryRouter направляет запросы напрямую в движки, зарегистрированные в словаре:

from laya.integrations.llamaindex import LayaQueryRouter

router = LayaQueryRouter(
    query_engines={
        "vector": vector_query_engine,
        "sql": sql_query_engine,
        "summary": summary_query_engine,
    },
    descriptions={
        "vector": "Semantic search over product documentation and guides",
        "sql": "Structured SQL queries for user accounts and transactions",
        "summary": "Quarterly reports and high-level business summaries",
    },
    confidence_threshold=0.75,
    fallback_key="vector",
)

# Route and execute in one call:
response = router.query("How many active subscriptions were renewed in Q3?")
print(response)

Поддерживаются как синхронный query(), так и асинхронный aquery().


4. Gating по порогу уверенности

Как и интеграция Laya с LangChain, LayaSingleSelector и LayaQueryRouter читают откалиброванную answer_confidence (max(p)):

  • Автоматический резервный вариант: укажите fallback_index (или fallback_key), чтобы плавно перенаправлять неуверенные запросы на безопасный движок по умолчанию.
  • Строгая защита: задайте raise_on_low_confidence=True в LayaSingleSelector, чтобы вызывать LayaLowConfidenceError, когда входные данные неоднозначны, — это позволяет вызывающей стороне выполнить эскалацию.

5. Удалённые развёртывания по HTTP

Для бессерверного RAG, периферийных сред или сред без локальных GPU:

from laya.integrations.llamaindex import LayaSingleSelector

selector = LayaSingleSelector(
    base_url="http://laya-serve.internal:8080",
    confidence_threshold=0.85,
    fallback_index=0,
)

Удалённый клиент использует стандартную библиотеку Python urllib без тяжёлых зависимостей, что предотвращает пересылку учётных данных между источниками и соответствует спецификации /v1/systemone.


6. Управление решением на каждый вызов

LayaSingleSelector, LayaMultiSelector и LayaQueryRouter принимают те же аргументы на каждый вызов, что и базовый API: два бюджета токенов (max_len, head_max_len), управление языком и воздержанием (lang, min_confidence), и пять аргументов хуков предсказаний (hooks, on_predict_start, on_predict_end, hooks_raise, hooks_timeout). Они задаются для каждого селектора отдельно, поэтому широкому шагу маршрутизации можно дать запас, пока остальной конвейер сохраняет значения по умолчанию чекпойнта.

Варианты choice-вопроса делят бюджет вариантов чекпойнта — head_max_len, 192 токена в laya — и каждый кандидат вносит своё имя и описание, поэтому примерно после 20 инструментов описания начинают доходить до модели как один и тот же текст.

selector = LayaSingleSelector(
    instructions="Which tool or query engine is best suited to answer this query?",
    max_len=1024,          # total window
    head_max_len=512,      # tokens shared by the option prompt
)
result = selector.select(tools, query)     # tools: 59 descriptions

Измерено на laya (Apple silicon, один прямой проход на запрос, оценка по выбранному инструменту) с набором из 59 инструментов, построенным из английских меток интентов MASSIVE и одной реплики на метку, поэтому эталонная истина точна. Каждая ячейка показывает, сколько из 59 запросов попали в свой собственный инструмент; оба повтора дали одинаковый результат.

Набор из 59 инструментов Бюджет по умолчанию max_len=1024, head_max_len=384 …, head_max_len=512
Запросы на своём собственном инструменте 2/59 8/59 15/59
Медиана мс на запрос 160 172 184

Абсолютная точность здесь не является утверждением: чекпойнт — не классификатор MASSIVE, а 59 похожих меток — это стрессовая форма. Утверждение — это направление и цена: набор, который бюджет по умолчанию сводит почти к нулю, становится читаемым, и при таком размере окно стоит мало времени. При менее чем примерно 20 вариантах метки уже помещаются, и расширение окна может сдвинуть ответы в неверную сторону, поэтому оба аргумента включаются по выбору для каждого селектора. См. интеграцию с LangChain, чтобы увидеть этот измеренный обрыв.

Хуки выполняются только на локальном пути. Селектор с base_url и hooks=[...] вызывает ValueError, а не сообщает об успехе, кэш которого так и не выполнился, — хук это Python callable, который выполняется внутри predict, и никакой формат передачи его не переносит. Устанавливайте хуки в процессе, который выполняет инференс. Два бюджета действительно доходят до удалённого узла, в теле запроса, до его потолка LAYA_MAX_TOKEN_BUDGET; большее значение возвращается как 422.

Язык и воздержание

lang фиксирует язык, на котором запрос маршрутизируется и получает ответ – он выбирает языковую калибровку отвечающего чекпойнта вместо того, чтобы полагаться на встроенное определение – а min_confidence — это гейт воздержания ядра: решение ниже него возвращается как воздержание, а не как принудительный выбор. Оба одинаково читаются Agent.predict и Router.predict и принимаются laya-serve в теле запроса, поэтому селектор передаёт их и на локальном, и на удалённом пути. Не заданное значение опускается, а не отправляется как None, так что оно не может заслонить собственное значение по умолчанию развёртывания; min_confidence=0.0 и lang="" — это реальные значения, и они передаются как есть.

selector = LayaSingleSelector(
    instructions="Which tool or query engine is best suited to answer this query?",
    lang="de",             # answer German queries in German
    min_confidence=0.3,    # abstain when no tool clears a 0.3 confidence
)