news 2026/10/1 17:49:11

Edge Gallery 端侧日历读取技能 read-calendar-events 深度解析:基于 run_intent 的 Agent 工具调用实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Edge Gallery 端侧日历读取技能 read-calendar-events 深度解析:基于 run_intent 的 Agent 工具调用实现
  • 人工智能
  • 大模型
  • 本地部署
  • 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
点击查看免费下载

read-calendar-events 是 Edge Gallery(Google 开源的端侧 ML/GenAI 演示应用)内置的一项 Agent 技能,它让本地运行的 LLM 能够通过run_intent工具读取操作系统日历中指定日期的日程。本文以该技能随仓库发布的SKILL.md为核心骨架,结合 IntentHandler、RunIntentTool、SkillManager 等源码,完整拆解技能从 SKILL.md 解析、指令注入、工具注册到日历查询与 JSON 返回的全链路实现,并给出可直接复用的参数格式与调用流程。

技能文件定位:一个极简但完整的 Agent 指令单元

在仓库中,该技能位于 Android/src/app/src/main/assets/skills/read-calendar-events/SKILL.md。文件本身非常短小,却包含了 Edge Gallery 技能体系的两个核心组成部分:YAML 风格的 frontmatter 元数据与自由文本的 Instructions 指令区。

--- name: read-calendar-events description: Read OS calendar events for a specific date. --- # Read calendar events ## Instructions To read calendar events for a specific date, you must follow these exact steps: ...

其中:

  • name是技能的唯一标识,也是模型在意图识别时用于匹配技能的名称(如read-calendar-events);
  • description用于向模型描述该技能的能力边界,帮助模型在多种技能中选择正确的那一个;
  • ## Instructions之后的正文则是注入到模型上下文的完整操作指令,指导模型以固定步骤完成"读取某日日历事件"这一任务。

这个三段式结构并不是随意的约定。在 SkillManager.kt 的convertSkillMdToProto中,解析器以---分隔符切分文件,第一段被解析为 header(提取name、description等键),其余内容作为instructions文本,最终转换为 skill.proto 中定义的Skill消息(包含name、description、instructions、built_in、selected等字段)。技能目录被放在assets/skills/下,因此被标记为 built-in 技能;而通过 URL 添加或本地导入的技能则分别携带skill_url或import_dir_name字段。

当模型需要该技能时,由 LoadSkillTool.kt 调用skillsProvider.loadSkill(skillName)取回技能内容,并通过 SkillExtensions.kt 中的模板SKILL_INSTRUCTIONS_TEMPLATE重新拼装为---\nname: %s\ndescription: %s\n---\n\n%s的形式,连同技能描述一起注入模型提示词(prompt),再交由 AgentRuntimeExecutor.kt 驱动的 ReAct 循环执行。

run_intent:技能指令与系统能力之间的桥梁

SKILL.md中反复出现的run_intent并不是日历功能专属接口,而是 Edge Gallery 提供给端侧 Agent 的通用 Android Intent 执行工具。其实现位于 RunIntentTool.kt,核心方法签名如下:

run_intent(intent: String, parameters: String) -> Map<String, String>
  • intent:要执行的 Intent 动作名(字符串);
  • parameters:包含该 Intent 所需参数值的 JSON 字符串。

调用成功后,工具返回一个包含三个键的 Map:action(回显的 intent 名)、parameters(回显的参数字符串)与result(IntentHandler 的执行结果字符串)。若传入的 intent 名不在已知 IntentAction 列表中,RunIntentTool会走guardMissingEntityWithSkillFallback兜底逻辑:若该名称恰好命中某个已启用技能,则返回提示"该实体不存在,请尝试作为技能运行",否则返回Tool not found。

该工具在 AgentTools.kt 的AgentToolsImpl中通过lazy方式注册为RunIntentTool(context, skillsProvider),与load_skill、run_js、run_mcp等工具一起作为 Agent 的可用工具集暴露给模型。同时,工具执行过程中会通过 ToolAction.kt 定义的SkillProgressToolAction向聊天界面推送"Executing intent ..."进度项,使用户在 UI 上可见每一步工具调用。

第一步:先取当前本地日期时间

SKILL.md规定,读取日历事件前必须先调用run_intent获取用户本地时间,而不能直接假设"今天":

intent: get_current_date_and_time parameters: {}

该动作在 IntentHandler.kt 中的实现非常直接:使用SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss EEEE", Locale.getDefault())格式化当前时间并原样返回。也就是说,返回字符串同时携带了:

  • yyyy-MM-dd:本地日期;
  • HH:mm:ss:本地时间;
  • EEEE:本地时区的星期几(如 Friday)。

由于格式化基于Locale.getDefault(),星期名称会随系统语言变化,这要求模型在后续日期推算时既能解析"May 15"这类绝对日期,也能解析"tomorrow""this Friday"这类相对表达。

第二步:显式推算目标日期(YYYY-MM-DD)

SKILL.md特别强调,在读取事件之前,模型必须在自己的回复中显式写出日期推算过程,包括:

  1. 今天的确切日期与星期几;
  2. 用户请求的目标日或相对时间(如 "tomorrow"、"this Friday"、"May 15");
  3. 最终计算得到的目标日期,格式为YYYY-MM-DD。

这种"把中间计算过程写进回复"的做法,是引导端侧小模型降低日期推算错误率的关键设计——它要求模型把隐性推理外化为可校验的显式步骤,同时让用户能在界面上看到模型的推算依据。推算时应特别留意跨月、跨年场景(例如 1 月 31 日加 1 天应为 2 月 1 日),这与同目录下 schedule-notification/SKILL.md 中对"added days 超过当月天数时要正确滚动到下月/下一年"的约束是一致的。

第三步:调用 read_calendar_events 读取指定日期

日期确定后,按SKILL.md的固定参数调用:

intent: read_calendar_events parameters: {"date": "YYYY-MM-DD"}

date是唯一的必填字段,类型为字符串,取值就是第二步推算出的目标日期。参数由 Moshi 解析为 IntentHandler.kt 中定义的ReadCalendarEventsParams(date: String)。

权限检查:READ_CALENDAR

readCalendarEvents在查询前会先检查Manifest.permission.READ_CALENDAR是否已授予(该权限已在 AndroidManifest.xml 中声明)。若未授予,则通过RunIntentTool传入的requestPermission回调发起授权请求——这个回调最终会转换为 ToolAction.kt 中的RequestPermissionToolAction,在 Agent 聊天界面弹出系统权限对话框,用户同意后返回true继续执行,拒绝则直接返回"failed: READ_CALENDAR permission denied by user"。

底层查询:CalendarContract.Instances 的当天时间窗

拿到权限后,源码使用 Android 标准的CalendarContract.Instances进行查询,逻辑分为四步:

  1. 用SimpleDateFormat("yyyy-MM-dd")解析传入的date,得到该日零点对应的Date;
  2. 构造当天的完整时间窗:先把小时/分钟/秒/毫秒全部清零得到startOfDayMillis,再add(DAY_OF_MONTH, 1)并减 1 毫秒得到endOfDayMillis(即当日 23:59:59.999);
  3. 通过Instances.CONTENT_URI.buildUpon()配合ContentUris.appendId把起止时间拼进 URI,构成按实例区间查询;
  4. 以projection = {Instances.TITLE, Instances.DESCRIPTION, Instances.BEGIN, Instances.END}查询,并按${Instances.BEGIN} ASC升序排序。

这意味着"读取某日事件"实际覆盖的是该日零点到次日零点前最后一毫秒的所有日历实例,既包含整天事件也包含跨日事件的当日片段。

第四步:解析 JSON 结果并组织回答

查询得到的光标被逐行转换为CalendarEventDto(title、description、begin_time、end_time),其中时间字段统一格式化为yyyy-MM-dd'T'HH:mm:ss,最终序列化为ReadCalendarEventsResponse的 JSON:

{ "events": [ { "title": "Team standup", "description": "Weekly sync", "begin_time": "2026-09-30T10:00:00", "end_time": "2026-09-30T10:30:00" } ] }

SKILL.md的最后一步要求模型解释这份 JSON 列表并给出清晰、友好的回答,即把结构化的时间/标题数据转述为自然语言日程摘要。若当天没有任何事件,返回的events为空数组[],模型应如实告知用户"当天没有安排"。若解析失败(如日期格式非法),工具返回"failed"或"failed: <错误信息>"。

与周边 Intent 的联动:一个完整的日程操作家族

read_calendar_events只是 IntentHandler.kt 中IntentAction枚举的一员。从枚举定义可以推断,同一套run_intent机制还支撑着另外五个动作:

intent 动作用途
get_current_date_and_time获取本地日期、时间与星期
read_calendar_events读取指定日期日历事件
create_calendar_event调起系统日历创建事件(title、description、begin_time、end_time)
schedule_notification调度一条通知(支持指定年月日与repeat_daily每日重复)
send_email调起系统邮件应用
send_sms调起系统短信应用

这些动作的 Skill 指令文档同样位于 assets/skills/ 下,例如 create-calendar-event/SKILL.md 与 schedule-notification/SKILL.md。其中schedule_notification的参数(title、message、hour、minute、可选的year/month/day、deeplink与repeat_daily)由ScheduleNotificationParams承载,并经 NotificationScheduleManager.kt 通过AlarmManager.setRepeating/setAndAllowWhileIdle落地到系统闹钟,由 NotificationReceiver.kt 在触发时弹出通知,并可携带构造出的com.google.ai.edge.gallery://...deeplink 在点击后回跳 Agent 聊天页面。由此可见,"读日历"与"建日程""定时提醒"共同构成了一个完整的日程管理技能矩阵,全部复用run_intent这一条工具通道。

从源码结构看技能体系的扩展方式

从 SkillManager.kt 的实现可以推断,Edge Gallery 的技能体系是开放的:除了assets/skills/下的 built-in 技能外,还支持从远程 URL(addSkillFromUrl,自动去掉末尾/SKILL.md或/后拼接下载)、本地目录导入(addSkillFromLocalImport,把目录递归拷贝进应用私有 files 目录并设置import_dir_name)以及用户在界面中直接编辑(saveSkillEdit,规范化空格为连字符并写入skills/<name>/SKILL.md)。任何满足 frontmatter + instructions 格式的SKILL.md都可以成为 Agent 的一项新能力,前提是它所依赖的动作(Intent、JS 脚本或 MCP 工具)已存在。技能启用状态由selected字段与模型级defaultDisabledSkills共同管理,用户手动修改后会标记user_modified_selection以保留用户偏好。

小结

read-calendar-events是一个把"端侧 LLM 意图理解"与"Android 系统日历能力"无缝衔接的最小范例:模型通过run_intent工具先后调用get_current_date_and_time与read_calendar_events,前者提供日期推算所需的本地基准时间,后者在READ_CALENDAR权限授权后利用CalendarContract.Instances完成当天时间窗内的事件查询,并以 JSON 形式返回结构化日程;而驱动这一切的,正是仓库中以SKILL.md为载体的可插拔技能指令体系。理解这条链路,也就掌握了 Edge Gallery 中所有基于 Intent 的系统操作类技能(建日程、发邮件、定时提醒)的共同运行原理。

  • 人工智能
  • 大模型
  • 本地部署
  • 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
点击查看免费下载

相关推荐

上一篇:如何轻松管理ComfyUI扩展:ComfyUI-Manager完整指南
下一篇:《The Concise TypeScript Book》条件类型(Conditional Types)实战详解:extends 三元表达式、分布式分发、infer 与内置工具类型

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 17:48:21

OpenClaw V2026.3.11 模型管理实战:从本地到API切换与Teams接入

升级到 OpenClaw 模型管理 V2026.3.11 之后的第一个下午&#xff0c;我只做了一件事&#xff1a;用几条指令把同一个推理服务从本地模型切到 API 模型&#xff0c;再切回来&#xff0c;中间没有改配置文件&#xff0c;也没有重启任何进程。这个版本把“模型管理”从一件靠记忆和…

作者头像 李华
网站建设 2026/10/1 17:47:07

JSP+Servlet+JDBC构建汽车售后管理系统:从数据库设计到工单闭环

简介&#xff1a;基于Web的汽车售后服务管理系统设计与实现项目包&#xff0c;面向计算机相关专业毕业设计、期末大作业及需要项目实战练习的学习者。系统采用JSPJava开发&#xff0c;覆盖客户信息管理、维修记录、配件库存、保养提醒、服务预约、投诉处理等典型业务模块&#…

作者头像 李华
网站建设 2026/10/1 17:45:38

OpenCvSharp轮廓检测实战:从环境配置到FindContours完整链路

简介&#xff1a;这份资源是面向.NET开发者与计算机视觉初学者的OpenCvSharp轮廓检测实战示例&#xff0c;基于OpenCV的C#封装库&#xff0c;帮助读者掌握从二值图像中提取、分析并绘制轮廓的完整流程。内容涵盖FindContours轮廓提取、Threshold与Canny预处理、轮廓面积与周长等…

作者头像 李华
网站建设 2026/10/1 17:45:29

同步优先与存储优先:企业协同架构选型及落地指南

1. 先搞懂“卡顿”到底卡在哪&#xff1a;从一次全员大表协作事故说起1.1 一场全员大表引发的“转圈”事故上个月跟一个做企业数字化项目的朋友吃饭&#xff0c;他给我看了一段他们客户内部的吐槽截图。那是一家三千人规模的制造集团&#xff0c;人力资源部发了一张全员绩效考核…

作者头像 李华
网站建设 2026/10/1 17:44:44

华为PDT经理角色认知:从项目经理到商业成功责任人的核心跨越

简介&#xff1a;这份PPT教材聚焦华为IPD体系下PDT经理的角色认知与履职能力&#xff0c;面向产品开发团队负责人、项目经理及希望理解重量级团队运作机制的产品线骨干。内容围绕PDT经理的重要性、基本角色定位、关键管理活动、能力模型与评估方法、培养路径五大模块展开&#…

作者头像 李华
网站建设 2026/10/1 17:44:19

WSL2 安装 Ubuntu 22.04 完整教程:从环境检查到磁盘迁移

折腾过 Linux 的人应该都有类似经历&#xff1a;手头一台 Windows 10 电脑&#xff0c;却要跑 Linux 下的工具链&#xff0c;装双系统嫌切换麻烦&#xff0c;开个虚拟机又卡得连拖动窗口都掉帧。我前两年就因为频繁在 Windows 和 Ubuntu 之间来回重启&#xff0c;实在忍无可忍&…

作者头像 李华