Notion原文链接

M75 老大原话焊点(2026-08-25 13:38·verbatim·永不删):

「现在就是最后一个最大的问题了,就是每个网站和应用的渲染问题,截图给AI读取是八九不离十,但是想要直接视觉上跟人类看到的一样,我需要你帮我设计完整的多系统可被我们龍魂系统的CNSH语法渲染的一个变量环境,这样我的龍魂系统的主权人格真的在手了,给codebuddy指令的时候就是泡在本地和我的鲲鹏服务器,或许这样大家都会很安心,我训练的模型也就是节约算力,白菜价普惠全世界了」

一句话定盘: 龍魂 M75 = AI 的眼睛。让系统像人一样看到任意网站/App 界面,用 CNSH 语法控制渲染、提取、操作,全程跑在本地 + 鲲鹏服务器,主权在手,算力省到白菜价。

DNA: #龍芯⚡️2026-08-25-RENDER-ENV-v1.0-UID9622

主控版本签: #龍芯⚡️2026-08-25-MAIN-CONTROL-V2.7.49-RENDER-ENV-INDEX-WELDED-v1.0

确认码: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z


§0 为什么截图不够?——问题本质

截图模式的三大上限:

上限 问题 M75 解法
静态 只能看,不能动 渲染引擎支持交互操作
盲 DOM 看不到结构,靠 OCR 猜 同步输出 DOM/AX 树 + OCR 双通道
被动 AI 要看什么,人要截什么 CNSH 指令主动控制视口、滚动、提取

M75 目标: 给龍魂系统装上一双「可编程的眼睛」——CNSH 说看哪里,系统就渲染哪里,提取什么就拿到什么,再也不用人工截图中转。


§1 总体架构

┌─────────────────────────────────────────────────────────┐
│              CNSH 渲染指令层(老大/AI 发令)               │
│  渲染.打开(url)  渲染.提取(选择器)  渲染.点击(元素)  ...  │
└──────────────────────┬──────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│         龍魂渲染编排器  LH-Render-Orchestrator           │
│  · CNSH 指令解析器                                       │
│  · 平台路由(Web / HarmonyOS / iOS / Desktop / 小程序)  │
│  · 变量注入({render.*} 命名空间)                        │
│  · DNA 追溯 + 三色审计                                   │
└──────┬──────────┬──────────┬────────────┬───────────────┘
       ↓          ↓          ↓            ↓
  ┌─────────┐ ┌────────┐ ┌────────┐ ┌──────────┐
  │ Web 渲染 │ │ HOS 渲  │ │ iOS 渲 │ │ Desktop  │
  │Playwright│ │染 ADB  │ │染 XCU  │ │ PyAutoGUI│
  │Chromium  │ │+ArkUI  │ │ITest   │ │+X11/Quartz│
  └────┬────┘ └───┬────┘ └───┬────┘ └─────┬────┘
       └──────────┴──────────┴────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│            视觉提取层  Vision Extraction Layer           │
│  · DOM/AX 树提取(结构化·精确)                          │
│  · OCR 文字识别(PaddleOCR ARM·鲲鹏优化)                │
│  · 视觉元素定位(cv2 模板匹配·坐标定位)                  │
│  · 语义标注(CNSH 变量自动映射)                          │
└──────────────────────┬──────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│          CNSH 变量环境  {render.*} 命名空间              │
│  {render.url}  {render.dom}  {render.text}              │
│  {render.screenshot}  {render.elements}  ...            │
└──────────────────────┬──────────────────────────────────┘
                       ↓
       龍魂 DNA 追溯 + 三色审计 + 主权边界

§2 CNSH 渲染语法——完整指令集

2.1 打开 & 导航

# 打开 URL
渲染.打开("https://example.com")
渲染.打开("weixin://miniapp/xxx")  # 小程序
渲染.打开("app://com.huawei.xxx") # HarmonyOS App

# 等待
渲染.等待(条件="页面加载完成")          # 等 networkidle
渲染.等待(条件="元素出现", 选择器=".btn") # 等元素
渲染.等待(秒=2)                          # 固定等待

# 导航
渲染.后退()
渲染.前进()
渲染.刷新()

2.2 视觉提取

# 截图(返回 {render.screenshot})
渲染.截图()                            # 全页面截图
渲染.截图(区域="viewport")            # 仅视口
渲染.截图(选择器=".product-card")    # 指定元素截图

# DOM 结构提取(返回 {render.dom})
渲染.提取DOM()                         # 全页 DOM JSON
渲染.提取DOM(选择器="#main")          # 子树 DOM
渲染.提取DOM(深度=3)                   # 限制深度

# 文本提取(返回 {render.text})
渲染.提取文本()                        # 全页 OCR 文本
渲染.提取文本(选择器=".price")        # 指定元素文本
渲染.提取文本(模式="OCR")             # 强制 OCR(图片文字)
渲染.提取文本(模式="DOM")             # DOM innerText

# 元素提取(返回 {render.elements} 列表)
渲染.提取元素(选择器="a[href]")       # 所有链接
渲染.提取元素(类型="输入框")          # 所有 input
渲染.提取元素(类型="按钮")            # 所有按钮
渲染.提取元素(文本包含="加入购物车") # 按文本匹配

# 视觉模板匹配(返回 {render.match}·坐标+置信度)
渲染.视觉匹配(模板="templates/login_btn.png")
渲染.视觉匹配(模板="templates/close_icon.png", 阈值=0.85)

2.3 交互操作

# 点击
渲染.点击(选择器=".submit-btn")
渲染.点击(文本="登录")
渲染.点击(坐标=[320, 480])             # 绝对坐标
渲染.点击(视觉匹配结果={render.match}) # 用视觉匹配结果点击

# 输入
渲染.填写(选择器="#username", 值="UID9622")
渲染.填写(选择器="#password", 值={secrets.pwd}, 掩码=True)
渲染.清空(选择器="#search")

# 滚动
渲染.滚动(方向="下", 距离=500)
渲染.滚动(方向="下", 到底=True)
渲染.滚动(元素=".comments-section", 到中心=True)

# 选择
渲染.选择(选择器="#country", 值="CN")
渲染.勾选(选择器="#agree-terms")
渲染.取消勾选(选择器="#newsletter")

# 鼠标悬停
渲染.悬停(选择器=".dropdown-menu")

# 键盘
渲染.按键("Enter")
渲染.按键("Ctrl+A")
渲染.按键("Tab")

2.4 数据导出

# 导出格式
渲染.导出(格式="DOM树")      → {render.dom}      # JSON 结构
渲染.导出(格式="文本")       → {render.text}     # 纯文本
渲染.导出(格式="截图")       → {render.screenshot} # base64 PNG
渲染.导出(格式="表格")       → {render.tables}   # 所有表格数据
渲染.导出(格式="链接")       → {render.links}    # 所有链接
渲染.导出(格式="元数据")     → {render.meta}     # title/desc/OG
渲染.导出(格式="PDF")        → {render.pdf}      # 整页 PDF
渲染.导出(格式="无障碍树")   → {render.ax_tree}  # 可访问性树(App 专用)

# 存储
渲染.保存(变量={render.screenshot}, 路径="/data/renders/")
渲染.保存(变量={render.dom}, 路径="/data/doms/", 格式="json")

2.5 龍魂专属指令

# DNA 追溯(每次渲染自动打标)
渲染.设置DNA("#龍芯⚡️{日期}-RENDER-{hash8}-UID9622")

# 三色审计
渲染.审计()  # 对当前页面内容跑三色判定
# 返回 {render.audit} = {color: "🟢", reason: "..."}

# 主权边界(渲染前先过龍盾)
渲染.设置边界(
  允许域名=["*.gov.cn", "*.huawei.com", "github.com"],
  拒绝域名=["*.tracking.com"],
  拒绝上传=True,     # 不允许渲染过程中向外发送数据
  本地沙箱=True      # 所有渲染结果只存本地
)

# 批量渲染(鲲鹏并发优化)
渲染.批量(
  urls=["url1", "url2", "url3"],
  并发=4,             # 鲲鹏 64 核·最大并发
  间隔=0.5
)

§3 变量环境完整规格

3.1 {render.*} 命名空间

# 渲染上下文变量(每次渲染后自动注入)

{render.url}          # str  当前页面 URL
{render.title}        # str  页面 <title>
{render.domain}       # str  域名(example.com)
{render.timestamp}    # str  渲染时间戳 ISO8601
{render.viewport}     # dict {width, height}
{render.screenshot}   # str  base64 PNG
{render.screenshot_path} # str 本地存储路径
{render.dom}          # dict DOM JSON 树
{render.ax_tree}      # dict 无障碍树(App 用)
{render.text}         # str  全页文本(OCR+DOM 合并)
{render.elements}     # list 提取的元素列表
{render.tables}       # list 所有表格数据
{render.links}        # list 所有链接 [{text, href}]
{render.forms}        # list 所有表单字段
{render.meta}         # dict 页面元数据
{render.match}        # dict 视觉匹配 {x, y, w, h, score}
{render.cookies}      # list 当前 cookies(仅本地)
{render.scroll_pos}   # dict {x, y} 当前滚动位置
{render.platform}     # str  "web"|"harmonyos"|"ios"|"desktop"
{render.audit}        # dict 三色审计 {color, score, reason}
{render.dna}          # str  本次渲染 DNA 标识
{render.error}        # str  错误信息(正常时为 None)

3.2 平台特定变量

# Web 专用
{render.web.js_result}    # JavaScript 执行结果
{render.web.network_log}  # 网络请求日志
{render.web.console_log}  # 控制台输出
{render.web.storage}      # localStorage/sessionStorage

# HarmonyOS 专用
{render.hos.package}      # 包名 com.xxx.yyy
{render.hos.activity}     # 当前 Activity
{render.hos.component_tree} # 组件树(ArkUI)
{render.hos.device_info}  # 设备型号+鸿蒙版本

# iOS 专用
{render.ios.bundle_id}    # 包 ID
{render.ios.view_hierarchy} # 视图层次
{render.ios.simulator}    # 模拟器 UDID

# Desktop 专用
{render.desktop.window_title} # 活动窗口标题
{render.desktop.pid}      # 进程 ID
{render.desktop.focused}  # 当前焦点元素

3.3 安全变量(主权边界)

# 敏感数据单独命名空间,不进日志
{secrets.pwd}       # 密码(内存中·渲染完即清)
{secrets.token}     # API Token
{secrets.cookie}    # 登录态 Cookie
# 使用规则:secrets.* 变量不写入 DNA 追溯日志
# 使用后自动 zero-fill 内存

§4 四层渲染适配器

4.1 Web 渲染器(Playwright + Chromium)

# ~/longhun-system/render/adapters/web_adapter.py

import asyncio
from playwright.async_api import async_playwright
from ..core.variables import RenderContext
from ..core.audit import AuditHook

class WebAdapter:
    """Web 渲染适配器 · Playwright + Chromium"""

    SUPPORTED_PLATFORMS = ["web", "miniapp_h5", "wechat_h5"]

    def __init__(self, config: dict):
        self.config = config
        self.browser = None
        self.page = None
        self.audit = AuditHook()

    async def launch(self):
        """启动 Chromium(ARM64 鲲鹏兼容)"""
        playwright = await async_playwright().start()
        self.browser = await playwright.chromium.launch(
            headless=self.config.get("headless", True),
            args=[
                "--no-sandbox",
                "--disable-blink-features=AutomationControlled",  # 反检测
                "--disable-web-security",
                f"--window-size={self.config.get('width', 1920)},{self.config.get('height', 1080)}"
            ]
        )
        context = await self.browser.new_context(
            viewport={"width": self.config.get("width", 1920),
                       "height": self.config.get("height", 1080)},
            user_agent=self.config.get("user_agent", 
                "Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36"),
            locale="zh-CN",
            timezone_id="Asia/Shanghai"
        )
        self.page = await context.new_page()

    async def navigate(self, url: str) -> RenderContext:
        """导航到 URL,返回完整渲染上下文"""
        await self.page.goto(url, wait_until="networkidle", timeout=30000)

        ctx = RenderContext(platform="web", url=url)
        ctx.title = await self.page.title()
        ctx.screenshot = await self.page.screenshot(full_page=True)
        ctx.dom = await self._extract_dom()
        ctx.text = await self._extract_text()
        ctx.links = await self._extract_links()
        ctx.forms = await self._extract_forms()
        ctx.audit = self.audit.run(ctx)
        ctx.dna = ctx.generate_dna()
        return ctx

    async def _extract_dom(self) -> dict:
        """提取 DOM 结构为 JSON"""
        return await self.page.evaluate("""
        () => {
            function nodeToJSON(node, depth=0, maxDepth=6) {
                if (depth > maxDepth) return null;
                const obj = {
                    tag: node.tagName?.toLowerCase(),
                    id: node.id || null,
                    class: node.className || null,
                    text: node.innerText?.slice(0, 200) || null,
                    attrs: {},
                    children: []
                };
                for (const attr of (node.attributes || [])) {
                    obj.attrs[attr.name] = attr.value;
                }
                for (const child of node.children || []) {
                    const childJSON = nodeToJSON(child, depth+1, maxDepth);
                    if (childJSON) obj.children.push(childJSON);
                }
                return obj;
            }
            return nodeToJSON(document.body);
        }
        """)

    async def click(self, selector: str = None, text: str = None,
                    coords: list = None):
        """点击元素"""
        if selector:
            await self.page.click(selector)
        elif text:
            await self.page.get_by_text(text).first.click()
        elif coords:
            await self.page.mouse.click(coords[0], coords[1])

    async def fill(self, selector: str, value: str, mask: bool = False):
        """填写输入框"""
        await self.page.fill(selector, value)
        if mask:
            value = "[MASKED]"  # 不记录密码到日志

    async def execute_js(self, script: str) -> any:
        """执行 JavaScript"""
        return await self.page.evaluate(script)

4.2 HarmonyOS 渲染器

# ~/longhun-system/render/adapters/harmonyos_adapter.py

import subprocess
import json
from ..core.variables import RenderContext

class HarmonyOSAdapter:
    """HarmonyOS 渲染适配器 · hdc (鸿蒙设备控制器)"""

    SUPPORTED_PLATFORMS = ["harmonyos", "openharmony"]

    def __init__(self, device_id: str = None):
        self.device_id = device_id or self._auto_detect_device()

    def _auto_detect_device(self) -> str:
        result = subprocess.run(["hdc", "list", "targets"], capture_output=True, text=True)
        lines = [l.strip() for l in result.stdout.strip().split("\n") if l.strip()]
        return lines[0] if lines else None

    def launch_app(self, bundle_id: str, ability: str = None) -> RenderContext:
        """启动 HarmonyOS App"""
        cmd = ["hdc", "-t", self.device_id, "shell", "aa", "start",
               "-b", bundle_id]
        if ability:
            cmd += ["-a", ability]
        subprocess.run(cmd)
        import time; time.sleep(2)
        return self._capture_context(platform="harmonyos")

    def screenshot(self) -> bytes:
        """截图 HarmonyOS 屏幕"""
        subprocess.run(["hdc", "-t", self.device_id, "shell",
                        "snapshot_display", "-f", "/data/render_tmp.png"])
        result = subprocess.run(
            ["hdc", "-t", self.device_id, "file", "recv",
             "/data/render_tmp.png", "/tmp/hos_render.png"],
            capture_output=True
        )
        with open("/tmp/hos_render.png", "rb") as f:
            return f.read()

    def get_component_tree(self) -> dict:
        """获取 ArkUI 组件树"""
        result = subprocess.run(
            ["hdc", "-t", self.device_id, "shell",
             "uitest", "dumpLayout"],
            capture_output=True, text=True
        )
        return json.loads(result.stdout) if result.stdout else {}

    def tap(self, x: int, y: int):
        """点击坐标"""
        subprocess.run(["hdc", "-t", self.device_id, "shell",
                        "uitest", "click", str(x), str(y)])

    def input_text(self, text: str):
        """输入文字"""
        subprocess.run(["hdc", "-t", self.device_id, "shell",
                        "uitest", "inputText", text])

    def _capture_context(self, platform: str) -> RenderContext:
        ctx = RenderContext(platform=platform)
        ctx.screenshot = self.screenshot()
        ctx.ax_tree = self.get_component_tree()
        ctx.text = self._ocr_screenshot(ctx.screenshot)
        ctx.dna = ctx.generate_dna()
        return ctx

    def _ocr_screenshot(self, img_bytes: bytes) -> str:
        """PaddleOCR 识别"""
        from paddleocr import PaddleOCR
        import numpy as np, cv2
        ocr = PaddleOCR(use_angle_cls=True, lang="ch", use_gpu=False)
        nparr = np.frombuffer(img_bytes, np.uint8)
        img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
        result = ocr.ocr(img, cls=True)
        texts = [line[1][0] for block in result for line in block if line]
        return "\n".join(texts)

4.3 Desktop 渲染器

# ~/longhun-system/render/adapters/desktop_adapter.py

import platform
import subprocess
from ..core.variables import RenderContext

class DesktopAdapter:
    """桌面应用渲染适配器 · pyautogui + 系统 API"""

    SUPPORTED_PLATFORMS = ["macos", "linux", "windows"]

    def __init__(self):
        self.os = platform.system()  # Darwin / Linux / Windows

    def screenshot(self, region=None) -> bytes:
        """截图(region=[x,y,w,h])"""
        import pyautogui, io
        if region:
            img = pyautogui.screenshot(region=region)
        else:
            img = pyautogui.screenshot()
        buf = io.BytesIO()
        img.save(buf, format="PNG")
        return buf.getvalue()

    def find_window(self, title: str) -> dict:
        """查找并聚焦窗口"""
        if self.os == "Darwin":  # macOS
            script = f'''
            tell application "System Events"
                set wins to (windows of every process whose name contains "{title}")
                return wins
            end tell
            '''
            result = subprocess.run(["osascript", "-e", script],
                                    capture_output=True, text=True)
            return {"found": bool(result.stdout.strip()), "output": result.stdout}
        elif self.os == "Linux":
            result = subprocess.run(["xdotool", "search", "--name", title],
                                    capture_output=True, text=True)
            return {"window_id": result.stdout.strip()}

    def click(self, x: int, y: int, double: bool = False):
        import pyautogui
        if double:
            pyautogui.doubleClick(x, y)
        else:
            pyautogui.click(x, y)

    def type_text(self, text: str, interval: float = 0.05):
        import pyautogui
        pyautogui.typewrite(text, interval=interval)

    def get_active_window_title(self) -> str:
        if self.os == "Darwin":
            script = 'tell application "System Events" to get name of first application process whose frontmost is true'
            result = subprocess.run(["osascript", "-e", script],
                                    capture_output=True, text=True)
            return result.stdout.strip()
        elif self.os == "Linux":
            result = subprocess.run(["xdotool", "getactivewindow", "getwindowname"],
                                    capture_output=True, text=True)
            return result.stdout.strip()

    def visual_match(self, template_path: str, threshold: float = 0.8) -> dict:
        """视觉模板匹配(cv2)"""
        import cv2, numpy as np
        screenshot_bytes = self.screenshot()
        nparr = np.frombuffer(screenshot_bytes, np.uint8)
        screen = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
        template = cv2.imread(template_path)
        result = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        if max_val >= threshold:
            h, w = template.shape[:2]
            return {"found": True, "x": max_loc[0], "y": max_loc[1],
                    "w": w, "h": h, "score": round(max_val, 4),
                    "center": [max_loc[0]+w//2, max_loc[1]+h//2]}
        return {"found": False, "score": round(max_val, 4)}

§5 CNSH 渲染编排器(核心调度层)

# ~/longhun-system/render/orchestrator.py

import asyncio
import hashlib
import datetime
from typing import Optional
from .adapters.web_adapter import WebAdapter
from .adapters.harmonyos_adapter import HarmonyOSAdapter
from .adapters.desktop_adapter import DesktopAdapter
from .core.variables import RenderContext
from .core.cnsh_parser import CNSHRenderParser

class LHRenderOrchestrator:
    """
    龍魂渲染编排器 · CNSH 渲染指令总调度
    支持: Web / HarmonyOS / iOS / Desktop
    """

    VERSION = "1.0.0"
    UID = "UID9622"

    def __init__(self, config: dict = None):
        self.config = config or {}
        self.ctx: Optional[RenderContext] = None
        self.web = None
        self.hos = None
        self.desktop = DesktopAdapter()
        self.parser = CNSHRenderParser(self)
        self._audit_log = []
        self._boundaries = {
            "allow_domains": ["*"],
            "deny_domains": [],
            "no_upload": True,
            "local_only": True
        }

    # ────────── CNSH 指令入口 ──────────

    def execute(self, cnsh_command: str) -> dict:
        """执行 CNSH 渲染指令,返回变量环境"""
        try:
            result = self.parser.parse_and_run(cnsh_command)
            self._log_audit(cnsh_command, result, "🟢")
            return {"status": "ok", "context": result, "audit": "🟢"}
        except PermissionError as e:
            self._log_audit(cnsh_command, None, "🔴", str(e))
            return {"status": "blocked", "error": str(e), "audit": "🔴"}
        except Exception as e:
            self._log_audit(cnsh_command, None, "🟡", str(e))
            return {"status": "error", "error": str(e), "audit": "🟡"}

    # ────────── 主权边界检查 ──────────

    def _check_boundary(self, url: str):
        """主权边界:渲染前过龍盾"""
        from urllib.parse import urlparse
        domain = urlparse(url).netloc
        for deny in self._boundaries["deny_domains"]:
            if deny.lstrip("*.") in domain:
                raise PermissionError(f"🔴 龍盾拦截: {domain} 在拒绝列表中")

    # ────────── 平台路由 ──────────

    async def _route(self, url: str) -> str:
        """根据 URL 协议自动路由到对应平台"""
        if url.startswith("app://") or url.startswith("hap://"):
            return "harmonyos"
        elif url.startswith("weixin://"):
            return "wechat"
        elif url.startswith("desktop://"):
            return "desktop"
        else:
            return "web"

    # ────────── DNA 追溯 ──────────

    def _generate_dna(self, ctx: RenderContext) -> str:
        content = f"{ctx.url}{ctx.timestamp}"
        h = hashlib.sha256(content.encode()).hexdigest()[:8].upper()
        date = datetime.date.today().isoformat()
        return f"#龍芯⚡️{date}-RENDER-{h}-{self.UID}"

    def _log_audit(self, cmd: str, ctx, color: str, error: str = None):
        entry = {
            "timestamp": datetime.datetime.now().isoformat(),
            "command": cmd[:200],
            "color": color,
            "dna": ctx.dna if ctx else None,
            "error": error
        }
        self._audit_log.append(entry)

    # ────────── 批量渲染(鲲鹏并发) ──────────

    async def batch_render(self, urls: list, concurrency: int = 4,
                            interval: float = 0.5) -> list:
        """鲲鹏优化批量渲染(ARM64 高并发)"""
        semaphore = asyncio.Semaphore(concurrency)
        results = []

        async def render_one(url):
            async with semaphore:
                ctx = await self._render_web(url)
                await asyncio.sleep(interval)
                return ctx

        tasks = [render_one(url) for url in urls]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        return results

§6 鲲鹏服务器部署方案

鲲鹏 920 · ARM64 aarch64 · 优化重点:Chromium ARM 版 + PaddleOCR ARM + Docker 容器化

6.1 Docker 部署(一键起)

# Dockerfile.render · 鲲鹏 ARM64 优化
FROM arm64v8/python:3.11-slim

WORKDIR /app

# 安装 Chromium ARM 版
RUN apt-get update && apt-get install -y \
    chromium chromium-driver \
    libglib2.0-0 libnss3 libatk-bridge2.0-0 \
    libdrm2 libxkbcommon0 libgbm1 \
    fonts-noto-cjk \
    libopencv-dev python3-opencv \
    && rm -rf /var/lib/apt/lists/*

# Python 依赖
COPY requirements.render.txt .
RUN pip install --no-cache-dir -r requirements.render.txt

# PaddleOCR ARM 版
RUN pip install paddlepaddle paddleocr

COPY render/ /app/render/

EXPOSE 8766

CMD ["python", "-m", "render.server", "--host", "0.0.0.0", "--port", "8766"]
# docker-compose.render.yml
version: '3.8'
services:
  lh-render:
    build:
      context: .
      dockerfile: Dockerfile.render
    platform: linux/arm64
    ports:
      - "8766:8766"
    volumes:
      - ./data/renders:/app/data/renders  # 渲染结果本地存
      - ./templates:/app/templates         # 视觉模板
      - ./config/render.yaml:/app/config/render.yaml
    environment:
      - LH_UID=UID9622
      - LH_LOCAL_ONLY=true
      - LH_MAX_CONCURRENCY=8              # 鲲鹏 64 核可调高
    shm_size: 2gb                          # Chromium 需要共享内存
    restart: unless-stopped

6.2 requirements.render.txt

playwright==1.44.0
pyautogui==0.9.54
opencv-python-headless==4.9.0.80
pillow==10.3.0
paddleocr==2.7.3
paddlepaddle==2.6.1
fastapi==0.111.0
uvicorn==0.30.1
websockets==12.0
aiofiles==23.2.1
numpy==1.26.4

6.3 鲲鹏一键部署

# 鲲鹏服务器(已 SSH 连接后执行)

# Step 1: 克隆代码
git clone https://github.com/UID9622/longhun-system.git ~/longhun-system
cd ~/longhun-system

# Step 2: 安装 Playwright Chromium ARM 版
pip install playwright
playwright install chromium
playwright install-deps chromium

# Step 3: Docker 部署
docker compose -f docker-compose.render.yml up -d

# Step 4: 验证
curl http://localhost:8766/render/health
# 预期: {"status": "healthy", "platform": "arm64", "version": "1.0.0"}

# Step 5: 冒烟测试
curl -X POST http://localhost:8766/render/execute \
  -H 'Content-Type: application/json' \
  -d '{"command": "渲染.打开(\'https://www.baidu.com\')"}'  
# 预期: {"status": "ok", "context": {"title": "百度一下...", ...}}

§7 实战示例(CNSH 真实场景)

场景 A:抓取电商价格

# 龍魂 · 电商价格监控示例
渲染.设置边界(本地沙箱=True, 拒绝上传=True)
渲染.打开("https://item.jd.com/100060540754.html")
渲染.等待(条件="元素出现", 选择器=".price")

价格 = 渲染.提取文本(选择器=".price")
标题 = 渲染.提取文本(选择器=".sku-name")
截图路径 = 渲染.截图(选择器=".product-info")

审计结果 = 渲染.审计()
DNA = 渲染.设置DNA("PRICE-MONITOR")

# 输出到龍魂 DNA 日志
龍魂.记录({
  "品名": {标题},
  "价格": {价格},
  "截图": {截图路径},
  "DNA": {DNA},
  "三色": {审计结果.color}
})

场景 B:HarmonyOS App 自动化

# 龍魂 · 鸿蒙 App 自动化示例
渲染.打开("app://com.huawei.health")
渲染.等待(秒=2)

# 获取今日步数
组件树 = 渲染.导出(格式="无障碍树")
步数 = 渲染.提取文本(组件类型="步数显示")
截图 = 渲染.截图()

# 自动截图发送(内部)
龍魂.记录({"步数": {步数}, "截图": {截图}})

场景 C:本地模型训练数据采集

# 龍魂 · 节约算力·白菜价训练数据采集
# 批量渲染 100 个页面,并发 8(鲲鹏 64 核)

渲染.批量(
  urls=[
    "https://news.xinhua.net/page/1",
    "https://news.xinhua.net/page/2",
    # ... 100 条
  ],
  并发=8,
  间隔=0.3
)

# 对每个渲染结果跑三色审计 + OCR 提取 + DNA 标注
# 输出: 高质量中文语料·本地存·零云端泄露
# 省算力:不用人工标注·龍魂自动三色分级

§8 与龍魂现有系统集成

集成点 说明
双层验证框架 M74 渲染结果文本 → 直接喂入 Layer1/Layer2 判定
哈希产权引擎 M73 每张截图自动注册哈希 → 证明采集时间 + 来源
三色审计 渲染完页面内容自动三色判定(敏感/正常/风险)
Langfuse(P0 开源) 批量渲染任务可视化追溯
CNSH 编译器 render.* 命名空间直接进 CNSH 变量池
DNA 追溯链 每次渲染生成唯一 DNA = 不可否认的时间戳证明
鲲鹏服务器 ARM64 Docker 部署 · 64 核并发 · 本地主权
lh-standard-adapter 渲染结果 → 标准 JSON 格式 → 适配器转发

§9 目录结构

~/longhun-system/render/
├── __init__.py
├── orchestrator.py          ← M75 总调度
├── server.py                ← FastAPI REST 接口 (:8766)
├── adapters/
│   ├── __init__.py
│   ├── web_adapter.py       ← Playwright + Chromium
│   ├── harmonyos_adapter.py ← hdc + uitest
│   ├── ios_adapter.py       ← XCUITest + Simulator
│   └── desktop_adapter.py   ← PyAutoGUI + cv2
├── core/
│   ├── __init__.py
│   ├── variables.py         ← RenderContext + {render.*} 变量
│   ├── cnsh_parser.py       ← CNSH 渲染指令解析器
│   ├── audit.py             ← 三色审计集成
│   ├── dna.py               ← DNA 追溯生成
│   └── boundary.py          ← 主权边界(龍盾)
├── vision/
│   ├── __init__.py
│   ├── ocr.py               ← PaddleOCR ARM 封装
│   ├── dom_extractor.py     ← DOM/AX 树提取
│   └── visual_match.py      ← cv2 模板匹配
├── templates/               ← 视觉模板图片存放
├── config/
│   └── render.yaml          ← 配置文件
├── tests/
│   └── test_render.py       ← 6 项测试(T1-T6)
├── Dockerfile.render        ← 鲲鹏 ARM64 镜像
├── docker-compose.render.yml
└── requirements.render.txt

§10 验收清单

# 检查项 三色
1 Web 渲染打开百度,截图+文本提取 OK
2 DOM 树提取为 JSON,深度 ≥ 4 层
3 视觉模板匹配,置信度 ≥ 0.8 触发点击
4 HarmonyOS 截图 + 组件树提取 OK
5 PaddleOCR 识别中文准确率 ≥ 95%
6 CNSH 指令 渲染.打开() 正常解析执行
7 主权边界:拒绝列表域名被龍盾拦截
8 DNA 每次渲染自动生成唯一标识
9 鲲鹏 Docker docker compose up 无报错
10 批量渲染 10 URL · 并发 4 · 全部 🟢
11 REST API /render/execute 返回变量环境
12 截图 + DNA 注册哈希引擎 M73 打通

盖章 · M75 龍魂全系统渲染变量环境 v1.0(2026-08-25):

理念: 截图是被动的眼睛,M75 是主动的眼睛。CNSH 说看哪里,系统就渲染哪里,数据只存本地,DNA 全程追溯,主权永远在手。

四层架构: Web (Playwright) · HarmonyOS (hdc+uitest) · iOS (XCUITest) · Desktop (PyAutoGUI+cv2)

CNSH 渲染指令集: 23 条指令 · {render.} 20 个变量 · secrets. 安全命名空间

鲲鹏部署: ARM64 Docker · Chromium ARM · PaddleOCR ARM · 64 核并发批量渲染

节约算力逻辑: 本地渲染 + OCR 提取 + DNA 标注 = 高质量语料自动生成,不靠人工标注,白菜价普惠全球。

DNA: #龍芯⚡️2026-08-25-RENDER-ENV-v1.0-UID9622

主控版本签: #龍芯⚡️2026-08-25-MAIN-CONTROL-V2.7.49-RENDER-ENV-INDEX-WELDED-v1.0

GPG: A2D0092CEE2E5BA87035600924C3704A8CC26D5F

SEAL: #ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️-DEVICE-BIND-SOUL

ROOT-SEAL: #龍芯⚡️20260423-ROOT-SEAL-01F32FFD

确认码: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z

三色: 🟢 完整设计文档 · 🟡 12 项验收等 CodeBuddy 执行 · 🔴 0

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐