comment-auto-reply 评论自动回复
video-engagement-cta「24h 回复承接」的自动化实现。单命令手动触发(2026-08-27 用户定规:不挂 schtasks 定时,用户自己跑)。
用法
make comment-reply # 全流程(B站+抖音 → 分诊 → 逐条确认 → 发送)
make comment-reply args="--dry" # 演练:走到发送前留截图,不实发
make comment-reply args="--platform douyin --no-llm"
make comment-reply args="--platform kuaishou" # 快手:只读采集+分诊,B 档确认稿存账本 manual(发送通道未建,人工在创作者平台评论管理页回)
平台矩阵(2026-08-30,openspec comment-coverage-kuaishou-shipinhao):
- B站:公开 API 读+回+回读验证(bili.py)——全能力
- 抖音:评论管理页 DOM 采集+回复+回读(douyin.py)——全能力
- 快手:全局评论流只读采集(kuaishou.py,
/rest/cp/creator/pc/home/commentListXHR 拦截免签名逆向;平铺行按创作者昵称 replyToCommentId 判已回;photoId 暂无 slug 映射以?入账)——发送通道未建 - 视频号:登录态已失效(落到 login.html,需扫码重登后才能探评论端点形态)——未接
定向回复单条(用户贴来一条评论要回时,不跑整轮):
make comment-reply args='--locate 本源之外' # 采集两平台按昵称/正文搜,命中落 work/locate.json
# 确认稿子后(≤120 字,见下方写稿纪律):
make comment-reply args='--reply douyin:<video_id>:<comment_id> --text "回复正文"'
--reply 走与整轮一致的纪律:合规门禁、配额、账本幂等(已 sent 拦截)、回读验证、kill switch。2026-08-27 首次实发并回读验证通过。
撤回自己已发的楼中楼回复(仅抖音;默认演练——点到「确定要删除吗」弹窗即自动取消,不实删,用于走通路子):
make comment-reply args='--delete douyin:<video_id>:<comment_id>' # 演练:定位→点「删除」→弹窗截图→取消
make comment-reply args='--delete douyin:<video_id>:<comment_id> --commit' # 实删 + 回读验证(账本记 manual 防重入队)
--delete 从账本取该 key 已 sent 的 reply_text 定位楼中楼;--commit 成功后记 manual(不再进自动队列),演练不写账本。2026-08-27 演练走通到确认弹窗(截图留证 work/del_*.png)。
实现在 scripts/comments/(reply.py 为 CLI 入口),数据落 data/comments/。
通道(2026-08-27 spike 实证,勿走回头路)
| 环节 | B站 | 抖音 |
|---|---|---|
| 作品源 | link-map bilibili_id(或 bilibili_aid,只有 aid 没有 bvid 的作品也要扫)→ aid |
link-map douyin_id |
| 读评论 | 公开 API x/v2/reply/main(root=0 一级) |
评论管理页 creator-micro/interactive/comment?item_id= 域内无签名 fetch comment/list/select(一级)/list/reply(子回复) |
| 已回判定 | 子回复 mid == DedeUserID |
子回复昵称 ∈ creator_nicknames(默认「1024工程笔记」) |
| 回复 | x/v2/reply/add(SESSDATA+bili_jct) |
页内「回复」按钮 → contenteditable(placeholder=回复 <昵称>:)→ 发送 |
| 验证 | 发送后回读 reply/reply |
发送后回读 list/reply |
| 删自己的回复 | —(后台手工) | 行内「删除」(直接文本节点匹配,从正文上溯 ≤20 级找公共容器,命中后回验同楼再取坐标点)→「确定要删除吗」弹窗 → 回读 list/reply 验证消失。楼中楼先展开:「N 条回复」入口在行容器外兄弟分支,页面级按「文本长度+矩形面积最小」候选逐个试点 |
⚠️ 抖音评论管理旧路由 comment/manage 已重定向(2026-08-27 UI 改版),勿再用;「回复」按钮带图标子节点,DOM 匹配用直接文本节点(childNodes text === '回复'),不能用叶子节点过滤。
⚠️ 楼中楼回复坑(2026-08-28 三次实发实测,改代码勿回退):
- 子行回复通路挂不上上下文——点子回复行的「回复」(JS click 和真实鼠标点击都试过)后主输入框无任何可见变化,往里输入发送会落成一级评论(两次误发均已删)。现行通路 = 回根评论行:根行「回复」会弹行内输入框(placeholder=
回复 <昵称>:),发出后正确落楼;根作者通常就是提问人,同楼通知到位。落法:locate.json 该条目的text字段改成根评论正文(DOM 定位用),key/comment_id保持原目标不变(账本幂等与回读定位用)。 - 展开「查看N条回复」:管理页过滤态入口文案是「查看N条回复」(非展开态「N条回复」/「回复N」),正则三态都要覆盖。
- 管理页搜索框只匹配一级正文:楼中楼目标直接搜必空(「暂无符合条件的评论」),须搜根评论把楼过滤出来再展开。
- 评论行正文常是容器的直接文本节点(与 reply-to span 平级),叶子匹配必漏,用「自身命中且无子元素命中」的最内层持有者匹配。
- 「发送」按钮同样非
<button>标签(同「回复」「删除」手法):直接文本节点定位取坐标真实点击;button:has-text()全不中会静默失败(正文留框、误报 sent)。行内输入框发送成功后整体关闭,text_content等元素超时 ≠ 失败。 - 回读用包含匹配:楼内回复发出后带「回复 @昵称:」前缀,
startswith会漏判。
分诊口径(triage.py,规则不依赖 LLM)
- A 无信息量(纯赞/表情/打卡/超短):不回只记账——刷回复是最强机器人信号,伤号
- B 技术提问(问句或技术词):LLM 草稿(可选)→ 交互确认后发
- C 人工队列:负面/引战、求资源/求私联(导流风险)、观点表态、超长评论——只展示不自动回
- 盖楼:默认不回;楼内纠错类 →
floor-correction进人工队列(必须让人看见,不回会持续误导);高热度追问(楼内 +1≥2)→ 人工队列 - LLM 草稿事实锚定:注入该视频口播稿/文章(anchor.py),材料没覆盖走兜底话术,禁止现编
风控纪律(spec 硬性要求,勿放宽)
- 单轮单平台 ≤5 条、每日单平台 ≤15 条(账本自动算配额)
- 多条发送间隔随机 40–180s(人工确认耗时计入)
- 仅近 14 天作品;kill switch:
data/comments/DISABLED存在即只采集不发送 - 每次发送后回读验证(不信发送动作返回);每步截图落
data/comments/work/ - 三态记账(2026-08-28 定规):回读验证通过记
sent;发送动作成功(输入框已清空)但回读未见记unverified——新评论回读有延迟,unverified 是终态(不自动重发防双发)、计入配额、留人工/API 复核;动作失败记failed可重发。禁止把回读未见的发送直接记 sent(2026-08-28 曾因此静默失败进账本,人工纠正才解锁重发) - 发送前过 platform-compliance 词库(HIGH 命中即拦)
- 账本
reply-ledger.jsonl进 git(幂等:已 sent/unverified/manual/skipped 的不重复出队)
LLM 配置(可选)
data/comments/config.json(git 忽略):
{"llm": {"base_url": "https://api.xxx/v1", "api_key": "sk-...", "model": "glm-4-flash"},
"creator_nicknames": ["1024工程笔记", "1024 工程笔记"]}
未配置时 B 档全部人工写稿(e 直接输入),整链不依赖 LLM。
写稿纪律(2026-08-27 用户反馈定规,LLM 与人工写稿同守)
- 直接从答案讲起:不设计回应评论措辞的口径化开场——观众求私下讲解时回「不用私,就在这讲」被判生硬;也不写「好问题」「问得好」类客套。观众想私下请教 = 把干货直接写进公开回复,不提私聊也不声明不私聊。
- 记住场景和受众(2026-08-28 定规):写稿前先想清楚回复落在哪条视频的评论区、看的人是谁——观众已看过该视频,视频里讲过的概念(Cordis、事件日志)直接点名,不复述第二遍、不补背景;评论区受众比文章读者杂,短句口语,≤120 字装不下就先保结论。禁「你记的是 X」式先接住再纠偏的缓冲句(de-ai-smell 行为规范 16),纠偏从第一句开始。
- 技术提问答底层原理和流程,不止给结论(2026-08-28 定规):B 档技术提问回复的主体是「机制链」——存哪(存储层,如文件系统/落盘)、什么动作动了什么、什么没被动、要取回走什么流程。例:dsh 记忆压缩问「会不会丢」,更好的答法 = 日志落盘在文件系统,压缩只压上下文(旧对话收成摘要腾位),原始事件文件一个不动;要找回就按事件日志查,分三档。结论只占一句,原理和流程占主体,宁可压掉客套也要保机制链;120 字装不下时优先砍铺垫不砍流程。
- 类比只用大白话生活类比,且禁一切账务色彩(2026-08-28 定规):「不装海马体」这类冷门领域梗连作者本人都要问一句什么意思,评论区受众比文章读者杂,看不懂的类比不如不比。记账/查账/翻旧账/算账/账本这级账务比喻全部禁用(「翻旧账+像账本旧页折起来」被打回,已并入 de-ai-smell L1 记账类比禁词,扫描脚本收录)——要类比就用无账务色彩的生活动作(折叠/收纳/建索引/归档),拿不准就直述不比。
- 事实锚定纪律不变:答案必须在锚定材料里有依据,材料没覆盖走兜底话术。
- 尊重事实(2026-08-27 定规):不虚构作者本人的做法——没做过的实践不能写成「我的做法是…」(编造「我锁一个稳定版跑日常」被打回撤稿)。收尾没真实个人实践可写时,直接删掉那段,改为说明该方案/做法自身存在的问题与取舍,不拿编造的经历凑收尾。
复盘
每周人工抽查账本里 sent 的回复质量(技术准确性 + 语气),翻车案例写回本 skill。首次 B站实发后确认 verify_sent 回读口径有效(写通道当时未实发验证)。
已沉淀案例:
- 2026-08-27 「本源之外」问 dsh 事件记忆原理:初稿「不用私,就在这讲」开场 + 「海马体」类比双双被用户打回(见写稿纪律),改为直答 + 记账类比后定向发送成功。
- 2026-08-27 「以古论今」评 dsh 本地化:初稿收尾编造「我锁一个稳定版跑日常」(作者并无此实践)→ 定规「尊重事实」(见写稿纪律),更正方向 = 删掉该段、只讲自维护分叉自身的问题(新功能要自己跟着上游升级合并、分叉越久越难合回)。同日楼主在该楼连发追问、暂缓删稿;删除通道(
--delete/--commit)以演练模式实测走通到「确定要删除吗」弹窗即取消,未实删。 - 2026-08-28 「本源之外」楼中楼追问「时空内核」:写稿被纠两处(「你记的是 X」先接住再纠偏缓冲句进 de-ai-smell 行为规范 16;场景受众纪律见写稿纪律);发送踩满楼中楼坑(两次误发一级评论已删、一次静默失败误报已纠正),最终走根评论行通路发送并回读验证通过,坑位全部沉淀进上方 ⚠️ 段。另:误报 sent 暴露「动作成功即记 sent」的结构缺陷,催生三态记账(
sent/unverified/failed,见风控纪律)——回读未见的发送不再进 sent,无需再手工改账本解锁。 - 2026-08-28 「海风」问 dsh 久远记忆会不会被压缩掉:初稿「真要翻旧账就按日志查……像账本旧页折起来,没撕」被用户打回——账务类比(翻旧账/账本)禁用,2026-08-27 案例里「记账类比」的推荐口径就此反转(见写稿纪律类比条),已同步 de-ai-smell L1 禁词表与扫描脚本;改稿方向 = 删账务类比直述收尾(「原文一直留着,只是不再占上下文」)。发送后用户复盘再补一条:终稿仍只给「结论 + 三档概念点名」,没讲日志存在文件系统、压缩只压上下文这层原理,也没讲找回流程 → 定规「技术提问答底层原理和流程」(见写稿纪律),该楼不再补发。
置顶评论发布器(2026-08-27 新增,scripts/pub/douyin_comment_post.py)
发一级评论(回复走 reply.py 的 send_reply,一级评论此前无工具)。三个子命令:
find --title <关键词>(内容管理页按标题找 item_id——卡片 DOM 不暴露 item_id,靠点「评论」入口从跳转 URL 抓)/ post --item-id <id> --text-file <文案> [--dry] / pin(web 端无置顶入口,抖音置顶只能在 App 长按自己评论——pin 子命令仅当页面出现按钮时可用)。
实战坑(全已修进代码,改时别回退):
- async playwright 的 screenshot 必须 await——同步 lambda 调用得到未执行 coroutine,截图全空且无报错(只有 RuntimeWarning),留证机制静默失效;
- 评论管理页「发送」按钮是非
<button>标签——button:has-text()全不中,用直接文本节点匹配(同「回复」按钮手法); - 新发评论不进管理页首屏列表(显示「暂无更多评论」)——定位/置顶前先检测并 reload;回读
list/select对新评论有延迟(total=0 是常态),回读失败时以 sent 截图为证返回,勿误报失败; - pin 流程改动时注意
needle = _norm(match)[:16]定义必须留在 reload 逻辑之后(吞过一次 NameError)。