私属科研AI助理 – 基于Python开发的LLM应用

利用在线云端大模型,基于Python与RAG框架开发私人AI科研助理应用。

承接上文,之前使用Ollama构建了本地的LLM,并通过文本向量化索引等方式,来建立语言模型与Obsidian知识库的连接,期待将自己的知识库装进AI的大脑。

但实际体验下来发现结果并不尽如人意,一方面原因是,受制于普通笔记本计算机的性能约束,通常只能使用LLaMA 7B版本,尽管在生活场景对话中,使用这种小型语言模型已经够用,然而,如果要满足科研需求,则实际表现和期望值相差甚远。

那么,该如何才能将自己的知识库“塞进AI的大脑”来为我完成专业性更强的一些任务呢?

于是我将目光转向了线上的超大规模模型。

现如今,大多数模型都已经支持了知识库与AI的嵌入,例如RAG项目。

RAG 模型

全称检索增强生成 (Retrieval-Augmented Generation) 模型,是一种结合了信息检索和文本生成的深度学习模型。它能够利用外部知识库来增强文本生成任务,例如问答、对话系统和文本摘要等。

工作原理

  1. 检索 (Retrieval): 当用户提出问题或请求时,RAG 模型首先会根据输入信息,从外部知识库中检索相关文档或段落。
  2. 增强 (Augmentation): 检索到的相关信息会被整合到模型的输入中,用于增强模型对任务的理解。
  3. 生成 (Generation): 最后,模型根据检索到的信息和原始输入,生成最终的文本输出。

搭建步骤

1. 知识库搭建

知识库管理

首先,需要有自己的一个知识库,也就是平时做的一些笔记性质的内容。

以笔记软件Obsidian为例,笔记是以Markdown文件格式.md来存储在本地的,因此,我使用Python简单写个程序,就可以将这些.md文件读取并写入到一个txt文件中。

之所以这样做是因为,我们需要一个知识库文件来输入给LLM模型。如果单独依次将文件上传,就会面临两个问题:(1)有些语言模型不支持对md文件的读取,.txt则极为通用;(2)如果知识库文件数量过多就会导致上传操作极其繁琐,并且可能会达到某些平台要求上传为文件数量上限(例如我的笔记截至目前产生了721个文件,而阿里云模型知识库文件上传数量上限仅为100个。



import os
import datetime


# 定义处理文件夹的函数
def process_directory(directory, output_file):
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.md'):
                file_path = os.path.join(root, file)
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                with open(output_file, 'a', encoding='utf-8') as f:
                    f.write(content + '!!!!\n\n\n')


# 定义知识库文件位置
base_filepath = 'C:/Users/Young/Documents/Obsidian Vault'

# 定义知识库文件夹名
file_list = ['01 文献阅读', '02 知识库', '06 名人堂', '07 科研', '08 哲学', '发现美好']

# 获取当前日期
now = datetime.datetime.now()

# 创建一个新的文件夹,命名为KnowledgeDB+当前日期
output_folder = 'KnowledgeDB' + now.strftime('%Y%m%d')

# 创建一个空白txt文件,命名为KnowledgeDB.txt
output_file = output_folder + '.txt'
with open(output_file, 'w', encoding='utf-8') as file:
    # 在文件夹第一行写入:“知识库更新日期:datetime”
    file.write('知识库更新日期:'+now.strftime('%Y%m%d\n'))
    pass  # 创建空文件

# 轮询知识库文件夹,拼接字符串
for folder in file_list:
    folder_path = os.path.join(base_filepath, folder)
    print('当前文件夹路径为:', folder_path)
    if os.path.isdir(folder_path):
        process_directory(folder_path, output_file)


# 统计KnowledgeDB.txt文件中的字符数
with open(output_file, 'r', encoding='utf-8') as f:
    content = f.read()
    print('本次知识数据转换后,KnowledgeDB.txt文件中的字符数为:', len(content))

print('知识库文件已经写入KnowledgeDB.txt文件中')

经过上述处理,所有的笔记文件就会集成到一个txt文件中(说来惭愧,顿时发现自己积攒了一年的笔记,也不过是一个区区500kb的txt文件)。

2. 嵌入、索引与应用构建

根据条件选择不同的语言模型平台作为自己的知识库的云端服务应用,下面以阿里百炼平台为例:阿里云登录页 (aliyun.com)

1. 注册百炼账号,免费 1000000 Tokens

2. 创建应用:【应用中心】-【我的应用】

3. 获取API_KEY与APP_ID

4. 上传数据库
【数据管理】-【导入数据】

【数据应用】-【知识索引】-【创建知识库】

【我的应用】-【管理】- 【知识检索增强】

3. 聊天应用终端实现

其实此时,已经可以通过网页端与自己的知识库对话了,但对于爱折腾的同学来说,可能这还远远不够,毕竟构建自己的应用就意味着可以今后对接任意别的模型。

这里我使用Panel库来构建一个交互web应用。启动服务后,即可在本地启动一个web应用实现交互。

代码如下:

"""
-*- coding: utf-8 -*-
@Author: Young
@Time: 2024/8/29 18:19
@File: AliAgent.py
@Contact: yangyuan0421@gmail.com
@Note:
    1. 与自己云端设定的Agent进行对话
    2. 目前Prompt在阿里云那边设置,这里不再使用Prompt
    3. 命令行输入:panel serve AliAgent.py --autoreload  启动服务
    4. 如果环境变量设置好后,os.getenv还是返回None,请重启Windows
    5. 程序搭好以后,可以写个bat脚本,启动服务
    6. Panel参考文档https://panel.holoviz.org/

"""

# 导入所需的库
import panel as pn
from http import HTTPStatus
from dashscope import Application
from panel.chat import ChatInterface
import param
import os


pn.extension()  # 加载Panel扩展
# 定义CSS样式
pn.config.raw_css.append("""
div > img {
    border-radius: 50% !important;  /* 使图片变成圆形 */
    width: 50px !important; height: 50px !important;
}
.nav.flex-column{
    height: 100% !important;
}
.sidenav  .bk-panel-models-layout-Column {
    height: 85% !important;
}
.bk-panel-models-reactive_html-ReactiveHTML{
    height: 10% !important;
}
""")

knowledgeDBUpdateTime = '2024年9月9日'
api_key = '{YOUR_API_KEY}'   # 获取API_KEY
app_id = '{YOUR_APP_ID}'  # 定义应用ID
# 定义用户和系统头像的路径
user_avatar = 'assets/user_avatar.JPG'
# 定义模型的价格信息
model_name = 'qwen-Max'
if model_name == 'qwen-Turbo':
    model_price_input = 0.002/1000  # 模型输入价格(每1000个token)
    model_price_output = 0.006/1000  # 模型输出价格(每1000个token)
elif model_name == 'qwen-Max':
    model_price_input = 0.04/1000
    model_price_output = 0.012/1000
# 后续可以添加更多模型的价格信息


# 创建一个ChatCount类,这样就可以参数化,然后调用watch方法,监控参数的变化
class ChatCount(param.Parameterized):
    turns_count = param.Integer(default=0)
    input_tokens = param.List(default=[])
    output_tokens = param.List(default=[])


# 处理信息
def process_message(response):
    # 输出参数表:https://help.aliyun.com/zh/model-studio/developer-reference/call-alibaba-cloud-model-studio-through-sdk?spm=a2c4g.11186623.0.0.20f968c1DoBFl8
    global session_id
    if response.status_code != HTTPStatus.OK:
        return '网络连接错误'
    else:
        session_id = response.output.session_id  # 获取session_id
        print('response:', response.usage)
        chat_count_instance.input_tokens.append(response.usage['models'][0]['input_tokens'])
        chat_count_instance.output_tokens.append(response.usage['models'][0]['output_tokens'])
        chat_count_instance.turns_count += 1
        return response.output.text


# 调用Agent应用
def call_agent_app(messages, user, instance):
    # 首轮对话不需要session_id
    if chat_count_instance == 1:
        response = Application.call(app_id=app_id,
                                    prompt=messages,
                                    api_key=api_key
                                    )
        return process_message(response)

    else:
        response = Application.call(app_id=app_id,
                                    prompt=messages,
                                    api_key=api_key,
                                    session_id=session_id
                                    )
        return process_message(response)


def indicators_update(*events):
    turns_count_indicator.value = chat_count_instance.turns_count
    lastInputTokens.value = chat_count_instance.input_tokens[-1]
    inputTokens.value = sum(chat_count_instance.input_tokens)
    lastOutputTokens.value = chat_count_instance.output_tokens[-1]
    outputTokens.value = sum(chat_count_instance.output_tokens)
    costTokens.value = round(inputTokens.value * model_price_input + outputTokens.value * model_price_output, 2)


# 创建实例
chat_count_instance = ChatCount()
session_id = None


# 监听对话轮次参数(chat_count_instance实例中的turns_count参数)的更新
chat_count_instance.param.watch(indicators_update, 'turns_count')

# 创建ChatInterface实例
chat_interface = ChatInterface(
    callback=call_agent_app,
    callback_exception='verbose',
    widgets=[
        pn.chat.ChatAreaInput(placeholder='Type your message here...', resizable='height')],
    user="Young",
    avatar=user_avatar,
    show_undo=False,
    show_rerun=False)

# 创建Indicators实例
turns_count_indicator = pn.indicators.Number(
    name="Conversation Turns",
    value=0,
    default_color="green",
    title_size='10pt',
    font_size='20pt'
)

lastInputTokens = pn.indicators.Number(name="Last Input Tokens", value=0, title_size='10pt', font_size='20pt')

inputTokens = pn.indicators.Number(name="Total Input Tokens", value=0, title_size='10pt', font_size='20pt')

lastOutputTokens = pn.indicators.Number(name="Last Output Tokens", value=0, title_size='10pt', font_size='20pt')

outputTokens = pn.indicators.Number(name="Total Output Tokens", value=0, title_size='10pt', font_size='20pt')

costTokens = pn.indicators.Number(name="本次消费(¥)", value=0, title_size='10pt', font_size='20pt')

sidebar = pn.FlexBox(
    pn.Column(
        turns_count_indicator,
        lastInputTokens,
        inputTokens,
        lastOutputTokens,
        outputTokens,
        costTokens),
    pn.FlexBox(f"Powered by {model_name} model<br>知识库更新于{knowledgeDBUpdateTime}"))

pn.template.FastListTemplate(title='AIAgent',
                             sidebar=sidebar,
                             main=[chat_interface],
                             ).servable()

如果比较懒的写代码,上述代码可以直接复制粘贴到本地使用,相应使用方法已经写入注释中,仅需要改一下自己的APP_ID与API_KEY即可。

应用效果如下:

注意:

1. 费用问题

可能有人会觉得使用线上模型需要购买Tokens。
我个人觉得,如果自己的知识库足够重要且有意义,这点费用其实算不了什么。计算了一下,我自己一天用下来也就一块钱左右。(截至目前,免费额度尚未用完)。

2. 流式传输问题

如果使用Python的Panel库来实现聊天终端且需要流式传输,Panel官方文档中是使用langchain框架调用和管理语言模型。
但可惜的是,目前阿里百炼仅常规模型支持langchian调用,不支持调用应用。

3. 适用情况

笔记的质量 > 应用本身
适用于笔记比较多的同学,如果平时没有做笔记的习惯,则完全没有折腾的必要性与意义。

最后,Enjoy!

相关代码已更新至Github:
ForeverYoungFly/PersonalAI: 基于RAG框架、Panel库与线上语言大模型构建的AI知识库应用 (github.com)

相关链接:

阿里云PAI大模型RAG对话系统最佳实践-阿里云开发者社区 (aliyun.com)


(注:以上内容系本人原创,创作不易,如若转载敬请注明出处,感谢!)

如果内容对你有所帮助,也欢迎赞助一杯咖啡~

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注