Skip to content

Cicalaqvq/Mini-Notes

Repository files navigation

Mini Notes with LLM Summary

这是一个按 Anna 本地开发模型实现的 Mini Notes App。UI 只能在 anna-app dev 提供的 iframe Runtime 中工作,笔记通过 anna.storage.get/set 保存,总结通过以下链路完成:

Anna App iframe
  -> AnnaAppRuntime.connect()
  -> anna.storage.get/set
  -> anna.tools.invoke(...)
  -> Notes Summarizer Executa
  -> reverse JSON-RPC sampling/createMessage
  -> host LLM 或 --mock-sampling fixture
  -> summary 返回 UI

项目不需要 Anna 账号、anna-app login、LLM API key 或云端数据库。

项目结构

.
├── src/
│   ├── components/                  # Vue 输入和笔记列表组件
│   └── services/
│       ├── annaRuntime.ts            # AnnaAppRuntime.connect / tool identity
│       └── noteStorage.ts            # anna.storage.get/set 唯一存储边界
├── public/anna-tool-ids.js           # 本地 tool_id,发布时由 Anna 覆盖
├── bundle/                           # pnpm build 生成的 Anna UI bundle
├── executas/notes-summarizer-python/
│   ├── executa.json                  # 本地 Executa 发现配置
│   ├── pyproject.toml                # Python entrypoint
│   └── notes_summarizer_plugin.py    # v2 双向 JSON-RPC + sampling
├── fixtures/sampling-summary.jsonl   # --mock-sampling 离线响应
├── scripts/
│   ├── protocol-smoke.mjs            # initialize/describe/invoke 协议测试
│   ├── run-mock-sampling.mjs         # anna-app executa dev mock 测试
│   ├── package-executa.mjs           # 当前平台二进制归档
│   ├── smoke-binary.mjs              # 二进制 describe 冒烟测试
│   └── verify-executa-archive.mjs     # 最终归档结构与 manifest 校验
├── .github/workflows/
│   └── release-executa.yml           # 三平台 GitHub Release workflow
├── manifest.json                     # Anna App schema 2 manifest
└── app.json                          # bundled_executas 映射

环境与安装

建议环境:

  • Node.js 22+
  • pnpm 10+
  • Python 3.10+
  • uv
  • Windows 打包需要 7z

安装前端和 Anna CLI 依赖:

pnpm install

@anna-ai/cli 已放在 devDependencies 中,不需要全局安装 anna-app

构建与严格校验

构建 Vue 静态 bundle:

pnpm build

产物为 bundle/index.htmlbundle/assets/*bundle/anna-tool-ids.jsmanifest.json#ui.bundle.entry 指向 index.html,Anna harness 会以 bundle/ 作为静态根目录。

运行类型检查和 Anna manifest 严格校验:

pnpm typecheck
pnpm validate

pnpm validate 等价于:

pnpm exec anna-app validate --strict

UI Harness 测试

启动无账号、无 LLM 的本地 harness:

pnpm dev

该命令执行:

anna-app dev --no-llm

在 dashboard 中完成以下检查:

  1. 新增笔记后输入框清空,列表显示内容和添加顺序。
  2. 删除笔记后列表立即更新。
  3. RPC log 中能看到 storage.getstorage.set
  4. 点击 Summarize 后 RPC log 中能看到 tools.invoke

--no-llm 会关闭 harness 的 LLM/sampling 能力,因此点击 Summarize 后预期出现类似错误:

[-32603] harness started with --no-llm

这表示 UI 已经成功走到 anna.tools.invoke -> Executa -> sampling/createMessage,只是当前 harness 主动禁用了采样,不代表 Executa sampling 实现失败。

manifest.json#ui.host_api.llm 声明 complete,因为当前本地 runtime 会把 Executa 的 reverse sampling 桥接为 host.llm.complete 后再经过 App ACL。前端源码没有调用 anna.llm.complete,唯一总结入口仍是 anna.tools.invoke

项目没有 localStorage、IndexedDB 或前端规则摘要兜底。直接运行 pnpm preview 只能查看未连接状态,不能创建、删除或总结笔记。

Storage 证据

存储边界位于 src/services/noteStorage.ts

await anna.storage.get({ key: STORAGE_KEY });
await anna.storage.set({ key: STORAGE_KEY, value: cleanNotes });

新增和删除都调用同一个 saveNotes。总结前还会重新调用 loadNotes,确保传给 Tool 的内容来自 Anna storage。

本地 anna-app dev 使用 legacy in-memory runtime_state;它用于 无登录 harness 调试,不承诺刷新外层 dashboard 或重启 dev 后仍保留。 生产环境中同一 Host API 会映射到 APS KV。

Sampling Mock 测试

使用官方 anna-app executa dev --mock-sampling 路径测试后端:

pnpm rpc:invoke

等价核心命令为:

pnpm exec anna-app executa dev \
  --dir executas/notes-summarizer-python \
  --mock-sampling fixtures/sampling-summary.jsonl \
  --no-agent --no-image --no-upload \
  --invoke summarize \
  --args '{"notes":[{"order":1,"content":"修复登录 bug"},{"order":2,"content":"明天跟客户 follow up"},{"order":3,"content":"准备 Workshop 提纲"}]}' \
  --json

Windows PowerShell 不便携地处理上述 JSON 引号,因此 pnpm rpc:invoke 通过 Node 参数数组启动同一条 CLI 命令。

输出中的 model: "mock-mini-notes" 和 fixture 文本证明结果来自 mock sampling response。Tool 内没有规则总结函数。

手动 JSON-RPC 测试

查看 describe 裸 manifest:

pnpm rpc:describe

完整测试 initializedescribeinvokehealthshutdown

pnpm test:protocol

scripts/protocol-smoke.mjs 会直接启动 Python 进程并扮演 host:

  1. 发送 protocol v2 initialize
  2. 验证 capabilities.samplingclient_capabilities.sampling
  3. 发送 describe 并验证 tool identity 与 llm.sample
  4. 发送 invoke
  5. 捕获 Tool 发出的 reverse sampling/createMessage
  6. 验证 prompt 包含 notes,metadata 包含 executa_invoke_id=manual-smoke-invoke
  7. 回写 sampling response,再验证最终 summary 使用该 response。

成功时会显示可审计证据:

[evidence] sampling/createMessage metadata={"executa_invoke_id":"manual-smoke-invoke",...}
[ok] initialize, describe, invoke, health and shutdown passed

不能把 invoke JSON 单独管道给进程后立即关闭 stdin,因为正确的 sampling Tool 会等待 host 对 reverse RPC 的响应。协议脚本保留同一个 stdin reader,同时处理 host 请求和 reverse RPC response。

Executa 协议实现

notes_summarizer_plugin.py 满足以下约束:

  • stdin 每行读取一个 JSON-RPC 2.0 frame。
  • stdout 只输出 JSON-RPC,且每次写入后 flush。
  • 日志只写 stderr。
  • 进程持续读取直到 EOF。
  • invoke 在线程池中运行,stdin reader 可继续接收 sampling response。
  • initialize 协商 protocol 2.0 并声明 sampling capability。
  • describe 返回裸 manifest,参数使用 Anna parameters[] 形态。
  • manifest 声明 host_capabilities: ["llm.sample"]
  • sampling/createMessage metadata 携带当前 invoke_id

身份映射保持一致:

app.json bundled handle: notes-summarizer
manifest required_executas: bundled:notes-summarizer
manifest host_api.tools: required:bundled:notes-summarizer
frontend handle: notes-summarizer
dev / describe tool_id: tool-test-notes-summarizer-12345678

本机二进制打包

执行:

pnpm package:executa

脚本会:

  1. 识别本机平台和架构。
  2. uv run --with pyinstaller 构建独立二进制。
  3. 对二进制发送 describe 做 smoke test。
  4. 生成 archive root manifest.json
  5. 将二进制放入 bin/ 并声明 entrypoint 和 0o755 权限。
  6. macOS 生成 .tar.gz,Windows 生成 .zip
  7. 从最终压缩包重新读取并校验 root manifest、平台 entrypoint 和权限。

支持的平台 key:

  • darwin-arm64
  • darwin-x86_64
  • windows-x86_64

Windows 输出示例:

dist/executa/notes-summarizer-windows-x86_64.zip

归档结构:

manifest.json
bin/
└── tool-test-notes-summarizer-12345678.exe

检查 Windows archive:

7z l dist\executa\notes-summarizer-windows-x86_64.zip
7z e -so dist\executa\notes-summarizer-windows-x86_64.zip manifest.json

GitHub Actions 发布

workflow 位于 .github/workflows/release-executa.yml

触发方式:

  • 推送 notes-summarizer-v* tag。
  • 在 Actions 页面运行 workflow_dispatch 并填写 release tag。

构建矩阵使用当前 GitHub runner labels:

  • macos-15 -> darwin-arm64
  • macos-15-intel -> darwin-x86_64
  • windows-latest -> windows-x86_64

每个平台都会安装依赖、构建并校验 App、运行 reverse sampling 协议 测试、打包当前平台二进制,并再次对二进制发送 describe。最后一个 release job 会把三个文件上传为同一个 GitHub Release assets:

notes-summarizer-darwin-arm64.tar.gz
notes-summarizer-darwin-x86_64.tar.gz
notes-summarizer-windows-x86_64.zip

Actions artifact 只用于 job 间传递,最终交付目标是 GitHub Release。

组件关系

  • bundle/ 是 iframe 加载的工程化前端产物。
  • manifest.json 声明 bundle、view、Host API ACL 和所需 Executa。
  • app.json#bundled_executas 把 handle 映射到本地 Executa 目录。
  • anna.storage.* 在本地使用 runtime_state,在生产映射到 APS KV。
  • anna.tools.invoke 把 UI 请求路由到 Executa 的 stdio invoke
  • Executa 的 sampling/createMessage 借用 host LLM,不持有 API key。
  • binary archive 把同一个 stdio Tool 打包为无需 Python 的发布产物。

Privacy

Mini Notes 不直接调用外部业务 API,也不收集账号或分析数据。笔记只通过 Anna storage Host API 保存;点击总结时,当前笔记仅通过 anna.tools.invoke -> Executa -> host sampling 链路交给 Anna host 处理。本地无登录 harness 使用进程内的 legacy runtime state。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors