1. 项目概述
在构建基于大语言模型(LLM)的应用时,LlamaIndex是一个非常强大的框架,它提供了数据索引、检索和查询的完整解决方案。今天我要分享的是如何在LlamaIndex中实现ReAct Agent,特别是如何通过自定义LLM来适配国内的大模型服务。
ReAct(Reasoning and Acting)是一种让LLM能够进行多步推理和行动的模式,它结合了推理链(Chain-of-Thought)和工具使用(Tool Use)的能力。通过LlamaIndex的ReAct Agent,我们可以构建能够自主选择工具、执行多步查询的智能系统。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心组件解析
2.1 自定义LLM实现
要让LlamaIndex支持国内的大模型(如Qwen、Deepseek等),我们需要实现一个自定义LLM类。这本质上是一个适配器模式(Adapter Pattern)的应用,将国内模型的API接口"翻译"成LlamaIndex能够理解的格式。
python复制class MyLLM(CustomLLM):
"""适配LlamaIndex的LLM自定义类"""
api_key: str = "xxx"
base_url: str = "xxx"
model_name: str = "xxx"
timeout: int = 120
callback_manager: CallbackManager = Field(default_factory=CallbackManager, exclude=True)
这里有几个关键点需要注意:
api_key和base_url是连接模型服务所需的凭证和地址callback_manager用于处理LLM调用过程中的回调事件,exclude=True表示这个字段不会被序列化timeout设置了API调用的超时时间,避免长时间等待
2.2 元数据配置
元数据定义了模型的基本能力,这对LlamaIndex正确使用模型至关重要:
python复制@property
def metadata(self) -> LLMMetadata:
return LLMMetadata(
context_window=32768, # 模型支持的最大上下文长度
num_output=4096, # 模型单次输出的最大token数
model_name=self.model_name,
)
这个配置告诉LlamaIndex:
- 模型能处理的最大上下文长度(32k tokens)
- 单次生成的最大token数(4k)
- 模型名称
2.3 同步与流式接口
自定义LLM需要实现两个核心方法:同步调用(complete)和流式调用(stream_complete)。
同步接口适合简单的问答场景:
python复制@llm_completion_callback()
def complete(self, prompt: str, **kwargs: Any) -> CompletionResponse:
response = self.Chat(prompt=prompt, steam=False)
content = response if isinstance(response, str) else response.choices[0].message.content
return CompletionResponse(text=content)
流式接口则适合需要实时显示结果的场景:
python复制@llm_completion_callback()
def stream_complete(self, prompt: str, **kwargs: Any) -> CompletionResponseGen:
response = self.Chat(prompt=prompt,stream=True)
def response_generator():
full_content = ""
for chunk in response:
if chunk.choices and chunk.choices[0].delta.content:
delta = chunk.choices[0].delta.content
full_content += delta
yield CompletionResponse(text=full_content, delta=delta)
return response_generator()
流式接口的实现使用了Python的生成器(generator),可以逐步返回结果,实现打字机效果。
2.4 底层请求实现
实际的API调用封装在Chat方法中:
python复制def Chat(self, prompt: str, stream: bool):
client = OpenAI(api_key=self.api_key, base_url=self.base_url, timeout=self.timeout)
try:
return client.chat.completions.create(
model=self.model_name,
messages=[{"role": "system", "content": "你是一个聪明的AI助手"},
{"role": "user", "content": prompt}],
max_tokens=4096,
temperature=0.7,
stream=stream
)
except Exception as e:
logging.error(f"LLM API调用失败: {e}")
raise
这里使用了OpenAI SDK的格式,但base_url可以指向任何兼容OpenAI API的国内模型服务。
3. ReAct Agent实现
3.1 基础查询引擎
在构建ReAct Agent之前,我们需要先创建基础的查询引擎:
python复制# 初始化组件
Settings.llm = MyLLM()
Settings.embed_model = HuggingFaceEmbedding(model_name="./models/bge-base-zh-v1.5")
# 构建索引
documents = SimpleDirectoryReader(input_files=["./data/A.txt"]).load_data()
index = VectorStoreIndex.from_documents(documents)
# 创建查询引擎
query_engine = index.as_query_engine(streaming=True)
这个流程包括:
- 设置自定义LLM和嵌入模型
- 从文档加载数据并构建向量索引
- 创建支持流式输出的查询引擎
3.2 多文档索引与持久化
对于更复杂的场景,我们可以处理多个文档并持久化索引:
python复制# 加载多个文档
A_docs = SimpleDirectoryReader(input_files=["./data/A.txt"]).load_data()
B_docs = SimpleDirectoryReader(input_files=["./data/B.txt"]).load_data()
# 创建并持久化索引
A_index = VectorStoreIndex.from_documents(A_docs)
B_index = VectorStoreIndex.from_documents(B_docs)
A_index.storage_context.persist(persist_dir="./storage/A")
B_index.storage_context.persist(persist_dir="./storage/B")
持久化后可以从磁盘加载:
python复制storage_context = StorageContext().from_defaults(persist_dir="./storage/A")
A_index = load_index_from_storage(storage_context=storage_context)
3.3 构建查询工具
ReAct Agent的核心是能够使用工具,我们需要为每个索引创建查询工具:
python复制query_engine_tools = [
QueryEngineTool(
query_engine=A_engine,
metadata=ToolMetadata(
name="A_Report",
description="用于检索可乐相关信息的工具"
)
),
QueryEngineTool(
query_engine=B_engine,
metadata=ToolMetadata(
name="B_Report",
description="用于检索雪碧相关信息的工具"
)
)
]
每个工具都有名称和描述,Agent会根据这些描述决定何时使用哪个工具。
3.4 创建ReAct Agent
最后,我们可以创建ReAct Agent实例:
python复制agent = ReActAgent(tools=query_engine_tools, llm=llm, verbose=True)
参数说明:
tools: 可用的工具列表llm: 自定义的LLM实例verbose: 是否显示详细推理过程
4. 实际应用与问题排查
4.1 执行查询
使用Agent进行查询非常简单:
python复制response = agent.run("可乐和雪碧哪个更好喝,为什么?详细描述它们各自的优点是什么?")
Agent会自动决定:
- 是否需要使用工具
- 使用哪个工具
- 如何组合多个工具的返回结果
4.2 常见问题与解决
-
API连接失败
- 检查
base_url是否正确 - 确认API密钥有效
- 检查网络连接是否正常
- 检查
-
索引加载失败
- 确认持久化目录存在
- 检查存储的索引版本是否与LlamaIndex版本兼容
-
工具选择不当
- 优化工具的描述文本
- 调整LLM的温度参数(temperature)
-
上下文长度不足
- 检查
metadata中的context_window设置 - 考虑使用更小的chunk size分割文档
- 检查
4.3 性能优化建议
- 批处理请求:对于大量文档,考虑批量处理
- 缓存机制:对频繁查询的结果进行缓存
- 异步处理:对于耗时操作使用异步方式
- 监控指标:记录查询延迟、token使用等指标
5. 扩展应用
这个框架不仅限于文档查询,还可以扩展到:
- 知识库问答系统:构建企业知识库
- 数据分析助手:连接数据库查询工具
- 自动化工作流:集成各种API工具
- 智能客服系统:结合对话管理
通过自定义更多的工具类型,ReAct Agent可以完成更复杂的任务。比如添加:
- 计算器工具
- 网络搜索工具
- 数据库查询工具
- API调用工具
每个工具只需要实现简单的接口,Agent就能学会在适当的时候使用它们。
