2026/10/11 16:55:03

手把手搭建24小时在线的私人AI助手:ClawdBot完整部署指南

手把手搭建24小时在线的私人AI助手:ClawdBot完整部署指南 先交代一下背景。我自己一直有个困扰平时有很多零散的问题、临时的想法、读到一半的文章摘要都希望能随手丢给一个“始终在线”的对话助手隔天还能接着聊。网页里的对话工具当然能用但会话一多就乱换个设备又是另一套时间久了很难形成真正的个人知识积累。后来我用一个周末的时间把一套叫 ClawdBot 的轻量级机器人从零搭了起来挂在旧笔记本上全天候运行一直用到现在。这篇文章就是把整个搭建过程完整拆开按照一个完全零基础的人能照做的标准来写。ClawdBot 本质上是一个基于 Python 的 Web 聊天服务前端是一个简单的网页对话框后端接一个大模型对话接口对话记录存在本地数据库里。你不需要懂前端也不需要懂服务端只要会执行命令就行。整个过程下来你会得到一个能 24 小时在线、拥有连续记忆、可以自定义人设的私人 AI 助手。1. 为什么非要自己搭一个 24/7 的私人 AI 助手1.1 网页对话工具的四个别扭之处先说说我为什么放着现成的网页对话不用非要自己搭。第一点是会话不连续。网页上的 AI 对话工具虽然也能保存历史但会话一多翻找起来非常费力。更麻烦的是每隔一段时间系统会自动清理旧会话你想回头找一个两周前的结论经常找不到。我自己攒了不少“当时觉得有用但没来得及整理”的问答结果全埋在对话列表里了。第二点是每次都要重新交代背景。网页对话默认没有长期人设你今天让它用“简洁专业”的口吻帮你写邮件明天再开一个新对话它又把你的偏好忘光了。你需要在每次对话里反复重复“我是做什么的、我希望你怎么回答、不要用语气词”这类信息浪费大量 token 和时间。第三点是隐私边界模糊。你输进去的问题往往带着工作内容、个人计划、生活细节这些信息放在第三方平台上我总觉得不踏实。自己搭的机器人对话记录存在自己的硬盘里心理边界清楚得多。第四点是入口不稳定。网页工具依赖浏览器浏览器依赖电脑电源和网络。你关了电脑它就不在。真正需要它的时候比如半夜突然冒出一个想法、周末在书房翻资料想顺手问一句那个“稳定入口”就找不到了。1.2 ClawdBot 做了什么取舍ClawdBot 没有做得很复杂它的核心能力只有三件事提供一个常驻的网页对话窗口随时打开浏览器就能聊把每一条对话记录持久化保存支持跨会话继续聊通过系统提示词设定一个固定的人设让机器人在所有对话里保持统一的语气和习惯。至于更高级的插件生态、联网搜索、知识库问答目前都不在 ClawdBot 的第一版范围内。这样设计是为了降低安装门槛——你只需要管理两个 Python 文件和一个配置文件出现问题也容易排查。我实际用下来的感受是这类“常驻助手”最大的价值不是功能多而是“永远在”。你在任何时间点打开它不用重新介绍自己不用等冷启动它会基于之前的全部对话继续回答这种感觉和网页聊天完全不一样。2. 安装前准备一台常开设备一个模型接口密钥2.1 硬件怎么选ClawdBot 对硬件要求很低。核心是要有一台能够长时间开机的设备而不是一台你每天关机带走的办公电脑。我列一个参考表方便你根据自己的情况选设备类型优点缺点适合人群旧笔记本自带屏幕、键盘调试方便功耗相对高风扇噪音可能偏大手边有闲置笔记本的人迷你主机功耗低安静体积小需要额外购买想认真长期运行的人云服务器不受本地断电影响随时随地从外部访问每月有费用公网安全需要自己把关经常不在家但需要远程用的人NAS 或软路由本身就是常开设备复用性强安装环境可能受限依赖已有设备能力已经有 NAS 的人我自己用的是旧笔记本装了 Linux 系统放在书房的角落里。ClawdBot 平时占用的内存大约 150MB 左右CPU 在对话之外基本是零负载对旧机器来说非常轻松。2.2 系统和 Python 环境本文以 Linux 环境为例Windows 和 macOS 也能运行只是后台服务管理方式略有不同后面第三章里我会分别说明。你先确认设备上有 Python 3.10 及以上版本。打开终端执行python3 --version如果输出类似Python 3.10.x就满足条件。如果没有 Python 或版本太老先通过系统的包管理工具安装这一步不同系统命令不同建议直接搜“你的系统版本 Python 安装”。接下来创建项目目录和虚拟环境mkdir -p ~/clawdbot cd ~/clawdbot python3 -m venv venv source venv/bin/activate虚拟环境的作用是把 ClawdBot 依赖的库和系统库隔离避免以后安装其他 Python 工具时互相干扰。这一步看着简单但很多新手省掉后后面会出现各种奇怪报错。然后安装两个核心依赖pip install flask requestsflask负责提供 Web 服务requests负责向大模型接口发送请求。一个负责“网页对话入口”一个负责“模型调用”这两个库就够用了。2.3 获取大模型接口的访问凭据ClawdBot 本身不包含模型它通过调用大模型服务商提供的对话接口来生成回复。你需要准备三样东西接口地址通常是一段 HTTP URLAPI 密钥一串用于身份验证的字符串模型名称服务商定义的模型标识。在服务商的开发者后台创建 API 密钥后建议复制到一个临时文件里保存。注意密钥相当于“账号密码”泄露意味着别人可以拿它调用服务并且由你付费。后面我们会把它写进配置文件但配置文件需要设好权限。如果你当前还没有任何大模型服务商的账号先注册一个。目前主流的大模型服务平台基本都提供了兼容的接口格式设计 ClawdBot 时我特意把接口地址、密钥、模型名称做成配置项换服务商只需要改config.json不用改代码。2.4 网络和端口的基本概念ClawdBot 启动后会在本机监听一个端口默认我用8180。浏览器通过“IP 地址加端口号”来访问服务。本地访问时就访问http://127.0.0.1:8180。如果想让同一局域网内的手机、平板也能访问ClawdBot 需要监听0.0.0.0然后用设备的局域网 IP 访问比如http://192.168.x.x:8180。这一步在配置里会体现后面详细说。提示如果你打算把 ClawdBot 部署到云服务器并对外提供服务请务必只在云平台的安全组里放行你信任的 IP 地址不要简单粗暴地把端口对全网开放。API 密钥一旦被拖走损失的就不只是流量费了。3. 搭建 ClawdBot 本体配置、代码与首次启动3.1 配置文件 config.json回到刚才创建的~/clawdbot目录第一步先建配置文件。新建一个config.json文件{ api_base_url: https://你的接口服务地址, api_key: 在这里填写你的密钥, model_name: clawd-chat, system_prompt: 你是一位可靠、耐心的私人助理。回答时尽量简洁直接不说客套话遇到不确定的信息要坦率说明。, temperature: 0.7, max_tokens: 1000, port: 8180, host: 127.0.0.1 }各字段的含义字段作用建议值api_base_url大模型服务商提供的接口根地址替换成你的真实地址api_key用于鉴权的密钥填真实值不要加引号model_name调用的具体模型标识按服务商文档填system_prompt系统提示词决定机器人人格和回复风格按你的需求调整temperature回复随机性数值越高越自由0.5~0.8 之间比较稳妥max_tokens单次回复的最大 token 数量日常问答 800~1000 足够portWeb 服务监听端口避免使用 80 等常被占用的端口host监听地址127.0.0.1 仅本机访问0.0.0.0 支持局域网访问先保持 127.0.0.1设置配置文件权限防止同机其他用户读取密钥chmod 600 config.json3.2 主程序 app.py在~/clawdbot下新建一个app.py内容如下。这段代码我尽量精简但保留了完整的核心逻辑import json import sqlite3 import requests from flask import Flask, request, jsonify, render_template app Flask(__name__) CONFIG json.load(open(config.json, encodingutf-8)) DB_PATH history.db def init_db(): conn sqlite3.connect(DB_PATH) conn.execute(CREATE TABLE IF NOT EXISTS messages(id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT, content TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP)) conn.commit() conn.close() def save_message(role, content): conn sqlite3.connect(DB_PATH) conn.execute(INSERT INTO messages(role, content) VALUES(?, ?), (role, content)) conn.commit() conn.close() def get_recent_messages(limit10): conn sqlite3.connect(DB_PATH) rows conn.execute( SELECT role, content FROM messages ORDER BY id DESC LIMIT ?, (limit,) ).fetchall() conn.close() rows.reverse() return rows def build_messages(): messages [{role: system, content: CONFIG[system_prompt]}] for role, content in get_recent_messages(20): messages.append({role: role, content: content}) return messages def call_model(messages): resp requests.post( CONFIG[api_base_url].rstrip(/) /chat/completions, headers{Authorization: fBearer {CONFIG[api_key]}}, json{ model: CONFIG[model_name], messages: messages, temperature: CONFIG[temperature], max_tokens: CONFIG[max_tokens], }, timeout60, ) resp.raise_for_status() return resp.json()[choices][0][message][content] app.route(/, methods[GET]) def index(): return render_template(index.html) app.route(/chat, methods[POST]) def chat(): user_input request.json.get(message, ).strip() if not user_input: return jsonify({error: 消息不能为空}), 400 save_message(user, user_input) messages build_messages() try: reply call_model(messages) save_message(assistant, reply) return jsonify({reply: reply}) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: init_db() app.run(hostCONFIG[host], portCONFIG[port], threadedTrue)这段代码做了几件事启动时初始化数据库history.db用来存历史消息每次用户发消息先保存到数据库再取出最近的历史记录拼进请求把系统提示词、历史记录、当前问题一并发给大模型接口收到回复后保存再返回给网页展示。注意接口路径我写的是/chat/completions这是目前大多数大模型服务通用的对话端点格式。如果你的服务商接口不同改为实际文档里的路径即可。3.3 前端页面 templates/index.html在~/clawdbot下新建templates目录创建templates/index.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleClawdBot/title style body { font-family: system-ui, sans-serif; max-width: 720px; margin: 40px auto; padding: 0 16px; } #history { border: 1px solid #ddd; border-radius: 8px; min-height: 400px; padding: 16px; margin-bottom: 12px; overflow-y: auto; } .msg { margin-bottom: 12px; } .user { color: #333; font-weight: 600; } .assistant { color: #555; white-space: pre-wrap; } #input { width: 100%; padding: 10px; border: 1px solid #aaa; border-radius: 6px; box-sizing: border-box; } #sendBtn { margin-top: 8px; padding: 8px 20px; border: none; background: #2678f0; color: #fff; border-radius: 6px; cursor: pointer; } /style /head body h1ClawdBot 私人助手/h1 div idhistory/div textarea idinput rows2 placeholder输入你的问题回车发送/textarea button idsendBtn发送/button script const historyEl document.getElementById(history); const inputEl document.getElementById(input); const sendBtn document.getElementById(sendBtn); function appendMessage(role, content) { const div document.createElement(div); div.className msg; div.innerHTML div class role (role user ? 我 : 助手) /div div class role content.replace(/\n/g, br) /div; historyEl.appendChild(div); historyEl.scrollTop historyEl.scrollHeight; } async function send() { const text inputEl.value.trim(); if (!text) return; inputEl.value ; appendMessage(user, text); const btn sendBtn; btn.disabled true; try { const resp await fetch(/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ message: text }) }); const data await resp.json(); if (data.reply) { appendMessage(assistant, data.reply); } else { appendMessage(assistant, 出错了 data.error); } } catch (err) { appendMessage(assistant, 请求失败 err); } finally { btn.disabled false; } } sendBtn.addEventListener(click, send); inputEl.addEventListener(keydown, function(e) { if (e.key Enter !e.shiftKey) { e.preventDefault(); send(); } }); /script /body /html这个页面没有任何引入外部框架打开就是一个朴素的聊天窗口。历史记录通过 JavaScript 动态添加到页面上。你可以根据自己的审美改样式核心逻辑不变。3.4 首次启动与验证在~/clawdbot目录下退出虚拟环境重进一次然后直接运行source venv/bin/activate python app.py看到类似Running on http://127.0.0.1:8180的输出就说明启动成功了。然后打开本机浏览器访问http://127.0.0.1:8180输入“你好介绍一下你自己”如果机器人正常回复说明整个链路已经打通。第一次跑通你会遇到几种情况我提前说一下最常见的一个请求报 401 或者 403。大部分原因都是 API 密钥填错、接口地址写错、或者模型名称不对。先检查 config 三个字段再检查网络请求是否真的发出去了。4. 让对话有记忆上下文管理的原理与实现4.1 为什么不能每次只问一句大模型接口本身是无状态的。你发一条“帮我写一封请假邮件”它只会根据这条信息回答。如果你下一条说“把理由改得再紧急一些”它不知道“再紧急一些”指的是哪封邮件。ClawdBot 的解决办法是每次请求时把最近的对话历史一起发给接口。这就好比你每次找同一个助理帮忙都会先把之前的交流记录摆在桌上对方才能接着干活。在代码里消息列表长这样[ {role: system, content: 你是一位可靠、耐心的私人助理。}, {role: user, content: 帮我写一封请假邮件}, {role: assistant, content: 好的以下是草稿……}, {role: user, content: 把理由改得再紧急一些} ]接口看到完整的历史就能知道最后一条指令的真实意图。ClawdBot 的build_messages()函数就是做这件事先在消息列表头部插入系统提示词再按时间顺序追加最近的对话记录最后把当前问题放到末尾。4.2 为什么不能无限传下去很多人会想那我干脆把所有历史都传给接口记忆不就最完整了不行。原因有两个一个是每次请求都有 token 数量上限另一个是历史越长响应越慢、成本越高。假设平均每条消息 300 个汉字20 条对话记录大约是 6000 个汉字按中文一两个汉字算一个 token大概是 3000 到 6000 token已经接近很多模型单次请求的一半额度了。你聊得越久历史膨胀得越快。我的策略是只取最近 20 轮user 和 assistant 各算一条作为上下文。设置一个合理的窗口既能保持连贯性又能控制请求体大小。4.3 滑动窗口与关键信息固定如果你聊的内容比较重要希望机器人一直记得某件事——比如“我的工作领域是教育信息化”你就把它写进系统提示词里也就是 config.json 的system_prompt字段。这样一来无论历史怎么滑动这段信息始终在每次请求的最前面。如果你想更精细地控制可以在build_messages()里区分“永久记忆”和“滑动历史”def build_messages(): permanent [ {role: system, content: CONFIG[system_prompt]}, {role: user, content: 请记住我一般工作日上午处理邮件下午做项目。以后安排任务时考虑这个时间偏好。}, {role: assistant, content: 好的我已记住。}, ] recent [ {role: role, content: content} for role, content in get_recent_messages(20) ] return permanent recent这样永久记忆不会被滑动窗口挤出请求而近期的对话又能正常引用。这个方法是我实际用了一个月后总结出来的比单纯把历史长度调大要省 token 得多。提示改变上下文策略后建议先重启服务再验证效果。因为历史是存在数据库里的调整代码后旧消息会以新的组织方式参与上下文有时会出现“突然忘了之前说的话”的错觉其实切换逻辑后相当于换了一次系统提示词。5. 让它 24/7 在线systemd 守护进程与开机自启5.1 为什么不能开着终端挂着很多人跑到这一步就以为大功告成了直接在终端里python app.py窗口挂着就关掉显示器。但这样有两个隐患一是终端窗口一旦关闭Python 进程就可能被终止助手就掉线了。二是重启电脑后你忘了重新运行它就一直不在线。在 Linux 系统下正规做法是使用 systemd 把 ClawdBot 注册成一个后台服务。systemd 负责在开机时自动启动它在进程崩溃时自动重启它还能统一收集日志。这才是“24/7 在线”的正解。5.2 编写 ClawdBot 服务文件在终端执行sudo nano /etc/systemd/system/clawdbot.service写入以下内容[Unit] DescriptionClawdBot AI Assistant Afternetwork-online.target Wantsnetwork-online.target [Service] User你的用户名 WorkingDirectory/home/你的用户名/clawdbot ExecStart/home/你的用户名/clawdbot/venv/bin/python /home/你的用户名/clawdbot/app.py Restartalways RestartSec5 EnvironmentPYTHONUNBUFFERED1 [Install] WantedBymulti-user.target逐项说明User以哪个系统用户运行服务。不要用 root万一程序出问题影响范围会小很多WorkingDirectory工作目录必须是项目目录程序里的相对路径才能生效ExecStart服务启动命令要指向虚拟环境里的 Python而不是系统 PythonRestartalways无论什么原因退出都尝试自动重启RestartSec5重启前等待 5 秒避免崩溃后陷入快速重启的循环。保存文件后重新加载 systemd 配置并启动服务sudo systemctl daemon-reload sudo systemctl enable clawdbot sudo systemctl start clawdbotenable是设置开机自启start是立即启动。这一步之后你重启电脑也不用管它了ClawdBot 会自动爬起来。检查运行状态sudo systemctl status clawdbot输出里看到active (running)就说明服务在跑。浏览器再访问一次功能正常后台服务也就正式上线了。5.3 查看日志和手动重启服务有问题时第一件事就是看日志sudo journalctl -u clawdbot -n 50-n 50表示查看最近 50 行。日志里会显示 Python 的完整错误堆栈排查问题比终端里看还方便。修改完代码后重启服务的命令统一为sudo systemctl restart clawdbot记住这套命令后面日常维护只需要这一个动作。5.4 Windows 和 macOS 的替代方案Windows 上最简单的做法是写一个.bat启动脚本然后把脚本快捷方式放进“启动”文件夹实现开机自启。但如果是长期挂机我建议直接使用 Windows 任务计划程序设置“用户登录时”或“系统启动时”运行脚本比塞启动文件夹更稳定。macOS 用 launchd写一个.plist文件放到~/Library/LaunchAgents?xml version1.0 encodingUTF-8? plist version1.0 dict keyLabel/key stringcom.local.clawdbot/string keyProgramArguments/key array string/Users/你的用户名/clawdbot/venv/bin/python/string string/Users/你的用户名/clawdbot/app.py/string /array keyWorkingDirectory/key string/Users/你的用户名/clawdbot/string keyKeepAlive/key true/ /dict /plist然后用launchctl load加载。这块不同系统的命令差异较大建议在虚拟机或真实设备上做一次实验确认重启后能自动拉起即可。6. 运行一段时间后必然会踩的坑6.1 请求超时与网络波动大模型接口的响应时间并不稳定遇到高峰时段一次请求可能 20 秒甚至更久。我在代码里设置的timeout60基本够用。如果你用的接口偶尔出现超时可以在调用层加一个简单的重试for attempt in range(3): try: resp requests.post(..., timeout60) resp.raise_for_status() return resp.json()[choices][0][message][content] except requests.exceptions.Timeout: if attempt 2: raise time.sleep(2)但要注意重试只适合幂等性请求。对话生成这类请求重试可能导致机器人重复回复同一段内容。所以我没把重试做进第一版宁可让前端直接报错也不要产生混乱的重复回复。6.2 上下文爆炸导致成本失控我前面提到只取最近 20 轮这是保险做法。如果你把窗口调大到 50 轮再加上回复本身很长单次请求消耗的 token 会迅速上升。连续对话一整天费用可能超出预期。一个简单的控制办法在配置里增加一个最大请求轮次限制MAX_ROUNDS 10然后在build_messages()里用history[-MAX_ROUNDS:]做截取。当你的对话特别长时它会自动忽略最远的历史。如果你发现 ClawdBot 突然“失忆”先看看是不是上下文窗口不够而不是模型本身出问题。6.3 API 密钥安全密钥泄暴露是最大的风险。以下几个习惯我强烈建议你有config.json不要提交到任何代码托管平台如果要备份先做加密密钥权限设置成只有自己可读前面说过的chmod 600不要省定期在服务商后台更换密钥一个月或一个季度换一次成本不高心理踏实把 ClawdBot 所在设备本身设置好登录密码不要裸奔在局域网里。6.4 日志和磁盘慢慢变大history.db会随着对话增加持续变大但就算每天聊几百条一年下来也就几十 MB对现代硬盘来说可以忽略。倒是 systemd 日志更容易增长特别是你频繁调试的时候。可以使用sudo journalctl --vacuum-size200M把日志压缩到 200MB 以内。如果你希望长期自动清理去查看系统对应的 journald 配置修改SystemMaxUse那个字段就行。6.5 局域网访问时不生效如果你想在手机上用把 config 里的host改成0.0.0.0然后重启服务。手机浏览器访问时用的地址是“电脑的局域网 IP 加端口号”不是127.0.0.1。查局域网 IP 的命令ip addr | grep inet或者ifconfig看到形如192.168.1.x的地址就是。把手机和电脑连到同一个 WiFi 下浏览器访问http://192.168.1.x:8180就能在手机上用同一个 ClawdBot 了。提示如果手机访问不了先检查电脑自带防火墙是否放行了 8180 端口。不同系统防火墙命令不同但排查思路都一样先确认服务真的在监听再确认防火墙允许了该端口最后确认手机和电脑在同一网段。运行这段时间我最大的体会是ClawdBot 这类自托管助手的真正门槛不在安装而在“怎么持续引导它成为你的助手”。我的建议是花一点时间把system_prompt写成一段真正贴合自己需求的说明而不是随手复制一句模板。它语气更像真人、回复更符合我的习惯用得越久价值越高。另外记得每个月把history.db备份一次哪怕只是复制到另一块硬盘——你积累的对话记录才是这个私人助手真正的不可替代资产。