从零到上线:和 AI 一起搭建 x-li.org 的决策记录
起点:我想要什么
我有一台空的 VPS 和一个闲置的域名。想做的事情很简单:一个个人技术博客,放一些科研和日常用得上的小工具;同时把这台服务器当成 AI agent 的试验场。
动手之前,我先给自己定了三条硬性要求:
- 只用 Max 订阅每月附带的 API 额度:不额外花钱,也不占用日常对话的额度。
- 我的交互要尽量简单:日常只在浏览器里和 agent 对话、点批准,不想天天 SSH 敲命令。
- 结构必须清晰:哪天不用 agent 了,我自己也能看懂、能维护,网站照样运行。agent 是维护者,不是运行的前提。
还有一条安全红线:这台 VPS 被攻破可以接受,但它绝不能成为攻击我自己电脑的跳板。 服务器上不放重要数据,坏了大不了重装;电脑不行。
后来很多选择,都是由这条红线决定的。
第一个大转折:让谁来干活
最初的设想是在 VPS 上常驻一个 Claude Code,用 Remote Control 从网页或手机上操作。讨论下来发现两个问题:
- 额度对不上:订阅附带的 API 额度覆盖 Claude API、Agent SDK 和 Managed Agents,但不覆盖交互式的 Claude Code。走 Remote Control,花的是订阅额度,而不是那份 API 额度。
- 凭据风险:Remote Control 要求在 VPS 上存一份完整的账号登录凭据。VPS 一旦被黑,这份凭据就可能被拿去操作同一账号下的其他东西,正好撞上红线。
我们比较了几条路:Claude Code 的 -p 模式加 API key(能用这份额度,但只能在终端里一问一答);开源的 Hermes Agent(有网页界面,但当时刚披露过与命令审批相关的漏洞)。最后选了 Claude Managed Agents 的自托管沙箱模式:
- 模型运行在 Anthropic 一侧,VPS 上只跑一个小 worker。它只发起出站连接,领取工具调用,在服务器上执行后把结果传回去;
- VPS 上只放一把 environment key,它只能领取这个环境的任务,碰不到我的账号;
- 费用走 API 额度;
- 我在浏览器里就能对话、看过程、批准操作。
中间还有一段插曲:我一度觉得"让本机的 Claude 远程操控 VPS"最方便。但这样一来,本机的 agent 会读取 VPS 返回的内容,而 VPS 一旦被黑,这些内容里就可能夹带指令。最后定下的原则是:本机不运行任何会读取 VPS 输出、又能在本机执行命令的 agent。
网络:先上锁,再开门
所有可能把自己锁在外面的操作(防火墙、SSH、Tailscale、网络配置),都按同一套"防锁死规程"来:先确认有两条独立的管理通道;改动前设一个定时自动回滚;我在本机实测仍能登录之后,才取消回滚。
几个关键决定:
- Tailscale 的访问规则默认拒绝一切,只允许我的设备访问 VPS 的 SSH 和文件共享端口;VPS 不能主动连接任何其他设备。我的电脑还额外关闭了 Tailscale 的入站连接,这道锁不依赖任何后台规则。
- 中间出了个意外:我的 tailnet 不是新建的,里面还躺着好几台旧服务器,访问规则也还是默认的"全部互通"。先清理设备、重写规则,然后才让 VPS 加入。
- 公网只开放 80 和 443 端口;SSH 和文件共享只走私有网络。
- 实测时发现,Tailscale 自带的防火墙链排在 ufw 前面,所以 ufw 实际管不到私有网络那一侧。那一侧的端口限制就交给 Tailscale 的访问规则:规则在后台,VPS 改不了,正好符合红线的要求。
网站:标准、可手动维护,优先于"最优"
需求是博客加小工具;只有我自己需要登录,访客账号由我手动创建;内容由 agent 生成并直接发布。
- 框架选了 Django:一套框架就能覆盖博客、工具、登录和数据库,还自带管理后台。不用 agent 时,我在浏览器里就能改内容。所有软件都用 Ubuntu 官方包安装,安全更新随系统自动进行。
- WSGI 服务器的小争论:Ubuntu main 仓库里的 cheroot 有官方的安全更新承诺;最标准的 gunicorn 在 universe 仓库里,没有这个承诺。起初的推荐是 cheroot,但我更看重"标准、好维护、以后不靠 agent,我也能自己查资料维护",最后选了 gunicorn。这类取舍没有标准答案,关键是把理由写清楚。
- 内容由 AI 写,安全上就要多一层:文章可能根据网页素材生成,而网页里可能藏着恶意指令。所以 Markdown 中的原始 HTML 一律转义,并加上严格的内容安全策略(CSP),禁止内联脚本。页脚注明"部分内容由 AI 生成"。
科研工具:先审规格,再写代码
除了普通的单位换算,我还想要真正用得上的科研工具:浓度换算与配液(mol、ppm、ppb、µg/mL)、光谱单位换算、频率与时间、AFM 成像参数、单分子体积与质量、Beer–Lambert 与蛋白 A280、气体 ppm 与 mg/m³。
这类工具的价值全在"算得对",所以流程是:agent 先交规格,写明公式、假设、适用条件、常数来源和测试用例,审过之后再写代码。审规格时确实抓到了问题:
- AFM 针尖去卷积中,"基底宽度"模式的适用范围判据用错了,套用了"半高宽"模式的公式。改正后的判据严格得多。
- 规格把 mmHg 当成了精确定义的单位,实际上只有 torr 是按定义精确的。
- 蛋白预设值不凭记忆写:直接从 UniProt 取序列,按 Pace 等人的系数重算 ε280 和分子量,结果与 ExPASy 逐项一致。常被引用的 BSA 值 43 824 其实是由 E1% 换算出来的厂商数据,没有采用。
- "蛋白密度 1.35 g/cm³"是常用的约定值,但文献中的取值从约 1.22 到 1.41 都有,页面上如实注明。"DNA 密度 1.7"其实是 DNA 在 CsCl 中的浮力密度,不等于分子本身的密度,干脆删掉了这个预设。
- 欧洲习惯用逗号做小数点。工具接受
1,5,但像1,000这种可能表示"一千"的写法会被拒绝,而且输入框旁边始终显示实际使用的数值。
不靠 agent 也能发文章
为了满足第三条要求,最后加了一个"投稿目录":把一个带元信息的 Markdown 文件拖进共享文件夹,服务器每 5 分钟扫描一次,自动生成或更新文章;成功还是失败、失败的原因,都写回同一个文件夹。投稿只能新增和修改文章,不能删除,避免误操作。这篇文章本身就是这样发出来的。
踩过的坑
这一部分可能是最有用的:
- 看不见的 BOM:Windows PowerShell 用管道把文本传给 ssh 时,会在开头加一个看不见的字符。密钥前面多了这个字符,服务就一直报 401。
- 被吃掉的引号:PowerShell 5.1 把参数传给外部程序时,会吞掉其中的双引号。此后传给 ssh 的远端命令一律不用双引号。
- pkill 杀了自己:
pkill -f 某个名字会把执行这条命令的 shell 也一起结束,因为 shell 的命令行里也包含这个名字。要用[x]的写法避开。 - 等着按回车的分页器:agent 执行
git diff时打开了分页器,停在"Press RETURN",一直等到超时。 - 9.3 GB 的内存峰值:做"反向验证"时,一个突变把文件大小上限改成了 10¹⁰ 字节,测试就真的造了 10 GB 的数据。内存耗尽后,systemd 按默认策略停掉了整个 worker 服务,正在执行的命令全部丢失,会话一直卡住。修复办法是给 worker 加内存上限,并让 OOM 只结束出问题的那个进程;长任务一律放进有内存上限的独立临时服务里运行。
- 开机多等 2 分钟:网络由 ifupdown 管理,但系统里同时启用了一个不管理任何网卡的 systemd-networkd,它的"等待联网"每次都要等满 2 分钟才超时。屏蔽它之后,联网就绪时间从 2 分钟缩短到 3 秒。
关于成本
搭建这一天,agent 大约用掉了月度额度的三分之一(约 33 美元)。钱主要不是花在"干活"上,而是花在长会话上:上下文涨到 30 多万 token 以后,每次调用都要把它读一遍;中间停顿超过几分钟,缓存失效,还得整段重新写入。经验是:一个任务开一个新会话,一次把要求说清楚。
分工
回头看,最后形成的分工是:
- 我:定目标和红线,做所有决定,在本机验证结果;
- Claude(规划与审查):讨论方案、审核 agent 的计划、查文献和核实数值、写交给 agent 的任务;
- VPS 上的 agent:在服务器上执行、测试、写运维手册。
几条让协作顺畅的规则:需要我决定的事,一律做成带推荐项的选择题;任何密码和密钥都不在对话中出现;查资料的活不交给 agent,省下它的额度。
下一步
网站和工具先用起来。agent 试验场会放在一个完全隔离的环境里,等用过一段时间、看清实际开销之后再规划。
本文由 AI(Claude)根据搭建过程的对话记录整理成文,经作者审阅后发布。