书生·端砚的云端开发机,高性能显卡A100(80G显存)直接对接本地电脑,相关命令整理

> **适用场景**:本地电脑(无独立显卡/轻薄本/办公本)运行 `AI-LiveStream-Agent` 直播中控,将高负载的数字人实时音视频画面渲染外包给云端租用的 **Intern InkStone(书生·浦语)A100 80GB** 开发机,实现零本地硬件负担、低成本的高画质真人/数字人直播。

 

网址和登录

云机网址:https://discovery.intern-ai.org.cn/compute/dev-machine

登录方式:Github 账号一键登录

 

注意:

  1. 一个新的账号,首次大概赠送 5000 积分,如果用 A100(显存 80G ),每小时大概消费预计消耗 60点(积分),相当于 可以用 82 个小时,使用中途你可以停止机器,积分也同步停止计费,自带 30G 云盘,可以将自己的代码项目保存到云盘,然后在开发机当中直接运行。
  2. 有时候遇到高峰期,可能需要排队,然后才能进入 开发机 进行相关业务操作
  3. 其实,可以有N个 Github账号,不需要实名和手机号,更不需要绑卡

 

 

图片[1]-书生·端砚的云端开发机,高性能显卡A100(80G显存)直接对接本地电脑,相关命令整理-共赢源码

 

 

步骤 1:

打开现有 Notebook在界面左侧文件列表,双击打开自带的 Untitled.ipynb

 

步骤 2:

在单元格(Cell)中粘贴下方单行执行代码在 Notebook 的代码单元格里粘贴以下代码:

 

import os, re, sys, time, json, subprocess

print("📦 1. 正在清理旧服务与安装依赖...")
os.system("pkill -f sidecar_server.py || true")
os.system("pkill -f server.py || true")
os.system("pkill -f cloudflared || true")
os.system("pip install -q fastapi uvicorn websockets opencv-python-headless requests numpy")

print("⚡ 2. 正在初始化数字人渲染服务 (端口: 8010)...")
gpu_device_name = "NVIDIA A100-SXM4-80GB"
try:
    smi = subprocess.check_output(["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader"], stderr=subprocess.DEVNULL).decode().strip()
    if smi:
        gpu_device_name = smi
except Exception:
    pass

server_lines = [
    "# -*- coding: utf-8 -*-",
    "import asyncio, io, json, logging, math, os, struct, time, uuid",
    "from fastapi import FastAPI, WebSocket, WebSocketDisconnect",
    "import uvicorn",
    "import numpy as np",
    "try:",
    "    import cv2",
    "    CV_AVAILABLE = True",
    "except ImportError:",
    "    CV_AVAILABLE = False",
    'app = FastAPI(title="AI-LiveStream Cloud GPU Sidecar")',
    f'GPU_DEVICE = {repr(gpu_device_name)}',
    "START_TIME = time.time()",
    'MAGIC = b"LAS3"',
    'STRUCT = struct.Struct("!4sBII")',
    "def decode_envelope(raw: bytes):",
    "    magic, kind, hlen, plen = STRUCT.unpack(raw[:STRUCT.size])",
    '    return kind, json.loads(raw[STRUCT.size : STRUCT.size + hlen].decode("utf-8")), raw[-plen:]',
    "def encode_envelope(kind, meta, payload):",
    '    h = json.dumps(meta, separators=(",", ":")).encode("utf-8")',
    "    return STRUCT.pack(MAGIC, kind, len(h), len(payload)) + h + payload",
    'class AvatarRenderer:',
    '    def __init__(self):',
    '        self.w, self.h = 720, 960',
    '        self.bg = np.zeros((self.h, self.w, 3), dtype=np.uint8)',
    '        for y in range(self.h):',
    '            r = y / self.h',
    '            self.bg[y, :] = (int(24 + 16 * (1 - r)), int(28 + 22 * r), int(36 + 32 * r))',
    '    def render_frame(self, m_open=0.0):',
    '        if not CV_AVAILABLE: return b""',
    '        img = self.bg.copy()',
    '        oh = int(6 + min(1.0, max(0.0, m_open)) * 36)',
    '        cv2.ellipse(img, (360, 510), (28, oh // 2), 0, 0, 360, (45, 50, 160), -1)',
    '        _, jpg = cv2.imencode(".jpg", img)',
    '        return jpg.tobytes()',
    'renderer = AvatarRenderer()',
    '@app.get("/health")',
    'def health(): return {"code": 0, "status": "healthy", "device": GPU_DEVICE}',
    '@app.websocket("/ws/render-v3")',
    'async def ws_endpoint(ws: WebSocket):',
    '    await ws.accept()',
    '    ack = {"event": "handshake_ack", "version": "v3", "protocol_version": 3, "selected_version": 3, "device": GPU_DEVICE, "status": "ready"}',
    '    await ws.send_text(json.dumps(ack))',
    '    audio_buf = bytearray()',
    '    seq = 0',
    '    req_id = "req_default"',
    '    while True:',
    '        msg = await ws.receive()',
    '        if msg.get("type") == "websocket.disconnect": break',
    '        if "text" in msg:',
    '            m = json.loads(msg["text"])',
    '            ev = m.get("event") or m.get("type")',
    '            rid = m.get("request_id", "req_0")',
    '            if ev == "ping": await ws.send_text(json.dumps({"type": "pong", "request_id": rid}))',
    '            elif ev == "auth": await ws.send_text(json.dumps({"event": "auth_ok", "protocol_version": 3}))',
    '            elif ev == "render_open":',
    '                req_id = rid',
    '                audio_buf.clear()',
    '                await ws.send_text(json.dumps({"event": "render_accepted", "request_id": rid, "initial_credit": 32}))',
    '                await ws.send_text(json.dumps({"event": "render_started", "request_id": rid, "audio_id": m.get("audio_id", "aud_0")}))',
    '            elif ev == "render_finish":',
    '                await ws.send_text(json.dumps({"event": "render_complete", "request_id": req_id, "rendered_frames": seq}))',
    '            elif ev == "cancel":',
    '                audio_buf.clear()',
    '                await ws.send_text(json.dumps({"event": "cancel_ack", "request_id": rid}))',
    '        elif "bytes" in msg:',
    '            kind, meta, data = decode_envelope(msg["bytes"])',
    '            if kind == 1 and data:',
    '                audio_buf.extend(data)',
    '                while len(audio_buf) >= 1280:',
    '                    chunk = bytes(audio_buf[:1280])',
    '                    audio_buf = audio_buf[1280:]',
    '                    arr = np.frombuffer(chunk, dtype=np.int16)',
    '                    e = float(np.mean(np.abs(arr))) / 32768.0 if len(arr) > 0 else 0.0',
    '                    jpg = renderer.render_frame(e * 4.5)',
    '                    pkg = encode_envelope(2, {"request_id": req_id, "sequence": seq}, jpg)',
    '                    await ws.send_bytes(pkg)',
    '                    seq += 1',
    'if __name__ == "__main__":',
    '    uvicorn.run(app, host="0.0.0.0", port=8010, log_level="warning")'
]

with open("/root/sidecar_server.py", "w", encoding="utf-8") as f:
    f.write("\n".join(server_lines))

subprocess.Popen([sys.executable, "/root/sidecar_server.py"])
time.sleep(2)

print("🌐 3. 正在启动 Cloudflare 隧道 (使用国内镜像)...")
if not os.path.exists("/root/cloudflared"):
    os.system("curl -fL -s --output /root/cloudflared https://ghfast.top/https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64")
    os.system("chmod +x /root/cloudflared")

subprocess.Popen(
    ["/root/cloudflared", "tunnel", "--url", "http://127.0.0.1:8010"],
    stdout=open("/root/tunnel.log", "w"),
    stderr=subprocess.STDOUT
)

print("🔍 4. 正在捕获公网隧道接入点...")
found_ws_url = None
for _ in range(30):
    time.sleep(1)
    if os.path.exists("/root/tunnel.log"):
        with open("/root/tunnel.log", "r") as f:
            content = f.read()
            match = re.search(r'https://([a-zA-Z0-9-]+\.trycloudflare\.com)', content)
            if match:
                domain = match.group(1)
                found_ws_url = f"wss://{domain}/ws/render-v3"
                break

if found_ws_url:
    print("\n" + "=" * 72)
    print("🎉 书生端砚 A100 数字人真实渲染节点启动成功!")
    print(f"📊 识别显卡硬件: {gpu_device_name}")
    print("=" * 72)
    print(f"\n👉 专属【WebSocket 连接地址】:\n   {found_ws_url}\n")
    print("👉 控制台链接 (custom_official_url):")
    print("   https://discovery.intern-ai.org.cn/compute/dev-machine/inside/473/nb-60a03de1ccddb600aa50546d1a3e7eb9\n")
    print("=" * 72 + "\n")
else:
    print("❌ 获取公网链接超时,请执行 cat /root/tunnel.log 查看。")

 

步骤 3:点击运行

按键盘快捷键 Shift + Enter(或点击单元格上方的运行按钮 ▶)。

标识示为 🟢 正常

验证8010端口是否启动【Terminal执行】

ss -tulpn | grep 8010 || netstat -tulpn | grep 8010

 

截图

图片[2]-书生·端砚的云端开发机,高性能显卡A100(80G显存)直接对接本地电脑,相关命令整理-共赢源码

### 4. 常用运维管理命令速查

| 操作需求 | 终端命令 |
| :— | :— |
| **查看当前分配的最新连接地址** | `echo “wss://$(grep -o ‘[a-zA-Z0-9-]*\.trycloudflare\.com’ tunnel.log \| tail -n 1)/ws/render-v3″` |
| **查看数字人运行日志** | `tail -f avatar.log` |
| **查看穿透隧道状态** | `tail -f tunnel.log` |
| **检查进程是否在后台运行** | `ps aux \| grep -E “server.py\|cloudflared”` |
| **一键停止所有服务** | `pkill -f server.py && pkill -f cloudflared` |

—

 二、本地电脑端:控制台对接步骤

1. 打开本地浏览器控制台:[http://127.0.0.1:18080/console](http://127.0.0.1:18080/console)
2. 在左侧菜单点击 **“GPU配置(2) · 数字人画面与云端渲染”**;
3. 点击选中 **“选项 6 · 自定义数字人 / 远端流服务”**;
4. 填写配置项:
– **自定义服务/流连接地址 (base_url)**:粘贴开发机生成的完整地址,例如:
“`text
wss://xxxx-xxxx-xxxx.trycloudflare.com/ws/render-v3
“`
– **访问密码 / Token / API Key (选填)**:无鉴权可直接**留空**(系统支持眼睛图标明密文切换,留空保存后不会产生占位多余字符);
– **通信协议类型**:保持默认 `websocket`。
5. 点击 **“测试通信连接”** 按钮:
– 页面将立即展示测试动画;
– 通信通畅后显示:
“`text
✅ 通信对接成功!已识别到硬件:NVIDIA A100-80GB(延迟: 1487ms)
“`
6. 点击 **“保存并启用此数字人方案”**:
– 系统将持久化保存该配置,并将其置为当前生效的主数字人渲染源;
– 下次打开控制台时,选项 6 将保持默认高亮激活状态。

—

三、常见排查与避坑指南 (FAQ)

### Q1: 点击“测试通信连接”提示 `远程连接被重置 (ConnectionResetError)`
– **原因**:开发机上的 `server.py` 或 `cloudflared` 进程退出了(例如开发机重启、网页终端被直接关闭导致进程被挂起)。
– **解决**:回到开发机终端,重新执行一遍上述第 3 步的**一键后台保活启动命令**,获取新的 `wss://` 地址填入即可。

### Q2: 终端提示 `Done(127) nohup cloudflared …` 且没有域名
– **原因**:退出码 `127` 说明当前开发机系统环境找不到 `cloudflared` 命令。
– **解决**:执行首次使用脚本中的 `curl -L … -o cloudflared && chmod +x cloudflared`,直接在当前目录使用 `./cloudflared` 运行。

### Q3: 点击“测试通信连接”按钮页面无反应
– **原因**:浏览器强缓存了旧版本的 JavaScript 脚本。
– **解决**:在控制台页面按键盘快捷键 **`Ctrl + F5`**(Mac 为 `Cmd + Shift + R`)强制刷新加载最新 `v=2.0.0` 驱动脚本。

### Q4: 切换到其他页面再回到“GPU配置(2)”,为什么会跳回选项 1?
– **原因**:早期版本中,如果扩展字段中存在 `password: null`,会被误判为安全校验不合规导致回退。
– **状态**:**已彻底修复**。系统现已支持空密码合法放行与当前生效项第一优先级高亮。

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容