KroMiose / nonebot_plugin_naturel_gpt

一个基于NoneBot框架的Ai聊天插件,对接OpenAi文本生成接口,实现了机器人的人格 自定义/切换,聊天记忆等功能
Apache License 2.0
471 stars 52 forks source link

关于聊天记录的记忆问题 #2

Open DanLCJ opened 1 year ago

DanLCJ commented 1 year ago

您好!我发现现阶段nonebot的对话记忆是全局的,即有关机器人的所有对话根据时间轴进行记忆更新。但是很多时候比如群聊与私聊并不需要相同的历史记录的记忆,对每一个单独的对话应当同时拥有一个单独的记忆。请问应该在哪里进行修改?或者之后会有这部分功能的更新吗?感谢

KroMiose commented 1 year ago

目前的设计方案是每个人格的记忆内容(聊天记录和摘要)相互独立,并且对于每个会话(即每个群组或者私聊)相互独立; 但是对于每个用户的印象(根据用户唯一id辨识)是单独记忆的 即在以下场景中: 用户A在群组A中发送一条消息,bot的回复参考信息有:bot的人格设定信息、当前会话的前n条信息、当前会话的历史摘要、对触发bot回复的用户印象 以上设计的是为了尽可能让bot"记住"某一用户,但是为了节省token消耗又不得不隔离出部分信息的一种权衡策略,希望我的解释对您有帮助,如果有更好的思路欢迎提出!

Yang-ZhenHao commented 1 year ago

大佬你好,当bot在群里聊天时,使用当前会话的前n条信息作为bot的回复参考信息,经常会使bot变得胡言乱语,因为群里的消息如果没有@或者提及bot,那所谈论的话题基本上都是与bot毫不相关的内容。大佬是否可以考虑让bot只使用@或提及bot名字的消息作为bot的回复参考信息,让bot不要记录没有@或提及他的消息。或者设置一个开关来控制是否使用当前会话的前n条信息作为参考信息。十分感谢!大佬的项目真的超级棒!◝(⑅•ᴗ•⑅)◜..°♡

KroMiose commented 1 year ago

谢谢提议哈!其实让bot参考最近聊天信息的这个设计是我有意为之的,这个项目思路参考了另外两个大佬的类似ai聊天项目,一个是koishi平台的OpenAi插件:https://github.com/TomLBZ/koishi-plugin-openai,另一个是nb的gpt3:https://github.com/chrisyy2003/nonebot-plugin-gpt3 openai的方案其实就是如你所说的方式,仅使用用户与bot相关的聊天信息,并且bot的记忆只针对单用户个人,在我的测试中发现这种方式虽然能够使bot的回复对应得上提及用户的发言,但是在群聊场景下记忆的割裂感比较强,例如说两个人连续问了bot相同或者类似的问题,bot可能根据不同的记忆(通常是过时的)给出了完全不同的回答,又或者群聊中其他用户在讨论某个话题,其中一人询问bot对该话题的看法时可能会因为参考信息割裂而给出比较出戏的回答(毕竟群聊中较近期的记录对人类来说也是决定发言内容的重要参考,另外改善这个问题也是我决定开发这个插件的一个重要原因) 至于“使bot变得胡言乱语”这个问题我认为比较重要的因素可能是你的群友不太想理会bot,导致bot难以融入话题,我会尝试在下个版本调整prompt生成方式,看能不能提高bot对“提及者”的注意力,但效果尚不能确定(;´д`)ゞ 有更好的想法欢迎提出哟!谢谢喜欢 ~(○` 3′○)

Lynn-zy commented 1 year ago

我也认为让bot被@、回复或提及bot名字的消息的时候才把该条消息记录在内会比较好,否则bot会一直记录消息,生成印象promot,这样是会一直消耗token的吧。况且群友平常聊天的时候对话通常是比较无厘头可能还会发癫(?),如果一直记录群消息的话,bot很有可能会在回答问题的时候胡言乱语,所有有选择性的记录消息效果应该会比较好。

KroMiose commented 1 year ago

目前bot没有被@或者提及时不会消耗token,只会把当前信息暂存到当前会话的消息队列中,作为接下来可能会被唤醒时使用的参考信息,该队列设定了最大限制,超出的消息会被自动清除 另外印象prompt的生成是仅根据bot对唤醒者的对话信息生成的,用户与bot无关的日常群组互动不会参与印象记忆

sudoskys commented 1 year ago

可以使用我制作的异步 LLM 框架,经过长期的应用测试 https://github.com/LlmKira/llm-kira 只需要传递即可,自动调整到最优解

KroMiose commented 1 year ago

@sudoskys 大佬您好,我尝试研究了下您的框架,遇到了些问题,简单的说就是我几乎没有看明白,能否简要分享一下您的实现思路或者说明一下框架的详细功能呢(小声) (/▽\)

sudoskys commented 1 year ago
import asyncio
import random
import llm_kira
from llm_kira.client import Optimizer
from llm_kira.client.types import PromptItem
from llm_kira.client.llms.openai import OpenAiParam
from typing import List

# 定义 ApiKey池
openaiApiKey = ["key1", "key2"]
openaiApiKey: List[str]

# 定义接收者
receiver = llm_kira.client

# 创建对话,一个ID一个记忆池
conversation = receiver.Conversation(
    start_name="Human:",
    restart_name="AI:",
    conversation_id=10093,  # random.randint(1, 10000000),
)

# 创建语言模型对象
llm = llm_kira.client.llms.OpenAi(
    profile=conversation,
    api_key=openaiApiKey,
    token_limit=3700,
    auto_penalty=False, # 暂时不能用的自动惩罚
    call_func=None, # Api错误回调函数,用于操作失败的情况,传入的是 key 和 错误返回
)
# 记忆管理器
mem = receiver.MemoryManager(profile=conversation)

# 创建聊天机器人
chat_client = receiver.ChatBot(profile=conversation,
                               memory_manger=mem,
                               optimizer=Optimizer.SinglePoint, # 单点聊天优化器(相对应的是群聊优化器)
                               llm_model=llm) 

async def chat():
    # 创建提示管理器
    promptManager = receiver.PromptManager(profile=conversation,
                                         connect_words="\n", 
                                         template="Templates, custom prefixes" # 模板,用于自定义你的 head,这个不会被自动对齐削减
                                         )
    # 后加入的会排在前面
    promptManager.insert(item=PromptItem(start=conversation.start_name, text="My id is 1596321")) # start 就是询问人的名字,从对话对象获取
    response = await chat_client.predict(llm_param=OpenAiParam(model_name="text-davinci-003", n=2, best_of=2),
                                         prompt=promptManager,
                                         predict_tokens=500,
                                         increase="External enhancements, or searched result",
                                         )
    # increase 作为 一个额外接口,作为实时信息或者其他信息的注入口
    print(f"id {response.conversation_id}")
    print(f"ask {response.ask}")
    print(f"reply {response.reply}")
    print(f"usage:{response.llm.usage}")
    print(f"usage:{response.llm.raw}")
    print(f"---{response.llm.time}---")

    promptManager.clean()
    promptManager.insert(item=PromptItem(start=conversation.start_name, text="Whats my id?"))
    response = await chat_client.predict(llm_param=OpenAiParam(model_name="text-davinci-003"),
                                         prompt=promptManager,
                                         predict_tokens=500,
                                         increase="外部增强:每句话后面都要带 “喵”",
                                         # parse_reply=None
                                         )
    _info = "parse_reply 函数回调会处理 llm 的回复字段,比如 list 等,传入list,传出 str 的回复。必须是 str。"
    _info2 = "The parse_reply function callback handles the reply fields of llm, such as list, etc. Pass in list and pass out str for the reply."
    print(f"id {response.conversation_id}")
    print(f"ask {response.ask}")
    print(f"reply {response.reply}")
    print(f"usage:{response.llm.usage}")
    print(f"usage:{response.llm.raw}")
    print(f"---{response.llm.time}---")

asyncio.run(chat())
sudoskys commented 1 year ago

抽象了 llm 大模型, 记忆管理,提示管理,对话身份 。传入多个 api key 自动轮流负载

KroMiose commented 1 year ago

@sudoskys 感谢大佬的耐心解释,学习了!看起来您这个已经是一个完整的项目了,或许直接做成一个新的插件会更合适呢?

sudoskys commented 1 year ago

@sudoskys 感谢大佬的耐心解释,学习了!看起来您这个已经是一个完整的项目了,或许直接做成一个新的插件会更合适呢?

是LLM的基础设施

KroMiose commented 1 year ago

@DanLCJ 已确认bot对话管理为全局是一个bug,其他群组或私聊的消息存在串线情况,预计今晚更新修复此问题(原谅我测试不充分过了这么久才发现这个bug (/▽\)

xuanmeikawaii commented 1 year ago

@KroMiose 作者你好,因为有时候会有同一批人来回水好几个群,群聊信息串线的bug也许是一个很有趣的特性,增加一个同步几个指定的群的群消息的功能会不会比较好?

KroMiose commented 1 year ago

@xuanmeikawaii 这个bug(特性)确实相当有趣,但是我个人觉得对于更多的情况来说还是弊大于利的,主要是出于以下几点考虑:

  1. 因为如果在不同的群组中正在聊天的话题不同或者没有明显关联会出现比较严重的串线情况导致答非所问(例如即使是同一批人在多个群组,出现在A群组中回答B群组中的上下文信息也会比较奇怪)
  2. 例如说某个群中主要的成员可能都是同学、同事,另一个群组可能都是线上的网友,如果处理不当可能会造成隐私泄露的情况或者其它后果;另外因为bot的同一个人格对每个用户的记忆是跨会话共享的,所以其实还是会有部分的信息跨群组进行流动,但是这部分信息只用到了该用户对某个人格的记忆,基本上完全取决于用户的个人使用行为,所以风险更加可控
  3. 跨会话同步的信息需要使用额外的来自另一个会话信息,从而导致更多的token消耗,出于上面几点原因所以这部分消耗掉的token的性价比也会比较低 当然如果有更好的方式能兼顾以上问题又能优化现有效果,我们可以继续讨论(或者加群一起探讨)
mycInk commented 1 month ago

@KroMiose 作者你好,很喜欢你做的聊天机器人。作为一个没有研究过这些的小白,我有几个问题想问您。首先就是关于api的,我选择的是国内第三方代理的gpt api请问这样可以吗,如果可以应该怎么调整呢?其次就是关于记忆功能,我看到说可以启动主动记忆,但是需要安装扩展,请问扩展是需要找其他作者的记忆扩展吗?如果不是请问能告诉我扩展的下载地址吗?谢谢!

KroMiose commented 1 month ago

@KroMiose 作者你好,很喜欢你做的聊天机器人。作为一个没有研究过这些的小白,我有几个问题想问您。首先就是关于api的,我选择的是国内第三方代理的gpt api请问这样可以吗,如果可以应该怎么调整呢?其次就是关于记忆功能,我看到说可以启动主动记忆,但是需要安装扩展,请问扩展是需要找其他作者的记忆扩展吗?如果不是请问能告诉我扩展的下载地址吗?谢谢!

  1. 配置文件中有 OPENAI_BASE_URL 字段,将其值的 api.openai.com 替换为你的第三方 api 地址即可
  2. 记忆扩展是早期的实验性功能,为 AI 提供了类似记事本的功能,可以主动记忆一些信息,但是因为其使用积极性和效果不佳,其额外支出的 prompt 复杂度并不划算,故弃用,目前该扩展文件仍在仓库的 extensions 中,但是已有多个版本未对其测试,可能存在潜在 bug

谢谢你的喜欢!(另外如有其他问题请开新 issue 讨论,在已有 issue 下提问会同时给该 issue 所有参与者发送邮件提醒)