HarmonyOS宠物邻里实战第18篇:小艺照护助手、本地知识库与智能体入口接入

摘要

宠物管理 App 做到档案、寄养、通知和社区之后,用户还会遇到一个很实际的问题:档案里已经有疫苗、驱虫、饮食、外出和清洁信息,但用户不知道下一步该记录什么、怎么补全、怎么把这些信息转成可执行的照护建议。宠物邻里项目里,我把这件事拆成两层:App 内置一个轻量本地智能体,保证没有平台配置也能回答基础问题;同时预留小艺开放平台智能体入口,后续写入 agentIdlaunchUri 后可以拉起平台侧“宠物照护助手”。

本文基于宠物邻里 HarmonyOS + ArkTS 项目,复盘小艺照护助手的工程实现:

  • XiaoyiKnowledgeDatabase 如何维护独立基础问答库;
  • XiaoyiPetAgentService 如何组合宠物档案和知识库;
  • XiaoyiAgentPage 如何渲染快捷问题、消息气泡和发送框;
  • XiaoyiAgentConfig 如何预留平台侧 agentIdlaunchUri 和知识库版本;
  • 本地智能体和平台智能体如何分层;
  • 如何设计匹配算法、兜底回答和能力边界;
  • 最后给出上线前验收清单。

这篇文章重点是“可落地的智能体接入”。不是一上来就追求复杂模型,而是先让 App 具备可解释、可测试、可回退的本地问答能力。

工程背景与源码定位

文件 作用
MyApp/entry/src/main/ets/pages/agent/XiaoyiAgentPage.ets 小艺智能体页面,负责消息、快捷问题和平台入口
MyApp/entry/src/main/ets/services/XiaoyiPetAgentService.ets 本地智能体服务,组合宠物档案和知识库回答
MyApp/entry/src/main/ets/services/XiaoyiKnowledgeDatabase.ets 独立基础问答知识库
MyApp/entry/src/main/ets/common/XiaoyiAgentConfig.ets 小艺开放平台配置
MyApp/docs/xiaoyi-agent.md 平台侧智能体搭建说明
MyApp/docs/xiaoyi-agent-knowledge-base.json 可导入平台的知识库材料
MyApp/entry/src/main/ets/common/MockStore.ets 本地宠物档案数据来源
MyApp/entry/src/main/ets/pages/home/HomeTab.ets 首页入口
MyApp/entry/src/main/ets/pages/user/SettingsPage.ets 设置页入口

环境信息:

项目
HarmonyOS 工程模型 Stage 模型
ArkTS 页面 NavDestination@State@Builder
target SDK 6.0.2(22)
本地知识库版本 pet-care-agent-v1
平台侧智能体名称 宠物照护助手
知识库名称 宠物基础照护问答库

版本兼容性要提前说明:本文的 common.UIAbilityContextWantstartAbilityNavDestinationContextTextArea.onChange@State 刷新行为基于当前工程验证。升级 HarmonyOS SDK 后,要重点回归页面生命周期、Want 拉起参数和输入框状态刷新。

宠物邻里小艺照护助手接入预览

一、为什么要先做本地智能体

很多项目接入智能体时会先依赖平台侧配置,但移动端项目有几个现实约束:

问题 影响
平台 agentId 还没审核 页面入口不能完全阻塞
真机、账号、地区、权限不稳定 开发阶段难以每次验证
知识库内容还在调整 直接上线容易回答不一致
用户宠物档案属于 App 本地业务数据 平台侧知识库不能直接替代用户上下文

所以更稳的做法是分层:

HarmonyOS 页面
  -> 本地智能体服务 XiaoyiPetAgentService
  -> 本地知识库 XiaoyiKnowledgeDatabase
  -> 平台配置 XiaoyiAgentConfig
  -> 可选拉起小艺开放平台智能体

本地智能体负责开发期和弱网场景可用,平台智能体负责后续扩展能力。

二、配置文件设计

平台侧配置集中在 XiaoyiAgentConfig.ets

export class XiaoyiAgentConfig {
  static readonly agentId: string = '';
  static readonly launchUri: string = '';
  static readonly agentName: string = '宠物照护助手';
  static readonly knowledgeBaseName: string = '宠物基础照护问答库';
  static readonly promptVersion: string = 'pet-care-agent-v1';
  static readonly queryText: string = '帮我查看宠物档案、照护提醒和日常照护记录';
  static readonly source: string = 'pet-care-app';

  static isConfigured(): boolean {
    return XiaoyiAgentConfig.agentId.trim().length > 0;
  }

  static canLaunch(): boolean {
    return XiaoyiAgentConfig.launchUri.trim().length > 0;
  }
}

这里把平台参数和业务文案拆开:

字段 含义
agentId 小艺开放平台创建智能体后的 ID
launchUri 平台提供的测试或发布启动链接
agentName 页面展示名称
knowledgeBaseName 平台侧知识库名称
promptVersion 知识库和提示词版本
queryText 拉起时传给智能体的默认问题
source 来源标记,便于后续统计

配置文件的好处是页面不用硬编码平台参数。没有配置时,本地智能体照常可用;配置完成后再打开平台入口。

三、页面状态判断

XiaoyiAgentPage 进入页面后先刷新状态:

private refreshStatus(): void {
  if (!XiaoyiAgentConfig.isConfigured()) {
    this.statusText = '本地智能体已可用;小艺开放平台的 agentId 尚未写入配置。';
    return;
  }
  if (!XiaoyiAgentConfig.canLaunch()) {
    this.statusText = '本地智能体已可用;agentId 已配置,等待写入小艺开放平台提供的测试/发布启动链接。';
    return;
  }
  this.statusText = '本地智能体已可用,也可在支持小艺能力的真机上拉起官网智能体。';
}

这段判断覆盖三个阶段:

阶段 页面状态
未配置 agentId 本地智能体可用,平台入口待配置
已配置 agentId 但无 launchUri 本地智能体可用,平台入口待链接
已配置 launchUri 本地智能体和平台入口都可测试

开发期最怕页面直接显示“不可用”。这里用状态说明把能力边界讲清楚,用户仍然能使用本地问答。

四、本地智能体启动

页面初始化:

aboutToAppear(): void {
  this.refreshStatus();
  this.bootstrapAgent();
}

启动本地智能体:

private bootstrapAgent(): void {
  const pets: Pet[] = MockStore.petsOf(MockStore.meId);
  this.knowledgeText = XiaoyiPetAgentService.knowledgeSummary();
  const welcome: XiaoyiAgentMessage = new XiaoyiAgentMessage();
  welcome.id = 'welcome';
  welcome.role = 'agent';
  welcome.text = XiaoyiPetAgentService.welcome(pets);
  this.messages = [welcome];
  this.quickQuestions = XiaoyiPetAgentService.quickQuestions(pets);
}

这里读了两类数据:

数据 来源 用途
宠物档案 MockStore.petsOf(MockStore.meId) 个性化回答、档案检查、提醒建议
知识库摘要 XiaoyiKnowledgeDatabase 展示当前问答库规模和覆盖分类

如果用户还没有宠物档案,智能体仍然可以回答通用养宠问题;如果已有宠物档案,就能生成更贴合的档案建议。

五、消息模型

本地消息模型很轻:

export class XiaoyiAgentMessage {
  id: string = '';
  role: string = 'agent';
  text: string = '';
}

快捷问题模型:

export class XiaoyiQuickQuestion {
  title: string = '';
  question: string = '';
}

这两个模型不依赖平台 SDK,页面可以直接渲染。后续如果接入真正的流式回复,也可以扩展 statuscreatedAterrorTexttraceId 等字段。

六、欢迎语

欢迎语根据宠物档案数量变化:

static welcome(pets: Pet[]): string {
  if (pets.length === 0) {
    return '你好,我是宠物照护助手。你可以先添加宠物档案,我会帮你梳理疫苗、驱虫、饮食、洗护和日常提醒要点;也可以直接问我宠物喂养、清洁、外出和新手照护的基础问题。';
  }
  return '你好,我是宠物照护助手。当前已读取到 ' + pets.length.toString() +
    ' 只宠物档案,可以帮你检查待完善信息、生成日常照护提醒,也能回答基础养宠问题。';
}

这一步看似只是文案,其实是在建立能力边界:

  • 没有档案时,引导用户先添加档案;
  • 有档案时,强调可以做档案检查和提醒;
  • 两种情况下都支持基础问答。

智能体页面不能只说“你想问什么”,要告诉用户当前能做什么。

七、快捷问题设计

快捷问题由服务层生成:

static quickQuestions(pets: Pet[]): XiaoyiQuickQuestion[] {
  const items: XiaoyiQuickQuestion[] = [];
  if (pets.length > 0) {
    items.push(XiaoyiPetAgentService.quick(pets[0].name + '建议', '根据' + pets[0].name + '的档案给我照护建议'));
  }
  items.push(XiaoyiPetAgentService.quick('档案检查', '帮我检查宠物档案还缺哪些照护信息'));
  items.push(XiaoyiPetAgentService.quick('今日提醒', '今天有哪些宠物照护事项需要记录'));
  items.push(XiaoyiPetAgentService.quick('饮食清洁', '帮我整理饮食和清洁方面的记录建议'));
  items.push(XiaoyiPetAgentService.quick('新手准备', '新手养宠需要准备什么'));
  items.push(XiaoyiPetAgentService.quick('疫苗记录', '疫苗记录应该怎么填'));
  return items;
}

页面渲染:

@Builder QuickQuestionBar() {
  Scroll() {
    Row() {
      ForEach(this.quickQuestions, (item: XiaoyiQuickQuestion) => {
        Text(item.title)
          .fontSize(FontSize.sm)
          .fontColor($r('app.color.brand_primary'))
          .fontWeight(FontWeight.Medium)
          .padding({ left: Spacing.md, right: Spacing.md, top: 9, bottom: 9 })
          .backgroundColor($r('app.color.brand_tint'))
          .borderRadius(Radius.pill)
          .margin({ right: Spacing.sm })
          .onClick(() => { this.ask(item.question); })
      }, (item: XiaoyiQuickQuestion) => item.title)
    }
    .padding({ top: Spacing.sm, bottom: Spacing.sm })
  }
  .scrollable(ScrollDirection.Horizontal)
  .scrollBar(BarState.Off)
  .width('100%')
}

快捷问题不是装饰,它能降低用户试错成本。尤其是智能体刚上线时,用户不知道怎么问,推荐问题能把用户带到已验证路径。

八、发送消息流程

发送逻辑:

private ask(question: string): void {
  const clean: string = question.trim();
  if (clean.length === 0) {
    return;
  }
  const user: XiaoyiAgentMessage = new XiaoyiAgentMessage();
  user.id = 'u_' + Date.now().toString();
  user.role = 'user';
  user.text = clean;

  const answer: XiaoyiAgentMessage = new XiaoyiAgentMessage();
  answer.id = 'a_' + Date.now().toString();
  answer.role = 'agent';
  answer.text = XiaoyiPetAgentService.reply(clean, MockStore.petsOf(MockStore.meId));
  this.messages = this.messages.concat([user, answer]);
  this.draft = '';
}

这里有三个细节:

  1. trim() 后空内容不发送;
  2. 用户消息和智能体回答都追加到 messages
  3. 回答时实时读取 MockStore.petsOf(MockStore.meId),避免宠物档案刷新后仍用旧数据。

本地智能体不是异步请求,所以页面体验会很快。后续接平台接口时,可以把回答部分改成异步 Promise。

九、消息气泡

消息气泡根据角色左右分布:

@Builder MessageBubble(message: XiaoyiAgentMessage) {
  Row() {
    if (message.role === 'user') {
      Blank().layoutWeight(1)
    }
    Text(message.text)
      .fontSize(FontSize.sm)
      .fontColor(message.role === 'user' ? $r('app.color.text_on_primary') : $r('app.color.text_primary'))
      .lineHeight(21)
      .padding(Spacing.md)
      .constraintSize({ maxWidth: '82%' })
      .backgroundColor(message.role === 'user' ? $r('app.color.brand_primary') : $r('app.color.surface_card'))
      .borderRadius(Radius.lg)
    if (message.role !== 'user') {
      Blank().layoutWeight(1)
    }
  }
  .width('100%')
  .margin({ top: Spacing.sm })
}

最大宽度限制为 82%,可以避免长文本气泡挤满整行。回答内容支持换行,因为 Text 会按字符串里的换行展示。

十、输入框与操作区

输入框:

TextArea({ placeholder: '问我宠物档案、提醒、饮食、清洁、外出或基础养宠问题', text: this.draft })
  .height(86)
  .maxLength(500)
  .fontSize(FontSize.md)
  .placeholderColor($r('app.color.text_placeholder'))
  .backgroundColor($r('app.color.surface_alt'))
  .borderRadius(Radius.md)
  .margin({ top: Spacing.md })
  .onChange((value: string) => { this.draft = value; })

按钮区:

Row() {
  SecondaryButton({
    label: '刷新档案',
    onTap: () => { this.bootstrapAgent(); }
  })
    .layoutWeight(1)
  Blank().width(Spacing.md)
  PrimaryButton({
    label: '发送',
    onTap: () => { this.ask(this.draft); }
  })
    .layoutWeight(1)
}

刷新档案 很重要。宠物档案可能在其他页面被修改,用户回到智能体页后可以手动刷新上下文。

十一、本地知识库结构

知识库条目模型:

export class XiaoyiKnowledgeEntry {
  id: string = '';
  category: string = '';
  question: string = '';
  answer: string = '';
  keywords: string[] = [];
}

每条知识包含:

字段 作用
id 稳定编号,便于导入平台
category 分类,例如新手照护、喂养、清洁
question 标准问题
answer 标准回答
keywords 匹配关键词

知识库初始化:

private static readonly records: XiaoyiKnowledgeEntry[] = XiaoyiKnowledgeDatabase.seed();

这说明当前版本是内置静态知识库,不依赖网络。上线前可以把同样数据导出为 JSON,导入小艺开放平台知识库。

十二、知识库统计

统计条数:

static count(): number {
  return XiaoyiKnowledgeDatabase.records.length;
}

统计分类:

static categories(): string[] {
  const out: string[] = [];
  for (let i = 0; i < XiaoyiKnowledgeDatabase.records.length; i++) {
    const category: string = XiaoyiKnowledgeDatabase.records[i].category;
    if (out.indexOf(category) < 0) {
      out.push(category);
    }
  }
  return out;
}

服务层展示摘要:

static knowledgeSummary(): string {
  return '独立知识库已加载 ' + XiaoyiKnowledgeDatabase.count().toString() +
    ' 条基础问答,覆盖:' + XiaoyiKnowledgeDatabase.categories().join('、') + '。';
}

用户能看到知识库已加载多少条、覆盖哪些分类,这比黑盒式回答更可信。

十三、匹配算法

搜索入口:

static search(question: string): XiaoyiKnowledgeEntry | null {
  const text: string = question.trim();
  if (text.length === 0) {
    return null;
  }
  let best: XiaoyiKnowledgeEntry | null = null;
  let bestScore: number = 0;
  for (let i = 0; i < XiaoyiKnowledgeDatabase.records.length; i++) {
    const record: XiaoyiKnowledgeEntry = XiaoyiKnowledgeDatabase.records[i];
    const score: number = XiaoyiKnowledgeDatabase.score(record, text);
    if (score > bestScore) {
      bestScore = score;
      best = record;
    }
  }
  return bestScore >= 2 ? best : null;
}

计分逻辑:

private static score(record: XiaoyiKnowledgeEntry, question: string): number {
  let value: number = 0;
  if (question.includes(record.question)) {
    value += 5;
  }
  for (let i = 0; i < record.keywords.length; i++) {
    if (question.includes(record.keywords[i])) {
      value += 2;
    }
  }
  if (question.includes(record.category)) {
    value += 1;
  }
  return value;
}

当前算法很朴素,但有两个优点:

  • 结果可解释,命中哪个关键词能看出来;
  • 失败可兜底,不会胡乱返回不相关答案。

bestScore >= 2 意味着至少命中一个关键词才返回知识库答案。

十四、知识库问答示例

知识库里的新手照护条目:

XiaoyiKnowledgeDatabase.item(
  'kb_pet_001',
  '新手照护',
  '新手养宠需要准备什么',
  '新手养宠可以先准备四类内容:1. 基础用品:食盆、水碗、牵引或猫砂盆、清洁用品。2. 档案信息:品种、年龄、体重、疫苗和驱虫记录。3. 日常规则:固定喂食、清洁、外出和休息区域。4. 观察记录:饮食、排便、精神状态和体重变化。建议把这些内容同步记录到宠物档案里,后续提醒会更准确。',
  ['新手', '准备', '用品', '刚养', '养宠']
)

疫苗记录条目:

XiaoyiKnowledgeDatabase.item(
  'kb_pet_007',
  '疫苗',
  '疫苗记录应该怎么填',
  '疫苗记录建议包含接种日期、疫苗类型、接种机构和下次建议时间。若不确定历史记录,可以先写“待确认”,再根据疫苗本或机构记录补全。完整记录有助于后续生成更清楚的照护提醒。',
  ['疫苗', '接种', '疫苗本', '记录']
)

这些回答都避免给出医疗诊断,只做日常记录建议。智能体的边界要保守,特别是宠物健康类问题,不能替代专业机构。

十五、回答路由

服务层的 reply 先处理业务上下文,再查知识库:

static reply(question: string, pets: Pet[]): string {
  const text: string = question.trim();
  if (text.length === 0) {
    return '可以直接问我宠物档案、照护提醒、饮食、清洁或外出习惯相关问题。';
  }
  if (text.includes('档案') || text.includes('缺') || text.includes('补全')) {
    return XiaoyiPetAgentService.profileAnswer(pets);
  }
  if (text.includes('提醒') || text.includes('今天')) {
    return XiaoyiPetAgentService.reminderAnswer(pets);
  }
  const knowledgeAnswer: string = XiaoyiKnowledgeDatabase.answer(text);
  if (knowledgeAnswer.length > 0) {
    return knowledgeAnswer;
  }
  if (text.includes('疫苗') || text.includes('驱虫')) {
    return XiaoyiPetAgentService.reminderAnswer(pets);
  }
  if (text.includes('饮食') || text.includes('清洁') || text.includes('洗') || text.includes('外出') || text.includes('遛')) {
    return XiaoyiPetAgentService.dailyCareAnswer(pets);
  }
  return XiaoyiPetAgentService.generalAnswer(pets);
}

顺序很关键:

  1. 空问题先返回输入提示;
  2. 档案类问题优先走宠物档案;
  3. 今日提醒优先走提醒模板;
  4. 再查独立知识库;
  5. 疫苗、驱虫、饮食、清洁做兜底;
  6. 最后返回通用能力说明。

这样可以避免“档案检查”被知识库里的通用词误命中。

十六、档案检查回答

档案检查逻辑:

private static profileAnswer(pets: Pet[]): string {
  if (pets.length === 0) {
    return '目前还没有宠物档案。建议先添加:昵称、品种、年龄、体重、疫苗记录、驱虫记录、饮食习惯、外出习惯、清洁习惯和注意事项。';
  }
  let answer: string = '我检查了当前宠物档案,建议优先补全这些信息:';
  for (let i = 0; i < pets.length; i++) {
    const pet: Pet = pets[i];
    const missing: string[] = [];
    if (pet.vaccineRecord.trim().length === 0) missing.push('疫苗记录');
    if (pet.dewormRecord.trim().length === 0) missing.push('驱虫记录');
    if (pet.dietHabit.trim().length === 0) missing.push('饮食习惯');
    if (pet.walkHabit.trim().length === 0) missing.push('外出习惯');
    if (pet.bathHabit.trim().length === 0) missing.push('清洁习惯');
    if (pet.notes.trim().length === 0) missing.push('注意事项');
    answer += '\n' + pet.name + ':' + (missing.length === 0 ? '档案较完整,可继续记录日常变化。' : missing.join('、'));
  }
  return answer;
}

这段逻辑利用了 App 已有数据,而不是只返回通用建议。用户问“档案还缺什么”,回答会列出每只宠物缺少的字段。

十七、今日提醒回答

提醒回答:

private static reminderAnswer(pets: Pet[]): string {
  if (pets.length === 0) {
    return '添加宠物档案后,我会按疫苗、驱虫、饮食和注意事项生成提醒。现在可以先准备最近一次疫苗时间、驱虫周期和常用主粮信息。';
  }
  return '今日建议记录三类事项:\n' +
    '1. 基础照护:饮水、喂食、排便、外出牵引和清洁状态。\n' +
    '2. 定期事项:疫苗记录、驱虫记录、洗护周期和体重变化。\n' +
    '3. 档案更新:体重、照片、饮食变化和临时注意事项。';
}

这里没有尝试生成真正的日历提醒,而是先给出记录建议。后续可以接入本地通知或日程能力,把提醒从文本建议升级成可执行任务。

十八、日常照护回答

日常照护:

private static dailyCareAnswer(pets: Pet[]): string {
  const petName: string = pets.length > 0 ? pets[0].name : '宠物';
  return petName + ' 的日常记录可以按这几项整理:\n' +
    '1. 饮食:主粮品牌、单次喂食量、喂食时间和饮水情况。\n' +
    '2. 外出:外出时间、牵引状态、排便情况和回家清洁。\n' +
    '3. 清洁:洗澡、梳毛、耳部和爪部清洁周期。\n' +
    '4. 备注:当天特殊变化和下次需要提醒自己的事项。';
}

有宠物档案时会带上宠物名,没有档案时用“宠物”兜底。这样回答不会因为缺少数据而中断。

十九、平台入口

页面里的平台入口:

@Builder PlatformEntry() {
  Column() {
    Row() {
      Text(XiaoyiAgentConfig.agentName)
        .fontSize(FontSize.lg)
        .fontWeight(FontWeight.Medium)
        .fontColor($r('app.color.text_primary'))
        .layoutWeight(1)
      Tag({
        text: XiaoyiAgentConfig.canLaunch() ? '可测试' : (XiaoyiAgentConfig.isConfigured() ? '待链接' : '待配置'),
        tone: XiaoyiAgentConfig.canLaunch() ? TagTone.Success : TagTone.Info
      })
    }
    .width('100%')
  }
}

根据配置状态展示:

配置状态 标签
launchUri 存在 可测试
只有 agentId 待链接
都没有 待配置

这样测试人员不用打开代码,也能从页面看出当前配置阶段。

二十、拉起小艺智能体

拉起逻辑:

private launchAgent(): void {
  if (!XiaoyiAgentConfig.canLaunch()) {
    this.statusText = '请先在 XiaoyiAgentConfig.ets 中写入小艺平台提供的 launchUri。';
    return;
  }
  const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  const want: Want = {
    action: 'ohos.want.action.viewData',
    uri: XiaoyiAgentConfig.launchUri,
    parameters: {
      agentId: XiaoyiAgentConfig.agentId,
      queryText: XiaoyiAgentConfig.queryText,
      source: XiaoyiAgentConfig.source
    }
  };
  context.startAbility(want)
    .then(() => {
      this.statusText = '已请求拉起小艺智能体。';
    })
    .catch(() => {
      this.statusText = '拉起失败,请确认 launchUri、应用关联、系统版本和当前账号测试权限。';
    });
}

这里要检查四件事:

  • launchUri 是否来自平台配置;
  • agentId 是否和平台侧智能体一致;
  • 应用包名和签名是否已经关联;
  • 真机系统版本和账号是否有测试权限。

失败时不要只显示“启动失败”,要提示排查方向。

二十一、平台侧智能体配置

docs/xiaoyi-agent.md 中定义了平台侧信息:

项目 内容
智能体名称 宠物照护助手
一句话简介 帮助用户管理宠物档案、日常照护提醒、饮食、清洁、外出习惯记录,并回答基础养宠问题
服务对象 宠物主人
知识库名称 宠物基础照护问答库
知识库版本 pet-care-agent-v1
能力边界 不扩展到当前应用以外的业务场景

平台侧提示词可以保持保守:

你是“宠物照护助手”,服务于一款宠物管理 HarmonyOS 应用。
回答时先确认宠物种类、年龄、体重、疫苗/驱虫记录和当前日常照护场景。
对喂养、外出、洗护、疫苗、驱虫和档案补全问题,给出清晰的步骤化建议。
当用户信息不足时,引导用户回到应用补全宠物档案。
保护隐私,不要求用户公开身份证号、详细住址、电话等敏感信息。
基础养宠问答优先参考“宠物基础照护问答库”,不要编造平台知识库之外的专业结论。

智能体能力边界要写进配置文档,而不是只靠开发口头约定。

二十二、知识库同步到平台

当前 App 侧知识库文件是:

entry/src/main/ets/services/XiaoyiKnowledgeDatabase.ets

平台侧导入材料是:

docs/xiaoyi-agent-knowledge-base.json

同步规则:

App 字段 平台字段
id 记录编号
category 分类
question 问题
answer 答案
keywords 关键词

建议每次更新知识库后同步修改 promptVersion 或知识库版本号。例如:

static readonly promptVersion: string = 'pet-care-agent-v1';

后续如果新增医疗免责声明、年龄分层喂养或多宠家庭规则,可以升级成 pet-care-agent-v2

二十三、真机验证流程

真机验证建议按下面步骤:

  1. 使用与小艺开放平台配置一致的应用包名、签名证书和发布渠道构建应用;
  2. 在支持小艺智能体能力的 HarmonyOS 真机上安装;
  3. 登录具备测试权限的华为账号;
  4. 打开“我的 -> 设置 -> 小艺智能体”;
  5. 确认本地智能体能回答“新手养宠需要准备什么”;
  6. 写入 agentId 后确认页面状态变为“待链接”;
  7. 写入 launchUri 后确认页面状态变为“可测试”;
  8. 点击“打开官网小艺智能体”,验证是否拉起成功;
  9. 失败时检查应用关联、签名、系统版本、地区和账号测试权限。

这套流程可以给测试同学直接使用。

二十四、本地测试用例

本地智能体可以用轻量断言测试:

const emptyPets: Pet[] = [];
const welcome = XiaoyiPetAgentService.welcome(emptyPets);
assert(welcome.includes('先添加宠物档案'));

const answer = XiaoyiPetAgentService.reply('新手养宠需要准备什么', emptyPets);
assert(answer.includes('基础用品'));

const vaccine = XiaoyiPetAgentService.reply('疫苗记录应该怎么填', emptyPets);
assert(vaccine.includes('接种日期'));

有宠物档案时:

const pets: Pet[] = [mockPetWithoutCareFields()];
const profile = XiaoyiPetAgentService.reply('帮我检查宠物档案还缺哪些照护信息', pets);
assert(profile.includes('疫苗记录'));
assert(profile.includes('驱虫记录'));
assert(profile.includes('饮食习惯'));

匹配不到知识库时:

const fallback = XiaoyiPetAgentService.reply('这个问题暂时不在知识库里', emptyPets);
assert(fallback.includes('宠物档案'));
assert(fallback.includes('知识库'));

二十五、常见问题排查

现象 可能原因 排查方式
页面显示待配置 agentId 为空 检查 XiaoyiAgentConfig.agentId
页面显示待链接 只填了 agentId,没填 launchUri 检查平台启动链接
点击平台入口失败 应用关联、签名、账号或系统版本不满足 statusText 提示
快捷问题不显示宠物名 当前用户没有宠物档案 MockStore.petsOf(MockStore.meId)
档案检查没有缺项 宠物档案字段都已填写 清空一个字段再测试
通用问答答非所问 关键词过宽或分类命中错误 调整 keywords 和阈值
知识库摘要为空 seed() 没返回记录 检查 records 初始化
平台回答和本地回答不一致 JSON 知识库没有同步 对比 XiaoyiKnowledgeDatabase 和平台导入材料
页面输入后没清空 this.draft = '' 没执行 检查 ask()
长回答显示拥挤 气泡宽度或行高不合理 检查 constraintSizelineHeight

二十六、上线前验收清单

  • 未配置 agentId 时,本地智能体仍然可用;
  • 写入 agentId 后,页面状态显示待链接;
  • 写入 launchUri 后,页面状态显示可测试;
  • “刷新档案”能重新读取当前用户宠物档案;
  • 没有宠物档案时,欢迎语引导添加档案;
  • 有宠物档案时,欢迎语显示档案数量;
  • 快捷问题至少包含档案检查、今日提醒、饮食清洁、新手准备、疫苗记录;
  • 点击快捷问题能自动追加用户消息和智能体回答;
  • 空输入不会发送;
  • 发送后输入框清空;
  • “新手养宠需要准备什么”命中知识库;
  • “疫苗记录应该怎么填”命中知识库;
  • “帮我检查宠物档案还缺哪些照护信息”走档案检查逻辑;
  • 知识库摘要显示条数和分类;
  • 平台拉起失败时给出排查提示;
  • 平台侧知识库版本和 XiaoyiAgentConfig.promptVersion 保持一致;
  • 平台导入 JSON 和 App 内置知识库内容保持同步;
  • 不要求用户输入身份证号、详细住址、电话等敏感信息;
  • 回答不扩展到当前应用以外的业务场景;
  • 真机验证覆盖包名、签名、系统版本、账号权限和平台应用关联。

总结

小艺照护助手不是简单加一个聊天页。宠物邻里项目把它拆成了本地智能体、本地知识库、宠物档案上下文和平台入口四层:本地能力保证开发期和未配置平台时仍然可用,知识库保证回答可解释,宠物档案让建议和用户数据相关,平台入口则为后续小艺开放平台接入留下扩展点。

这种分层方式适合大多数 HarmonyOS 应用。先把能验证的本地闭环做好,再把稳定的知识库和提示词同步到平台,最后通过 agentIdlaunchUri、包名签名和真机账号完成平台拉起验证。这样智能体功能不会停留在演示,而是能真正进入项目的工程质量体系。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐