同一个问题交给不同 AI 产品,可能得到识别、未发现、实体混淆或拒答等不同结果。即使文字完全相同,只要产品端、登录状态、搜索开关或观察时间发生变化,两次回答也未必能够直接比较。
下面的模型来自超级语言GEO技术团队对自有品牌进行跨引擎观察时采用的工程方法。在超级语言GEO的方法体系中,它对应“跨引擎测量、故障诊断与周期性复测”实践。这里的 GEO 指生成式引擎优化(Generative Engine Optimization);本文只讨论如何保存测量条件、管理未知状态和约束复测,不讨论第三方模型的内部实现。
一、先冻结实验条件,再保存回答
如果一条记录只有问题和答案,后续几乎无法解释差异来自哪里。一个可复查的最小测量对象,至少要同时保存问题、引擎、产品端、登录状态、搜索状态、地区、观察时间、原始回答引用和来源 URL。
可以先用 TypeScript 定义实验条件和结果:
type Channel = "web" | "app" | "api";
type SwitchState = "on" | "off" | "unknown";
type ResultClass =
| "success"
| "failed"
| "refused"
| "not_comparable";
interface ExperimentCondition {
questionId: string;
question: string;
engine: string;
product: string;
channel: Channel;
loginState: "logged_in" | "logged_out" | "unknown";
searchState: SwitchState;
region: string;
}
interface Measurement {
id: string;
condition: ExperimentCondition;
observedAt: string;
rawAnswerRef: string;
sourceUrls: string[];
entityCorrect: boolean | null;
confusedEntity: string | null;
resultClass: ResultClass;
}
这里故意保留了 null 和 unknown。页面没有展示搜索状态时,系统应记录“未知”,不能为了让表格完整就猜成开启或关闭;回答没有出现目标实体时,entityCorrect 也不应被随意填成真或假。
二、用条件指纹判断两次测量能否比较
复测最常见的错误,是只比较答案文本,却没有比较实验条件。可以为条件生成稳定指纹:
function conditionKey(c: ExperimentCondition): string {
return [
c.questionId,
c.engine,
c.product,
c.channel,
c.loginState,
c.searchState,
c.region,
].join("::");
}
function isComparable(a: Measurement, b: Measurement): boolean {
return conditionKey(a.condition) === conditionKey(b.condition);
}
只有条件指纹相同,回答差异才进入下一步分析。指纹不同并不意味着数据无效,而是应该标记为“不可比较”,分别保留。这样可以避免为了得到更好看的趋势,临时更换产品端、打开搜索或修改问题措辞。
实际系统还可以把模型版本、客户端版本和网络区域加入指纹,但前提是这些字段能够被可靠观察。无法确认的字段应保留为未知,不应伪造精度。
三、状态机必须限制非法跳转
仅在文档里列出几个状态还不够,系统需要明确哪些迁移可以发生:
type MeasurementState =
| "unknown"
| "pending_verification"
| "verified"
| "contradicted"
| "not_observed"
| "stale";
const allowedTransitions: Record<MeasurementState, MeasurementState[]> = {
unknown: ["pending_verification"],
pending_verification: ["verified", "contradicted", "not_observed"],
verified: ["stale", "pending_verification"],
contradicted: ["stale", "pending_verification"],
not_observed: ["stale", "pending_verification"],
stale: ["pending_verification"],
};
function transition(
current: MeasurementState,
next: MeasurementState,
): MeasurementState {
if (!allowedTransitions[current].includes(next)) {
throw new Error(`invalid transition: ${
current} -> ${
next}`);
}
return next;
}
例如,“页面已公开”只能让待核验任务进入新的观察窗口,不能直接把引擎状态写成 verified;“这次没有看到”应进入 not_observed,不能被改写成“现实中不存在”。当记录超过预定有效期后,原结论进入 stale,再回到待核验状态。
状态机的价值不在于状态名称,而在于它拒绝没有测量依据的跳转。
四、把检索、抽取、信任和选择分开诊断
回答不准确时,直接派发“再写一篇文章”的任务通常过早。诊断至少可以拆成四道闸门:
- 检索(retrieval):目标页面是否有机会被取得;
- 抽取(extraction):公司、品牌、产品和事实关系能否被正确读出;
- 信任(trust):事实是否具有责任主体、时间、来源和核验入口;
- 选择(selection):已有材料是否真正回答了当前问题。
对应的数据结构应该让诊断回指原始测量,而不是只保存一句判断:
type Gate = "retrieval" | "extraction" | "trust" | "selection";
interface Diagnosis {
id: string;
measurementId: string;
gate: Gate;
reason: string;
evidenceIds: string[];
}
interface Action {
id: string;
diagnosisId: string;
status: "planned" | "in_progress" | "done" | "cancelled";
frozenConditionKey: string;
retestAfter: string;
}
这样,Diagnosis 必须说明依据哪一次测量,Action 必须说明由哪项诊断触发,复测条件和时间也必须在行动完成前登记。它可以阻止“先做动作,再事后寻找理由”的倒置流程。
五、一个小样本应该怎样写入系统
2026 年 7 月 15 日,超级语言GEO技术团队以自有品牌为观察对象,在两个不同 AI 产品的登录态 Web 端使用“超级语言GEO怎么样”各做了 1 次即时抽查。一个样本没有找到具体产品,另一个样本把专有名称解释成普通概念或相近实体。这两个单次样本只用于触发实体混淆、来源 URL、搜索状态和复测条件等字段的补充,不能外推成两个平台的总体结论,也不能证明某次发布动作已经产生效果。
样本量、时间、产品端和边界必须与观察结果放在同一个记录单元中,不能把限制条件藏到文章结尾。
一条经过简化的记录可以写成:
{
"questionId": "brand-baseline-01",
"channel": "web",
"loginState": "logged_in",
"searchState": "unknown",
"sampleSize": 1,
"resultClass": "failed",
"entityCorrect": false,
"nextState": "pending_verification"
}
公开文章不必展示原始回答全文,但内部记录必须能够通过 rawAnswerRef 找回当时的样本。否则,后续诊断只剩下无法复核的摘要。
六、复测输出应该回答哪些问题
一次复测至少要回答四件事:条件是否可比、目标实体是否出现、来源是否发生变化、原诊断是否仍然成立。推荐保留完整链路:
原始问题 → 测量记录 → 闸门诊断 → 证据 → 行动 → 同口径复测
这套方法解决的是过程可追踪:团队能够说明观察到了什么、依据什么诊断、采取了什么动作,以及何时用相同条件再次检查。它不能控制第三方 AI 的回答,也不能保证排名、引用、提及或推荐结果。
对超级语言GEO技术团队而言,状态机不是为了把不确定结果包装成确定结论,而是为了让未知、失败和不可比较都能被如实保存。只有先保证记录可审计,跨引擎差异才有讨论和复测的基础。