大语言模型学习(含LLM+Neo4j)

前言

由于改篇文档基于本科毕设所写,可能很多地方写的不是很详细,大语言模型部分也大多数也是本人用到的部分。会有瑕疵请大家见谅。

Langchain

Langchain是一个开源框架,允许开发人员将像 GPT-4 这样的大型语言模型与外部的计算和数据源结合起来。目前,提供了 Python 和 JavaScript(TypeScript)的软件包。

是一个用于提升大型语言模型(LLMs)功能的框架

  • 首先是 Compents“组件”,为LLMs提供接口封装、模板提示和信息检索索引;
  • 其次是 Chains“链”,它将不同的组件组合起来解决特定的任务,比如在大量文本中查找信息;
  • 最后是 Agents“代理”,它们使得LLMs能够与外部环境进行交互,例如通过API请求执行操作。

Langchain 由几个核心概念组成:

LLM Wrappers:这些包装器允许你连接到大型语言模型,如 GPT-4 或 Hugging Face 提供的模型。

Prompt Templates:这些模板让你避免硬编码文本输入。你可以动态地将用户输入插入到模板中,并发送给语言模型。

Indexes:索引帮助你从语言模型中提取相关信息。

Chains:链允许你将多个组件组合在一起,解决特定的任务,并构建完整的语言模型应用程序。

Agents:代理允许语言模型与外部API交互。

img

  • 模型 Models 负责理解和生成语言,提示用于引导模型输出;
  • 链条 Chains 代表将多个步骤串联起来完成复杂任务的过程;
  • 代理 Agents 则用于让模型与外部环境互动,比如执行API调用。
  • Embedding 嵌入与向量存储 VectorStore 是数据表示和检索的手段,为模型提供必要的语言理解基础。

Langchain 的工作流程

Langchain 的工作流程可以概括为以下几个步骤:

提问:用户提出问题。向语言模型查询:问题被转换成向量表示,用于在向量数据库中进行相似性搜索。获取相关信息:从向量数据库中提取相关信息块,并将其输入给语言模型。生成答案或执行操作:语言模型现在拥有了初始问题和相关信息,能够提供答案或执行操作。

从用户提出的问题(Question)开始,然后通过相似性搜索(Similarity Search)在一个大型数据库或向量空间中找到与之相关的信息。

得到的信息与原始问题结合后,由一个处理模型分析,以产生一个答案(Answer)。这个答案接着被用来指导一个代理采取行动(Action),这个代理可能会执行一个API调用或与外部系统交互以完成任务。

Token计算

说明

支持的领域 / 任务:aigc

Token计算API是帮助用户估算指定文本对应多少Token数目的API。在灵积平台中,包括通义千问、LLaMa2等在内的语言模型都是基于用户输入和输出的token数目来进行计量和计费的。在语言模型使用中,字符数目和Token数目并不一定是一一对应的,例如在通义千问开源7B模型中:

  • "苹果"对应1个token;
  • "my friends"对应3个token;
  • " 周"对应3个token。

故而,灵积平台提供Token计算API供用户参考。

您也可以通过灵积平台提供的token计算器页面,直接获取tokenize结果。

1. 异步任务查询接口(结果获取接口)

DashScope平台提供了一系列的API能力调用,其中部分接口因为相关能力需要比较长的处理时间或者一些其他的限制,采用了异步任务接口,即用户调用对应的能力API将会提交一个相关的异步任务,接口将立刻返回并告知客户任务提交的结果,如果提交成功,返回内容中将包含任务的序列号(task_id);随后客户可以通过任务状态/结果查询任务的情况,相关的接口详细描述在对应的API详情文档中有详细说明。DashScope针对这一类的异步任务,提供了一系列通用的异步任务管理接口方便客户管控自己提交的异步任务,客户可以使用对应的API在需要的时候直接查询和管理队列中的异步任务。同时,除了提供DashScope的平台接口,异步任务模块还接入了阿里云事件总线(EventBridge),用户可以更加灵活地以消息等其他方式接收任务状态的更新信息。

说明

GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}

异步任务查询接口是一个全局的调用接口,用户在调用时候指定 task_id 即可获取到任务的运行状态,同时在任务结束(成功或者失败)的时候获取到任务的结果或者相应出错信息。

dotenv 库的作用

dotenv库是一个用于加载环境变量的库,它的主要作用是从一个.env文件中读取配置信息,并将其设置为环境变量。这样,在应用程序中就可以方便地访问和使用这些配置项,而不需要在各个文件中手动引入配置文件。此外,dotenv库也可以很好地解决敏感信息的泄漏问题,因为配置信息存储在.env文件中,而不是直接硬编码在代码中[2][5]。

以下是使用dotenv库的基本步骤:

  1. 首先,通过命令行或代码中的适当方式安装dotenv库,并将其添加到项目的依赖中[6]。

  2. 创建一个.env文件,并在其中定义你的环境变量,例如

    API_KEY=your_api_key
    SECRET_KEY=your_secret_key
    
    
    from dotenv import load_dotenv
    import os
    
    load_dotenv()
    
    api_key = os.getenv("API_KEY")
    secret_key = os.getenv("SECRET_KEY")
    

实现一个简单的langchain应用

在VScode或者PyCharm中编码都行,创建好文件后就要开始编写代码了.

1.导入相关包

#导入相关包
import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ['OPENAI_API_BASE'] 
OPENAI_API_KEY=os.environ['OPENAI_API_KEY'] 
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

2.实例化一个llm,定义它的角色

llm = ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE)

template='''
        你的名字是喵喵,当回答问题的时候,你都会在开头加上'喵喵~',然后再回答{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]#这个question就是用户输入的内容,这行代码不可缺少
)
chain = LLMChain(#将llm与prompt联系起来
        llm=llm,
        prompt=prompt
        )
question='你是谁'

res=chain.invoke(question)#运行
    
print(res['text'])#打印结果

3.创建一个.env文件

OPENAI_API_BASE="你的代理地址"#请求地址换源
#通常情况下使用openai的官方账号申请的apikey是不需要代理地址的,但国内是无法访问的
OPENAI_API_KEY="你的apikey"

4.直接运行python [文件名].py或者pycharm直接运行

temperature

Temperature: 用于调整随机从生成模型中抽样的程度,因此每次“生成”时,相同的提示可能会产生不同的输出。温度为 0 将始终产生相同的输出。温度越高随机性越大!主要用于控制创造力。

它影响模型生成文本时采样预测词汇的概率分布。当模型的「Temperature」较高时(如 0.8、1 或更高),模型会更倾向于从较多样且不同的词汇中选择,这使得生成的文本风险性更高、创意性更强,但也可能产生更多的错误和不连贯之处。而当「Temperature」较低时(如 0.2、0.3 等),模型主要会从具有较高概率的词汇中选择,从而产生更平稳、更连贯的文本。但此时,生成的文本可能会显得过于保守和重复。因此在实际应用中,需要根据具体需求来权衡选择合适的「Temperature」值。

温度(Temperature)是一个用于控制人工智能生成文本的创造力水平的参数。通过调整“温度”,您可以影响AI模型的概率分布,使文本更加集中或更多样化。

考虑以下示例:AI 模型必须完成句子**“一只猫正在____”。**下一个字具有以下标记概率:

玩:0.5
睡:0.25
吃:0.15
驾:0.05
飞:0.05
  • **低温(例如0.2):**AI模型变得更加专注和确定性,选择概率最高的标记,例如“玩”。
  • **中温(例如1.0):**AI模型在创造力和专注度之间保持平衡,根据概率选择标记,没有明显的偏见,例如“玩”、“睡”或“吃”。
  • **高温(例如2.0):**AI模型变得更加冒险,增加了选择不太可能的标记的机会,例如“驾”和“飞”。

简单来说就是temperature越小这个AI模型越老实泛化能力也就越小,输出的一致性就越高可以提高其准确性和可预测性。temperature越大模型在选择输出时会更加随机泛化能力会比较大,可能导致生成的文本包含更多错误或不相关的内容。通过调整Temperature的值,可以在一定程度上平衡模型的准确率和泛化能力。

因此,Temperature的好坏取决于具体需求:

  • 如果需要模型生成准确、一致的输出,可能会选择一个较小的Temperature值。
  • 如果需要模型生成多样化、创造性的文本,可能会选择一个较大的Temperature值。

langchain中的提示词

添加了prompts

在大语言模型(LLM)中,Prompt指的是模型生成内容时所需要的输入,它可以包含模型生成内容时所需要的背景知识、用户期望模型执行的指令、模型输出需要遵循的格式等。

在这里插入图片描述

从图中可以看出,提示词主要由一个任务描述,一个输入文本,输出指示组成.他们会一同发送给大语言模型,而大语言模型就会根据提示词进行回答.

提示词公式=角色+角色技能+任务关键词+任务目标+任务背景+任务范围+任务结果判定+限制条件+输出格式+输出量

为什么要用prompts

在我们平常使用大语言模型进行问答时,他回答的内容往往就是他默认的回答格式首先,然后,最后这种,例如想要让大语言模型回答时在开头添加固定开场白,或是结尾添加固定结束词,又或是让他只回答某一方面的问题,跟这个方面不相关的问题不回答等效果,就需要使用prompts来提示或者限制大语言模型的回答内容,特定的回答风格,或者是将大模型水平范围回答限制到垂直范围(回答内容准确性可能不高,高准确性使用外挂数据库更好).

prompts将模型输入模板化、动态选择和管理

langchian提供了几个prompts模板,可以自定义提示词模板

from langchain.prompts import (
    ChatPromptTemplate,
    PromptTemplate,
    SystemMessagePromptTemplate,
    AIMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)

用聊天消息作为输入,每条消息都与一个角色有关,是一个消息列表。

SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate 是分别用于创建不同角色提示词的模板。

LangChain提供了几个对象,区分不同角色

  • HumanMessage:来自人类/用户的ChatMessage
  • AIMessage:来自AI/助手的ChatMessage
  • SystemMessage:来自系统的ChatMessage
  • FunctionMessage:来自函数调用的ChatMessage可以使用ChatMessage类手动指定角色

添加了prompts后

import re
import json
from rich import print
from rich.console import Console
from transformers import AutoTokenizer, AutoModel
# 分类 example
class_examples = {
        '故障': '传感器驱动舌轴断裂。原因是传动机构同轴度差,处理方法是更换舌轴.信号输出状态始终为高或低,原因是光电模块损坏。处理方法是打开后盖,更换模块。'
    }
class_list = list(class_examples.keys())
CLS_PATTERN = f"“{{}}”是 {class_list} 里的什么类别?"
# 定义不同实体下的具备属性
schema = {
    '故障': ['故障现象', '故障原因', '解决办法']
}

IE_PATTERN = "{}\n\n提取上述句子中{}类型的实体,并按照JSON格式输出,上述句子中不存在的信息用['原文中未提及']来表示,多个值之间用','分隔。"
# 提供一些例子供模型参考
ie_examples = {
        '故障': [
                    {
                        'content': '传感器驱动舌轴断裂。原因是传动机构同轴度差,处理方法是更换舌轴.',
                        'answers': {
                                        '故障现象': ['传感器驱动舌轴断裂'],
                                        '故障原因': ['传动机构同轴度差'],
                                        '解决办法': ['更换舌轴']
                            }
                    }
        ]
}


def init_prompts():
    """
    初始化前置prompt,便于模型做 incontext learning。
    """
    class_list = list(class_examples.keys())
    cls_pre_history = [
        (
            f'现在你是一个文本分类器,你需要按照要求将我给你的句子分类到:{class_list}类别中。',
            f'好的。'
        )
    ]

    for _type, exmpale in class_examples.items():
        cls_pre_history.append((f'“{exmpale}”是 {class_list} 里的什么类别?', _type))

    ie_pre_history = [
        (
            "现在你需要帮助我完成信息抽取任务,当我给你一个句子时,你需要帮我抽取出句子中三元组,并按照JSON的格式输出,上述句子中没有的信息用['原文中未提及']来表示,多个值之间用','分隔。",
            '好的,请输入您的句子。'
        )
    ]
    for _type, example_list in ie_examples.items():
        for example in example_list:
            sentence = example['content']
            properties_str = ', '.join(schema[_type])
            schema_str_list = f'“{_type}”({properties_str})'
            sentence_with_prompt = IE_PATTERN.format(sentence, schema_str_list)
            ie_pre_history.append((
                f'{sentence_with_prompt}',
                f"{json.dumps(example['answers'], ensure_ascii=False)}"
            ))
    return {'ie_pre_history': ie_pre_history, 'cls_pre_history': cls_pre_history}

def clean_response(response: str):
    """
    后处理模型输出。

    Args:
        response (str): _description_
    """
    if '```json' in response:
        res = re.findall(r'```json(.*?)```', response)
        if len(res) and res[0]:
            response = res[0]
        response.replace('、', ',')
    try:
        return json.loads(response)
    except:
        return response


def inference(
        sentences: list,
        custom_settings: dict
    ):
    """
    推理函数。

    Args:
        sentences (List[str]): 待抽取的句子。
        custom_settings (dict): 初始设定,包含人为给定的 few-shot example。
    """
    for sentence in sentences:
        with console.status("[bold bright_green] Model Inference..."):
            sentence_with_cls_prompt = CLS_PATTERN.format(sentence)
            cls_res, _ = model.chat(tokenizer, sentence_with_cls_prompt, history=custom_settings['cls_pre_history'])

            if cls_res not in schema:
                print(f'The type model inferenced {cls_res} which is not in schema dict, exited.')
                exit()

            properties_str = ', '.join(schema[cls_res])
            schema_str_list = f'“{cls_res}”({properties_str})'
            sentence_with_ie_prompt = IE_PATTERN.format(sentence, schema_str_list)
            ie_res, _ = model.chat(tokenizer, sentence_with_ie_prompt, history=custom_settings['ie_pre_history'])
            ie_res = clean_response(ie_res)
        print(f'>>> [bold bright_red]sentence: {sentence}')
        print(f'>>> [bold bright_green]inference answer: ')
        print(ie_res)


if __name__ == '__main__':
    console = Console()

    device = 'cuda:0'
    tokenizer = AutoTokenizer.from_pretrained("/data/zhuojian/models/chatglm-6b", trust_remote_code=True)
    model = AutoModel.from_pretrained("/data/zhuojian/models/chatglm-6b", trust_remote_code=True).half().cuda()
    model = model.eval()

    sentences = {
        '信号输出状态始终为高或低,原因是光电模块损坏。处理方法是打开后盖,更换模块'
    }

    custom_settings = init_prompts()
    inference(
        sentences,
        custom_settings
    )

Chain的意义

Chain,翻译成链的意思,顾名思义将其他东西链接起来,串联起来,链式链接.

在langchain中,单独使用llm进行问答是没有问题的,但是对于llm大模型,prompt提示词,memory记忆组件,agent代理等单独得模块如何串联起来需要一个解决办法.langchain中Chain的意义就在于将这些模块串联起来,实现一个完善的大模型应用.

Chain位于langchain.chains目录下的base.py文件中,从文件命名就可以看出这是一个基类,其中包含了一些关键属性和方法。对于属性,包含memory、callbacks、verbose等。而对于方法,既有抽象方法,又有具体实现的方法。抽象方法定义了所有派生Chain类必须遵循的接口,具体实现的方法为所有派生类提供了通用的功能。

总的来说,这个基类为Langchain创建了一个灵活的架构,使得开发者能够通过创建新的Chain子类来快速扩展和自定义功能。

在这里插入图片描述

在这里插入图片描述

如何使用chain将模块进行串联

对于单独使用llm,非常简单,但是局限性很大,没有记忆,没有提示词的限制.得到的结果往往也达不到需求.

#举个例子
llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
print("第一次对话:",llm.invoke("你是一只小狗,只会汪汪叫"),"\n\n第二次对话:",llm.invoke("你是一只小狗嘛"))

什么是memory

存储对话历史中的信息的能力称之为’记忆‘,这种工具可以单独使用,也可以无缝的集成到一条链中,记忆的存储长度是程序执行到结束,执行一次的所有记忆。

记忆组件需要支持

  • 读取
  • 写入

每条链定义了核心执行逻辑,期望某些输入,一些来自用户,一些来自记忆组件,在一次与LLM的交互中,链与记忆组件交互两次

  1. 读取记忆:将之前的交互内容进行读取,放入到本次交互中
  2. 写入记忆:将本次的交互内容写入到记忆当中

为什么需要使用记忆组件

在langchain中,直接使用llm.invoke进行大模型对话,llm的记忆范围只有层序执行到运行结束,再次对话就是新的开始,没有以前的记忆内容,当提问’我刚刚说了什么’时,他就回答不出前一次的交互内容.

llm=ChatOpenAI()
print("第一次对话:",llm.invoke("今天天气真好啊"),
        "\n\n第二次对话:",llm.invoke("我刚刚说了什么"))

而使用记忆组件就可以让llm有记忆能力,能够将进行上下文联想.让与大模型对话时有和真人对话的感觉.

使用步骤

需要四个部件组合起来使用,大模型,提示词模板,链,记忆组件

  1. 实例化一个LLM
  2. 定义记忆组件
  3. 创建提示词模板
  4. 使用链将他们链接起来

四种记忆组件

ConversationBufferMemory会话缓冲区

如实的记录列表中记录的对话历史消息,并且是记录所有的历史消息,随着历史记录的增加,运行会越来越慢,直到大模型无法处理.适用于交互次数少,输入输出字符量不大的情况下

使用方法

import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ["OPENAI_API_BASE"] 
OPENAI_API_KEY=os.environ["OPENAI_API_KEY"] 
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
   

from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.memory import (ConversationBufferMemory)
   
llm=ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE)
template='''你是一个成熟的大姐姐,你的任务是用温柔的语气回答人类的问题。
        {chat_memory}
       human:{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]
)
#ConversationBufferMemory
memory = ConversationBufferMemory(memory_key="chat_memory",return_messages=False)
chain = LLMChain(
        llm=llm,
        prompt=prompt,
        memory=memory,
        verbose=True
        )

chain.invoke("我喜欢美食,我最喜欢的美食是清蒸鲈鱼")
chain.invoke("你是谁?")
chain.invoke("今天的天气真好啊")
res = chain.invoke("我最开始跟你聊的什么呢?")
print(res['text'])

在prompt中的template里面有一个{chat_memory},这就是记忆组件的输入也就是链与记忆组件的第一次交互

在memory定义中需要将这个记忆组件的输入定义出来

memory = ConversationBufferMemory(memory_key="chat_memory",return_messages=False)中的

memory_key就是记忆组件的输入,return_messages是返回值中是否带有记忆内容.

ConversationBufferWindowMemory 会话缓冲窗口

持续记录对话历史,但只使用最近的k个交互。确保缓存大小不会过大,运行速度比较稳定

使用方法
import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ["OPENAI_API_BASE"] 
OPENAI_API_KEY=os.environ["OPENAI_API_KEY"]
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.memory import (ConversationBufferMemory,ConversationBufferWindowMemory,ConversationSummaryMemory)
   
llm=ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE)
template='''你是一个成熟的大姐姐,你的任务是用温柔的语气回答人类的问题。
        {chat_memory}
       human:{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]
)
#ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(memory_key="chat_memory",
                                            k=2,
                                            return_messages=False)

chain = LLMChain(
        llm=llm,
        prompt=prompt,
        memory=memory,
        verbose=True
        )

chain.invoke("我喜欢美食,我最喜欢的美食是清蒸鲈鱼")
chain.invoke("你是谁?")
chain.invoke("今天的天气真好啊")
res = chain.invoke("我最开始跟你聊的什么呢?")
print(res['text'])

memory = ConversationBufferWindowMemory(memory_key="chat_memory",k=3,return_messages=False)中的k就是记录交互的次数,其余两个参数与上一个一致

ConversationSummaryMemory会话摘要

随着时间的推移总结对话内容,并且将摘要存储在记忆中,需要的时候将摘要注入提示词或链中,缓存不会过大,运行稳定,但是运行速度比ConversationBufferWindowMemory慢很多,因为他在写入记忆的时候,做了一个摘要的操作.这使得她可以记住很长的交互记忆,不过随着交互的增加,摘要的内容不断迭代更换,使得某些内容会遗失.

使用方法
import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ["OPENAI_API_BASE"] 
OPENAI_API_KEY=os.environ["OPENAI_API_KEY"]
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.memory import (ConversationBufferMemory,ConversationBufferWindowMemory,ConversationSummaryMemory)
   
llm=ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE)
template='''你是一个成熟的大姐姐,你的任务是用温柔的语气回答人类的问题。
        {chat_memory}
       human:{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]
)
#ConversationSummaryMemory
memory = ConversationSummaryMemory(llm=llm, 
                                    memory_key="chat_memory",
                                    return_messages=False)
chain = LLMChain(
        llm=llm,
        prompt=prompt,
        memory=memory,
        verbose=True
        )

chain.invoke("我喜欢美食,我最喜欢的美食是清蒸鲈鱼")
chain.invoke("你是谁?")
chain.invoke("今天的天气真好啊")
res = chain.invoke("我最开始跟你聊的什么呢?")
print(res['text'])

对记忆进行摘要.大模型与真实的人还有一段距离,使用他认为重要的内容或许与我们真实的人认为重要的内容不一样,而且随着摘要的增加,记忆的范围,内容的重点可能就会发生偏移

这个类型的记忆组件需要传入一个llm参数memory = ConversationSummaryMemory(llm=llm, memory_key="chat_memory",return_messages=False),使用llm来进行对话摘要.

AnalyzeDocumentChain 文档拆分

文档拆分可以确保语义相关的内容在同一块中组合在一起。

在加载完文档后需要将它们拆分为更小的块,用于语义的分析

LangChain提供了几种类型的文本拆分器,每种都有自己的优势和用例。以下是一些最常用的分离器:

1.CharacterTextSplitter
一个基本的拆分器,它基于单个字符分隔符(如空格或换行符)拆分文本。在处理结构不清晰的文本或想要在特定点拆分文本时,此拆分器非常有用。

2.RecursiveCharacterTextSplitter
用于通用文本拆分,它根据分隔符的层次结构拆分文本,从双换行符开始,然后是单换行符 、空格,最后是单个字符。这种方法旨在通过优先考虑段落和句子等自然边界的拆分来保持文本的结构和连贯性。RecursiveCharacterTextSplitternnn

3.TokenTextSplitter
根据标记计数而不是字符计数拆分文本,因为许多语言模型都具有由标记计数而不是字符计数指定的上下文窗口。标记的长度通常约为四个字符,因此基于标记计数进行拆分可以更好地表示语言模型将如何处理文本。TokenTextSplitter

4.MarkdownHeaderTextSplitter
旨在根据标题结构拆分 Markdown 文档。它将标头元数据保留在生成的块中,从而允许上下文感知拆分和使用文档结构的潜在下游任务。MarkdownHeaderTextSplitter

VectorStoreRetrieverMemory向量存储

将记忆存储在向量存储中,并在每次调用时查询前K个最"显著"的文档。

与大多数其他记忆类不同的是,它不明确跟踪交互的顺序。

在这种情况下,"文档"是先前对话片段。这对于提及AI在对话中早些时候被告知的相关信息可能是有用的。这段话是官方文档的描述,猜测应该是将记忆做成了一个文档,使用文档阅读器来进行读取

使用方式
from datetime import datetime
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.llms import OpenAI
from langchain.memory import VectorStoreRetrieverMemory
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
import faiss

from langchain.docstore import InMemoryDocstore
from langchain.vectorstores import FAISS

embedding_size = 1536 # OpenAIEmbeddings的维度
index = faiss.IndexFlatL2(embedding_size)
embedding_fn = OpenAIEmbeddings().embed_query
vectorstore = FAISS(embedding_fn, index, InMemoryDocstore({}), {})
llm = OpenAI(temperature=0) # 可以是任何有效的LLM
_DEFAULT_TEMPLATE = """以下是人类和AI之间友好的对话。AI健谈并从其上下文中提供了许多具体细节。如果AI不知道问题的答案,它会真诚地说自己不知道。

先前对话的相关部分:
{history}

(如果不相关,您无需使用这些信息)

当前对话:
人类:{input}
AI:"""
PROMPT = PromptTemplate(
    input_variables=["history", "input"], template=_DEFAULT_TEMPLATE
)
conversation_with_summary = ConversationChain(
    llm=llm, 
    prompt=PROMPT,
    # 出于测试目的,我们将max_token_limit设置得非常低。
    memory=memory,
    verbose=True
)
conversation_with_summary.predict(input="Hi, 我叫Perry,有什么新鲜事?")

总结

对于一个聊天机器人,在对话中可能需要进行上下文联想,分析的操作,或者是进行一个情景对话,记忆组件都是不可或缺的重要组成部分.在langchain的早期版本中,记忆组件运行速度非常的慢,如果作为一个请求内容返回给前端百分百会超时,在稳定的版本出来之后就流畅很多了,应用到实际的应用中也更具有体验感.在这样的条件下,对大模型进行角色定制,对话中这个角色的丰富度就会高很多.也能做出更多更有意思的聊天机器人.

抽取:使用kor.extraction的create_extraction_chain

#pip install kor
import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ["OPENAI_API_BASE"] 
OPENAI_API_KEY=os.environ["OPENAI_API_KEY"] 
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.schema.prompt_template import BasePromptTemplate
from kor.extraction import create_extraction_chain# %% 信息抽取链 %%
from kor.nodes import Object, Text, Number #%% 节点类型 %%
import json

def json_dump(json_object): #这个辅助函数用于将JSON对象格式化并打印出来,使输出更易读
   json_formatted_str = json.dumps(json_object, indent=2, ensure_ascii=False) 
#调用了 json 模块的 dumps 函数,它将 json_object 序列化为一个 JSON 格式的字符串
#indent=2 参数指定了在序列化输出的 JSON 字符串中每个级别缩进的空格数使得输出的 JSON 字符串具有良好的可读性,因为它以层次结构的方式展示了对象的嵌套关系
#ensure_ascii=False 参数指示 dumps 函数在序列化字符串时不要将非ASCII字符转换为 ASCII 转义序列。允许非ASCII字符以其原始形式显示,在输出中保持了它们的可读性。
   print(json_formatted_str)

llm=ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE,temperature=0)

res_schema = Object(
    id="成绩",
    description="关于成绩的信息",# %%描述信息 %%
    attributes=[#%% 属性text表示字符串字段,number表示数字字段 %%
        Text(
            id="name",
            description="这个人的名字."
        ),
        Text(
            id="Results",
            description="关于这个人的成绩."
        ),
        Number(
            id="ranking",
            description="关于这个人的排名."
        )
    ],
    examples=[# 给模型的样本例子,将数据输出成例子一样的格式 
        (
            '''张山这次获得第一名,他考了100分, 而李四获得第五名,他考了80.''',
            [
                {"name": "张三", "Results": "100", "ranking": 1},
                {"name": "李四", "Results": "80", "ranking": 5},
            ],
        )
    ]
)
extraction_chain = create_extraction_chain(llm, res_schema)
text="王五比赵六多10分,赵六只考了60分排十名,赵六比他多三名"
output=extraction_chain.run(text)
json_dump(output)

抽取:langchain的create_extraction_chain

#使用langchain稳定版本中的抽取链
import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ["OPENAI_API_BASE"] 
OPENAI_API_KEY=os.environ["OPENAI_API_KEY"]
from langchain.chains import create_extraction_chain
from langchain_openai import ChatOpenAI
from langchain.prompts import (
    PromptTemplate,
)

llm=ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE,temperature=0)

def extract(res:str):
    prompt = PromptTemplate(
    template="分析出\n{res}\n中的水果\n",#进行提示告诉它该怎么做
    input_variables=["res"],
    )
    # Schema定义结构化的数据模型
    schema = {
        "properties": {
            "reason": {"fruit": "string"},
        },
        "required": ["fruit"],
    }
    chain = create_extraction_chain(schema, llm, prompt,verbose=True)
    return chain.run(res)

if __name__ == "__main__":
    res = "我喜欢吃苹果,它让我感到幸福"
    print(extract(res))

langchain中还有更多其他的链,可在官方文档中查看,最好是英文文档,中文文档更新速度很慢

千问

# For prerequisites running the following sample, visit https://help.aliyun.com/document_detail/611472.html
from http import HTTPStatus
import dashscope

dashscope.api_key="key复制到这里"
#构建请求并调用DashScope服务
def call_with_messages():
    #第一条消息是'system'角色的,内容为'You are a helpful assistant.',这意味着系统正在设置场景,表明对话的上下文是与一个有帮助的助手进行交互。第二条消息是'user'角色的,内容为'如何做炒西红柿鸡蛋?',这是一个用户提出的问题,询问如何制作炒西红柿鸡蛋这道菜。
    messages = [{'role': 'system', 'content': 'You are a helpful assistant.'},
                {'role': 'user', 'content': '如何做炒西红柿鸡蛋?'}]

    response = dashscope.Generation.call(#使用dashscope.Generation.call方法发送请求到DashScope服务。
        dashscope.Generation.Models.qwen_turbo,#指定要使用的DashScope模型,这里使用的是qwen_turbo模型
        messages=messages,
        result_format='message',  # set the result to be "message" format.指定了期望从DashScope服务接收到的响应数据的格式
    )
    if response.status_code == HTTPStatus.OK:
        print(response)
    else:
        print('Request id: %s, Status code: %s, error code: %s, error message: %s' % (
            response.request_id, response.status_code,
            response.code, response.message
        ))


if __name__ == '__main__':
    call_with_messages()

在IDE中通过messages调用

创建一个Python文件,将下述代码贴进文件即可运行

from http import HTTPStatus
from dashscope import Generation
import dashscope
from dashscope.api_entities.dashscope_response import Role


def call_with_messages():
    dashscope.api_key = 'YOUR_API_KEY' # 将 YOUR_API_KEY 改成您创建的 API-KEY
    systemRole = input("请告诉我,我是什么角色:")
    userRole = input("请告诉我你要问什么:")
    messages = [{'role': 'system', 'content': systemRole}, 
                {'role': 'user', 'content': userRole}]
    gen = Generation()
    response = gen.call(
        Generation.Models.qwen_turbo,
        messages=messages,
        result_format='message', # 设置结果为消息格式
    )
    if response.status_code == HTTPStatus.OK:   
        print(response.output.choices[0]['message']['content'])  
        messages.append({'role': response.output.choices[0]['message']['role'], 
                         'content': response.output.choices[0]['message']['content']})
    else:
        print('Request id: %s, Status code: %s, error code: %s, error message: %s'%(
            response.request_id, response.status_code, 
            response.code, response.message
        ))
    while 1:
        question = input("还有什么问题吗?")
        if question == '':
            break
        messages.append({'role': Role.USER, 'content': question})
        response = gen.call(
            Generation.Models.qwen_turbo,
            messages=messages,
            result_format='message', # 设置结果为消息格式
        )
        if response.status_code == HTTPStatus.OK:   
            print(response.output.choices[0]['message']['content'])
        else:
            print('Request id: %s, Status code: %s, error code: %s, error message: %s'%(
                response.request_id, response.status_code, 
                response.code, response.message
            )) 
if __name__ == '__main__':
    call_with_messages()

千问调用promote

# For prerequisites running the following sample, visit https://help.aliyun.com/document_detail/611472.html
from http import HTTPStatus
import dashscope


def call_with_prompt():
    response = dashscope.Generation.call(
        model=dashscope.Generation.Models.qwen_turbo,
        prompt='如何做炒西红柿鸡蛋?'#此prompt是直接与AI模型交互的文本,AI模型会根据这个输入生成一个回答
    )
    # The response status_code is HTTPStatus.OK indicate success,
    # otherwise indicate request is failed, you can get error code
    # and message from code and message.
    if response.status_code == HTTPStatus.OK:
        print(response.output)  # The output text
        print(response.usage)  # The usage information
    else:
        print(response.code)  # The error code.
        print(response.message)  # The error message.

if __name__ == '__main__':
    call_with_prompt()

openai调用PromptTemplate

import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ["OPENAI_API_BASE"] 
OPENAI_API_KEY=os.environ["OPENAI_API_KEY"] 
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
   
llm=ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE)
template='''
        你是一个不耐烦的老奶奶,非常不愿意回答问题,请你不耐烦的回答:{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]
)
chain = LLMChain(
        llm=llm,
        prompt=prompt
        )
question='什么是人工智能?'

res=chain.invoke(question)
print("无prompt--->\n",llm.invoke(question),"\n")
print("有prompt--->\n",res['text'])

千问定义回答风格

from http import HTTPStatus
import dashscope

def call_with_customized_prompt():
    # 定义一个具有特定风格和格式要求的提示
    prompt = ("请用幽默且友好的语气回答以下问题:\n"
              "\n"
              "问题:如何做炒西红柿鸡蛋?\n"
              "\n"
              "期望的回答格式:\n"
              "1. 简介\n"
              "2. 材料列表\n"
              "3. 逐步做法\n"
              "4. 小贴士")
    # 调用DashScope服务
    response = dashscope.Generation.call(
        model=dashscope.Generation.Models.qwen_turbo,
        prompt=prompt
    )
    # 检查响应状态并处理结果
    if response.status_code == HTTPStatus.OK:
        print("回答内容:", response.output.get('choices', [{}])[0]['message']['content'])
        print("使用情况信息:", response.usage)
    else:
        print("错误代码:", response.code)
        print("错误信息:", response.message)

if __name__ == '__main__':
    call_with_customized_prompt()

LLM和Neo4j结合

在某些模型不能很好解决的场景下,基于规则的方法还是可以发挥不小的价值。模型+规则可以让系统运行的更加高效和准确(LLM Prompt工程涉及规则的方法)。为了让系统生成Cypher的能力更强,本文尝试了GPT4的能力。

LLM和neo4j数据库结合的方法有两种

  • 用QA链对图谱进行提问
  • 从图谱出发的检索顺序

用千问和neo4j做结合:需要用到的类为GraphCypherQAChain,用QA链对图谱进行提问

chain_language_example = GraphCypherQAChain.from_llm(
    Tongyi(temperature=0), graph=graph, verbose=True,
    cypher_prompt=CYPHER_GENERATION_PROMPT
)

提问模式:

chain.run(“what is Intel going to build?”)

直接上代码:

import os
import csv
import pandas as pd
import qianfan
from langchain_community.chat_models import QianfanChatEndpoint
from langchain_community.llms import QianfanLLMEndpoint
from langchain_community.llms import Tongyi
from langchain.chains import GraphCypherQAChain
from langchain_community.graphs import Neo4jGraph
from langchain.prompts.prompt import PromptTemplate
from neo4j import GraphDatabase
from py2neo import Graph, Node, Relationship
from langchain_core.messages import HumanMessage
from kor.nodes import Object, Text, Number
from kor.extraction import create_extraction_chain
import json
from playsound import playsound
def json_dump(json_object): #这个辅助函数用于将JSON对象格式化并打印出来,使输出更易读
   json_formatted_str = json.dumps(json_object, indent=2, ensure_ascii=False)
# 设置你的通义千问 API 密钥
API_KEY=os.environ['dashscope_API_KEY'] =

# 设置通义千问 API 的端点
API_BASE=os.environ['dashscope_ENDPOINT'] = "https://api.tongyiqianwen.com/v1/"
qianwen=Tongyi(api_key=API_KEY,base_url=API_BASE,temperature=0)

# chat_comp = qianfan.ChatCompletion(ak="4rnte6CwCBDA68BbV3phjTJ0", sk="e84jweHm7v1BtWOuaeWfC1zF51cr88DJ")
os.environ["QIANFAN_AK"] = 
os.environ["QIANFAN_SK"] = 
graph = Neo4jGraph(
    url="bolt://localhost:7687",
    username="neo4j",
    password=""
)
# graph = Graph("http://localhost:7474/", auth=("neo4j", ""),name = "neo4j")
CYPHER_GENERATION_TEMPLATE = """
                任务:生成Cypher语句以查询图数据库。
                说明:仅使用提供的关系类型和属性。不要使用任何其他未提供的关系类型或属性。
                模式:{schema}/
                Cypher示例:
                # 某点信标一直无法识别所有机车上的读写器均如此出现故障的位置是在哪?
                MATCH(c:phenomenon)-[:故障的定位]->(t:location) WHERE c.phenomenon = '某点信标一直无法识别所有机车上的读写器均如此'
                RETURN t.location

                # 某点信标故障的分析是什么?
                MATCH(c:location)-[:故障的分析]->(t:analysis) WHERE c.location = '某点信标'
                RETURN t.analysis
                #信标安装错误,特别是极化方向解决的方案是什么?
                MATCH(c:`故障分析`)-[:`解决的方案`]->(t:`解决方案`) WHERE c.analysis = 	'硬盘故障或操作系统软件损坏、缺失'
                RETURN t.method
                #什么导致解决的方案是按正确方法重新安装,特别是注意与定位天线之间的极化关系?
                MATCH(c:analysis)-[:解决的方案]->(t:method) WHERE t.method = '按正确方法重新安装,特别是注意与定位天线之间的极化关系'
                RETURN c.analysis
                # 直接说出输出端口未驱动1故障的解决方案是什么?不用说故障的分析。
                match(c:`故障现象`)-[:故障的定位]->(t:`故障定位`)-[:`故障的定位`]->(t:`故障分析`)-[:`解决的方案`]->(p:`解决方案`) where c.phenomenon='输出端口未驱动1'
                return p.method
                
                
                注意:请不要在回答中包含任何解释或道歉。请不要回答任何不要求构建Cypher语句的问题。请只返回生成的Cypher语句
                The question is:{question}"""

CYPHER_GENERATION_PROMPT = PromptTemplate(
    input_variables=["schema", "question"], template=CYPHER_GENERATION_TEMPLATE
)

chain_language_example = GraphCypherQAChain.from_llm(
    Tongyi(temperature=0), graph=graph, verbose=True,
    cypher_prompt=CYPHER_GENERATION_PROMPT
)

# result = chain_language_example.invoke("信标安装错误解决的方案是什么?")
# print(result)
text1 = '1'
while True:
   with open('D:/1/input.txt', 'r', encoding='ANSI') as file:
      # 读取文件全部内容到一个字符串
      text = file.read()
   if(text1!=text):
      text1=text
      print(text1)
      print(text)
      output=chain_language_example.invoke(text1)

      json_dump(output)
      # data = json.loads(output)
      newoutput = output.get('result', '')
      print(newoutput)
      print("1")
      with open('D:/1/output.txt', 'w', encoding='ANSI') as file:
          file.write(str(newoutput))

import requests
import Levenshtein
import json


# 使用HTTP封装的ChatGLM接口,请根据具体接口情况替换
def chatglm(msg):
    url = "https://localhost:8080/llm/chatglm/chat"
    payload = json.dumps({
        "query": msg,
        "history": [],
        "max_length": 2048,
        "temperature": 0.95
    })
    headers = {
        'Content-Type': 'application/json'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    return response.json()['response']


def example_list():
    ex_list = [{
        'qa': '火力发电行业博士学历的男性高管有多少位?',
        'cypher': '''
            MATCH 
                p0=(n1:行业)<-[r0:所属行业]-(n0:股票)<-[r1:任职于]-(n2:高管)-[r2:性别]->(n3:性别)-[r4:别名]->(n5:性别_别名),
                p1=(n2)-[r3:学历]->(n4:学历) 
            WHERE n1.value='火力发电' AND n5.value='男性' AND n4.value='博士'
            RETURN COUNT(DISTINCT n2) AS n3;
            '''
    }, {
        'qa': '山西都有哪些上市公司?',
        'cypher': '''
            MATCH p0=(n0:股票)-[r0:地域]->(n1:地域) WHERE n1.value='山西' 
            RETURN DISTINCT n0 AS n4 LIMIT 10;
               '''
    }, {
        'qa': '富奥股份的高管都是什么学历?',
        'cypher': '''
                 MATCH p0=(n1:股票名称)<-[r0:股票名称]-(n0:股票)<-[r1:任职于]-(n2:高管)-[r2:学历]->(n3:学历) 
            WHERE n1.value='富奥股份'
            RETURN DISTINCT n3 AS n2 LIMIT 10;
                   '''
    }, {
        'qa': '中国宝安属于什么行业?',
        'cypher': '''
                MATCH p0=(n1:股票名称)<-[r0:股票名称]-(n0:股票)-[r1:所属行业]->(n2:行业) 
            WHERE n1.value='中国宝安'
            RETURN DISTINCT n2 AS n5 LIMIT 10;
                       '''
    }, {
        'qa': '建筑工程行业有多少家上市公司?',
        'cypher': '''
                MATCH p0=(n0:股票)-[r0:所属行业]->(n1:行业) 
            WHERE n1.value='建筑工程'
            RETURN COUNT(DISTINCT n0) AS n4;
                           '''
    }, {
        'qa': '刘卫国是哪个公司的高管?',
        'cypher': '''
             MATCH p0=(n0:股票)<-[r0:任职于]-(n1:高管) 
            WHERE n1.value='刘卫国'
            RETURN DISTINCT n0 AS n4 LIMIT 10;
                            '''
    }, {
        'qa': '美丽生态上市时间是什么时候?',
        'cypher': '''
                MATCH p0=(n1:股票名称)<-[r0:股票名称]-(n0:股票)-[r1:上市日期]->(n2:上市日期) 
            WHERE n1.value='美丽生态'
            RETURN DISTINCT n2 AS n1 LIMIT 10;
                                '''
    }, {
        'qa': '山西的上市公司有多少家?',
        'cypher': '''
                MATCH p0=(n0:股票)-[r0:地域]->(n1:地域) 
            WHERE n1.value='山西'
            RETURN COUNT(DISTINCT n0) AS n4;
                                    '''
    }, {
        'qa': '博士学历的高管都有哪些?',
        'cypher': '''
                MATCH p0=(n0:高管)-[r0:学历]->(n1:学历) 
            WHERE n1.value='博士' 
            RETURN DISTINCT n0 AS n3 LIMIT 10;
                                        '''
    }, {
        'qa': '上市公司是博士学历的高管有多少个?',
        'cypher': '''
               MATCH p0=(n0:高管)-[r0:学历]->(n1:学历) 
            WHERE n1.value='博士'
            RETURN COUNT(DISTINCT n0) AS n3;
                                            '''
    }, {
        'qa': '刘卫国是什么学历?',
        'cypher': '''
                MATCH p0=(n0:高管)-[r0:学历]->(n1:学历) 
            WHERE n0.value='刘卫国'
            RETURN DISTINCT n1 AS n2 LIMIT 10;
                                                '''
    }, {
        'qa': '富奥股份的男性高管有多少个?',
        'cypher': '''
               MATCH p0=(n1:股票名称)<-[r0:股票名称]-(n0:股票)<-[r1:任职于]-(n2:高管)-[r2:性别]->(n3:性别)-[r3:别名]->(n4:性别_别名) 
            WHERE n1.value='富奥股份' AND n4.value='男性'
            RETURN COUNT(DISTINCT n2) AS n3;
                                                    '''
    }, {
        'qa': '同在火力发电行业的上市公司有哪些?',
        'cypher': '''
                MATCH p0=(n0:股票)-[r0:所属行业]->(n1:行业) 
            WHERE n1.value='火力发电' 
            RETURN DISTINCT n0 AS n4 LIMIT 10;
                                                        '''
    }, {
        'qa': '同在火力发电行业的上市公司有多少家?',
        'cypher': '''
                MATCH p0=(n0:股票)-[r0:所属行业]->(n1:行业) 
            WHERE n1.value='火力发电'
            RETURN COUNT(DISTINCT n0) AS n4;
                                                            '''
    }, {
        'qa': '大悦城和荣盛发展是同一个行业嘛?',
        'cypher': '''
                MATCH p0=(n1:股票名称)<-[r0:股票名称]-(n0:股票)-[r1:所属行业]->(n2:行业) 
            WHERE n1.value IN ['大悦城','荣盛发展']
            RETURN DISTINCT n2 AS n5 LIMIT 10;
                                                                '''
    }, {
        'qa': '同在河北的上市公司有哪些?',
        'cypher': '''
                MATCH p0=(n0:股票)-[r0:地域]->(n1:地域) 
            WHERE n1.value='河北' 
            RETURN DISTINCT n0 AS n4 LIMIT 10;
                                                                    '''
    }, {
        'qa': '神州高铁是什么时候上市的?',
        'cypher': '''
               MATCH p0=(n1:股票名称)<-[r0:股票名称]-(n0:股票)-[r1:上市日期]->(n2:上市日期) 
            WHERE n1.value='神州高铁' 
            RETURN DISTINCT n2 AS n1 LIMIT 10;
                                                                       '''
    }, {
        'qa': '火力发电行业男性高管有多少个?',
        'cypher': '''
               MATCH p0=(n1:行业)<-[r0:所属行业]-(n0:股票)<-[r1:任职于]-(n2:高管)-[r2:性别]->(n3:性别)-[r3:别名]->(n4:性别_别名) 
            WHERE n1.value='火力发电' AND n4.value='男性'
            RETURN COUNT(DISTINCT n2) AS n3;
                                                                          '''
    }, {
        'qa': '2023年三月六日上市的股票代码?',
        'cypher': '''
               MATCH p0=(n0:股票)-[r0:上市日期]->(n1:上市日期) 
            WHERE (n1.value>=20230306 AND n1.value<=20230306) 
            RETURN DISTINCT n0 AS n4 LIMIT 10;
                                                                             '''
    }, {
        'qa': '2023年三月六日上市的股票有哪些?',
        'cypher': '''
               MATCH p0=(n0:股票)-[r0:上市日期]->(n1:上市日期) 
            WHERE (n1.value>=20230306 AND n1.value<=20230306) 
            RETURN DISTINCT n0 AS n4 LIMIT 10;
                                                                                 '''
    }, {
        'qa': '2023年三月六日上市的股票有多少个?',
        'cypher': '''
                MATCH p0=(n0:股票)-[r0:上市日期]->(n1:上市日期) 
            WHERE (n1.value>=20230306 AND n1.value<=20230306) 
            RETURN COUNT(DISTINCT n0) AS n4;
                                                                                     '''
    }, {
        'qa': '胡永乐是什么性别?',
        'cypher': '''
                 MATCH p0=(n0:高管)-[r0:性别]->(n1:性别) 
            WHERE n0.value='胡永乐' 
            RETURN DISTINCT n1 AS n7 LIMIT 10;
                                                                                         '''
    }, {
        'qa': '在山东由硕士学历的男性高管任职的上市公司,都属于哪些行业?',
        'cypher': '''
               MATCH 
    	        p1=(n1:`地域`)<-[:`地域`]-(n2:`股票`)<-[:`任职于`]-(n3:`高管`)-[:`性别`]->(n4:`性别`),
                p2=(n3)-[:`学历`]->(n5:学历),
                p3=(n2)-[:`所属行业`]->(n6:行业)
            WHERE n1.value='山东' AND n5.value='硕士' AND n4.value='M'
            RETURN DISTINCT n6.value AS hy;
                                                                                            '''
    }]
    return ex_list


def examples(ask):
    examples_str = ''
    examples_list = []
    for index, map in enumerate(example_list()):
        qa = map['qa']
        cypher = map['cypher']
        dis = Levenshtein.distance(ask, qa)
        examples_list.append({'qa': qa, 'cypher': cypher, 'dis': dis})

    sorted_list = sorted(examples_list, key=lambda map: map['dis'])

    for map in sorted_list:
        qa = map['qa']
        cypher = map['cypher']
        dis = map['dis']
        ex = f'''
          # {qa}
          {cypher}
          '''
        # `prompt(ask)` Prompt Length 300
        if dis < 6 and len(examples_str + ex) + 300 <= 2048:
            examples_str += ex

    return examples_str


def prompt(ask):
    return f"""
        您是一名助手,能够根据示例Cypher查询生成Cypher查询。
        示例Cypher查询有:\n {examples(ask)} \n
        除了Cypher查询之外,不要回复任何解释或任何其他信息。
        您永远不要为你的不准确回复感到抱歉,并严格根据提供的cypher示例生成cypher语句。
        不要提供任何无法从Cypher示例中推断出的Cypher语句。
        当由于缺少对话上下文而无法推断密码语句时,通知用户,并说明缺少的上下文是什么。
        现在请为这个查询生成Cypher:
        # {ask}
        """


if __name__ == '__main__':
    # 输入问题
    ask = '某点信标一直无法识别所有机车上的读写器均如此?'

    print(chatglm(prompt(ask)))

ChatGLM(清华园)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/4/23 17:03
# @Author  : 陈卓见
# @File    : llm_financial_ie.py
# @Description : 这个函数是用来干llm_financial_ie的
# !/usr/bin/env python3

import re
import json

from rich import print
from rich.console import Console
from transformers import AutoTokenizer, AutoModel


# 分类 example
class_examples = {
        '基金': '4月21日,易方达基金公司明星基金经理张坤在管的4只基金产品悉数发布2023年一季报。通联数据显示,一季度张坤的在管规模为889.42亿元,虽然较2022年末的894.34亿元减少约4.92亿元,但同比2022年一季度末的849.27亿元增加了39.15亿元。一季度,除了易方达优质企业三年持有处于封闭期内,其余三只基金产品均遭到了净赎回。其中,易方达蓝筹精选的净赎回份额为6.64亿份。受此影响,3月末张坤的总管理规模较去年年末小幅降低,为889.42亿元。',
        '股票': '国联证券04月23日发布研报称,给予东方财富(300059.SZ,最新价:17.03元)买入评级,目标价格为22.12元。董事长其实表示,对于东方财富未来的增长充满信心'
    }
class_list = list(class_examples.keys())


#CLS_PATTERN 是一个字符串变量,它包含了一个格式化的模板,用于生成文本分类任务中的查询句子。模板的作用是帮助构建一个询问类别的问题,其中 {} 是一个占位符,用于插入一个具体的类别名称。
CLS_PATTERN = f"“{{}}”是 {class_list} 里的什么类别?"
#如果 class_list 包含了 ['基金', '股票'],那么 CLS_PATTERN 将会生成类似于以下的问题:
#“易方达基金”是 ['基金', '股票'] 里的什么类别?

# 定义不同实体下的具备属性
schema = {#名为 schema 的字典
    '基金': ['基金名称', '基金经理', '基金公司', '基金规模', '重仓股'],
    '股票': ['股票名称', '董事长', '涨跌幅']
}

IE_PATTERN = "{}\n\n提取上述句子中{}类型的实体,并按照JSON格式输出,上述句子中不存在的信息用['原文中未提及']来表示,多个值之间用','分隔。"


# 提供一些例子供模型参考
ie_examples = {
        '基金': [
                    {
                        'content': '4月21日,易方达基金公司明星基金经理张坤在管的4只基金产品悉数发布2023年一季报。通联数据显示,一季度张坤的在管规模为889.42亿元,虽然较2022年末的894.34亿元减少约4.92亿元,但同比2022年一季度末的849.27亿元增加了39.15亿元。一季度,除了易方达优质企业三年持有处于封闭期内,其余三只基金产品均遭到了净赎回。其中,易方达蓝筹精选的净赎回份额为6.64亿份。受此影响,3月末张坤的总管理规模较去年年末小幅降低,为889.42亿元。重仓茅台和五粮液。',
                        'answers': {
                                        '基金名称': ['易方达优质企业','易方达蓝筹精选'],
                                        '基金经理': ['张坤'],
                                        '基金公司': ['易方达基金公司'],
                                        '基金规模': ['889.42亿元'],
                                        '重仓股': ['五浪液','茅台']
                            }
                    }
        ],
        '股票': [
                    {
                        'content': '国联证券04月23日发布研报称,给予东方财富(300059.SZ,最新价:17.03元)买入评级,目标价格为22.12元。董事长其实表示,对于东方财富未来的增长充满信心',
                        'answers': {
                                        '股票名称': ['东方财富'],
                                        '董事长': ['其实'],
                                        '涨跌幅': ['原文中未提及']
                            }
                    }
        ]
}

#初始化一些前置提示(prompts)这些提示用于指导预训练的语言模型进行上下文学习(in-context learning)为文本分类和信息抽取任务创建了示例对话,这些对话作为模型推理时的上下文信息。
def init_prompts():
    """
    初始化前置prompt,便于模型做 incontext learning。
    """
    class_list = list(class_examples.keys())#从 class_examples 字典中提取所有的类别键,并存储在 class_list 变量中。
    cls_pre_history = [#教会模型如何进行文本分类
        (
            f'现在你是一个文本分类器,你需要按照要求将我给你的句子分类到:{class_list}类别中。',
            f'好的。'
        )
    ]
    
#_type 代表字典中的键(即类别名称),而 exmpale 代表与该键关联的值(即该类别的一个示例句子)
#cls_pre_history列表最终将被用于模型的上下文学习,作为模型推理时的参考信息。
    for _type, exmpale in class_examples.items():
        cls_pre_history.append((f'“{exmpale}”是 {class_list} 里的什么类别?', _type))

    ie_pre_history = [#教会模型如何进行抽取三元组
        (
            "现在你需要帮助我完成信息抽取任务,当我给你一个句子时,你需要帮我抽取出句子中三元组,并按照JSON的格式输出,上述句子中没有的信息用['原文中未提及']来表示,多个值之间用','分隔。",
            '好的,请输入您的句子。'
        )
    ]
    
    
    
#负责为信息抽取(Information Extraction, IE)任务构建一系列的前置提示(pre-history)。这些提示将用于指导预训练的语言模型如何从给定的句子中抽取特定的信息,并将这些信息以JSON格式输出。
    for _type, example_list in ie_examples.items():
        for example in example_list:
            sentence = example['content']
            properties_str = ', '.join(schema[_type])
            schema_str_list = f'“{_type}”({properties_str})'
            #如果 _type 是 '基金',那么我们想要构建的属性字符串将会是 '基金名称,基金经理,基金公司,基金规模,重仓股'。
            
#使用字符串的 format 方法来填充 IE_PATTERN 模板中的占位符以便构建一个完整的信息抽取任务提示(prompt)
            sentence_with_prompt = IE_PATTERN.format(sentence, schema_str_list)
#IE_PATTERN 是一个字符串模板,它定义了信息抽取任务的格式。这个模板包含两个占位符 {},第一个占位符用于插入待处理的句子,第二个占位符用于插入实体类型和其属性的列表
#format 方法的工作是将 sentence 和 schema_str_list 插入到 IE_PATTERN 模板的相应位置。具体来说:
#第一个 {} 占位符会被 sentence 替换。
#第二个 {} 占位符会被 schema_str_list 替换。
            
            ie_pre_history.append((
                f'{sentence_with_prompt}',
                f"{json.dumps(example['answers'], ensure_ascii=False)}"
            ))
#sentence_with_prompt信息抽取任务提示
#example['answers']预期的答案
#ie_pre_history.append((...)):这个方法将一个元组添加到ie_pre_history列表中。元组中的第一个元素是信息抽取任务的提示,第二个元素是预期的参考答案(以JSON格式)。这样的结构使得在模型推理时,可以将模型的输出与这些参考答案进行比较,以评估模型的性能。

    return {'ie_pre_history': ie_pre_history, 'cls_pre_history': cls_pre_history}#对应信息抽取和文本分类任务的前置提示列表。

#目的是对模型的输出进行后处理,以便确保输出的格式正确并且可以被进一步处理或分析
def clean_response(response: str):
    """
    后处理模型输出。

    Args:
        response (str): _description_
    """
    if '```json' in response:
        res = re.findall(r'```json(.*?)```', response)#通过调用 re.findall 方法和一个正则表达式 r'```json(.*?)```',函数尝试找到所有被 '```json' 标记包围的内容
        
#如果找到匹配项,并且匹配项不为空,那么 response 将被更新为第一个匹配项的内容(res[0])   
        if len(res) and res[0]:
            response = res[0]
        response.replace('、', ',')#检查 response 字符串中是否包含中文逗号 '、',如果是,将其替换为英文逗号 ','
    try:
        return json.loads(response)
    except:
        return response
#函数尝试使用 json.loads 方法将处理后的 response 字符串解析为JSON对象。如果解析成功,返回解析后的JSON对象。
#如果解析失败(例如,response 不是有效的JSON格式),函数将捕获异常,并返回原始的 response 字符串。
    
    
    
    
#用于执行文本分类和信息抽取任务的推理函数
def inference(
        sentences: list,#待处理文本的列表
        custom_settings: dict#用于指导模型推理的自定义设置的字典
    ):
    """
    推理函数。

    Args:
        sentences (List[str]): 待抽取的句子。
        custom_settings (dict): 初始设定,包含人为给定的 few-shot example。
    """
    
    #使用 console.status 来显示当前模型正在执行推理任务的状态信息
    for sentence in sentences:
        with console.status("[bold bright_green] Model Inference..."):#状态信息
            sentence_with_cls_prompt = CLS_PATTERN.format(sentence)
            cls_res, _ = model.chat(tokenizer, sentence_with_cls_prompt, history=custom_settings['cls_pre_history'])
#tokenizer 用于将文本转换为模型可理解的格式,custom_settings['cls_pre_history'] 提供了分类任务的上下文信息(例如之前分类的例子)。
            if cls_res not in schema:
                print(f'The type model inferenced {cls_res} which is not in schema dict, exited.')
                exit()

            properties_str = ', '.join(schema[cls_res])
            schema_str_list = f'“{cls_res}”({properties_str})'#使用属性字符串构建一个用于信息抽取的提示。
            sentence_with_ie_prompt = IE_PATTERN.format(sentence, schema_str_list)#这一行将句子和构建好的信息抽取提示插入到预定义的信息抽取提示模板 IE_PATTERN 中。
            ie_res, _ = model.chat(tokenizer, sentence_with_ie_prompt, history=custom_settings['ie_pre_history'])#再次调用模型的 chat 方法来执行信息抽取任务
            ie_res = clean_response(ie_res)
        print(f'>>> [bold bright_red]sentence: {sentence}')#原始文本
        print(f'>>> [bold bright_green]inference answer: ')#分类或抽取的结果
        print(ie_res)


if __name__ == '__main__':
    console = Console()

    device = 'cuda:0'
    tokenizer = AutoTokenizer.from_pretrained("/data/zhuojian/models/chatglm-6b", trust_remote_code=True)
    model = AutoModel.from_pretrained("/data/zhuojian/models/chatglm-6b", trust_remote_code=True).half().cuda()
    model = model.eval()#上下文管理器,当调用这个上下文管理器时,eval()会将模型中的所有模块都设置为评估模式。
    #实际应用中,如果在训练模型之后想要对其进行评估或使用模型进行预测,需要确保在开始预测之前调用 model.eval()。这样可以确保模型在推理时使用正确的参数和行为。如果你忘记调用这个函数,模型可能会继续以训练模式运行,这可能会导致不准确的预测结果,因为一些层的行为在训练和评估时是不同的。

    sentences = [#定义了一个名为 sentences 的Python列表
        '同花顺董事长易峥虚拟“分身”谈人工智能:ALL IN但也要保持清醒',
        '葛兰一季度末管理规模再度跌破900亿元,同时交出了公募主动权益基金管理规模的头把交椅。从管理规模来看,一季度末葛兰在管5只公募基金合计管理规模降至844.40亿元,较2022年末的906.53亿元,环比下降6.85%。葛兰一季度调仓力度并不大。以中欧医疗健康混合基金为例',
        '东财芯片ETF即将发售,初始募集金额为20亿'
    ]#改为字典{}

    custom_settings = init_prompts()
    inference(
        sentences,
        custom_settings
    )
    #用于执行文本分类和信息抽取任务的推理函数接收两个参数:sentences 是一个包含待处理文本的列表,custom_settings 是一个包含用于指导模型推理的自定义设置的字典。这个函数的主要目的是利用预训练的语言模型来对输入的文本进行分类和信息抽取,并输出结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值