展开本页目录
开始前准备什么
- Python 3.10+,安装独立依赖而不是假设 llama-index 默认包已经包含 OpenAILike。
- apizt Key、模型权限,以及已确认的通道上下文与输出限制。
- 使用脱敏、可核对的小样本资料,准备一个有答案和一个无答案的问题。
- 配置 OpenAILike
- 载入短资料
- 返回答案与来源
- 验收边界
- 增加检索
两段明确标注的测试资料;不调用 Embedding。
llama-index-llms-openai-like 0.8.1 / openai 2.54.0。
把 OpenAILike 的能力声明写准确
| 配置 | 样例值 | 含义 |
|---|---|---|
| model | glm-5.3 | 从模型目录复制准确 ID。 |
| api_base | https://api.apizt.com/v1 | SDK 前缀;不追加 /chat/completions。 |
| api_key | tokenba_API_KEY | 从环境变量读取。 |
| is_chat_model | True | 使用 Chat 消息接口。 |
| is_function_calling_model | False | 本例不使用工具;不是对整个型号能力的判断。 |
| context_window | 32000 或环境变量 | 人为保守规划值,不能扩大服务端限制。 |
| max_tokens | 1024 | 试跑输出预算,按通道核对。 |
| timeout / max_retries | 60 秒 / 0 | 首调设置,不代表绝不发生重复业务操作。 |
- 配置
- model
- 样例值
- glm-5.3
- 含义
- 从模型目录复制准确 ID。
- 配置
- api_base
- 样例值
- https://api.apizt.com/v1
- 含义
- SDK 前缀;不追加 /chat/completions。
- 配置
- api_key
- 样例值
- tokenba_API_KEY
- 含义
- 从环境变量读取。
- 配置
- is_chat_model
- 样例值
- True
- 含义
- 使用 Chat 消息接口。
- 配置
- is_function_calling_model
- 样例值
- False
- 含义
- 本例不使用工具;不是对整个型号能力的判断。
- 配置
- context_window
- 样例值
- 32000 或环境变量
- 含义
- 人为保守规划值,不能扩大服务端限制。
- 配置
- max_tokens
- 样例值
- 1024
- 含义
- 试跑输出预算,按通道核对。
- 配置
- timeout / max_retries
- 样例值
- 60 秒 / 0
- 含义
- 首调设置,不代表绝不发生重复业务操作。
安装固定的适配包
下载 Python 与 requirements 两个文件,安装 llama-index-llms-openai-like==0.8.1 与 openai==2.54.0。适配包包含所需的 LlamaIndex 核心依赖。记录实际解析的依赖版本,项目上线时再生成 lockfile。
tokenba_CONTEXT_TOKENS 是本地规划预算,默认 32000 为示例保守值。应根据当前通道确认值设置,不能把客户端字段当作官方规格或提额方式。
terminal
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r framework-llamaindex-requirements.txt
export tokenba_API_KEY="YOUR_tokenba_API_KEY"
python framework-llamaindex.py完整示例:答案要能追溯到资料
这两段「示例商店」规则是人工编写的测试资料。SummaryIndex 默认取出全部节点,让你先检查资料链;这里没有向量检索,也没有生成或查询 Embedding。样例显式检索候选后,调用一次 llm.chat,并从原始响应检查 usage 与截断。
问题询问定制商品是否适用退货规则。应依据 policy-02 回答定制商品不适用,并保留来源标记。打印的 source_ids 只是送入模型的候选资料,不等于模型真正引用了它;最后仍要检查答案中的 [policy-02] 与资料一致。
framework-llamaindex.py
import os
from llama_index.core import Document, SummaryIndex
from llama_index.core.llms import ChatMessage
from llama_index.core.node_parser import SentenceSplitter
from llama_index.llms.openai_like import OpenAILike
def main():
llm = OpenAILike(
model=os.getenv("tokenba_MODEL", "glm-5.3"),
api_base=os.getenv("tokenba_BASE_URL", "https://api.apizt.com/v1"),
api_key=os.environ["tokenba_API_KEY"],
is_chat_model=True,
is_function_calling_model=False,
context_window=int(os.getenv("tokenba_CONTEXT_TOKENS", "32000")),
max_tokens=1024,
timeout=60,
max_retries=0,
)
documents = [
Document(text="[policy-01] 示例商店:未使用的商品可在收货后 7 天内申请退货。", metadata={"source_id": "policy-01"}),
Document(text="[policy-02] 示例商店:定制商品不适用上述退货规则;质量问题联系人工客服。", metadata={"source_id": "policy-02"}),
]
index = SummaryIndex.from_documents(
documents,
transformations=[SentenceSplitter(chunk_size=512, chunk_overlap=32)],
)
question = "定制商品能在 7 天内无理由退货吗?"
candidates = index.as_retriever().retrieve(question)
context = "\n".join(node.node.get_content() for node in candidates)
result = llm.chat([
ChatMessage(role="system", content="只根据资料回答。资料不足时说无法确认,答案引用 [source_id]。"),
ChatMessage(role="user", content=f"资料:\n{context}\n问题:{question}"),
])
answer = result.message.content
raw = result.raw.model_dump()
finish_reason = raw["choices"][0]["finish_reason"]
print("answer:", answer)
print("source_ids:", [node.node.metadata.get("source_id") for node in candidates])
print("finish_reason:", finish_reason)
print("usage:", raw.get("usage"))
if not isinstance(answer, str) or not answer.strip():
raise RuntimeError("答案为空;先检查 API 记录与模型配置。")
if finish_reason == "length":
raise RuntimeError("输出被截断;先核对账单,再调整输出预算。")
if __name__ == "__main__":
main()规模变大后,单独设计检索层
| 方案 | 适合什么 | 新增成本与风险 |
|---|---|---|
| 本页 SummaryIndex | 两段短资料、检查连接与引用 | 默认返回全部节点,长资料会增加输入或分成多次生成。 |
| 向量索引 | 需要按问题筛选大量片段 | 另行配置 Embedding 型号、维度、重建索引与召回数量。 |
| 混合检索 / Rerank | 关键词与语义召回组合 | 检索、重排、生成分别有耗时与可能的 API 费用。 |
| 多租户知识库 | 按用户或组织过滤 | 在检索前做权限过滤,不能依赖模型提示词保护资料。 |
- 方案
- 本页 SummaryIndex
- 适合什么
- 两段短资料、检查连接与引用
- 新增成本与风险
- 默认返回全部节点,长资料会增加输入或分成多次生成。
- 方案
- 向量索引
- 适合什么
- 需要按问题筛选大量片段
- 新增成本与风险
- 另行配置 Embedding 型号、维度、重建索引与召回数量。
- 方案
- 混合检索 / Rerank
- 适合什么
- 关键词与语义召回组合
- 新增成本与风险
- 检索、重排、生成分别有耗时与可能的 API 费用。
- 方案
- 多租户知识库
- 适合什么
- 按用户或组织过滤
- 新增成本与风险
- 在检索前做权限过滤,不能依赖模型提示词保护资料。
用四类问题检查引用与拒答
检索候选与最终引用都保存,但不要只检查 response 不为空。对数字、期限与否定词设置业务验收规则;模型答案可能通顺却引用错误。
- 直接命中:问普通商品的退货期限,核对 7 天与 policy-01。
- 例外规则:问定制商品,核对例外与 policy-02,不套用普通商品期限。
- 资料缺失:问海外运费,应说明无法从给定资料确认。
- 冲突资料:另建测试副本加入不同规则,要求指出冲突并交给人工确认。
统计整条查询,而不是一个 token 数
输入预算包括系统规则、提问、资料节点和历史,输出还要预留余量。本例显式检索后发出一次 Chat 请求。以后若改成 query_engine、多段合成或工具回路,一次用户查询可能产生多次模型调用。
样例打印本次原始 usage、finish_reason 和候选 source_ids。实际费用仍要查看全部请求记录、重试和控制台实扣;不能用候选节点数量推算 token 账单。usage 缺失时保留未知,不把它记成零成本。
常见错误与定位路径
| 错误 | 优先排查 |
|---|---|
| No module named openai_like | 安装专用适配包,并确认当前 Python 在同一个虚拟环境。 |
| Unknown model / metadata error | 使用 OpenAILike,明确 Chat 和上下文元数据;不是硬塞到官方 OpenAI 型号映射。 |
| 意外请求 Embedding | 检查是否改用了向量索引、Embedding Retriever 或默认 Settings.embed_model。 |
| context length exceeded | 缩短资料、减少召回并预留输出;提高客户端 context_window 不能突破通道限制。 |
| 有来源但答案不支持 | 分别检查候选节点、提示模板和引用内容;候选来源不是正确性的证明。 |
| 超时 / 费用超预期 | 检查实际请求数量与合成模式,先查记录再重试。 |
- 错误
- No module named openai_like
- 优先排查
- 安装专用适配包,并确认当前 Python 在同一个虚拟环境。
- 错误
- Unknown model / metadata error
- 优先排查
- 使用 OpenAILike,明确 Chat 和上下文元数据;不是硬塞到官方 OpenAI 型号映射。
- 错误
- 意外请求 Embedding
- 优先排查
- 检查是否改用了向量索引、Embedding Retriever 或默认 Settings.embed_model。
- 错误
- context length exceeded
- 优先排查
- 缩短资料、减少召回并预留输出;提高客户端 context_window 不能突破通道限制。
- 错误
- 有来源但答案不支持
- 优先排查
- 分别检查候选节点、提示模板和引用内容;候选来源不是正确性的证明。
- 错误
- 超时 / 费用超预期
- 优先排查
- 检查实际请求数量与合成模式,先查记录再重试。
怎样确认已经完成
- 例外规则回答依据 policy-02;资料缺失的问题明确无法确认。
- source_ids 与答案引用分别核对,不把候选节点当有效证据。
- 样例没有调用 Embedding;换检索器后重新核对实际请求。
- 检查 finish_reason 与 usage,核对这次查询的实际 API 记录与费用;缺失 usage 不显示零成本。
常见问题
OpenAILike 是把任意模型伪装成 OpenAI 吗?
它提供面向兼容接口的适配与元数据声明。你仍需确认 model、Chat 协议、工具能力和通道参数;声明 True 不会让服务端获得原本没有的能力。
这个示例是完整的向量 RAG 吗?
不是。本页用 SummaryIndex 的全部候选资料检查生成与引用链,刻意不引入第二个 Embedding API。真正的向量检索需要单独建立索引和验证召回。
我可以把 PDF 直接传给 glm-5.3 吗?
本页仅传入提取后的文字,不承诺文件或视觉能力。文字层 PDF 可以先提取带页码的文本,再进行带出处的信息提取;扫描 PDF 还需要 OCR 或已验证的视觉模型。