用LangChain打造一个可以管理日程的智能助手

  • 存储设计
  • 定义工具
  • 创建llm
  • 提示词模板
  • 创建Agent
  • 执行
  • 总结

  众所周知,GPT可以认为是一个离线的软件的,对于一些实时性有要求的功能是完全不行,比如实时信息检索,再比如我们今天要实现个一个日程管理的功能,这个功能你纯依赖于ChatGPT或者其他大语言模型(后文简称llm),是完全实现不了的,比如你这次让他帮你记录个日程,你要是和他聊的内容过多,历史聊天记录滚动覆盖了就找不回来了。 你要是换个聊天窗口,之前的日程信息你就更找不回来了,其根本原因是目前所有的llm都是无状态的,每轮对话必须携带所有历史聊天记录才能实现多轮对话,而所有的llm都有输入长度限制,比如gpt4目前是128k。

存储设计

  所以,如果要实现日程记录永不丢失我们就需要用第三方存储来记录所有的日程信息,这里为了简单,我直接使用了sqlite3(用mysql或者其他存储都是可以的),我创建了一个非常简单的日程表,只有一个时间和描述,整体代码如下:

# 连接到 SQLite 数据库
# 如果文件不存在,会自动在当前目录创建一个名为 'langchain.db' 的数据库文件
import sqlite3
conn = sqlite3.connect('langchain.db')# 创建一个 Cursor 对象并通过它执行 SQL 语句
c = conn.cursor()
# 创建表
c.execute('''
create table if not exists schedules 
(id          INTEGER    primary key autoincrement,start_time  TEXT default (strftime('%Y-%m-%d %H:%M:%S', 'now', 'localtime')) not null,description text default ''                                                  not null
);
''')conn.commit()
conn.close()
print("数据库和表已成功创建!")

定义工具

  那么接下来的问题就是如何让GPT能够查询和操作这个表了。这里我们直接使用了LangChain的@tool装饰器,讲schedules表的基本操作设置为GPT可以识别的接口,当然使用OpenAI的纯原始接口也是可以实现的(参加我之前的文章OpenAI的多函数调用),就是代码量相对会多很多。具体的代码如下,这里我定义了对schedules表的增、删、查的功能。


def connect_db():""" 连接到数据库 """conn = sqlite3.connect('langchain.db')return conn@tool
def add_schedule(start_time : str, description : str) -> str: """ 新增日程,比如2024-05-03 20:00:00, 周会 """conn = connect_db()cursor = conn.cursor()cursor.execute("""INSERT INTO schedules (start_time, description) VALUES (?, ?);""", (start_time, description,))conn.commit()conn.close()return "true"@tool
def delete_schedule_by_time(start_time : str) -> str:""" 根据时间删除日程 """conn = connect_db()cursor = conn.cursor()cursor.execute("""DELETE FROM schedules WHERE start_time = ?;""", (start_time,))conn.commit()conn.close()return "true"@tool
def get_schedules_by_date(query_date : str) -> str:""" 根据日期查询日程,比如 获取2024-05-03的所有日程 """conn = connect_db()cursor = conn.cursor()cursor.execute("""SELECT start_time, description FROM schedules WHERE start_time LIKE ?;""", (f"{query_date}%",))schedules = cursor.fetchall()conn.close()return str(schedules)

创建llm

  到这里,所以依赖的逻辑就已经完成了,接下来就是创建agent了,首先就是想定义好llm,这里我还是选用了OpenAI的gpt3.5,(个人认为这是目前性价比最高的模型),注意llm必须要调用bind_tools方法绑定好我们上面声明好的工具

## 创建llm
llm = ChatOpenAI(model="gpt-3.5-turbo", max_tokens=4096)
tools = [add_schedule, delete_schedule_by_time, get_schedules_by_date]
llm_with_tools = llm.bind_tools(tools)

提示词模板

  然后就是创建提示词模板,这里额外提一下,因为目前所有的llm都不具备对时间的感知能力,所以这里必须在模板里将当前时间传给llm,方便llm去做时间的计算

## 创建提示词模板  
prompt = ChatPromptTemplate.from_messages([("system","你是一个日程管理助手",),("placeholder", "{chat_history}"),("user", "{input} \n\n 当前时间为:{current_time}"),("placeholder", "{agent_scratchpad}"),]
)

创建Agent

  之后就是创建agent和执行器了,这里自己创建一个一遍,又直接使用了LangChain封装好的方法创建了一遍,二者功能上没有区别,区别就是直接用别人的方法,自己可以少写两行代码。


## agent创建方式1 
from langchain.agents.format_scratchpad.openai_tools import (format_to_openai_tool_messages,
)
agent = ({"current_time": lambda x: x["current_time"],"input": lambda x: x["input"],"agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),}| prompt| llm_with_tools| OpenAIToolsAgentOutputParser()
)## agent创建方式2
agent = create_tool_calling_agent(llm_with_tools, tools, prompt)  
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=False)

执行

  用如下的方式就可以执行agent验证功能是否可以正常了。

invoke({"input": "查询下我明天有啥安排?","current_time": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')  # 当前时间必须传})

  这里我简单实现了一个多轮对话用来验证各功能是否正常。


def ask(question):res = agent_executor.invoke({"input": question,"current_time": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})return res["output"]while True:question = input(">")if question.lower() == '退出':breakprint(ask(question))
> 删除今天所有的日程
已成功删除今天所有的日程。
> 创建一套明天晚上6点的日程,开周会
日程已成功创建,明天晚上6点有周会安排。
> 我明天第一条日程是啥?
您明天的第一条日程是沟通会,时间为2024-05-05 09:00:00。祝您顺利!
> 看下我明天早上10点有没有安排?
明天早上10点没有安排,您的日程是:
- 09:00:00 沟通会
- 18:00:00 周会
> 把我明天早上9点的会议改到10点
已成功将您明天早上9点的会议改到10点。

总结

  日程管理的能力本质上还是建立在llm的函数调用能力,说白了其实你告诉llm有什么样的函数可以调用,然后让llm自行决策是否需要调用,这也是当下llm智能的体现。使用LangChain其实也只是将函数的定义、调用以及结果返回的流程简化而已。这里额外说下,上面代码中,我并未给llm提供修改日程的方法,但后续测试工程中我让它修改某个日程,它居然修改成功了,你猜它是怎么实现的?

备注:本文完整示例代码已放在Github https://github.com/xindoo/langchain-examples/blob/main/schedules.ipynb。

在这里插入图片描述

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

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

相关文章

Hdfs小文件治理策略以及治理经验

小文件是 Hadoop 集群运维中的常见挑战,尤其对于大规模运行的集群来说可谓至关重要。如果处理不好,可能会导致许多并发症。Hadoop集群本质是为了TB,PB规模的数据存储和计算因运而生的。为啥大数据开发都说小文件的治理重要,说HDFS 存储小文件…

Rust中的并发性:Sync 和 Send Traits

在并发的世界中,最常见的并发安全问题就是数据竞争,也就是两个线程同时对一个变量进行读写操作。但当你在 Safe Rust 中写出有数据竞争的代码时,编译器会直接拒绝编译。那么它是靠什么魔法做到的呢? 这就不得不谈 Send 和 Sync 这…

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘ 原因是notebook新版本没有notebook.auth 直接输入以下命令即可设置密码 jupyter notebook password

ICode国际青少年编程竞赛- Python-1级训练场-基础训练1

ICode国际青少年编程竞赛- Python-1级训练场-基础训练1 1、 Dev.step(4)2、 Dev.step(-4) Dev.step(8)3、 Dev.turnLeft() Dev.step(4)4、 Dev.step(3) Dev.turnLeft() Dev.step(-1) Dev.step(4)5、 Dev.step(-1) Dev.step(3) Dev.step(-2) Dev.turnLeft() Dev.step(…

tomcat打开乱码修改端口

将UTF-8改成GBK 如果端口冲突,需要修改tomcat的端口

PR2019软件下载教程

打开下载网址:rjctx.com 选择Premiere: 选择PR2019,并点击: 拉到最后,选择百度网盘下载: 下载到本地。 二,软件安装 解压缩后,双击set_up 选择位置后,进行安装&…

Java中接口的默认方法

为什么要使用默认方法 当我们把一个程序的接口写完后 用其他的类去实现,此时如果程序需要再添加一个抽象方法的时候我们只有两种选择 将抽象方法写在原本的接口中 但是这样写会导致其他所有改接口的实现类都需要实现这个抽象方法比较麻烦 写另一个接口 让需要的实…

13.1 QQ邮箱

1. 邮箱发送 2. 准备工作 3. 整合SpringBoot 3.1 配置 依赖引入 <!-- 邮件服务--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>application.…

Ubuntu GUI使用Root用户登录指南

Ubuntu GUI使用Root用户登录指南 一、前言 默认情况下&#xff0c;Ubuntu 禁用了 root 账户&#xff0c;我们必须使用 sudo 命令来执行任何需要 root 权限的任务&#xff0c;比如像这样删除一个系统配置文件&#xff08;操作危险&#xff0c;请勿尝试&#xff09;&#xff1a;…

接收区块链的CCF会议--APSEC 2024 截止7.13 附录用率

会议名称&#xff1a;APSEC&#xff08;Asia-Pacific Software Engineering Conference&#xff09; CCF等级&#xff1a;CCF C类学术会议 类别&#xff1a;软件工程/系统软件/程序设计语言 录用率&#xff1a;2023年&#xff0c;90 submissions were recommended for accep…

【前端项目——分页器】手写分页器实现(JS / React)

组件介绍 用了两种方式实现&#xff0c;注释详细~ 可能代码写的不够简洁&#xff0c;见谅&#x1f641; 1. 包含内容显示的分页器 网上看了很多实现&#xff0c;很多只有分页器部分&#xff0c;没和内容显示联动。 因此我增加了模拟content的显示&#xff0c;这里模拟了32条数…

如何免费体验 gpt2-chatbot

如何免费体验 gpt2-chatbot 就在五一假期期间&#xff0c;一个神秘模型在没有任何官方文件的情况下突然发布。发布后不到 12 小时就立即引起人工智能爱好者和专家们的关注。这个名为“gpt2-chatbot”的神秘新模型凭借其令人印象深刻的能力轰动全球。有人猜测它可能是 OpenAI 的…

Docker Compose如何安装

Docker Compose的安装通常依赖于你的操作系统。以下是在不同操作系统中安装Docker Compose的方法&#xff1a; Linux 系统 //下载最新版本的Docker Compose sudo curl -L "https://github.com/docker/compose/releases/download/v2.5.1/docker-compose-$(uname -s)-$(un…

SQL如何利用Bitmap思想优化array_contains()函数

目录 0 问题描述 1 位图思想 2 案例实战 3 小结 0 问题描述 在工作中&#xff0c;我们往往使用array_contains()函数来进行存在性问题分析&#xff0c;如判断某个数是否在某个数组中&#xff0c;但是当表数据量过多&#xff0c;存在大量array_contains()函数时&#xff0c;…

C语言:数据结构(双向链表)

目录 1、双向链表的结构2、顺序表和双向链表的优缺点分析3、双向链表的实现 1、双向链表的结构 注意&#xff1a;这⾥的“带头“跟前面我们说的“头节点”是两个概念&#xff0c;实际前面的在单链表阶段称呼不严谨&#xff0c;但是为了更好的理解就直接称为单链表的头节点。 带…

网络聊天室:通过Servlet和JSP,结合session和application实现(文末附源码)

目录 一.成品效果 二.代码部分 chat.jsp ChatServlet 一.成品效果 在启动成功后&#xff0c;我们就可以在任意俩个浏览器页面中相互发消息&#xff0c;如图所示左边屏幕使用的是Edge浏览器&#xff0c;右图使用的是火狐浏览器。当然笔者这里只是简单实现最基本的一些功能&…

从键入网址到网页显示,期间发生了什么?

从键入网址到网页显示&#xff0c;期间发生了什么&#xff1f; 孤单小弟【HTTP】真实地址查询【DNS】指南帮手【协议栈】可靠传输【TCP】远程定位【IP】两点传输【MAC】出口【网卡】送别者【交换机】出境大门【路由器】互相扒皮【服务器与客户端】相关问答 不少小伙伴在面试过程…

RabbitMq基础概念知识复习

消息拥有消息头和消息体&#xff0c;消息具有rounting key&#xff0c;主题交换机和扇形交换机都是发布与订阅的实现方式&#xff0c;主题交换机用于匹配接收的消息的rount key 动态匹配模式匹配到多个符合的队列&#xff0c;扇形fanout交换机则不会使用消息的路由key&#xff…

SQLite如何处理CSV 虚拟表(三十七)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite的DBSTAT 虚拟表&#xff08;三十六&#xff09; 下一篇:SQLite的扩展函数Carray()表值函数(三十八) ​ RFC4180格式是一种文本文件格式&#xff0c;被用于表格数据间的交互&#xff0c;也可将表格数据转化…

【机器学习】集成方法---Boosting之AdaBoost

一、Boosting的介绍 1.1 集成学习的概念 1.1.1集成学习的定义 集成学习是一种通过组合多个学习器来完成学习任务的机器学习方法。它通过将多个单一模型&#xff08;也称为“基学习器”或“弱学习器”&#xff09;的输出结果进行集成&#xff0c;以获得比单一模型更好的泛化性…