什么是 PocketHook Agent Server?#
代理服务器将 PocketHook 变成一个完整的 AI 助手。无需自己编写响应逻辑,只需连接一个大语言模型(Claude、GPT、Gemini 等)来处理消息、调用工具并返回结构化的 PocketHook 响应——包括快捷指令触发。
服务器运行在您自己的机器上。您的数据由您掌控。
这是一个起点。 服务器附带一组核心工具,并被设计为可由您扩展。添加您自己的集成——邮件、日历、文档、API——让它成为您的。
功能#
- 多提供商 LLM — Anthropic、OpenAI、GitHub Copilot、Google、Mistral、Groq、xAI、OpenRouter、Ollama(本地)、LM Studio(本地)
- OAuth 认证 — GitHub Copilot 和 OpenAI Codex,通过设备码/浏览器流程
- 代理工具 — Shell 命令、文件读写、目录列表、网络搜索、网页抓取、开发服务器管理
- 框架/用户分离 — 框架文件(
skills/、custom-tools/、config/)保持只读。您的自定义位于data/user/(技能、自定义工具、说明、类型化偏好)。框架更新不会覆盖您的工作 - 类型化用户偏好 — 将偏好的地图应用或隧道域名等值存储在
data/user/prefs.json中。在技能中通过{{prefs.key}}引用,服务器会在加载时进行替换 - 一次调用完成编程任务 —
run_code_job元工具创建一个 prompt 类型的后台任务(由您配置的 LLM 运行)并在一步内向用户发送确认,替代易出错的"respond + create-job"模式 - 类型化协议工具 — 六个专用
respond_*工具(respond_text、respond_image、respond_buttons、respond_shortcut、respond_html、respond_sequence),加上类型化任务工具(create_once_job、create_cron_job)和类型化工作区工具(create_project、list_projects、delete_project)。在到达设备之前,模式会拒绝格式错误的 URL、按钮语法以及type/schedule组合 - 类型化定制写入器 —
create_user_skill和create_custom_tool使用正确的前置元数据构建用户层 markdown,因此加载器始终能解析它们,代理也无需手写这些文件 - 后台任务 — 使用 cron 表达式或简单间隔的一次性或重复任务
- 动态技能 — 将快捷指令和行为规则定义为
.md文件。仅紧凑索引加载到提示中;完整内容通过load_skill工具按需获取 - 自我管理技能 — 代理可以创建、编辑和删除技能定义(写入始终落在用户层)
- 语义记忆 — 使用嵌入(Ollama、LM Studio 或 OpenAI)的向量搜索。记忆由 LLM 自动分类到 wing/room/hall/status 维度
- 知识图谱 — 带有自动失效的时间三元组存储。多值关系共存;单值事实自动替换
- PARA 方法(含项目结束级联) — 每条记忆都标记了状态(Project、Area、Resource、Archive)。项目结束时,单次
complete_project调用即可归档其向量、使与其 slug 关联的所有计划三元组失效并记录完成 — 一次调用而非三次 - 混合召回 — 使用倒数排名融合将 FTS5 关键词搜索与向量语义搜索相结合
- 长期记忆 — 当语义记忆禁用时,使用 SQLite + FTS5 全文搜索作为回退
- 带隧道契约的开发服务器管理 — 启动、停止和列出开发服务器。当请求
tunnel: true时,服务器在启动前和启动后都会强制执行 — 不可达的 localhost 服务器绝不会被悄悄保留运行 - 自动 URL 清理 — 如果代理在响应中留下
localhostURL,respond_*工具会将其重写为匹配的隧道 URL,以便您的手机始终能获得可达链接 - 自定义工具 — 代理可以安装 CLI 工具并将其注册为新功能
- 版本控制 — 工作区文件的自动 git 版本控制;技能和权限的配置备份
- Web 仪表板 — 后台任务的实时概览,可按用户自定义。
/dashboard和/api/jobs在设计上是未认证的——请在网络层(Tailscale ACL、防火墙、带基本认证的反向代理)限制访问,或者在不需要时设置DASHBOARD=false - HTTPS 隧道 — 内置 Tailscale、ngrok 和 Cloudflare Tunnel 支持
- 系统服务 — 在 macOS、Linux 或 Windows 上安装为持久服务
- 速率限制 — 可配置阈值的逐令牌请求限制
要求#
- Bun 运行时
- LLM 提供商的 API 密钥或 OAuth 凭证
- (可选)Tailscale、ngrok 或 cloudflared(用于 HTTPS 隧道)
快速开始#
git clone https://github.com/pockethook-app/pockethook-agent-server.git
cd pockethook-agent-server
bun install
# 交互式设置 — 选择提供商、模型、认证令牌、端口
bun run setup
# 启动服务器 + HTTPS 隧道
bun run dev:tunnel
设置向导将引导您选择 LLM 提供商、配置认证和设置工具权限。
运行后,将显示的 URL 复制到 PocketHook 设置中:
| PocketHook 设置 | URL |
|---|---|
| 服务器 URL | https://your-host |
| 健康检查 URL | https://your-host/health |
| 轮询 URL | https://your-host/jobs |
工作原理#
- 您在 PocketHook 中发送消息
- 服务器将消息连同对话历史、召回的记忆和可用工具一起转发给您选择的 LLM
- LLM 处理消息——它可以运行 Shell 命令、读写文件、搜索网络、安排后台任务、记忆事实或启动开发服务器
- 响应以 PocketHook 格式(
msg+shortcut+data+url)返回 - PocketHook 显示消息并在您的设备上执行快捷指令
支持的 LLM 提供商#
| 提供商 | 认证 | 默认模型 |
|---|---|---|
| Anthropic | API 密钥 | claude-sonnet-4-20250514 |
| OpenAI | API 密钥 | gpt-4.1-mini |
| OpenAI Codex | OAuth | gpt-5.1-codex-mini |
| GitHub Copilot | OAuth | claude-sonnet-4 |
| Google (Gemini) | API 密钥 | gemini-2.5-flash |
| Mistral | API 密钥 | mistral-medium-latest |
| Groq | API 密钥 | llama-3.3-70b-versatile |
| xAI (Grok) | API 密钥 | grok-3-mini-fast |
| OpenRouter | API 密钥 | anthropic/claude-sonnet-4 |
| Ollama(本地) | 无 | llama3.2 |
| LM Studio(本地) | 无 | qwen3.5-4b-mlx |
使用 bun run switch 随时切换提供商。Ollama 和 LM Studio 完全在您的机器上运行——无需 API 密钥,数据不会离开您的网络。
记忆#
记忆系统有三个层次,各自服务于不同的目的。
语义记忆的设计结合了 MemPalace(一种将记忆组织成 wing、hall 和 room 的记忆宫殿架构)和 Tiago Forte 的 PARA 方法(Projects、Areas、Resources、Archive——知识生命周期管理)的理念。
对话记忆#
带有 FTS5 全文搜索的 SQLite。所有消息都附带时间戳和会话 ID 存储。
- 短期 — 每个会话保留最近
MAX_HISTORY条消息在内存中 - 长期 — 所有消息持久化存储在 SQLite 中,可通过 FTS5 关键词匹配搜索
- 每轮召回 — 当语义记忆开启时,
MAX_RECALL控制每轮注入提示的相关记忆数量 - 会话在
SESSION_TTL_MINUTES后过期,但长期记忆永久保留
使用 bun run memory 进行交互式调整。
语义记忆#
需要 VECTOR_MEMORY=true 和嵌入提供商(Ollama、LM Studio 或 OpenAI)。
每条记忆被嵌入为向量,并由 LLM 自动分类到四个维度:
- Wing — 实体:
user、person:john、project:blog、place:london - Room — 类型:
facts、preferences、events、decisions、requests - Hall — 主题:
personal、tech、health、travel、food、work - Status — PARA 分类:
project、area、resource、archive
当您提问时,实体提取会将向量搜索聚焦到最相关的 wing。结果使用倒数排名融合与 FTS5 关键词结果合并——这样您可以同时获得关键词匹配和语义匹配的最佳效果。
知识图谱#
用于结构化、持久事实的时间三元组存储:
- 三元组:
(subject, predicate, object)带有valid_from/valid_until时间戳 - 单值谓词(
lives_in、partner)在更新时自动使旧值失效 - 多值谓词(
child、friend、hobby)共存而不失效 - 知识图谱事实与召回的记忆一起注入每次对话
当您告诉代理*“我搬到柏林了”*时,它会使旧的 lives_in 三元组失效并自动创建新的——全自动。
PARA 生命周期#
每条记忆都标记了 PARA 状态:
- Project — 活跃的、有时间限制的工作
- Area — 持续的责任
- Resource — 参考资料(列表、推荐、教程)
- Archive — 已完成或已取消的项目
当项目完成时,代理使用语义相似度仅归档该项目的记忆,同时保留参考资料以供未来使用。
项目结束级联#
说一句*“我要取消巴塞罗那之行”*,单次工具调用就能处理一切:
- 归档项目的向量(与巴塞罗那相关的 events、decisions、requests)。
- 使所有谓词与项目 slug 匹配的活跃知识图谱三元组失效(
scheduled_visit_barcelona、planning_visit_barcelona、confirmed_visit_barcelona)。 - 将完成记录为一个新三元组:
(user, "cancelled_visit_barcelona", "2026-04-15")。
匹配是边界感知的 — 一个名为 revisit_barcelona 的不同项目会保持不变。代理不再需要按正确顺序编排三次独立调用,因此较小的模型也能正确处理。
如果 VECTOR_MEMORY 被禁用或嵌入提供商不可达,系统会无错误地回退到仅 FTS5 模式。
技能#
技能是 skills/ 目录中的 .md 文件,定义代理可以触发的 iOS 快捷指令和/或行为规则。它们使用动态加载:仅紧凑索引(标题、描述、快捷指令列表)注入系统提示。代理通过 load_skill 工具按需加载完整内容,在添加更多技能时保持低令牌使用量。
每个技能文件使用 YAML 前置元数据:
---
title: Notes
description: Create notes on the user's device with a title and body
shortcuts: [newNote]
target: mac
sync_app: Notes
---
### New Note
Shortcut name: `newNote`
Creates a new note on the user's device.
Data fields:
- title (string, required): Note title
- content (string, required): Note body
前置元数据字段#
| 字段 | 必填 | 描述 |
|---|---|---|
title | 是 | 人类可读的名称 |
description | 是 | 用于向代理展示的技能索引中的一句话描述 |
shortcuts | 是 | 文件中定义的快捷指令名称数组。对于仅包含行为规则的技能,使用 [] |
target | 否 | 快捷指令执行位置:device(默认,发送至 iOS)或 mac(在服务器上运行) |
sync_app | 否 | 服务器端执行后在后台唤起以触发 iCloud 同步的应用(例如 Notes、Calendar、Reminders)。省略或使用 none 以跳过 |
技能也可以是没有快捷指令的行为规则(例如"如何计划家庭旅行")。这种情况使用 shortcuts: []。
代理可以在被要求时创建和管理技能——让它*“创建一个控制灯光的技能”*,它会为您编写 .md 文件。新建和编辑的技能始终落在您的用户层(data/user/skills/),因此框架更新永远不会覆盖它们。请参阅下面的自定义您的代理部分。
在 Mac 服务器上执行快捷指令#
当技能设置了 target: mac 时,快捷指令会通过 shortcuts run CLI 在 Mac 服务器上静默运行,而不是发送到 iOS 设备。这非常适合创建 iCloud 同步内容的操作——笔记、提醒事项、日历事件——因为结果会自动同步到所有设备,无需 PocketHook 应用做任何事情。
工作原理:
- 代理决定应该运行某个快捷指令(例如"用今天的会议笔记创建一条笔记")
- 服务器以 PocketHook iOS 使用的相同包装格式,通过 stdin 以 JSON 形式传递数据,调用
shortcuts run "shortcutName" - 如果设置了
sync_app,服务器会短暂在后台打开该应用(open -gj -a Notes)以强制 iCloud 同步,然后在 5 秒后关闭它 - 用户在聊天中收到确认消息;快捷指令本身不会发送到设备
要求:
- 服务器必须运行在 macOS 上——
shortcuts run仅限 macOS。在其他平台上,服务器会记录警告并回退到设备执行 - 快捷指令必须已安装在服务器 Mac 的 Shortcuts.app 中
- 快捷指令应当接受 Dictionary 作为输入(PocketHook 会将数据包装为
{ context, timestamp, app, data })
何时使用 target: mac:
- iCloud 同步的操作(Notes、Reminders、Calendar)——结果无论如何都会同步到每台设备
- 希望不占用 iOS 设备的长时间处理
- 不需要与 iPhone UI 交互的任何快捷指令
何时保持 target: device(默认):
- 需要 iPhone 专属功能的快捷指令(相机、精确定位、本地应用自动化)
- 需要用户交互式输入的快捷指令
- 使用仅限 iOS 应用 App Intents 的快捷指令
后台任务#
让代理安排任务,它会处理其余的一切:
- “每天早上8点检查天气并创建笔记”
- “每小时运行这个脚本”
- “30分钟后提醒我检查邮件”
任务支持 cron 表达式(0 8 * * *)和简单间隔(30m、1h、2d)。结果在 PocketHook 轮询 /jobs 端点时传递。
两种执行类型:
- Shell — 运行 bash 命令,捕获输出。完成后可触发快捷指令
- Prompt — 由拥有完整工具访问权限的 AI 代理处理,存储完整的 PocketHook 响应
开发服务器#
当代理在工作区创建 Web 项目(Hugo、Astro、Next.js、Flask、Go 等)时,它会主动提议进行服务:
- 预览 — 在自动分配的端口上启动本地开发服务器以快速查看
- 公开 — 启动服务器并通过 HTTPS 隧道暴露,使其可从任何地方访问
代理管理生命周期:启动、停止和列出运行中的服务器。主服务器停止时,所有服务器都会被清理。
隧道契约#
当代理请求隧道暴露启动服务器时,运行时会强制执行:如果未安装任何隧道工具(Tailscale、ngrok、cloudflared),服务器将拒绝启动。如果在派生之后隧道设置失败,孤立进程会被停止,并明确通知代理 — 以便它可以回退到预览模式,或请您安装隧道。启用隧道时,返回的 URL 始终是隧道 URL,并附带本地 URL 仅限主机的说明。
作为安全网,每个 respond_* 工具会对其消息进行后处理:任何悄悄混入回复的 localhost 或 127.0.0.1 URL,只要托管服务器有对应的隧道,就会被自动改写为匹配的隧道 URL。当无法改写时,您会在日志中看到警告,而不是在手机上看到损坏的链接。
仪表板#
/dashboard 处的内置 Web 仪表板显示后台任务的实时概览。
设计上未认证。
/dashboard和/api/jobs都是开放的GET端点——任何能访问主机的人都可以列出任务。请在网络层(Tailscale ACL、防火墙、带基本认证的反向代理)限制访问,或者在不需要时设置DASHBOARD=false。PocketHook iOS 应用不使用这些端点。
它完全可自定义:
- 快速编辑 — 在
workspace/dashboard/中放置dashboard.html进行简单自定义 - 完整项目 — 在
workspace/dashboard/中创建框架项目(Svelte、React、Vue 等),构建输出到dist/
让代理自定义您的仪表板,它会处理其余的——每个用户获得独特的、个性化的仪表板。
自定义工具#
代理可以安装 CLI 工具并将其注册为新功能——无需修改服务器代码即可自我扩展。
例如,说*“安装 Playwright 并用它来截图”*。代理将:
- 安装依赖
- 创建工具定义(一个简单的
.md文件) - 在未来的对话中使用新工具
自定义工具热重载——无需重启。删除 .md 文件即可移除工具。
版本控制#
所有用户数据都自动进行版本控制:
- 工作区文件 — 使用
workspace/内的本地 git 仓库追踪。每次写入都创建自动提交。让代理*“撤销上次更改”*或手动使用git revert HEAD - 配置文件 —
config/agent-instructions.md、config/personality.md、skills/和permissions.json在每次修改前备份。每个文件最多 20 个版本
git 是可选的——如果未安装,工作区变更不会被版本控制。配置备份始终有效。
自定义您的代理#
代理服务器附带一个极简的框架基础,并期望您在其之上叠加自己的自定义内容。运行时将二者分离,因此框架更新永远不会破坏您的工作。
框架 vs 用户#
pockethook-agent-server/
├── skills/ # 框架自带的技能(只读)
├── custom-tools/ # 为框架自带工具预留(只读)
├── config/
│ ├── agent-instructions.md # 框架代理说明(只读)
│ └── personality.md # 框架人格(只读)
└── data/user/ # 您的自定义放在这里(被 git 忽略)
├── skills/ # 您自己的技能(按文件名覆盖基础)
├── custom-tools/ # 您安装的自定义工具
├── instructions.md # 您对代理说明的追加内容
└── prefs.json # 以 {{prefs.key}} 引用的类型化值
用户自定义内容通过专用的类型化工具(create_user_skill、create_custom_tool)写入,因此生成的文件始终匹配加载器的格式。write 工具还会拒绝任何位于 skills/、custom-tools/ 或 config/ 下的路径,并将代理重定向至 data/user/* — 这样即使是直接的文件编辑也最终会落在用户层。
关于基础
custom-tools/目录的说明。 目前它只包含一个加载器会忽略的模板(_example.md) — 代理为您安装的每个工具都会进入data/user/custom-tools/。保留这个目录是为了让未来的框架版本能够提供可选的内置工具而不会覆盖您已安装的内容。届时工具名冲突时您的用户层文件仍然优先,因此无需迁移。
四种自定义方式#
| 您想更改什么 | 放在哪里 | 示例 |
|---|---|---|
| 快捷指令或行为技能 | data/user/skills/<name>.md | “创建一个记录我锻炼的技能” |
| 包装为代理能力的 CLI 工具 | data/user/custom-tools/<name>.md | “安装 ffmpeg 并让我用它做转换” |
| 全局规则(“始终用英语回复”、“不要使用表格”) | data/user/instructions.md | “从现在起,始终用3个要点总结文章” |
| 技能引用的类型化默认值 | data/user/prefs.json | “我默认的路线起点是马德里” → {"routeOrigin": "Madrid"} |
您无需手动编写这些文件。只需告诉代理您想要什么,它会自动选择合适的层。
使用 {{prefs.*}} 的类型化偏好#
假设您编写一个需要知道默认起点的路线规划技能。不要将"Madrid"硬编码到技能中,而是引用偏好:
- **起点**: {{prefs.routeOrigin}},除非用户指定了不同的起点。
并将值存储在 data/user/prefs.json 中:
{
"routeOrigin": "Madrid, Spain",
"preferredMapsApp": "apple",
"tunnel": { "domain": "my-host.ts.net" }
}
服务器会在加载技能时替换占位符。嵌套键({{prefs.tunnel.domain}})也可以使用。未知键保持不变,因此拼写错误仍然可见。
直接编辑框架基础#
如果您正在自托管并想调整框架本身,可以直接编辑 config/agent-instructions.md、config/personality.md、skills/ 或 custom-tools/ — 当您使用文件编辑器时服务器不会阻止您。但代理不会在对话中向这些路径写入。并且框架更新会覆盖您的编辑。对于想保留的内容,请优先使用用户层。
扩展服务器#
- 自定义工具 — 让代理安装 CLI 工具;它们会自动落在
data/user/custom-tools/ - 添加技能 — 让代理创建技能;文件会进入
data/user/skills/ - 更改行为 — 让代理应用全局规则;它会追加到
data/user/instructions.md - 配置权限 — 运行
bun run permissions控制代理可以使用的工具 - 添加内置工具 — 在
src/tools.ts中实现新的工具函数以进行更深入的集成(需要 fork 服务器)
配置#
所有设置存储在 .env(由 bun run setup 创建)中。主要选项:
| 变量 | 默认值 | 描述 |
|---|---|---|
AUTH_TOKEN | (必填) | 与 PocketHook 的共享密钥 |
LLM_API_KEY | (必填) | LLM 提供商 API 密钥 |
LLM_PROVIDER | anthropic | 提供商名称 |
LLM_MODEL | claude-sonnet-4-20250514 | 模型 ID |
LLM_REASONING | off | 推理强度:off、minimal、low、medium、high、xhigh。更高级别会增加隐藏的思考令牌(更慢且更昂贵)。不支持的模型会忽略此项 |
PORT | 3000 | 服务器端口 |
AGENT_NAME | PocketHook Assistant | 代理显示名称 |
MAX_HISTORY | 50 | 短期记忆中的消息数 |
MAX_RECALL | 5 | 语义召回每轮返回的记忆数(仅在 VECTOR_MEMORY=true 时) |
SESSION_TTL_MINUTES | 60 | 会话过期时间 |
VECTOR_MEMORY | false | 启用语义记忆(需要嵌入提供商) |
EMBEDDING_PROVIDER | ollama | 嵌入提供商:ollama、lm-studio 或 openai |
EMBEDDING_MODEL | nomic-embed-text | 嵌入模型名称 |
EMBEDDING_URL | (自动) | 嵌入 API URL |
EMBEDDING_API_KEY | — | OpenAI 嵌入 API 密钥 |
LOG_LEVEL | info | 日志级别:debug、info、warn、error |
RATE_LIMIT_MAX | 30 | 每窗口最大请求数 |
DASHBOARD | true | 启用 Web 仪表板(/dashboard 路由) |
INSTANCE_NAME | (项目目录基本名,去掉 pockethook- 前缀) | 用于系统服务标签、日志目录和进程匹配的后缀。在同一台机器上运行多个检出时显式设置 |
完整配置参考请查看 GitHub 仓库。
作为服务运行#
安装为自动启动的持久服务:
bun run service install
| 平台 | 后端 | 服务位置 |
|---|---|---|
| macOS | launchd | ~/Library/LaunchAgents/com.pockethook.${INSTANCE_NAME}.plist |
| Linux | systemd(用户) | ~/.config/systemd/user/pockethook-${INSTANCE_NAME}.service |
| Windows | NSSM | PocketHook-${PascalCase(INSTANCE_NAME)}(位于 Windows 服务管理器中) |
INSTANCE_NAME 默认为去掉 pockethook- 前缀后的项目目录基本名(例如,位于 pockethook-agent-server/ 的检出会变成 agent-server)。在同一台机器上运行多个检出而不冲突时请显式设置 — 每个实例会保留自己的 data/ 和日志。
使用 bun run service status、restart、stop 或 uninstall 管理。
安全#
- HTTPS 必需 — PocketHook 对所有 URL 强制要求 HTTPS
- Bearer 令牌认证 — 应用和服务器之间的共享密钥
- 速率限制 — 逐令牌限制防止滥用
- 沙箱工具 — Shell 命令和文件访问受权限限制
- 阻止模式 — 危险命令(
sudo、rm -rf /)默认被阻止 - 工作目录边界 — 代理无法逃出其指定目录
- 敏感文件保护 —
.env、.git、*.key、*.pem被阻止代理访问 - 自动版本控制 — 所有工作区变更通过 git 追踪,便于回滚