python + ollama 手敲实现私有大模型知识库

在不依赖 LangChain、LlamaIndex 等框架,以及各种知识问答软件的情况下,尽量减少第三方库的使用,仅通过 Ollama 和 NumPy 两个外部库来实现 RAG(Retrieval-Augmented Generation)应用。

一、安装python

下载:https://python.org/downloads/
安装:一路下一步即可,安装完成后运行以下代码,即可查看对应版本号

python --version

在这里插入图片描述

二、安装PyCharm

(推荐,也可以用vscode或windows记事本等编辑软件)
PyCharm的优点:
1、自动提示
2、创建项目时自动创建python虚拟环境
3、整合本地终端(且基于python对应本项目的虚拟环境)
在这里插入图片描述

三、安装ollama

下载:https://ollama.com/download
安装:一路下一步。安装完成后用以下命令检验

ollama --version

在这里插入图片描述
windows安装的ollama默认不适用GPU,也可以使用,如果你手痒且有独立显卡,可先执行此命令,查看下GPU是否可用
(启用GPU的这一步也可省略哦)

nvidia-smi

执行后(如下图)能找到如①标识,说明有GPU可用。
在这里插入图片描述
然后设置下系统变量:CUDA_VISIBLE_DEVICES = 0(此值为上图①)
在这里插入图片描述
设置了系统变量后,重启下ollama,就可以如上图②,已经有一个叫ollama_llama_server.exe的程序在使用gpu了。

四、在ollama中安装开源chat大模型:qwen2.5 或deepseek-r1

ollama官网models(https://ollama.com/search)页面可以找到很多开源大模型,我们下载用的比较多的千问中文大模型(显卡差的建议安装ollama2.5:0.5b)

ollama run qwen2.5

在这里插入图片描述

五、在ollama中安装开源embedding大模型:milkey/m3e nomic-embed-text

embedding作用是将问题和知识库文本转换成向量,便于查询。
原本打算使用nomic-embed-text模型,但是使用时效果不好,发现m3e的效果不错,所以改用m3e

ollama run milkey/m3e

在这里插入图片描述
在这里插入图片描述

六、在项目python虚拟环境中安装ollama

pip install ollama

在这里插入图片描述

七、安装numpy

pip install numpy

八、编写代码

项目文件目录结构如下图
在这里插入图片描述

1. kb.py

import numpy as np
from ollama import embeddings

class Kb:
    def __init__(self, filepath):
        # 读取文件内容
        content = self.read_file(filepath)
        # print(content)
        # 读取拆分好的数组
        self.chunks = self.split_content(content)
        # print(chunks)
        # for chunk in chunks:
        #     print(chunk)
        #     print('=' * 10)
        # 转换成向量
        self.embeds = self.get_embeddings(self.chunks)

    # 读取文件
    def read_file(self, filepath):
        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()
        return content

    # 拆分知识库
    @staticmethod
    def split_content(content):
        chunks = content.split('# ')
        # 过滤掉空块
        chunks = [chunk.strip() for chunk in chunks if chunk.strip()]
        return chunks

    # 字符串转向量(embeddings)
    def get_embedding(self, chunk):
        # milkey/m3e    0.642084887746903
        # bge-m3    0.6073383067378445
        # nomic-embed-text  完全找不到
        res = embeddings(model='milkey/m3e', prompt=chunk)
        # print(chunk)
        # print(res)
        # print(res['embedding'])
        return res['embedding']

    def get_embeddings(self, chunks):
        embeds = []
        for chunk in chunks:
            embed = self.get_embedding(chunk)
            embeds.append(embed)
        return np.array(embeds)

    # 查询相似性向量
    def search(self, text):
        print(text)
        max_similarity = 0
        max_similarity_index = 0
        ask_embed = self.get_embedding(text)
        for kb_embed_index, kb_embed in enumerate(self.embeds):
            similarity = self.similarity(kb_embed, ask_embed)
            # print(similarity)
            # print(self.chunks[kb_embed_index])
            if similarity > max_similarity:
                max_similarity = similarity
                max_similarity_index = kb_embed_index
        print(max_similarity)
        print(self.chunks[max_similarity_index])
        # print(self.embeds[max_similarity_index])
        # 返回查到的相关文本
        return self.chunks[max_similarity_index]

    # 相似度
    @staticmethod
    def similarity(A, B):
        # 计算点积
        dot_product = np.dot(A, B)
        # 计算范数
        norm_A = np.linalg.norm(A)
        norm_B = np.linalg.norm(B)
        # 计算余弦相似度
        cosine_sim = dot_product / (norm_A * norm_B)
        return cosine_sim

2. rag.py

from kb import Kb
from ollama import chat, Message

class Rag:
    def __init__(self, model, kb_filepath):
        self.kb_filepath = kb_filepath
        self.kb = Kb(kb_filepath)
        self.model = model
        self.prompt_template = """
        基于:%s
        回答:%s
        """

    def chat(self, message):
        # 用户消息检索相关上下文
        context = self.kb.search(message)
        # print(context)
        # prompt = self.prompt_template % (context, message)
        prompt = '请基于以下内容回答问题:\n' + context
        response = chat(self.model, [Message(role='system', content=prompt), Message(role='user', content=message)])
        return response['message']

3. index.py

from rag import Rag

rag = Rag('deepseek-r1:14b', '私人知识库.txt')
msg = rag.chat('请介绍下刘芳')
print(msg)

4. 私人知识库.txt

MIS部门人员名单

# 1. 张小刚
姓名:张小刚
性别:男
爱好:打篮球、踢足球
电话:13223344422
籍贯:山东菏泽

# 2. 李光亮
姓名:李光亮
性别:男
爱好:踢足球、打排球
电话:1595959559
籍贯:河南平顶山

# 3. 王丽丽
姓名:王丽丽
性别:女
爱好:游泳、阅读
电话:13812345678
籍贯:江苏南京

# 4. 陈大明
姓名:陈大明
性别:男
爱好:跑步、爬山
电话:13987654321
籍贯:浙江杭州

# 5. 刘芳
姓名:刘芳
性别:女
爱好:瑜伽、绘画
电话:13711223344
籍贯:广东深圳

# 6. 赵强
姓名:赵强
性别:男
爱好:打羽毛球、钓鱼
电话:13566554433
籍贯:四川成都

# 7. 孙婷婷
姓名:孙婷婷
性别:女
爱好:跳舞、唱歌
电话:13677889900
籍贯:福建厦门

# 8. 周伟
姓名:周伟
性别:男
爱好:打乒乓球、下棋
电话:13455667788
籍贯:湖南长沙

# 9. 吴晓梅
姓名:吴晓梅
性别:女
爱好:摄影、旅行
电话:13344556677
籍贯:湖北武汉

# 10. 郑小龙
姓名:郑小龙
性别:男
爱好:打篮球、游泳
电话:13233445566
籍贯:陕西西安

# 11. 高静
姓名:高静
性别:女
爱好:阅读、写作
电话:13122334455
籍贯:辽宁沈阳

# 12. 林浩
姓名:林浩
性别:男
爱好:踢足球、跑步
电话:13011223344
籍贯:广西南宁

# 13. 黄雅婷
姓名:黄雅婷
性别:女
爱好:跳舞、瑜伽
电话:13900112233
籍贯:云南昆明

# 14. 徐志强
姓名:徐志强
性别:男
爱好:打排球、爬山
电话:13899001122
籍贯:贵州贵阳

# 15. 何丽
姓名:何丽
性别:女
爱好:绘画、摄影
电话:13788990011
籍贯:江西南昌

# 16. 马超
姓名:马超
性别:男
爱好:打篮球、钓鱼
电话:13677889900
籍贯:山西太原

# 17. 郭晓燕
姓名:郭晓燕
性别:女
爱好:唱歌、旅行
电话:13566778899
籍贯:河北石家庄

# 18. 罗志勇
姓名:罗志勇
性别:男
爱好:踢足球、下棋
电话:13455667788
籍贯:吉林长春

# 19. 邓丽丽
姓名:邓丽丽
性别:女
爱好:瑜伽、阅读
电话:13344556677
籍贯:黑龙江哈尔滨

# 20. 许文强
姓名:许文强
性别:男
爱好:打羽毛球、跑步
电话:13233445566
籍贯:安徽合肥

# 21. 韩雪
姓名:韩雪
性别:女
爱好:跳舞、摄影
电话:13122334455
籍贯:甘肃兰州

# 22. 曹阳
姓名:曹阳
性别:男
爱好:打篮球、爬山
电话:13011223344
籍贯:青海西宁

# 23. 谢婷婷
姓名:谢婷婷
性别:女
爱好:唱歌、绘画
电话:13900112233
籍贯:宁夏银川

# 24. 董志刚
姓名:董志刚
性别:男
爱好:踢足球、钓鱼
电话:13899001122
籍贯:新疆乌鲁木齐

# 25. 苏静
姓名:苏静
性别:女
爱好:阅读、旅行
电话:13788990011
籍贯:内蒙古呼和浩特

# 26. 潘伟
姓名:潘伟
性别:男
爱好:打乒乓球、下棋
电话:13677889900
籍贯:海南海口

# 27. 钟丽
姓名:钟丽
性别:女
爱好:瑜伽、摄影
电话:13566778899
籍贯:重庆

# 28. 田小龙
姓名:田小龙
性别:男
爱好:打篮球、游泳
电话:13455667788
籍贯:天津

# 29. 白晓梅
姓名:白晓梅
性别:女
爱好:跳舞、唱歌
电话:13344556677
籍贯:北京

# 30. 石浩
姓名:石浩
性别:男
爱好:踢足球、跑步
电话:13233445566
籍贯:上海

通过以上代码,可基本实现用RAG技术,搭建本地知识库问答AI助理,非流式回复(steam=False)。需要等待大模型输出所有文字之后,才能全部返回,太慢,肯定要实现一般聊天大模型的流式回复(steam=True)
首先,在rag.py中增加以下方法

    def stream_chat(self, message):
        context = self.kb.search(message)
        prompt = '请基于以下内容回答问题:\n' + context
        response = chat(self.model, [Message(role='system', content=prompt), Message(role='user', content=message)], stream=True)
        # 遍历流式响应
        for chunk in response:
            print(chunk['message']['content'], end='', flush=True)  # 实时打印输出
        print()  # 输出完成后换行

然后,调整index.py代码为

from rag import Rag

rag = Rag('deepseek-r1:14b', '私人知识库.txt')
rag.stream_chat('请介绍下刘芳')

经过以上的修改,就可以看到AI的流式答复了。
在这里插入图片描述
注:以上RAG只是获取到了本地知识库中的最为匹配的一条记录,如果需要实现一次性获取多个关联内容,并根据多个关联内容进行答复,需要再调整代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/961266.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TypeScript中的函数:类型安全与高级特性

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

EasyExcel使用详解

文章目录 EasyExcel使用详解一、引言二、环境准备与基础配置1、添加依赖2、定义实体类 三、Excel 读取详解1、基础读取2、自定义监听器3、多 Sheet 处理 四、Excel 写入详解1、基础写入2、动态列与复杂表头3、样式与模板填充 五、总结 EasyExcel使用详解 一、引言 EasyExcel 是…

概率论里的特征函数,如何用卷积定理去理解

概率论里的特征函数,如何用卷积定理去理解_哔哩哔哩_bilibili

具身智能技术趋势

参考: 【北京大学-董豪】具身智能技术趋势分析 2024.8 回答了具身智能技术G3、G4的必要性,以及真实数据、仿真数据、互联网数据之间的关系 具身智能趋势 趋势:寻求一个通用路径实现所有的上肢操作 要求:① 低成本 ② 拓展到所有…

新型智慧城市解决方案-3

智慧城市概述 智慧城市是运用物联网、云计算、大数据等现代科技手段,构建集网络化、信息化、智能化于一体的新型城市模式,涵盖智慧管理、智慧产业、智慧民生等核心内容。 智慧城市建设背景 随着城镇化快速发展,城市病问题日益突出&#x…

【云安全】云原生-K8S-搭建/安装/部署

一、准备3台虚拟机 务必保证3台是同样的操作系统! 1、我这里原有1台centos7,为了节省资源和效率,打算通过“创建链接克隆”2台出来 2、克隆之前,先看一下是否存在k8s相关组件,或者docker相关组件 3、卸载原有的docker …

C++的类Class

文章目录 一、C的struct和C的类的区别二、关于OOP三、举例:一个商品类CGoods四、构造函数和析构函数1、定义一个顺序栈2、用构造和析构代替s.init(5);和s.release();3、在不同内存区域构造对象4、深拷贝和浅拷贝5、构造函数和深拷贝的简单应用6、构造函数的初始化列…

HTML<kbd>标签

例子 在文档中将一些文本定义为键盘输入&#xff1a; <p>Press <kbd>Ctrl</kbd> <kbd>C</kbd> to copy text (Windows).</p> <p>Press <kbd>Cmd</kbd> <kbd>C</kbd> to copy text (Mac OS).</p>…

windows下部署安装 ELK,nginx,tomcat日志分析

1.安装包 如上就是elk- windows下部署需要用到的安装包 &#xff08;ps:注意版本需要对应&#xff0c;另外es7版本对应是 jdk8&#xff0c;若更高版本 请自行查询版本对应号&#xff09;。 下载地址&#xff1a; Past Releases of Elastic Stack Software | Elastic 此地址可…

Controller 层优化四步曲

Controller 层优化四步曲 前言 在开发过程中&#xff0c;Controller 层作为系统与外界交互的桥梁&#xff0c;承担着接收请求、解析参数、调用业务逻辑、处理异常等职责。 然而&#xff0c;随着业务复杂度的增加&#xff0c;Controller 层的代码往往会变得臃肿且难以维护。 …

CVE-2025-0411 7-zip 漏洞复现

文章目录 免责申明漏洞描述影响版本漏洞poc漏洞复现修复建议 免责申明 本文章仅供学习与交流&#xff0c;请勿用于非法用途&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任 漏洞描述 此漏洞 &#xff08;CVSS SCORE 7.0&#xff09; 允许远程攻击者绕…

洛谷P1030 [NOIP2001 普及组] 求先序排列(c++)详解

题目链接&#xff1a;P1030 [NOIP2001 普及组] 求先序排列 - 洛谷 | 计算机科学教育新生态 思路&#xff1a; 1.先确定跟节点 2.根据根节点&#xff0c;划分出左右子树 中&#xff1a;BADC 后&#xff1a;BDCA 分析&#xff1a; 根据后序遍历&#xff0…

基于 PyTorch 的深度学习模型开发实战

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 引言 深度学习已广泛应用于图像识别、自然语言处理、自动驾驶等领域&#xff0c;凭借其强大的特征学习能力&#xff0c;成为人工…

VScode+ESP-IDF搭建ESP32开发环境

VScodeESP-IDF搭建ESP32开发环境 ESP-IDF安装方式&#xff1a; 离线安装 ESP-IDF下载;VSCode插件安装ESP-IDF; 这里选择VSCode 环境 ESP-IDF 插件方式安装&#xff0c; VSCode 插件市场中搜索并安装 ESP-IDF 插件&#xff1a; 安装完成后侧边栏会多出一个 ESP-IDF 标志&…

【数据分享】1929-2024年全球站点的逐月平均能见度(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff01;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2024年全球气象站点…

(1)SpringBoot入门+彩蛋

SpringBoot 官网(中文)&#xff1a;Spring Boot 中文文档 Spring Boot是由Pivotal团队提供的一套开源框架&#xff0c;可以简化spring应用的创建及部署。它提供了丰富的Spring模块化支持&#xff0c;可以帮助开发者更轻松快捷地构建出企业级应用。Spring Boot通过自动配置功能…

PydanticAI应用实战

PydanticAI 是一个 Python Agent 框架,旨在简化使用生成式 AI 构建生产级应用程序的过程。 它由 Pydantic 团队构建,该团队也开发了 Pydantic —— 一个在许多 Python LLM 生态系统中广泛使用的验证库。PydanticAI 的目标是为生成式 AI 应用开发带来类似 FastAPI 的体验,它基…

面向对象编程简史

注&#xff1a;本文为 “面向对象编程简史” 相关文章合辑。 英文引文&#xff0c;机翻未校。 Brief history of Object-Oriented Programming 面向对象编程简史 Tue, May 14, 2024 Throughout its history, object-oriented programming (OOP) has undergone significant …

四层网络模型

互联网由终端主机、链路和路由器组成&#xff0c;数据通过逐跳的方式&#xff0c;依次经过每条链路进行传输。 网络层的工作是将数据包从源端到目的端&#xff0c;跨越整个互联网。 网络层的数据包称为数据报。网络将数据报交给链路层&#xff0c;指示它通过第一条链路发送数据…

Linux探秘坊-------4.进度条小程序

1.缓冲区 #include <stdio.h> int main() {printf("hello bite!");sleep(2);return 0; }执行此代码后&#xff0c;会 先停顿两秒&#xff0c;再打印出hello bite&#xff0c;但是明明打印在sleep前面&#xff0c;为什么会后打印呢&#xff1f; 因为&#xff…