PostgreSQL码农集散地

我的数字人:投喂中

文中参考文档点击阅读原文打开, 同时推荐2个学习环境: 

1、懒人Docker镜像, 已打包200+插件:《最好的PostgreSQL学习镜像》

2、有web浏览器就能用的云起实验室: 《免费体验PolarDB开源数据库》

3、PolarDB开源数据库内核、最佳实践等学习图谱:  https://www.aliyun.com/database/openpolardb/activity 

关注公众号, 持续发布PostgreSQL、PolarDB、DuckDB等相关文章. 

我的数字人: 投喂中

背景

系列文章:

经过十几年不懈努力, github已经发表了4000余篇文章, 把这些文章喂给模型后回答一些专业问题, 会不会有更好的效果呢? 试试看吧。

请先参考如下文章在macOS中部署ollam:《低配macbook成功跑AI大模型LLama3:8b, 感谢ollama》

一、用github的文章来喂模型

为了方便使用, 写个python脚本. 嘿嘿, 下面的python脚本也是ollama生成我简单调整了一下.

安装ollama模块

pip3 install ollama  

这个python脚本输入问题, 以及相关的参考文本, 让模型参考文本进行回答.

vi exp.py  

  import ollama  
import sys  

  def read_files(file_paths):  
    file_contents = []  
    for file_path in file_paths:  
        with open(file_path, 'r') as f:  
            for _ in range(14):  
                next(f)  
            file_contents.append(f.read())  
    return '\n'.join(file_contents)  

  # Get the file path from the command line argument  
if len(sys.argv) < 3:  
    print("Usage: python script.py [prompt] [file1] [file2] ... ")  
    sys.exit(1)  

    try:  
    prompt = sys.argv[1]  
    file_paths = sys.argv[2:]  
    data = read_files(file_paths)  

      output = ollama.generate(  
        model="llama3:8b",  
        prompt=f"请先学习后面提供的参考信息, 使用中文回答这个问题(回答前请先复述问题): {prompt} . 以下全部都是参考信息: {data}"  
    )  

      print(output['response'])  

  except Exception as e:  
    print(f"Error: {e}")  

使用举例, 让模型参考 “## 重新发现PostgreSQL之美 - 9 面向多值列的倒排索引GIN|RUM” 的内容来进行回答.

python3 ./exp.py "PostgreSQL gin和rum索引的差别是什么? 列举一些SQL例子, 最后做个总结." /Users/digoal/blog/202105/20210531_02.md  

模型回答如下

根据提供的参考信息,我将回答这个问题。

问题:关于 PostgreSQL 的 RUM 模块和 rum_anyarray_addon_ops 操作符的使用和示例。

答案:

RUM(Range Unified Manipulation)是 PostgreSQL 中的一个模块,用于对TSVECTOR和ANYARRAY类型进行范围搜索。rum_anyarray_addon_ops 是 rum 模块中的一个操作符,可以用于将ANYARRAY元素存储到索引中,并支持 prefix 搜索。

示例代码:

create extension rum;  
create table tbl (id serial8 primary key, a int[], n int, crt_time timestamp);  
create index idx_tbl_1 on tbl using rum (a rum_anyarray_addon_ops, n) with (attach='n', to='a');  

  explain (analyze, verbose, timing, costs, buffers) select * from tbl where a @> array[1,2,3] and n <= 1;  

这段代码首先创建了 RUM 扩展,然后创建了一张名为tbl的表,其中包含一个整数数组列a和一个 timestamp 列crt_time。接着,使用 rum 模块创建了一个索引idx_tbl_1,用于对a列和n列进行范围搜索。

在查询语句中,我们使用了@>操作符来搜索数组a中包含 1、2 和 3 的元素,并且要求n小于或等于 1。结果显示, PostgreSQL 使用的是索引idx_tbl_1来执行查询,而不是全表扫描。

总的来说,rum_anyarray_addon_ops 操作符可以帮助我们快速地对 ANYARRAY 类型进行范围搜索,并且可以减少查询时间和提高数据库性能。

二、会话记忆 & 外脑知识库

上面是需要我们自己去找相关文章, 然后告诉模型, 能不能让模型自己去找相关文章呢?

还有一种情况是, 模型在交互过程中, 怎么把好的问题和答案收集起来, 方便回答时有上下文的记忆呢? 而不是每次都要你提醒上一次问的是什么.

基于以上问题, 我们需要用到向量数据库.

未来可以把好的答案写入向量数据库、好的参考信息写入向量数据库, 根据会话ID和相似向量提取文本, 作为输入再反馈给模型, 实现模型记忆与外脑知识库.

如何把文本向量化?

再次感谢ollama, 已经集成了小型embedding模型(不到1GB, 照样是先pull模型然后就可以使用了.):

  • https://ollama.com/blog/embedding-models
  • https://ollama.com/library/mxbai-embed-large

在macOS本机安装postgresql, https://www.postgresql.org/ftp/source/

1、安装依赖包  

  export HOMEBREW_API_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api"  
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"  
export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git"  
export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git"  
export HOMEBREW_PIP_INDEX_URL="https://pypi.tuna.tsinghua.edu.cn/simple"  
echo 'export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"' >> ~/.bash_profile  
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"

  brew install openssl  
brew install pkg-config  

  2、下载安装icu:  
https://github.com/unicode-org/icu/releases  

  解压后进入 $SRC/icu4c/source 目录安装  

  ./configure --prefix=/usr/local/icu4c  
make -j 4  
sudo make install  

  3、下载安装PostgreSQL 16  

  cd ~/Downloads  
curl https://ftp.postgresql.org/pub/source/v16.3/postgresql-16.3.tar.bz2 -o postgresql-16.3.tar.bz2  

  tar -jxvf postgresql-16.3.tar.bz2  
cd postgresql-16.3  
export PKG_CONFIG_PATH=/usr/local/icu4c/lib/pkgconfig/  
CC=clang CXX=clang++ ./configure --prefix=/Users/digoal/pg16 --with-ssl=openssl  
make world -j 4  
make install-world  

  4、配置环境变量  

  vi ~/pg16env.sh  
export PGHOME=/Users/digoal/pg16  
export DYLD_LIBRARY_PATH=$PGHOME/lib:$PGHOME/lib/postgresql:$DYLD_LIBRARY_PATH  
export PGHOST=127.0.0.1  
export PGPORT=1921  
export PGUSER=postgres  
export PGDATABASE=postgres  
export PATH=$PGHOME/bin:$PATH

  echo ". /Users/digoal/pg16env.sh" >> ~/.bash_profile  

  . ~/.bash_profile  

  which psql  

  /Users/digoal/pg16/bin/psql  

在macOS本机安装psycopg2

pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple  

  /Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip  

    CPPFLAGS="-I/opt/homebrew/include/openssl" LDFLAGS="-L/opt/homebrew/lib" pip3 install psycopg2  

使用以下镜像拉起PostgreSQL, 内置vector插件用法请参考: https://github.com/pgvector/pgvector

  • 《2023-PostgreSQL Docker镜像学习环境 ARM64版, 已集成热门插件和工具》

进入容器后, 建表如下:

psql  

  alter role postgres encrypted password '123';  
create extension vector;  
create table tbl (  
  id serial8 primary key,  
  prompt text,  
  answer text,  
  v_prompt vector,  
  v_answer vector  
);  

  -- 向量索引请参考手册创建  

在macOS本机可连接映射端口

docker ps -a --no-trunc  

  0.0.0.0:55008->1921/tcp  

pull 小型embedding模型

ollama pull mxbai-embed-large

编写一个python脚本, 用于提取blog的markdown文件的标题以及内容, 写入向量数据库中.

vi exp1.py  

    import psycopg2  
import sys  
import ollama  

  # Define the PostgreSQL connection settings  
HOST = 'localhost'  
PORT = 55008  
DBNAME = 'postgres'  
USER = 'postgres'  
PASSWORD = '123'

  def read_file(file_name):  
    try:  
        with open(file_name, 'r') as f:  
            lines = [line.strip() for line in f.readlines()]  
            prompt = lines[0]  
            answer = '\n'.join(lines[14:])  
            return prompt, answer  
    except Exception as e:  
        print(f"Error reading file: {e}")  
        sys.exit(1)  

  def write_to_db(prompt, answer, v_prompt, v_answer, db_config):  
    try:  
        conn = psycopg2.connect(**db_config)  
        cursor = conn.cursor()  
        cursor.execute("INSERT INTO tbl (prompt, answer, v_prompt, v_answer) VALUES (%s, %s, %s, %s)", (prompt, answer, v_prompt.get("embedding"), v_answer.get("embedding")))  
        conn.commit()  
        conn.close()  
    except Exception as e:  
        print(f"Error writing to DB: {e}")  
        sys.exit(1)  
    finally:  
        # Close the cursor and connection  
        cursor.close()  
        conn.close()  

  if __name__ == "__main__":  
    # Get the file path from the command line argument  
    if len(sys.argv) != 2:  
        print("Usage: python script.py <file_path>")  
        sys.exit(1)  
    file_path = sys.argv[1]  

      db_config = {  
        "host": HOST,  
        "port": PORT,  
        "database": DBNAME,  
        "user": USER,  
        "password": PASSWORD  
    }  

      prompt, answer = read_file(file_path)  

      v_prompt = ollama.embeddings(model='mxbai-embed-large', prompt=prompt)  
    v_answer = ollama.embeddings(model='mxbai-embed-large', prompt=answer)  

      write_to_db(prompt, answer, v_prompt, v_answer, db_config)  

用法举例

python3 ./exp1.py /Users/digoal/blog/202407/20240717_02.md  

写入正常, 生成向量维度1024.

postgres=# select prompt, vector_dims(v_prompt), vector_dims(v_answer) from tbl;  
                    prompt                     | vector_dims | vector_dims  
-----------------------------------------------+-------------+-------------  
 ## DuckDB官方推出"社区扩展插件市场", 未来可期 |        1024 |        1024  
(1 row)  

实际上llama3也是可以生成embedding的, 只是更慢一点. 另外需要注意的是, 同一张表请使用同一个模型来生成embedding, 而且问题也要使用这个模型来生成然后再去该表进行向量相似搜索. 因为不同的模型哪怕是相同的文字生成的向量的映射也可能不一样, 或者向量维度不一样, 所以不同模型的两个向量其实是没法进行比对的.

如果你有很多的模型, 并且想对比不同模型的效果, 我建议你设计1张映射表, 存储模型和表的关系. 类似这样:

create table t_mapping (id int primary key, model_name text, table_name text);

insert into t_mapping values (1, 'llama3:8b', 'tbl1'), (2, 'gemma2:9b', 'tbl2'), (3, 'phi3:medium', 'tbl3');  

   create table tbl1 (...);
create table tbl2 (...);
create table tbl3 (...);

三、批量入库并根据输入问题检索相关文章

批量写入数据库

vi imp.py  

  import os  
import psycopg2  
import ollama  

  # 配置数据库连接信息  
conn = psycopg2.connect(  
    dbname="postgres",  
    user="postgres",  
    password="123",  
    host="127.0.0.1",  
    port="55008"  
)  
cursor = conn.cursor()  

  # 创建表(如果表不存在)  
create_table_query = """  
create extension if not exists vector;  

  CREATE TABLE IF NOT EXISTS markdown_files (  
    id SERIAL PRIMARY KEY,  
    filename TEXT,  
    prompt TEXT,  
    answer TEXT,  
    v_prompt vector(1024),  
    v_answer vector(1024)  
);  
"

""  
cursor.execute(create_table_query)  
conn.commit()  

  # 指定目录路径  
directory_path = "/Users/digoal/blog"

  # 遍历目录及其子目录  
for root, dirs, files in os.walk(directory_path):  
    for file in files:  
        # 检查文件名是否符合 yyyymmdd_nn.md 格式   
        if file.endswith(".md") and len(file) == 14 and file[:8].isdigit() and file[9:11].isdigit():  
            file_path = os.path.join(root, file)  
            with open(file_path, 'r', encoding='utf-8') as f:  
                lines = f.readlines()  
                prompt = lines[0].strip() if len(lines) > 0 else ''  
                answer = '\n'.join([line.strip() for line in lines])   

                                  v_prompt = ollama.embeddings(model='mxbai-embed-large', prompt=prompt)    
                v_answer = ollama.embeddings(model='mxbai-embed-large', prompt=answer)    

                  # 插入数据到PostgreSQL表中  
                insert_query = """  
                INSERT INTO markdown_files (filename, prompt, answer, v_prompt, v_answer)  
                VALUES (%s, %s, %s, %s, %s);  
                "
""  
                cursor.execute(insert_query, (file, prompt, answer, v_prompt.get("embedding"), v_answer.get("embedding")))  
conn.commit()  

  # 关闭连接  
cursor.close()  
conn.close()  

python3 ./imp.py  

写入成功

postgres=# select count(*) from markdown_files;  
 count   
-------  
  4551  
(1 row)  

  postgres=# select filename, prompt from markdown_files limit 10;  
    filename    |                                                               prompt                                                                 
----------------+------------------------------------------------------------------------------------------------------------------------------------  
 20170825_01.md | ## 通过空间思想理解GiST索引的构造  
 20170818_01.md | ## PostgreSQL 单列组合查询优化 - 多个多边形查询优化  
 20170801_01.md | ## 小微贷款、天使投资(风控助手)业务数据库设计(图式搜索\图谱分析) - 阿里云RDS PostgreSQL, HybridDB for PostgreSQL最佳实践  
 20170803_01.md | ## 菜鸟末端轨迹 - 电子围栏(解密支撑每天251亿个包裹的数据库) - 阿里云RDS PostgreSQL最佳实践  
 20170827_01.md | ## 音视图(泛内容)网站透视分析 DB设计 - 阿里云(RDS、HybridDB) for PostgreSQL最佳实践  
 20170823_01.md | ## 万亿级电商广告 - brin黑科技带你(最低成本)玩转毫秒级圈人(视觉挖掘姊妹篇) - 阿里云RDS PostgreSQL, HybridDB for PostgreSQL最佳实践  
 20170807_01.md | ## PostgreSQL 读写分离代理 - Crunchy Proxy(base on golang)  
 20170809_02.md | ## 解密上帝之手 - 阿里云HDB for PostgreSQL数据库metascan特性(存储级、块级、batch级过滤与数据编排)  
 20170821_01.md | ## Greenplum 内存与负载管理(resource queue)最佳实践  
 20170802_01.md | ## plpgsql 编程 - JSON数组循环  
(10 rows)  

创建向量索引

CREATE INDEX ON markdown_files USING hnsw (v_prompt vector_cosine_ops);  
CREATE INDEX ON markdown_files USING hnsw (v_answer vector_cosine_ops);  

通过文本向量相似检索问题相关文本

vi q.py  

    import psycopg2  
import ollama   

  def get_embedding(text):  
    # 调用 ollama.embeddings 获取嵌入值  
    embedding = ollama.embeddings(model='mxbai-embed-large', prompt=text)  
    return embedding  

  def query_database(embedding):  
    # 连接到 PostgreSQL 数据库  
    conn = psycopg2.connect(  
        dbname="postgres",  
        user="postgres",  
        password="123",  
        host="localhost",  
        port="55008"  
    )  
    cursor = conn.cursor()  

          # SQL 查询语句  
    sql_query = """  
    SELECT filename, prompt, answer   
    FROM markdown_files   
    ORDER BY v_prompt <=> %s::vector(1024)   
    LIMIT 5;   
    "
""

          # 执行查询  
    cursor.execute(sql_query, (embedding['embedding'],))  
    results = cursor.fetchall()  

          # 打印结果  
    for row in results:   
        # print(f"Filename: {row[0]}, Prompt: {row[1]}, Answer: {row[2]}")  
        print(f"Filename: {row[0]}, Prompt: {row[1]}")  

          # 关闭连接  
    conn.close()  

  def main():  
    # 读取输入文字  
    text = input("请输入一段文字: ")  

          # 获取嵌入值  
    embedding = get_embedding(text)  

          # 查询数据库并打印结果  
    query_database(embedding)  

  if __name__ == "__main__":  
    main()  

测试检索准确性

5c1bf480a210:~ digoal$ python3 q.py  
请输入一段文字: 数据库如何高效的管理内存  
Filename: 20160608_03.md, Prompt: ## 从尿检取中段谈数据库压测  
Filename: 20210129_05.md, Prompt: ## 如何从下往上触达决策者 - 阶级突破  
Filename: 20161110_05.md, Prompt: ## 数据仓库架构的变迁  
Filename: 20160723_03.md, Prompt: ## 一致性哈希在分布式数据库中的应用探索  
Filename: 20210210_03.md, Prompt: ## 喜马拉雅的奶牛  
5c1bf480a210:~ digoal$ python3 q.py  
请输入一段文字: PostgreSQL rum索引接口和gin索引的差别  
Filename: 20201128_02.md, Prompt: ## PostgreSQL RUM 索引原理  
Filename: 20170204_01.md, Prompt: ## PostgreSQL GIN索引实现原理  
Filename: 20170221_01.md, Prompt: ## PostgreSQL GIN 单列聚集索引 应用  
Filename: 20161231_01.md, Prompt: ## 从难缠的模糊查询聊开 - PostgreSQL独门绝招之一 GIN , GiST , SP-GiST , RUM 索引原理与技术背景  
Filename: 20210531_02.md, Prompt: ## 重新发现PostgreSQL之美 - 9 面向多值列的倒排索引GIN|RUM  
5c1bf480a210:~ digoal$ python3 q.py  
请输入一段文字: postgresql brin索引的用法  
Filename: 20170219_01.md, Prompt: ## PostgreSQL 聚集存储 与 BRIN索引 - 高并发行为、轨迹类大吞吐数据查询场景解说  
Filename: 20160414_01.md, Prompt: ## PostgreSQL 物联网黑科技 - 瘦身几百倍的索引(BRIN index)  
Filename: 20160929_01.md, Prompt: ## PostgreSQL 巧妙的数据采样方法  
Filename: 20170602_01.md, Prompt: ## PostgreSQL 数据去重方法大全  
Filename: 20160529_01.md, Prompt: ## PostgreSQL 批量权限 管理方法  
5c1bf480a210:~ digoal$ python3 q.py  
请输入一段文字: postgresql bloom索引原理  
Filename: 20201128_04.md, Prompt: ## PostgreSQL bloom 索引原理  
Filename: 20160523_01.md, Prompt: ## PostgreSQL 9.6 黑科技 bloom 算法索引,一个索引支撑任意列组合查询  
Filename: 20160616_01.md, Prompt: ## PostgreSQL 同步流复制原理和代码浅析  
Filename: 20160823_02.md, Prompt: ## PostgreSQL 最佳实践 - 冷备份与还原介绍  
Filename: 20210605_07.md, Prompt: ## 重新发现PostgreSQL之美 - 14 bloom 布隆过滤器索引  

召回准确率还有很大的提升空间

四、未完待续

从向量数据库提取相关知识文章, 然后参考该文章进行回答.

其他相关问题:

  • 文章和问题的关键词提取, 提高召回准确率等
  • 分段切分整理问题.
  • 存储往期会话相关问题
  • 如何微调模型? 我瞎猜会不会是这样的: 准备1类学习素材以及有答案的习题(给模型进行训练), 1类有答案的考试试卷(不给模型训练). 让模型训练学习素材和习题, 然后通过考试评分反馈训练来修正、同时加固训练后的认知确定性(权重). 有点像机器学习里的监督学习?

模型与数据的结合应用项目: https://github.com/mindsdb/mindsdb

ollama 开发参考:

  • https://pypi.org/project/ollama/
  • https://github.com/ollama/ollama-python

参考

https://www.timescale.com/blog/use-open-source-llms-in-postgresql-with-ollama-and-pgai

https://ollama.com/blog/embedding-models

https://ollama.com/library/mxbai-embed-large

https://github.com/timescale/pgai/

图片识别和分析

  • https://ollama.com/library/llava

文本生成图片

  • https://github.com/Stability-AI/StableDiffusion
  • https://stability.ai/

文本生成图片 - stable-diffusion提示词生成器

  • https://ollama.com/brxce/stable-diffusion-prompt-generator

phi3 模型微调

  • https://www.datacamp.com/tutorial/phi-3-tutorial

是不是很有趣?

Image

本期彩蛋-招商中,有需要的小伙伴可联系嵌入...

文章中的参考文档请点击阅读原文获得. 


欢迎关注我的github (https://github.com/digoal/blog) , 学习数据库不迷路.  

近期正在写公开课材料, 未来将通过视频号推出, 欢迎关注视频号:

Image