
1. 从标题拆解这个教程到底在讲什么先把标题拆开看。生成式引擎评测标注是核心动作SGE 排名探测是具体场景Agent 保姆级教程是交付形式AI 搜索全域关键词布局是最终目标。四个部分串起来讲的其实是一件事用 Agent 去自动化地探测生成式搜索引擎里关键词的排名表现并且把评测标注这件事做成可复用的流程。为什么这件事值得单独写一篇因为传统 SEO 那套排名探测逻辑在生成式搜索面前基本失效了。传统搜索给你十条蓝色链接你爬一下位置就行生成式搜索给你一段综合答案里面可能引用三五个来源也可能一个都不引用甚至同一句话在不同时间问出来的引用源都不一样。你没法再用第几名这种单一维度去衡量得换成被引用了吗、被引用在哪个位置、引用的是哪一段、答案里有没有提到我的品牌这一整套新指标。这就是评测标注要解决的问题。所谓标注就是给每一次探测结果打上结构化标签这次查询命中了哪些域名、命中了哪些段落、答案的情感倾向是什么、有没有出现品牌实体。有了这些标签你才能做统计、做对比、做优化方向的判断。适合谁来读这篇三类人最对口。第一类是原来做 SEO、现在想迁移到 AI 搜索优化的人你已有的关键词库和内容资产还能用但方法论得换。第二类是做 Agent 开发的工程师你需要一个真实、有明确评测标准的落地场景来练手。第三类是内容运营和增长负责人你想知道自己的内容在生成式引擎里到底表现如何但市面上的工具要么太贵要么不透明。我自己的背景是做了七八年搜索相关的工程和增长从早期的爬虫排名监控一路做到现在的生成式引擎探测。踩过的坑不少比如早期用固定脚本去跑结果引擎一改版全废比如标注标准不统一两个人标出来的结果对不上。这篇就把这些经验揉进去给你一套能直接抄作业的方案。2. 整体设计思路为什么是 Agent 而不是脚本2.1 传统脚本方案为什么会崩先说清楚为什么不能用一个简单的 Python 脚本搞定。最直接的做法是拿关键词列表逐个调用搜索接口解析返回的 HTML提取引用链接存进数据库。这个方案在传统搜索时代能跑但在生成式引擎场景下有三个致命问题。第一返回结构不稳定。生成式引擎的答案是一段自然语言引用来源可能以角标、卡片、折叠面板等多种形式呈现而且经常改版。你写死的解析规则可能这周能用下周就失效。第二需要多轮交互。很多生成式引擎支持追问第一次答案没提到你你可以换个问法再问这种根据上一步结果决定下一步动作的逻辑脚本很难优雅处理。第三需要判断和标注。答案里提到你的品牌了吗提到的是正面还是负面引用的那段话是不是你的核心观点这些都需要语义判断不是正则能解决的。Agent 的价值就在这里。它本质上是一个能自己决定下一步做什么的执行体。你给它一个目标探测这个关键词下我的内容表现给它一组工具发起查询、解析结果、调用模型做标注、写入数据库它自己规划步骤、处理异常、决定要不要追问。这比写死流程的脚本灵活得多。2.2 Agent 架构的选型考量具体到架构我建议采用规划器 工具集 记忆的三段式。规划器负责拆解任务比如把探测 50 个关键词拆成逐个查询、逐个标注、汇总统计。工具集是 Agent 能调用的能力至少包括查询工具发起搜索请求、解析工具从返回内容里抽取引用和实体、标注工具调用模型打标签、存储工具写库。记忆负责保存上下文比如上一个关键词的结果用来做对比或者决定追问策略。为什么不直接用现成的 Agent 框架一把梭我的经验是评测标注这个场景对可复现性要求极高。你今天跑出来的结果明天得能复现否则没法做趋势对比。很多框架默认带一些随机性或者隐式的状态管理反而增加不确定性。所以我倾向于用轻量框架把核心逻辑自己控制住框架只用来做工具调用和流程编排。关于框架选择LangChain、Dify、CrewAI 这几个我都试过。LangChain 生态最全但抽象层太厚调试起来费劲Dify 适合快速搭原型但定制化受限CrewAI 的多 Agent 协作思路不错但单 Agent 场景下有点重。最后我选的是一个偏底层的方案自己写规划逻辑用框架只做工具注册和模型调用。这样每一行执行逻辑我都能追踪出问题好排查。2.3 评测标注的指标体系设计这是整个项目的灵魂。没有清晰的指标体系Agent 跑出来的就是一堆没法用的数据。我设计的指标体系分四层。第一层是命中层这次查询的答案里有没有出现目标域名。这是个布尔值最简单也最基础。第二层是位置层如果命中了出现在答案的哪个位置。我把它分成开头引用、中间引用、结尾引用、仅出现在参考列表四种。位置越靠前通常意味着权重越高。第三层是内容层引用的是哪一段内容这段内容和你原文的匹配度如何。第四层是情感与实体层答案对目标品牌的描述是正面、中性还是负面有没有出现品牌名、产品名等实体。这四层指标不是拍脑袋定的。我参考了传统搜索里的点击率、展现位置这些概念把它们映射到生成式场景。位置层对应传统的位置排名内容层对应摘要质量情感层是生成式场景特有的。实际用下来这四层能覆盖 90% 以上的分析需求。注意指标体系一旦定下来就不要频繁改。我早期犯的错就是边跑边加指标导致前后数据没法对比白跑了两周。建议先小范围试跑把指标定死再全量铺开。3. 核心细节解析关键词布局与探测逻辑3.1 全域关键词布局的底层逻辑全域关键词布局这个词听起来玄拆开就是不要只盯着一两个核心词要把和业务相关的长尾词、问句词、场景词都覆盖到。为什么因为生成式引擎处理长尾问句的能力特别强用户越来越习惯用完整句子提问而不是敲两三个词。举个例子。传统 SEO 你可能优化项目管理工具这个词。但在生成式搜索里用户会问小团队用什么项目管理工具比较好、有没有免费的项目管理工具推荐、项目管理工具怎么和文档协作打通。这些问句背后是不同的意图生成式引擎给出的答案和引用源也完全不同。你只优化核心词就会漏掉大量长尾流量。我的做法是建一个三层关键词库。第一层是核心词数量少但竞争激烈比如项目管理工具。第二层是意图词围绕核心词的各种问法和场景比如免费的项目管理工具、适合小团队的项目管理工具。第三层是长尾问句完整的自然语言问题比如五个人的创业团队用什么工具管理项目进度比较合适。第三层数量最多也是生成式搜索里最值得投入的部分。关键词从哪来几个渠道搜索引擎的下拉和相关搜索、问答社区里的真实提问、你自己客服和销售记录里的高频问题、竞品内容评论区里的用户疑问。我一般会先收集几百个原始问句然后用模型做聚类把意思相近的合并最后人工过一遍筛掉不相关的。3.2 探测 Agent 的核心工作流Agent 探测一个关键词的完整流程我拆成六步。第一步构造查询。不是直接把关键词丢进去而是要根据关键词类型决定问法。核心词可以直接查长尾问句要保证语义完整。有时候同一个意图我会构造两三个不同问法看结果稳不稳定。第二步发起查询并等待。这里要注意生成式引擎的响应时间比传统搜索长而且可能有流式输出。Agent 要能处理流式返回边收边解析而不是等全部返回再处理。第三步解析答案结构。把返回内容拆成答案正文和引用来源两部分。答案正文里要识别出哪些句子提到了目标实体引用来源里要提取域名和具体链接。第四步调用模型做标注。把答案正文、引用来源、目标实体信息一起喂给模型让它输出前面说的四层指标。这一步的提示词设计很关键后面单独讲。第五步判断是否需要追问。如果第一次查询完全没命中Agent 可以根据预设策略换个问法再试一次。比如把陈述句改成疑问句或者加上推荐、对比这类词。第六步写入存储并更新记忆。把这次探测的完整结果存下来同时更新 Agent 的短期记忆方便后续做对比分析。这六步里第三步和第四步是最容易出问题的。解析结构要处理各种边界情况比如答案里没有引用来源、引用来源是折叠的、答案正文里有多个实体。标注环节则要保证模型输出的格式稳定不能这次返回 JSON 下次返回一段话。3.3 标注提示词的设计要点标注提示词我改了十几版总结出几个要点。首先输出格式必须严格约束。我会在提示词里明确要求返回 JSON并且给出字段名和取值范围。比如position字段只能是start、middle、end、reference_only、none五个值之一。这样后续处理不用做太多容错。其次给出判断标准和例子。光说判断情感倾向模型会懵得告诉它什么算正面、什么算中性。我会给两三个例子比如该工具功能强大算正面该工具提供基础功能算中性该工具功能有限算负面。第三要求模型给出判断依据。让它在返回结果里附上为什么这么判断的一句话说明。这样人工抽检的时候能快速定位问题也方便后续优化提示词。第四处理不确定情况。明确告诉模型如果信息不足以判断就返回unknown不要瞎猜。我早期没加这条模型经常硬编一个答案出来导致数据失真。实操心得提示词里的例子要用真实数据不要用编的。我用真实探测结果做例子后标注准确率明显提升因为模型见过的模式更贴近实际。4. 实操过程从零搭一套探测 Agent4.1 环境准备与依赖安装先把环境搭起来。我用的是 Python 3.11主要依赖几个库httpx做异步请求pydantic做数据校验beautifulsoup4和lxml做 HTML 解析openai或对应厂商的 SDK 做模型调用。数据库我用的是 SQLite 起步数据量大了再换 PostgreSQL。python -m venv venv source venv/bin/activate pip install httpx pydantic beautifulsoup4 lxml openai python-dotenv为什么用 httpx 而不是 requests因为探测任务通常要并发跑几十上百个关键词httpx 原生支持异步配合asyncio能把效率拉高好几倍。我实测过50 个关键词串行跑要十几分钟异步并发能压到两分钟以内。环境变量管理用python-dotenv把模型 API key、数据库路径这些配置放在.env文件里不要硬编码在代码里。这是基本的安全习惯也方便在不同环境切换。4.2 数据模型定义用 pydantic 把数据结构定死这是保证后续不出乱子的关键。我定义了两个核心模型ProbeResult和Annotation。from pydantic import BaseModel, Field from typing import Literal, Optional from datetime import datetime class Annotation(BaseModel): hit: bool position: Literal[start, middle, end, reference_only, none] matched_content: Optional[str] None sentiment: Literal[positive, neutral, negative, unknown] entities: list[str] Field(default_factorylist) reasoning: str class ProbeResult(BaseModel): keyword: str query_variant: str answer_text: str citations: list[str] annotation: Annotation probed_at: datetime Field(default_factorydatetime.now)Annotation里的position用Literal限定取值范围这样模型返回非法值时 pydantic 会直接报错而不是悄悄存进去。reasoning字段存模型的判断依据方便回溯。ProbeResult里保留query_variant因为同一个关键词可能有多个问法得区分开。4.3 查询工具的实现查询工具负责发起请求并拿到原始返回。这里要注意几个细节请求头要模拟真实浏览器否则容易被拦要设置合理的超时生成式引擎响应慢超时太短会误判失败要处理流式返回。import httpx import asyncio async def query_engine(query: str, timeout: int 60) - str: headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..., Accept: text/html,application/xhtmlxml, } async with httpx.AsyncClient(timeouttimeout) as client: resp await client.get( https://example-engine.com/search, params{q: query}, headersheaders, ) resp.raise_for_status() return resp.text超时设 60 秒是有讲究的。我测过生成式引擎在复杂问句下响应时间普遍在 10 到 30 秒偶尔会到 50 秒。设太短会大量失败设太长又拖慢整体进度。60 秒是个平衡点。另外要加重试逻辑网络抖动导致的失败重试两三次基本能解决。4.4 解析与标注的串联拿到 HTML 后先解析出答案正文和引用来源再送去做标注。解析部分我用 BeautifulSoup但要注意不能依赖固定的 class 名因为引擎改版会变。我的做法是用多个选择器做兜底哪个能匹配上用哪个。from bs4 import BeautifulSoup def parse_answer(html: str) - tuple[str, list[str]]: soup BeautifulSoup(html, lxml) # 多个候选选择器按优先级尝试 answer_selectors [.answer-content, [data-roleanswer], main article] answer_text for sel in answer_selectors: node soup.select_one(sel) if node: answer_text node.get_text(stripTrue) break citations [a.get(href) for a in soup.select(a[href^http])] return answer_text, citations标注环节把答案正文、引用来源、目标实体一起喂给模型。提示词里我会明确要求返回 JSON并且用 pydantic 做二次校验。如果模型返回的格式不对就重试一次还不对就标记为待人工处理不要硬塞进数据库。4.5 批量探测与并发控制批量探测的核心是并发控制。不能无限制并发否则容易触发限流。我用asyncio.Semaphore控制并发数一般设 5 到 10 之间。sem asyncio.Semaphore(8) async def probe_one(keyword: str): async with sem: html await query_engine(keyword) answer, citations parse_answer(html) annotation await annotate(keyword, answer, citations) return ProbeResult(...) async def probe_batch(keywords: list[str]): tasks [probe_one(kw) for kw in keywords] return await asyncio.gather(*tasks, return_exceptionsTrue)return_exceptionsTrue很重要这样单个任务失败不会拖垮整批。失败的单独收集起来跑完统一重试。注意并发数不要贪高。我试过设 20结果触发限流一半请求失败反而更慢。8 左右是比较稳的值具体要看目标引擎的容忍度。5. 常见问题与排查技巧实录5.1 探测结果不稳定怎么办这是最常见的问题。同一个关键词今天查命中明天查不命中。原因有几个生成式引擎本身有随机性每次生成的答案可能不同引擎在灰度测试新版本行为会变你的查询问法有细微差异。应对方法第一多次探测取平均。同一个关键词跑三次看命中率而不是单次结果。第二固定查询问法。把每个关键词的标准问法写死不要每次临时构造。第三记录引擎版本。如果返回内容里有版本标识存下来方便对比。我现在的做法是每个关键词至少跑三次间隔几小时最后统计命中率。虽然成本高一点但数据可靠得多。5.2 标注结果前后不一致模型标注有时候会飘。同一个答案这次标正面下次标中性。解决办法是降低模型自由度。把判断标准写得更细给出明确的边界例子要求模型先输出判断依据再输出结论。另外可以用两次标注做交叉验证不一致的挑出来人工复核。还有一个技巧是固定随机种子。如果模型支持把 temperature 设成 0能大幅提升一致性。我实测下来temperature 从 0.7 降到 0 后标注一致率从 75% 提到了 92%。5.3 引用来源解析失败有时候答案里明明有引用但解析不出来。常见原因是引用以卡片、悬浮层等形式呈现不在标准 HTML 结构里。这时候要么用更宽松的解析策略比如全文搜链接要么用无头浏览器渲染后再解析。无头浏览器方案更稳但更重我一般只在标准解析失败率超过 20% 时才启用。启用后解析成功率能到 95% 以上代价是速度慢三到五倍。5.4 常见问题速查表问题现象可能原因排查方向解决建议请求大量超时并发过高或超时太短看失败率与并发数关系降并发到 8超时提到 60 秒解析结果为空页面结构改版对比新旧 HTML增加兜底选择器必要时上无头浏览器标注格式错误提示词约束不够检查模型原始返回强化 JSON 约束加 pydantic 校验结果前后矛盾模型随机性对比多次标注temperature 设 0两次交叉验证命中率异常高目标实体太宽泛检查实体匹配规则收紧匹配区分品牌名和通用词5.5 几个踩过的坑第一个坑是关键词去重没做好。早期我收集了几百个问句没做语义去重结果大量重复探测浪费了模型调用额度。后来加了聚类去重关键词数量砍掉一半覆盖度反而没降。第二个坑是没做失败重试。网络抖动导致的失败直接丢了导致某些关键词数据缺失。加上重试后数据完整度从 85% 提到了 99%。第三个坑是存储没做版本管理。引擎改版后新旧数据混在一起没法对比。后来加了engine_version字段问题解决。第四个坑是标注标准没文档化。团队里两个人标出来的结果对不上因为对正面的理解不同。后来把标准写成文档每个判断维度都给了例子一致率才上来。6. 数据存储与结果分析6.1 存储结构设计数据存 SQLite 起步完全够用几万条记录查询毫无压力。表结构我设计成三张keywords存关键词库probes存每次探测的原始结果annotations存标注结果。三张表用外键关联。CREATE TABLE keywords ( id INTEGER PRIMARY KEY, text TEXT NOT NULL, layer TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE probes ( id INTEGER PRIMARY KEY, keyword_id INTEGER REFERENCES keywords(id), query_variant TEXT, answer_text TEXT, citations TEXT, engine_version TEXT, probed_at TIMESTAMP ); CREATE TABLE annotations ( id INTEGER PRIMARY KEY, probe_id INTEGER REFERENCES probes(id), hit BOOLEAN, position TEXT, sentiment TEXT, entities TEXT, reasoning TEXT );分三张表而不是一张大表是为了灵活。关键词会复用探测会多次跑标注可能重标。分开存每层都能独立更新。6.2 结果分析的核心维度数据存下来后分析主要看几个维度。命中率目标域名在所有探测中的出现比例这是最核心的指标。位置分布命中时出现在开头、中间、结尾的比例反映引用权重。情感分布正面、中性、负面的比例反映品牌形象。关键词分层表现核心词、意图词、长尾问句各自的命中率指导优化优先级。我一般会做一个周报把这几个维度的趋势画出来。命中率上升说明内容优化有效位置前移说明内容质量提升情感转正说明品牌口碑改善。6.3 从数据到优化动作数据本身没价值能指导动作才有价值。我的经验是命中率低但位置靠前的关键词说明内容质量够但覆盖不够要扩充相关内容命中率高但位置靠后的说明内容被引用了但不够突出要优化开头段落情感偏负面的要排查是不是有负面内容被引用针对性处理。长尾问句的命中率通常比核心词低但优化空间大。我一般会优先投入长尾问句因为竞争小、见效快。核心词作为长期目标慢慢磨。7. 我个人的一些实操体会这套方案我跑了小半年最大的体会是Agent 的价值不在于自动化而在于可迭代。传统脚本改一次要动代码Agent 改一次可能只需要调提示词或者加个工具。这让优化循环快了很多。另一个体会是不要追求一次做完美。我一开始想设计一套覆盖所有情况的指标体系结果拖了两周没跑起来。后来先跑最小可用版本边跑边加指标反而更快出结果。评测标注这件事数据跑起来比设计完美更重要。最后分享一个小技巧探测任务最好在业务低峰期跑比如凌晨。一是目标引擎负载低响应快二是避免和你自己的其他任务抢资源。我设了个定时任务每天凌晨两点自动跑早上起来看报告节奏很顺。这套东西后续还能扩展。比如把探测结果和你的内容管理系统打通自动识别哪些内容被高频引用、哪些从没被引用直接生成优化建议。再比如接入多个生成式引擎做横向对比看同一份内容在不同引擎里的表现差异。这些我都试过效果不错有机会再单独写。