拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Edge Gallery 端侧技能解析:基于 run_js 的 query-wikipedia 维基百科查询技能实现原理与实战指南

AI Edge Gallery 端侧技能解析:基于 run_js 的 query-wikipedia 维基百科查询技能实现原理与实战指南
  • 人工智能
  • 大模型
  • 本地部署
  • AI 应用
  • 移动开发
  • AI Agent
  • AI 技能
  • MCP Clients

【免费下载链接】gallery

A gallery that showcases on-device ML/GenAI use cases and allows people to try and use models locally.

项目地址:https://gitcode.com/GitHub_Trending/gallery44/gallery
点击查看免费下载

本篇技术指南以 AI Edge Gallery(gallery)仓库中的内置技能query-wikipedia为主体,系统讲解如何在端侧(on-device)大语言模型环境中,通过SKILL.md指令编排 + 隐藏 WebView 中运行的 JavaScript 脚本,实现对 Wikipedia 的模糊搜索、Infobox 信息抽取与多语言摘要查询。读完本文,你将掌握该技能的完整参数规范、底层run_js工具调用链、ai_edge_gallery_get_result脚本接口契约,以及语言截断、实体抽取等工程细节,并可据此理解并改造同类"API 查询型"端侧技能。

一、技能定位:端侧 LLM 如何"查询 Wikipedia"

AI Edge Gallery 是一个展示端侧机器学习 / GenAI 用例的应用仓库,其核心能力之一是Agent Skills(代理技能):以模块化方式扩展端侧 LLM 的能力边界。由于端侧 LLM 运行在沙箱化的移动环境中,无法像云端那样直接执行 Python 脚本或 CLI 命令,仓库采用了两条主要执行路径(见 skills/README.md):

  1. JavaScript 技能:在轻量级隐藏 WebView 中执行自定义逻辑;
  2. 原生 App Intent:调用 Android/iOS 系统内置能力(如发送邮件/短信)。

query-wikipedia正是第一类JS 技能(同时依赖外部 API)的典型代表。它的SKILL.md元数据明确标注了其类别标签:JS+API(见 skills/README.md 的技能示例清单),在仓库中有两个等价副本:

  • 打包进 App 的版本:Android/src/app/src/main/assets/skills/query-wikipedia/SKILL.md(含 scripts/index.html);
  • 仓库开发目录版本:skills/built-in/query-wikipedia/SKILL.md(含 scripts/index.html)。

用户向 Agent 提问后,LLM 根据系统提示词中附带的技能名称与描述判断相关性,命中后自动触发该技能;技能背后的 JS 脚本在隐藏 WebView 中完成对https://<lang>.wikipedia.org/w/api.php的 HTTP 请求,再把结构化结果交回 LLM 生成最终回答。

二、技能契约:SKILL.md 指令文件逐字段解析

SKILL.md是每个技能的灵魂文件,采用 YAML frontmatter + Markdown 指令体的结构(Android 端通过 SkillManager.kt 的convertSkillMdToProto将其解析为Skillproto,对应字段定义见 skill.proto)。

2.1 frontmatter 元数据

--- name: query-wikipedia description: Query summary from Wikipedia for a given topic. ---
  • name:技能唯一标识(kebab-case),也是 LLM 调用run_js时传入的skillName;
  • description:插入系统提示词的技能摘要,LLM 据此判断用户请求是否与该技能匹配。

2.2 Instructions:run_js 调用规范与 data JSON 结构

## Instructions Call the `run_js` tool using `index.html` and a JSON string for `data` with the following fields: - **topic**: Required. Extract ONLY the primary entity, person, or event (e.g., "2026 Oscars", "Albert Einstein"). You MUST REMOVE all specific question details, action words, or conversational text (e.g., do NOT include words like "winner", "best picture", "who won", "history of"). Search for the broad subject so the tool can return the main article. - **lang**: Required. The 2-letter language code. This code MUST match the language of the keywords you provided in the `topic` field. Use standard codes, e.g., "en" (English), "es" (Spanish), "zh" (Chinese), "fr" (French), "de" (German), "ja" (Japanese), "ko" (Korean), "it" (Italian), "pt" (Portuguese), "ru" (Russian), "ar" (Arabic), "hi" (Hindi).

字段规格汇总:

字段是否必填类型取值与规则
topic必填String只提取主要实体/人物/事件(如2026 Oscars、Albert Einstein);必须剔除疑问词、动作词、会话性文本(如winner、best picture、who won、history of),以宽泛主题词触发主词条搜索
lang必填String两位字母语言代码,且必须与topic关键词的语言一致;标准代码见下表

支持的lang标准代码(来自原指令,均为 ISO 639-1 风格两位代码):

代码语言代码语言
en英语ja日语
es西班牙语ko韩语
zh中文it意大利语
fr法语pt葡萄牙语
de德语ru俄语
——ar阿拉伯语
——hi印地语

topic 提取示例(指令中的规则在实际问答中的运用):

用户提问应提取的topic应剔除的内容
谁赢得了 2026 年奥斯卡最佳影片?2026 Oscarswho won、best picture
爱因斯坦的相对论是什么?Albert Einsteinrelativity(具体问题细节)
法国大革命的历史意义French Revolutionhistory of、significance

2.3 Constraints:输出与行为约束

**Constraints:** - Provide a concise summary (1-3 complete sentences) to conserve context. Always ensure your response ends with a finished sentence. your response MUST BE written in the SAME language as the user's original prompt. - For recurring events or time-sensitive facts, query the specific iteration (e.g., "2026 Oscars"). If the user omits the year, default to the current year. - If the exact answer to the user's question is not found in the extract, briefly state this, then proactively offer a related piece of information that *was* found in the text.

三条约束分别解决三个工程问题:

  1. 上下文节约:摘要必须控制在 1–3 个完整句子且以完整句收尾,避免污染后续对话上下文;同时回答语言必须与用户原始提问语言一致(而非与lang一致);
  2. 时效性:对周期性事件(如奥斯卡、奥运会)查询具体届次;用户未给年份时默认当年;
  3. 信息兜底:抽取文本中找不到确切答案时,如实说明并主动补充文本中确实存在的相关内容,保证回答"有料且诚实"。

三、脚本实现:index.html 的 Wiki 查询流水线

技能的真实执行逻辑位于 scripts/index.html,核心是fetchWikiFuzzyAndInfobox(topic, lang)函数,分四步完成"模糊搜索 → 抽取引言 → 解析 Infobox → 语言截断"。

3.1 第一步:模糊搜索获取精确词条标题与引言

const baseUrl = `https://${lang}.wikipedia.org/w/api.php`; const searchParams = new URLSearchParams({ action: "query", format: "json", generator: "search", gsrsearch: topic, gsrlimit: "1", prop: "extracts", explaintext: "1", exintro: "1", origin: "*", });

要点:

  • 通过generator=search+gsrlimit=1做模糊搜索并只取第一条,从而把用户给出的宽泛topic归一化为维基百科的精确词条标题;
  • prop=extracts+explaintext=1获取纯文本提取内容(剥离 Wiki 标记),exintro=1只取引言(词条开篇摘要),这是后续 LLM 总结的主要素材;
  • origin=*允许跨域请求,适配 WebView 环境的 CORS 策略;
  • 若searchData.query.pages为空,直接返回error: No Wikipedia articles found matching '<topic>' in language '<lang>'.。

3.2 第二步:拉取 Section 0 的 HTML 并解析 Infobox

const parseParams = new URLSearchParams({ action: "parse", page: title, section: "0", prop: "text", format: "json", origin: "*", });

拿到标题后,通过action=parse+section=0获取词条顶部(含信息框)的 HTML,再用DOMParser定位table.infobox,遍历其tr行,取每行的th(键)与td(值):

let key = th.textContent.replace(/\[\d+\]/g, "").trim(); let value = td.textContent .replace(/\[\d+\]/g, "") .trim() .replace(/\n+/g, " | "); infoboxText += `${key}: ${value}\n`;

这里有两处关键的数据清洗:删除[1]、[2]之类的引注角标,并将多行值合并为|分隔的紧凑文本。若解析失败(如页面无 infobox),仅打印console.warn后静默继续,不阻塞主流程。

3.3 第三步:合并 Infobox 与引言摘要

let finalResult = ""; if (infoboxText) { finalResult += `--- INFOBOX ---\n${infoboxText}\n\n`; } if (extract) { finalResult += `--- SUMMARY ---\n${extract}`; } if (!finalResult.trim()) { return { error: `Found page '${title}' but no text or infobox was available.` }; }

最终输出以--- INFOBOX ---与--- SUMMARY ---两个带标记的区块返回,明确区分"结构化属性数据"与"自然语言引言",方便 LLM 按需取材。

3.4 第四步:语言相关的安全截断

let maxChars; switch (lang.toLowerCase()) { case "zh": maxChars = 1500; break; case "fr": maxChars = 4300; break; case "es": maxChars = 4500; break; case "en": default: maxChars = 5000; break; } if (finalResult.length > maxChars) { finalResult = finalResult.substring(0, maxChars) + "\n\n... [TRUNCATED TO SAVE CONTEXT]"; }

不同语言的字符密度差异很大,脚本按语言设置了不同的安全上限,防止超长文本一次性塞满 LLM 上下文窗口:中文zh上限 1500 字符,法语fr4300,西班牙语es4500,英语及其余语言默认 5000,截断处以... [TRUNCATED TO SAVE CONTEXT]显式标记。这一设计与SKILL.md中"Provide a concise summary to conserve context"的约束前后呼应。

3.5 对外接口:ai_edge_gallery_get_result

window["ai_edge_gallery_get_result"] = async (data) => { try { const jsonData = JSON.parse(data); if (!jsonData.topic) return JSON.stringify({ error: "No topic provided to search." }); if (!jsonData.lang) return JSON.stringify({ error: "No language code (lang) provided." }); const wikiResponse = await fetchWikiFuzzyAndInfobox(jsonData.topic, jsonData.lang); return JSON.stringify(wikiResponse); } catch (e) { console.error(e); return JSON.stringify({ error: `Failed to query Wikipedia: ${e.message}` }); } };

这是所有 JS 技能必须遵守的统一契约(详见 skills/README.md):在window上暴露名为ai_edge_gallery_get_result的异步函数,接收 App 传入的字符串化 JSON(即SKILL.md定义的data),解析后执行业务逻辑,返回字符串化的 JSON 对象——成功时含title与result字段,失败时含error字段。返回值会被 Android 端的run_js工具解析并回传给 LLM。

四、调用链剖析:run_js 工具如何在 Android 端驱动脚本

SKILL.md指令让 LLM 调用run_js工具,该工具在 Android 端的实现位于 RunJsTool.kt,其注解声明了三个参数:

@Tool(description = "Runs JS script") fun runJs( @ToolParam(description = "The name of skill") skillName: String, @ToolParam(description = "The script name to run. Use 'index.html' if not provided by user") scriptName: String, @ToolParam(description = "The data to pass to the script. Use empty string if not provided by user") data: String, ): Map<String, Any>

完整执行链路如下:

  1. 定位技能:通过skillsProvider.loadSkill(skillName)从已启用技能列表(SkillManager.getSelectedSkills(),见 SkillManager.kt)中按名称模糊匹配(忽略大小写、下划线/连字符差异);未找到则返回error+status: failed;
  2. 密钥处理(可选):若技能requireSecret为 true,先从 DataStore 读取已保存密钥,缺失时弹原生对话框向用户索取(本技能不涉及);
  3. 构造脚本 URL:调用 SkillExtensions.kt 中的getJsSkillUrl(scriptName),把技能目录拼接为<baseUrl>/scripts/<scriptName>形式的本地 URL(内置技能基于LOCAL_URL_BASE指向 assets);
  4. 发送执行动作:通过CallJsToolAction(url, data, secret)让隐藏 WebView 加载index.html并调用ai_edge_gallery_get_result;
  5. 解析结果:用 Moshi 将脚本返回的字符串按CallJsSkillResult反序列化:error非空判为失败;result字段作为文本结果回传;image/webview字段则触发对应的 UI 呈现(本技能仅返回文本result)。

期间通过SkillProgressToolAction在聊天界面展示"Calling JS script"的执行进度与传入的data,便于用户观察与调试。此外,LoadSkillTool.kt 提供了配套的load_skill工具,可将技能完整指令内容注入模型上下文,让 LLM 在首次调用前精确理解脚本契约。

五、在 App 中启用与安装该技能

query-wikipedia属于内置(built-in)技能。Android 端在启动时会通过 SkillManager.kt 的loadSkills()读取assets/skills目录下的所有SKILL.md并解析为 proto 存储到 DataStore,其selected状态决定是否进入 LLM 的系统提示词技能清单(getSelectedSkillsNamesAndDescriptions())。若该技能默认未启用,用户可参照 skills/README.md 的三种方式添加:

  1. 从社区精选列表添加:进入 Agent Skills 用例 → 点击 "Skills" 芯片进入 Skill Manager → 点(+)→ 选择Add skill from featured list;
  2. 从 URL 添加:将技能托管到真正的 Web 服务(如 GitHub Pages,需在仓库根目录放置.nojekyll禁用 Jekyll 渲染),在Load skill from URL弹窗中输入指向技能文件夹的地址(浏览器可直接打开https://your/url/SKILL.md验证);远端版本由SkillManager.addSkillFromUrl()拉取校验;
  3. 从本地导入:adb push技能目录到设备后,通过Import local skill使用系统文件选择器选中目录,App 会将其复制到内部存储。

启用后,用户只需用自然语言提问(如"用中文介绍一下阿尔伯特·爱因斯坦"),LLM 即会按SKILL.md指令组装{"topic": "Albert Einstein", "lang": "zh"},驱动脚本完成查询并以用户语言总结回答。

六、工程启示与扩展建议

从query-wikipedia的完整实现中,可以提炼出通用"API 查询型 JS 技能"的三个最佳实践:

  1. 指令即契约:SKILL.md必须把data的 JSON 字段、取值规则(必填/可选、格式)写清楚,并给出反例(如"不要包含 winner 这类动作词"),LLM 才能稳定生成合法参数;
  2. 脚本侧做防御与限流:在ai_edge_gallery_get_result内校验必填字段、捕获异常返回error,并按语言做字符截断——这比在指令层约束更可靠,是保护端侧上下文窗口的最后一道闸门;
  3. 结构化输出便于消费:--- INFOBOX ---/--- SUMMARY ---的分区标记让 LLM 能清晰区分"事实属性"与"叙述摘要",从而在约束 3 的兜底逻辑下给出更精准的回答。

若需在此基础上扩展(例如增加维基百科其他 API action、按日期区间检索、或把查询结果以 WebView 形式渲染成富文本卡片),只需同步修改index.html的返回 JSON(加入webview/image字段)与SKILL.md的指令说明,其余调用链(run_js工具、URL 构造、结果解析)无需改动,体现了 AI Edge Gallery 技能体系的低耦合可扩展设计。

七、进一步阅读

  • 技能目录结构、JS 技能开发契约与ai_edge_gallery_get_result返回格式:skills/README.md
  • run_js工具完整实现:RunJsTool.kt
  • 技能 URL 拼接逻辑:SkillExtensions.kt
  • 技能加载、解析与 DataStore 持久化:SkillManager.kt
  • Skillproto 字段定义:skill.proto
  • 其他同类内置技能(如calculate-hash、qr-code):skills/built-in
  • 人工智能
  • 大模型
  • 本地部署
  • AI 应用
  • 移动开发
  • AI Agent
  • AI 技能
  • MCP Clients

【免费下载链接】gallery

A gallery that showcases on-device ML/GenAI use cases and allows people to try and use models locally.

项目地址:https://gitcode.com/GitHub_Trending/gallery44/gallery
点击查看免费下载

相关推荐

上一篇:ZenlessZoneZero-OneDragon MCP Tool 实现规范:绝区零一条龙项目特化的 tool 编写落地指南
下一篇:react-jsonschema-form中的表单数据校验规则复用策略

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表