开发交接文档

项目:HarmonyOS NEXT AI 智能体助手

仓库:https://atomgit.com/2501_93461085/talk

分支:main

文档状态:2026-07-28,与当前功能工作区同步;提交后以 main 最新提交为准

这份文档面向第一次接手项目的开发者。产品介绍和用户操作请先读 README,隐私边界请读 隐私与安全,真机验证请按 验收清单 执行,原生依赖及许可证见 第三方组件说明。

1. 项目目标

本项目是一款 HarmonyOS NEXT 原生 AI 智能体应用:

  • 支持用户自定义 OpenAI 兼容模型供应商。
  • 支持流式对话、工具调用、长期记忆和角色卡。
  • 支持经过明确授权的有限无障碍自动化。
  • 业务数据优先保存在本地,API Key 使用 HUKS 加密。

项目不是服务端应用,不随 HAP 打包模型权重,也不提供默认 API Key、云端账号系统或远程数据同步。应用提供人工核对的公开 GGUF 下载目录,只有用户主动选择后才下载到应用私有目录。

2. 当前技术基线

项目 当前值
语言与 UI ArkTS 严格模式、ArkUI
应用模型 Stage 模型
系统目标 HarmonyOS 6.1.1 / API 24
Bundle name com.example.talk
应用版本 0.1.0 / versionCode 1000000
数据库 RelationalStore,业务库版本 v5
向量检索 独立向量库,按真实 Embedding 维度初始化
模型协议 OpenAI 兼容 Chat Completions / Embeddings + 本地 GGUF
本地推理 llama.cpp 固定提交、N-API、ARM64 CPU
安全插件原型 WAMR 2.4.5 固定提交、经典解释器、无 WASI/AOT/JIT
页面 Chat、Settings、ModelConfig、EmbeddingConfig、Character、Memory、Toolbox、ToolRuntime、Alarm、Automation
工具 18 个内置工具 + 3 个自动化工具,共 21 个
UI 基线 AppTheme 语义令牌 + 根 Stack 应用内弹层 + 系统模糊

必须使用安装了 API 24 SDK 的 DevEco Studio。JDK、Node.js 和 Hvigor 优先使用 DevEco Studio 自带版本,避免版本错配。

首次从干净缓存构建原生模块时会下载 entry/src/main/cpp/CMakeLists.txt 固定提交的 llama.cpp 和 WAMR;构建目标目前仅为 arm64-v8a。离线构建必须预先保留对应 FetchContent 缓存,不能自动漂移到其他提交。

3. 当前完成情况

聊天与模型

  • OpenAI 兼容 Base URL 规范化。
  • HUKS AES-256-GCM 加密 API Key。
  • 冷启动检查并复用 HUKS 主密钥,HUKS 操作有 5 秒超时;密钥失效时明确要求重新填写,不以明文兜底。
  • DeepSeek、OpenAI、通义千问、Moonshot 快捷配置,以及自定义 OpenAI 兼容配置。
  • 内置 GGUF 下载页:可信目录、流式落盘、下载进度/取消、大小与 SHA-256 校验、失败清理、自动创建配置和设为默认。
  • GGUF 系统文件选择入口继续保留,支持文件头校验、应用私有目录复制、推理参数编辑和本地生成测试。
  • ModelAdapterFactory 在 OpenAI 网络适配器与本地 llama.cpp 适配器之间切换。
  • 本地模型支持聊天模板、逐 token 流式输出、停止生成和文本工具协议,不要求 API Key。
  • DevEco debug HAP 继续保留调试符号,但 llama、ggml、ggml-base 和 ggml-cpu 推理核心必须在 CMake 中显式追加 -O3;否则工具链注入的 -O0 会让轻量模型也长期没有首 token。
  • 本地生成取消同步通知原生停止标记;页面退后台或锁屏时停止本地推理,避免恢复后遗留忙状态。
  • 本地模型使用 LocalToolSelector 按当前用户输入裁剪工具 schema;同一份清单必须继续传给执行授权检查,不能只缩短提示词却允许模型执行本轮未暴露的工具。
  • ToolCallFallback 对显式 <tool_call> 块做受限兼容:允许回复末尾漏结束标签、已知时间字段数字前导零、对象后少量闭合符号、最多两个尾随 },并展开唯一的 arguments.parameters 包装。Gemma 另只接受完整 tool_code 围栏中的单个 工具名() 或 工具名(JSON 对象);不得扩展为解析标签外普通 JSON、普通代码块或任意表达式,且解析结果必须继续受本轮工具白名单约束。
  • LocalThreadPolicy 使用原生 sched_getaffinity 返回的应用可用处理单元数;未测速时自动留一个处理单元给系统,已测速结果仅在 tunedForProcessors 与当前数量一致时复用。手动模式仍接受 1–16。
  • LocalThreadTuner 使用同一模型、提示词和上下文,对三个候选线程正序/倒序各测一次并按平均 elapsedMs 选择;测速结束必须卸载临时上下文,确保聊天按正式配置重新加载。
  • 模型新增、编辑、删除、默认项和连通性测试;快捷配置只要求供应商、API Key 和模型。
  • 聊天页模型选择。
  • SSE 流式解析,支持 UTF-8 跨分块、CRLF、data: 变体。

麒麟真机 Qwen3.5 0.8B 当前基准:普通“你好”首 token 7574 ms、总耗时 12914 ms;“现在几点”完整工具回路包含两轮推理,分别耗时 8909 ms 和 12774 ms,工具卡片及最终回复均正常结束。真机数据会受对话历史、温度和系统负载影响,回归时重点检查不能重新出现全量工具注入或永久“正在生成”。

Mate X7 线程调优基准:应用 affinity 可用 9 个处理单元,220-token 固定输入正反各测一次后的平均值为 6 线程 3652 ms、8 线程 3810 ms、9 线程 3714 ms,因此当前 Qwen3.5 0.8B 保存为 6 线程“实测最优”。后续更换模型、系统调度策略或设备后应重新测速,不要仅按物理核心数直接覆盖。

  • 取消、60 秒单轮超时、有限重试和网络资源释放。
  • 已产生内容后不重试,避免重复回复或重复工具调用。
  • 中断时持久化部分回复,并标记“已停止”或“生成中断”。

对话与界面

  • 对话历史抽屉、搜索、新建、重命名、置顶和删除。
  • 历史操作使用锚点菜单,避免列表内操作按钮点击后没有可见反馈。
  • 从左侧向右滑打开,全屏向左滑关闭。
  • 抽屉开合使用线性动画。
  • Chat 与 Settings 共用窗口管理器;页面切换后重新同步全屏和上下安全区。
  • 首页顶栏提供角色选择:默认助手、角色卡列表、创建/管理角色;切换角色时创建新对话。
  • 页面统一使用 AppTheme 的颜色、圆角、触控尺寸与安全区基线。
  • 角色、模型、记忆、白名单、备份和危险确认采用应用内弹层,不再使用 bindSheet。
  • 弹层使用系统模糊和半透明表面;返回键优先关闭弹层,危险确认除外。
  • 生成期间阻止重复发送、模型切换、历史删除和进入破坏性设置流程。

工具系统

  • 三级风险:READONLY / WRITE / DANGEROUS。
  • 危险工具确认与可撤销白名单。
  • 工具启停持久化,并在模型工具清单和运行时双重拦截。
  • 云端严格模式只接受原生 tool_calls;本地 llama.cpp 没有原生函数调用通道,使用受限 <tool_call> 协议,并兼容 Gemma 的受限 tool_code 围栏调用。
  • Qwen3.5 0.8B 真机可能漏结束标签、输出 minute:00 或在 JSON 后追加闭合符号;兼容解析只在显式工具标签内部修复。重复闹钟还必须用用户原句校正星期、时间和标题,不能直接信任小模型参数。
  • 工具参数在调度前统一做 JSON、长度、必填项和基础类型校验。
  • 同批纯只读调用并行执行;含写入或危险操作时保持顺序。
  • 连续重复工具批次会先拒绝,持续重复则终止循环。
  • create_reminder 按系统错误码区分通知关闭(1700001)、数量超限(1700002)和权限拒绝(201)。数量超限时仅取消当前应用中过期、非重复的日历提醒并重试一次;禁止为腾出配额静默删除未来提醒或循环提醒。
  • 系统级提醒池也会返回 1700002。如果 Talk 查询到 0 条有效提醒,先调用当前应用作用域的 cancelAllReminders 重置残留状态并重试;仍失败时必须提示系统级配额问题,不能声称 Talk 已创建 30 条,也不能尝试清理其他应用提醒。
  • 华为系统时钟使用独立的系统内部闹钟能力;真机已验证系统时钟可创建闹钟,而第三方 ReminderRequestCalendar 和 ReminderRequestAlarm 同时返回 1700002。不要把打开或操作系统时钟私有页面包装成跨设备通用能力,也不能在未获得公开 API 成功结果时向模型返回“提醒已创建”。
  • Mate X7 + Qwen3.5 0.8B 已完成真实重复闹钟调用:用户原句“每周一早上 8 点”经 create_recurring_alarm 落库为 Talk ID=1,list_alarms 复核为“每周一 08:00”;系统 1700002 时记录保留为失败状态。
  • 本地模型的闹钟确定性工具通过 ToolResult.finalAnswer 权威直出。只有同一批所有结果都提供该字段时才能跳过第二轮推理;云端模型、普通工具和混合批次不得提前结束。
  • AlarmPage 直接复用 AlarmDao 与 AlarmScheduler 展示和管理本地记录;删除必须先确认系统提醒已取消,系统取消失败时不得删除本地记录。
  • AlarmPage 会直接查询系统通知开关,并提供开启通知和“两分钟测试”入口,便于在没有模型配置时独立验证 reminderAgentManager。
  • Mate X7 已执行备份后的彻底卸载重装:当前签名/Profile/权限一致,历史旧 App ID 已清除,通知已开启,但 Talk 有效提醒为 0、重置自身状态后两分钟测试仍返回 1700002。后续不要再把同一故障归因于旧签名残留或通知未开启。
  • 文件工具限制在应用 filesDir,拒绝路径穿越。
  • file_read 支持字符偏移分段读取,file_search 支持受限递归文本搜索。
  • 网络工具只允许公开 HTTPS,阻止明显 SSRF、数字主机变体和自动重定向。
  • 请求体、响应体、文件内容、请求头数量和超时均有限制。
  • 工具审计保存统计但不保存真实正文、密钥、Cookie 或 URL 查询参数。
  • 数据库 v4 升级会清理旧审计中的原始参数和结果;数据库 v5 新增不保存凭据的 alarm 表。

工具运行环境

  • 设置页已新增“工具运行环境”。当前不仅做系统能力探测,还在 libtalkllama.so 内静态包含 WAMR 2.4.5 经典解释器,执行编译时固定的 WASM add(20, 22) 自检。
  • WAMR 固定到提交 25bd7eb63e828e4bd242cc9b38d260b4b31c6605;仅启用经典解释器,AOT、JIT、WASI、libc、文件、网络、线程、共享内存和多模块均关闭。运行时使用 512 KiB 固定内存池,模块实例的栈和堆各限制为 16 KiB。
  • WAMR 加载器会临时修改输入字节,因此内嵌只读模块必须先复制到可写缓冲区再调用 wasm_runtime_load()。直接 const_cast 传只读段会在 Mate X7 的 wasm_const_str_list_insert 触发 SIGSEGV(SEGV_ACCERR);当前代码已修复并完成回归。
  • native_tool_runtime.cpp 在 N-API 异步任务中依次运行固定 WASM 计算,并测试 fork + pipe、ptrace、应用自带静态 ARM64 ELF、匿名内存 ELF、openpty 与 posix_spawn 系统 Shell。固定模块、标记和命令均不可被页面或模型修改。
  • 探测通过不等于 Ubuntu 已安装;当前没有 PRoot、rootfs、包管理器或 shell_exec 工具。
  • Mate X7 真机连续执行两次均显示“WAMR 2.4.5 解释器已执行固定 add(20, 22) = 42”,Talk 进程保持存活,证明当前解释器最小闭环可重复初始化和销毁;这不等于任意 WASM 文件或插件系统已经实现。
  • Mate X7 真机结果:N-API、普通 fork + pipe 和 PTRACE_TRACEME/PTRACE_CONT 通过;filesDir 中的应用自带 ELF、openpty 和系统 Shell 的 posix_spawn 均返回 errno=13 (Permission denied)。
  • 静态 ARM64 ELF 经 memfd_create 写入成功,但 fchmod 返回 errno=13,直接 execveat(..., AT_EMPTY_PATH) 返回 errno=2。因此标准的落地执行与匿名内存执行通道当前都不可用。
  • 不能直接照搬 Operit 的 Android 预编译可执行文件方案。主线已改为“WAMR 安全插件 → 资源限制、签名下载与权限声明 → Python-WASI → 可选远程 Ubuntu”;PRoot/进程内 ELF loader 只保留为独立研究路线,不阻塞主线。
  • PRoot 上游默认仍会把内置 loader 写入临时文件并通过 tracee 的 execve 启动,必须改成向停止的子进程注入 loader 与加载脚本;同时上游是 GPL-2.0-or-later,Talk 是 MIT,许可证方案确定前禁止直接链接或复制 PRoot/Operit 二进制。

数据、记忆和角色

  • 会话、消息、角色、记忆、模型和工具审计 DAO。
  • 删除单会话和清空聊天使用事务保持关联数据一致。
  • 长期记忆主动写入、人工 CRUD、关键词降级和 Embedding 召回。
  • 记忆召回 2 秒超时,不阻塞主聊天链路。
  • 旧对话异步摘要与上下文裁剪。
  • 角色卡 CRUD、JSON 导入导出、酒馆 PNG chara / ccv3 导入、头像落盘。
  • 角色开场白先落库再展示。
  • “设置 → 角色卡 → 创建第一个角色”和聊天首页“角色”入口已在 Pura 90 模拟器完成 UI 路径验证。
  • 隐私安全备份导出、合并恢复、替换恢复和向量重建。

自动化

  • AutoOpAbility 无障碍扩展。
  • 应用内事件桥、UI 树压缩、点击和滑动。
  • 最多 20 步、重复动作保护、危险确认和急停。
  • 自动化设置页提供服务状态与 UI 树自检。

4. 尚未完成或需要扩大验证

  1. 多个真实 OpenAI 兼容供应商的 Chat、工具调用、限流和异常响应回归。
  2. 多种 Embedding 供应商、不同向量维度、重建和旧记忆回填的真机回归。
  3. 更多酒馆 PNG 卡片样本、异常元数据和大图片导入回归。
  4. 无障碍跨应用场景、系统弹窗、后台/前台切换和服务重连回归。
  5. 弱网、断网、切网、超时、取消和应用退后台的真机回归。
  6. Phone / Tablet 不同尺寸、折叠屏、深色聊天模式、字体和上下安全区视觉回归。
  7. 处理 SDK 弃用 API 与 ArkTS “Function may throw” 警告。
  8. 正式图标、应用说明、隐私政策、签名和上架材料。
  9. KeyPool 多 Key 批量健康检查仍是后续功能。
  10. MemoryClassifier 仍使用关键词启发式,尚未增加模型驱动的会话自动记忆提取。
  11. 本地 GGUF 首版只有 ARM64 CPU 后端,尚未接入麒麟 NPU;需在麒麟真机扩大架构、量化、内存、温控和 token/s 回归。
  12. 本地 GGUF 暂不提供 Embedding,语义记忆仍需独立 Embedding 配置。
  13. 内置目录的下载地址、文件大小、SHA-256 和许可证提示需要随上游版本人工维护;新增条目前必须从模型官方或维护方仓库逐项核对,禁止指向不明网盘或未固定文件。
  14. WAMR 2.4.5 经典解释器固定计算已在 Mate X7 连续两次通过;任意 WASM 导入、插件清单/签名、资源计量、宿主能力授权、Python-WASI 和远程 Ubuntu 尚未实现。Operit 式标准外部 ELF 启动通道不可用,PRoot 注入原型及 GPL/MIT 分发方案仅作为独立研究项。

不要把“构建成功”描述成“真机验收完成”。每一项用户可见能力必须按验收清单记录实际结果。

5. 关键目录

entry/src/main/ets/
├── entryability/EntryAbility.ets
├── accessibility/AutoOpAbility.ets
├── pages/
│   ├── ChatPage.ets
│   ├── SettingsPage.ets
│   ├── ModelConfigPage.ets
│   ├── EmbeddingConfigPage.ets
│   ├── CharacterPage.ets
│   ├── MemoryPage.ets
│   ├── ToolboxPage.ets
│   ├── ToolRuntimePage.ets
│   ├── AlarmPage.ets
│   └── AutomationPage.ets
├── agent/
│   ├── AgentEngine.ets
│   ├── ContextManager.ets
│   └── PromptBuilder.ets
├── model/
│   ├── ModelAdapter.ets
│   ├── ModelAdapterFactory.ets
│   ├── ModelProviderPresets.ets
│   ├── local/
│   ├── openai/OpenAIAdapter.ets
│   └── sse/SseParser.ets
├── tools/
│   ├── Tool.ets
│   ├── ToolRegistry.ets
│   ├── SandboxPath.ets
│   ├── NetworkUrlPolicy.ets
│   ├── builtin/
│   ├── automation/
│   └── runtime/
├── automation/
├── alarm/
│   └── AlarmScheduler.ets
├── memory/
├── character/
├── data/
│   ├── DbHelper.ets
│   ├── BackupService.ets
│   └── dao/
│       └── AlarmDao.ets
└── common/
    ├── AppTheme.ets
    ├── Crypto.ets
    ├── Abort.ets
    └── WindowLayoutManager.ets

entry/src/main/cpp/
├── CMakeLists.txt
├── native_llama.cpp
├── native_tool_runtime.cpp
├── native_tool_runtime.h
├── runtime_probe_helper.cpp
└── types/libtalkllama/

6. 不可破坏的约束

数据一致性

  • 用户消息、助手消息和工具消息必须先写数据库,再通知 UI。
  • 删除会话时先清工具审计,再删消息,最后删会话。
  • 清空和恢复过程中必须使用事务;失败时不能留下半套数据。
  • 生成过程中不得执行会破坏当前会话依赖的数据操作。

模型与流式处理

  • SSE 必须使用流式 UTF-8 解码,不能逐块直接转字符串。
  • 已经产生 token 或工具调用后不能自动重试整个请求。
  • 所有网络请求结束、失败或取消时必须释放监听器和 HTTP 对象。
  • 单轮响应 60 秒超时;工具循环最多 15 轮。
  • 本地模型单轮含首次加载允许 300 秒;上下文请求必须按 GGUF 训练上限收敛。
  • 模型目录下载必须先写 .download,只有大小、SHA-256 和 GGUF 头均通过才可重命名为正式文件;取消、网络失败和配置写入失败都必须清理对应文件。
  • 直接冷启动的路由参数可能是 undefined;页面生命周期读取参数前必须同时排除 null 与 undefined。
  • 聊天模型属于首页主链路,应先于 Embedding 初始化;Embedding 失败只能降级关键词检索,不能阻塞模型显示与聊天。

页面与弹层

  • 新增页面级面板时优先在页面根 Stack 中渲染,避免重新引入已在 API 24 设备上出现无可见反馈的 bindSheet。
  • 弹层必须提供明确关闭入口,并在 onBackPress() 中优先消费返回事件。
  • 危险确认不能通过遮罩或返回键静默关闭,必须显式完成确认 Promise。
  • 生成期间不得切换模型或角色;角色切换必须开启新对话,不能修改已有历史的人设语义。
  • 顶栏新增入口时需在 1320 × 2760 模拟器和窄宽布局检查标题、角色、模型与设置是否挤压。

工具与安全

  • 单个工具输出默认不超过 2000 字符。
  • 文件路径必须经过 SandboxPath,不能直接拼接模型输入。
  • 网络地址必须经过 NetworkUrlPolicy。
  • 动态风险判断失败时必须按危险操作处理。
  • 审计日志不得保存 API Key、Token、Cookie、请求正文、文件正文或剪贴板正文。
  • 自动化点击和滑动必须保留危险确认与急停路径。
  • 提醒数量超限时只允许自动清理当前应用的过期一次性提醒;若没有安全清理项,应明确失败并提示用户先删除旧提醒,不能取消仍在生效的提醒。

凭据与签名

  • 不得提交 API Key、仓库访问令牌、Cookie、HUKS 明文或聊天备份。
  • 不得提交个人签名证书、私钥、密码和本机绝对路径。
  • 根目录 build-profile.json5 可能包含开发者本地签名变更;提交前必须逐文件暂存,不能直接 git add .。
  • 仓库凭据应使用系统凭据管理器或交互式认证,不能放进远程 URL、命令历史或文档。

7. 构建方法

DevEco Studio

  1. 打开仓库根目录。
  2. 确认 SDK 为 HarmonyOS 6.1.1 / API 24。
  3. 配置个人调试签名。
  4. 运行 entry。

命令行

设置以下环境变量:

  • DEVECO_HOME
  • DEVECO_SDK_HOME
  • JAVA_HOME

然后执行:

macOS / zsh:

"$DEVECO_HOME/tools/node/bin/node" \
  "$DEVECO_HOME/tools/hvigor/bin/hvigorw.js" \
  --mode project -p product=default assembleApp -p buildMode=debug --no-daemon

Windows / PowerShell:

& "$env:DEVECO_HOME\tools\node\node.exe" `
  "$env:DEVECO_HOME\tools\hvigor\bin\hvigorw.js" `
  --mode project -p product=default assembleApp -p buildMode=debug --no-daemon

产物:

entry/build/default/outputs/default/entry-default-signed.hap

构建日志中的已知弃用和异常处理警告目前不会阻止 HAP 生成,但新增修改不能引入 ArkTS 编译错误。

8. 提交流程

  1. 查看状态和差异:

    git status --short
    git diff --check
    git diff
    
  2. 只暂存本次相关文件,明确排除本地签名配置。

  3. 执行完整 HAP 构建。

  4. 再检查暂存差异与敏感信息。

  5. 创建小而明确的提交。

  6. 推送到 origin/main,然后用远程提交哈希核对。

禁止使用会丢失用户工作区内容的 Git 命令,例如未经明确授权的 git reset --hard 或 git checkout --。

9. 推荐后续顺序

  1. 定义 WASM 插件清单、入口 ABI、版本、SHA-256、发布者签名和宿主能力声明;先只支持仓库内固定的纯计算样例。
  2. 为 WAMR 增加模块大小、内存、调用时限、输出、并发和取消上限,并完成重复执行、切后台和熄屏回归。
  3. 把文件、网络、剪贴板、提醒和 UI 自动化做成默认拒绝的显式宿主能力,复用工具风险确认与脱敏审计。
  4. 纯 WASM 插件闭环稳定后再接 Python-WASI;完整 Ubuntu 只做可选远程 SSH,PRoot 继续作为不阻塞主线的研究项。
  5. 在至少一台真实 Phone 和折叠屏上复测首页角色、模型、历史、全部应用内弹层和 Chat → Settings → Chat 安全区。
  6. 对至少两个真实模型供应商执行快捷配置、普通聊天、工具调用、取消和弱网测试,并扩大 Embedding、角色 PNG 和无障碍样本。
  7. 收敛构建警告、补齐正式许可证随包材料,再进入发布准备。

10. 当前 Git 注意事项

  • 主线为 main,远程为 AtomGit origin。
  • 当前文档以 AtomGit main 最新提交为功能基线,不在文档内固化容易过期的提交号。
  • 本地开发环境可能保留未提交的签名配置差异,这是预期行为,不应随功能提交推送。
  • 接手时先运行 git status --short 和 git log --oneline -10,不要根据旧聊天记录猜测仓库状态。