创新实训2024.05.26日志:服务端接口实现——用户开启多个会话

news2024/12/23 10:25:28

1. 概念图

类似于Kimi,文心一言,chatGPT等市面上主流的大模型,我们的大模型也支持同一个用户的多个会话,并且提供支持联系上下文给出解答的能力。

2. 基于会话的对话

在langchain chatchat这个对langchain框架进行二次封装的第三方框架中,提供了一个chat函数接口:

async def chat(query: str = Body(..., description="用户输入", examples=["恼羞成怒"]),
               conversation_id: str = Body("", description="对话框ID"),
               history_len: int = Body(-1, description="从数据库中取历史消息的数量"),
               history: Union[int, List[History]] = Body([],
                                                         description="历史对话,设为一个整数可以从数据库中读取历史消息",
                                                         examples=[[
                                                             {"role": "user",
                                                              "content": "我们来玩成语接龙,我先来,生龙活虎"},
                                                             {"role": "assistant", "content": "虎头虎脑"}]]
                                                         ),
               stream: bool = Body(False, description="流式输出"),
               model_name: str = Body(LLM_MODELS[0], description="LLM 模型名称。"),
               temperature: float = Body(TEMPERATURE, description="LLM 采样温度", ge=0.0, le=2.0),
               max_tokens: Optional[int] = Body(None, description="限制LLM生成Token数量,默认None代表模型最大值"),
               # top_p: float = Body(TOP_P, description="LLM 核采样。勿与temperature同时设置", gt=0.0, lt=1.0),
               prompt_name: str = Body("default", description="使用的prompt模板名称(在configs/prompt_config.py中配置)"),
               ):

2.1. 提取上下文

这里使用conversation_id对每次会话进行标记,并且在当前对话中,查询数据库中对应会话id的消息记录,使得能够进行联系上下文的对话。

memory = ConversationBufferDBMemory(conversation_id=conversation_id,
                                   llm=model,
                                   message_limit=history_len)

这个ConversationBufferDBMemory的功能是管理和维护与特定对话ID相关的消息缓存,以支持基于历史对话的智能助手响应生成。他继承自Langchain提供的一个基类:BaseChatMemory

class BaseChatMemory(BaseMemory, ABC):
    """Abstract base class for chat memory."""

    chat_memory: BaseChatMessageHistory = Field(default_factory=ChatMessageHistory)
    output_key: Optional[str] = None
    input_key: Optional[str] = None
    return_messages: bool = False

    def _get_input_output(
        self, inputs: Dict[str, Any], outputs: Dict[str, str]
    ) -> Tuple[str, str]:
        if self.input_key is None:
            prompt_input_key = get_prompt_input_key(inputs, self.memory_variables)
        else:
            prompt_input_key = self.input_key
        if self.output_key is None:
            if len(outputs) != 1:
                raise ValueError(f"One output key expected, got {outputs.keys()}")
            output_key = list(outputs.keys())[0]
        else:
            output_key = self.output_key
        return inputs[prompt_input_key], outputs[output_key]

    def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
        """Save context from this conversation to buffer."""
        input_str, output_str = self._get_input_output(inputs, outputs)
        self.chat_memory.add_user_message(input_str)
        self.chat_memory.add_ai_message(output_str)

    def clear(self) -> None:
        """Clear memory contents."""
        self.chat_memory.clear()
  • _get_input_output: 一个私有方法,用于获取输入和输出的键。如果没有设置 input_key 或 output_key,则使用 get_prompt_input_key 函数或默认输出键。
  • save_context: 一个公共方法,用于保存对话上下文到缓冲区。它从 inputs 和 outputs 字典中提取输入和输出字符串,并调用 chat_memory 的方法来添加用户消息和AI消息。
  • clear: 一个公共方法,用于清除记忆内容,通过调用 chat_memory 的 clear 方法实现。

我们要做的就是覆写这个基类,由于我们是基于conversation_id(也即会话id)进行上下文关联记录的,因此这个覆写的子类中,最重要的就是这个会话id属性。此外,提取历史记录时,需要区分ai与人类的角色,因此还需要消息前缀,来区分是ai回复的,还是用户提问的:

    conversation_id: str
    human_prefix: str = "Human"
    ai_prefix: str = "Assistant"
    llm: BaseLanguageModel
    memory_key: str = "history"
    max_token_limit: int = 2000
    message_limit: int = 10

提取上下文完毕后,将生成类似于:

[
    HumanMessage(content="你好,助手。"),
    AIMessage(content="你好!有什么可以帮助你的吗?"),
    HumanMessage(content="我想了解天气预报。"),
    AIMessage(content="请告诉我你的城市。"),
    ...
]

的缓冲区历史记录

2.2. 生成prompt模板

我们想方设法的还原上下文,是为了让ai知道之前和他的对话中都发生过什么。从而基于这个语境回答用户之后的问题。因此从ConversationBuffer中拿到之前的会话信息后,我们就需要生成prompt模板,使得模型能够基于上下文环境回答问题。

这个过程是开发者根据具体需求手动去写prompt进行,随后调用langchain中已经封装好的prompt template库进行优化的。

# 使用memory 时必须 prompt 必须含有memory.memory_key 对应的变量
prompt = get_prompt_template("llm_chat", "with_history")
chat_prompt = PromptTemplate.from_template(prompt)
# 根据conversation_id 获取message 列表进而拼凑 memory
memory = ConversationBufferDBMemory(conversation_id=conversation_id,
                                    llm=model,
                                    message_limit=history_len)
                                    
chain = LLMChain(prompt=chat_prompt, llm=model, memory=memory)

其中from_template与LLMChain都是langchain封装好的,前者是用来优化开发者的prompt模板的,后者是使用优化的prompt模板与历史会话信息(也即memory)进行大模型对话。

而开发者编写的模板,其实就和平时我们与大模型进行对话差不多:

"with_history":
    'The following is a friendly conversation between a human and an AI. '
    'The AI is talkative and provides lots of specific details from its context. '
    'If the AI does not know the answer to a question, it truthfully says it does not know.\\n\\n'
    'Current conversation:\\n'
    '{history}\\n'
    'Human: {input}\\n'
    'AI:',

例如,我们可以在基于上下文会话信息的对话中,告诉他,接下来我们给他的对话是一个人类和一个AI之间的对话,如果AI不知道人类的问题的答案,他就诚实的说不知道。并且告诉他这个对话的格式是先Human,再AI的,对应了我们上面的human/ai_prefix

2.3. 与大模型对话

随后的部分,交给大模型与RAG技术,利用预训练与微调后的大模型自身的能力,通过RAG技术与向量知识库建立连接,检索相关知识,给出回答。

3. 接口实现

首先这个新建会话的功能肯定是异步的,因为一个用户新建一个会话和另一个用户新建会话是没有任何依赖关系的。

在为每个会话生成唯一标识时,我采用了python的uuid。随后向Conversation表中添加这个新的会话的记录。

注意这里防止用户伪造请求,我们要捕获user_id不存在引发的外键约束的异常。

async def new_conversation(nc: NewConv) -> BaseResponse:
    """
    用户建立新的对话:
    1. 为新的对话生成一个全局唯一的uuid
    2. 将这个conversation_id插入到数据库中 同时确保外键约束
    """
    conv_id = uuid.uuid4().hex

    try:
        with Session(engine) as session:
            session.add(Conversation(id=conv_id, conv_name=nc.conv_name, create_time=datetime.datetime.utcnow(),
                                     user_id=nc.user_id))
            session.commit()
        logging.info(f"{nc.user_id} 创建了会话 {conv_id} 会话名{nc.conv_name}")
    except Exception as e:
        logging.error(f"{nc.user_id} 创建会话失败 {e}")
        return BaseResponse(code=200, message="会话创建失败", data={"error": f'{e}'})

    return BaseResponse(code=200, message="会话创建成功", data={"conv_id": conv_id})

随后将这个函数作为post接口调用的回调函数即可。

app.post(self.generate_route_path(["new-conversation"]), tags=self.tag, response_model=BaseResponse,
                 summary="用户创建新会话")(new_conversation)

4. 接口测试

我们可以利用.http文件编写接口测试

假设数据库中现在有一个名叫lyh的用户,他的id是:d078b124cf27413bbb99f6484782e98c

基于这个id,我们进行新会话的创建:

POST <http://127.0.0.1:9090/conversation/new-conversation>
Content-Type: application/json
Accept: application/json

{
  "user_id": "d078b124cf27413bbb99f6484782e98c",
  "conv_name": "dummy_conv"
}

回环地址是因为目前测试环境和开发环境是一个,都是在本机。

成功,返回了会话id。

可以看到数据库中新插入了一条会话记录。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汇编语言程序设计-5-流程转移与子程序

5. 流程转移与子程序 文章目录 5. 流程转移与子程序5.0 导学5.1 “转移”综述5.2 操作符offset5.3 jmp指令5.4 其他转移指令-jcxz、loop5.5 call指令和ret指令5.6 call和ret的配合使用5.7 mul指令5.8 汇编语言的模块化程序设计5.9 寄存器冲突的问题-子程序标准框架5.10 标志寄存…

postgresql|数据库|闪回插件e-maj的部署和使用

前言&#xff1a; E-Maj 是 PostgreSQL 数据库的一个扩展插件&#xff0c;它的全称为 "Elementary Majordomo"。这个扩展的主要功能是为数据库中的表集提供细粒度的写入日志记录和时间旅行能力。这意味着使用 E-Maj 的用户可以在数据库的特定子集上实现事务的回滚&a…

python列表元素的增减之道:删除篇

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、前言 二、删除元素的基本方法 1. 使用remove()方法 2. 使用pop()方法 3. 使用del语句…

mars3d的V2版本的Video2D与V3版本的Video2D实现数据快速迁移

场景&#xff1a; 目前是v2和v3的两个相机视角的不同格式&#xff0c;在Mars3d的V2的旧数据想可以快速迁移到V3版本。 V2版本的数据&#xff1a; {"camera": {"fov": 1.0471975511965976,"dis": 20,"stRotation": 0,"showFrust…

第 33 次CCF认证

1. 词频统计 题目描述 样例输入 代码 #include <bits/stdc.h>using namespace std;int main() {int n,m;cin>>n>>m;vector<int> ans1(m,0),ans2(m,0);while (n --) {int t;cin>>t;vector<int> vis(m1,0);for (int i 1;i < t;i ) {i…

这样的直男程序员,活该你单身一万年!

#分享下相亲时遇到过哪些奇葩现象# 这样的直男程序员&#xff0c;活该你单身一万年&#xff01; 在丛丛脱单小程序上相亲&#xff0c;遇到一个程序员妹纸&#xff0c;于是有了如下的真实故事&#xff1a; 妹子说她是程序员来着&#xff0c;想着我也是程序员&#xff0c;就想交…

【HMGD】STM32/GD32 CAN通信

各种通信协议速度分析 协议最高速度(btis/s)I2C400KCAN1MCAN-FD5M48510MSPI36M CAN协议图和通信帧 CubeMX CAN配置说明 CAN通信波特率 APB1频率 / 分频系数 /&#xff08;BS1 BS2 同步通信段&#xff09;* 1000 ​ 42 / 1 / (111) * 1000 ​ 14,000 KHz ​ 1400000…

1.4 Mac 电脑 Clion 安装教程

目录 1 安装 2 激活 3 汉化 1 安装 去 https://www.jetbrains.com/clion/download/other.html 下载: 也可以直接到链接进行下载:https

DOS学习-目录与文件应用操作经典案例-comp

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一.前言 二.使用 三.案例 案例 1: 基本比较 案例 2: 十进制显示差异 案例 3: 字符形式显…

1-Django开端--学生管理系统

目录 项目结构 前端页面: add_data.html class_data.html index.html apps.py models.py views.py settings,py urls.py ...实现简略的身架... 项目结构 前端页面: add_data.html --添加数据. {% extends index/index.html %}{% block content %} <div class&qu…

基于机器学习的一线城市租房价格预测分析与实现,实现三种算法预测

本文旨在基于机器学习方法&#xff0c;对一线城市租房价格进行预测分析&#xff0c;并使用Matplotlib可视化、随机森林、一元线性回归和多元线性模型进行模型对比。通过爬取北京链家二手房数据作为研究对象&#xff0c;探讨了租房价格与各种因素之间的关系&#xff0c;阐述了研…

实时计算及异构计算随笔笔记

3、异构计算的典型应用 异构计算并不神秘&#xff0c;目前已渗透各个领域&#xff0c;不仅是PC领域&#xff0c;也包括了手持移动设备领域、行业领域&#xff0c;甚至是云计算、分布式计算领域。事实上&#xff0c;异构计算至少在应用端&#xff08;前台&#xff09;并不像它的…

【java程序设计期末复习】chapter4 类和对象

类和对象 编程语言的几个发展阶段 &#xff08;1&#xff09;面向机器语言 计算机处理信息的早期语言是所谓的机器语言&#xff0c;使用机器语言进行程序设计需要面向机器来编写代码&#xff0c;即需要针对不同的机器编写诸如0101 1100这样的指令序列。 &#xff08;2&#x…

【XSS CSRF 】访问时篡改密码——以DVWA-High为例

【XSS & CSRF 】泄露cookie——以DVWA-High为例-CSDN博客第一阶段 目录 前言 一、场景想定 二、过程步骤 1.High等级下的CSRF利用 2.XSSCSRF实现页面访问后密码被修改 三、最终利用——cookie可变下的admin密码修改 1.cookie可变 2.利用过程 总结 前言 第二阶段…

编写子函数+最大公约数和最小公倍数

目录 计算级数和 判断并找出非素数 主函数操作流程 求最大公约数和最小公倍数 编写子函数&#xff0c;该函数的功能是是计算下列级数和&#xff0c;并将和值返回主调函数输出。数据由主函数输入。 fun 函数 sum 函数 main 函数 注意事项 编写函数&#xff0c;该函数的…

C语言——malloc和free用法和常见误区

最近写了个关于动态数组的代码&#xff0c;遇到了一个大坑&#xff0c;特此记录 先说结论&#xff1a; 1.利用malloc创建堆空间&#xff0c;大小最好设置大一点&#xff0c;不然后面存进去的值需要的空间过大会导致各种的堆、指针问题 2.只能使用realloc对已经创建的空间进行修…

自定义类型:结构体详解

1.结构体 1.1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。一个整型数组&#xff0c;它的每个数组元素只能是整型&#xff0c;字符型的数组它的每个元素只能是字符型。但是结构体的每个成员可以是各种不同类型的变量。 1.2结构的声明 //声明 struct t…

视频拼接融合产品的产品与架构设计(四)分布式GPU运算合并单元

上一篇如下 视频拼接融合产品的产品与架构设计(三&#xff09;内存和显存单元数据迁移 视频合并单元说明 对下面这张图做些说明&#xff0c;视频接入是比较常见&#xff0c;可以说是普通&#xff0c;但是做到接入后随即进行比较重的算法运算&#xff0c;这个在视频领域并不多…

海外抖音TK自动挂机,手机全自动挂机,每天轻松搞2张

海外抖音TK自动挂机&#xff0c;手机全自动挂机&#xff0c;每天轻松搞2张 课程获取方式&#xff1a; https://zzmbk.com/

linux系统部署Oracle11g:netca成功启动后1521端口未能启动问题

一、问题描述 执行netca命令&#xff0c;进入图形化界面&#xff0c;进行Oracle端口监听设置 #终端输入命令 netca 最终提示设置成功&#xff1a; 但是我们进行下一步“创建数据库”的时候会报错&#xff0c;说数据库端口1521未开启&#xff01; 二、问题处理 使用命令查看开…