# Agent 项目级改代码原则

## 适用范围

本仓库包含生产级自动投放系统。凡是修改 `examples/auto_put_ad_mini` 下的代码,都要按生产自动化系统处理,不能当成一次性脚本。

## 生产自动化原则

- 优先使用数据库和飞书配置,避免硬编码账户级参数。
- 人工配置应尽量只包含账户 ID、人群包名称、出价、预算、是否执行。
- 每日自动流程应根据腾讯侧当前状态自行判断是否需要创建广告或补创意。
- 飞书配置行关闭时,只代表不再新建广告或补创意,不能因此暂停、删除或改动既有广告。
- 尽量保持幂等。重复运行时应跳过已完成的工作,只补缺口。
- 当前新建广告统一使用每天 `06:00-20:00` 的投放时段,由 `ad_delivery_template.time_series_json` 管理;修改全局时段时要同时保持代码默认值与数据库启用模板一致。
- 飞书「自动化账户」表按 `日期` 作为配置生效批次;主流程只处理当天日期的行,其他日期不覆盖或禁用已有账户配置。`年龄` 支持如 `30-66+`; `地域` 支持中文省市或 `不限`。

## 生产服务架构

- 生产使用同一个完整 `ad-put-agent` 镜像启动两个容器:`ad-control-service` 和 `ad-daily-service`。
- `ad-control-service` 负责唯一飞书 WebSocket、运营暂停/停止/恢复、ROI 整批审批执行和每 10 分钟实时 CPM 调控。
- 实时调控账户范围是历史 `ad_creation_account_config` 与启用白名单的交集;飞书关闭创建配置只停止新建/补创意,不能让既有广告退出实时管理。
- `ad-daily-service` 每天 10:30 调用现有广告/创意创建流程,每 2 小时扫描腾讯创意正式审核结果,并可在每天 11:00 计算和发布日级 ROI 批次。
- 旧 `server.py`、`execute_once.py` 和 `run.py` 的模型调控链路不再作为生产入口,但暂不删除历史代码。
- 飞书生产控制命令使用确定性解析,不能让模型直接决定账户范围或执行腾讯写操作。
- `暂停` 表示仅暂停到下一投放日 06:00;`停止` 表示持续停止;`恢复` 只解除运营暂停,不能解除仍生效的 CPM 暂停。
- 所有飞书触发的腾讯写操作都必须二次确认。群聊命令必须来自配置群并 @机器人,发送人必须在允许列表。
- 运营暂停状态和 CPM 暂停状态必须分开持久化。原本人工暂停的广告不能被系统认领或自动开启;腾讯后台人工重新开启时以人工操作为准。
- 实时控制和飞书写操作必须共用 MySQL advisory lock,并执行腾讯写后回读校验。
- `RTC_APPLY_ENABLED`、`DAILY_ROI_ENABLED`、`ROI_APPLY_ENABLED` 默认关闭;生产切换前必须先完成 dry-run 和只通知验证。

## ROI 北极星指标与日级调控

- ROI 是指导渠道、账户、广告和创意动作的核心北极星指标,必须作为独立领域模块维护,不能散落在飞书、腾讯 API 或调度代码中。
- ROI 指标计算必须保持纯数据输入/输出,不能依赖飞书审批、腾讯写操作或具体动作执行器。动作策略只能消费一个明确的 `metric_version`。
- ROI 公式、收入/成本归属、裂变口径或实体粒度发生语义变化时,必须升级指标版本并保存新快照;不得覆盖或重算成旧版本历史结果。
- 策略阈值和动作语义使用独立 `policy_version`;指标版本与策略版本必须同时写入每个运行批次。
- 每次运行必须保存完整实体快照、阈值配置、动作建议、可执行性原因和最终执行审计,不能只保存候选或飞书表格。
- 日级 ROI 当前读取 T-1 至 T-3 的连续三日数据。所有渠道都进入指标和通知,只有当前自动化腾讯账户允许在整批审批后执行。
- 当前低 ROI 动作只暂停 `dynamic_creative_id`,不能暂停整个广告;高 ROI 动作只调整广告永久基础出价,默认上调 10%。
- 同广告永久基础出价 3 天内最多上调一次,且不超过首次纳管基础价的 2 倍。调整后必须同步实时 CPM 模块的基础出价,避免恢复旧值。
- ROI 整批审批有效期默认 120 分钟。腾讯写操作必须与实时调控共用数据库 advisory lock,执行前回读映射和状态,执行后再次回读校验。
- `DAILY_ROI_ENABLED=1`、`ROI_APPLY_ENABLED=0` 只允许计算、快照、报表和审批预览;只有两个开关都为 1 时,审批通过后才允许执行腾讯写操作。
- 禁止配置 `DAILY_ROI_ENABLED=0`、`ROI_APPLY_ENABLED=1`;服务启动时必须拒绝这种不完整配置。

## 模块 B 创意创建规则

- 目标是单广告最终合格创意数,不是单次生成的 pending 行数。
- 当前目标:每条广告最终有效创意不少于 8 个。
- `DENIED` 创意不计入有效创意。
- 每次创意准备中,每个视频来源最多获取并尝试 100 条视频。
- 先尝试 primary 视频来源。
- 如果 primary 视频经过品类过滤、风险审核、素材质量过滤后仍无法产出可用 pending creative,再用同一个 `crowdPackage` 尝试 `source=hot`。
- `source=hot` 只能改变内容服务的视频来源,不能改变账户配置的人群包。
- hot 兜底必须走和 primary 完全一致的品类过滤、风险审核、素材召回和审批流程。

## 承接视频安全和质量

- 创建落地计划或腾讯创意前,必须先做视频风险审核。
- 默认风险阈值是 5。风险等级 6-10 应拦截。
- 风险接口异常时要保守处理,不能创建落地计划。
- 使用 `videoContentList.category` 做内容品类过滤。
- 默认过滤品类是 `早中晚好`、`祝福音乐`、`历史名人`。
- 品类过滤必须发生在风险审核、素材召回和 `xcx/save` 之前。
- 不要把 `categoryName` 当作内容品类使用;它是召回逻辑使用的语义/视觉特征字段。

## 素材召回规则

- `videoContentList` 只负责选择承接视频,素材召回特征必须通过 `video_id` 查询 ODPS 表 `loghubods.dwd_video_element_contribution_analysis`。
- primary 和 hot 视频都必须走同一套 ODPS 特征查询和素材召回逻辑。
- 当前只使用 ODPS 特征中的 `解构选题` 列和 `实质` 维度;`形式`、`意图` 不参与素材召回。
- 当前召回映射:
  - `解构选题` -> `VIDEO_TOPIC`
  - `实质 + 灵感点` -> `INSPIRATION_SUBSTANCE`
  - `实质 + 关键点` -> `KEYPOINT_SUBSTANCE`
  - `实质 + 目的点` -> `PURPOSE_SUBSTANCE`
- 同一视频的多个召回 query 可以并行调用 `batchByText`,再按 `material_id` 合并去重。
- 当前素材硬筛只看相似度:`score >= 0.8`。
- 曝光、CTR、ROI 只作为审批展示和兜底排序参考,不作为硬筛。
- 素材通过相似度筛选后,默认按历史消耗 `cost` 倒序选择。
- 落地页视频去重按素材来源拆池。历史素材链路同一 `crowd_package + landing_video_id` 默认最多 1 次。
- AI 生成素材链路和历史素材链路互不占用 landing 去重名额;AI 链路同一 `crowd_package + landing_video_id` 默认最多 1 次。
- 落地页视频跨轮近期排重只能基于已提交腾讯、已执行或已有明确投放结果的记录。
- 未提交腾讯的 `prepared` / `pending` 记录不能进入跨轮排重;它们只能用于当前运行内去重,或作为中断后可复用的恢复缓存。
- 同一广告内同一 `landing_video_id` 仍保留限频保护,默认最多 1 次。
- 素材需要跨账户/跨天排重,默认按同人群包下近期使用过的 `material_id` 排除。
- 当前运行内的审批候选需要做轻量展示去重:同一 `crowd_package + material_id` 只进入本轮审批表一次;该去重只存在内存,不写入历史排重库。
- 当前 `prepare_one_creative_for_ad` 会真实上传图片并创建 `xcx/save` 落地计划,默认不能直接并行执行;如需并行,必须先拆分为纯候选选择和副作用串行确认两段。
- `videoContentList` 每个 source 默认最多读取 3 页,每页 100 条,按 `video_id` 去重合并。

## 视频召回人群包映射

- 腾讯投放、人群包授权、落地计划仍使用账户配置的人群包。
- 内容服务 `videoContentList` 的 `crowdPackage` 可以有独立映射。
- 当前默认映射:
  - `cell*year*商业` 获取视频时映射为 `wx*商业`
  - `回流330以上人群` 获取视频时映射为 `R_330+`
- `source=hot` 只能改变内容服务的视频来源,不能改变映射后的召回人群包。
- 内容服务 `source` 也可以按人群包做独立映射。
- 当前默认没有 source 覆盖;`回流330以上人群` 获取视频时使用 `.env` 中的 `prior`。

## 腾讯人群包和资产规则

- 创建广告前,人群包 ID 必须验证为目标广告账户可见。
- 跨账号使用人群包时,要走腾讯人群授权接口,授权后再次验证目标账户可见。
- 品牌图 ID 是账户级资产,不能复用其他账户的 `brand_image_id`。
- 监测链接 `feedback_id` 是账户级资产,应通过配置模板查找或创建。

## 飞书同步规则

- 飞书配置同步是每日主流程的一部分。
- 飞书同步失败时应停止创建流程。
- `是否自动化执行` 不等于 `是` 时,只关闭新建/补量,不删除 DB 配置,不修改腾讯既有资产。
- 不要因为飞书行被关闭就删除或回滚线上状态。

## 代码修改纪律

- 修改范围要收敛在当前生产链路相关模块内。
- 运营可能需要调整的生产行为,应提供配置项。
- 属于账户、人群包、预算、出价、品牌、监测链接的值,优先进入飞书或 DB,不要写死在代码里。
- 运行 `examples/auto_put_ad_mini` 下的 Python 脚本时,默认使用仓库根目录 `.venv/bin/python`;不要直接使用系统 `python3`,避免缺少 FastAPI、PyODPS、OSS 等项目依赖导致误判。
- 端到端执行前,必须意识到可能产生外部副作用:腾讯广告/创意创建、图片上传、DataNexus 监测链接创建、人群包授权、`xcx/save` 落地计划创建。
- 端到端流程中断时,已经完成的外部副作用不会自动回滚。
- 行为、环境变量或运营流程变化时,要同步更新面向生产的文档。
