这段时间我一直在折腾一个叫 Pi Bot 的项目。
一开始想法其实很简单:我想要一个能长期待在聊天环境里的 Bot。但真的开始做之后,我发现“接收一条消息,再调用一次大模型”只是最容易的部分。一个 Chatbot 要是每句话都接、每个问题都写成小论文、聊过的事转头就忘,哪怕背后用的是再强的模型,也很难让人觉得它真的属于这个聊天空间。
所以后来我花的时间,更多不是在追求“回答得有多聪明”,而是在琢磨另一件事:怎样让一个 Pi Agent 更像一个懂得相处的人。
先学会什么时候开口
群聊和一对一问答完全不是一回事。
有人点名、叫了它的名字,或者引用了它之前的回复,它当然应该回应。但更多时候,群里的话并不是说给 Bot 听的。两个人在互相开玩笑,有人随手发了一张图,或者只是感叹了一句,如果它每次都跳出来总结一下,存在感很快就会变成打扰。
我给 Pi Bot 做了一套单独的参与判断:点名、唤醒词、引用、自然续聊、公共提问、热聊状态,都会产生不同的触发理由。它也有安静时段,夜里不会因为普通闲聊突然冒出来;群冷了很久以后,可以尝试自然暖场,但如果想不到合适的话,宁可不说。
这里有一个我很喜欢的小设计:Pi Agent 可以明确选择 skip_reply。沉默不是生成失败,也不会往群里发一个 NO_REPLY 之类莫名其妙的占位符,而是一次正常、可解释的决定。
我觉得这点挺重要。一个像人的 Chatbot,首先应该拥有不说话的能力。
记忆不是把聊天记录一股脑塞回去
很多 Agent 项目提到“记忆”,做法是把历史消息不断追加到上下文里。短时间看起来有效,聊久了以后,成本、噪声和错误都会一起增长。
Pi Bot 里,我把状态拆成了三层:
- transcript 保存真实发生过的对话;
- memory 保存一周以后仍然有价值的事实,比如成员偏好、固定称呼和长期关系;
- behavior 保存大家教给它的相处方式,比如“回复短一点”或者“不要每次都接话”。
这三层互相独立,也都可以审计和纠正。有人说“你记错了”,Agent 不应该只是当场道歉,下次继续犯;它会去找到对应记忆,修改归属、修正内容,或者彻底忘掉。表达偏好的变化则进入 behavior,不和事实记忆混在一起。
我特意没有让它什么都记。一次性的请求、临时情绪、聊天总结,默认都不值得进入长期记忆。记住得多不等于理解得好,有时候恰恰相反。
文字之外,回复的形态也属于对话
真实聊天里,人不会永远只发文字。
Pi Bot 能处理图片、音频、语音、视频、文件、链接、引用和成员提及,但我更在意的是它什么时候应该用这些能力。图片不会因为“有视觉模型”就每张都分析,只有当前问题确实依赖画面时才调用理解工具;别人发语音时,它可以跟随对方的消息形态用语音接话,也可以进入持续语音或耳语模式。
表情包同样不是一个藏在菜单深处的附加功能。我给 Agent 做了表情检索和发送工具:遇到“哈哈”“离谱”“确实”这种短态度,如果表情库里正好有合适的,它可以只发表情,不再补一句同义文字。项目里还集成了 meme 生成、斗图和复读逻辑,自己上传的图片也能直接加入表情库。
这些功能单独看都不大,却很影响一个 Bot 在聊天里的“手感”。
Pi Agent 负责思考,但不是所有事都交给模型
项目底层使用 Pi Agent。每次运行时,Core 会为它重新组织当前消息、最近上下文、召回的长期记忆、已学习的行为和可用工具,而不是让模型直接面对平台原始数据。
我也刻意划了一条边界:平台声明自己“能做什么”,不代表模型就自动获得了这个能力。一个 Adapter 也许支持发文件、解析媒体或管理成员,但只有显式注册、经过限制的 Tool Provider 才会进入 Pi Agent 的工具列表。外部网页、附件正文、MCP 返回值和平台 metadata 都只是不可信材料,不能反过来变成系统指令。
还有一些事情根本没必要调用大模型。比如固定的入退群通知、拍一拍回复、连续两人发出相同内容后的复读,都可以交给确定性插件完成。需要语义判断、记忆或工具协作时才让 Pi Agent 上场,这样更快,也更可控。
我不想让它因为一次重启“失忆”
Chatbot 做 demo 很容易,长期运行是另一回事。
Pi Bot 会在判断是否触发模型之前先保存入站事件;同一个事件不会重复运行 Agent;同一会话里的消息严格串行,避免两条消息同时生成、互相看不见;回复记录和待发送动作在同一个事务里提交。消息如果暂时发送失败,会留在 durable outbox 中,进程恢复后继续重试。
媒体本身也不会直接塞进 SQLite 或主提示词,只保存 AssetRef,二进制交给独立的 AssetStore。这样做稍微麻烦一点,但项目跑久以后,状态是否清楚、故障能不能恢复,比少写几行代码重要得多。
整个核心也被设计成与聊天平台无关:Adapter 负责连接、事件转换和实际发送,Core 只接收标准事件、输出标准动作。换一个聊天入口时,不需要重写记忆、插件和 Agent 逻辑。
现在它已经不只是一个聊天脚本
目前项目里已经有多模型 Provider、主备模型降级、知识检索、MCP、视觉理解、语音转写与 TTS、定时任务、群聊总结、主动参与、表情库和 meme 生成,也做了一个 React 管理界面,用来配置 Bot、人设、模型、插件、知识库、会话和运行状态。
但我还是更愿意把它看成一个持续实验:Chatbot 到底怎样才能从“会回答问题的接口”,慢慢变成一个有记忆、有分寸、能一起相处的 Agent?这里面还有很多细节值得继续做。
项目已经以 MIT License 开源:
GitHub:JimiZhou/pi-bot
如果你也在做 Chatbot、Pi Agent、长期记忆、群聊参与策略,或者只是发现某个细节可以更自然,欢迎来提 issue,也非常欢迎直接提交 PR。很多真正好用的小功能,往往就来自一句“这里是不是还能再顺手一点”。
若无法加载请检查网络环境。
若无法加载请检查网络环境,或切回 Disqus 稍后再试。