/* AI 选购助手(壳内层)— 9 个统一 Agent 工具的脚本化演示:
工具 needs_input → 对话内 GUI → resume → 结构化结果卡 → Agent 继续编排。
四个固定小工具仍可从“小工具”页独立打开;AR 量房 / 采光检测也可在同一对话内弹出。
电视对比看板以紧凑交互卡嵌入对话,并保留展开完整看板入口。
三条剧本:主线(标准选购闭环)· 反光 · 烧屏。对话为 demo 剧本,非真模型。
深链:?tool=agent · &auto=1 自动替用户点选项(走 AR→设备兼容→对比→配件→链接核验,评审/截图用) */
const AG_RM = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
const AG_AUTO = new URLSearchParams(location.search).get('auto') === '1';
const agCx = (...a) => a.filter(Boolean).join(' ');
const AG_CMD = '我想买台客厅用的电视';
/* 主页 Agent 卡片的三个建议问题 —— 文案单一事实源(home.jsx 读 window.AG_ASKS),顺序即剧本路由 */
const AG_ASKS = ['预算 1.5 万,客厅怎么选', '白天客厅很亮,怕反光', 'OLED 会烧屏吗'];
/* 协议层保留英文 ID;展示层一律使用中文名称。 */
const AG_TOOL_LABELS = {
research_products: '产品研究',
compare_products: '电视对比',
verify_model_and_offer: '购买链接核验',
evaluate_room_and_size: '房间与尺寸评估',
recommend_tv_accessories: '电视配件推荐',
plan_device_setup: '设备与最佳设置',
plan_installation_and_delivery: '安装与入户规划',
evaluate_lifecycle_options: '长期成本与处置',
diagnose_tv_problem: '验机与故障诊断',
};
const AG_RESULT_LABELS = {
needs_confirmation: '等待确认',
needs_input: '需要补充',
partial: '部分完成',
ok: '已完成',
PurchaseQuestionnaire: '选购问卷',
RoomMeasurementWizard: '空间测量',
AmbientLightCapture: '环境光采集',
UsageMixSelector: '使用习惯选择',
PurchaseLinkInput: '商品链接输入',
required: '必需',
recommended: '建议',
not_needed: '不需要',
};
const agToolLabel = (id) => AG_TOOL_LABELS[id] || id;
const agResultLabel = (text) => Object.entries(AG_RESULT_LABELS)
.reduce((s, [raw, label]) => s.replaceAll(raw, label), String(text || ''));
/* 尺寸 → 在库命中数(dbdata.jsx 按尺寸实数:75″=7 · 85″=23 · 98″+=13)/ 包装箱最长边(入户预检用) */
const AG_HIT = { 75: 7, 85: 23, 98: 13 };
const AG_BOX = {
75: { long: '1.86 m' },
85: { long: '2.08 m' },
98: { long: '2.36 m', warn: '箱体超 2.3 m:需斜立进梯,建议约 2 名搬运(免费)' },
};
/* 预算选项按尺寸给(价格全部来自机型库在售录价;85″ 从 7 千档起步——在库 7499 有雷鸟鹤6U/Vidda发现X/酷开K6 PRO 三台) */
const AG_BUDGETS = {
75: [
{ label: '4 – 6 千', v: '75b1' },
{ label: '1 万左右', v: '75b2' },
{ label: '不确定,直接推荐', v: '75b2' },
],
85: [
{ label: '7 – 8 千', v: '85v' },
{ label: '1 万左右', v: '85low' },
{ label: '1 – 1.5 万', v: '85mid' },
{ label: '不设上限', v: '85high' },
],
98: [
{ label: '3 万左右', v: '98b1' },
{ label: '5 万以上', v: '98b2' },
{ label: '不确定,直接推荐', v: '98b1' },
],
};
/* Agent 里出现的机型只取两类证据:db/1.sql 的 brightness_plot,或仓库内完整评测报告。
peak / full 分别为该模式窗口曲线最大值与 100% 窗口平均值;价格为报告录入快照,不是实时到手价。 */
const AG_TV_DATA = {
vision5: { id: 10000228, brand: '华为', model: '智慧屏 Vision 5 75', panel: 'MiniLED', size: 75, peak: 1080, full: 592, mode: 'HDR 标准', source: 'DB 实测', price: 5647, hl: '生态与大屏体验', why: 'HDR 标准模式实测峰值 1080 nit、100% 窗口 592 nit。报告更看重智慧交互与家居生态;若只追求极端画质,不必为生态功能加预算。' },
vision5se: { id: 10000233, brand: '华为', model: '智慧屏 Vision 5 SE 75', panel: 'MiniLED', size: 75, peak: 1088, full: 446, mode: 'HDR 标准', source: 'DB 实测', price: 4499, hl: '预算友好', why: 'HDR 标准模式实测峰值 1088 nit、100% 窗口 446 nit。峰值接近 Vision 5,但大面积亮场更弱;更适合预算优先、华为生态明确的家庭。' },
he6: { id: 10000223, brand: '雷鸟', model: '85 鹤6 Ultra 25款', panel: 'QD-MiniLED', size: 85, peak: 2428, full: 685, mode: 'HDR 标准', source: 'DB 实测', price: 7499, hl: '7 千档均衡', why: 'HDR 标准模式实测峰值 2428 nit、100% 窗口 685 nit。库内报告认为它同价位均衡、控光好、系统干净;短板是画面略有提亮。' },
viddaX: { id: 10000231, brand: 'VIDDA', model: '发现X 2026款 85', panel: 'QD-MiniLED', size: 85, peak: 1424, full: 500, mode: 'HDR 标准', source: 'DB 实测', price: 7499, hl: '雾面屏路线', why: 'HDR 标准模式实测峰值 1424 nit、100% 窗口 500 nit,亮度明显低于同价鹤6。优势是报告记录的雾面抗反与入门性价比,代价是清晰度和通透感。' },
he7: { id: 10000219, brand: '雷鸟', model: '85 鹤7 PRO 25款', panel: 'QD-MiniLED', size: 85, peak: 4016, full: 708, mode: 'HDR 标准', source: 'DB 实测', price: 9799, hl: '影音游戏双修', why: 'HDR 标准模式实测峰值 4016 nit、100% 窗口 708 nit。报告把它列为大屏游戏与观影的高性价比选择;短板是提亮偏多、外壳做工一般。' },
viddaXPro: { id: 10000222, brand: 'VIDDA', model: '发现X Pro 2026款 85', panel: 'QD-MiniLED', size: 85, peak: 1883, full: 721, mode: 'HDR 标准', source: 'DB 实测', price: 9999, hl: '调校较标准', why: 'HDR 标准模式实测峰值 1883 nit、100% 窗口 721 nit。报告评价是没有明显大短板、调校较标准;不足是画面偏清淡、对比感不强。' },
q10l: { id: 10000225, brand: 'TCL', model: '85 Q10L', panel: 'QD-MiniLED', size: 85, peak: 4728, full: 835, mode: 'HDR 标准', source: 'DB 实测', price: 11999, hl: '持续亮度强', why: 'HDR 标准模式实测峰值 4728 nit、100% 窗口 835 nit。库内报告认可亮度、光晕控制和超窄边;像素响应与背光延迟不是它的强项。' },
e8s: { id: 10000788, brand: '海信', model: '85 E8S', panel: 'RGB-MiniLED', size: 85, peak: 2631, full: null, refl: 1.732, mode: 'HDR 鲜艳', source: '本地报告实测', price: 14999, hl: 'RGB 控光均衡', why: '本地报告 HDR 鲜艳模式实测峰值 2631 nit、全反射率 1.732%。报告认为画面偏中性、光晕和延迟控制好;HDR 小窗口与白点仍有短板。' },
q10lPro: { id: 10000346, brand: 'TCL', model: '98 Q10L PRO', panel: 'QD-MiniLED', size: 98, peak: 7810, full: 826, mode: 'HDR 标准', source: 'DB 实测', price: 28999, hl: '98″ 高亮旗舰', why: 'HDR 标准模式实测峰值 7810 nit、100% 窗口 826 nit。报告认为整体达到高端水准,但相较 85 Q10L 的提升有限,性价比一般。' },
e8qPro: { id: 10000230, brand: '海信', model: '100 E8Q PRO', panel: 'MiniLED', size: 100, peak: 2887, full: 737, mode: 'HDR 标准', source: 'DB 实测', price: 28999, hl: '真正 100″', why: 'HDR 标准模式实测峰值 2887 nit、100% 窗口 737 nit。优势是比 98″ 更大的实际面积;报告也提醒它对空间、入户和安装要求更高。' },
g5: { id: 10000735, brand: 'LG', model: '65 G5', panel: 'OLED evo', size: 65, peak: 2484, full: null, refl: 0.816, mode: 'HDR 影院', source: '本地报告实测', price: 21999, hl: 'OLED 暗场与抗反', why: '本地报告 HDR 影院模式实测峰值 2484 nit、全反射率 0.816%。暗场、响应和抗反是强项;中国区应用生态与长期静态画面风险需要接受。' },
};
const agTv = (key, extra = {}) => ({ ...AG_TV_DATA[key], ...extra });
/* 推荐会同时吃预算与用途。游戏不再把有明显延迟短板的机型排到前面,直播/体育优先看 100% 窗口。 */
function agPickFor(budgetKey, usage = []) {
const movie = usage.includes('movie');
const game = usage.includes('game');
const live = usage.includes('live');
const seniorFamily = usage.includes('senior') || usage.includes('family');
if (budgetKey === '75b1' || budgetKey === '75b2') return {
say: budgetKey === '75b2' ? '库内有完整实测的 75″ 里,不需要为了花满一万元硬上未知机型——' : '4–6 千的 75″,先看这组有完整实测的候选——',
main: agTv('vision5'), altSay: '如果预算更优先:', alt: agTv('vision5se'),
};
if (budgetKey === '85v') return {
say: '7 千档两台都实测过,亮度和综合完成度更好的这台排前面——',
main: agTv('he6'), altSay: '如果更在意雾面屏:', alt: agTv('viddaX'),
};
if (budgetKey === '85low') {
const calmFirst = (live || seniorFamily) && !game;
return calmFirst
? { say: '你更重视长时间观看和家庭共用,我把调校较标准的放前面——', main: agTv('viddaXPro'), altSay: '想要更强 HDR 高光和游戏能力:', alt: agTv('he7') }
: { say: '你包含游戏或电影需求,实测和报告都更支持这台——', main: agTv('he7'), altSay: '更偏好克制、均衡的调校:', alt: agTv('viddaXPro') };
}
if (budgetKey === '85mid' || budgetKey === '85high') {
if (live && !game) return {
say: '直播、体育和综艺更吃大面积持续亮度,100% 窗口实测更强的排前面——',
main: agTv('q10l'), altSay: '如果更看重 RGB 控光与综合均衡:', alt: agTv('e8s'),
};
const sceneLead = game && movie
? '电影和游戏都要,我先选报告里延迟与控光更均衡的一台——'
: game
? '游戏优先,我先选报告里延迟与控光更均衡的一台——'
: movie
? '电影为主,我先选暗场、控光和画面调校更均衡的一台——'
: seniorFamily
? '家庭共用更看重均衡与稳定,我先把综合完成度更高的一台放前面——'
: '按当前用途和现有实测,我先把综合更均衡的一台放前面——';
return {
say: budgetKey === '85high' ? '85″ 不设上限也不等于要花满;按现有实测,这两台已经覆盖高端需求——' : sceneLead,
main: agTv('e8s'), altSay: '想少花 3 千并换更高的实测亮度:', alt: agTv('q10l'),
};
}
return {
say: budgetKey === '98b2' ? '5 万档机型缺少完整实测,我不会拿标称值硬推;先回到这两台证据完整的百吋候选——' : '3 万左右的百吋档,按相同 HDR 标准模式实测先看这台——',
main: agTv('q10lPro'), altSay: '如果你更在意真正的 100″ 面积:', alt: agTv('e8qPro'),
};
}
/* 反光路线:有反射率实测才展示数值;缺失时明确标“未测”,不再用亮度冒充抗反。 */
const AG_GLARE = {
g07: { say: '7 千档里,鹤6的亮度与报告结论更完整——', main: agTv('he6'), altSay: '想要雾面屏路线可以看:', alt: agTv('viddaX') },
oled: { say: '混合画面路线里,G5 的抗反有本地实测支撑——', main: agTv('g5'), altSay: '想上 85″,就换这台 RGB-MiniLED:', alt: agTv('e8s') },
g1: { say: '1 万左右先看 HDR 高光更强、影音游戏证据更完整的这台——', main: agTv('he7'), altSay: '想要调校更克制的备选:', alt: agTv('viddaXPro') },
g15: { say: '满屏亮画面看得多,100% 窗口实测 835 nit 的这台更稳——', main: agTv('q10l'), altSay: '更看重 RGB 控光与已测反射率:', alt: agTv('e8s') },
gbig: { say: '百吋路线里先按同模式实测亮度排序——', main: agTv('q10lPro'), altSay: '要真正 100″ 再看:', alt: agTv('e8qPro') },
};
/* 烧屏路线只使用仓库内两份完整报告给出的实测与定性结论。 */
const AG_BURN = {
oled: agTv('g5', { hl: '暗场与响应优先', why: '逐像素控光、快速响应和 0.816% 全反射率是核心优势;报告也明确提醒,长期展示固定台标或游戏界面仍有烧屏风险。' }),
mled: agTv('e8s', { hl: '固定画面更省心', why: 'MiniLED 背光没有 OLED 的像素老化路径,更适合台标或比分牌长时间固定;本地报告同时记录了不错的光晕与延迟控制。' }),
};
const agSleep = (ms) => new Promise((r) => setTimeout(r, AG_RM ? Math.min(ms, 80) : ms));
/* ---- 假测量浮层(AR 量房 / 采光检测共用骨架):四相位扫描 → 两条标注 → 回传 ---- */
function AgScanOverlay({ bg, hints, l1, l2, runText, onDone }) {
const { useState, useEffect } = React;
const [ph, setPh] = useState(0);
useEffect(() => {
const k = AG_RM ? 0.1 : 1;
const ts = [
setTimeout(() => setPh(1), 1000 * k),
setTimeout(() => setPh(2), 2300 * k),
setTimeout(() => setPh(3), 3600 * k),
setTimeout(onDone, 4500 * k),
];
return () => ts.forEach(clearTimeout);
}, []);
return (
{hints[ph]}
{ph >= 1 &&
{l1}
}
{ph >= 2 &&
{l2}
}
= 3 && 'ok')}>
{ph >= 3 ? : }
{ph >= 3 ? '测量完成' : runText}
);
}
const AgArOverlay = (p) => (
);
const AgLuxOverlay = (p) => (
);
/* ---- 消息卡片 ---- */
const agPurchaseUrl = (tv) => 'https://search.jd.com/Search?keyword=' + encodeURIComponent(`${tv.brand}${tv.model.replace(/\s+/g, '')}电视`);
const agNit = (n) => n == null ? '未提供' : `${Number(n).toLocaleString()} nit`;
const agRefl = (n) => n == null ? '未测' : `${Number(n).toFixed(3).replace(/0+$/, '').replace(/\.$/, '')}%`;
function AgTvCard({ tv, tag }) {
const color = (BRANDS.find((b) => b.zh === tv.brand) || {}).color || 'var(--accent)';
const second = tv.full != null
? { label: '100% 窗口', value: agNit(tv.full) }
: { label: '全反射率', value: agRefl(tv.refl) };
return (
{tv.brand}
{tv.model}
{tag}
{tv.panel} {tv.size}″ {tv.hl} {tv.source}
HDR 峰值 {agNit(tv.peak)}
{second.label} {second.value}
实测模式 {tv.mode}
{tv.why}
¥{tv.price.toLocaleString()} 库内报告价
完整看板
);
}
function AgEntryCard({ size }) {
const box = AG_BOX[size] || AG_BOX[85];
return (
入户可行性预检通过 ✓
包装箱最长边 {box.long}电梯轿厢 1.6×1.5×2.3 m
入户门净宽 0.95 m拆箱前可直进
楼道转角 · 无台阶路径已核对
{box.warn &&
{box.warn}
}
数据来自小区众包入户库(演示数据)
);
}
function AgArCard() {
return (
AR 量房 · 测量结果
电视墙净宽 3.62 m
沙发视距 2.9 m
推荐尺寸 85″
);
}
function AgLuxCard() {
return (
采光检测 · 测量结果
午后峰值照度 ≈1400 lux
直射时段 西晒 ≈2 h
抗反需求 高
);
}
function AgTradeCard() {
return (
旧机估值 · 回收抵扣
¥360 – 520 55″ 液晶 · 约 2017 年 · 屏幕完好
含上门拆挂与搬运费 · 回收款下单直接抵扣
预约与新机同日上门
);
}
function AgEndCard({ chips }) {
return (
本次方案小结
{chips.map((c, i) => {c} )}
);
}
/* ---- 电视对比的对话内结果 GUI:紧凑查看,必要时再展开完整看板 ---- */
function AgCompareCard({ tvs, scene = '客厅综合' }) {
const { useState } = React;
const [tab, setTab] = useState('evidence');
const sameMode = new Set(tvs.map((t) => t.mode)).size === 1;
const evidenceRows = [
{ n: 'HDR 峰值', get: (t) => agNit(t.peak), better: 'hi', raw: (t) => t.peak, modeBound: true },
tvs.some((t) => t.full != null) && { n: '100% 窗口', get: (t) => agNit(t.full), better: 'hi', raw: (t) => t.full, modeBound: true },
tvs.some((t) => t.refl != null) && { n: '全反射率', get: (t) => agRefl(t.refl), better: 'lo', raw: (t) => t.refl },
{ n: '实测模式', get: (t) => t.mode },
].filter(Boolean);
const rows = tab === 'fit'
? [
{ n: '屏幕 / 背光', get: (t) => t.panel },
{ n: '尺寸', get: (t) => `${t.size}″` },
{ n: '场景亮点', get: (t) => t.hl },
]
: tab === 'cost'
? [
{ n: '库内报告价', get: (t) => `¥${t.price.toLocaleString()}`, better: 'lo', raw: (t) => t.price },
{ n: '每英寸', get: (t) => `¥${Math.round(t.price / t.size)}`, better: 'lo', raw: (t) => t.price / t.size },
{ n: '价格口径', get: () => '报告录入快照' },
]
: evidenceRows;
const bestAt = (row) => {
if (!row.better || (row.modeBound && !sameMode)) return -1;
const valid = tvs.map((tv, i) => ({ i, v: row.raw(tv) })).filter(({ v }) => Number.isFinite(v));
if (valid.length !== tvs.length) return -1;
return valid.reduce((best, cur) => row.better === 'hi'
? (cur.v > best.v ? cur : best)
: (cur.v < best.v ? cur : best)).i;
};
return (
电视对比看板
{scene} 按当前需求权重排序
{[['evidence', '关键实测'], ['fit', '场景适配'], ['cost', '价格']].map(([k, n]) => (
setTab(k)}>{n}
))}
{tvs.map((tv, i) => {i === 0 && 首选 }{tv.brand}{tv.model} {tv.source} )}
{rows.map((row) => {
const best = bestAt(row);
return
{row.n}
{tvs.map((tv, i) => {row.get(tv)}{best === i && } )}
;
})}
{sameMode ? '同模式数据可比较;首选按当前用途排序' : '实测模式不同,保留原始口径,不把亮度强行判胜'} 展开完整看板
);
}
function AgAccessoryNeedsCard({ usage = [] }) {
const stateLabels = { required: '必需', recommended: '建议', not_needed: '不需要' };
const movieHeavy = usage.includes('movie');
const familyUse = usage.includes('family');
const liveUse = usage.includes('live');
const gameUse = usage.includes('game');
const rows = [
['电视支架', 'required', familyUse ? '除匹配 VESA 与承重,还需优先防倾倒' : '立式或壁挂都需匹配 VESA 与承重'],
['回音壁', movieHeavy ? 'recommended' : 'not_needed', movieHeavy ? '电影权重高,电视原生低频偏弱' : '当前用途不需要优先升级声音'],
['电视盒子', 'not_needed', liveUse ? '沿用运营商机顶盒,不重复购买' : '原生流媒体与 DRM 已满足'],
['HDMI 线', 'not_needed', gameUse ? '主机原装线带宽足够' : '当前连接设备无需另购'],
];
return (
配件需要性分析
{rows.map(([n, state, why]) => (
{n} {stateLabels[state]} {why}
))}
);
}
const AG_MOUNT_STYLES = {
floor: [
{ id: 'easel', name: '画架式', desc: '家具感最强', image: 'accessory-stand-easel.webp', roomImage: 'room-composites/room-mount-easel.webp', roomColor: 'walnut' },
{ id: 'pedestal', name: '立柱式', desc: '占地更小', image: 'accessory-stand-pedestal.webp', roomImage: 'room-composites/room-mount-pedestal.webp', roomColor: 'black' },
{ id: 'mobile', name: '移动式', desc: '带轮可推', image: 'accessory-stand-mobile.webp', roomImage: 'room-composites/room-mount-mobile.webp', roomColor: 'gray' },
],
wall: [
{ id: 'slim', name: '超薄固定', desc: '贴墙最薄', image: 'accessory-mount-slim.webp', roomImage: 'room-composites/room-mount-slim.webp', roomColor: 'black' },
{ id: 'tilt', name: '俯仰可调', desc: '可调整上下角度', image: 'accessory-mount-slim.webp', roomImage: 'room-composites/room-mount-tilt.webp', roomColor: 'black' },
{ id: 'arm', name: '全向伸缩', desc: '可拉出与左右转向', image: 'accessory-mount-slim.webp', roomImage: 'room-composites/room-mount-arm.webp', roomColor: 'black' },
],
};
function AgMountResultCard({ mount, tv }) {
return (
支架方案已选商品示意
{mount.modeLabel} · {mount.style.name}
{mount.style.desc}
{mount.lift} {mount.power}
{mount.color.label}
已按 {tv.size}″ 预筛尺寸范围 下单前核对 VESA 与承重
);
}
function AgSoundbarCard() {
return (
回音壁方案商品示意
5.1.2 声道套装 eARC · 杜比全景声 · 无线低音炮
);
}
function AgRoomPreviewCard({ tv, mount }) {
const exactPreview = tv.size === 85 && mount.style.roomColor === mount.color.v;
const bg = exactPreview && mount.style.roomImage ? mount.style.roomImage : (mount.mode === 'wall' ? IMG.wall : IMG.stand);
return (
已保存客厅合成
{exactPreview ? `${mount.style.name} · ${mount.color.label}` : `摆放参考 · ${mount.style.name}/${mount.color.label}已记录`}
{tv.brand} {tv.model} · {tv.size}″
);
}
function AgReturnTvCard({ tv, mount }) {
return (
回到电视方案 {tv.brand} {tv.model}
{mount &&
{mount.style.name}已保存 }
);
}
function AgBuyLinkCard({ tv }) {
return (
推荐搜索入口
京东
{tv.brand} {tv.model} · {tv.size}″ 型号关键词已锁定
打开
);
}
function AgLinkInputCard({ onSubmit }) {
const [value, setValue] = React.useState('');
const valid = /^https?:\/\/\S+$/i.test(value.trim());
const submit = () => { if (valid) onSubmit(value.trim()); };
return (
购买链接核验
粘贴商品详情页链接
setValue(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') submit(); }}
/>
核验
);
}
function AgOfferCard({ tv, source }) {
const custom = source === '你提供的链接';
const checks = custom
? ['型号 / 尺寸一致', '销售主体可开票', '国补地区匹配', '无缩水后缀']
: ['型号关键词一致', '尺寸关键词一致', '进入商品页后核对店铺', '下单前确认地区补贴'];
return (
{custom ? '购买链接核验' : '搜索入口检查'}{custom ? '通过 ✓' : '入口可用 ✓'}
{tv.brand} {tv.model} · {tv.size}″
{source}
{checks.map((check) => {check} )}
价格和补贴会随地区与时间变化,下单当天建议再检查一次
);
}
/* ---- 聊天主体:剧本引擎 ---- */
function AgentChatInner({ onBack, onRestart, ask }) {
const { useState, useEffect, useRef } = React;
const [msgs, setMsgs] = useState([]);
const [ov, setOv] = useState(null); // 'ar' | 'lux' | 'room'
const [ovData, setOvData] = useState(null);
const seq = useRef(0);
const alive = useRef(true);
const pend = useRef(null); // 选项或链接输入等待 {id, res}
const ovRes = useRef(null); // 浮层等待 resolve
const scRef = useRef(null);
useEffect(() => () => { alive.current = false; }, []);
useEffect(() => {
const el = scRef.current;
if (el) el.scrollTo({ top: el.scrollHeight, behavior: AG_RM ? 'auto' : 'smooth' });
}, [msgs, ov]);
const push = (m) => { const id = ++seq.current; setMsgs((s) => [...s, { id, ...m }]); return id; };
const patch = (id, p) => setMsgs((s) => s.map((m) => (m.id === id ? { ...m, ...p } : m)));
const sleep = async (ms) => { await agSleep(ms); if (!alive.current) throw 0; };
const ai = async (text, wait = 950) => {
const id = push({ kind: 'ai', typing: true });
await sleep(wait);
patch(id, { typing: false, text });
await sleep(260);
};
const tool = async (icon, name, detail, result, wait = 1400, mode = '') => {
const id = push({ kind: 'tool', icon, name, mode, detail, state: 'run' });
await sleep(wait);
patch(id, { state: 'done', result });
await sleep(320);
};
const opts = (items, meta = null) => new Promise((res) => {
const id = push({ kind: 'opts', items, meta });
pend.current = { id, res };
if (AG_AUTO) agSleep(1100).then(() => {
if (pend.current && pend.current.id === id)
pick(items.find((it) => it.auto) || items.find((it) => it.wide) || items[0]);
});
});
const multiOpts = (items, meta = null, max = 3) => new Promise((res) => {
const id = push({ kind: 'multi-opts', items, meta, selected: [], max });
pend.current = { id, res, items, selected: new Set(), max };
if (AG_AUTO) agSleep(900).then(() => {
const p = pend.current;
if (!p || p.id !== id) return;
p.selected = new Set(items.filter((it) => it.auto).map((it) => it.v));
patch(id, { selected: [...p.selected] });
agSleep(260).then(() => confirmMulti(id));
});
});
const pick = (it) => {
const p = pend.current; if (!p) return;
pend.current = null;
patch(p.id, { done: true });
push({ kind: 'user', text: it.bubble || it.label });
p.res(it);
};
const toggleMulti = (id, it) => {
const p = pend.current;
if (!p || p.id !== id) return;
if (p.selected.has(it.v)) p.selected.delete(it.v);
else if (p.selected.size < p.max) p.selected.add(it.v);
patch(id, { selected: [...p.selected] });
};
const confirmMulti = (id) => {
const p = pend.current;
if (!p || p.id !== id || !p.selected.size) return;
const chosen = p.items.filter((it) => p.selected.has(it.v));
pend.current = null;
patch(id, { done: true });
push({ kind: 'user', text: chosen.map((it) => it.short || it.label).join(' + ') });
p.res(chosen);
};
const submitLink = (id, value) => {
const p = pend.current;
if (!p || p.id !== id) return;
pend.current = null;
patch(id, { done: true, value });
push({ kind: 'user', text: '核验我找到的商品链接' });
p.res(value);
};
const linkInput = (tv) => new Promise((res) => {
const demoUrl = agPurchaseUrl(tv);
const id = push({ kind: 'link-input', demoUrl });
pend.current = { id, res };
if (AG_AUTO) agSleep(1100).then(() => submitLink(id, demoUrl));
});
const ovRun = (kind, data = null) => new Promise((res) => { ovRes.current = res; setOvData(data); setOv(kind); });
const ovDone = (payload = null) => {
setOv(null); setOvData(null);
const r = ovRes.current; ovRes.current = null;
if (r) r(payload);
};
/* 剧本路由:三个建议问题各走一条;其余(固定命令/自由输入)走主线 */
useEffect(() => {
const go = ask === AG_ASKS[1] ? runGlare : ask === AG_ASKS[2] ? runBurn : run;
go().catch(() => {});
}, []);
async function runMountFlow(tv) {
let selected = null;
let previewSaved = false;
let choosing = true;
await ai('支架我单独帮你选。先定安装方式和款式;立式再看升降与电动,最后选颜色。选好后还能直接放进你家客厅看看。');
while (choosing) {
const mode = await opts([
{ label: '立式', sub: '不打墙,款式更多', v: 'floor', accent: true, auto: true },
{ label: '壁挂', sub: '更省地面空间', v: 'wall' },
], { tool: 'recommend_tv_accessories', title: '支架怎么安装?' });
const styles = AG_MOUNT_STYLES[mode.v];
const style = await opts(styles.map((item, i) => ({
label: item.name, sub: item.desc, v: item.id, image: item.image, item, auto: i === 1,
})), { tool: 'recommend_tv_accessories', title: `选择${mode.v === 'floor' ? '立式' : '壁挂'}款式` });
let lift = { label: '固定安装', v: 'fixed' };
let power = { label: '非电动', v: 'none' };
if (mode.v === 'floor') {
lift = await opts([
{ label: '固定高度', sub: '结构最简单', v: 'fixed', auto: true },
{ label: '需要升降', sub: '沙发与餐桌视线都能兼顾', v: 'lift', accent: true },
], { tool: 'recommend_tv_accessories', title: '需要升降吗?' });
}
if (lift.v === 'lift' && style.v === 'mobile') {
power = await opts([
{ label: '手动调节', sub: '偶尔调一次即可', v: 'manual', auto: true },
{ label: '电动升降', sub: '遥控调高低', v: 'electric', accent: true },
], { tool: 'recommend_tv_accessories', title: '升降方式' });
} else if (lift.v === 'lift') {
power = { label: '手动调节', v: 'manual' };
}
const colorSet = style.v === 'easel'
? [
{ label: '胡桃木', v: 'walnut', hex: '#6B4430' },
{ label: '浅橡木', v: 'oak', hex: '#C49A68' },
{ label: '曜石黑', v: 'black', hex: '#24252A' },
]
: mode.v === 'floor'
? [
{ label: '曜石黑', v: 'black', hex: '#24252A' },
{ label: '云母灰', v: 'gray', hex: '#777A80' },
{ label: '奶油白', v: 'white', hex: '#E9E2D6' },
]
: [
{ label: '曜石黑', v: 'black', hex: '#24252A' },
{ label: '云母灰', v: 'gray', hex: '#777A80' },
{ label: '钛金色', v: 'titanium', hex: '#A59A88' },
];
const color = await opts(colorSet.map((item, i) => ({ ...item, swatch: item.hex, auto: i === 0 })), {
tool: 'recommend_tv_accessories', title: '选择颜色',
});
const wallAdjustment = style.v === 'tilt' ? '俯仰调节' : style.v === 'arm' ? '全向调节' : '固定安装';
selected = {
mode: mode.v,
modeLabel: mode.v === 'floor' ? '立式' : '壁挂',
style: style.item,
lift: mode.v === 'wall' ? wallAdjustment : lift.v === 'lift' ? '可升降' : '固定高度',
power: power.label,
color,
};
await tool('layers', 'recommend_tv_accessories', `${tv.size}″ · ${selected.modeLabel} · ${selected.style.name} · ${selected.lift} · ${selected.power}`, 'needs_confirmation · 尺寸范围通过,VESA 与承重待商品页核对', 1100, 'recommend');
push({ kind: 'mount-result', mount: selected, tv });
await sleep(650);
const next = await opts([
{ label: '放进我家看看', sub: '调用“在我家试电视”合成', v: 'preview', accent: true },
{ label: '换一个支架', v: 'again' },
{ label: '选这个,回到电视方案', v: 'done', auto: true },
], { tool: 'recommend_tv_accessories', title: '支架方案下一步' });
if (next.v === 'again') continue;
if (next.v === 'preview') {
await tool('sparkle', 'evaluate_room_and_size', `${tv.brand} ${tv.model} + ${selected.style.name} + ${selected.color.label}`, 'needs_input · 请确认客厅摆放效果', 850, 'room_preview');
const roomResult = await ovRun('room', { tv, mount: selected });
if (roomResult) {
previewSaved = true;
push({ kind: 'room-preview', tv, mount: selected });
await sleep(550);
await ai('这套电视和支架已经按客厅尺度合成并保存。');
} else {
await ai('合成没有保存,但刚才选的支架方案还在。');
}
const afterPreview = await opts([
{ label: previewSaved ? '回到电视方案' : '保留支架并返回', v: 'done', accent: true, auto: true },
{ label: '换一个支架', v: 'again' },
], { tool: 'recommend_tv_accessories', title: '完成支架选择' });
if (afterPreview.v === 'again') continue;
}
choosing = false;
}
return { mount: selected, previewSaved };
}
async function run() {
push({ kind: 'user', text: ask || AG_CMD });
await sleep(650);
await tool('database', 'research_products', '先了解尺寸、家庭使用方式和预算', 'needs_input · 还差 3 个关键信息', 1200, 'candidate_search');
await ai('没问题。要把客厅电视选准,我先确认三件事:多大、家里谁用和主要看什么、预算多少。如果尺寸拿不准,我也可以帮你量。');
const s = await opts([
{ label: '75″', sub: '视距 2.3 – 2.8 m', v: 75 },
{ label: '85″', sub: '视距 2.8 – 3.4 m', v: 85 },
{ label: '98″ 以上', sub: '视距 3.4 m+', v: 98 },
{ label: '不知道怎么选,帮我量一下', sub: '用手机测电视墙和观看距离', v: 'ar', accent: true, wide: true, auto: true, bubble: '不知道怎么选' },
], { tool: 'research_products', title: '1 / 3 · 先确定尺寸或测量空间' });
let size = s.v;
if (size === 'ar') {
await tool('cube', 'evaluate_room_and_size', '需要电视墙净宽和观看距离', 'needs_input · 请扫描电视墙', 850, 'room_preview');
await ai('尺寸拿不准很正常。请把镜头对准电视墙,慢慢扫一圈;我会一起看墙宽和观看距离。', 750);
await sleep(900);
await ovRun('ar');
push({ kind: 'ar' });
await sleep(750);
await ai('量好了:电视墙净宽 3.62 m,观看距离 2.9 m。85″ 最合适;98″ 虽然放得下,但坐得有点近。我先按 85″ 给你选。');
size = 85;
}
const usage = await multiOpts([
{ label: '电影 / 流媒体', short: '电影', sub: 'HDR、暗场、杜比与应用兼容', v: 'movie', auto: true },
{ label: '主机 / PC 游戏', short: '游戏', sub: '4K 高刷、VRR 与低延迟', v: 'game', auto: true },
{ label: '电视直播 / 体育综艺', short: '直播体育', sub: '换台快、运动清晰、长时间亮屏', v: 'live' },
{ label: '长辈经常使用', short: '长辈使用', sub: '开机直达、少广告、语音好用', v: 'senior' },
{ label: '儿童 / 全家共看', short: '儿童家庭', sub: '护眼、家长控制与防倾倒', v: 'family' },
{ label: '手机投屏 / K歌', short: '投屏娱乐', sub: '投屏稳定、麦克风与家庭娱乐', v: 'cast' },
], { tool: 'research_products', title: '2 / 3 · 主要怎么用(最多选 3 项)' });
const usageValues = usage.map((it) => it.v);
const usageLabel = usage.map((it) => it.short || it.label).join(' + ');
const usageSignals = [
usageValues.includes('movie') && '流媒体 · HDR 音画',
usageValues.includes('game') && '主机 / PC · 4K 高刷 · VRR',
usageValues.includes('live') && '直播源 · 开机直达 · 长时稳定',
usageValues.includes('senior') && '简洁系统 · 语音 · 少广告',
usageValues.includes('family') && '护眼 · 家长控制 · 防倾倒',
usageValues.includes('cast') && '投屏 · 麦克风兼容',
].filter(Boolean).join(' · ');
/* AG_ASKS[0]「预算 1.5 万」:预算已知,不再重复追问,按在库真实价位直接落档 */
const preBud = ask === AG_ASKS[0];
let b;
if (!preBud) {
b = await opts(AG_BUDGETS[size].map((it) => ({ ...it, auto: it.v === '85mid' })), { tool: 'research_products', title: '3 / 3 · 电视本体预算' });
} else if (size === 85) {
await ai('预算 1.5 万已经在你的消息里,我不重复问。它正好落在 85″ 甜点档。');
b = { label: '1 – 1.5 万', v: '85mid' };
} else if (size === 75) {
await ai('75″ 目前最贵的候选约 ¥9,749,1.5 万预算用不完。我会保留升级尺寸的余量,不为了花满而抬价。');
b = { label: '1 万左右', v: '75b2' };
} else {
await ai('98″ 目前约 ¥28,999 起,1.5 万预算不够。你可以回到 85″ 选更好的画质,也可以增加预算上百吋。');
const g = await opts([
{ label: '那就 85″ 里选最强', v: '85mid', accent: true, auto: true },
{ label: '加预算,上百吋', v: '98b1' },
], { tool: 'research_products', title: '硬条件冲突 · 请选择调整项', status: 'needs_confirmation' });
if (g.v === '85mid') { size = 85; b = { label: '1 – 1.5 万', v: '85mid' }; }
else b = { label: '3 万左右', v: '98b1' };
}
const P = agPickFor(b.v, usageValues);
await tool('database', 'research_products', `${size}″ · ${usageLabel} · ${b.label} · 读取实测与证据`, `已完成 · 58 台评测 → 命中 ${AG_HIT[size]} 台 → 2 台证据完整`, 1500, 'candidate_search');
await tool('bolt', 'plan_device_setup', usageSignals, 'ok · 端口、协议与内容兼容无硬伤', 1200, 'compatibility_check');
await tool('cube', 'plan_installation_and_delivery', `${size}″ 包装箱 × 电梯 × 入户门 × 支架安装`, 'ok · 入户可行,立式 / 壁挂均可选', 1350, 'precheck');
push({ kind: 'entry', size });
await sleep(650);
await tool('activity', 'compare_products', '候选 2 台 · 保留原始实测口径 · 当前场景权重', 'ok · 排序、优势、短板与妥协已生成', 1500, 'compare');
push({ kind: 'compare', tvs: [P.main, P.alt], scene: `${usageLabel} · ${size}″ · ${b.label}` });
await sleep(850);
await ai(`${P.say} 我把两台最值得看的关键差异列在下面,你可以切换“关键实测、场景适配、价格”看看。`);
const fb = await opts([
{ label: `选首选 · ${P.main.brand} ${P.main.model}`, v: 'main', accent: true, auto: true },
{ label: `选备选 · ${P.alt.brand} ${P.alt.model}`, v: 'alt' },
], { tool: 'compare_products', title: '根据对比结果确认候选', status: 'needs_confirmation' });
const selected = fb.v === 'alt' ? P.alt : P.main;
if (fb.v === 'alt') await ai(P.altSay, 700);
push({ kind: 'tv', tv: selected, tag: fb.v === 'alt' ? '你的选择 · 备选' : '你的选择 · 首选' });
await sleep(750);
const needsSoundbar = usageValues.includes('movie');
await tool('layers', 'recommend_tv_accessories', '根据电视、设备链、安装方式与已有配件分析缺口', `needs_confirmation · 1 required / ${needsSoundbar ? 1 : 0} recommended / ${needsSoundbar ? 2 : 3} not_needed`, 1200, 'needs_assessment');
push({ kind: 'accessory-needs', usage: usageValues });
await sleep(650);
await ai('我先把该买、可买和不用买的配件分开,避免你多花冤枉钱。');
const ac = await opts([
{ label: '继续推荐支架', v: 'y', accent: true, small: true, auto: true },
{ label: '支架以后再说', v: 'n', small: true },
], { tool: 'recommend_tv_accessories', title: '是否继续选支架?', status: 'needs_confirmation' });
let mountPlan = null;
let soundbarAdded = false;
if (ac.v === 'y') {
const mountResult = await runMountFlow(selected);
mountPlan = mountResult.mount;
await ai(`支架方案先收好。现在回到电视本体:你选的仍是 ${selected.brand} ${selected.model}。`);
push({ kind: 'return-tv', tv: selected, mount: mountPlan });
await sleep(650);
}
if (needsSoundbar) {
const sb = await opts([
{ label: '继续看回音壁', v: 'y', small: true },
{ label: '声音以后再说', v: 'n', small: true, auto: true },
], { tool: 'recommend_tv_accessories', title: '电影使用较多,是否继续看声音方案?' });
if (sb.v === 'y') {
await tool('broadcast', 'recommend_tv_accessories', '电影权重 · eARC · 杜比全景声', 'ok · 回音壁类别建议已生成', 900, 'recommend');
push({ kind: 'soundbar' });
soundbarAdded = true;
await sleep(550);
}
}
await ai('我先给你一个匹配当前型号的搜索入口。你也可以把自己看到的商品链接贴进来,我会核对型号、尺寸、版本、店铺和补贴资格。');
push({ kind: 'buy-link', tv: selected });
await sleep(650);
const vf = await opts([
{ label: '检查推荐入口', v: 'recommended', accent: true, auto: true },
{ label: '粘贴我的链接', v: 'custom' },
{ label: '暂时不用', v: 'skip' },
], { tool: 'verify_model_and_offer', title: '选择要检查的入口或链接', status: 'needs_confirmation' });
let linkChecked = false;
let linkSource = null;
if (vf.v !== 'skip') {
let source = '推荐搜索入口';
if (vf.v === 'custom') {
await tool('search', 'verify_model_and_offer', '需要商品详情页链接', 'needs_input · PurchaseLinkInput', 850, 'verify');
await linkInput(selected);
source = '你提供的链接';
}
const verifyResult = source === '你提供的链接'
? 'ok · 型号、尺寸、版本与销售条件一致'
: 'ok · 推荐入口可用,具体店铺与补贴需进入商品页确认';
await tool('search', 'verify_model_and_offer', `${source} · ${selected.brand} ${selected.model} · 上海地区`, verifyResult, 1400, 'verify');
push({ kind: 'offer', tv: selected, source });
linkSource = source;
linkChecked = true;
await sleep(650);
}
await ai(linkChecked
? linkSource === '你提供的链接'
? '你贴的商品链接与推荐机型一致,可以按核验结果继续;下单当天我建议再看一次最终价格和补贴。'
: '推荐搜索入口已经按目标型号和尺寸匹配好;进入具体商品页后,再核对店铺、版本和当地补贴。'
: '机型和配件方案已经整理好。你准备下单时,把商品链接发给我,我再帮你核验一次。');
push({ kind: 'end', chips: [`${size}″`, b.label, `${selected.brand} ${selected.model}`, '对比已完成', mountPlan && `${mountPlan.style.name}支架`, soundbarAdded && '回音壁方案', linkChecked && (linkSource === '你提供的链接' ? '购买链接已核验 ✓' : '推荐入口已匹配')].filter(Boolean) });
}
/* 剧本「反光」(AG_ASKS[1]):后台「光环境适配」引擎 + 前台「采光检测」小工具回传。
口径:全反射率(done) × 窗口亮度曲线(done·1%–100% 分档) × 满屏亮度(done) × 朝向反光风险规则(ext)。
结论逻辑:亮厅 ≠ 排除 OLED —— 混合画面下窗口高光 + 超低反射占优;满屏高亮内容才是 MiniLED 主场。 */
async function runGlare() {
push({ kind: 'user', text: ask });
await sleep(650);
await tool('sun', 'evaluate_room_and_size', '需要确认照度区间、直射方向和最差观看时段', 'needs_input · AmbientLightCapture', 1100, 'ambient_light');
await ai('怕反光我会分开看:全反射率决定倒影有多重,窗口和满屏亮度决定画面能不能压住环境光。缺少反射率实测的机型我会直接标“未测”,不拿亮度冒充抗反。先摸清你家多亮:白天阳光会直射到电视墙吗?');
const L = await opts([
{ label: '会,西晒 / 落地窗直射', v: 'hard' },
{ label: '挺亮,但晒不到屏幕', v: 'soft' },
{ label: '说不准,帮我测一下', sub: '拍两张照片测反光和照度', v: 'lux', accent: true, wide: true, auto: true, bubble: '说不准,帮我测一下' },
], { tool: 'evaluate_room_and_size', title: '采集客厅光环境', status: 'needs_input' });
let lvl = L.v;
if (lvl === 'lux') {
await ai('说不准没关系。请分别对着电视墙和窗户拍一张,我帮你估照度和直射时段。', 750);
await sleep(900);
await ovRun('lux');
push({ kind: 'lux' });
await sleep(750);
await ai('测好了:午后峰值约 1400 lux,西晒直射约 2 小时,属于强光客厅。我按最严格的抗反和亮度标准筛。');
lvl = 'hard';
} else {
await ai(lvl === 'hard'
? '西晒直射是最难伺候的光,我按 1500 lux 高光害标准筛。'
: '没有直射就好办,按 600–800 lux 明亮客厅的标准筛。', 800);
}
await tool('sun', 'evaluate_room_and_size', '照度区间 × 直射方向 × 反光源', 'ok · 抗反需求高 · 最差观看时段 15:00–17:00', 1200, 'ambient_light');
await ai('亮厅不等于不能买 OLED。LG G5 在本地报告的 HDR 影院模式峰值是 2484 nit,全反射率 0.816%,看电影、剧和游戏这类混合画面很有优势;但如果经常看满屏明亮的体育和综艺,还要重点看 100% 窗口实测。');
await ai('所以真正的分水岭是内容:满屏高亮画面看得多不多。白天开电视,你们主要看什么?', 850);
const R = await opts([
{ label: '电影 / 剧 / 游戏为主', sub: '混合画面 · 高光都在窗口档', v: 'oled', auto: true },
{ label: '体育·综艺·新闻开一天', sub: '满屏亮画面多 · 吃持续输出', v: 'mled' },
], { tool: 'research_products', title: '补充内容类型,决定亮度路线' });
let P, bud;
if (R.v === 'oled') {
P = AG_GLARE.oled; bud = '2 万档';
} else {
await ai('满屏亮画面更需要看 100% 窗口,而不是只看宣传峰值。预算大概什么范围?', 850);
const b = await opts([
{ label: '7 – 8 千', v: 'g07' },
{ label: '1 万左右', v: 'g1' },
{ label: '1.5 万左右', v: 'g15', auto: true },
{ label: '3 万左右,上百吋', v: 'gbig' },
], { tool: 'research_products', title: '电视本体预算' });
P = AG_GLARE[b.v]; bud = b.label;
}
await tool('database', 'research_products', `${R.v === 'oled' ? '已测反射率 + HDR 峰值' : '100% 窗口 + 抗反证据状态'} · ${bud}`, 'ok · 候选实测与缺失项均已标注', 1350, 'candidate_search');
await tool('activity', 'compare_products', '全反射率 · HDR 峰值 / 100% 窗口 · 尺寸 · 价格', 'ok · 2 台按原始实测口径对比', 1450, 'compare');
push({ kind: 'compare', tvs: [P.main, P.alt], scene: `强光客厅 · ${R.v === 'oled' ? '混合画面' : '满屏亮画面'}` });
await sleep(800);
await ai(`${P.say} 下面把两台的关键实测和价格放在一起,你可以直接比较。`);
const fb = await opts([
{ label: '很合适,就它了', v: 'ok', accent: true, auto: true },
{ label: '再看看备选', v: 'alt' },
], { tool: 'compare_products', title: '确认当前路线', status: 'needs_confirmation' });
const chosen = fb.v === 'alt' ? P.alt : P.main;
if (fb.v === 'alt') {
await ai(P.altSay, 700);
}
push({ kind: 'tv', tv: chosen, tag: fb.v === 'alt' ? '你的选择 · 备选' : '你的选择 · 首选' });
await sleep(650);
await ai('到货后告诉我电视型号和连接设备,我再按你家的光环境把亮度、HDR 和护眼设置调好。');
push({ kind: 'end', chips: [`${chosen.size}″`, bud, `${chosen.brand} ${chosen.model}`, chosen.refl != null ? '反射率已实测 ✓' : '反射率待补测', L.v === 'lux' && '照度已实测 ✓', '对比已完成'].filter(Boolean) });
}
/* 剧本「烧屏」(AG_ASKS[2]):evaluate_lifecycle_options 评估长期风险 → compare_products 对比 OLED / MiniLED。
耐久库口径照实:质保条款已入库,烧屏/衰减实测 tier=todo(进行中)。 */
async function runBurn() {
push({ kind: 'user', text: ask });
await sleep(650);
await tool('gear', 'evaluate_lifecycle_options', '检索烧屏机制、防护能力、质保条款与使用风险', 'needs_input · UsageMixSelector', 1300, 'reliability_risk');
await ai('直说结论:OLED 确实存在烧屏风险,但风险主要取决于使用习惯。台标、比分牌、游戏血条这类固定画面停留越久,风险越高;正常混着看,并保持像素位移和补偿刷新开启,风险会低很多。');
await ai('风险大小主要看用法——你家电视平时开着干嘛?', 800);
const u = await opts([
{ label: '剧 / 电影为主,内容很杂', v: 'mix', auto: true },
{ label: '新闻·体育台,一挂一整天', v: 'logo' },
{ label: '主机游戏比较多', v: 'game' },
], { tool: 'evaluate_lifecycle_options', title: '长期风险取决于固定画面与连续时长' });
await tool('activity', 'evaluate_lifecycle_options', '使用结构 × ABL 行为 × 质保条款 × 耐久证据', 'partial · 条款已入库,长期衰减实测进行中', 1500, 'reliability_risk');
const RISK = {
mix: ['低风险用法 ✓', '你的内容一直在换,属于相对低风险的用法。OLED 可以优先考虑,但不要关闭系统自带的像素刷新。'],
logo: ['高风险用法 ⚠', '固定台标 + 长时间开机是烧屏最高危组合,我不建议上 OLED——MiniLED 是背光方案,物理上不存在烧屏,这个用法选它更省心。'],
game: ['中风险用法', '游戏属于中风险:血条和小地图位置固定,但 OLED 的响应速度确实更有优势。保持像素位移开启,并避免固定界面长时间连续显示,风险会更可控。'],
}[u.v];
await ai(RISK[1]);
await ai('要不要我把两条路线各拉一台实测对比?看完你心里就有数了。', 800);
const c = await opts([
{ label: '对比给我看', v: 'y', accent: true, auto: true },
{ label: '不用,我了解了', v: 'n' },
]);
if (c.v === 'y') {
await tool('database', 'research_products', 'OLED 与 MiniLED 各建立一个有完整证据的候选', 'ok · OLED 5 台 / MiniLED 30+ 台', 1200, 'candidate_search');
await tool('activity', 'compare_products', '黑位 · HDR 峰值 · 响应 · 反射率 · 长期风险', 'ok · 2 台保留各自报告模式', 1400, 'compare');
const burnTvs = u.v === 'logo' ? [AG_BURN.mled, AG_BURN.oled] : [AG_BURN.oled, AG_BURN.mled];
push({ kind: 'compare', tvs: burnTvs, scene: `${RISK[0]} · OLED vs MiniLED` });
await sleep(800);
await ai('一句话总结:固定台标开一整天选 MiniLED;内容混着看、追求暗场和响应,可以选 OLED 并保留像素刷新。完整指标已经列在下面。');
push({ kind: 'end', chips: ['烧屏答疑 ✓', RISK[0], 'OLED vs MiniLED', '实测对比 2 台'] });
} else {
await ai('行。送你三条防烧屏习惯:① 像素刷新保持开启;② 亮度别常年拉满;③ 长时间暂停记得息屏。真到要买的时候回来找我,我帮你核对具体机型的烧屏质保条款。');
push({ kind: 'end', chips: ['烧屏答疑 ✓', RISK[0], '防烧屏习惯 ×3'] });
}
}
return (
AI 选购助手 已接入 {DB_STATS.inStock} 台电视评测库
{msgs.map((m) => {
switch (m.kind) {
case 'user': return
{m.text}
;
case 'ai': return (
{m.typing
?
: m.text}
);
case 'tool': return (
{agToolLabel(m.name)}
{agResultLabel(m.state === 'done' ? m.result : m.detail)}
{m.state === 'run'
?
:
}
);
case 'opts': return m.done ? null : (
{m.meta && <>
{agToolLabel(m.meta.tool)}
{m.meta.title}
>}
{m.items.map((it, i) => (
pick(it)}>
{it.image && }
{it.label} {it.sub && {it.sub} }
{it.swatch && }
))}
);
case 'multi-opts': return m.done ? null : (
{m.meta && <>
{agToolLabel(m.meta.tool)}
{m.meta.title}
>}
{m.items.map((it) => {
const selected = m.selected.includes(it.v);
const maxed = m.selected.length >= m.max && !selected;
return (
toggleMulti(m.id, it)}
>
{it.label}
{it.sub && {it.sub} }
);
})}
confirmMulti(m.id)}>
{m.selected.length ? `选好了 · 已选 ${m.selected.length} 项` : '请至少选择 1 项'}
);
case 'ar': return
;
case 'lux': return
;
case 'entry': return
;
case 'tv': return
;
case 'trade': return
;
case 'compare': return
;
case 'accessory-needs': return
;
case 'mount-result': return
;
case 'soundbar': return
;
case 'room-preview': return
;
case 'return-tv': return
;
case 'buy-link': return
;
case 'link-input': return m.done ? null :
submitLink(m.id, value)} />;
case 'offer': return ;
case 'end': return ;
default: return null;
}
})}
{msgs.some((m) => m.kind === 'link-input' && !m.done) ? '请粘贴商品链接继续' : msgs.some((m) => m.kind === 'opts' && !m.done) ? '请选择一个选项继续' : msgs.some((m) => m.kind === 'end') ? '方案已整理好 · 可以重新选择或返回主页' : '正在为你整理…'}
{ov === 'ar' &&
}
{ov === 'lux' &&
}
{ov === 'room' &&
ovDone(null)}
onComplete={ovDone}
initialTv={ovData && ovData.tv}
initialMount={ovData && ovData.mount && ovData.mount.mode}
standSelection={ovData && ovData.mount}
initialPhase="place"
/>
}
);
}
/* 外壳:epoch 换 key 实现「重新演示」整段重跑 */
function AgentChat(props) {
const { useState } = React;
const [ep, setEp] = useState(0);
return setEp((e) => e + 1)} />;
}
Object.assign(window, { AgentChat, AG_ASKS });