在 LlamaIndex 抽象中定制 LLMs#
您可以将这些 LLM 抽象插入到 LlamaIndex 的其他模块中(索引、检索器、查询引擎、代理),从而可以构建对数据进行高级工作流程。
默认情况下,我们使用 OpenAI 的 gpt-3.5-turbo
模型。但您可以选择自定义使用的基础 LLM。
下面我们展示了一些 LLM 定制的示例。这包括:
- 更改基础 LLM
- 更改输出标记的数量(适用于 OpenAI、Cohere 或 AI21)
- 对任何 LLM 的所有参数进行更精细的控制,从上下文窗口到块重叠
示例:更改基础 LLM#
下面是一个自定义使用的 LLM 的示例片段。
在此示例中,我们使用 gpt-4
而不是 gpt-3.5-turbo
。可用模型包括 gpt-3.5-turbo
、gpt-3.5-turbo-instruct
、gpt-3.5-turbo-16k
、gpt-4
、gpt-4-32k
、text-davinci-003
和 text-davinci-002
。
请注意,您还可以插入 Langchain 的 LLM 页面上显示的任何 LLM。
from llama_index.core import KeywordTableIndex, SimpleDirectoryReader
from llama_index.llms.openai import OpenAI
# 或者
# from langchain.llms import ...
documents = SimpleDirectoryReader("data").load_data()
# 定义 LLM
llm = OpenAI(temperature=0.1, model="gpt-4")
# 构建索引
index = KeywordTableIndex.from_documents(documents, llm=llm)
# 从查询获取响应
query_engine = index.as_query_engine()
response = query_engine.query(
"What did the author do after his time at Y Combinator?"
)
示例:更改输出标记的数量(适用于 OpenAI、Cohere、AI21)#
默认情况下,输出标记的数量通常设置为较低的数字(例如,对于 OpenAI,默认值为 256)。
对于 OpenAI、Cohere、AI21,您只需设置 max_tokens
参数(或者对于 AI21 是 maxTokens)。我们将在后台处理文本分块/计算。
from llama_index.core import KeywordTableIndex, SimpleDirectoryReader
from llama_index.llms.openai import OpenAI
from llama_index.core import Settings
documents = SimpleDirectoryReader("data").load_data()
# 定义全局 LLM
Settings.llm = OpenAI(temperature=0, model="gpt-3.5-turbo", max_tokens=512)
示例:显式配置 context_window
和 num_output
#
如果您使用 Langchain 的其他 LLM 类,可能需要通过 Settings
显式配置 context_window
和 num_output
,因为默认情况下这些信息是不可用的。
from llama_index.core import KeywordTableIndex, SimpleDirectoryReader
from llama_index.llms.openai import OpenAI
from llama_index.core import Settings
documents = SimpleDirectoryReader("data").load_data()
# 设置上下文窗口
Settings.context_window = 4096
# 设置输出标记的数量
Settings.num_output = 256
# 定义 LLM
Settings.llm = OpenAI(
temperature=0,
model="gpt-3.5-turbo",
max_tokens=num_output,
)
示例:使用 HuggingFace LLM#
LlamaIndex 支持直接使用来自 HuggingFace 的 LLM。请注意,为了完全私密的体验,还需设置一个 本地嵌入模型。
来自 HuggingFace 的许多开源模型通常需要在每个提示之前添加一些序文,这是一个 system_prompt
。此外,查询本身可能需要在 query_str
周围添加额外的包装器。所有这些信息通常可以在您使用的模型的 HuggingFace 模型卡中找到。
下面的示例使用了来自模型卡 这里 找到的特定提示,同时使用了 system_prompt
和 query_wrapper_prompt
。
from llama_index.core import PromptTemplate
# 将字符串转换为适用于 Zephyr 的输入
def completion_to_prompt(completion):
return f"<|system|>\n</s>\n<|user|>\n{completion}</s>\n<|assistant|>\n"
# 将聊天消息列表转换为适用于 Zephyr 的输入
def messages_to_prompt(messages):
prompt = ""
for message in messages:
if message.role == "system":
prompt += f"<|system|>\n{message.content}</s>\n"
elif message.role == "user":
prompt += f"<|user|>\n{message.content}</s>\n"
elif message.role == "assistant":
prompt += f"<|assistant|>\n{message.content}</s>\n"
# 确保以系统提示开始,如果需要则插入空白
if not prompt.startswith("<|system|>\n"):
prompt = "<|system|>\n</s>\n" + prompt
# 添加最终的助手提示
prompt = prompt + "<|assistant|>\n"
return prompt
import torch
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core import Settings
Settings.llm = HuggingFaceLLM(
model_name="HuggingFaceH4/zephyr-7b-beta",
tokenizer_name="HuggingFaceH4/zephyr-7b-beta",
context_window=3900,
max_new_tokens=256,
generate_kwargs={"temperature": 0.7, "top_k": 50, "top_p": 0.95},
messages_to_prompt=messages_to_prompt,
completion_to_prompt=completion_to_prompt,
device_map="auto",
)
token_type_ids
。下面是一个配置预测器以在将输入传递给模型之前移除该标记的示例:
HuggingFaceLLM(
# ...
tokenizer_outputs_to_remove=["token_type_ids"]
)
完整的 API 参考可以在这里找到。
下面还列举了几个示例笔记本:
示例:使用自定义 LLM 模型 - 高级#
要使用自定义的 LLM 模型,您只需要实现 LLM
类(或者对于更简单的接口,可以使用 CustomLLM
)。您将负责将文本传递给模型并返回新生成的标记。
这个实现可以是某个本地模型,甚至是您自己 API 的封装。
请注意,为了完全私密的体验,还需要设置一个本地嵌入模型。
下面是一个简单的样板示例:
from typing import Optional, List, Mapping, Any
from llama_index.core import SimpleDirectoryReader, SummaryIndex
from llama_index.core.callbacks import CallbackManager
from llama_index.core.llms import (
CustomLLM,
CompletionResponse,
CompletionResponseGen,
LLMMetadata,
)
from llama_index.core.llms.callbacks import llm_completion_callback
from llama_index.core import Settings
class OurLLM(CustomLLM):
context_window: int = 3900
num_output: int = 256
model_name: str = "custom"
dummy_response: str = "My response"
@property
def metadata(self) -> LLMMetadata:
"""获取 LLM 元数据。"""
return LLMMetadata(
context_window=self.context_window,
num_output=self.num_output,
model_name=self.model_name,
)
@llm_completion_callback()
def complete(self, prompt: str, **kwargs: Any) -> CompletionResponse:
return CompletionResponse(text=self.dummy_response)
@llm_completion_callback()
def stream_complete(
self, prompt: str, **kwargs: Any
) -> CompletionResponseGen:
response = ""
for token in self.dummy_response:
response += token
yield CompletionResponse(text=response, delta=token)
# 定义我们的 LLM
Settings.llm = OurLLM()
# 定义嵌入模型
Settings.embed_model = "local:BAAI/bge-base-en-v1.5"
# 加载您的数据
documents = SimpleDirectoryReader("./data").load_data()
index = SummaryIndex.from_documents(documents)
# 查询并打印响应
query_engine = index.as_query_engine()
response = query_engine.query("<query_text>")
print(response)
使用这种方法,您可以使用任何 LLM。也许您有一个在本地运行的模型,或者在自己的服务器上运行的模型。只要类被实现并且生成的标记被返回,它应该能够正常工作。请注意,我们需要使用提示助手来自定义提示的大小,因为每个模型的上下文长度略有不同。
装饰器是可选的,但通过 LLM 调用的回调提供了可观察性。
请注意,您可能需要调整内部提示以获得良好的性能。即使这样,您也应该使用足够大的 LLM 来确保它能够处理 LlamaIndex 在内部使用的复杂查询,因此您的效果可能会有所不同。