Yansu 本地逆向分析报告

基于 macOS 本地安装包、SQLite 数据库、内置 skill 文档、Mach-O/Swift 符号、Go module 元信息和二进制字符串的静态分析。分析时间:2026-05-15。

结论摘要

App 框架
Wails + Go + Web UI

主程序是 Go/Wails,前端资源打包在二进制中,不是 Electron。

长期记忆
Markdown + SQLite

Markdown 是可读存储,SQLite 负责索引、FTS 和 embedding cache。

Computer Use
Swift cua-driver

AX + ScreenCaptureKit + Vision OCR + 定向 CGEvent,强调后台操作不抢焦点。

当前能恢复到“流程、模块、接口、状态机、数据落点”级别。完整源码级 prompt 和 Go 逻辑因为主程序使用 -w -s 去符号构建,无法仅靠 strings 无损恢复。

本地文件与数据库

对象路径 / 证据作用
App bundle/Applications/Yansu.appWails 桌面 App,版本 0.1.276
主程序/Applications/Yansu.app/Contents/MacOS/YansuGo 1.24.13,依赖 wailsapp/wails/v2go-rod/rodmodernc.org/sqlitesherpa-onnx
用户数据~/.yansu-agent数据库、截图、模型、记忆、bin、日志。
Activity DB~/.yansu-agent/activity.db桌面活动、截图、OCR、pipeline runs、summary、prediction/handoff。
Memory DB~/.yansu-agent/memory.dbmemory/knowledge markdown 的索引、FTS、embedding cache。
Sessions DB~/.yansu-agent/sessions.db聊天、stream items、cron、workspaces、crystal versions、git snapshots。
截图~/.yansu-agent/activity/snapshots/YYYY-MM-DD/*.jpgactivity snapshot 原始图片。
长期记忆~/.yansu-agent/memory/*.md人类可读的 memory/knowledge 文件。

Activity Pipeline 复原

activity_pipeline_runs 是后台活动加工流水线的审计表。它不是原始事件表,而是记录每轮扫描、过滤、模型判断和沉淀结果。

1. tick定时扫描 pending activity sessions,统计 group_count、filtered_count、candidate_count。
2. group把若干 activity_sessions 合并成一组,按时间、app 和事件密度形成候选。
3. prefilter低价值活动直接 dismiss,例如 too few events: 2 < 20。
4. summary构造 Sessions、Time span、Applications used、Activity timeline。
5. AI triage模型返回 worthMemory / worthKnowledge / worthAutomation / worthHandoff / worth。
6. persist生成 memory、knowledge、handoff、automation 或 crystal,并写状态字段。

关键字段

字段含义
kindtick 是调度扫描,group 是实际处理一组活动。
prefilter_pass, prefilter_reason无需进模型的低价值活动会在这里被过滤。
ai_summary发给模型前的活动摘要,含时间线和应用列表。
ai_response_raw模型 triage JSON 原文。
worth_memory, worth_knowledge, worth_automation, worth_handoff不同沉淀分支的布尔判断。
memory_status, knowledge_status, handoff_status, crystal_status分支执行结果,例如 created、updated、dismissed。

实际样本

{
  "id": 76,
  "kind": "group",
  "apps": ["飞书", "Warp", "loginwindow", "Codex", "Google Chrome", "微信", "QSpace Pro", "iShot"],
  "event_count": 217,
  "duration_ms": 650908,
  "worth_memory": true,
  "worth_knowledge": true,
  "worth_handoff": true,
  "worth_automation": false,
  "crystal_status": "dismissed",
  "memory_status": "created",
  "knowledge_status": "created",
  "handoff_status": "created"
}

这条记录是在本轮分析过程中 Yansu 自己生成的:它判断当前任务值得沉淀为 memory、knowledge,并且还生成了 handoff。

记忆机制复原

CaptureAccessibility 事件、窗口、app、浏览器 URL、终端文本、截图。
OCR截图进入 activity_ocr_frames,并建立 trigram FTS。
Pipelineactivity group 被总结、过滤、模型 triage。
Markdown生成 ~/.yansu-agent/memory/*.md
Indexmemory.db 分块、FTS、embedding。
Inject新会话中按维度/查询检索并注入 agent 上下文。

Activity DB schema 重点

作用
activity_sessions一次活动片段。包含 app_names、trigger_kind、ocr_status、snapshot_count、meeting/transcript/audio 字段。
activity_events原始事件时间线,data 为 JSON 文本。
activity_snapshots截图文件路径,并附带 ax_text、browser_url、content_type、app、parsed_json。
activity_ocr_framesOCR 文本、confidence、embedding、embed_hash。
activity_ocr_ftsFTS5 trigram 全文检索。
activity_pipeline_runsAI 加工流水线审计记录。

Memory DB 状态

schema_version3
embedding modelall-MiniLM-L6-v2
chunk_tokens400
chunk_overlap80
memory chunks15 chunks, 10130 bytes
knowledge chunks9 chunks, 7310 bytes

维度

user_insight topic_highlight deep_user_model agentic_memory custom

二进制和 CLI 文本显示,新会话可用 memory searchmemory list --dimension agentic_memory 等能力检索这些记忆。

记忆如何被检索

现有证据显示 Yansu 有两条检索路径:宿主自动检索并注入上下文,以及智能体/用户显式调用记忆工具检索。两者使用同一套 memory.db 索引。

自动注入

证据含义
Search memories using hybrid vector + FTS retrieval — the same index the app uses to auto-inject context into the agent.Yansu CLI 帮助明确说 app 会用同一个索引自动注入 agent 上下文。
[memory] inject search failed二进制日志字符串显示存在 memory inject 阶段,失败时会记录。
DATA YOU HAVE (do NOT claim you lack it):宿主会把已检索到的数据作为 prompt 上下文交给模型。
AUTO-INJECT (already happening, no action needed):提示词会告诉 agent 某些上下文已经自动注入,不需要再主动查询。
<recent-activity-context>近期活动上下文也会以 prompt 片段形式注入。

因此,普通新对话里,Yansu 很可能会先用用户输入或会话上下文生成检索 query,再从 memory.db 做 hybrid search,把命中的记忆塞进系统/开发者上下文。

主动检索工具

工具/接口作用
SearchMemoryWails/App binding 中存在的搜索接口;二进制有 main.(*App).SearchMemory
GetMemoryEntry, GetMemoryEntries读取单条或列表。
SaveMemoryEntry保存手动记忆或编辑后的记忆。
SyncMemoryIndex同步 markdown 文件到 SQLite 索引。
yansu memory search <query> --dimension agentic_memoryCLI 暴露的同一套 hybrid vector + FTS 搜索。

二进制 prompt 里还出现 memory search "<query>" --dimension user_insightmemory list --dimension agentic_memory 这样的用法提示,说明 agent 可被建议主动调用记忆检索。

Markdown metadata 与 memory.db 的分工

何时使用说明
Markdown 正文长期存储、人类阅读、索引源文件自动生成的 activity memory 通常不是 YAML frontmatter,而是普通 Markdown 字段,如 - **Dimension**: user_insight
Markdown metadata导入旧 Claude memory/feedback 时导入文件可能保留 namedescriptiontypeoriginSessionId frontmatter;这些内容会作为文本进入索引。
memory_files同步索引时记录 path/source/hash/mtime/size,判断文件是否变更。
memory_chunks检索时把 markdown 切成 chunk,保存 start_line/end_line/text/embedding/source。
memory_chunks_fts关键词检索时SQLite FTS5 虚拟表,用于 BM25/全文匹配。
memory_embedding_cache语义检索时缓存 embedding,当前本机配置为 all-MiniLM-L6-v2

Dimension 本身更像过滤/分组标签,而不是 skill 那种“description 触发器”。CLI 明确说 --dimension 只过滤 memory-source 命中,knowledge hits 会被 drop。

FTS 是什么

FTS 是 Full-Text Search,SQLite 里的全文搜索虚拟表。Yansu 用的是 FTS5:

CREATE VIRTUAL TABLE memory_chunks_fts USING fts5(
  text,
  id UNINDEXED,
  path UNINDEXED,
  source UNINDEXED,
  model UNINDEXED,
  start_line UNINDEXED,
  end_line UNINDEXED,
  tokenize='unicode61 remove_diacritics 2'
);

简单说:FTS 负责关键词/短语匹配和 BM25 排序;vector search 负责语义相似。Yansu 的 memory search 是 hybrid,也就是两者合并。

CLI 与 Memory 同步机制

后续实测显示,yansu memory ... 不是直接打开 SQLite 或扫描 Markdown 的重型工具,而是一个很薄的本地 HTTP 客户端。真正读写 memory.db~/.yansu-agent/memory/*.md 的是正在运行的 Yansu 桌面 App 后端。

CLI 如何与主环境交互

Agent智能体或用户执行 ~/.yansu-agent/bin/yansu memory search
PortCLI 读取 YANSU_CRON_PORT~/.yansu-agent/cron-port
SecretCLI 读取 YANSU_CRON_SECRET~/.yansu-agent/cron-secret
HTTP请求 127.0.0.1:<port>/memory/search 等本地 API。
BackendYansu App 后端校验 secret,并执行搜索/保存/同步。
DB后端读写 Markdown、SQLite、FTS 和 embedding cache。
证据结论
go version -m ~/.yansu-agent/bin/yansu 只显示 cobrapflagwebsocket 等轻量依赖,没有 SQLite 依赖。CLI 本身不像是直接读写 memory.db 的实现。
YANSU_CRON_PORT=1 后执行 memory search,报错中出现 Get "http://127.0.0.1:1/memory/search?...": connect: connection refusedmemory search 走本地 HTTP API。
HOME 换成空目录后执行,报 cron API port not foundCLI 默认从 ~/.yansu-agent/cron-port / cron-secret 发现本地服务。
运行中的 Yansu.app 进程持有 memory.dbactivity.dbsessions.db 及 WAL/SHM 文件句柄。主 App 后端是数据库连接和持久化的所有者。

因此,智能体“调用 memory CLI”时,本质是调用宿主提供的本地受控 API。它不是绕过宿主去直接改数据库;如果要保存或同步记忆,也应是 CLI 发请求,Yansu 后端再执行 SaveMemoryEntrySyncMemoryIndex 这类逻辑。

Markdown 如何与 memory.db 同步

同步关系更像“Markdown 是记忆实体,SQLite 是检索索引”。Markdown 文件保留人类可读内容;memory.db 保存路径登记、chunk、FTS 和向量缓存。

Markdown~/.yansu-agent/memory/*.md 是可读、可迁移的持久内容。
memory_files记录 path/source/hash/mtime/size,用于检测变化。
Chunkchunk_tokens=400overlap=80 切成 memory_chunks
FTS同一份文本进入 memory_chunks_fts,用于关键词检索。
Vector生成或复用 all-MiniLM-L6-v2 embedding。
Search搜索时合并 FTS 和 vector 结果,再返回来源路径和行号。
DB 表同步职责
memory_files每个 Markdown 文件一行,记录 pathsourcehashmtimesize
memory_chunksMarkdown 切块后的结构化副本,含 start_lineend_linetextembedding
memory_chunks_ftsSQLite FTS5 虚拟表,给关键词/短语检索使用。
memory_embedding_cache缓存 embedding,避免同一内容重复向量化。

如果 Markdown 被新增或修改,合理的同步过程是:后端发现 hash/mtime/size 变化,删除旧 path 对应 chunk,重新切块,更新 FTS,再生成或复用 embedding。CLI 文案里也明确说 memories “live in ~/.yansu-agent/memory/ and are indexed via FTS5 + vector search”,这说明文件是记忆来源,DB 是索引层。

本地抓包结果

为了验证 CLI 到主环境的交互方式,额外做了一次本地 HTTP 转发抓包:让 yansu memory 命令先请求临时代理 127.0.0.1:65284,代理记录 request/response 后再转发到真实 Yansu cron API。

项目结果
抓包日志~/Downloads/yansu-memory-http-capture.jsonl
认证方式Authorization: Bearer <cron-secret>。报告和日志中已隐藏 secret。
Search APIGET /memory/search?q=Codex+memory+retrieval&limit=2
List APIGET /memory/list?limit=2
Show APIGET /memory/show/activity-df6994e28cca7163
响应格式JSON。search 返回 path/source/startLine/endLine/score/snippet;show 返回完整 Markdown 内容和 metadata。

抓到的 search 请求

{
  "method": "GET",
  "path": "/memory/search?q=Codex+memory+retrieval&limit=2",
  "request_headers": {
    "Host": "127.0.0.1:65184",
    "User-Agent": "Go-http-client/1.1",
    "Authorization": "Bearer [REDACTED]",
    "Accept-Encoding": "gzip"
  },
  "status": 200,
  "response_headers": {
    "Content-Type": "application/json"
  }
}

云端模型请求

系统级网络层面,Yansu 当前通过本地 Clash 代理 127.0.0.1:7897 访问外网。未安装 MITM 证书时,只能看到代理连接和 TLS 加密流,拿不到 HTTPS 请求体明文。

不过 ~/.yansu-agent/yansu-gui.log 已经记录了模型请求元信息,包含:

[native] HTTP POST https://dashboard.yansu.ai/anthropic/v1/messages
model=claude-haiku-4-5-20251001
bodyBytes=...
hasXAPIKey=false
hasAuth=true
ct=text/event-stream

日志还会打印部分 suggestion prompt 的完整文本。因此后续要拿完整 prompt,有两条路线:优先挖 yansu-gui.log 中已经明文打印的 prompt;如果要抓所有云端请求体,则需要给 Yansu/系统代理安装受信任的 MITM 根证书,或对 Go HTTP client 做运行时插桩。

模拟聊天抓包观察

为了观察真实 agent 对话的 prompt 组织方式,进行了一个新建会话,两轮无害测试:

  1. 请用两句话介绍一下你自己,不要使用任何工具。
  2. 刚才我让你做什么?请只回答用户上一轮的请求,不要扩展。

第一轮请求

09:19:10 [activity-context] INJECTED tag=1778894338864-0b3505c7d6415 summary_len=273
09:19:10 [compose-prompt] final-len=22958 (parallel, 10 chunks, skill-domain=chat)
09:19:10 [prompt-func] prompt-bytes=22958 history-bytes=0
09:19:10 [native-loop] model=claude-sonnet-4-6 messages=1 tools=14 max_tokens=64000
09:19:10 [native] HTTP POST https://dashboard.yansu.ai/anthropic/v1/messages bodyBytes=33359

含义:首轮没有历史消息,Yansu 先拼一个约 22.9KB 的基础 prompt,并把近期 activity context 注入进去;发送给 Anthropic-compatible endpoint 时,请求体约 33.3KB,带 14 个工具定义。

并行的 activity context 生成

09:19:11 [activity-context] generate: sessions=48 titles=8 ocr=10 user="so2liu" lookbackH=48
09:19:11 [native-loop] model=claude-haiku-4-5-20251001 messages=1 tools=0
09:19:11 [native] HTTP POST ... bodyBytes=4888
09:19:14 [activity-context] generate: haiku ok, summary_len=254

这说明 activity context 不是简单拼数据库字段,而是会把最近 48 小时的 sessions、titles、OCR 内容交给 Haiku 做一次摘要,然后把摘要注入主 agent prompt。

第二轮请求

09:19:33 [compose-prompt] final-len=21661 (parallel, 10 chunks, skill-domain=chat)
09:19:33 [prompt-func] prompt-bytes=21661 history-bytes=346
09:19:33 [native-loop] model=claude-sonnet-4-6 messages=3 tools=14 max_tokens=64000
09:19:33 [native] HTTP POST ... bodyBytes=32394

第二轮 messages=3,对应上一轮 user、上一轮 assistant、当前 user。history-bytes=346 说明多轮历史以普通 conversation messages 进入模型,而不是被揉进基础 prompt。基础 prompt 仍然重新组合,长度略有变化。

sessions.db 中保存的内容

sessions.db.stream_items 保存了用户消息、assistant 回复、thinking、run created/completed 等 stream items。本次测试会话 1778894338864-0b3505c7d6415 中可见:

0 user      请用两句话介绍一下你自己,不要使用任何工具。
2 thinking  根据 SOUL.md 中的指导...真诚有帮助...有个性...
3 assistant 我是Yansu,一个能帮你写代码、查资料、还能记住你工作习惯的AI助手...
5 user      刚才我让你做什么?请只回答用户上一轮的请求,不要扩展。
7 thinking  上一轮用户说的是:"请用两句话介绍一下你自己,不要使用任何工具。"
8 assistant 我是Yansu,一个能帮你写代码、查资料、还能记住你工作习惯的AI助手...

这里还暴露了一个 prompt 来源:模型 thinking 中提到 SOUL.md,说明主 prompt 内部包含一个类似人格/行为规范的文档,至少有“真诚有帮助、不是表演性、有个性、有观点、简洁”等规则。

会话结束后的记忆 triage

09:19:58 [memory] starting pipeline for conversation:1778894338864-0b3505c7d6415
09:19:58 [memory] triage ExecuteTask starting
09:19:58 [native-loop] model=claude-haiku-4-5-20251001 messages=1 tools=0
09:19:58 [native] HTTP POST ... bodyBytes=12903
09:20:01 [memory] triage: not worth for conversation:... — trivial test interaction...
09:20:01 [triage] memory decision=not_worth worth_memory=false worth_knowledge=false

这说明聊天结束或静置后,Yansu 会把 conversation 送入单独的 memory triage 模型调用,判断是否值得沉淀为 memory/knowledge。本次因为是测试对话,被判定为 not_worth,没有新增记忆。

当前能确定的 prompt 组织模型

Base Prompt约 21-23KB,由 10 个 chunk 并行拼装,skill-domain=chat。
SOUL.md人格/行为规则被纳入基础 prompt。
Activity近 48 小时 activity 由 Haiku 摘要后注入。
History多轮历史作为 Anthropic messages 传入。
Tools主 agent 带 14 个工具定义;辅助摘要/triage 不带工具。
Triage对话后再跑 memory triage,决定是否写入记忆。
本轮没有拿到 HTTPS 请求体完整明文;主请求体仍在 TLS 内。以上结论来自 Yansu 本地日志、sessions.db 和可见 stream items。若要拿完整 JSON body,需要 MITM 证书或运行时插桩。

MITM 抓到完整 Prompt

后续通过 mitmproxy 成功抓到了 Yansu 发往 https://dashboard.yansu.ai/anthropic/v1/messages 的 HTTPS 明文请求体。

方法

  1. 安装 mitmproxy,生成本地 CA。
  2. ~/.mitmproxy/mitmproxy-ca-cert.cer 加入当前用户钥匙串并信任。
  3. 启动 mitmdump -p 8089 --mode upstream:http://127.0.0.1:7897,让 MITM 继续走原 Clash 上游。
  4. 临时把 ~/.zprofile 中代理端口从 7897 改为 8089,启动 Yansu 后立刻恢复为 7897
  5. 确认 Yansu 进程连接 127.0.0.1:8089 后发送测试消息:抓包测试:请只回复 OK,不要调用工具。
  6. 抓包结束后,重启 Yansu 回到 127.0.0.1:7897,并停掉 mitmproxy。
产物路径
原始 MITM JSONL~/Downloads/yansu-anthropic-mitm-capture.jsonl
拆分后的请求体~/Downloads/yansu-mitm-extracted/
主 agent 请求03-claude-sonnet-4-6.json / 03-claude-sonnet-4-6.messages.txt
抓包脚本/Users/yangliu35/daily/yansu_mitm_capture.py

抓到的请求分类

文件模型用途messagestools大小
01-claude-haiku-4-5-20251001Haiku启动后的 activity-context warmup 摘要。10约 4.9KB
02-claude-haiku-4-5-20251001HaikuCrystal intent classifier,判断用户是否要创建交互式应用。10约 1.1KB
03-claude-sonnet-4-6Sonnet主 agent 请求。114约 33.1KB
04-claude-haiku-4-5-20251001Haiku对话后的 memory triage / 是否值得沉淀。10约 6.4KB

主请求结构

主请求的 system 不是简单字符串,而是 Anthropic system content block,内容以 “You are Yansu, a helpful desktop coding assistant...” 开头。大量 Yansu 专属上下文被放在第一条 user message 的 text 中。

{
  "model": "claude-sonnet-4-6",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "<memory-cli>...</memory-cli>\\n\\n<activity-cli>...</activity-cli>\\n\\n<soul-context>...</soul-context>\\n..."
        }
      ]
    }
  ],
  "system": [{"type": "text", "text": "You are Yansu, a helpful desktop coding assistant..."}],
  "tools": [...14 tools...],
  "thinking": {...},
  "stream": true
}

主 message 内部顺序

区块作用
<memory-cli>解释 memory 的定义、维度、自动注入机制、何时主动调用 yansu memory CLI,以及禁止声称“没有记忆”。
<activity-cli>解释 activity sessions、meeting transcripts、OCR、桌面活动 CLI,并要求在相关问题中主动使用。
<soul-context>人格和行为准则,来自 SOUL.md,包含“不要像 chatbot、要有观点、先自己查、尊重隐私”等规则。
<yansu-context>工具和浏览器使用规范,包括 cron/automation 约束、browser server API、browser state rules 等。
用户消息最后追加真实用户输入:抓包测试:请只回复 OK,不要调用工具。

14 个主 agent 工具

cua_driver, TodoWrite, Bash, BashOutput, KillShell, Glob, WebSearch, Write, Grep, ExitPlanMode, Read, Edit, WebFetch, NotebookEdit

关键结论

Prompt 原文索引与构建洞见

完整 prompt 原文已经由 MITM 抓包保存到本机文件。报告中不再内嵌整段第三方 App 提示词全文;下面提供原文文件入口、行号索引、结构解析和关键短摘录,方便直接打开原文件阅读。

原文文件入口

文件用途大小链接
03-claude-sonnet-4-6.json 主 agent 完整 Anthropic JSON 请求体,含 system、messages、tools、thinking、stream。 36KB / 374 行 打开 JSON
03-claude-sonnet-4-6.messages.txt 主请求中 message text 的可读拆分版,最适合直接看 prompt。 22KB / 268 行 打开文本
01-claude-haiku-4-5-20251001.messages.txt activity-context warmup prompt,负责生成当前用户工作状态摘要。 4.6KB / 85 行 打开文本
02-claude-haiku-4-5-20251001.json Crystal intent classifier,请求模型只回答 YES/NO。 1.2KB / 10 行 打开 JSON
04-claude-haiku-4-5-20251001.messages.txt conversation memory triage prompt,判断对话是否值得沉淀为 memory/knowledge。 6.2KB / 100 行 打开文本
yansu-anthropic-mitm-capture.jsonl MITM 原始抓包。Authorization / x-api-key 已隐藏。 69KB / 8 行 打开 JSONL

主 Prompt 行号索引

位置区块说明
JSON system基础系统提示约 1142 字符,定义 Yansu 是桌面 coding assistant,约束文件路径、工具使用、编辑方式等。
messages.txt 第 2 行<memory-cli>解释 memory 的维度、自动注入、CLI 查询时机、禁止说“不记得”。
messages.txt 第 38 行<activity-cli>解释 activity sessions、meeting transcripts、OCR、桌面活动检索命令。
messages.txt 第 63 行<soul-context>人格层。规定 Yansu 的语气、价值观、主动性和隐私边界。
messages.txt 第 128 行<yansu-context>产品/工具层。包含 automation、browser、cron、工具使用规则等。
messages.txt 第 269 行附近真实用户消息最后追加用户输入:抓包测试,只回复 OK。

短摘录

来源短摘录洞见
systemYou are Yansu, a helpful desktop coding assistant身份定义放在 Anthropic system block,而不是普通 user text。
memory-cliWHAT MEMORY IS记忆模块是显式 prompt 教学,不是隐藏 Anthropic tool。
memory-cliAUTO-INJECT (already happening, no action needed)自动检索在宿主侧完成,agent 只消费已注入块。
activity-cliDATA YOU HAVEYansu 明确告诉 agent:桌面活动、OCR、会议 transcript 是“已经拥有的数据”。
soul-contextYou're not a chatbot.人格层很强,解释了 UI 回复里的非模板化风格。
yansu-contextAvailable tools:浏览器、automation 等产品能力通过 prompt 说明和工具 schema 共同暴露。

构建链路复原

1. ClassifyHaiku 判断是否要创建 Crystal / 交互式应用。
2. WarmupHaiku 从 48h activity 生成当前上下文摘要。
3. Compose主进程并行拼 10 个 chunk,日志显示 skill-domain=chat
4. Inject插入 activity summary;若 memory 命中则插入 memory/knowledge context。
5. SendSonnet 请求带 system、1 条 user message、14 个 tools。
6. Triage对话静置后 Haiku 判断是否写 memory/knowledge。

构建洞见

提示词与模型调用

完整 prompt 未能无损恢复,但可以确认 prompt 家族和输出 schema。

可见的 prompt / schema 片段

片段含义
worthMemory, worthKnowledge, worthAutomation, worthHandoffActivity triage 输出 schema。
## Memory Points, - **Dimension**, - **Activity Session**memory/knowledge markdown 生成模板。
AI returned no memory points, AI returned no knowledge points抽取为空时的分支。
<recent-activity-context>新会话可注入近期活动上下文。
Return exactly 5 short, first-person prompts as strict JSONcomposer suggestions 生成。
ADDITIONAL DATA SOURCES (OpenCLI + Computer Use API)agent runtime context 注入。
主程序是 Go Mach-O,构建参数包含 -ldflags "-w -s",调试符号被剥离。prompt 还可能由多段字符串、模板和 runtime activity summary 组合,或由云端服务下发。因此不能仅靠静态 strings 拿到完整提示词。

下一步可做

Computer Use / cua-driver 逆向

Yansu 内置路径:

/Applications/Yansu.app/Contents/Resources/cua-driver-bundle/CuaDriver.app/Contents/MacOS/cua-driver

它是 Swift/原生 macOS 二进制,通用架构 x86_64 + arm64。help 输出显示:

AlmaComputerUse — macOS desktop automation helper (AX + ScreenCaptureKit)
Subcommands:
  daemon [--socket PATH] [--idle-seconds N]
      Start the long-running daemon. Opens a Unix domain socket and serves NDJSON requests.

上游关系

内置 README 指向 https://github.com/trycua/cua/tree/main/libs/cua-driver,并标注 MIT。二进制字符串保留 AlmaComputerUse,因此较可能是 trycua/cua-driver 系列的 Alma/Yansu 改造版本。

Swift 符号恢复出的模块

模块恢复出的职责
DaemonUnix socket daemon、NDJSON 请求、dispatch、idle timer、signal handlers。
MCPServerMCP tools/resources/prompts、token、whitelist、PKCE、JSON sanitise。
AXBridgeAccessibility 读取、snapshot、click/type/press/scroll/setValue、launchApp、windowInfo。
ElementStoresnapshot element cache,resolve/refetch,AXObserver 监听元素销毁。
WindowListCGWindow 枚举、top window、window layer、on screen、visible at point。
VisualIndex基于图片路径和 maxResults 生成 OCR region,字段含 ref/text/bounds/confidence。
EventDispatchclick/drag/scroll/pressKey/typeText,构造 CG/NSEvent,保焦点。
SCPermissionProbeScreenCaptureKit 权限检测。
SecCodePeer校验调用方签名,返回 teamId/bundleId。
PIPManager, LensOverlay窗口预览、overlay、点击/滚动/type badge。

近似 API 面

AXBridge.snapshot(bundle:pid:windowId:depth:interactiveOnly:store:wakeupSeconds)
AXBridge.click(ref:store:strategy:button:clickCount:showCursor)
AXBridge.type(ref:text:replace:store:showCursor)
AXBridge.press(ref:key:store:showCursor)
AXBridge.scroll(ref:direction:pages:store:showCursor)
AXBridge.setValue(ref:value:store)
AXBridge.launchApp(bundle:activates)

EventDispatch.click(at:button:clickCount:pid:modifiers)
EventDispatch.drag(from:to:button:pid:steps)
EventDispatch.scroll(at:direction:pages:pid)
EventDispatch.pressKey(_:pid:)
EventDispatch.typeText(_:pid:)

VisualIndex.index(imagePath:maxResults) -> [Region(ref,text,bounds,confidence)]
WindowList.windows(for: pid) -> [Entry(id,title,bounds,pid,layer)]

分层操作策略

AX populated使用 element_index,AXPress / AXValue / AX actions。
AX sparse尝试 wakeup,Chromium/Electron 可能需要触发 AX。
Visual OCRScreenCaptureKit 截图,Vision OCR 产出 visual_index。
Pixel fallback通过 target pid/window 的 CGEvent 定向点击和拖拽。
Verify动作后重新 get_window_state/screenshot 验证。
PolicyYansu 额外拒绝会抢焦点的快捷键。

底层 API 证据

AXUIElementCopyAttributeValue AXUIElementPerformAction AXUIElementSetAttributeValue CGEventCreateKeyboardEvent CGEventSetIntegerValueField CGEventTapCreate CGWindowListCopyWindowInfo CGWindowListCreateImage SCStream Vision

Browser User 逆向

主程序 Go module 明确依赖 github.com/go-rod/rod v0.116.2。二进制里有大量 Chrome DevTools Protocol 方法名和 browser server 日志。

证据解释
github.com/go-rod/rodGo 侧浏览器自动化核心库。
Target.createBrowserContext, DOMSnapshot.captureSnapshot, Runtime.evaluateChrome DevTools Protocol。
YANSU_BROWSER_BRIDGE, [browser-server] bridge enabledYansu 自己封装了 browser bridge/server。
chromium-browser-snapshots, playwright.azureedge.net/builds/chromium可能按平台下载或复用 Chromium 构建。

结论:Browser User 主要是 Go + go-rod + CDP。Computer Use 负责原生 macOS app 和无法 CDP 接入的 Electron/CEF/国产桌面 app。

多轮上下文与自动检索抓包

2026-05-16 15:10-15:13 通过 MITM 做了三轮真实对话抓包。原始请求已保存为 /Users/yangliu35/Downloads/yansu-multiturn-mitm-capture.jsonl, 结构化拆解在 /Users/yangliu35/Downloads/yansu-multiturn-extracted-final/

轮次 / 请求模型messages自动上下文结论
03-claude-sonnet-4-6 Sonnet 4.6 1: user <recent-activity-context> 第一轮只有当前 user message;包含桌面活动上下文,没有 memory/knowledge 命中块。
05-claude-sonnet-4-6 Sonnet 4.6 3: user, assistant, user <knowledge-context> 第二轮会基于本轮用户发言自动检索并注入 knowledge;不是智能体手动调用工具。
12-claude-sonnet-4-6 Sonnet 4.6 5: 两轮历史 + 当前 user <knowledge-context> + <memory-context> 第三轮再次自动检索,这次同时命中 knowledge 和 memory。
13-claude-sonnet-4-6 Sonnet 4.6 7: 加上 tool_use/tool_result 沿用第三轮注入块 Read 工具执行后,结果作为 Anthropic tool_result 回填给下一次模型请求。

System prompt

有 system prompt,并且主模型请求每次都带同一段约 1143 字符的 system content block。 原文已拆到以下文件,内容一致:

它定义 Yansu 是桌面 coding assistant,强调只能通过工具接触用户系统、文件路径必须是绝对路径、Edit 要精确匹配、长运行命令要后台执行,并注入本轮临时工作目录。

第二次发言是否会自动检索

会。第二轮主请求 05-claude-sonnet-4-6.messages.txt 的结构是:

message 0: user      第一轮用户原话
message 1: assistant  ALPHA
message 2: user      <knowledge-context> + <memory-cli> + <activity-cli> + ... + 第二轮用户原话

这说明检索发生在主模型请求之前,由宿主侧按当前 user utterance 自动完成。第二轮没有任何工具调用,智能体仍然看到了 <knowledge-context>,因此不是“智能体主动搜索后得到”的。

什么时候用自动块,什么时候用 memory CLI

抓包里的提示词规则很明确:每一轮 App 会先语义检索 memory/knowledge 并把命中结果放进 <memory-context> / <knowledge-context>。智能体应优先直接使用这些块。 只有当用户问题需要更深、更窄、或当前自动块不够时,才用 memory CLI 继续查;提示词还明确要求不要为同一件事重复调用 CLI。

读知识时如何展示

第三轮要求“必须使用 Read 工具”后,主模型先产生 tool_useRead {"path":"/Users/yangliu35/.yansu-agent/knowledge/conversation-deca122ee72fa6a6.md"}。 Yansu 执行后在本地 sessions.db.stream_items 写入:

kind=tool    tool_name=Read   tool_input={"path":"...conversation-deca122ee72fa6a6.md"}
kind=result  tool_name=Read   content=带行号的 Markdown 文件内容

随后的 13-claude-sonnet-4-6 请求把 assistant 的 tool_use 和 user 的 tool_result 一起作为消息历史传回模型,最后模型回答标题 LLM 多轮对话中知识/记忆上下文注入的验证方法。UI 侧表现为对话中出现可折叠的工具步骤/step chip,然后展示最终自然语言答案。

写 memory / knowledge 时如何展示

本次测试期间,后台记忆流水线自动生成了两个文件:

用户可见展示不是插入一条 assistant message,而是 toast:New KnowledgeNew Memory。 日志随后显示 memory sync 索引了这两个文件:2 indexed, 0 removed, 22 total files。 这说明写入可以由后台 triage 自动发生,不一定是主智能体工具调用。

多轮上下文构建

Turn 1system + 当前 user,其中可含 recent activity。
Turn 2历史 user/assistant 原样进入 messages;当前 user 前置 knowledge-context。
Turn 3两轮历史 + 当前 user;当前 user 前置 knowledge-context 和 memory-context。
Tool passassistant tool_use + user tool_result 追加到 messages,再请求模型产出最终答复。

关键点:历史对话不是被揉进 system prompt,而是 Anthropic messages 数组里的交替 user/assistant。 每一轮新的自动检索块则被拼在“当前 user message”的最前面。

Embedding 模型与本地资源占用

当前这台机器上的 Yansu memory/knowledge 语义检索使用 all-MiniLM-L6-v2。 这是本地 ONNX 模型,不是每次检索都调用远端 embedding API。

项目实测值
memory 元数据模型memory_meta.model = all-MiniLM-L6-v2
模型文件/Users/yangliu35/.yansu-agent/models/all-MiniLM-L6-v2/model.onnx
词表/Users/yangliu35/.yansu-agent/models/all-MiniLM-L6-v2/vocab.txt
模型目录大小86M,其中 model.onnx86Mvocab.txt226K
向量维度384
缓存向量本机当前 memory_embedding_cache52 条,平均每条 JSON 向量约 8064 字符
memory.db 大小1.0M
当前索引规模23 个文件,30 个 chunks
chunk 配置chunk_tokens=400chunk_overlap=80
Yansu 进程 RSS观察时约 1.25GB RSS;这是整 App 占用,不能全部归因于 embedding

二进制字符串里能看到 ONNX Model (all-MiniLM-L6-v2)ONNX Runtimeset onnx intra-op threadsset onnx inter-op threadsdownload onnx runtime 等证据;模型文件实际存在于用户数据目录。

注意:缓存表字段里的 provider 值显示为 openai,但结合本地 ONNX 模型文件、模型名、二进制字符串和设置页文案, 更像是内部 provider 命名或兼容层遗留字段;当前 active 模型实际是本地 all-MiniLM-L6-v2

推理方式与跨平台判断

这个模型本质上是 Sentence Transformers 系列的 MiniLM encoder,ONNX 推理流程通常是: 用 vocab.txt 做 BERT WordPiece tokenization,构造 input_ids / attention_mask / 可能的 token_type_ids,送入 ONNX Runtime,取 token embeddings 后按 attention mask 做 mean pooling,再做 L2 normalize, 得到 384 维向量。Yansu 二进制里有 ONNX Runtimeset onnx intra-op threadsset onnx inter-op threads 等字符串,说明它不是用 PyTorch,而是本地 ONNX Runtime。

跨平台方面,model.onnxvocab.txt 本身是平台无关的;真正依赖平台的是 ONNX Runtime 动态库和 tokenizer/推理封装。 macOS arm64、Linux x86_64、Linux arm64/aarch64 理论上都能跑。若是 ARM 镜像,建议用 linux/arm64 基础镜像并安装/打包 arm64 版 onnxruntime;不要把 macOS 的 .dylib 或 x86_64 的 .so 复制进 ARM 容器。

速度方面,all-MiniLM-L6-v2 属于小模型,CPU 推理通常足够快,适合本地 memory chunk embedding。 对 400-token 左右 chunk,单条一般是毫秒到几十毫秒量级,批量时吞吐会更好。实际速度主要取决于 ONNX Runtime 线程数、 CPU 架构、是否启用 SIMD/NEON、batch size 和 tokenization 实现。Yansu 设置页里有 embedding test latency 文案,说明它自己也支持测一次端到端延迟。

无法继续确定的部分

动态抓包会碰到个人 token、截图/OCR、聊天记录和云端请求体。建议只在隔离账号或临时环境里做,并先备份 ~/.yansu-agent

建议的下一步逆向路线

  1. 下载或 clone trycua/cua,先看 libs/cua-driver 源码,再与 Yansu 符号表比对。
  2. 安装 Ghidra,对 Yansu 做 string xref:从 worthMemoryexecute memory generationactivity-contextprompt-func 反查调用链。
  3. cua-driver 做 Swift 符号级函数图,重点看 Daemon.dispatchEventDispatch.makeClickEvent
  4. 如要 prompt,优先动态抓 /v1/messages 或 Yansu gateway 请求体,静态反编译只是辅助。