LlamaIndex 系列(四):入门案例(阿里云百炼适配)
本文以阿里云百炼为模型底座,演示 LlamaIndex 入门案例:OpenAILike 接入通义千问、FunctionAgent 工具调用智能体、Context 多轮对话记忆、RAG 文档检索与索引持久化,并给出完整可运行代码。
一、项目背景
大模型落地应用时,仅靠模型自身的通用能力远远不够:企业私有数据(设备手册、工艺文件、生产记录)如何被模型理解并正确引用,才是工程化的核心问题。LlamaIndex 正是为解决这一问题而生的数据框架,它把文档解析、分块、向量化、检索、提示词组装等繁琐环节封装成标准组件,让开发者专注于业务逻辑本身。
在国内生产环境中,直接调用海外模型服务存在网络与合规成本。阿里云百炼平台提供 OpenAI 兼容接口与免费额度,配合通义千问系列模型,是国产化适配的主流选择。本系列文章以 LlamaIndex 为骨架、阿里云百炼为模型底座,从零演示一个可运行的入门案例,覆盖:工具调用智能体、多轮对话记忆、RAG 检索与索引持久化。
二、技术方案
很多开发者把 LlamaIndex 简单等同于 RAG 框架,但官方将数据增强型大模型应用归纳为五大核心形态:
- Agents 智能体:由 LLM 驱动的自主决策程序,绑定各类工具与记忆组件,运行推理循环动态选择执行动作,适合复杂开放式任务
- Workflows 工作流:事件驱动的通用编排底座,用于组织多阶段逻辑与 LLM 调用,是框架底层的核心抽象
- 结构化数据提取:依托 Pydantic 定义目标结构,从 PDF、网页等非结构化文档中类型安全地提取标准化信息
- Query Engines 查询引擎:端到端单次问答链路,标准 RAG 实现载体
- Chat Engines 对话引擎:面向多轮交互会话,自动维护历史上下文
理解这五大形态,才能跳出 Demo,设计出可落地的生产级应用。本案例的技术选型如下:
- 模型接入:使用 OpenAILike 连接百炼的 OpenAI 兼容端点,注意参数名是
api_base(传base_url会被静默忽略) - 智能体:FunctionAgent + 自定义工具函数,要求模型支持原生 function calling(百炼 qwen-plus / qwen-max / qwen-turbo 均支持)
- 多轮记忆:Context 对象在多次 run() 调用之间持久化会话上下文
- RAG 检索:SimpleDirectoryReader 读取文档 → VectorStoreIndex 建索引 → query_engine 检索;embedding 使用百炼 text-embedding-v3(1024 维,中英文)
- 索引持久化:StorageContext + load_index_from_storage,避免每次启动重复解析文档与计算向量
环境要求:Python >= 3.10 且 < 4.0。安装有三种方式:
- Pip 快速安装:
pip install llama-index(基础包含 core、llms-openai、embeddings-openai、readers-file) - 自定义按需安装:如 Ollama 本地模型 + HuggingFace Embedding,只装
llama-index-core等必要包 - 源码编译安装:clone 仓库后以 poetry 安装核心库与各集成包
三、系统架构
整体分为三层:
- 模型层:阿里云百炼通义千问负责生成与工具决策,text-embedding-v3 负责语义向量化,均经 OpenAI 兼容端点接入
- 编排层:FunctionAgent 推理循环决定"调用哪个工具 / 是否检索文档",Context 维护跨轮记忆,Workflow 承载复杂流程编排
- 数据层:SimpleDirectoryReader 读取本地文档 → VectorStoreIndex 构建索引 → query_engine 执行检索 → StorageContext 持久化到 storage/ 目录
数据流转:用户提问 → 智能体判断意图并选择动作(工具调用或文档检索)→ 执行结果回填提示词 → LLM 生成最终答案返回。
四、实施过程
4.1 环境配置
获取百炼 API Key 后写入 .env 文件(比 export 更稳定,不受终端与启动方式影响),并安装 python-dotenv:
pip install python-dotenv
DASHSCOPE_API_KEY=sk-xxx
4.2 基础智能体:工具调用
创建 starter.py,实现一个具备乘法计算工具的智能体:
import asyncio
import os
from dotenv import load_dotenv
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai_like import OpenAILike
load_dotenv()
api_key = os.environ["DASHSCOPE_API_KEY"]
llm = OpenAILike(
model="qwen-plus",
api_key=api_key,
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
is_chat_model=True,
is_function_calling_model=True,
)
def multiply(a: float, b: float) -> float:
"""两个数字相乘"""
return a * b
agent = FunctionAgent(
tools=[multiply],
llm=llm,
system_prompt="你是助手,可以完成两个数字相乘计算。",
)
async def main():
response = await agent.run("1234 * 4567 等于多少?")
print(str(response))
if __name__ == "__main__":
asyncio.run(main())
运行输出:1234 × 4567 = 5,635,678。
执行流程:用户问题 + 工具描述传入 LLM → 模型选择工具并填充参数 → 执行函数 → 整合结果生成回答。框架推荐异步写法,可提升应用并发性能。
4.3 增加多轮对话记忆
依靠 Context 对象持久化会话上下文:同一个 ctx 上的多次 run() 共享对话记忆;不传 ctx 则每次都是全新会话。
import asyncio
import os
from dotenv import load_dotenv
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.workflow import Context
from llama_index.llms.openai_like import OpenAILike
load_dotenv()
llm = OpenAILike(
model="qwen-plus",
api_key=os.environ["DASHSCOPE_API_KEY"],
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
is_chat_model=True,
is_function_calling_model=True,
)
def multiply(a: float, b: float) -> float:
"""两个数字相乘"""
return a * b
agent = FunctionAgent(
tools=[multiply],
llm=llm,
system_prompt="你是助手,可以完成两个数字相乘计算。",
)
async def main():
ctx = Context(agent)
response = await agent.run("我叫Logan", ctx=ctx)
print("第1轮:", str(response))
response = await agent.run("我的名字是什么?", ctx=ctx)
print("第2轮:", str(response))
response = await agent.run("我叫Logan,12乘以12等于多少?", ctx=ctx)
print("第3轮:", str(response))
response = await agent.run("我的名字是什么?")
print("无ctx对照:", str(response))
if __name__ == "__main__":
asyncio.run(main())
执行效果:第 1 轮打招呼;第 2 轮能答出名字(记忆生效);第 3 轮记忆与工具调用共存;无 ctx 对照轮完全不记得之前对话。Context 内部维护会话的 memory(对话历史)与 state,跨 run() 保留,多智能体场景下还可作为共享黑板传递结构化状态。
4.4 为智能体接入 RAG 检索能力
准备测试文档:将任意 .txt 文件放入 data/ 目录即可(示例使用官方 Paul Graham 随笔)。注意:RAG 依赖 embedding 模型,若不配置框架会默认回退到 OpenAI 并报错,必须把 LLM 与 embedding 同时切到百炼。
import asyncio
import os
from dotenv import load_dotenv
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.embeddings.openai_like import OpenAILikeEmbedding
from llama_index.llms.openai_like import OpenAILike
load_dotenv()
API_KEY = os.environ["DASHSCOPE_API_KEY"]
API_BASE = "https://dashscope.aliyuncs.com/compatible-mode/v1"
Settings.llm = OpenAILike(
model="qwen-plus",
api_key=API_KEY,
api_base=API_BASE,
is_chat_model=True,
is_function_calling_model=True,
)
Settings.embed_model = OpenAILikeEmbedding(
model_name="text-embedding-v3",
api_key=API_KEY,
api_base=API_BASE,
)
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents, show_progress=True)
query_engine = index.as_query_engine()
def multiply(a: float, b: float) -> float:
"""两个数字相乘"""
return a * b
async def search_documents(query: str) -> str:
"""检索本地文档"""
response = await query_engine.aquery(query)
return str(response)
agent = FunctionAgent(
tools=[multiply, search_documents],
llm=Settings.llm,
system_prompt="你可以进行数学计算,也可以检索文档回答问题。",
)
async def main():
response = await agent.run("作者大学时期做了什么?7乘以8等于多少?")
print(response)
if __name__ == "__main__":
asyncio.run(main())
智能体会自动判断何时调用计算工具、何时检索文档,一次提问同时完成"文档问答 + 数学计算"两类任务。
4.5 RAG 索引持久化
避免每次启动重复解析文档,推荐"有缓存则加载、无则构建并保存"的模式:
import asyncio
import os
from pathlib import Path
from dotenv import load_dotenv
from llama_index.core import (
Settings, SimpleDirectoryReader, StorageContext,
VectorStoreIndex, load_index_from_storage,
)
from llama_index.embeddings.openai_like import OpenAILikeEmbedding
from llama_index.llms.openai_like import OpenAILike
load_dotenv()
API_KEY = os.environ["DASHSCOPE_API_KEY"]
API_BASE = "https://dashscope.aliyuncs.com/compatible-mode/v1"
Settings.llm = OpenAILike(
model="qwen-plus", api_key=API_KEY, api_base=API_BASE,
is_chat_model=True, is_function_calling_model=True,
)
Settings.embed_model = OpenAILikeEmbedding(
model_name="text-embedding-v3", api_key=API_KEY, api_base=API_BASE,
)
PERSIST_DIR = "./storage"
if Path(PERSIST_DIR).exists() and any(Path(PERSIST_DIR).iterdir()):
print(">> 检测到已持久化的索引,直接加载(跳过文档解析)...")
storage_context = StorageContext.from_defaults(persist_dir=PERSIST_DIR)
index = load_index_from_storage(storage_context)
else:
print(">> 首次运行:解析文档并构建索引...")
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents, show_progress=True)
index.storage_context.persist(persist_dir=PERSIST_DIR)
print(f">> 索引已保存到 {PERSIST_DIR}")
query_engine = index.as_query_engine()
async def main():
response = await query_engine.aquery("作者大学时期做了什么?")
print(response)
if __name__ == "__main__":
asyncio.run(main())
以 75KB 文档实测,storage/ 目录共五个文件:
| 文件 | 大小 | 内容 |
|---|---|---|
| docstore.json | 137KB | 文档库:分块文本、节点元数据、分块与源文档的溯源关系 |
| index_store.json | 2KB | 索引结构:节点与索引、节点与向量的映射 |
| default__vector_store.json | 501KB | 向量数据(体积最大):文本向量、向量到源文档映射 |
| graph_store.json | 18B | 知识图谱存储(本例未建图索引,为空壳) |
| image__vector_store.json | 72B | 图像向量存储(本例无图像,为空壳) |
两点提醒:
- 即使加载已持久化的索引,查询时仍要用 embedding 模型向量化问题,因此 Settings.embed_model 在加载路径同样必需,省掉会在查询时报错
- 若使用第三方向量数据库,可用
VectorStoreIndex.from_vector_store(vector_store)重建索引,注意向量维度需与 text-embedding-v3 的 1024 维对齐
文档越大,解析与 embedding 成本越高,持久化的收益越明显。
4.6 拓展方向
- 扩展更多自定义工具
- 切换各类开源 / 闭源大模型
- 通过系统提示词定制智能体行为
- 开启流式输出
- 搭建人机交互工作流
- 实现多智能体协同系统
五、应用价值
- 工业知识问答:设备手册、工艺文件、质检标准的私有数据检索问答,替代人工翻阅
- 文档自动化:利用结构化数据提取能力,批量处理 PDF / 网页中的表单与参数信息
- 智能体应用:将工具调用能力接入 MES 查询、SCADA 取数、报表生成等系统,实现自主作业
- 国产化适配:百炼平台免费额度 + 通义千问,成本可控、数据合规
- 工程效率:索引持久化避免重复解析与向量化,降低启动开销,便于生产部署
六、SEO关键词
LlamaIndex、阿里云百炼、RAG、FunctionAgent、智能体、通义千问、OpenAILike、text-embedding-v3、向量检索、多轮对话记忆
