我把「记忆」接入了「小爱」,成功搞定多轮对话!

前段时间,搞了个微信 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下架,申诉无效,也懒得费口舌了,有需要的小伙伴可关注下方公众号,同步更新中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值