文件導航

LlamaIndex 整合

Laya 為 LlamaIndex 的 RAG 流水線、RouterQueryEngine 和工具選擇提供低於 35ms 的非自迴歸 決策元件(單問題延遲:Tesla T4 GPU 上用 laya-multilingual 測得 32.8 ms,用 laya 測得 39.5 ms;CPU 上為 193–464 ms):

  • LayaSingleSelector:低於 35ms 的單選選擇器,替代 RouterQueryEngine 的 LLMSingleSelector。
  • LayaMultiSelector:多選選擇器,替代 LLMMultiSelector,用於橫跨多個數據源的複合查詢。
  • LayaQueryRouter:獨立的查詢分發器,把進來的請求直接路由到目標查詢引擎或可呼叫物件。

同時支援本地程序內推理(Agent 或 Router)和遠端 HTTP 推理(對接你自己的 laya-serve 例項),邊緣客戶端不需要裝 PyTorch。


安裝

pip install "laya[llamaindex]"

1. 用 RouterQueryEngine 做單選路由

在 LlamaIndex 裡,RouterQueryEngine 用一個選擇器決定該由哪個底層查詢引擎或工具來回答 問題。自迴歸的 LLM 選擇器(LLMSingleSelector)要花 1,000–2,000 ms 生成文本。LayaSingleSelector 用 ~33 ms 評估候選工具,不做 token 生成:

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. 置信度閾值門控

和 Laya 的 LangChain 整合一樣,LayaSingleSelector 和 LayaQueryRouter 讀取校準過的 answer_confidence(max(p)):

  • 自動回退: 指定 fallback_index(或 fallback_key),把不確定的查詢無縫轉到一個安全的 預設引擎。
  • 嚴格防護: 在 LayaSingleSelector 上設定 raise_on_low_confidence=True,輸入含義不清時 丟擲 LayaLowConfidenceError,讓呼叫方升級處理。

5. 遠端 HTTP 部署

適用於 serverless 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 一樣的逐呼叫參數: 兩個 token 預算(max_len、head_max_len)、語言與棄答控制(lang、min_confidence)和五個預測鉤子參數(hooks、on_predict_start、 on_predict_end、hooks_raise、hooks_timeout)。它們是按選擇器設定的,所以可以讓一個寬度大 的路由步驟多留些空間,而流水線的其餘部分仍保持 checkpoint 的預設值。

一個 choice 問題的各個選項共享 checkpoint 的選項預算 —— head_max_len,在 laya 上是 192 個 token —— 而每個候選都會把自己的名字和描述貢獻進去,所以超過大約 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 晶片,每個查詢一次前向傳播,按選中的工具計分),用一份 59 個工具的名單, 它是從 MASSIVE 英文意圖標籤構建的、每個標籤一句話語,所以真值是精確的。每個單元格是 59 個查詢裡 有多少個命中了它自己的工具;兩次重複得到了相同的計數。

59 工具名單 預設預算 max_len=1024, head_max_len=384 …, head_max_len=512
命中自己工具的查詢 2/59 8/59 15/59
每個查詢的中位 ms 160 172 184

這裡主張的不是絕對準確率:這個 checkpoint 不是 MASSIVE 分類器,而 59 個相似的標籤是一種壓力形狀。 主張的是方向和代價 —— 一份會被預設預算壓到近乎為零的名單變得可讀了,而在這種規模下,加寬視窗 幾乎不花時間。選項少於約 20 個時,標籤本來就已經裝得下,加寬反而可能把答案帶偏,這就是兩個參數 都按選擇器選擇加入的原因。那條實測的懸崖見 LangChain 整合。

鉤子只在本地路徑上執行。 一個帶 base_url 和 hooks=[...] 的選擇器會丟擲 ValueError, 而不是報告一個其快取從未執行過的成功 —— 鉤子是一個在 predict 內部執行的 Python 可呼叫物件, 沒有任何線上格式能攜帶它。請把鉤子裝在執行推理的那個程序裡。兩個預算確實會隨請求體傳到遠端節點, 上限是該節點的 LAYA_MAX_TOKEN_BUDGET;更大的值會以 422 返回。

語言與棄答

lang 釘住查詢所路由和作答所用的語言 —— 選擇作答 checkpoint 的按語言校準,而不是依賴內建檢測 —— 而 min_confidence 是 core 的棄答門:低於它的決策會作為一次棄答返回,而不是一次強制的選擇。兩者都同樣被 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
)