前段时间,搞了个微信 AI 小助理-小爱(AI)
,爸妈玩的不亦乐乎。
目前小爱(AI)
仍在持续迭代中,受到了很多粉丝朋友的关注,故新建了一个体验群,邀请大家免费体验。
最近收到了很多需求和反馈,人力有限,只能按照优先级进行安排。
最近,把文生图的能力接入了进来:
不过,每次都要输入完整提示词,要让它基于生成图片进行调整,就搞不赢了👇
因为:目前小爱
只支持单轮对话,没有上下文信息,它怎能听懂你在说什么呢?
这个需求刚的很,必须尽快安排上!
前两天,已经在开发板上把记忆功能给机器人加上了:
基本思路是一致的,但实操起来,发现差别还挺大。。。
今日分享,带大家实操:利用本地数据库,为小爱
接入记忆功能,实现多轮对话。
1. 数据库实现
本次实现,数据库依然选用 SQLite,自己玩完全足够。
1.1 数据表结构设计
和上篇语音对话机器人不一样,微信中的聊天记录分为:私聊
和群聊
两种,二者的对话逻辑不完全一样,所以最好解耦开。
原打算放在一个消息表中,后面发现逻辑较为复杂,所以最终实现方案:一张用户表
+两张消息表
。
用户表
:用于存放用户相关信息;
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
uid = Column(String, index=True) # id 字段,唯一标识,建立索引
name = Column(String, index=True) # name 字段,建立索引
alias = Column(String) # 备注名
room_messages = relationship("RoomMessage", back_populates="user", cascade="all, delete-orphan")
single_messages = relationship("SingleMessage", back_populates="user", cascade="all, delete-orphan")
群聊消息表
:通过uid和用户表
进行关联,存放群聊消息;
class RoomMessage(Base):
__tablename__ = 'room_messages'
id = Column(Integer, primary_key=True, index=True) # 自增主键
uid = Column(String, ForeignKey('users.uid', ondelete='CASCADE'), index=True) # 外键关联 uid
uname = Column(String) # 发送方 name
room_name = Column(String, index=True) # 群名
content = Column(String) # 文本内容
timestamp = Column(String) # 时间戳
user = relationship("User", back_populates="room_messages")
私聊消息表
:通过uid和用户表
进行关联,存放私聊消息;
class SingleMessage(Base):
__tablename__ = 'single_messages'
id = Column(Integer, primary_key=True, index=True) # 自增主键
uid = Column(String, ForeignKey('users.uid', ondelete='CASCADE'), index=True) # 发送方 uid
uname = Column(String) # 发送方 name
to_name = Column(String, index=True) # 接收方 name
content = Column(String) # 文本内容
timestamp = Column(String) # 时间戳
user = relationship("User", back_populates="single_messages")
1.2 定义请求体模型
上一步定义的数据库模型,用于与数据库交互。
接下来,还需定义请求体模型,用于描述和验证 FastAPI 中接收到的数据。
比如,对应 User 的Pydantic 模型,定义如下:
class UserCreate(BaseModel):
uid: str = Field(..., description="user ID,required")
name: str = Field(default="", description="name")
alias: str = Field(default="", description="alias")
1.3 实现增删改查
接下来,实现用户表
和消息表
增删改查的基本功能。
比如要实现用户新增:
@app.post("/users/", response_model=UserCreate)
def add_user(user: UserCreate, db: Session = Depends(get_db)):
# 先查询是否存在该用户
db_user = db.query(User).filter(User.uid == user.uid).first()
if not db_user:
db_user = User(uid=user.uid, name=user.name, alias=user.alias)
db.add(db_user)
else:
db_user.name = user.name
db_user.alias = user.alias
db.commit()
db.refresh(db_user)
return db_user
实现群聊消息新增:
@app.post("/messages/room/", response_model=RoomMessageCreate)
def add_message_room(message: RoomMessageCreate, db: Session = Depends(get_db)):
db_message = RoomMessage(uid=message.uid, uname=message.uname, room_name=message.room_name, content=message.content, timestamp=message.timestamp)
db.add(db_message)
db.commit()
db.refresh(db_message)
return db_message
基本功能实现后,就可以参考上篇教程,启动数据库服务端。
2. 多轮对话实现
多轮对话的核心就是接入数据库,从库中检索到一段时间内的聊天记录,统一送给大模型进行答复。
所以,在原来应用的基础上,主要有三点改造:
- 新增好友信息
- 新增聊天记录
- 检索聊天记录
我们来一一搞定它!
2.1 新增好友信息接口
当有好友发消息后,首先需要将用户信息更新到用户表
中,为此可以编写一个接口函数,去请求数据库:
def add_user(payload): # source['from']['payload']
response = requests.post(f"{db_base_url}/users/", json={
"uid": payload['id'],
"name": payload['name'],
"alias": payload['alias']
})
if response.status_code == 200:
logger.info(f"Add/update user {payload['name']} into user table")
else:
logger.error(f"Failed to add user {payload['name']} into user table, {response.text}")
2.2 新增聊天记录
当有用户消息过来时,需要将其保存到数据库中:
def add_message_room(uid, uname, room_name, content, timestamp):
response = requests.post(f"{db_base_url}/messages/room/", json={
"uid": uid,
"uname": uname,
"room_name": room_name,
"content": content,
"timestamp": timestamp,
})
if response.status_code == 200:
logger.info(f"Add message {content} from {uname} into message table")
else:
logger.error(f"Failed to add message {content} from {uname} into message table, {response.text}")
2.3 检索聊天记录
在大模型回答之前,需要从数据库中检索到相关聊天记录:
def get_message_room(uid='', room_name='', limit=6, start_time='', end_time=''):
param = {
"uid": uid,
"room_name": room_name,
"limit": limit,
"start_time": start_time,
"end_time": end_time
}
response = requests.get(f"{db_base_url}/messages/room/", params=param)
if response.status_code == 200:
return response.json()
else:
return []
最后,把以上功能嵌入到原有对话流程中,测试成功,就可以上线啦。
2.4 效果展示
还是本文开头的例子:
从润色提示词上可以发现,已经把实现了我要重绘的细节 wearing an official hat
。不过,从结果来看,绘画模型怕是不知道王阳明是明朝
的吧。
最后,我们来看下日志:
再看一个连续对话的例子:
我问他清朝有和王阳明齐名的思想家么
,你看,对阳明先生
的认知还是相当到位的。
写在最后
本文通过数据库接入
,成功为小爱
接入了多轮对话
能力。
如果对你有帮助,不妨点赞 收藏备用。
大家有更好的想法,欢迎来聊👇
为方便大家交流,新建了一个 AI 交流群
,欢迎感兴趣的小伙伴加入。
小爱
也在群里,公众号后台「联系我」,拉你进群。
猴哥的文章一直秉承分享干货 真诚利他
的原则,最近陆续有几篇分享免费资源
的文章被CSDN下架,申诉无效,也懒得费口舌了,有需要的小伙伴可关注下方公众号,同步更新中。