给抖音聊天机器人接入 HDSI:从自动回复到持续剧情,以及我踩过的坑
之前写过一篇抖音白名单好友 AI 自动回复机器人的部署记录。原项目已经能识别指定好友、读取私信,再交给模型生成回复。这次我给它接入了 HDSI(hds-interlude),想让角色在两次聊天之间也有连续的生活、记忆和变化。
接入之后才发现,难点远不止让模型多写一点剧情。网页漏读消息,模型就会失去依据;后台虚构了一条好友消息,后面的故事又可能沿着这条假消息继续走。管理面板如果只显示“系统已接收”,我也不知道回复到底有没有发出去。
这篇记录这次改造的做法和实际踩过的坑。它是个人项目的实践记录,具体行为以我这套接入为准,不是抖音官方接口教程。
一、为什么接入 HDSI
原来的机器人更接近“收到消息,再生成一条回答”。上下文可以让它记住刚才聊了什么,但无人聊天时,角色的生活基本没有继续。
我希望角色能有自己的日程、当前正在做的事和逐渐积累的关系。对方下次发来消息时,角色可以从之前的状态继续,而不是每次都重新从人设介绍开始。
在这套部署里,HDSI 负责持续叙事、记忆与设定演化,以及是否产生后续联系等判断。原来的抖音脚本负责网页操作、身份核验、读取消息和实际发送。角色后台写了什么,与抖音上实际发出了什么,是两件需要分别记录的事。
二、保留原来的抖音接入,中间加一层桥接
我没有把整套机器人推倒重写,而是在 Python 的抖音接入和运行 HDSI 的 Node.js 进程之间加了一层桥接。
流程大致如下:
1 | 抖音网页版 |
Python 端继续负责白名单和发送边界,HDSI 负责角色的思考与剧情。桥接层记录消息是否接收、是否进入处理,以及回复是否等待投递,尽量避免重复提交和重复发送。
后台剧本不会整段发给好友。发送的是剧本中明确产生的聊天内容,可以拆成多条;原有发送频率和单条长度限制也仍然保留。接入 HDSI,并不意味着原项目的约束全部失效。
三、第一批问题出在消息读取
连发几条消息,只读到最后一条
最开始遇到的问题是,对方连续发了几条消息,机器人却只拿最新一条去回复。前面的问句和补充条件就这样丢了。
修复后,程序按稳定的消息标识维护处理进度,将尚未处理的消息按时间顺序收集成一批。只有消息获得明确接收或处理结果后,才推进相应进度,避免一次读取失败就把消息当成已经处理。
这样模型看到的是完整的一段输入,而不是聊天窗口底部的一小截。
视频和直播卡片,被当成发布者的名字
另一个很典型的问题是好友分享视频。卡片里有头像、发布者名字、标题等内容,原来的提取逻辑只抓到发布者名字,模型就把它当成好友发来的文字。
分享直播间也有类似问题:读到了主播名字,却没有识别出这是直播分享卡片。
现在会先识别消息类型,再整理成明确的输入。视频和直播卡片尽量保留可读取的标题、发布者或主播、链接等信息;表情包和图片在有可靠图像来源时交给视觉模型。暂时无法读取的内容会明确标注类型和限制,而不是直接跳过,或者编造内容。
这里的边界很重要:识别到分享卡片,不代表机器人已经看过视频或直播。 只有卡片文字时,就只能围绕这些文字作判断。
四、没有红点,不代表没有新消息
这是最隐蔽的一次漏回复。
脚本正停在某个好友的聊天窗口时,对方发来的消息会被网页自动标记为已读,因此会话列表没有新的未读红点。如果只靠红点发现消息,机器人就会认为“没有新消息”,即使新内容已经显示在当前窗口里。
而聊天气泡旁的“已读”,通常表示对方读了我发出的消息,也不能拿来判断我是否处理过对方的输入。
改造后的检测有两条路径:会话列表的未读提示帮助发现其他会话;当前已打开的会话则定期检查消息进度,不依赖红点。
不过,第一次加上当前窗口检查后,实际运行还是漏了。继续排查才发现,身份读取逻辑没有适配网页实际使用的会话数据字段,程序无法确认当前窗口属于谁,于是按安全规则停止读取。
最终还要修复当前会话身份绑定,并在读取前后核对身份,防止切换窗口时混入另一个人的消息。检测逻辑写进代码,不等于真实网页上的消息一定能走到它;必须沿着身份核验、读取、提交这条链路看结果。
五、剧情可以虚构,好友消息不能虚构
持续剧情带来了一个原版自动回复不太明显的问题:模型会把角色的生活写得很连贯,也可能顺手替真实好友补一条不存在的消息。
这次就出现过这样的情况:后台剧本写了“好友发来一只猫的表情包”,但实际聊天记录里根本没有这条消息。它只是叙事生成的内容,却被写成了收到的事实。
我在宿主层加了真实消息依据约束,并在剧本提交前检查一部分可识别的虚构收信描述。不符合真实输入的结果先尝试纠正;无法纠正时走失败处理,而不是直接写入历史。已经发现的错误剧情也做了定点修正,保留真实消息和实际发送记录。
这类检查仍有局限,不能保证识别所有语义层面的编造。但至少明确了一条原则:角色自己的日常可以是创作,真实联系人发了什么必须有接收记录或可靠的媒体识别依据,生成的旧剧本本身不能成为“对方确实说过”的证据。
天气也类似。当前项目没有接入所在地的实时天气接口,所以剧情里的下雨、阳光等是虚构环境描写,不能当作现实天气报告。
六、管理面板要能回答“消息到底发了没有”
接入初期,面板里有不少内部字段直接展示出来,比如 characterTraits、activeSceneId、continuityDirty。它们对排查实现有用,但对日常使用来说,很难一眼知道该看什么。
我把面板重新整理成以聊天与发送结果为中心的布局:收到的内容、生成的回复、投递状态和运行健康放在更容易看到的位置。常用字段改成中文,不需要日常关注的内部结构收起来,同时移除这套抖音部署用不到的其他平台功能入口。
动态记录也不能停在“幕间系统已接收,等待生成或投递”。接收成功、生成成功和发送成功需要区分;发送后应该展示实际内容与结果,而不是留下一张只有名字的空白卡片。
剧情查询则从长页面改成了“目录+单条阅读”:先看时间和摘要,再打开全文,支持较新、较早切换与分页。后来又修了一次布局问题——目录较多时内部高度被撑大,固定外框把正文末尾和分页按钮裁掉了。最终让目录和正文各自在可用高度内滚动,并检查长文末段是否可见。
做面板时我才体会到,最有用的信息往往很具体:消息有没有漏读,回复有没有发出,失败在哪一步。把所有运行状态原样堆出来,并不会更容易理解。
七、持续生活也会持续花 Token
自动回复的模型调用通常由聊天触发,但持续生活还有无人聊天时的推进、世界事件生成和其他后台任务。单次输出看起来不长,次数多了也会累计成明显开销。
还有一个容易忽略的配置关系:我开启了“联系冲动”的弹性推进,它会接管普通的固定推进节奏。因此只把固定间隔改长,未必就能减少实际调用。
这次调整后的主要设置是:
| 后台活动 | 调整后的节奏 |
|---|---|
| 安静时自主生活推进 | 约 3~4 小时一次 |
| 密聊后的自主推进 | 约 90~120 分钟一次 |
| 慢节奏生活推进 | 约 4~6 小时一次 |
| 额外加速推进 | 关闭 |
| 世界事件生成检查 | 6 小时一次 |
| 世界事件注入 | 每 24 小时最多 1 条 |
同时要求没有真实新消息的后台回合,将剧本正文尽量控制在 200~350 个中文字,少重复环境、背景和已经记载的细节。平静的一段时间可以简短交代,不必每次写成长篇。
这里没有把所有模型输出额度一起砍掉。HDSI 需要返回完整的 JSON 和动作信息,过低的输出上限可能截断结果,引发解析失败和重试,反而多花一次调用。
所以目前主要靠降低后台频率、缩短正文来节省用量,正常聊天的模型额度保留。200~350 字是写作目标,不是硬 Token 上限,也没有实现每日总 Token 封顶。 日程锚点、到期提醒、聊天量和重试仍会影响实际消耗,节省幅度还需要继续观察。
八、这次改造留下的经验
对我来说,这次接入最有价值的部分,是把几个原来混在一起的问题拆清楚了:网页上显示了什么,程序实际读到了什么,模型写出了什么,最后又发出了什么。
持续剧情可以让角色有更好的连续性,但它很依赖可靠的真实输入。消息读取和身份核验出了问题,再丰富的记忆系统也只是在错误信息上继续推演。管理面板需要把这些过程展示清楚,才能知道该修哪里。
目前这套项目仍然依赖抖音网页版,页面结构、登录状态和网络环境变化都可能影响运行。它仍是一个低频、面向指定好友的个人工具;后台剧情是角色创作,真实聊天记录则需要始终保持可核对。

