"""启动确认窗 agent 兜底样本表(launch_confirm_sample)。 pricebot 的 launch_confirm_agent 每次"静态 PROFILES 没认出、靠 LLM 兜底放行"一个跨 App 启动确认窗(繁体 / 英文 / 小语种 / ROM 改版)时,把完整样本 server→server 落这里。研发定期 **人工**把 exec_success=true 的样本沉淀进 pricebot 的 launch_confirm.PROFILES(静态快路径), 让以后同款窗不必再调 LLM。 设计: - 与登录无关、不鉴权(server→server 共享密钥头 X-Internal-Secret)。 - **都上报、不去重**(by design):同一种窗多台机器上报多条,研发按 host_package / locale 聚合后人工筛。 - 大字段(弹窗树快照 + LLM plan + 设备信息)塞 payload(JSONB)兜底,免得加字段就迁移 schema。 """ from __future__ import annotations from datetime import datetime from sqlalchemy import JSON, Boolean, DateTime, Integer, String, func from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column from app.db.base import Base # PG 上用 JSONB(可建 GIN 索引),SQLite(本地/测试)退化为通用 JSON(同 price_observation)。 _JSON = JSON().with_variant(JSONB(), "postgresql") class LaunchConfirmSample(Base): __tablename__ = "launch_confirm_sample" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), index=True, nullable=False ) # pricebot 侧 trace_id:回指原始 trace,能去 price.shaguabijia.com 查完整上下文。 trace_id: Mapped[str | None] = mapped_column(String(64), index=True, nullable=True) device_id: Mapped[str | None] = mapped_column(String(64), index=True, nullable=True) # 弹窗宿主包(= 触发判据, 也是研发沉淀进 PROFILES.host_packages 的键)。按它聚合人工筛。 host_package: Mapped[str | None] = mapped_column(String(128), index=True, nullable=True) # 被打开的目标 App 包名(标题里"想要打开 X"的 X 是变量, 据此把它从 sign 里挖空)。 target_app: Mapped[str | None] = mapped_column(String(128), nullable=True) # 系统语言地区(zh-TW / en-US…)—— 多语言问题的核心维度。 # ⚠️ 前端 AgentStepRequest 暂未上报设备信息 → 现恒 None,待前端加 device_info 字段后回填。 system_locale: Mapped[str | None] = mapped_column(String(32), index=True, nullable=True) # 这次兜底是否成功放行(弹窗消失=True;放弃 / LLM 没认出=False)。研发只沉淀 True 的。 exec_success: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) # 弹窗标题全文(研发据此提 launch_sign),如"「傻瓜比价」想要開啟「京東」"。 dialog_title: Mapped[str | None] = mapped_column(String(256), nullable=True) # 大 JSON:完整样本。{is_launch_confirm, llm_model, llm_reason, plan, dialog_tree, # device_info: {screen, locale, brand, model, android_version, rom_version}} payload: Mapped[dict | None] = mapped_column(_JSON, nullable=True) def __repr__(self) -> str: # pragma: no cover return ( f"" )