在本地跑一个 amp(ampcode.com)服务端 —— 让官方 amp CLI 完全在本机工作:LLM 调用走你自己的 端点(CLIProxyAPI / ollama / vllm / one-api / opencode Zen Go / OpenAI / Anthropic …),线程、 项目、附件全部落在本地磁盘,不依赖云端。
定位:CLI 是 executor(在本地执行工具),服务端是 agent 大脑(跑 LLM、编排回合)。amp-local 替代后者。协议细节来自对 amp bundle 与真实 ampcode.com 流量的逆向,见 notes/protocol.md。
# 1. 准备配置(默认读取 ~/.config/amp-local/config.json,可用 AMP_LOCAL_CONFIG 指定)
mkdir -p ~/.config/amp-local
cp config.example.json ~/.config/amp-local/config.json
$EDITOR ~/.config/amp-local/config.json
# 2. 启动
bun src/main.ts
# 3. 让 amp CLI 连到本地。未配置 users 时 key 任意;配置 users 后使用对应 apiKey
AMP_URL=http://127.0.0.1:9876 AMP_API_KEY=local amp -x "你好"Web 前端(webapp)构建:cd web && bun install && bun run build(产物 web/build/,
服务器启动后直接访问 http://127.0.0.1:9876/)
依赖:Bun。bun install 安装依赖后即可运行。
兼容性基线:0.0.1787286307-ga0cd7a(0821)。普通 execute、Docker Orb、
MCP/Plugin/Skill repository 列表、Sites 明确拒绝路径及附件 resumable upload 已实测;
0821 的 actor/bridge 调用集合与 0820 相同,核心 WS/bare 无需改版。
amp CLI (executor) ──REST + WebSocket──▶ amp-local (agent 大脑)
本地执行工具 · LLM 适配器(多 provider / 多协议方言)
文件/Shell/工具注册 · 回合编排、工具调度(tool_lease)
· 线程/项目/附件/标签/技能存储
两条通道:
- REST(
/api/internal、/api/thread-actors、/actors/*、附件、线程搜索、远程线程等) - WebSocket
threadActor:文本帧 JSON-RPC 2.0(心跳ping→pong);CLI 的 executor 连接、工具结果回传、 回合消息流(delta / message_added / agent_state / tool_lease…)都走这里userActor/ runner:二进制 rivet bare 帧(client-protocol v4);侧边栏摘要、pin、runner 注册、 远程线程意图等
支持 runner(Agents Anywhere) 闭环:CLI 注册为 runner 后,/api/remote-thread 创建的远程线程
通过 runnerIntentsUpdated 事件推给对应 runner 自动执行。
所有配置在 ~/.config/amp-local/config.json(可用 AMP_LOCAL_CONFIG 指定),无需环境变量。结构:
| 字段 | 说明 |
|---|---|
port / dataDir / user |
监听端口、数据目录、本机用户标识(userId 同时用作项目 namespace 前缀) |
provider baseUrl |
LLM 端点,不带尾斜杠(如 http://127.0.0.1:8317/v1) |
provider apiKey / apiKeyEnv / apiKeyFile |
明文 / 环境变量名 / 文件路径(文件内容 trim 后使用);三选一,优先级 apiKey > apiKeyEnv > apiKeyFile。路径不做 ~ 展开,请写绝对路径 |
provider provider |
引用另一个 provider,继承其 baseUrl/apiKey/protocol 后再覆盖 |
llm.default |
全局默认 provider;model 为默认模型 |
llm.modes.<mode> |
每个 mode 独立绑定;缺 provider/baseUrl 时走全局默认;model 支持 models 数组 |
mode models |
fallback 模型链:数组按序尝试,上游模型间歇不可用时自动降级(如 ["gpt-4o", "gpt-4o-mini"]) |
protocol |
协议方言,见下 |
| 值 | 端点 | 适用 |
|---|---|---|
responses(默认) |
/responses(OpenAI Responses API) |
CLIProxyAPI 默认;直连 OpenAI 原生 |
completions |
/chat/completions |
ollama / vllm / one-api / opencode Zen Go 等 OpenAI 兼容网关 |
anthropic |
/messages |
直连 Anthropic(适配器可用,部分特性尚待完善) |
| 变量 | 作用 |
|---|---|
AMP_LOCAL_CONFIG |
配置文件路径(默认 ~/.config/amp-local/config.json) |
AMP_LOCAL_PORT |
端口 |
AMP_LOCAL_DATA_DIR |
数据目录 |
AMP_LOCAL_LLM_BASE_URL / AMP_LOCAL_LLM_API_KEY / AMP_LOCAL_LLM_MODEL |
全局默认 provider 的旧式覆盖 |
AMP_LOCAL_MODELS |
JSON 对象 {mode: model} 兼容旧写法(配置文件 modes 优先) |
- 多 provider + 多模式绑定:
low / medium / high / ultra各自映射到 provider + 模型; CLI 切换 mode 即换模型 - 协议方言适配:同一套 amp 消息块(text/thinking/tool_use/tool_result)自动转换为
responses/completions/anthropic格式,各上游怪癖(completions 要求 tool 消息紧跟 assistant、 responses 的 item_id 关联、reasoning 回传等)隔离在适配器内部 - 模型 fallback 链:
models数组按序降级 - 线程全生命周期:创建/恢复/导入(
/request/import)、归档、pin、重命名、标签、全文搜索、 编辑消息、重试、truncate、turn 串行队列、草稿 - 项目注册表:
projects.json持久化;listAccessibleProjects / getProject / createProject / updateProject / deleteProject / resolveProjectByRepositoryURLs等,支持 git remote 匹配 - 服务器实现工具(直接进 LLM 工具集,不走本地 executor):
read_threadcreate_threadthread_interactwait_for_threadsget_scheduleset_scheduleupdate_scheduleclear_scheduleTaskfinderview_medialibrarianlist_agent_modeslist_runnersget_current_user_identity - Skills:
loadSkills从~/.agents/skills、~/.config/agents/skills、~/.claude/skills及 工作区.agents/skills、.claude/skills读取 SKILL.md,CLI 落盘后 skill 工具直接可用 - 附件:普通 base64 上传(
/api/attachments)和 0820+ 可恢复流式上传 (/api/attachments/resumable,最大 512 MiB)→ SHA-256 存盘 → 多级重定向下载; 0821 TUI 超出 4 张的图片会在后台复用此 resumable 通道 (/attachments→/user-content/attachments→/raw-attachments) - Runner / 远程线程(Agents Anywhere):
registerRunner/runnerHeartbeat//api/remote-thread/createRemoteExecutorThread完整闭环 - 插件生命周期:回合发
session.start / agent.start / agent.end插件消息 - 标题自动生成:无标题线程取首条用户消息截取 48 字
- Web 前端(
web/,SvelteKit 5):对齐 ampcode.com 的 webapp ——- 侧边栏线程列表 + 线程视图(WS 实时消息:thinking 折叠/富文本/工具调用)
- Command Palette(Ctrl+K):命令面板(Global/Thread/Navigation 三组 40+ 命令, 输入过滤、Prompt 区可直接用文本建线程/搜索)+ 新建线程模式(Executor 选 New Orb 或本地 Runner、Project、Mode)+ 无匹配场景对齐真实
- 快捷键:Ctrl+K 面板 / Ctrl+Shift+K 搜索 / Ctrl+/ Puck / Alt+Enter 新建 / Ctrl+M/J/S 新建表单(Executor/Project/Mode)
- Projects 页(项目卡片 + New Project 对话框对齐真实)
- 模块化架构(lib/ui + lib/thread 组件分层);服务器 serve 静态产物(SPA fallback)
| 路径 | 内容 |
|---|---|
threads/<TID>.json |
线程 + 全部消息(消息粒度持久化,中断/被杀不丢) |
projects.json |
项目注册表 |
attachments/ |
附件(sha256 文件名 + .meta.json 元数据) |
REST 端点(详见 notes/protocol.md):
| 端点 | 说明 |
|---|---|
POST /api/internal?<method> |
核心方法网关:getUserInfo loadPlugins listAgentModes loadSkills archiveThread、项目/线程/标签/账单等(方法在 query 或 body.method) |
POST /api/thread-actors |
创建/复用线程,返回 {threadId, wsToken, ...} |
POST /api/thread-actors/<id> |
标记线程已导入 |
POST /api/user-actor-credentials |
userActor 凭据 |
POST /api/remote-thread |
模拟 web 端远程创建线程(Agents Anywhere) |
POST /api/attachments |
附件上传 |
POST /api/attachments/resumable |
0820+ 大媒体 prepare / signed PUT / complete,可恢复且不把文件整体读入内存 |
GET /api/threads/find?q= |
线程全文搜索 |
GET /actors/metadata、GET /actors/actors |
rivet actor 解析/元数据 |
POST /gateway/…/request/import |
线程数据导入 |
POST /gateway/…/action/<name> |
userActor HTTP action(rivet HTTP 通道) |
WebSocket:
threadActor:JSON-RPC 2.0 文本帧;CLI→服务器executor_connectexecutor_tools_registerclient_append_user_msgexecutor_tool_result等;服务器→CLI 初始化序列 +delta/message_added/agent_state/tool_lease等回合事件流userActor:bare/cbor 二进制帧(Init/ActionRequest/Event);getRecentThreads、setThreadPinned、registerRunner、runnerHeartbeat、订阅事件
认证:未配置 users 时接受任意 API key;配置 users 后仅接受对应 apiKey,
WS 使用 /api/thread-actors 发放的 wsToken。本机默认可用 AMP_API_KEY=local。
bun install # 安装依赖(@rivetkit/bare-ts、cbor-x、vbare)
bun run typecheck # tsc --noEmit
bun src/main.ts # 启动服务器目录结构:
src/
main.ts # 入口:Bun HTTP + WebSocket
config.ts # 声明式配置 + 环境变量覆盖
rest-api.ts # HTTP 端点(fetch handler)
ws-handler.ts # threadActor 的 JSON-RPC 消息分发
agent-loop.ts # agent 回合循环 + turn 队列 + 标题生成
llm.ts # LLM 适配器层(blocks ↔ 各协议方言 + SSE 解析)
server-tools.ts # 服务器实现工具
server-state.ts # 共享状态 + rivet bare 帧编解码
store.ts # 线程本地存储
projects.ts # 项目注册表
thread-format.ts # CLI 线程格式转换
types.ts # 协议类型定义
notes/
protocol.md # 协议规格(逆向自真实流量)
server-tools.md # 服务器工具实测格式
web/
src/routes/ # SvelteKit 页面(layout/threads/[id]/projects)
src/lib/ # 前端逻辑分层
config.ts/types.ts # 配置 + 共享类型
api.ts/ws.ts # API 客户端 + threadActor WS 客户端
commands.ts # Command Palette 命令定义(对齐真实枚举)
markdown.ts # 富文本渲染 + 消息块提取
ui/ # Sidebar / CommandPalette / NewThreadForm / ExecutorPicker
thread/ # MessageList / MessageItem(WS 消息渲染)
- 未配置
users时不校验 API key,仅适合本机或可信网络;团队部署应配置users anthropic适配器可用但部分特性未完善;模型注册表(src/models.ts)为逆向数据,部分条目不完整- domains/sites/secrets/global repositories/account MCP 当前只提供真实空列表;云端管理写操作和
0821 新增的
createSite/updateSite返回明确not_supported - 未知 internal API 默认返回
not_supported,不会再用成功{}掩盖缺失实现并触发 CLI schema 崩溃 - Gmail 服务、云 model-router 管理及 0821
list_schedules尚未在 amp-local 实现 - 单进程内存态会话(session),重启后线程从磁盘恢复
对齐 Amp 的 orb 架构:每线程一个沙箱执行器,amp --orb-execute -x "msg" 创建 orb 线程后,
服务器在沙箱工作区启动 amp --headless=<threadId>(agent-headless)作为该线程的 executor,
CLI 以 observer 连接查看结果。工具在沙箱执行器上运行(不是用户本地 CLI)。
配置(~/.config/amp-local/config.json):
{
"orb": {
"enabled": true,
"executor": "docker",
"dockerImage": "amp-agent:latest",
"ampDir": "",
"workspaceDir": "~/.local/share/amp-local/sandboxes"
}
}执行器后端(orb.executor):
- local(P0):服务器本地子进程沙箱,无隔离(单机验证)
- docker(P1):每线程一个容器(进程/文件隔离,团队化基础)
Docker 模式要点:
- 镜像:
docker build -f docker/agent.Dockerfile -t amp-agent:latest .(全部组件从官方渠道安装, 零本机文件 COPY,可分享;固定版本--build-arg AMP_VERSION=<version>; amp=static.ampcode.com / websocat=github.com/vi/websocat / base=debian:bookworm-slim) - docker 模式:镜像内装官方 amp CLI(
amp --headless=<tid>运行),无需宿主的 amp bundle; 升级 amp 版本 = 重建镜像(--build-arg AMP_VERSION) - local 模式(宿主子进程):
orb.spawnCommand可配["amp"](官方 CLI)或 bun 方式 - 工作区
sandboxes/<threadId>bind mount 到容器/workspace(双向可见,容器删除文件保留) - bridge 网络 + 动态端口映射(多容器并发):容器内 sshd 固定 2222,宿主端口按容器分配(2200-2299)
- headless 通过
host.docker.internal连服务器(--add-host) - 容器 root + 定向 drop 危险能力(NET_RAW/NET_ADMIN/SYS_ADMIN/SYS_PTRACE/SYS_MODULE/MKNOD/SETPCAP/SYS_BOOT/SYS_TIME)+
no-new-privileges(保留 sshd 所需 DAC/SETGID/SETUID/SYS_CHROOT) - 资源限制:
--memory 2g --cpus 2 --pids-limit 256;--restart unless-stopped(服务器重启自恢复) - 自动暂停/恢复(对齐 amp 官方:15 分钟无活动自动暂停,活动到达立即恢复,归档立即暂停):
- 暂停 =
docker stop(保留容器与文件系统状态,快速唤醒,不占资源) - 恢复 =
docker start+ 跑工作区.agents/resumehook(最多等 10s,官方语义) - 空闲超时可用
AMP_LOCAL_ORB_IDLE_MINUTES调整(默认 15)
- 暂停 =
- 创建时
.agents/setuphook(对齐官方:从 repo root 跑一次准备依赖;共享工作区用标记文件只跑一次) - gh 认证注入(对齐官方 "Authenticated gh"):从宿主
GH_TOKEN环境变量或~/.config/gh/hosts.yml读 token →docker run -e GH_TOKEN+ 容器内gh auth setup-git(私有仓库可 clone) - 工具层对齐官方 orb 预装清单:git/ssh/tmux/vim/less/procps/iproute2/net-tools/busybox/python3 +
ffmpeg/ImageMagick/jq/fzf/unzip/zstd/lsof/ripgrep + gh(认证注入见上)+ node 24/npm + pnpm/yarn
- 暂未预装(此网络环境 GitHub 下载过慢):Bun、agent-browser(91MB)——需要时容器内
npm i -g bun/npm i -g agent-browser;--network注意:构建镜像时容器 IPv6 不通, 相关 curl 已加-4、npm 用NODE_OPTIONS=--dns-result-order=ipv4first
- 暂未预装(此网络环境 GitHub 下载过慢):Bun、agent-browser(91MB)——需要时容器内
- 服务器启动时自动清理孤儿容器(线程已归档的残留)
- 线程关联项目(
--orb-execute --project <namespace/name | id | repositoryURL>)→ 工作区 =sandboxes/<projectKey>/(同项目线程共享工作区,像多个 agent 在同一 git checkout 上工作) - 无项目线程 →
sandboxes/<threadId>/(按线程隔离,默认) - 项目创建:
amp projects create --repository <git-url>(或/api/internal?createProject)
- 容器内 sshd 固定 2222(bridge 网络),宿主端口动态分配(2200-2299,每容器独立)
- 使用:
ssh -p <动态端口> user@<服务器-IP>(端口见服务器日志或 listOrbSandboxes API;Tailscale 下为 tailnet IP) - user 免密 sudo;容器内
/workspace即项目工作区(bind mount) - 待办:websocat(WebSocket→SSH 隧道,供 web/TUI 终端,orb-repro 模式)
devPortalTunnelEnsure{threadID, childPort}→{"url":"http://<容器IP>:<childPort>"}(bridge 网络)- 宿主可直连容器 IP(单机场景);团队成员(远程)访问需后续 TCP 代理(服务器端口转发)
- 待办:服务器 TCP 代理(宿主端口 → 容器 IP:devPort)
- 真实语义(逆向 amp.js
hW()+ pi-openai-fast-mode 同源):fast = GPT 模型的service_tier: "priority"(OpenAI 优先级服务层);仅 openai provider +gpt-前缀模型支持 - 链路:CLI Alt+R →
client_update_thread_feature {feature:"fast", enabled}→ 线程 features 持久化 → agent 回合 LLM 调用注入service_tier: "priority"(模型名gpt-前缀才生效) - 线程 features 随 getSession 加载、thread_features 推送、重启保留
- 真实 amp:oracle 用独立的 second-opinion 前沿模型(manual:"the second opinion always comes from a different frontier model")
- 本地配置
llm.oracle/llm.librarian(provider/model,与 modes 同格式);未配置回退: oracle →highmode、librarian →mediummode - reasoningEffort 可配置(所有 mode + oracle/librarian):
"reasoningEffort": "low|medium|high"(OpenAI responses 透传reasoning.effort,chat 透传reasoning_effort) - 例:
"llm": { "oracle": { "provider": "xai", "model": "gpt-5.6-sol", "reasoningEffort": "high" }, "librarian": { "provider": "cliproxy", "model": "gpt-4o-mini" } }
- 工具:
set_schedule(RRULE + prompt)/get_schedule/update_schedule/clear_schedule(agent 侧) - 触发:服务器每分钟检查,到点把 prompt 作为用户消息 append 到线程 + 唤醒 orb(agent 接管执行)
- RRULE 解析用
rrulenpm 包;DTSTART;TZID=...时区正确换算(北京 9:00 → UTC 01:00)
配置 users 即开启鉴权(不配则保持不校验,单机模式):
{
"serverApiKey": "key-server-000",
"users": [
{ "userId": "alice", "username": "alice", "apiKey": "key-alice-xxx" },
{ "userId": "bob", "username": "bob", "apiKey": "key-bob-yyy" }
]
}serverApiKey:服务器自身身份(orb 沙箱 headless 执行器用;必须配,否则沙箱 401)- 团队成员:
AMP_URL=http://<服务器>:9876 AMP_API_KEY=<自己的key> amp -x "..." - 错误/缺失 key → 401(CLI 报 "Invalid or missing API key")
凭证通道(对齐真实 CLI):
| 通道 | 用途 |
|---|---|
Authorization: Bearer <key> |
用户 REST 请求(getUserInfo/thread-actors 等) |
x-rivet-token: <wsToken> |
actor 请求(CLI 用 thread-actors 发放的 wsToken) |
WS subprotocol rivet_conn_params |
WS 握手(wsToken;headless 执行器用 apiKey 字段) |
/actors/metadata |
豁免鉴权(actor 发现,CLI 无凭证调用) |
src/rivet/ = 复制自 npm rivetkit@2.3.10 的官方生成文件(勿手改):
client-protocol/v1-v4.ts+client-protocol-versioned.ts(vbare 版本化封装)- 帧 = u16 LE 版本(4) + bare body,与上游 engine 逐字节同源
- 升级协议版本:替换生成文件 + 补转换链即可
{ "port": 9876, // 监听端口(默认 9876;AMP_LOCAL_PORT 覆盖) "dataDir": "/home/you/.local/share/amp-local", // 数据目录(默认 ~/.local/share/amp-local;AMP_LOCAL_DATA_DIR 覆盖) "user": { "userId": "user_local", "username": "local" }, "llm": { // 命名 provider,可多个;可互相引用(provider: "cliproxy" 之类) "providers": { "cliproxy": { "baseUrl": "http://127.0.0.1:8317/v1", "apiKey": "amp-local-dev" // 明文密钥 }, "xai": { "baseUrl": "http://127.0.0.1:8000/v1", "apiKeyEnv": "XAI_API_KEY" // 从环境变量读(推荐,密钥不落盘) }, "opencode-go": { "baseUrl": "https://api.example-llm.com/v1", "apiKeyFile": "/home/you/.secrets/opencode.key", // 从文件读 "protocol": "completions" // 协议方言覆盖(见下) } }, // 全局默认 provider(model 为默认模型) "default": { "provider": "cliproxy", "model": "gpt-4o-mini" }, // mode → provider + model 绑定(low/medium/high/ultra,任意 key 均可) "modes": { "low": { "provider": "cliproxy", "model": "gpt-4o-mini" }, "medium": { "provider": "cliproxy", "model": "gpt-4o", "models": ["gpt-4o", "gpt-4o-mini"] // fallback 链(见下) }, "high": { "provider": "cliproxy", "model": "claude-3-5-sonnet" }, "ultra": { "provider": "cliproxy", "model": "claude-3-5-sonnet" } }, // 全局协议方言(可被 provider/mode 级覆盖) "protocol": "responses" } }