Multica 自托管实战:面向 AI Agent 的两步本地部署、CLI 接入与端口排障全解
本篇基于仓库中的 SELF_HOSTING_AI.md 展开,完整讲解如何用两条命令在一台装有 Docker 的机器上拉起 Multica 自托管服务(前端 3000 端口 + 后端 8080 端口 + PostgreSQL),把 multica CLI 接入本地服务器并启动 agent daemon,以及端口冲突时如何正确改端口、如何用健康检查接口定位故障。读完后你既能手动复现整个部署链路,也能理解安装脚本、Makefile 与 CLI 在端口解析、凭证生成、登录探针等细节上的真实实现。
一、适用场景与前置条件
SELF_HOSTING_AI.md 的定位很明确:这份文档是为 AI agent 设计的执行手册——"Follow these steps exactly to deploy a local Multica instance and connect to it"。步骤高度确定、可机器执行,因此对人类用户同样是一份可靠的本地部署清单。它解决的是"在一台开发机上跑起一套完整可用的 Multica"这件事,而不是生产集群部署(Kubernetes/Helm 方案见 SELF_HOSTING.md)。
前置条件共三项:
- 已安装 Docker 与 Docker Compose(服务全部以容器方式运行);
- 已安装 Homebrew(用于安装 CLI,macOS 上最常用);
- PATH 上至少有 一个 AI agent CLI,文档点名
claude或codex——daemon 启动后会探测本机安装的 agent CLI 并向服务器注册,没有可探测到的 agent,daemon 虽能运行但没有可执行任务的运行时。
二、推荐路径:安装脚本两步部署
2.1 部署命令
# 安装 CLI + 拉起自托管服务器
curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.sh | bash -s -- --with-server
# 为 CLI 配置 localhost、完成认证并启动 daemon
multica setup self-host
关键纪律是时序:必须等待安装脚本输出 ✓ Multica server is running and CLI is ready! 之后再运行 multica setup self-host。预期结果是三个可验证的状态:
- 前端位于 http://localhost:3000;
- 后端 API 位于 http://localhost:8080;
multicaCLI 已安装且配置指向 localhost。
2.2 安装脚本内部到底做了什么
阅读 scripts/install.sh 可以确认 --with-server 模式(run_with_server)的完整执行链:
- 环境检测:
detect_os只支持 Darwin/Linux(Windows 会直接提示改用 PowerShell 版安装器);check_docker既检查docker命令存在,也用docker info确认守护进程正在运行。 - 检出服务器资产:
setup_server把仓库浅克隆到~/.multica/server(可用环境变量MULTICA_INSTALL_DIR覆盖),默认检出的 ref 由get_selfhost_ref决定——优先MULTICA_SELFHOST_REF,否则取最新 release tag,再退到main。已有安装目录则复用并更新,而非重新克隆。 - 生成
.env:若不存在,则从.env.example复制并用openssl生成随机JWT_SECRET(hex 32)与POSTGRES_PASSWORD(hex 24),同时用正则同步DATABASE_URL中的密码。JWT_SECRET是硬性要求——docker-compose.selfhost.yml 中写作${JWT_SECRET:?JWT_SECRET must be set...},缺失时 Compose 直接拒绝启动。 - 拉取官方镜像并启动:
docker compose -f docker-compose.selfhost.yml pull拉取 GHCR 上的官方镜像;若所选 tag 尚未发布,脚本会明确提示改用docker compose ... -f docker-compose.selfhost.build.yml up -d --build从源码构建。随后up -d启动 postgres、backend、frontend 三个服务。 - 端口回读(关键设计):
compose_published_port通过docker compose -f docker-compose.selfhost.yml port backend 8080读取 Compose 实际发布的宿主端口,而不是从.env重新推导。脚本注释给出了原因:Compose 的插值让调用进程环境变量优先于.env,任何只从文件推导端口的做法都可能探测到错误端口。健康检查与最终打印的 URL 共用这一次回读结果,二者永不失配。 - 健康等待:循环最多 45 次、每次 2 秒探测
http://localhost:<backend_port>/health,成功才打印✓ Multica server is running。 - 安装 CLI:优先走 Homebrew(
brew tap multica-ai/tap+brew install multica-ai/tap/multica),失败则回退到 GitHub Releases 二进制下载,安装位置按/usr/local/bin→sudo→~/.local/bin依次回退。已安装时还会比较multica version与最新 release tag,自动升级。
--stop 模式则是 docker compose down 安装目录下的服务并执行 multica daemon stop,与第六节的停止操作对应。
三、替代路径:手动分步部署
git clone https://github.com/multica-ai/multica.git
cd multica
make selfhost
brew install multica-ai/tap/multica
multica setup self-host
Makefile 中 selfhost 目标的行为(第 82–113 行):
- 若
.env不存在,从.env.example复制并生成三个随机密钥:JWT_SECRET、POSTGRES_PASSWORD,以及 SELF_HOSTING_AI.md 未提及但 Compose 文件实际需要的MULTICA_VCS_SECRET_KEY(自托管 Git 集成 docker-compose.selfhost.yml 中MULTICA_VCS_INTEGRATION_ENABLED默认为 true); - 拉取官方镜像;若
pull失败(例如所选MULTICA_IMAGE_TAG尚未在 GHCR 发布),会提示回退到make selfhost-build,后者用本地multica-backend:dev/multica-web:dev标签从当前检出构建,不会覆盖已拉取的:latest镜像; - 启动后调用 scripts/selfhost-wait.sh 等待并打印连接信息。
scripts/selfhost-wait.sh 的注释值得细读,它解释了端口链路的完整优先级:容器内部恒定监听 8080/3000,宿主发布端口按 BACKEND_PORT → API_PORT → SERVER_PORT → PORT 的别名顺序取值(docker-compose.selfhost.yml 第 61 行的 ${BACKEND_PORT:-${API_PORT:-${SERVER_PORT:-${PORT:-8080}}}} 即为实现)。脚本坚持用 docker compose port 回读实际发布端口,因为历史上"探测用的变量"与"Compose 发布的变量"不一致,导致健康堆栈被报告为仍在启动。
四、multica setup self-host 的四步语义与源码实现
文档明确该命令完成四件事:
- 把 CLI 配置为连接
localhost:8080/localhost:3000; - 打开浏览器登录——使用邮件验证码,或在未配置 Resend 时使用后端日志中打印的生成码;
- 自动发现 workspace;
- 后台启动 daemon。
对照 server/cmd/multica/cmd_setup.go,有几个实现细节决定了它的可预期性:
- URL 解析顺序:
resolveSelfHostServerURL依次取--server-url标志 →MULTICA_SERVER_URL环境变量 → 已有配置中的server_url→ 由--port(默认 8080)拼出的 localhost 兜底。app_url同理取--app-url→MULTICA_APP_URL→ 已有配置(除非传了--frontend-port)。这意味着重复运行setup self-host(例如重新登录)会保留已配置的远端部署,而不是悄悄重置回 localhost。 - 远端部署必须显式给前端地址:当 server URL 指向非本机时,CLI 不会去猜
api.x.co对应的app.x.co,而是交互式提示输入(或要求--app-url),避免把错误推迟到浏览器登录环节才暴露。 - 先探活、后落盘:
persistSelfHostConfigIfReachable用 2 秒超时的GET /health探活,只有服务器应答才覆写配置。一次失败的setup self-host绝不会清掉可用的配置和已保存的 token——这是针对旧版"先写配置再探测、失败即清空 token"行为的修正。 - daemon 的启动/重启决策:setup 完成后
runDaemonAfterSetup先探测该 profile 的 daemon 健康端口。若 daemon 正在运行且有活动任务,它不会重启 daemon(以免取消进行中的工作),而是提示"等待任务结束后手动执行multica daemon restart以应用新配置";无活动任务则自动重启,未运行则首次启动。这解释了为什么有时 setup 后需要手动multica daemon restart。
验证命令(与文档一致):
multica daemon status
正常输出为 running 并列出检测到的 agent。
五、停止服务
# 停止 daemon(本地进程,不在 Docker 内)
multica daemon stop
# 停止所有 Docker 服务
cd multica
make selfhost-stop
Makefile 中 selfhost-stop 就是 docker compose -f docker-compose.selfhost.yml down;若当初用的是安装脚本,等价的官方入口是 install.sh | bash -s -- --stop(它会 down ~/.multica/server 下的 Compose 栈并尝试停 daemon)。注意数据卷 pgdata 与 backend_uploads 由 docker-compose.selfhost.yml 声明,down 不会删除它们——重新 up 后数据仍在。
六、自定义端口:变量优先级是唯一的坑
当默认端口(8080/3000)被占用时,文档给出的三步是:
- 编辑
.env,修改PORT与FRONTEND_PORT。这些是宿主端口;容器内部恒定监听 8080/3000,因此改端口无需重新构建镜像; - 运行
make selfhost; - 运行
multica setup self-host --port <PORT> --frontend-port <FRONTEND_PORT>。
文档专门警告了一个极易踩中的优先级陷阱,Makefile、docker-compose.selfhost.yml 与 .env.example 三方注释可交叉印证:
make selfhost时:Makefile 是include.env文件的,所以文件里的值压过 shell 环境变量——PORT=9100 make selfhost在.env已设PORT时会被忽略;而命令行赋值make selfhost PORT=9100则生效(Make 变量覆盖机制)。- 直接
docker compose时优先级反过来:调用进程的环境变量压过.env文件。 - 所以文档的建议是"改文件,而不是依赖环境变量"。
别名规则同样来自 Compose 文件的插值链:BACKEND_PORT 是覆盖后端端口的可选别名,其后依次是 API_PORT、SERVER_PORT,最后才是 PORT——前者优先级更高。无论走哪条路径,make selfhost(经 scripts/selfhost-wait.sh)与安装脚本都会从 Docker Compose 回读实际发布的端口,因此启动输出里打印的地址就是栈真正对外暴露的地址,不存在"打印 8080、实际监听 9100"的错位。
七、故障排查
文档给出四条对症命令:
| 症状 | 排查命令 |
|---|---|
| 后端未就绪 | docker compose -f docker-compose.selfhost.yml logs backend |
| 前端未就绪 | docker compose -f docker-compose.selfhost.yml logs frontend |
| daemon 异常 | multica daemon logs |
| 存活/就绪检查 | curl http://localhost:8080/health(liveness)、curl http://localhost:8080/readyz(依赖感知 readiness) |
结合 Compose 文件可以补充两个判断依据:postgres 带 pg_isready 健康检查且 backend 声明 depends_on: postgres: condition: service_healthy,所以"backend 反复重启/起不来"优先查数据库容器是否 healthy;backend 启动时会自动执行迁移(SELF_HOSTING.md 说明迁移在后端启动时自动运行),冷启动慢时看 backend 日志即可。/readyz 与 /health 的区别在于前者会检查依赖(如数据库连通性)是否就绪,安装脚本的探活循环用的就是 /health。
八、延伸阅读
- 完整自托管指南(含 Kubernetes/Helm、手动 Compose 配置、升级与回滚):SELF_HOSTING.md
- 高级配置(环境变量全集、邮件/注册控制、Usage Dashboard Rollup):SELF_HOSTING_ADVANCED.md
- 端口变量示例与注释:.env.example
- Compose 编排(127.0.0.1 绑定、端口别名链、各集成开关):docker-compose.selfhost.yml
- CLI setup 命令实现与测试:server/cmd/multica/cmd_setup.go、server/cmd/multica/cmd_setup_test.go
一句话总结部署心智模型:Docker 栈只负责服务器三件套(postgres/backend/frontend,宿主端口可配、容器端口恒定),multica CLI 与 daemon 永远跑在宿主机上——前者通过 setup self-host 指向后端 URL 完成认证与注册,后者探测本机 agent CLI 并等待任务。端口、凭证、探活这三个环节的任何异常,都能用第二节的回读机制与第七节的检查命令快速定位。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00