作者:团团 🫧
日期:2026-04-18
适用场景:自己使用的私有 AI 工作站,不想每次输网址,想在手机桌面点开像 APP 一样用 已关注关注重播 分享 赞
一、这篇文章解决了什么问题?
把三套 Web 管理界面——OpenClaw Control UI、Chat Gateway、Hermès——串联成一套可以装进手机桌面的 APK,像原生 APP 一样点击即用。
效果预览:
手机桌面一个图标 → 点开是三栏导航 → 底部快捷跳转 OpenClaw / Chat Gateway / Hermès
二、先认识三个主角
2.1 OpenClaw Control UI(控制台)
端口:10474
地址:http://你的服务器IP:10474/8snn7a#token=你的登录token
作用:OpenClaw 核心的 Web 控制面板,管理智能体、会话、配置、系统设置。
2.2 Chat Gateway(对话网关)
端口:3115
地址:http://你的服务器IP:3115
作用:多智能体对话管理界面,支持团队模式、会话历史、模型配置、文档预览(Word/Excel/PPT/PDF)。
2.3 Hermès(偏好设置)
端口:8787
地址:http://你的服务器IP:8787/
作用:OpenClaw 的偏好设置面板,管理 SOUL/USER 文件夹、导入导出、个人配置。
三、三者是怎么串联起来的?
1 2 3 4 5 6 7 8 9 10 11 ┌─────────────────────────────────────────┐
│ OpenClaw Control UI(端口 10474) │
│ 左侧栏底部有三个跳转链接 │
│ │
│ [OpenClaw] → http://IP:3115 │
│ [Hermès] → http://IP:8787 │
└────────┬────────────────────────────────┘
│
├─→ Chat Gateway(3115)← 多智能体对话
│
└─→ Hermès(8787)← 个人偏好配置 技术细节:
OpenClaw Control UI 的侧边栏底部(原sidebar-bottom区域)硬编码了三条跳转链接:
- • OpenClaw 链接指向 Chat Gateway 的 IP:3115
- • Hermès 链接指向 Hermès 的 IP:8787
- • Chat Gateway 底部导航里同样有指向 OpenClaw Control UI 的快捷回程
所以在三个页面之间来回跳转,完全不用手动输地址。
四、服务器端准备
4.1 确认三个服务都在运行
1 2 3 systemctl --user status openclaw-gateway.service
systemctl --user status clawui-3115.service
# Hermès 随 OpenClaw 一起启动,无需单独服务 4.2 确认端口可访问
1 2 3 curl -s -o /dev/null -w "%{http_code}" 自定义端口
curl -s -o /dev/null -w "%{http_code}" 自定义端口
curl -s -o /dev/null -w "%{http_code}" 自定义端口 三个都返回 200 即正常。
4.3 防火墙放行(如需要)
1 2 # 阿里云轻量服务器控制台 → 防火墙 → 添加规则
# 放行端口:10474、3115、8787(TCP) 五、用的是什么技术方案?(方案选择)
目前有三个主流方案,优缺点对比如下:
| 方案 | 实现难度 | APP 体验 | 离线可用 | 维护成本 |
|---|---|---|---|---|
| WebView APK(本文推荐) | ⭐ 低 | ⭐⭐⭐ 好 | ❌ 需联网 | 低 |
| Capacitor 打包 | ⭐⭐⭐ 中 | ⭐⭐⭐⭐ 佳 | 部分 | 中 |
| PWA(渐进式) | ⭐ 低 | ⭐⭐ 一般 | 部分 | 最低 |
本文选用方案一:WebView APK,原因:零门槛、当下可用、三个页面天然支持移动端响应式。
六、动手做 APK(详细步骤)
6.1 环境准备
电脑端(开发打包用):
- • 系统:Windows / macOS / Linux 均可
- • Android Studio 最新版(用于生成 APK)
- • 一台 Android 测试机(开启开发者模式 + USB 调试)
服务器端:
- • 已部署好的三个 Web 服务(上面第四步已确认)
6.2 创建 Android 项目
如果你不想装 Android Studio,可以用在线打包平台(见 6.6 节),连这一步都可以跳过。
打开 Android Studio → New Project →Empty Views Activity→ 项目名填MyAIStation
6.3 修改布局文件 activity_main.xml
路径:app/src/main/res/layout/activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 顶部栏(可选:显示当前在哪个服务) -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#1a1a2e"
android:title="AI 工作站"
android:titleTextColor="#ffffff" />
<!-- WebView 主区域 -->
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<!-- 底部快捷导航栏 -->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#16213e"
app:itemIconTint="#F5C542"
app:itemTextColor="#F5C542"
app:menu="@menu/bottom_nav_menu"
xmlns:app="http://schemas.android.com/apk/res-auto" />
</LinearLayout> 6.4 创建底部导航菜单
路径:app/src/main/res/menu/bottom_nav_menu.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/nav_control"
android:icon="@android:drawable/ic_menu_compass"
android:title="控制台" />
<item
android:id="@+id/nav_chat"
android:icon="@android:drawable/ic_menu-chat"
android:title="对话" />
<item
android:id="@+id/nav_settings"
android:icon="@android:drawable/ic_menu_preferences"
android:title="设置" />
</menu> 6.5 编写 MainActivity.java
路径:app/src/main/java/com/aiworksation/MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package com.aiworksation;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class MainActivity extends AppCompatActivity {
private WebView webView;
// ★ 改成你自己的服务器 IP
private static final String CONTROL_URL = "http://42.193.158.92:10474/8snn7a#token=26d300a2bfd2421a95118ef265a89b6b80b072e022885afb";
private static final String CHAT_URL = "http://42.193.158.92:3115/";
private static final String HERMES_URL = "http://42.193.158.92:8787/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webView);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// WebView 基础配置
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setSupportZoom(true);
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setUserAgentString("Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36");
// 页面在 WebView 内打开,不调用外部浏览器
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, android.webkit.WebResourceRequest request) {
view.loadUrl(request.getUrl().toString());
return true;
}
});
// 默认打开控制台
webView.loadUrl(CONTROL_URL);
// 底部导航点击切换
BottomNavigationView bottomNav = findViewById(R.id.bottomNav);
bottomNav.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_control) {
webView.loadUrl(CONTROL_URL);
toolbar.setTitle("控制台");
} else if (id == R.id.nav_chat) {
webView.loadUrl(CHAT_URL);
toolbar.setTitle("对话网关");
} else if (id == R.id.nav_settings) {
webView.loadUrl(HERMES_URL);
toolbar.setTitle("Hermès 设置");
}
return true;
});
}
// 返回键回到上一页,而不是直接退出 APP
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
} 6.6 添加网络权限
路径:app/src/main/AndroidManifest.xml
在<manifest>标签内加入:
1 2 <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 同时确保<application>标签有:
1 android:usesCleartextTraffic="true" 6.7 免 Android Studio 的替代方案:在线打包
如果不想装 Android Studio,用Capacitor + 极简 HTML的方式:
Step 1:创建项目(任意目录)
1 2 3 4 5 npm create vite@latest ai-station -- --template vanilla
cd ai-station
npm install @capacitor/core @capacitor/cli @capacitor/android
npx cap init "AI工作站" "com.aiworksation.app" --web-dir=dist
npx cap add android Step 2:修改index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>AI 工作站</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; background: #0f0f1a; color: #fff; }
iframe { width: 100%; height: calc(100vh - 60px); border: none; }
nav { display: flex; height: 60px; background: #16213e; }
nav button { flex: 1; background: none; border: none; color: #F5C542; font-size: 14px; cursor: pointer; }
nav button.active { background: #1a1a2e; }
</style>
</head>
<body>
<iframe id="frame" src="http://42.193.158.92:10474/8snn7a#token=26d300a2bfd2421a95118ef265a89b6b80b072e022885afb"></iframe>
<nav>
<button class="active" onclick="switchTo('control')">控制台</button>
<button onclick="switchTo('chat')">对话</button>
<button onclick="switchTo('hermes')">设置</button>
</nav>
<script>
const PAGES = {
control: 'http://42.193.158.92:10474/8snn7a#token=26d300a2bfd2421a95118ef265a89b6b80b072e022885afb',
chat: 'http://42.193.158.92:3115/',
hermes: 'http://42.193.158.92:8787/'
};
function switchTo(key) {
document.getElementById('frame').src = PAGES[key];
document.querySelectorAll('nav button').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
}
</script>
</body>
</html> Step 3:打包
1 2 npm run build
npx cap sync android Step 4:生成 APK
1 2 3 4 5 # 用 Android Studio 打开 ai-station/android/ 目录
# 或者用命令行:
cd ai-station/android
./gradlew assembleRelease
# APK 输出在:app/build/outputs/apk/release/app-release.apk 6.8 安装到手机
方法 A(USB 直装):
1 adb install -r app-release.apk 方法 B(扫码安装):
上传 APK 到手机可访问的 URL,或通过微信/QQ文件传输助手发送到手机安装。
首次安装需要开启「安装未知来源应用」权限。
七、怎么让 APK 更像原生 APP?
7.1 添加启动页(Splash Screen)
在res/drawable放一张 1080×1920 的启动图splash.png,然后创建res/layout/splash.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0f0f1a"
android:gravity="center"
android:orientation="vertical">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="🫧 AI 工作站"
android:textColor="#F5C542"
android:textSize="28sp"
android:textStyle="bold" />
</LinearLayout> 7.2 隐藏状态栏(沉浸式)
在MainActivity的onCreate()里加:
1 2 3 4 5 getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
); 7.3 添加 APP 图标
在res/mipmap-*目录放不同分辨率的图标 PNG文件(512×512 原图),Android Studio 会自动生成各尺寸。
八、安全注意事项
⚠️ 重要:这三个地址暴露公网意味着什么?
| 风险项 | 等级 | 应对 |
|---|---|---|
| Token 泄露 | 🔴 高 | 当前 token 有效期请确认,目前用的是永久 token,建议添加 IP 白名单 |
| 3115 端口无密码 | 🔴 高 | 至少加一层 Basic Auth 或 Gatekeeper 插件 |
| HTTP 明文传输 | 🟡 中 | 条件允许的话配上 HTTPS |
快速加固:给 Chat Gateway 加密码
1 2 # 在 Chat Gateway 的设置页开启 Basic Auth
# 或在 Nginx 层做反向代理认证 快速加固:Nginx HTTPS 反代配置参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:10474;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /chat {
proxy_pass http://127.0.0.1:3115;
}
location /hermes {
proxy_pass http://127.0.0.1:8787;
}
} 九、日常使用流程
打开 APP
手机桌面点「AI 工作站」图标 → 进入 OpenClaw 控制台
在三个服务间切换
点底部三个 Tab:控制台/对话/设置
切换智能体对话
控制台 → 左侧栏选智能体 → 自动切换到对应团队的 Chat Gateway 会话
发送文件预览
Chat Gateway 对话 → 直接拖入 Word/Excel/PPT/PDF → 自动预览(依赖 LibreOffice)
十、常见问题
Q:装了 APK 打不开页面,显示空白?
A:检查手机网络能否访问服务器 IP(公司网络可能限制了非标准端口)。建议配上域名 + 443 端口。
Q:底部导航点击没反应?
A:在AndroidManifest.xml确认<application android:usesCleartextTraffic="true">。
Q:页面加载很慢?
A:服务器带宽不够,建议开启 CDN 加速静态资源;也可以把三个服务都配上 Nginx 缓存。
Q:APP 图标能不能换成自己的?
A:可以。用 512×512 PNG 图标,通过 Android Studio 的 Image Asset 工具自动生成各尺寸,替换res/mipmap-*目录下的文件。
Q:APP 要更新怎么办?
A:重新打 APK,通过 URL 分发(如http://your-server.com/app.apk),在 APP 里写一个版本检测逻辑,跳转下载即可。
十一、今天的修改记录(技术细节存档)
如果你是在自己的服务器上复刻这套三联系统,以下是今天的改动点:
| 文件 | 改动 | 说明 |
|---|---|---|
openclaw/dist/control-ui/assets/index-M4TNVXB3.js | 第 4333 行 | 侧边栏加入 OpenClaw + Hermès 跳转链接 |
hermes-webui/static/index.html | 第 163 行 | 底部加入 OpenClaw Control UI 快捷入口 |
Chat-Gateway/frontend/src/components/Sidebar.tsx | 第 138-162 行 | 侧边栏底部加 OpenClaw 快捷按钮 |
有问题或需要进一步定制化开发,欢迎联系作者。
夜雨聆风