当前位置: 首页 > ds >正文

基于Llama的RAG 3种模型配置方法

目录

方法一:使用Llama-index为智谱构建的专门的包

安装六个包

环境变量读取APIkey

配置对话模型

测试对话模型

流式输出

出现的原因

解决方案

配置嵌入模型

测试嵌入模型

方法二:借助OpenAI的库接入智谱

重写类

重写OpenAI类

用NewOpenAI这个类来配置llm

测试对话模型

修改源码

修改对话模型源码

修改嵌入模型源码

方法三:自定义模型接口

确认电脑IP

在本地启动Ollama服务

参考文章


RAG(检索增强生成)是结合检索与生成式 AI 的技术框架。核心逻辑是先从外部知识库精准检索相关信息,再将其作为上下文输入大模型生成回答。技术上依赖检索引擎(如向量数据库、BM25)、大语言模型(如 GPT、LLaMA)及数据预处理技术。通过检索增强,解决大模型知识滞后、幻觉问题,提升回答准确性。应用广泛,涵盖智能客服、医疗问答、法律检索、教育辅导等场景,能基于特定领域知识提供精准、可控的生成内容。

wow-RAG 是 Datawhale 推出的 RAG 技术实践项目,网址:datawhalechina/wow-rag: A simple and trans-platform rag framework and tutorial    https://github.com/datawhalechina/wow-rag

方法一:使用Llama-index为智谱构建的专门的包

安装六个包

%pip install llama-index-core
%pip install llama-index-embeddings-zhipuai
%pip install llama-index-llms-zhipuai
%pip install llama-index-readers-file
%pip install llama-index-vector-stores-faiss
%pip install llamaindex-py-client

安装的库分别支持:从文件中读取知识内容;使用嵌入模型为文本生成向量;将向量存入 FAISS 数据库进行高效检索;在用户提问时,召回最相关的文本块;使用大语言模型(如 GLM)根据上下文生成高质量回答。

环境变量读取APIkey

import os
from dotenv import load_dotenv# 加载环境变量
load_dotenv()
# 从环境变量中读取api_key
api_key = os.getenv('ZHIPU_API_KEY')
base_url = "https://open.bigmodel.cn/api/paas/v4/"
chat_model = "glm-4-flash"
emb_model = "embedding-2"

配置对话模型

from llama_index.llms.zhipuai import ZhipuAI
llm = ZhipuAI(api_key = api_key,model = chat_model,
)

测试对话模型

# 测试对话模型
response = llm.complete("你是哪位?你是干什么的?")
print(response)

调用了 llm 对象的 complete 方法,向大语言模型发送一条提示词。complete 是 LlamaIndex 中用于触发文本生成的标准方法。模型接收到请求后会返回生成的回复内容,并将其赋值给变量 response。 

流式输出

response = llm.stream_complete("你是谁?")
for chunk in response:print('\n')print(chunk, end="",flush=True)

与普通的 complete 方法不同,stream_complete 会以流式(streaming)方式获取模型的生成结果,这意味着模型在生成回答的过程中会逐步返回中间结果(即每个文本块 chunk),而不是等到全部内容生成完毕才一次性返回。

使用 for 循环遍历 response 中的每一个 chunk,即模型生成的一小段文本。每次接收到新的文本块时,先打印一个换行符 \n,以便每个新接收的文本块单独显示一行;然后使用 print(chunk, end="", flush=True) 将当前文本块输出到控制台,end="" 表示不自动换行,使得多个 chunk 可以连贯地拼接成完整句子,flush=True 则确保内容立即刷新显示在终端上,不会因为缓冲区未满而延迟输出。

我想体验真正的流式输出,所以我删除第一个print:

response = llm.stream_complete("你是谁?")
for chunk in response:print(chunk, end="", flush=True)

输出为:

问题出在 print('\n') 这一行。它在每次接收到一个 chunk 之前都会打印一个换行符,这就导致了每个 chunk 都被强制显示在新的一行,结果看起来就像是一次性输出了好多行,而不是我期望的“逐字打印”的流式效果。

这说明模型在生成回答时并不是一次性完整输出,而是逐块(chunk by chunk)生成文本片段,并且每个新 chunk 是对前一个内容的扩展和覆盖,而不是追加新的句子。这种行为通常出现在一些语言模型的流式接口中,尤其是在封装层没有正确处理增量更新逻辑的情况下。

出现的原因

(1)模型返回的是增量文本(delta),而非完整上下文:
每个 chunk 并不是新增的一段内容,而可能是当前模型认为完整的最新版本;
(2)封装层未做去重或上下文拼接处理:
在代码中只是简单地打印了每一个 chunk,而没有判断它是“新增内容”还是“整体替换”,所以你会看到每一段都是前面内容的重复叠加,看起来像是“越说越多”,但其实只是模型不断修正自己的输出;
(3)输出粒度过细:
有些模型会将每个词甚至字作为一个 chunk 发送回来,导致你在终端上看到一连串看似冗余的输出,这种设计是为了模拟“打字效果”,但如果前端没有做合并优化,就会出现看到的现象。

解决方案

运行以下代码就能解决问题:

response = llm.stream_complete("你是谁?")
current_output = ""for chunk in response:current_text = chunk.text  # 获取当前完整生成的文本new_part = current_text[len(current_output):]  # 只取新增部分if new_part:print(new_part, end="", flush=True)current_output = current_text  # 更新已输出内容

错误示例:

response = llm.stream_complete("你是谁?")
current_output = ""
for chunk in response:new_part = chunk[len(current_output):]  # 只取出新增部分if new_part:print(new_part, end="", flush=True)current_output = chunk

会报错:TypeError Traceback (most recent call last) Cell In[10], line 4 2 current_output = "" 3 for chunk in response: ----> 4 new_part = chunk[len(current_output):] # 只取出新增部分 5 if new_part: 6 print(new_part, end="", flush=True) TypeError: 'CompletionResponse' object is not subscriptable

TypeError: 'CompletionResponse' object is not subscriptable
说明你在尝试对一个 CompletionResponse 类型的对象使用索引操作(即 chunk[len(current_output):] 中的 [] 操作),但该对象并不支持这种类似字符串或列表的切片行为。应该先提取出 chunk 对象中的实际文本字段(通常是 .text 或 .delta.content,具体取决于模型封装方式),然后再进行比较和输出控制。

配置嵌入模型

# 配置嵌入模型
from llama_index.embeddings.zhipuai import ZhipuAIEmbedding
embedding = ZhipuAIEmbedding(api_key = api_key,model = emb_model,
)

测试嵌入模型

# 测试嵌入模型
emb = embedding.get_text_embedding("你好呀呀")
len(emb), type(emb)

配置成功

方法二:借助OpenAI的库接入智谱

核心目的是绕过 LlamaIndex 对模型上下文长度的硬编码限制检查,从而实现使用非官方支持的 OpenAI 兼容接口(如 Zhipu AI、Moonshot、阿里云百炼等)的大语言模型。

重写类

重写OpenAI类

from llama_index.llms.openai import OpenAI
from llama_index.core.base.llms.types import LLMMetadata,MessageRole
class NewOpenAI(OpenAI):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)@propertydef metadata(self) -> LLMMetadata:# 创建一个新的LLMMetadata实例,只修改context_windowreturn LLMMetadata(context_window=8192,num_output=self.max_tokens or -1,is_chat_model=True,is_function_calling_model=True,model_name=self.model,system_role=MessageRole.USER,)
class NewOpenAI(OpenAI):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)

构造函数只是简单地调用了父类的初始化方法,不做任何修改,保持原有行为一致。

@propertydef metadata(self) -> LLMMetadata:return LLMMetadata(context_window=8192,num_output=self.max_tokens or -1,is_chat_model=True,is_function_calling_model=True,model_name=self.model,system_role=MessageRole.USER,)

metadata 是一个只读属性,LlamaIndex 在运行时会用它来判断当前模型的能力,比如:

  • 支持多长的上下文输入?
  • 最多能输出多少个 token?
  • 是否是 chat 模型?
  • 是否支持 function calling?

默认情况下,LlamaIndex 的 OpenAI 类会根据 model 名称查找对应的上下文窗口长度(context_window),如果找不到就会报错。而通过重写这个属性,直接指定了一个固定的 context_window=8192,跳过了模型名称匹配的过程,从而绕开了限制。

用NewOpenAI这个类来配置llm

llm = NewOpenAI(temperature=0.95,api_key=api_key,model=chat_model,api_base=base_url
)

temperature=0.95:控制生成文本的随机性,数值越高越发散,越低越保守。
api_key=api_key:访问 API 所需的身份认证密钥,从环境变量中获取。我们的存放在.env里面。
model=chat_model:指定使用的模型名称,例如 "glm-4-flash"。
api_base=base_url:指定要调用的第三方 API 地址,这里是智谱AI的 base URL。

测试对话模型

response = llm.complete("你是谁?")
print(response)

同样也可以用流式输出,步骤同上

修改源码

修改对话模型源码

找到这个文件:Lib\site-packages\llama_index\llms\openai\utils.py

在大约第30行,有一个GPT4_MODELS: Dict[str, int]

在这里面列举了很多模型的名称,把智谱的glm-4-flash加进入,变成这样:

"gpt-4": 8192,

"gpt-4-32k": 32768,

改成:

"gpt-4": 8192,

"glm-4-flash": 8192,

"gpt-4-32k": 32768,

修改嵌入模型源码

改源码:

Lib\site-packages\llama_index\embeddings\openai\base.py

一共改四个地方

class OpenAIEmbeddingModelType(str, Enum):

最下面增加

EMBED_2 = "embedding-2"

class OpenAIEmbeddingModeModel(str, Enum):

最下面增加

EMBED_2 = "embedding-2"

_QUERY_MODE_MODEL_DICT = {

最下面增加

(OAEM.TEXT_SEARCH_MODE, "embedding-2"): OAEMM.EMBED_2,

   

_TEXT_MODE_MODEL_DICT = {

最下面增加

(OAEM.TEXT_SEARCH_MODE, "embedding-2"): OAEMM.EMBED_2,

改了上面这四个地方,再调用OpenAIEmbedding这个类,就可以正常使用了

如果怕麻烦,也可以用教程里附的base.py 直接替换。

如果不小心导入过llama_index,改完源码不要忘了重启jupyter内核。

方法三:自定义模型接口

Ollama里的qwen2系列非常棒,有很多尺寸规模的模型,最小的模型只有 0.5b,然后还有1.5b、7b、32b等等,可以适应各种设备。访问 https://ollama.com。 下载Windows版本。直接安装。 安装完成后,打开命令行窗口,输入 ollama,如果出现 Usage: Available Commands: 之类的信息,说明安装成功。我们用qwen2:7b这个模型就行,整个还不到4G。 运行 ollama run qwen2:7b如果出现了success,就说明安装成功。 然后会出现一个>>>符号,这就是对话窗口。可以直接输入问题。想要退出交互页面,直接输入 /bye 就行。斜杠是需要的。否则不是退出交互页面,而是对大模型说话,它会继续跟你聊。浏览器中输入 127.0.0.1:11434,如果出现 Ollama is running说明端口运行正常。

ollama的安装以及模型导入可以参考以下博客:

Ollama 自定义导入模型_外部下载的gguf大模型文件怎么安装到ollama里面-CSDN博客文章浏览阅读290次,点赞7次,收藏4次。nvidia-smi我的笔记本 可怜兮兮的GPU。_外部下载的gguf大模型文件怎么安装到ollama里面 https://blog.csdn.net/m0_69704099/article/details/148778969?fromshare=blogdetail&sharetype=blogdetail&sharerId=148778969&sharerefer=PC&sharesource=m0_69704099&sharefrom=from_linkOllama API-CSDN博客文章浏览阅读368次,点赞4次,收藏9次。datawhale.com 动手学Ollama。 https://blog.csdn.net/m0_69704099/article/details/148812245?fromshare=blogdetail&sharetype=blogdetail&sharerId=148812245&sharerefer=PC&sharesource=m0_69704099&sharefrom=from_linkOllama 在LangChain中的应用 Python环境-CSDN博客文章浏览阅读882次,点赞16次,收藏16次。你是一位经验丰富的电商文案撰写专家。你的任务是根据给定的产品信息创作吸引人的商品描述。请确保你的描述简洁、有力,并且突出产品的核心优势。""")请为以下产品创作一段吸引人的商品描述:产品类型: {product_type}核心特性: {key_feature}目标受众: {target_audience}价格区间: {price_range}品牌定位: {brand_positioning}请提供以下三种不同风格的描述,每种大约50字:1. 理性分析型2. 情感诉求型。 https://blog.csdn.net/m0_69704099/article/details/148855725?fromshare=blogdetail&sharetype=blogdetail&sharerId=148855725&sharerefer=PC&sharesource=m0_69704099&sharefrom=from_link

确认电脑IP

Windows: 打开命令提示符(cmd),输入 ipconfig 命令,找到你的网络适配器对应的IPv4地址。
macOS/Linux: 打开终端,输入 ifconfig 或 ip addr show 命令,查找你的网络接口对应的IPv4地址。

# 我们先用requets库来测试一下大模型
import json
import requests
# 190.168.000.12就是部署了大模型的电脑的IP,
# 请根据实际情况进行替换
BASE_URL = "http://190.168.000.12:11434/api/chat"

将请求代码中的 BASE_URL 修改为指向你当前电脑的IP地址

在本地启动Ollama服务

CMD:

set OLLAMA_HOST=0.0.0.0:11434
ollama run qwen2:7b

如果使用的是 PowerShell,设置 OLLAMA_HOST 环境变量(PowerShell):

$env:OLLAMA_HOST = "0.0.0.0:11434"
ollama run qwen2:7b

上述命令只是临时设置环境变量,只对当前终端窗口有效。关闭窗口后失效。
如果想永久设置,可以使用系统环境变量配置:
(1)搜索“编辑系统环境变量”
(2)点击“环境变量”
(3)在“用户变量”或“系统变量”中添加:
变量名:OLLAMA_HOST
变量值:0.0.0.0:11434

payload = {"model": "qwen2:7b","messages": [{"role": "user","content": "请写一篇100字左右的文章,论述法学专业的就业前景。"}]
}
response = requests.post(BASE_URL, json=payload)
print(response.text)

payload = {"model": "qwen2:7b","messages": [{"role": "user","content": "请写一篇100字左右的文章,论述法学专业的就业前景。"}],"stream": True
}
response = requests.post(BASE_URL, json=payload, stream=True)  # 在这里设置stream=True告诉requests不要立即下载响应内容  
# 检查响应状态码  
if response.status_code == 200:  # 使用iter_content()迭代响应体  for chunk in response.iter_content(chunk_size=1024):  # 你可以设置chunk_size为你想要的大小  if chunk:  # 在这里处理chunk(例如,打印、写入文件等)  rtn = json.loads(chunk.decode('utf-8')) # 假设响应是文本,并且使用UTF-8编码  print(rtn["message"]["content"], end="")
else:  print(f"Error: {response.status_code}")  # 不要忘记关闭响应  
response.close()

如果遇到报错,可以在CMD里输入:

curl http://localhost:11434/api/chat -X POST -H "Content-Type: application/json" -d "{\"model\": \"qwen2:7b\", \"messages\": [{\"role\": \"user\", \"content\": \"你好\"}]}"

如果输出为:

{"model":"qwen2:7b","created_at":"2025-07-16T04:50:25.9652222Z","message":{"role":"assistant","content":"你好"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:26.6918219Z","message":{"role":"assistant","content":"!"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:27.3889671Z","message":{"role":"assistant","content":"很高兴"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:28.0123335Z","message":{"role":"assistant","content":"能"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:28.6996211Z","message":{"role":"assistant","content":"为你"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:29.2292556Z","message":{"role":"assistant","content":"服务"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:29.7292238Z","message":{"role":"assistant","content":"。"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:30.2031634Z","message":{"role":"assistant","content":"有什么"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:30.6622801Z","message":{"role":"assistant","content":"我能"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:31.0990926Z","message":{"role":"assistant","content":"帮助"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:31.5958556Z","message":{"role":"assistant","content":"你的"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:32.0505737Z","message":{"role":"assistant","content":"吗"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:32.5099374Z","message":{"role":"assistant","content":"?"},"done":false} {"model":"qwen2:7b","created_at":"2025-07-16T04:50:32.9703164Z","message":{"role":"assistant","content":""},"done_reason":"stop","done":true,"total_duration":40135156500,"load_duration":19543345200,"prompt_eval_count":20,"prompt_eval_duration":13423753200,"eval_count":14,"eval_duration":7025921300}

则证明:Ollama 正常监听 localhost;qwen2:7b 模型加载成功;推理过程正常进行

如果代码块半天没有反应,这是因为让模型写一篇 1000 字左右的文章,需要大量的推理 token(可能超过 1500 token),而本地大模型(特别是 CPU 上运行的)速度较慢,导致:

  • 推理时间长(几十秒到几分钟)
  • 没有流式输出,所以 response.text 会等到整个响应完成才返回
  • 给人一种“卡住”或“转圈”的错觉

另外,如果刚才输入CMD的代码是:

ollama run qwen2:7b

这会进入交互聊天模式(出现 >>> 提示符),它会占用模型资源并可能导致 API 响应变慢或延迟。

建议改用:

set OLLAMA_HOST=0.0.0.0:11434
ollama serve

这样 Ollama 就只会专注于处理 API 请求,不会卡在命令行输入界面。

参考文章

https://github.com/datawhalechina/wow-raghttps://github.com/datawhalechina/wow-rag

http://www.xdnf.cn/news/15680.html

相关文章:

  • Django REST Framework 入门指南:从 0 到 1 实现 RESTful API
  • Linux-局域网构建+VLAN 划分 + 端口 MAC-IP 绑定 + 静态 DHCP
  • Python 进阶学习之全栈开发学习路线
  • 如何删除 VSCode 账号的远程同步备份记录数据
  • 如何使用VScode使用ssh连接远程服务器不需要输入密码直接登录
  • 信息收集知识总结
  • LabVIEW液压机智能监控
  • gem install报错解析
  • 【C# in .NET】11. 探秘泛型:类型参数化革命
  • JAVA面试宝典 -《分布式ID生成器:Snowflake优化变种》
  • 基于CentOS的分布式GitLab+Jenkins+Docker架构:企业级CI/CD流水线实战全记录
  • 基于 Spring Boot 构建的文件摆渡系统(File Ferry System)
  • 更灵活方便的初始化、清除方法——fixture【pytest】
  • AWS WebRTC 并发 Viewer 拉流失败分析:0.3 秒等待为何如此关键?
  • 消息转换器--通过此工具进行时间转换
  • Mybatis-2快速入门
  • 【WRFDA数据教程第一期】LITTLE_R 格式详细介绍
  • 【源力觉醒 创作者计划】百度携文心 4.5 入局,开源大模型市场再添一员猛将,与 Qwen3 对比如何?
  • 3DGS之COLMAP
  • iOS 抓包工具选择与配置指南 从零基础到高效调试的完整流程
  • Android动态获取当前应用占用的内存PSS,Java
  • 汽车功能安全-相关项集成和测试(系统集成测试系统合格性测试)-12
  • 从电子管到CPU
  • 迁移学习的概念和案例
  • 【前端Vue】this.resetForm(“form“)重置表单时出现indexOf报错的解决方案
  • Java 增强 switch 语句详解:从基础到进阶的全面指南
  • Sersync和Rsync部署
  • Ubuntu 安装
  • 22-C#的委托简单使用-2
  • Linux715 磁盘管理:逻辑卷