
一、实验目的一知识目标理解AI Agent的基本概念、核心特征与工作原理。掌握ReActReasoning Acting模式的工作流程与适用场景。理解Function Calling机制的设计原理与安全优势。掌握鸿蒙Calendar Kit日历服务API与Notification Kit通知服务API的使用方法。二能力目标能够设计并实现基于Function Calling的Agent工具函数体系。能够实现多轮对话管理器支持上下文维护与指代消解。能够将云端大模型能力与鸿蒙系统能力日历、通知融合构建完整的智能应用。能够对Agent应用进行功能测试与性能优化。三素养目标培养以用户为中心的AI应用设计思维关注交互体验与隐私安全。理解端云协同架构中数据最小化原则的工程价值。建立AI Agent在智能办公场景中的应用视野。二、实验环境与准备2.1 开发环境2.2 知识准备完成本实验前请确认已掌握以下知识1.ArkTS基本语法接口interface、枚举enum、泛型、异步编程async/await。2.ArkUI声明式开发组件定义Component、状态管理State、Prop、Link。3.HTTP网络请求使用kit.NetworkKit的http模块发送POST请求。4.第5章内容云端大模型API调用方法、请求参数格式、响应解析。5.第10章理论内容Agent概念、ReAct模式、Function Calling机制、意图理解与实体抽取。2.3 项目创建请按以下步骤创建项目6.打开DevEco Studio选择Create Project。7.选择Empty Ability模板点击Next。8.项目配置Project Name填入SmartScheduleAgentBundle Name填入com.example.smartscheduleCompile SDK选择API 12。9.点击Finish完成项目创建。【注意】请确保在module.json5中声明INTERNET权限否则无法调用云端大模型API。三、实验前思考题请在进行实验前思考以下问题并在实验报告中给出你的理解在传统的日程管理应用中用户需要手动填写标题、时间、地点等表单字段。使用AI Agent方式后用户只需用自然语言说下周三下午3点开项目评审会即可自动创建日程。请思考这种交互方式相比传统表单有哪些优势又可能带来哪些新的挑战Function Calling机制中大模型只返回工具调用指令函数名参数而由应用程序负责实际执行。请思考为什么不直接让大模型执行函数这种决策与执行分离的设计在安全性和隐私保护方面有什么好处在多轮对话场景中用户可能说把那个会议改到后天。Agent需要知道那个会议指的是哪条日程。请思考Agent可以通过哪些信息来确定指代对象上下文管理需要维护哪些状态四、实验任务与步骤本实验共10个任务按照从底层到上层、从数据到界面的顺序逐步构建智能日程Agent应用。任务1 项目结构与类型定义【任务目标】搭建项目工程结构定义全局类型和数据模型。步骤1.1 创建目录结构在项目ets目录下创建以下子目录SmartScheduleAgent/ ├── entry/ │ └── src/main/ets/ │ ├── entryability/ │ │ └── EntryAbility.ets # Ability入口 │ ├── model/ │ │ └── Types.ets # 全局类型定义 │ ├── service/ │ │ ├── CalendarManager.ets # 日历管理服务 │ │ ├── ReminderService.ets # 提醒通知服务 │ │ ├── DialogManager.ets # 对话管理器 │ │ └── AgentService.ets # Agent核心服务 │ ├── pages/ │ │ ├── ChatPage.ets # 对话界面 │ │ ├── ScheduleListPage.ets # 日程列表界面 │ │ └── IndexPage.ets # 入口页面Tab │ └── common/ │ └── Constants.ets # 常量定义 └── entry/src/main/resources/ └── base/profile/ └── main_pages.json # 页面路由配置步骤1.2 定义类型模型在model/Types.ets中定义以下类型。请根据提示完成代码// model/Types.ets // 意图类型枚举 export enum IntentType { CREATE create, QUERY query, UPDATE update, DELETE delete, CHAT chat } // 日程事件接口 export interface ScheduleEvent { id: string; title: string; startTime: string; // ISO格式时间字符串 endTime: string; location?: string; reminderMinutes?: number; // 提前提醒分钟数 isAllDay?: boolean; } // 对话消息接口 export interface ChatMessage { role: user | assistant | tool; content: string; timestamp: number; toolCallId?: string; } // 工具调用接口 export interface ToolCall { name: string; arguments: Recordstring, string | number | undefined; } // 对话状态接口 export interface DialogState { messages: ChatMessage[]; // 对话历史 pendingAction: string | null; // 待确认操作 contextScheduleId: string | null; // 上下文日程ID lastIntent: IntentType | null; // 上一轮意图 } // Agent响应接口 export interface AgentResponse { success: boolean; message: string; data?: Recordstring, Object; }【提示】Types.ets是全局类型定义文件所有其他文件通过import引用这些类型。良好的类型定义是TypeScript/ArkTS工程的基础。步骤1.3 定义常量在common/Constants.ets中定义API地址等常量// common/Constants.ets export const LLM_API_URL https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation; export const LLM_MODEL qwen-plus; export const API_KEY your_api_key; // 请替换为你的实际API Key export const MAX_HISTORY_LENGTH 10; // 对话历史最大轮数 export const DEFAULT_REMINDER_MINUTES 15; // 默认提前提醒时间【注意】API Key属于敏感信息请勿将真实Key提交到版本控制系统。实际部署时应使用安全存储机制参见第14章。任务2 日历管理服务【任务目标】封装Calendar Kit API实现日程的创建、查询、修改、删除和冲突检测。步骤2.1 导入Calendar Kit在service/CalendarManager.ets中导入日历服务模块// service/CalendarManager.ets import { calendarMgr } from kit.CalendarKit; import { ScheduleEvent } from ../model/Types;步骤2.2 实现日程创建方法创建日程需要调用calendarMgr.addEvent方法。请完成以下代码框架export class CalendarManager { private calendar: calendarMgr.Calendar | null null; // 初始化日历 async init(): Promisevoid { // TODO: 获取默认日历实例 // 提示使用 calendarMgr.getCalendar() 获取日历对象 } // 创建日程 async createSchedule(params: { title: string; startTime: Date; endTime: Date; location?: string; reminderMinutes?: number; }): Promisestring { // TODO: 构造Event对象并调用addEvent // 提示 // 1. 构造 calendarMgr.Event 对象 // 2. 设置 title, startTime, endTime, location // 3. 设置 reminderTime 为 reminderMinutes // 4. 调用 this.calendar.addEvent(event) 获取eventId // 5. 返回 eventId } }【提示】calendarMgr.Event对象的核心字段title标题、startTime开始时间Date、endTime结束时间Date、location地点、reminderTime提前提醒分钟数。步骤2.3 实现日程查询方法查询日程使用getEvents方法支持按时间范围查询// 查询日程 async querySchedules( startDate: Date, endDate: Date ): PromiseScheduleEvent[] { // TODO: 按时间范围查询日程 // 提示 // 1. 调用 this.calendar.getEvents({ start: startDate, end: endDate }) // 2. 将返回的Event数组转换为ScheduleEvent数组 // 3. 按startTime排序 }步骤2.4 实现修改与删除方法请参考创建方法的模式实现以下两个方法// 修改日程 async updateSchedule( scheduleId: string, updates: PartialScheduleEvent ): Promiseboolean { // TODO: 根据scheduleId获取日程更新字段后保存 // 提示使用 calendar.getEvent(id) 获取修改后 updateEvent } // 删除日程 async deleteSchedule(scheduleId: string): Promiseboolean { // TODO: 根据scheduleId删除日程 // 提示使用 calendar.deleteEvent(id) }步骤2.5 实现冲突检测冲突检测是日程管理的核心逻辑用于判断新日程是否与已有日程时间重叠// 检测时间冲突 async checkConflict( startTime: Date, endTime: Date ): Promise{ hasConflict: boolean; conflictTitle?: string } { // TODO: 查询该时间段内是否已有日程 // 提示 // 1. 查询 startTime 到 endTime 范围内的日程 // 2. 如果有日程返回 hasConflicttrue 和 conflictTitle // 3. 时间重叠判断两个时间段 [s1,e1) 和 [s2,e2) 重叠的条件是 s1 e2 s2 e1 }【注意】时间重叠判断是本任务的核心算法。两个时间段[A_start, A_end)和[B_start, B_end)重叠的条件是A_start B_end 且 B_start A_end。请仔细验证这个逻辑。任务3 提醒通知服务【任务目标】封装Notification Kit实现日程提醒通知功能。步骤3.1 导入通知模块// service/ReminderService.ets import { notificationManager } from kit.NotificationKit; import { ScheduleEvent } from ../model/Types;步骤3.2 实现即时通知当用户创建日程后可以立即发送一条通知告知用户export class ReminderService { // 发送即时通知 async publishNotification( title: string, content: string ): Promisevoid { // TODO: 构造NotificationContent并发布 // 提示 // 1. 构造 notificationManager.NotificationContent 对象 // 2. 设置 title 和 text // 3. 构造 notificationManager.NotificationRequest // 4. 调用 notificationManager.publish(request) } }步骤3.3 实现定时提醒定时提醒需要在日程开始前指定时间发送通知。请实现以下方法// 设置定时提醒 async setReminder( schedule: ScheduleEvent, minutesBefore: number 15 ): Promisenumber { // TODO: 计算提醒时间并设置定时通知 // 提示 // 1. 将schedule.startTime转为Date对象 // 2. 计算提醒时间 startTime - minutesBefore分钟 // 3. 如果提醒时间已过直接发送即时通知 // 4. 否则使用 notificationManager.publishScheduledNotification 设置定时通知 // 5. 返回通知ID用于后续取消 } // 取消提醒 async cancelReminder(notificationId: number): Promisevoid { // TODO: 调用 notificationManager.cancel(notificationId) }【提示】notificationManager.publishScheduledNotification需要传入触发时间戳毫秒。使用Date.getTime()获取时间戳。任务4 对话管理器【任务目标】实现多轮对话管理器维护对话历史、上下文状态和待确认操作。步骤4.1 定义对话管理器类// service/DialogManager.ets import { ChatMessage, DialogState, IntentType } from ../model/Types; import { MAX_HISTORY_LENGTH } from ../common/Constants; export class DialogManager { private state: DialogState { messages: [], pendingAction: null, contextScheduleId: null, lastIntent: null }; // 添加用户消息 addUserMessage(content: string): void { // TODO: 将用户消息添加到messages数组 // 提示构造ChatMessage对象roleuser // 注意如果消息数超过MAX_HISTORY_LENGTH移除最早的消息 } // 添加助手消息 addAssistantMessage(content: string): void { // TODO: 将助手消息添加到messages数组 // 提示构造ChatMessage对象roleassistant } }步骤4.2 实现上下文管理上下文管理是指代消解和多轮交互的关键。请实现以下方法// 设置上下文日程ID setContextScheduleId(id: string | null): void { // TODO: 更新state.contextScheduleId } // 设置待确认操作 setPendingAction(action: string | null): void { // TODO: 更新state.pendingAction } // 设置上一轮意图 setLastIntent(intent: IntentType | null): void { // TODO: 更新state.lastIntent } // 获取上下文日程ID getContextScheduleId(): string | null { // TODO: 返回state.contextScheduleId }步骤4.3 构建系统提示词系统提示词需要包含上下文信息帮助大模型理解指代关系// 获取系统提示词 getSystemPrompt(): string { // TODO: 构建包含上下文信息的系统提示词 // 提示 // 1. 基础提示词你是一个智能日程助手可以帮助用户创建、查询、修改和删除日程。 // 2. 如果有contextScheduleId添加当前上下文关联的日程ID为xxx // 3. 如果有pendingAction添加待确认操作为xxx // 4. 添加指代消解指引当用户使用指代词时使用上下文中的日程ID }步骤4.4 获取对话历史对话历史需要格式化为大模型能理解的格式// 获取对话历史供大模型使用 getHistoryForLLM(): Array{ role: string; content: string } { // TODO: 将messages数组转换为大模型API所需的格式 // 提示将ChatMessage数组映射为 {role, content} 数组 // 注意tool角色的消息可能需要特殊处理 }【提示】对话历史长度限制MAX_HISTORY_LENGTH10是为了控制Token消耗。每轮对话都会增加Token数量过长的历史会导致API调用变慢且费用增加。任务5 Agent核心服务【任务目标】实现AgentService集成云端大模型Function Calling能力完成意图理解、工具调用和结果生成的完整ReAct流程。步骤5.1 定义工具SchemaFunction Calling的核心是工具函数的定义。大模型根据这些定义决定调用哪个工具// service/AgentService.ets import { http } from kit.NetworkKit; import { ToolCall, AgentResponse, ScheduleEvent } from ../model/Types; import { LLM_API_URL, LLM_MODEL, API_KEY } from ../common/Constants; import { CalendarManager } from ./CalendarManager; import { ReminderService } from ./ReminderService; import { DialogManager } from ./DialogManager; export class AgentService { private calendarManager: CalendarManager new CalendarManager(); private reminderService: ReminderService new ReminderService(); private dialogManager: DialogManager new DialogManager(); // 工具函数定义 private static readonly TOOLS [ { name: createSchedule, description: 创建一条新日程。当用户想要新建、安排或设置日程时调用此工具。, parameters: { type: object, properties: { title: { type: string, description: 日程标题 }, startTime: { type: string, description: 开始时间格式YYYY-MM-DD HH:mm }, endTime: { type: string, description: 结束时间格式YYYY-MM-DD HH:mm }, location: { type: string, description: 地点可选 } }, required: [title, startTime] } }, { name: querySchedule, description: 查询日程。当用户想要查看、搜索或列出日程时调用此工具。, parameters: { type: object, properties: { startDate: { type: string, description: 查询开始日期 }, endDate: { type: string, description: 查询结束日期 } }, required: [startDate] } }, { name: updateSchedule, description: 修改已有日程。当用户想要修改、调整或变更日程时调用此工具。, parameters: { type: object, properties: { scheduleId: { type: string, description: 日程ID }, updates: { type: object, description: 修改的字段 } }, required: [scheduleId, updates] } }, { name: deleteSchedule, description: 删除日程。当用户想要取消或删除日程时调用此工具。, parameters: { type: object, properties: { scheduleId: { type: string, description: 日程ID } }, required: [scheduleId] } } ]; }【注意】工具的description字段非常重要大模型根据描述判断是否调用该工具。描述应清晰说明工具的用途和调用时机。步骤5.2 实现callLLM方法callLLM方法将用户输入、对话历史和工具定义发送给云端大模型// 调用大模型带Function Calling private async callLLM(input: string): Promisestring { // TODO: 构造API请求并发送 // 提示 // 1. 构造请求体 // - model: LLM_MODEL // - messages: [system_prompt, ...history, user_input] // - tools: AgentService.TOOLS (转换为API要求的格式) // - tool_choice: auto (让模型自动选择) // 2. 设置Header: Authorization: Bearer API_KEY // 3. 发送POST请求到LLM_API_URL // 4. 返回响应文本 }【提示】qwen-plus的Function Calling API格式在请求体中添加tools参数工具定义数组和tool_choice参数设为auto表示自动选择工具。响应中如果模型决定调用工具会包含tool_calls字段。步骤5.3 实现parseLLMResponse方法解析大模型响应判断是返回文本回复还是工具调用// 解析大模型响应 private parseLLMResponse(response: string): ToolCall | null { // TODO: 解析响应JSON判断是否包含工具调用 // 提示 // 1. 解析JSON响应 // 2. 检查是否有 tool_calls 字段 // 3. 如果有提取第一个tool_call的name和arguments // 4. 如果没有返回null表示模型直接回复文本 }步骤5.4 实现executeTool方法executeTool是工具执行分发器根据工具名调用对应的方法// 执行工具调用 private async executeTool(toolCall: ToolCall): PromiseAgentResponse { // TODO: 根据toolCall.name分发到对应的工具方法 // 提示使用switch语句 // case createSchedule: return this.toolCreateSchedule(toolCall.arguments) // case querySchedule: return this.toolQuerySchedule(toolCall.arguments) // case updateSchedule: return this.toolUpdateSchedule(toolCall.arguments) // case deleteSchedule: return this.toolDeleteSchedule(toolCall.arguments) // default: 返回错误信息 }步骤5.5 实现各工具方法请实现4个工具方法每个方法调用CalendarManager完成实际操作// 工具创建日程 private async toolCreateSchedule(args: Recordstring, string): PromiseAgentResponse { // TODO: // 1. 解析参数title, startTime, endTime, location // 2. 转换时间为Date对象 // 3. 调用 calendarManager.createSchedule() // 4. 设置提醒reminderService.setReminder() // 5. 更新上下文dialogManager.setContextScheduleId() // 6. 返回成功消息 } // 工具查询日程 private async toolQuerySchedule(args: Recordstring, string): PromiseAgentResponse { // TODO: // 1. 解析参数startDate, endDate // 2. 调用 calendarManager.querySchedules() // 3. 格式化查询结果为文本摘要 // 4. 返回查询结果 } // 工具修改日程 private async toolUpdateSchedule(args: Recordstring, string): PromiseAgentResponse { // TODO: // 1. 解析参数scheduleId, updates // 2. 调用 calendarManager.updateSchedule() // 3. 返回修改结果 } // 工具删除日程 private async toolDeleteSchedule(args: Recordstring, string): PromiseAgentResponse { // TODO: // 1. 解析参数scheduleId // 2. 调用 calendarManager.deleteSchedule() // 3. 清除上下文dialogManager.setContextScheduleId(null) // 4. 返回删除结果 }步骤5.6 实现handleUserInput主方法handleUserInput是Agent的入口方法串联完整的ReAct流程// 处理用户输入主入口 async handleUserInput(input: string): PromiseAgentResponse { // TODO: 实现完整的ReAct流程 // 提示 // 1. 将用户输入添加到对话历史dialogManager.addUserMessage(input) // 2. 调用大模型const response await this.callLLM(input) // 3. 解析响应const toolCall this.parseLLMResponse(response) // 4. 如果有工具调用 // a. 执行工具const result await this.executeTool(toolCall) // b. 将工具结果反馈给大模型await this.callLLMWithToolResults(input, toolCall, result) // c. 大模型基于工具结果生成最终回复 // 5. 如果没有工具调用直接返回大模型的文本回复 // 6. 将助手回复添加到对话历史dialogManager.addAssistantMessage(message) }【注意】步骤5.6是整个Agent的核心流程。如果工具执行后大模型需要再次生成回复需要实现callLLMWithToolResults方法将工具执行结果作为tool角色的消息发送给大模型。任务6 对话界面【任务目标】实现ChatPage对话界面支持消息气泡展示、文本输入和发送。步骤6.1 定义页面组件// pages/ChatPage.ets import { AgentService } from ../service/AgentService; import { ChatMessage } from ../model/Types; Entry Component struct ChatPage { State messages: ChatMessage[] []; State inputText: string ; State isLoading: boolean false; private agentService: AgentService new AgentService(); build() { Column() { // 消息列表区域 List({ space: 12 }) { ForEach(this.messages, (msg: ChatMessage) { // TODO: 根据msg.role渲染不同样式的消息气泡 }) } .layoutWeight(1) .padding(12) // 输入栏区域 // TODO: 实现输入框和发送按钮 } } }步骤6.2 实现消息气泡用户消息和助手消息使用不同的样式背景色、对齐方式// 消息气泡组件 Builder MessageBubble(msg: ChatMessage) { Row() { if (msg.role user) { // 用户消息右对齐蓝色背景 Column() { Text(msg.content) .fontSize(14) .fontColor(#FFFFFF) .padding(10) } .backgroundColor(#0070C0) .borderRadius(12) .constraintSize({ maxWidth: 75% }) .margin({ left: 80 }) } else { // 助手消息左对齐灰色背景 Column() { Text(msg.content) .fontSize(14) .fontColor(#333333) .padding(10) } .backgroundColor(#F0F0F0) .borderRadius(12) .constraintSize({ maxWidth: 75% }) .margin({ right: 80 }) } } .width(100%) }步骤6.3 实现输入栏与发送逻辑Builder InputBar() { Row({ space: 8 }) { TextInput({ text: this.inputText, placeholder: 输入消息... }) .layoutWeight(1) .height(40) .onChange((value: string) { this.inputText value; }) Button(发送) .height(40) .enabled(this.inputText.length 0 !this.isLoading) .onClick(() { this.sendMessage(); }) } .padding(8) } private async sendMessage(): Promisevoid { if (this.inputText.length 0 || this.isLoading) return; const userInput this.inputText; this.inputText ; // 添加用户消息到界面 this.messages.push({ role: user, content: userInput, timestamp: Date.now() }); // 显示加载状态 this.isLoading true; try { // 调用Agent处理 const response await this.agentService.handleUserInput(userInput); // 添加助手回复到界面 this.messages.push({ role: assistant, content: response.message, timestamp: Date.now() }); } catch (e) { this.messages.push({ role: assistant, content: 处理失败 e.message, timestamp: Date.now() }); } finally { this.isLoading false; } }【提示】使用isLoading状态控制加载指示器的显示。在等待大模型响应时可以显示正在思考…的加载动画。任务7 日程列表界面【任务目标】实现ScheduleListPage日程列表界面展示已有日程并支持查看详情。步骤7.1 定义列表页面// pages/ScheduleListPage.ets import { CalendarManager } from ../service/CalendarManager; import { ScheduleEvent } from ../model/Types; Entry Component struct ScheduleListPage { State schedules: ScheduleEvent[] []; State selectedDate: string ; private calendarManager: CalendarManager new CalendarManager(); aboutToAppear(): void { this.loadSchedules(); } private async loadSchedules(): Promisevoid { // TODO: 加载今日及未来7天的日程 // 提示 // 1. 计算今天和7天后的日期 // 2. 调用 calendarManager.querySchedules() // 3. 更新schedules状态 } build() { Column() { // 标题栏 Text(我的日程) .fontSize(20).fontWeight(FontWeight.Bold) .padding(16) // 日程列表 List({ space: 8 }) { ForEach(this.schedules, (schedule: ScheduleEvent) { // TODO: 渲染日程卡片 }) } .layoutWeight(1) .padding(12) } } }步骤7.2 实现日程卡片Builder ScheduleCard(schedule: ScheduleEvent) { Row({ space: 12 }) { // 时间列 Column() { Text(this.formatTime(schedule.startTime)) .fontSize(16).fontWeight(FontWeight.Bold) Text(this.formatTime(schedule.endTime)) .fontSize(12).fontColor(#999999) } .width(60) .alignItems(HorizontalAlign.Center) // 分隔线 Divider() .vertical(true) .height(40) // 内容列 Column() { Text(schedule.title) .fontSize(15).fontWeight(FontWeight.Medium) if (schedule.location) { Text(schedule.location) .fontSize(12).fontColor(#666666) .margin({ top: 4 }) } } .layoutWeight(1) .alignItems(HorizontalAlign.Start) // 提醒图标 if (schedule.reminderMinutes schedule.reminderMinutes 0) { Image($r(app.media.ic_reminder)) .width(20).height(20) } } .padding(12) .backgroundColor(#FFFFFF) .borderRadius(8) .shadow({ radius: 2, color: #10000000 }) } private formatTime(timeStr: string): string { // TODO: 将ISO时间字符串格式化为HH:mm const date new Date(timeStr); const h date.getHours().toString().padStart(2, 0); const m date.getMinutes().toString().padStart(2, 0); return h : m; }任务8 入口页面与配置【任务目标】实现IndexPage入口页面Tab布局、EntryAbility和module.json5权限配置。步骤8.1 实现Tab入口页面// pages/IndexPage.ets import { ChatPage } from ./ChatPage; import { ScheduleListPage } from ./ScheduleListPage; Entry Component struct IndexPage { State currentIndex: number 0; build() { Column() { TabBar({ currentIndex: this.currentIndex, onTabClick: (index: number) { this.currentIndex index; } }) if (this.currentIndex 0) { ChatPage() } else { ScheduleListPage() } } } } Component struct TabBar { Prop currentIndex: number; onTabClick: (index: number) void; build() { Row() { Button(对话) .layoutWeight(1) .backgroundColor(this.currentIndex 0 ? #0070C0 : #F0F0F0) .fontColor(this.currentIndex 0 ? #FFFFFF : #333333) .onClick(() this.onTabClick(0)) Button(日程) .layoutWeight(1) .backgroundColor(this.currentIndex 1 ? #0070C0 : #F0F0F0) .fontColor(this.currentIndex 1 ? #FFFFFF : #333333) .onClick(() this.onTabClick(1)) } .height(50) } }步骤8.2 配置module.json5权限在module.json5中声明所需权限// module.json5 { module: { name: entry, type: entry, srcEntry: ./ets/entryability/EntryAbility.ets, requestPermissions: [ { name: ohos.permission.INTERNET, reason: 调用云端大模型API, usedScene: {} }, { name: ohos.permission.READ_CALENDAR, reason: 读取日程信息, usedScene: {} }, { name: ohos.permission.WRITE_CALENDAR, reason: 创建和修改日程, usedScene: {} }, { name: ohos.permission.NOTIFICATION_CONTROLLER, reason: 发送日程提醒通知, usedScene: {} } ] } }步骤8.3 配置页面路由// resources/base/profile/main_pages.json{ src: [ pages/IndexPage, pages/ChatPage, pages/ScheduleListPage ] }【注意】READ_CALENDAR和WRITE_CALENDAR属于用户授权权限需要在运行时动态申请。请在EntryAbility的onWindowStageCreate中添加权限申请逻辑。任务9 功能测试【任务目标】对完成的智能日程Agent应用进行全面功能测试验证各功能正常工作。9.1 测试用例请按以下测试用例逐一验证应用功能9.2 测试方法1.启动应用进入对话页面。2.按T01-T08顺序输入测试用例中的文本。3.观察Agent的回复是否符合预期结果。4.切换到日程列表页面验证日程是否正确创建/修改/删除。5.在通过列填写测试结果通过/失败。9.3 常见问题排查任务10 优化与完善【任务目标】对应用进行性能优化和体验完善。10.1 延迟优化大模型API调用通常需要1-3秒在此期间应给用户明确的反馈// 在ChatPage中添加加载动画Builder LoadingIndicator() { if (this.isLoading) { Row({ space: 8 }) { LoadingProgress() .width(20).height(20) Text(Agent正在思考...) .fontSize(13).fontColor(#999999) } .padding(12) } }10.2 意图准确率优化如果大模型偶尔无法正确理解意图可以通过优化系统提示词提升准确率// 在DialogManager.getSystemPrompt()中添加few-shot示例 const FEW_SHOT 示例1 用户明天下午3点开会 意图创建日程title会议startTime明天15:00 示例2 用户这周有什么安排 意图查询日程startDate本周一endDate本周日 示例3 用户取消明天的会议 意图删除日程需要先查询确定scheduleId;10.3 用户体验优化消息列表自动滚动到最新消息。长按消息可复制内容。日程列表支持下拉刷新。添加空状态提示无日程时显示暂无日程。五、拓展任务选做以下拓展任务供学有余力的同学选做完成任意一项即可获得额外加分。拓展1 语音输入扩展在ChatPage中集成语音识别功能允许用户通过语音输入创建日程。要求使用AudioCapturer采集音频调用云端ASR服务识别文本自动填入输入框。拓展2 日程冲突智能解决当检测到时间冲突时Agent不仅提示冲突还主动建议替代时间。要求修改toolCreateSchedule方法在冲突时查询附近可用时间段并返回建议。拓展3 日程智能分析扩展Agent能力支持自然语言分析日程统计。例如用户输入这周开了多少会Agent调用querySchedule获取数据并统计回复。要求新增analyzeSchedule工具函数。拓展4 重复日程支持扩展createSchedule工具支持重复日程如每周三下午3点开周会。要求解析重复规则在日历API中设置recurrence字段。