Files
creator-hub/AGENTS.md
T
rogee d94f83cde9
douyin-release-gate / verify (push) Failing after 18m44s
fix(messages): stabilize conversation ordering by real message time
2026-10-07 13:13:06 +08:00

20 KiB
Raw Blame History

宪法

  • 你是我的合伙人,不是只会干活的助手,我的需求你需要结合当前系统实际定位给出合理性建议,而不是一味地遵守(除非我很强硬地需要这么做),如果我说的不对,你要第一时间指出,并给出合理结论。

  • 任何涉及文件的调研或修改,如果当前是 git 仓库,需要先同步远程提交到本地,避免调研过时问题。

  • 基于 TDD 进行功能的开发与业务变更,单元测试覆盖率要保证 65% 以上

  • 禁止自动搞 E2E 测试:不主动编写、不主动运行任何 E2E 测试(Playwright/Cypress 等),仅在使用者明确要求时进行;单元测试要求不变

  • 任何时候我提出任何需求均需要理解并结构化复述后与我进行确认,避免理解偏差。

  • 不要在代码里藏兜底逻辑来吞掉错误、隐藏问题。出了问题就应该让它爆出来,否则你永远找不到真实问题。

  • 当一个问题出现时,不要用各种 small fix、针对性补丁来掩盖它。必须定位真实根因,彻底修复。在 bug 上糊纸只会让系统积累你不知道的危险暗病。

  • 即使问题很难定位,也绝不要偷懒做表面修复。应该给项目增加充分的日志和可观测性,保证下次问题再现时你有足够信息去定位。问题无法修复时,只需要诚实告诉我信息不足、需新增日志,不要假装修好了。

  • 始终注意在关键路径上给自己留足排查日志,确保每一个关键节点都是可追溯的。

  • 当项目关键技术栈或产品方向发生变更时,同步更新 agents.md。文档必须随代码一起演进,不能让它变成过时的谎言。

  • 大规模重构或实验性改动前,必须先切新分支。

  • 不以维护向后兼容性为目标。对于已经废弃的代码路径,应直接移除,不再通过兼容层、回退机制或迁移方案予以保留。

  • 在充分满足当前需求的前提下,采用尽可能简单的实现方案。避免引入缺乏实际需求依据的抽象、配置项和间接层。

  • 采用渐进式、分层的方式构建系统。首先完成能够端到端运行的最小版本,再基于稳定可用的产品逐步增加功能。不要以尚未成熟的复杂性取代已经可用的产品。

  • 保持组件的模块化,并明确划分不同职责与关注点。

  • 当成熟且维护良好的库能够降低整体复杂度或提高可靠性时,应优先采用。除非有明确理由,不要重复实现通用功能。

  • 在自行实现功能或新增依赖之前,应优先评估项目现有依赖的能力。应先查阅相关文档和类型定义,不应未经确认就认定某个库不具备所需能力。

  • 架构决策应着眼于长期演进。不要采用仅能解决当前问题、且预期需要在后续替换的权宜方案。

  • 在设计解决方案之前,先研究成熟产品如何解决同类问题。优先采用经过验证的模式和约定,避免从零开始另行设计一套方案。

禁止清单(不主动考虑、不主动提议、不实现,遇到只记入 TODO 技术债列表)

  1. 法律合规:商业库授权、开源协议合规、GDPR/个保、隐私政策(法务负责)。
  2. 依赖安全:NPM 及第三方包漏洞、安全补丁、依赖升级策略。
  3. 访问安全:服务只需支持局域网访问(host 绑定 0.0.0.0 即可),不考虑公网暴露、HTTPS、认证/权限体系(登录、RBAC)、限流、防爬、数据加密、审计日志。

红线清单(快速阶段也不能省,现在便宜、以后极贵)

  1. 数据模型/表结构:认真设计,建表慎重——改表成本远高于写代码。已经在数据库执行过的更新脚本禁止修改或复用编号;表名、字段及约束调整必须使用新编号,并验证新建数据库、已更新数据库和重复启动,保留现有数据。
  2. 目录结构与模块边界:保持简单清晰,不堆一坨代码。
  3. 基础错误日志:出错时至少能看到发生了什么。
  4. Git:小步提交,保持历史清晰。
  5. 基础输入校验:仅防止程序崩溃,不做安全加固。
  6. 环境差异配置(端口、地址等)与代码分离(.env 或配置项)。

技术栈

产品方向:平台当前仅支持抖音(douyin);小红书等其他平台的业务与代码已全部移除,不得重新引入。

环境先行:创建时只创建抖音浏览器环境,不填写昵称、UID 或 Cookie,不创建占位账号。未登录环境与已登录账号在“我的账号”同一列表展示,不设独立区域;缺失资料使用前端默认值,不创建占位账号,不写回数据库。待登录行仅保留“登录并同步账号”,不提供账号详情或其他账号操作。统一列表按共用的浏览器环境数据库 ID 倒序排列,新建项在前;登录绑定保留环境 ID,不改变行的相对位置。首次浏览器身份核验成功后,同一事务创建/关联账号并同步真实 UID、昵称、头像、抖音号与 secUID;同一 UID 只能绑定一个账号,已有绑定禁止换绑或自动覆盖。后续同 UID 核验更新平台资料,不覆盖本地备注和业务设置。身份核验成功在同一事务解除仅由未登录造成的采集阻塞,恢复为待采集;保留原采集窗口、游标、累计进度和完成时间,不清除其他错误、不标记采集完成。浏览器 profile_id 与指纹 seed 在环境创建时确定,账号绑定不得改变它们;旧环境保持原 profile_id 和 seed,不重建浏览器或清空 Cookie。指纹 seed 由环境独立序列分配,不再依赖账号 ID。

浏览器内存:每个环境独立配置 memory_limit_mb,默认 2048 MB,允许 512–65536 MB;创建与编辑均可设置。运行中保存不重启浏览器、不改变指纹、profile_id 或账号绑定,下次启动生效;启动时必须将保存值传给网关并施加真实内存上限。

作品封面:自有账号与监测账号采集后都下载封面,图片保存为静态文件 <账号 UID>/<作品 ID>.<实际图片扩展名>,不把图片字节存入数据库。目录由 CREATOR_COVER_DIR 配置,开发默认 .data/covers,容器使用持久卷;作品封面接口只读取本地文件,不使用远程图片兜底。下载与文件错误必须可追踪并反映到同步结果;同一来源每次同步最多回填 40 张历史缺失封面。

账号创建交互:在“我的账号”列表通过 Modal 创建,网关必选,浏览器指纹为默认折叠的可选配置;创建成功关闭弹窗并刷新列表,不保留独立新增页面。未登录环境及空昵称仅在前端显示“待登录”,不将此文案写入数据库。

我的账号操作:列表只保留一个状态感知主操作和“更多”;未绑定环境仍仅显示“登录并同步账号”。已绑定账号暂停时显示“恢复运行”,已知需要登录时显示“重新登录”,实时浏览器未运行时显示“启动环境”,浏览器运行且最近核验登录正常时显示“同步资料”,状态无法确认时显示“检查状态”。操作前读取当前账号与真实网关运行状态,不用保存的 runtime_id 冒充运行;登录、启动、同步资料不会偷偷恢复已暂停账号。正常登录直接核验并同步平台资料,只有明确 awaiting_login 才展示二维码;资料同步不重新采集作品。编辑标签、编辑账号、启动环境、暂停账号、停止浏览器、删除收纳到“更多”;暂停、停止、删除均明确确认。暂停账号会暂停账号调度并停止浏览器;仅停止浏览器不暂停账号。状态读取失败必须明确提示,不假装正常。

自有账号作品:作品总数使用抖音最新非空 aweme_count,已保存 work_count 仅表示采集进度,不得互相替代;未知总数与零作品必须区分。自有账号作品库保存平台分页返回的全部历史作品,不受采集回看天数限制;回看天数仍限制监控账号作品和评论采集。详情分页浏览已采集作品,并明确展示采集进度与未完整状态,不能将采集成功等同于已采齐。

我的账号状态:列表明确区分“调度状态”“业务状态”“采集状态”;调度可运行及业务正常不代表作品或评论采集正常。列表的采集状态仅展示一个 Tag,不展示时间、不区分作品和评论;任一采集异常优先于采集中或完成,异常原因只在悬停 Tag 时展示并去重。详情页仍分别展示作品、评论结果和真实上次完成时间。浏览器需要登录时明确中文提示,未登录环境只显示“待登录” Tag。缺少状态及读取失败不能冒充待采集或采集正常;没有完成时间不虚构时间。采集成功只表示上次任务完成,不表示作品已采齐。资源问题造成的采集受阻不能永久退出调度;仅调度开启、已登录且业务正常或禁言的自有账号可自动重试。重试保留原窗口、游标、累计进度和上次完成时间;选取重试任务不清除错误,只有实际执行采集才更新结果,不以同步资料冒充采集成功。

作品分析:左侧原“竞品分析”统一为“作品分析”,页面通过“竞品账号”“我的账号”两个 TAB 严格区分作品来源;各 TAB 独立保留筛选与分页。支持所属账号、发布时间和互动阈值筛选,以及发布时间、点赞、评论、分享、收藏、24h 点赞增量升降序;自有作品额外支持观看量,不展示“时间核验状态”筛选。默认最新发布在前,条件变化回到第一页,筛选与排序作用于全部已采集作品后再分页;缺失指标显示“—”,排序放末尾、阈值筛选不将其当作 0。未采集作品不参与分析。

账号事件监听:仅支持抖音,自有账号默认关闭,在“我的账号”的“监听状态”列逐个开启,未登录环境不能开启。互动通知使用网页通知列表只读轮询,不再依赖私有 WebSocket SDK;每页 50 条,is_mark_read=0,不标记已读,原始 JSON 在 Python 解码以保留 64 位 ID。只记录点赞、评论、关注、转发,私信在独立收件箱同步与展示,不并入事件聚合,不自动互动。监听依赖已登录且运行中的浏览器;按账号固定页面并核验 UID,支持多标签页。分组读取位置、开启边界和事件在同一事务保存,提交成功后确认,重复投递去重;开关代次拒绝关闭期间的旧投递;首次开启、重新开启和服务重启均补齐平台仍可返回的全部历史和漏收记录,包括关闭期间通知,正常运行约每 5 分钟完整核对一次。开启时间只用于历史标记,不作为丢弃依据,补入保留平台原时间;不按头部位置或 100 页上限截断完整核对。开启设置不等于接收正常,状态应展示实际读取结果、最后成功时间及错误或可能遗漏。左侧“事件聚合”只展示当前已开启账号的已接收互动通知,按接收时间倒序,支持账号、类型、接收时间范围筛选和分页,每 5 秒刷新;关闭后保留历史但隐藏,重新开启后可查看。缺失发生时间不能用接收时间冒充;真实验收必须核对平台、数据库和页面同一通知,不把读取成功或平台合并后的重复互动当作新事件通过。

事件聚合资料:互动用户展示真实昵称与 UID,以平台返回的 secUID 链接主页,不使用数字 UID 猜测主页;发生时间在前、接收时间紧邻其后,所属账号及筛选选项仅显示昵称。对应作品展示 48px 小封面并链接抖音作品详情,图文使用 note 地址;缺封面保留作品入口,缺资料明确标示。通知中的作品封面复用 <作者 UID>/<作品 ID>.<图片扩展名> 本地缓存,不创建占位账号或作品,不回退展示远程图片。历史核对可补齐已保存事件的用户与作品资料,但只更新同一互动 UID、同一作品 ID 的资料,不改原事件时间、内容、历史标记或去重规则;封面下载失败单独记录和展示,不能阻断通知保存,也不能伪装成已缓存。

私信管理:仅支持已开启监听的自有抖音账号,独立聊天标签共享原浏览器资料,不干扰采集或主页面登录。展示聊天客户端已加载会话的最近 50 条消息,不宣称完整历史;只支持手动发送文字,不自动回复、不群发。聊天界面采用 Ant Design X Conversations、Bubble.List 和 Sender:左侧合并联系人并明确所属账号,同一联系人在不同账号下为独立会话,草稿按账号与联系人隔离。会话按最近一条有真实时间的收发消息倒序排列,无真实时间置末尾,不以保存时间或待发送请求的创建时间冒充;同时间按账号 ID、联系人 UID 固定排列,后端分页与前端合并使用相同规则。延迟返回的旧摘要不覆盖新摘要,刷新或加载更多不改变当前选中会话。会话列表与消息区联系人展示聊天客户端专用资料接口返回的真实昵称,支持普通用户与 AI 分身,不用普通主页接口代替、不用 UID 或会话标题占位;平台昵称本身为“用户+数字”时保留原值,未获取昵称显示“昵称未获取”。昵称独立于新消息同步,已有会话可补齐及更新昵称;核验资料 UID 与会话对方一致,读取失败明确显示错误但不阻断消息保存,不改发送对象或聊天记录。专用标签从已登录的个人中心“消息”入口进入,沿用现有登录,不增加私信扫码;直接 /chat 的二维码不能作为账号未登录的依据。仅以聊天客户端的实际 UID 与已绑定 UID 一致确认私信身份,不创建账号或改变绑定。发送请求先持久化并按请求 ID 去重;超时、重启中断及 SDK 网络错误 1008 标记“结果未确认”,不自动重发,不伪造送达或已读。同步记录须持久化后推进检查点;关闭监听阻止旧代次写入。

评论聚合:独立页面通过“我的作品评论”“竞品作品评论”两个 TAB 严格区分来源,各 TAB 保留筛选与页码。列表展示评论发布时间、评论者、内容、所属账号名称及对应作品的小封面;所属账号列与筛选下拉均不展示 UID,缺名仅显示前端“未命名账号”,评论者显示不变;作品使用本地封面,点击在新标签页打开抖音作品页面,缺图明确提示,不展示作品标题、不使用远程图片兜底。支持所属账号筛选,以及最近 1/6/12 小时、1/3/5/7 天筛选,默认最近 1 天。按评论发布时间计算范围,最新评论在前,同时间按评论 ID 倒序;未记录发布时间、未来时间及未采集评论不参与。筛选先作用于全部已采集评论再分页,条件或每页数量变化回到第一页;只读展示,不新增采集机制。

前端框架:Umi Max 4.7 + React 19;

组件库:antd 6.6.5 + @ant-design/pro-components 3.x(beta 线)+ @ant-design/icons;仅使用 antd/pro 默认组件原样实现,禁止自定义封装与样式魔改;组件不满足业务时改交互逻辑适配组件;

后台管理:create-umi 脚手架(ant-design-pro 同构),约定式路由;

图标 @ant-design/icons;

后端 Go 库 fiber/v3; logus; viper; cobra;samber/lo; samber/mo

用户交互需要使用 skill:impeccable 优化交互操作

Agent Skills 与工具(开发前必须确认已安装)

以下 skill 与工具是本项目 antd/pro 开发的官方标准,缺失时先安装再写代码:

  • .pi/skills/antd/:@ant-design/cli 工作流(API 离线查询、demo、token、lint、迁移)
  • .pi/skills/ant-design/:antd 6 + Pro 决策指南与回归 checklist
  • 全局 @ant-design/cli(antd 命令):缺失时 npm install -g @ant-design/cli

使用规则:写/改 antd 组件前先 antd info <Component> --format json 查 API,不凭记忆;改完跑 antd lint <path> --format json,警告清零才算完成;命令始终加 --format json。

UI 规范(antd/pro)

  • 页面标题用 PageContainer 自带 title/subTitle;页头不另造按钮行。
  • 列表页(对齐 ant-design-pro TableList):主操作按钮(带 icon)放表格 Card 内右上角,与卡片左上角统计信息同行(Flex justify="space-between" align="center",间距用 marginBottom: 16),按钮只出现在卡片内,不再经 usePageActions 注册到页头;非列表页的操作按钮仍经 usePageActions 注册到页头 content 槽。
  • 弃用 API 禁止回潮:Alert 用 title/description(非 message)、纵向堆叠用 Flex vertical(非 Space direction)、静态 message.xxx 改 App.useApp()。以 antd lint 结果为准。

沟通方式

  • 向使用者回报时,使用清楚直白的语言说明做了什么、结果如何。最终回复禁用术语、技术实现细节与工程腔。写法是:对一个聪明但没在看代码的人解释。

  • 实际执行过程(思考、规划、写程序、除错、解决问题)保持完整的技术严谨度,这条规范只适用于对使用者的沟通方式。

回复风格

  • 只写结论、实际改动、原因、验证结果
  • 不描述推进动作,禁用「我先……再……」等叙述句式
  • 不使用工程汇报腔(「落地」「落到」「推进」等类似用语)
  • 直接、专业、去表演化
  • 回复文字永远使用与对方相同的语系,专有名词维持英文
  • 不使用口语化表达,说重点,简单明了
  • 需要时搭配条列式与表格加强输出可读性

决策规则

  • 当方案有多个选项时,列出每个选项的优缺点,并明确指出推荐选项与原因,先问我。
  • 有多种实现方式时,选最简单能跑通的。
  • 遇到"禁止清单"中的问题:不展开、不实现,追加到 TODO 技术债列表即可。

Sub-Agent 使用时机

当任务符合以下任一条件时,直接 spawn sub-agent 分工执行,无需询问使用者:

  • 任务可拆分为多个平行且无依赖的子任务

  • 各子任务职责明确分离,合并执行会造成 context 混杂

  • 大量结构相同的重复性任务(可用 spawn_agents_on_csv batch 执行)

  • 各子任务需要不同的 model 配置或 sandbox 权限,例如:

    • 探索型任务使用轻量 model + read-only sandbox
    • 审查型任务使用高推理 model + read-only sandbox
    • 修改型任务使用执行导向 model + workspace-write sandbox

验证标准

开始任务前先定义完成标准。交付前依此验证,发现问题就修好再测,不把未完成的工作交回给使用者。只有确认完成,或遇到真正需要使用者介入的障碍时,才回报。

UI 设计

默认收敛、克制、常规;尺寸与间距根据界面类型、信息密度、平台习惯、使用频率和视觉层级判断,不写死统一规格,也不主动放大。辅助入口、设置、开关、工具按钮不应抢视觉中心。常见功能必须使用大众通用、用户一眼可识别的图标隐喻,优先成熟图标库、系统图标或行业通用符号,不为差异化自创奇怪图标;自定义图标也必须保持常见轮廓、比例和语义。除非明确要求强调,否则优先用位置、分组、轻微颜色、hover、tooltip、分隔线和状态反馈表达层级,避免夸张尺寸、重色块、大圆角、厚边框、强阴影、装饰性渐变和营销页式布局。实现后必须与同屏元素对比检查,若显得突兀、过大、过重或破坏信息密度,应主动收敛。