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

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] = Noneinput_key: Optional[str] = Nonereturn_messages: bool = Falsedef _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_keyif 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_keyreturn 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: strhuman_prefix: str = "Human"ai_prefix: str = "Assistant"llm: BaseLanguageModelmemory_key: str = "history"max_token_limit: int = 2000message_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. 为新的对话生成一个全局唯一的uuid2. 将这个conversation_id插入到数据库中 同时确保外键约束"""conv_id = uuid.uuid4().hextry: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.mzph.cn/pingmian/16328.shtml

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

相关文章

vulnhub靶场之FunBox-8

一.环境搭建 1.靶场描述 Its a box for beginners and can be pwned in the lunch break. This works better with VirtualBox rather than VMware 2.靶场下载 Funbox: Lunchbreaker ~ VulnHub 3.靶场启动 二.信息收集 1.寻找靶场真实IP地址 nmap -sP 192.168.2.0/24 arp-…

html中被忽略的简单标签

1&#xff1a; alt的作用是在图片不能显示时的提示信息 <img src"https://img.xunfei.cn/mall/dev/ifly-mall-vip- service/business/vip/common/202404071019208761.jp" alt"提示信息" width"100px" height"100px" /> 2&#…

嵌入式进阶——震动马达

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 原理图控制分析功能设计 原理图 控制分析 S8050 NPN三极管特性 NPN型三极管的工作原理是基于PN结和PNP型晶体管的工作原理。 当外…

【qt】QTreeWidget 树形组件

QTreeWidget 树形组件 一.什么是树形组件二.界面设计树形组件三.代码实现1.清空2.设置列数3.设置头标签4.添加根目录①QTreeWidgetitem②设置文本③设置图标④添加为顶层目录 5.添加子目录①初始化为父目录②子目录添加到父目录③获取到子目录 四.插入目录1.获取当前选中目录项…

部署PIM-SM

拓扑图 配置 使能组播路由 配置OSPF 组播路由器接口配置pim-sm 连接组成员的接口使能igmp pim路由器上配置静态RP sysname AR1 # multicast routing-enable # interface GigabitEthernet0/0/0ip address 10.1.12.1 255.255.255.0 pim sm # interface GigabitEthernet0/0/…

flutter项目运行报错Exception: Gradle task assembleDebug failed with exit code 1各种报错合集

1.报错 Launching lib/main.dart on sdk gphone64 arm64 in debug mode... Running Gradle task assembleDebug... Exception in thread "main" java.net.ConnectException: Operation timed out at java.base/sun.nio.ch.Net.connect0(Native Method) at j…

云动态摘要 2024-05-26

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [免费试用]大模型知识引擎体验招募 腾讯云 2024-05-21 大模型知识引擎产品全新上线&#xff0c;为回馈新老客户&#xff0c;50万token免费送&#xff0c;开通服务即领取&#xff01; 云服…

【学习心得】回归任务的评估指标决定系数R^2

一、决定系数是什么&#xff1f; scikit-learn库在进行回归任务的时候&#xff0c;进行模型评估时的score()方法&#xff0c;默认采取的是计算的是决定系数&#xff08;Coefficient of Determination&#xff09;&#xff0c;通常表示为得分。这个值衡量了模型预测值与实际观测…

【开源可视化报表设计器】借力实现高效率流程化办公!

进行数字化转型、实现流程化办公&#xff0c;这些应该是目前很多企业都想要实现的目标吧。那么&#xff0c;利用什么样的软件平台可以实现&#xff1f;低代码技术平台拥有可视化界面、灵活操作、好维护等众多优势特点&#xff0c;可以借助低代码技术平台、开源可视化报表设计器…

游戏缺失steam_api64.dll的修复方法,快速解决游戏启动问题

在现代科技发展的时代&#xff0c;电脑已经成为我们生活中不可或缺的一部分。然而&#xff0c;在使用电脑的过程中&#xff0c;我们经常会遇到一些常见的问题&#xff0c;其中之一就是找不到某个特定的动态链接库文件&#xff0c;比如steamapi64.dll。这个问题可能会导致某些应…

深度学习中的优化算法二(Pytorch 19)

一 梯度下降 尽管梯度下降&#xff08;gradient descent&#xff09;很少直接用于深度学习&#xff0c;但了解它是理解下一节 随机梯度下降算法 的关键。例如&#xff0c;由于学习率过大&#xff0c;优化问题可能会发散&#xff0c;这种现象早已在梯度下降中出现。同样地&…

民国漫画杂志《时代漫画》第25期.PDF

时代漫画25.PDF: https://url03.ctfile.com/f/1779803-1248635084-fd4794?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

03:PostgreSQL逻辑结构(表空间、数据库、模式、表、索引)

环境规划&#xff1a; 操作系统&#xff1a;CentOS 7.9 64bitPostgreSQL 版本&#xff1a;16.x 或 15.x安装用户&#xff1a;postgres软件安装目标路径&#xff1a;/usr/pgsql-<version>数据库数据目录&#xff1a;/pgdata 目录 表空间Tablespace 默认表空间 手动创建…

民国漫画杂志《时代漫画》第15期.PDF

时代漫画15.PDF: https://url03.ctfile.com/f/1779803-1247458444-8befd8?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps:资源来源网络&#xff01;

【LeetCode:2769. 找出最大的可达成数字 + 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Nodejs(文件操作,构建服务器,express,npm)

文章目录 文件操作1.读取文件1&#xff09;步骤2&#xff09;范例 2.写文件1&#xff09;步骤2&#xff09;范例 3.删除文件4.重命名文件夹5删除文件夹 Url1.url.parse()2.url.fomat() Query1.query.parse()2.query.stringfy()3.编码和解码 第三方模块1.nodemailer2.body-parse…

BUUCTF-Misc24

从娃娃抓起1 1.打开附件 是两个文本文件 2.电报码 电报码在线翻译网站&#xff1a;https://usetoolbar.com/convert/cccn.html 3.汉字五笔编码 汉字五笔编码在线网站查询&#xff1a;https://www.qqxiuzi.cn/bianma/wubi.php 4.转化为MD5值 将文字保存到文本文档 用winR输入…

部署ELK日志分析系统——超详细

ELK日志分析系统 文章目录 ELK日志分析系统资源列表基础环境一、环境准备二、部署Elasticsearch软件2.1、安装Elasticsearch软件2.2、加载系统服务2.3、更改Elasticsearch主配置文件2.4、创建数据存放路径并授权2.5、启动Elasticsearch2.6、查看节点信息 三、安装Elasticsearch…

【WEEK13】 【DAY5】Shiro第五部分【中文版】

2024.5.24 Friday 接上文【WEEK13】 【DAY4】Shiro第四部分【中文版】 目录 15.7.Shiro请求授权的实现15.7.1.修改ShiroConfig.java15.7.1.1.添加一行验证授权的代码15.7.1.2.重启 15.7.2.修改MyController.java15.7.3.修改ShiroConfig.java15.7.4.重启15.7.5.修改UserRealm.ja…