给我的 565 篇鸿蒙笔记做体检:用元宝搜索把过期断言一条条揪出来

一、需求从哪里来:一个我自己每天在踩的坑

我写鸿蒙技术文章写了两年多,Obsidian 库里现在躺着 565 篇 Markdown。前两天我随手扫了一下,其中 276 篇里写死了版本号、API Level 或者某个 Kit 的名字。这些数字当年写下去的时候都是准确的,问题是鸿蒙迭代太快,它们会悄悄过期。

举一个真实的例子。我 2024 年那篇讲 ArkTS 与 C++ 混合开发的文章里有这么一句:“我们注意到最新发布的 HarmonyOS 5.0.1 版本,新增的能力主要是 C API”。这句话在 2024 年完全成立,可现在 HarmonyOS 都出到 7 了,读者要是照着这句去判断当前版本的能力边界,就会被带偏。

原来我是怎么处理这件事的。说实话,基本靠运气。要么是自己写新文章时正好翻到旧文,顺手改一下;要么是读者在评论区提醒我某处过时了,我再回去修。这两种方式都太被动,565 篇文档我根本不可能逐篇回头重读,而且重读也判断不出来,因为我脑子里记的也是旧的。

困难就在这里。我的知识库能非常准确地回答“我当初是怎么写的”,但它回答不了“我当初写的现在还成立吗”。后面这个问题需要的信息根本不在库里,它在库外面。

二、为什么需要搜索:私域提供断言,公网提供事实

把这件事拆开看,它其实是两类信息的交叉。

一类是私域的,就是我库里那些写死的版本断言,这部分只有我自己有,任何搜索引擎都查不到我 2024 年写过什么。另一类是公网的,就是“HarmonyOS 现在到哪个版本了”“API 26 是什么时候发布的”这类事实,这部分恰恰是私有库最缺的。

单靠任何一边都做不成。只翻自己的库,永远不知道外面变了;只搜公网,不知道该拿什么去比对。把两边接起来才有意义:私域负责提供“待核验的断言”,公网负责提供“最新的事实”,两者一交叉,就能得出“哪些旧了”。

这正好是元宝搜索(Tencent Yuanbao Search,产品代号 wsa)适合干的活。它是运行时调用,不是我写工具的时候查一次就完事。每次巡检都要拿当天的最新事实去比对,时效性是硬需求,缓存的答案没有意义。而且我要的不是十条蓝链接让我自己读,我需要结构化字段:标题、发布时间、来源站点、可点开的 URL,这样工具才能自动判定、自动出报告。

我选了方向里的“技术版本变更查证”这一类。下面说怎么做的。

三、怎么实现:一条四步流水线

整个工具叫 claim_patrol,Python 写的,零第三方依赖,四步。

第一步扫描抽断言。遍历库里 Markdown,用四类正则去抓时效性断言:API\s*(\d{2}) 抓 API Level,HarmonyOS\s*(\d(?:\.\d)*) 抓版本号,([A-Z][a-zA-Z]+\s+Kit) 抓 Kit 名称,还有一类抓“截至”“目前”“最新”这种带时间锚点的句子。抓完按段落去重,再把纯图片嵌入的段落剔掉,因为那些不是可核验的文字断言。截图这一轮扫的是库的前 200 个文件,抽出 703 条原始断言,去重后 59 条;把扫描范围放开到全部 565 篇,对应数字是 2340 条和 95 条。

第二步把断言转成检索词。这一步是搜索词设计的关键,不能把原文直接丢进去搜。HarmonyOS 6.1.1 这种,转成“HarmonyOS 6.1.1 最新版本 更新”;API 12 转成“HarmonyOS API 12 版本 发布”;Kit 名称转成“HarmonyOS XX Kit 新特性”。检索词里带上“最新”“发布”这类词,是为了把召回往时效性事实上引。

第三步调 SearchPro,带两个参数。Freshness=m3 把结果锁在最近三个月,Industry=news 把信源收拢到新闻垂类。这两个参数是这个工具能成立的前提,后面第五节我用数据说明为什么。

第四步规则判定加出报告。判定逻辑很朴素:拿库里的版本号跟当前基线比,低于基线就标“疑似过期”;同时从搜索结果的标题里再找一遍有没有更高版本的报道,找到了就把标题记进判定依据。最后输出一份 Markdown 报告,每条疑似过期都带上原句、出现文件、判定依据,以及最新来源的标题、站点、日期和可点开的 URL。

这里要说清楚一点:工具只标记,不改库。它给我一份“这些地方可能旧了,来源在这儿,你自己看”的清单,改不改、怎么改是我人工判断。自动改文档这种事风险太大,一条误判就可能把对的改成错的。

扫描 565 篇 Markdown

正则抽取时效性断言
版本号 / API Level / Kit / 时间锚点

去重 + 剔除图片嵌入段
703 条 → 59 条(全库 2340 → 95)

断言转检索词
HarmonyOS 6.1.1 → 加'最新''发布'

调 SearchPro
Freshness=m3 + Industry=news

Pages 二次解析
取 title / date / url / site

规则判定
低于当前基线 → 疑似过期

输出巡检报告
每条带可点开来源与日期

作者人工复核
工具不自动改库

接入环节踩的两个坑

签名这块我卡了两轮,简单记一下,因为文档确实没写清楚。

第一个坑是域名。元宝搜索有两套鉴权,腾讯云通用的 SecretId/SecretKey 走 TC3-HMAC-SHA256 签名,请求打到 wsa.tencentcloudapi.com;另一种 sk- 开头的服务 API KEY 走 Bearer,打到 api.wsa.cloud.tencent.com,那个域名同时也是官方 MCP 的地址。我第一版按 Bearer 的习惯打错了域名,服务端只回一句“未授权操作”,完全不提示是域名的问题。换成正确域名后错误变成了 AuthFailure.SignatureFailure,这反而是好消息,说明密钥有效,纯粹是签名算法不对。

第二个坑是签名格式。我按 AWS SigV4 的肌肉记忆写,待签名字符串第二行放了 ISO8601 时间,结果一直签不过。后来装官方 SDK 读源码才发现,腾讯云这里用的是 Unix 时间戳的字符串形式,就是 X-TC-Timestamp 那个值本身。还有一处更隐蔽:签名作用域里的 Date 必须取 UTC 日期,我早上八点测的时候东八区和 UTC 还是同一天所以没炸,换到晚上跑就会对不上。这类只在特定时段复现的错误最难查。

t = int(time.time())
date = dt.datetime.fromtimestamp(t, dt.timezone.utc).strftime("%Y-%m-%d")  # 必须 UTC
string_to_sign = "\n".join([
    "TC3-HMAC-SHA256",
    str(t),                    # Unix 时间戳,不是 AWS 的 ISO8601
    credential_scope,
    _sha256(canonical_request.encode("utf-8")),
])

结果处理上还有一个反直觉的地方。SearchPro 的返回里 Pages 不是对象数组,官方文档写明是 Array of String,每一条都是一个 JSON 字符串,拿到之后必须对每个元素再 json.loads 一次。我第一次调用时以为结果解析不出来,其实是漏了这层。

四、最后得到什么:一份能点开核对的体检报告

工具跑完 12 条断言,判出 9 条疑似过期。挑三条真实的贴出来。

HarmonyOS 6.1.1,出现在我的公众号欢迎词模板里。判定依据是主版本低于当前的 7,检索到的最新来源是 10 月 10 日新浪网的《鸿蒙手机买哪款好?Mate 90 还是 Pura 90?》。这条挺有意思,6.1.1 在我的库里同时是“全栈实战录”的系列名和事实陈述里的版本号,前者不该改,后者该加时间限定,工具把两种混在一起标了出来,这正是需要人工复核的地方。

HarmonyOS 5.0.1,就是我开头提到的那篇 2024 年的混合开发文章,原句“最新发布的 HarmonyOS 5.0.1 版本”放在今天已经完全误导。这轮检索到的来源是 10 月 9 日网易新闻的《鸿蒙 7 彻底杀疯了!5.0 版本一夜归零》,中间隔了两个大版本,属于最典型的必须回改的过期断言。

API 24,出现在《6.1.1 全栈实战录 - 88 冷启动加速》里,原句说 6.1.1 (API 24) 在 AbilityStage 新增了两个生命周期钩子。这个事实当时成立、现在也仍然成立(那个版本确实加了),但今天的读者可能把 API 24 当成当前版本,而基线已经是 26。这类该改的不是删掉事实,是给事实加上时间限定,工具把它标出来提醒的就是这个。

报告里每一条都是这个格式:断言是什么、出现在哪个文件、原句是什么、判定依据、最新来源的标题加站点加日期加可点开的链接。我拿这份清单回去改文章,改哪一篇、改成什么,都有据可依,不用凭记忆猜。

给565篇鸿蒙笔记做体检:用元宝搜索把过期断言一条条揪出来_01.png

给565篇鸿蒙笔记做体检:用元宝搜索把过期断言一条条揪出来_02.png

五、实际效果如何:测量方法和几组数据

规则要求写清楚测量方法,我说明一下。延迟和参数效果的数据来自 scripts/bench.py,四类查询各跑三轮,记录客户端观测耗时,也就是包含本地网络往返的时间,每轮之间隔 0.4 秒规避 QPS 限制。巡检那 12 条的数据来自 demo/claim_patrol.py 的真实运行。原始数据都留在 material/bench.json 和 material/patrol_report.md 里,脚本可以复现。

延迟方面,基准脚本我前后跑了三轮,截图这轮热态 p50 是 294ms、最大 346ms,前两轮热态 p50 分别是 634ms 和 328ms。同样的代码、同样的查询,三轮从 294 抖到 634,我一开始以为是服务端冷启动,但首轮和热态的高低关系每轮都不一样,这个解释站不住,所以更倾向是本地网络抖动,这里不下定论。三轮原始数据都留档在 material/bench_run1.json、bench_run2.json 和 bench.json,最快那轮跟官方说的“最快 300ms”能对上。巡检那 12 条的实测耗时在 446ms 到 687ms 之间,中位数 596ms,12 条跑完总共十几秒,对我这种一次巡检几十条的用量完全够用。

参数效果是这次实测里我觉得最有价值的发现。同一条查询“HarmonyOS 鸿蒙 最新 政策 适配”,四种参数配置下返回的站点集中度差别很大:

配置结果数带日期不同站点数延迟
默认1099371ms
Freshness=m110108461ms
Industry=news10108442ms
两者都加10105483ms

默认模式下十条结果来自九个不同站点,而且信源很杂,里头混着 wikipedia、几个地方政府网站,对“鸿蒙最新政策”这种查询基本没用。加上时效和垂类参数之后,站点收敛到五个,全是今日头条、新浪网、腾讯新闻这类新闻源;带日期的结果从 9 条变成 10 条,全都有时间戳。这个收敛的方向三轮都一致,绝对值有正负一到两个的波动。对我这个工具来说带日期这一条很关键,因为判定过期靠的就是发布时间,没有 date 字段的结果等于白给。

这里顺便说一个文档没写、只能实测发现的东西。官方文档对 score 的描述是“取值 0~1,越靠近 1 越相关”,默认模式下确实如此,截图这轮测到的是 0.607 到 0.782。但一旦加上 Industry,量纲就变了,这轮新闻垂类到了 2.53、财经垂类 3.59,之前两轮也在 1.98 到 2.30 和 1.19 到 1.99 这个量级,三轮全都超出文档说的上界。区间本身每次会变,越界这个事实没变过。排序的单调性还在,分数高的确实更相关,可如果你按文档在代码里写 if score > 0.8 这种阈值判断,加了 Industry 之后整套逻辑就废了。我的处理是不拿 score 做绝对阈值,只用它做组内相对排序。

给565篇鸿蒙笔记做体检:用元宝搜索把过期断言一条条揪出来_03.png

遇到的问题和现在的局限

这个工具现在只是可演示原型,几个地方还不成熟,我如实列出来。

判定规则太粗。我目前只做了“版本号低于基线”这一条硬规则,基线还是手写在代码里的常量。这意味着它只能抓版本类断言,抓不了“某 Kit 已废弃”“某接口签名变了”这种更细的过期,而那恰恰是技术文章里最容易出错的地方。

误报没法完全避免,而且有两种不同的来源。一种是语义上的:报告里“HarmonyOS 6.1.1”那条出现在公众号欢迎词模板里,可它在库里同时是“6.1.1 全栈实战录”的系列名,系列名永远不该改,工具分不清“陈述当前最新”和“描述历史版本”。另一种是抽取上的,我早期测试时抓到过一条“HarmonyOS 2”,去翻原句发现写的是“HarmonyOS 20+ 版本支持任意采样率解码”,正则只匹配到了开头的“2”。后一种好修,加个词边界约束就行,但它提醒我一件事:巡检工具的可靠性上限不在搜索,在抽取。这也是我不敢让它自动改库的原因,判断权必须留在人手里。

Kit 类断言还没接判定。全库去重后有 66 条 Kit 断言,现在只是抽出来了、检索也发了,但判定规则里没写 Kit 的有效性核验,全都落在“暂无法判定”。下一步想做的是把 Kit 名称跟官方最新的 Kit 清单比对,那需要另一路检索或者一份权威列表。

改进方向也清楚:把基线从常量改成每次巡检时先查一次“HarmonyOS 当前最新版本”,让基线自己更新;给判定加上上下文分类,区分系列名和事实陈述;Kit 那块补上官方清单比对。这些都不难,难的是规则要覆盖得够全又不过度误报,这个平衡得靠实际用一段时间慢慢调。

六、往后想

做这个工具的起因很朴素,就是我自己的库烂在那儿没人管。但做完之后我意识到,这个问题不止我一个人有。任何长期维护技术文档的人,任何写版本相关内容的人,都会遇到同一件事:写下去的时候是对的,过一阵就悄悄错了,而且错得毫无声响,没人会通知你。

传统的做法是定期人工复查,成本高到基本没人真做。现在有了能结构化返回、带时间戳、能限定信源的搜索接口,这件事第一次变得可以自动化。不是让 AI 替你改文档,那太危险;而是让 AI 替你找出“这些地方可能旧了,证据在这儿”,把判断权还给人,但把翻找的体力活接过去。

我觉得这才是搜索接口对知识管理真正的价值。它不是用来回答问题的,是用来给你的存量知识做体检的。问题总有人回答,可那些两年前写下的、当时正确的断言,正在悄悄地变成误导,而这件事在有了可核验的公网事实源之前,几乎无解。


附:代码与复现

完整代码和三轮基准的原始数据放在 Gitee:https://gitee.com/qingkouwei/yuanbao-search-lab,含 claim_patrol.py 巡检工具、yuanbao_search.py 零依赖签名实现、bench.py 测量脚本,以及 patrol_report.md、bench_run1/2.json 等全部留档,与文中截图逐数对应。

复现步骤:

export YUANBAO_API_KEY='AKIDxxxx'        # 腾讯云 SecretId
export YUANBAO_API_SECRET='xxxxxxxx'     # SecretKey

# 巡检自己的知识库(任何 Markdown 目录都行)
python3 claim_patrol.py --repo /path/to/your-knowledge-base --limit 12 --freshness m3

# 基准测量:延迟分布、参数效果、score 量纲
python3 bench.py

判定过期的核心逻辑就几行,贴出来方便讨论:

def check_outdated(claim, pages):
    reasons = []
    if claim["kind"] == "api_level" and int(claim["value"]) < CURRENT["api_latest"]:
        reasons.append("库中 API %s 低于当前基线 API %s"
                       % (claim["value"], CURRENT["api_latest"]))
    if claim["kind"] == "harmony_ver":
        major = claim["value"].split(".")[0]
        if major.isdigit() and int(major) < int(CURRENT["harmony_latest"]):
            reasons.append("库中 HarmonyOS %s 主版本低于当前 %s"
                           % (claim["value"], CURRENT["harmony_latest"]))
    # 再从检索结果的标题里找更高版本的报道作为旁证
    return ("疑似过期" if reasons else "暂无法判定"), reasons

关于 AI 参与环节,按活动要求说明:代码由 AI 辅助编写,我负责场景设计、判定规则的定夺、全部真实数据的采集与核对,以及对局限性的判断。文中所有延迟数字、参数对照、巡检结果均来自本机真实调用,原始数据在仓库里可查,我对内容和运行结果负责。在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐