前言

在 Agentic AI 時代,智能體需要與真實世界交互,而瀏覽器是連接虛擬世界與現實世界的重要橋樑。AgentRun Browser Sandbox 為智能體提供了安全、高性能、免運維的瀏覽器執行環境,讓 AI Agent 真正具備"上網"的能力——從網頁抓取、信息提取到表單填寫、自動化操作,一切皆可實現。

AgentRun Browser Sandbox 介紹

什麼是 Browser Sandbox?

Browser Sandbox 是 AgentRun 平台提供的雲原生無頭瀏覽器沙箱服務,基於阿里雲函數計算(FC)構建。它為智能體提供了一個安全隔離的瀏覽器執行環境,支持通過標準的 Chrome DevTools Protocol (CDP) 遠程控制瀏覽器實例。

核心特性

無頭瀏覽器能力

  • 內置 Chromium/Chrome 瀏覽器,支持完整的 Web 標準
  • 原生兼容 Puppeteer、Playwright 等主流自動化框架
  • 支持通過 CDP 協議進行精細化控制

實時可視化

  • 內置 VNC 服務,支持實時查看瀏覽器界面
  • 提供操作錄製功能,方便調試和回放
  • 支持通過 noVNC 客户端在網頁中直接觀看

安全與隔離

  • 每個沙箱實例運行在獨立的容器環境中
  • 文件系統和進程空間完全隔離
  • 支持 WSS 加密傳輸,確保數據安全

Serverless 架構

  • 按需創建,按量付費,無需提前預置資源
  • 快速彈性伸縮,支持高併發場景
  • 零運維,無需管理服務器和瀏覽器依賴

主要應用場景

  • AI Agent 賦能: 為大模型提供"眼睛"和"手",執行網頁瀏覽、信息提取、在線操作等任務
  • 自動化測試: 在雲端運行端到端(E2E)測試和視覺迴歸測試
  • 數據採集: 穩定、高效地進行網頁抓取,應對動態加載和反爬蟲挑戰
  • 內容生成: 自動化生成網頁截圖或 PDF 文檔

上手使用 Agentrun Browser Sandbox

AgentRun SDK 快速介紹

後續的內容將基於 Agentrun SDK 進行,因此我們先對 SDK 進行簡要介紹

AgentRun SDK 是一個開源的 Python 工具包,旨在簡化智能體與 AgentRun 平台各種服務(包括 Browser Sandbox)的集成。它提供了統一的接口,讓您可以用幾行代碼就將沙箱能力集成到現有的 Agent 框架中。SDK 的核心功能如下:

統一集成接口

  • 提供對 LangChain、AgentScope 等主流框架的開箱即用支持
  • 統一的模型代理接口,簡化多模型管理
  • 標準化的工具註冊機制

Sandbox 生命週期管理

  • 自動創建和銷燬沙箱實例
  • 支持會話級別的狀態保持
  • 靈活的資源配置和超時控制

安裝 AgentRun SDK

pip install agentrun-sdk[playwright,server]

注意: 確保您的 Python 環境版本在 3.10 及以上。

基本使用示例

以下是使用 AgentRun SDK 創建和管理 Browser Sandbox 的核心代碼:

from agentrun.sandbox import Sandbox, TemplateType
from playwright.sync_api import sync_playwright

# 創建 Browser Sandbox
sandbox = Sandbox.create(
    template_type=TemplateType.BROWSER,
    template_name="your-template-name",
    sandbox_idle_timeout_seconds=300
)

# 獲取 CDP URL(用於 Playwright 連接)
cdp_url = sandbox.get_cdp_url()

# 使用 Playwright 連接並操作
with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(cdp_url)
    page = browser.contexts[0].pages[0]
    
    page.goto("https://www.example.com")
    page.screenshot(path="screenshot.png")
    
    browser.close()

# 銷燬 Sandbox
sandbox.delete()

關鍵概念:

  • template_name: 控制枱創建的瀏覽器環境模板
  • cdp_url: 用於 Playwright/Puppeteer 連接
  • vnc_url: 用於實時查看瀏覽器畫面(可通過 sandbox.get_cdp_url() 獲取)

注意: 由於所有瀏覽器操作都在雲端進行,您無需在本地安裝瀏覽器。Playwright 僅用於通過 CDP 協議連接到雲端的瀏覽器實例。


如何創建 sandbox 模板

使用 Browser Sandbox 需要新建 Sandbox 模板,您需要訪問 Agentrun 控制枱網站,並按照如下步驟創建模板:

  1. 在頂部菜單欄選擇“運行時與沙箱”;
  2. 在左側邊欄選擇“Sandbox沙箱”;
  3. 點擊右上角“創建沙箱模板”;

  1. 選擇“瀏覽器”;
  2. 在彈出的抽屜對話框中填寫和選擇您的模板的規格、網絡等配置,並複製模板名稱;

6. 點擊“創建瀏覽器” 等待其就緒即可。

從零開始用 LangChain 創建 Browser Sandbox 智能體

本教程將指導您從零開始創建一個完整的 Browser Sandbox 智能體項目。

基於 LangChain 集成 Browser Sandbox

本教程將詳細講解如何使用 LangChain 創建 Browser Sandbox 相關的 tools 並集成到 Agent 中。

項目結構

為了保持代碼的內聚性和可維護性,我們將代碼拆分為以下模塊:

模塊職責劃分:

  • sandbox_manager.py: 負責 Sandbox 的創建、管理和銷燬,提供統一的接口
  • langchain_agent.py: 負責創建 LangChain tools 和 Agent,集成 VNC 信息
  • main.py: 作為入口文件,演示如何使用上述模塊
步驟 1: 創建項目並安裝依賴

首先創建項目目錄(如果還沒有):

mkdir -p langchain-demo
cd langchain-demo

創建 requirements.txt 文件,內容如下:

# LangChain 核心庫
langchain>=0.1.0
langchain-openai>=0.0.5
langchain-community>=0.0.20

# AgentRun SDK
agentrun-sdk[playwright,server]>=0.0.8

# 瀏覽器自動化
playwright>=1.40.0

# 環境變量管理
python-dotenv>=1.0.0

然後安裝依賴:

pip install -r requirements.txt

主要依賴説明:

  • langchain 和 langchain-openai: LangChain 核心庫
  • agentrun-sdk[playwright,server]: AgentRun SDK,用於 Sandbox 管理
  • playwright: 瀏覽器自動化庫 python-dotenv: 環境變量管理
步驟 2: 配置環境變量

在項目根目錄創建 .env 文件,配置以下環境變量:

# 阿里雲百鍊平台的 API Key,用於調用大模型能力
# 請前往 https://bailian.console.aliyun.com/?tab=app#/api-key 創建和查看
DASHSCOPE_API_KEY=sk-your-bailian-api-key

# 阿里雲賬號的訪問密鑰 ID 和訪問密鑰 Secret,用於 AgentRun SDK 鑑權
ALIBABA_CLOUD_ACCESS_KEY_ID=your-ak
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your-sk
ALIBABA_CLOUD_ACCOUNT_ID=your-main-account-id
ALIBABA_CLOUD_REGION=cn-hangzhou

# browser sandbox 模板的名稱,可以在 https://functionai.console.aliyun.com/cn-hangzhou/agent/runtime/sandbox 控制枱創建
BROWSER_TEMPLATE_NAME=sandbox-your-template-name

# agentrun 的控制面和數據面的 API 端點請求地址,默認cn-hangzhou
AGENTRUN_CONTROL_ENDPOINT=agentrun.cn-hangzhou.aliyuncs.com
AGENTRUN_DATA_ENDPOINT=https://${your-main-account-id}.agentrun-data.cn-hangzhou.aliyuncs.com
步驟 3: 創建 Sandbox 生命週期管理模塊

創建 sandbox_manager.py 文件,負責 Sandbox 的創建、管理和銷燬。核心代碼如下:

"""
Sandbox 生命週期管理模塊

負責 AgentRun Browser Sandbox 的創建、管理和銷燬。
提供統一的接口供 LangChain Agent 使用。
"""

import os
from typing import Optional, Dict, Any
from dotenv import load_dotenv

# 加載環境變量
load_dotenv()


class SandboxManager:
    """Sandbox 生命週期管理器"""
    
    def __init__(self):
        self._sandbox: Optional[Any] = None
        self._sandbox_id: Optional[str] = None
        self._cdp_url: Optional[str] = None
        self._vnc_url: Optional[str] = None
    
    def create(
        self,
        template_name: Optional[str] = None,
        idle_timeout: int = 3000
    ) -> Dict[str, Any]:
        """
        創建或獲取一個瀏覽器 sandbox 實例
        
        Args:
            template_name: Sandbox 模板名稱,如果為 None 則從環境變量讀取
            idle_timeout: 空閒超時時間(秒),默認 3000 秒
        
        Returns:
            dict: 包含 sandbox_id, cdp_url, vnc_url 的字典
        
        Raises:
            RuntimeError: 創建失敗時拋出異常
        """
        try:
            from agentrun.sandbox import Sandbox, TemplateType
            
            # 如果已有 sandbox,直接返回
            if self._sandbox is not None:
                return self.get_info()
            
            # 從環境變量獲取模板名稱
            if template_name is None:
                template_name = os.getenv(
                    "BROWSER_TEMPLATE_NAME",
                    "sandbox-browser-demo"
                )
            
            # 創建 sandbox
            self._sandbox = Sandbox.create(
                template_type=TemplateType.BROWSER,
                template_name=template_name,
                sandbox_idle_timeout_seconds=idle_timeout
            )
            
            self._sandbox_id = self._sandbox.sandbox_id
            self._cdp_url = self._get_cdp_url()
            self._vnc_url = self._get_vnc_url()
            
            return self.get_info()
        
        except ImportError as e:
            print(e)
            raise RuntimeError(
                "agentrun-sdk 未安裝,請運行: pip install agentrun-sdk[playwright,server]"
            )
        except Exception as e:
            raise RuntimeError(f"創建 Sandbox 失敗: {str(e)}")
    
    def get_info(self) -> Dict[str, Any]:
        """
        獲取當前 sandbox 的信息
        
        Returns:
            dict: 包含 sandbox_id, cdp_url, vnc_url 的字典
        
        Raises:
            RuntimeError: 如果沒有活動的 sandbox
        """
        if self._sandbox is None:
            raise RuntimeError("沒有活動的 sandbox,請先創建")
        
        return {
            "sandbox_id": self._sandbox_id,
            "cdp_url": self._cdp_url,
            "vnc_url": self._vnc_url,
        }
    
    def get_cdp_url(self) -> Optional[str]:
        """獲取 CDP URL"""
        return self._sandbox.get_cdp_url()
    
    def get_vnc_url(self) -> Optional[str]:
        """獲取 VNC URL"""
        return self._sandbox.get_vnc_url()
    
    def get_sandbox_id(self) -> Optional[str]:
        """獲取 Sandbox ID"""
        return self._sandbox_id
    
    def destroy(self) -> str:
        """
        銷燬當前的 sandbox 實例
        
        Returns:
            str: 操作結果描述
        """
        if self._sandbox is None:
            return "沒有活動的 sandbox"
        
        try:
            sandbox_id = self._sandbox_id
            
            # 嘗試銷燬 sandbox
            if hasattr(self._sandbox, 'delete'):
                self._sandbox.delete()
            elif hasattr(self._sandbox, 'stop'):
                self._sandbox.stop()
            elif hasattr(self._sandbox, 'destroy'):
                self._sandbox.destroy()
            
            # 清理狀態
            self._sandbox = None
            self._sandbox_id = None
            self._cdp_url = None
            self._vnc_url = None
            
            return f"Sandbox 已銷燬: {sandbox_id}"
        
        except Exception as e:
            # 即使銷燬失敗,也清理本地狀態
            self._sandbox = None
            self._sandbox_id = None
            self._cdp_url = None
            self._vnc_url = None
            return f"銷燬 Sandbox 時出錯: {str(e)}"
    
    def is_active(self) -> bool:
        """檢查 sandbox 是否活躍"""
        return self._sandbox is not None
    
    def __enter__(self):
        """上下文管理器入口"""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器退出,自動銷燬"""
        self.destroy()
        return False


# 全局單例(可選,用於簡單場景)
_global_manager: Optional[SandboxManager] = None


def get_global_manager() -> SandboxManager:
    """獲取全局 SandboxManager 單例"""
    global _global_manager
    if _global_manager is None:
        _global_manager = SandboxManager()
    return _global_manager


def reset_global_manager():
    """重置全局 SandboxManager"""
    global _global_manager
    if _global_manager:
        _global_manager.destroy()
    _global_manager = None

關鍵功能:

  1. 創建 Sandbox: 使用 AgentRun SDK 創建瀏覽器 Sandbox
  2. 獲取連接信息: 自動獲取 CDP URL 和 VNC URL,支持多種屬性名兼容
  3. 生命週期管理: 提供銷燬方法,確保資源正確釋放
步驟 4: 創建 LangChain Tools 和 Agent

創建 langchain_agent.py 文件,定義 LangChain tools 並創建 Agent。核心代碼如下:

"""
LangChain Agent 和 Tools 註冊模塊

負責創建 LangChain Agent,註冊 Sandbox 相關的 tools,並集成 VNC 可視化。

本模塊使用 sandbox_manager.py 中封裝的 SandboxManager 來管理 sandbox 生命週期。
"""

import os
from dotenv import load_dotenv
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from pydantic import BaseModel, Field

# 導入 sandbox 管理器
from sandbox_manager import SandboxManager

# 加載環境變量
load_dotenv()

# 全局 sandbox 管理器實例(單例模式)
_sandbox_manager: SandboxManager | None = None


def get_sandbox_manager() -> SandboxManager:
    """獲取 sandbox 管理器實例(單例模式)"""
    global _sandbox_manager
    if _sandbox_manager is None:
        _sandbox_manager = SandboxManager()
    return _sandbox_manager


# ============ LangChain Tools 定義 ============

@tool
def create_browser_sandbox(
    template_name: str = None,
    idle_timeout: int = 3000
) -> str:
    """創建或獲取一個瀏覽器 sandbox 實例。
    
    當需要訪問網頁、執行瀏覽器操作時,首先需要創建 sandbox。
    創建成功後,會返回 sandbox 信息,包括 VNC URL 用於可視化。
    
    Args:
        template_name: Sandbox 模板名稱,如果不提供則從環境變量 BROWSER_TEMPLATE_NAME 讀取
        idle_timeout: 空閒超時時間(秒),默認 3000 秒
    
    Returns:
        Sandbox 信息字符串,包括 ID、CDP URL、VNC URL
    """
    try:
        manager = get_sandbox_manager()
        # 如果 template_name 為空字符串,轉換為 None 以便從環境變量讀取
        if template_name == "":
            template_name = None
        info = manager.create(template_name=template_name, idle_timeout=idle_timeout)
        
        result = f"""✅ Sandbox 創建成功!

📋 Sandbox 信息:
- ID: {info['sandbox_id']}
- CDP URL: {info['cdp_url']}
"""
        
        vnc_url = info.get('vnc_url')
        if vnc_url:
            result += f"- VNC URL: {vnc_url}\n\n"
            result += "提示: VNC 查看器應該已自動打開,您可以在瀏覽器中實時查看瀏覽器操作。"
        else:
            result += "\n警告: 未獲取到 VNC URL,可能無法使用可視化功能。"
        
        return result
    
    except Exception as e:
        return f" 創建 Sandbox 失敗: {str(e)}"


@tool
def get_sandbox_info() -> str:
    """獲取當前 sandbox 的詳細信息,包括 ID、CDP URL、VNC URL 等。
    
    當需要查看當前 sandbox 狀態或獲取 VNC 連接信息時使用此工具。
    
    Returns:
        Sandbox 信息字符串
    """
    try:
        manager = get_sandbox_manager()
        info = manager.get_info()
        
        result = f"""📋 當前 Sandbox 信息:

- Sandbox ID: {info['sandbox_id']}
- CDP URL: {info['cdp_url']}
"""
        
        if info.get('vnc_url'):
            result += f"- VNC URL: {info['vnc_url']}\n\n"
            result += "您可以使用 VNC URL 在瀏覽器中實時查看操作過程。\n"
            result += "   推薦使用 vnc.html 文件或 noVNC 客户端。"
        
        return result
    
    except RuntimeError as e:
        return f" {str(e)}"
    except Exception as e:
        return f" 獲取 Sandbox 信息失敗: {str(e)}"


class NavigateInput(BaseModel):
    """瀏覽器導航輸入參數"""
    url: str = Field(description="要訪問的網頁 URL,必須以 http:// 或 https:// 開頭")
    wait_until: str = Field(
        default="load",
        description="等待頁面加載的狀態: load, domcontentloaded, networkidle"
    )
    timeout: int = Field(
        default=30000,
        description="超時時間(毫秒),默認 30000"
    )


@tool(args_schema=NavigateInput)
def navigate_to_url(url: str, wait_until: str = "load", timeout: int = 30000) -> str:
    """使用 sandbox 中的瀏覽器導航到指定 URL。
    
    當用户需要訪問網頁時使用此工具。導航後可以在 VNC 中實時查看頁面。
    
    Args:
        url: 要訪問的網頁 URL
        wait_until: 等待頁面加載的狀態(load/domcontentloaded/networkidle)
        timeout: 超時時間(毫秒)
    
    Returns:
        導航結果描述
    """
    try:
        manager = get_sandbox_manager()
        
        if not manager.is_active():
            return " 錯誤: 請先創建 sandbox"
        
        # 驗證 URL
        if not url.startswith(("http://", "https://")):
            return f" 錯誤: 無效的 URL 格式: {url}"
        
        cdp_url = manager.get_cdp_url()
        if not cdp_url:
            return " 錯誤: 無法獲取 CDP URL"
        
        # 使用 Playwright 連接瀏覽器並導航
        try:
            from playwright.sync_api import sync_playwright
            
            with sync_playwright() as p:
                browser = p.chromium.connect_over_cdp(cdp_url)
                pages = browser.contexts[0].pages if browser.contexts else []
                
                if pages:
                    page = pages[0]
                else:
                    page = browser.new_page()
                
                page.goto(url, wait_until=wait_until, timeout=timeout)
                title = page.title()
                
                return f"已成功導航到: {url}\n📄 頁面標題: {title}\n💡 您可以在 VNC 中查看頁面內容。"
        
        except ImportError:
            return f"導航指令已發送: {url}\n💡 提示: 安裝 playwright 以啓用實際導航功能 (pip install playwright)"
        except Exception as e:
            return f" 導航失敗: {str(e)}"
    
    except Exception as e:
        return f" 操作失敗: {str(e)}"


@tool("browser_screenshot", description="在瀏覽器 sandbox 中截取當前頁面截圖")
def take_screenshot(filename: str = "screenshot.png") -> str:
    """截取瀏覽器當前頁面的截圖。
    
    Args:
        filename: 截圖文件名,默認 "screenshot.png"
    
    Returns:
        操作結果
    """
    try:
        manager = get_sandbox_manager()
        
        if not manager.is_active():
            return " 錯誤: 請先創建 sandbox"
        
        cdp_url = manager.get_cdp_url()
        if not cdp_url:
            return " 錯誤: 無法獲取 CDP URL"
        
        try:
            from playwright.sync_api import sync_playwright
            
            with sync_playwright() as p:
                browser = p.chromium.connect_over_cdp(cdp_url)
                pages = browser.contexts[0].pages if browser.contexts else []
                
                if pages:
                    page = pages[0]
                else:
                    return " 錯誤: 沒有打開的頁面"
                
                page.screenshot(path=filename)
                return f"截圖已保存: {filename}"
        
        except ImportError:
            return " 錯誤: 需要安裝 playwright (pip install playwright)"
        except Exception as e:
            return f" 截圖失敗: {str(e)}"
    
    except Exception as e:
        return f" 操作失敗: {str(e)}"


@tool("destroy_sandbox", description="銷燬當前的 sandbox 實例,釋放資源。注意:僅在程序退出或明確需要釋放資源時使用,不要在一輪對話後銷燬。")
def destroy_sandbox() -> str:
    """銷燬當前的 sandbox 實例。
    
    重要提示:此工具應該僅在以下情況使用:
    - 程序即將退出
    - 明確需要釋放資源
    - 用户明確要求銷燬
    
    不要在一輪對話完成後就銷燬 sandbox,因為 sandbox 可以在多輪對話中複用。
    
    Returns:
        操作結果
    """
    try:
        manager = get_sandbox_manager()
        result = manager.destroy()
        return result
    except Exception as e:
        return f" 銷燬失敗: {str(e)}"


# ============ Agent 創建 ============

def create_browser_agent(system_prompt: str = None):
    """
    創建帶有 sandbox 工具的 LangChain Agent
    
    Args:
        system_prompt: 自定義系統提示詞,如果為 None 則使用默認提示詞
    
    Returns:
        LangChain Agent 實例
    """
    # 配置 DashScope API
    api_key = os.getenv("DASHSCOPE_API_KEY")
    if not api_key:
        raise ValueError("請設置環境變量 DASHSCOPE_API_KEY")
    
    base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
    model_name = os.getenv("QWEN_MODEL", "qwen-plus")
    
    # 創建 LLM
    model = ChatOpenAI(
        model=model_name,
        api_key=api_key,
        base_url=base_url,
        temperature=0.7,
    )
    
    # 創建工具列表
    tools = [
        create_browser_sandbox,
        get_sandbox_info,
        navigate_to_url,
        take_screenshot,
        destroy_sandbox,
    ]
    
    # 默認系統提示詞
    if system_prompt is None:
        system_prompt = """你是一個瀏覽器自動化助手,可以使用 sandbox 來訪問和操作網頁。

當用户需要訪問網頁時,請按以下步驟操作:
1. 首先創建或獲取 sandbox(如果還沒有)
2. 使用 navigate_to_url 導航到目標網頁
3. 執行用户請求的操作
4. 如果需要,可以截取截圖

重要提示:
- 創建 sandbox 後,會返回 VNC URL,用户可以使用它實時查看瀏覽器操作
- 所有操作都會在 VNC 中實時顯示,方便調試和監控
- sandbox 可以在多輪對話中複用,不要在一輪對話完成後就銷燬
- 只有在用户明確要求銷燬時才使用 destroy_sandbox 工具
- 不要主動建議用户銷燬 sandbox,除非用户明確要求
- 請始終用中文回覆,確保操作準確、高效。"""
    
    # 創建 Agent
    agent = create_agent(
        model=model,
        tools=tools,
        system_prompt=system_prompt,
    )
    
    return agent


def get_available_tools():
    """獲取所有可用的工具列表"""
    return [
        create_browser_sandbox,
        get_sandbox_info,
        navigate_to_url,
        take_screenshot,
        destroy_sandbox,
    ]

關鍵要點:

  1. Tool 定義: 使用 @tool 裝飾器定義 LangChain tools
  2. 類型提示: 所有參數必須有類型提示,用於生成工具 schema
  3. 文檔字符串: 詳細的文檔字符串幫助 LLM 理解何時使用工具
  4. 單例模式: 使用全局管理器實例確保 Sandbox 在會話中複用
步驟 5: 創建主入口文件

創建 main.py 文件,作為程序入口。核心代碼如下:

"""
LangChain + AgentRun Browser Sandbox 集成示例

主入口文件,演示如何使用 LangChain Agent 與 AgentRun Browser Sandbox 集成。
"""

import os
import sys
import signal
import webbrowser
import urllib.parse
import threading
import http.server
import socketserver
from pathlib import Path
from dotenv import load_dotenv
from langchain_agent import create_browser_agent, get_sandbox_manager

# 加載環境變量
load_dotenv()

# 全局 HTTP 服務器實例
_http_server = None
_http_port = 8080

# 全局清理標誌,用於防止重複清理
_cleanup_done = False


def start_http_server():
    """啓動一個簡單的 HTTP 服務器來提供 vnc.html"""
    global _http_server
    
    if _http_server is not None:
        return _http_port
    
    try:
        current_dir = Path(__file__).parent.absolute()
        
        class VNCRequestHandler(http.server.SimpleHTTPRequestHandler):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, directory=str(current_dir), **kwargs)
            
            def log_message(self, format, *args):
                # 靜默日誌,避免輸出過多信息
                pass
        
        # 嘗試啓動服務器
        for port in range(_http_port, _http_port + 10):
            try:
                server = socketserver.TCPServer(("", port), VNCRequestHandler)
                server.allow_reuse_address = True
                
                # 在後台線程中運行服務器
                def run_server():
                    server.serve_forever()
                
                thread = threading.Thread(target=run_server, daemon=True)
                thread.start()
                
                _http_server = server
                return port
            except OSError:
                continue
        
        return None
    except Exception as e:
        print(f"啓動 HTTP 服務器失敗: {str(e)}")
        return None


def open_vnc_viewer(vnc_url: str):
    """
    自動打開 VNC 查看器並設置 VNC URL
    
    Args:
        vnc_url: VNC WebSocket URL
    """
    if not vnc_url:
        return
    
    try:
        # 獲取當前文件所在目錄
        current_dir = Path(__file__).parent.absolute()
        vnc_html_path = current_dir / "vnc.html"
        
        # 檢查文件是否存在
        if not vnc_html_path.exists():
            print(f"警告: vnc.html 文件不存在: {vnc_html_path}")
            print_vnc_info(vnc_url)
            return
        
        # 啓動 HTTP 服務器
        port = start_http_server()
        
        if port:
            # 編碼 VNC URL 作為 URL 參數
            encoded_url = urllib.parse.quote(vnc_url, safe='')
            
            # 構建 HTTP URL
            http_url = f"http://localhost:{port}/vnc.html?url={encoded_url}"
            
            # 打開瀏覽器
            print(f"\n正在打開 VNC 查看器...")
            print(f"HTTP 服務器運行在: http://localhost:{port}")
            print(f"VNC URL: {vnc_url[:80]}...")
            print(f"完整 URL: {http_url[:100]}...")
            webbrowser.open(http_url)
            print(f"VNC 查看器已打開")
            print(f"VNC URL 已通過 URL 參數自動設置,頁面加載後會自動連接")
        else:
            # 如果 HTTP 服務器啓動失敗,嘗試使用 file:// 協議
            print(f"HTTP 服務器啓動失敗,嘗試使用文件協議...")
            encoded_url = urllib.parse.quote(vnc_url, safe='')
            file_url = f"file://{vnc_html_path}?url={encoded_url}"
            webbrowser.open(file_url)
            print(f"VNC 查看器已打開(使用文件協議)")
            print(f"提示: 如果無法自動連接,請手動複製 VNC URL 到輸入框")
        
    except Exception as e:
        print(f"自動打開 VNC 查看器失敗: {str(e)}")
        print_vnc_info(vnc_url)


def print_vnc_info(vnc_url: str):
    """打印 VNC 連接信息"""
    if not vnc_url:
        return
    
    print("\n" + "=" * 60)
    print("VNC 可視化連接信息")
    print("=" * 60)
    print(f"\nVNC URL: {vnc_url}")
    print("\n使用方式:")
    print("   1. 使用 noVNC 客户端連接")
    print("   2. 或在瀏覽器中訪問 VNC 查看器頁面")
    print("   3. 實時查看瀏覽器操作過程")
    print("\n" + "=" * 60 + "\n")


def cleanup_sandbox():
    """
    清理 sandbox 資源
    
    這個函數可以被信號處理器、異常處理器和正常退出流程調用
    """
    global _cleanup_done
    
    # 防止重複清理
    if _cleanup_done:
        return
    
    _cleanup_done = True
    
    try:
        manager = get_sandbox_manager()
        if manager.is_active():
            print("\n" + "=" * 60)
            print("正在清理 sandbox...")
            print("=" * 60)
            result = manager.destroy()
            print(f"清理結果: {result}\n")
        else:
            print("\n沒有活動的 sandbox 需要清理\n")
    except Exception as e:
        print(f"\n清理 sandbox 時出錯: {str(e)}\n")


def signal_handler(signum, frame):
    """
    信號處理器,處理 Ctrl+C (SIGINT) 和其他信號
    
    Args:
        signum: 信號編號
        frame: 當前堆棧幀
    """
    print("\n\n收到中斷信號,正在清理資源...")
    cleanup_sandbox()
    print("清理完成")
    sys.exit(0)


def main():
    """主函數"""
    global _cleanup_done
    
    # 重置清理標誌
    _cleanup_done = False
    
    # 註冊信號處理器,處理 Ctrl+C (SIGINT)
    signal.signal(signal.SIGINT, signal_handler)
    
    # 在 Windows 上,SIGBREAK 也可以處理
    if hasattr(signal, 'SIGBREAK'):
        signal.signal(signal.SIGBREAK, signal_handler)
    
    print("=" * 60)
    print("LangChain + AgentRun Browser Sandbox 集成示例")
    print("=" * 60)
    print()
    
    try:
        # 創建 Agent
        print("正在初始化 LangChain Agent...")
        agent = create_browser_agent()
        print("Agent 初始化完成\n")
        
        # 示例查詢
        queries = [
            "創建一個瀏覽器 sandbox",
            "獲取當前 sandbox 的信息,包括 VNC URL",
            "導航到 https://www.aliyun.com",
            "截取當前頁面截圖",
        ]
        
        # 執行查詢
        for i, query in enumerate(queries, 1):
            print(f"\n{'=' * 60}")
            print(f"查詢 {i}: {query}")
            print(f"{'=' * 60}\n")
            
            try:
                result = agent.invoke({
                    "messages": [{"role": "user", "content": query}]
                })
                
                # 提取最後一條消息的內容
                output = result.get("messages", [])[-1].content if isinstance(result.get("messages"), list) else result.get("output", str(result))
                print(f"\n結果:\n{output}\n")
                
                # 如果是創建 sandbox,自動打開 VNC 查看器
                if i == 1:
                    try:
                        # 等待一下確保 sandbox 完全創建
                        import time
                        time.sleep(1)
                        
                        manager = get_sandbox_manager()
                        if manager.is_active():
                            info = manager.get_info()
                            vnc_url = info.get('vnc_url')
                            if vnc_url:
                                print(f"\n檢測到 VNC URL: {vnc_url[:80]}...")
                                open_vnc_viewer(vnc_url)
                                print_vnc_info(vnc_url)
                            else:
                                print("\n警告: 未獲取到 VNC URL,請檢查 sandbox 創建是否成功")
                    except Exception as e:
                        print(f"打開 VNC 查看器時出錯: {str(e)}")
                        import traceback
                        traceback.print_exc()
                
                # 如果是獲取信息,顯示 VNC 信息
                elif i == 2:
                    try:
                        manager = get_sandbox_manager()
                        if manager.is_active():
                            info = manager.get_info()
                            if info.get('vnc_url'):
                                print_vnc_info(info['vnc_url'])
                    except:
                        pass
            
            except Exception as e:
                print(f"查詢失敗: {str(e)}\n")
                import traceback
                traceback.print_exc()
        
        # 交互式查詢
        print("\n" + "=" * 60)
        print("進入交互模式(輸入 'quit' 或 'exit' 退出,Ctrl+C 或 Ctrl+D 中斷)")
        print("=" * 60 + "\n")
        
        while True:
            try:
                user_input = input("請輸入您的查詢: ").strip()
            except EOFError:
                # 處理 Ctrl+D (EOF)
                print("\n\n檢測到輸入結束 (Ctrl+D),正在清理資源...")
                cleanup_sandbox()
                print("清理完成")
                break
            except KeyboardInterrupt:
                # 處理 Ctrl+C (在 input 調用期間)
                print("\n\n檢測到中斷信號 (Ctrl+C),正在清理資源...")
                cleanup_sandbox()
                print("清理完成")
                break
            
            if not user_input:
                continue
            
            if user_input.lower() in ['quit', 'exit', '退出']:
                print("\nBye")
                # 退出前清理 sandbox
                cleanup_sandbox()
                break
            
            try:
                result = agent.invoke({
                    "messages": [{"role": "user", "content": user_input}]
                })
                
                output = result.get("messages", [])[-1].content if isinstance(result.get("messages"), list) else result.get("output", str(result))
                print(f"\n結果:\n{output}\n")
                
                # 檢查是否需要打開或顯示 VNC 信息
                user_input_lower = user_input.lower()
                if "創建" in user_input_lower and "sandbox" in user_input_lower:
                    # 如果是創建 sandbox,自動打開 VNC 查看器
                    try:
                        # 等待一下確保 sandbox 完全創建
                        import time
                        time.sleep(1)
                        
                        manager = get_sandbox_manager()
                        if manager.is_active():
                            info = manager.get_info()
                            vnc_url = info.get('vnc_url')
                            if vnc_url:
                                print(f"\n檢測到 VNC URL: {vnc_url[:80]}...")
                                open_vnc_viewer(vnc_url)
                                print_vnc_info(vnc_url)
                            else:
                                print("\n警告: 未獲取到 VNC URL,請檢查 sandbox 創建是否成功")
                    except Exception as e:
                        print(f"打開 VNC 查看器時出錯: {str(e)}")
                        import traceback
                        traceback.print_exc()
                elif "sandbox" in user_input_lower or "vnc" in user_input_lower:
                    # 其他情況只顯示信息
                    try:
                        manager = get_sandbox_manager()
                        if manager.is_active():
                            info = manager.get_info()
                            if info.get('vnc_url'):
                                print_vnc_info(info['vnc_url'])
                    except:
                        pass
            
            except Exception as e:
                print(f"查詢失敗: {str(e)}\n")
                import traceback
                traceback.print_exc()
        
        # 清理資源(僅在程序正常退出時)
        cleanup_sandbox()
    
    except KeyboardInterrupt:
        # 處理頂層 KeyboardInterrupt (Ctrl+C)
        print("\n\n檢測到中斷信號 (Ctrl+C),正在清理資源...")
        cleanup_sandbox()
        print("清理完成")
        sys.exit(0)
    except EOFError:
        # 處理頂層 EOFError (Ctrl+D)
        print("\n\n檢測到輸入結束 (Ctrl+D),正在清理資源...")
        cleanup_sandbox()
        print("清理完成")
        sys.exit(0)
    except ValueError as e:
        print(f"配置錯誤: {str(e)}")
        print("\n提示: 請確保已設置以下環境變量:")
        print("   - DASHSCOPE_API_KEY: DashScope API Key")
        print("   - ALIBABA_CLOUD_ACCOUNT_ID: 阿里雲賬號 ID")
        print("   - ALIBABA_CLOUD_ACCESS_KEY_ID: 訪問密鑰 ID")
        print("   - ALIBABA_CLOUD_ACCESS_KEY_SECRET: 訪問密鑰 Secret")
        print("   - ALIBABA_CLOUD_REGION: 區域(默認: cn-hangzhou)")
    except Exception as e:
        print(f"發生錯誤: {str(e)}")
        import traceback
        traceback.print_exc()
        # 發生錯誤時也嘗試清理
        cleanup_sandbox()


if __name__ == "__main__":
    main()

關鍵功能:

  1. VNC 自動打開: 創建 Sandbox 後自動打開 VNC 查看器
  2. 信號處理: 捕獲 Ctrl+C,確保資源正確清理
  3. 交互模式: 支持持續對話,複用 Sandbox 實例
VNC 可視化集成

VNC(Virtual Network Computing)功能允許您實時查看和監控瀏覽器在 Sandbox 中的操作過程,這對於調試和監控 Agent 行為非常有用。

獲取 VNC URL:

創建 Sandbox 後,可以通過 get_sandbox_info tool 獲取 VNC URL:

# 通過 Agent 調用
result = agent.invoke({
    "messages": [{"role": "user", "content": "獲取 sandbox 信息"}]
})

# 或直接通過管理器獲取
manager = get_sandbox_manager()
info = manager.get_info()
vnc_url = info['vnc_url']

自動打開 VNC 查看器:

main.py 中,我們實現了自動打開 VNC 查看器的功能:

import webbrowser
import urllib.parse
from pathlib import Path

def open_vnc_viewer(vnc_url: str):
    """自動打開 VNC 查看器"""
    current_dir = Path(__file__).parent.absolute()
    vnc_html_path = current_dir / "vnc.html"
    
    if vnc_html_path.exists():
        # 通過 URL 參數傳遞 VNC URL
        encoded_url = urllib.parse.quote(vnc_url, safe='')
        file_url = f"file://{vnc_html_path}?url={encoded_url}"
        webbrowser.open(file_url)

VNC HTML 頁面:

vnc.html 頁面會從 URL 參數中讀取 VNC URL,並自動連接到 VNC 服務器。頁面包含以下核心功能:

  1. noVNC 庫加載: 從 CDN 動態加載 noVNC 客户端庫
  2. 自動連接: 讀取 URL 參數中的 VNC URL 並自動連接
  3. 狀態顯示: 顯示連接狀態(連接中、已連接、已斷開)
  4. 手動控制: 支持手動輸入 VNC URL、斷開重連等操作

核心 JavaScript 代碼片段:

// 從 URL 參數獲取 VNC URL
const urlParams = new URLSearchParams(window.location.search);
const vncUrl = urlParams.get('url');

// 加載 noVNC 庫
async function loadNoVNC() {
    const module = await import('https://cdn.jsdelivr.net/gh/novnc/noVNC@v1.4.0/core/rfb.js');
    return module.default;
}

// 連接 VNC
async function connectVNC(url) {
    const RFB = await loadNoVNC();
    rfb = new RFB(vncScreen, url, {
        shared: true,
        credentials: { password: '' }
    });
    
    rfb.addEventListener('connect', () => {
        console.log('VNC 連接成功');
    });
}

完整的 vnc.html 文件可以在示例代碼倉庫中獲取。

手動使用 VNC 查看器:

如果自動打開失敗,您也可以手動使用 VNC 查看器:

  1. 使用 noVNC 在線客户端:
    • 訪問 noVNC 在線客户端
    • 在連接設置中填入 VNC URL
    • 點擊連接
  2. 使用本地 VNC HTML 頁面:
    • 打開 vnc.html
    • 輸入 VNC URL
    • 點擊連接按鈕

實時監控功能:

  • 所有瀏覽器操作都會在 VNC 中實時顯示
  • 可以看到 Agent 的每一步操作(導航、點擊、輸入等)
  • 方便調試和監控 Agent 行為
  • 支持交互式操作(在 VNC 中直接操作瀏覽器)
運行和測試
python main.py

程序會自動:

  1. 創建 Browser Sandbox
  2. 打開 VNC 查看器(實時查看瀏覽器操作)
  3. 執行預設查詢
  4. 進入交互模式

工作原理

為了更好地理解系統架構,我們將工作流程拆分為兩個部分:LangChain Agent 工作流程SandboxManager 生命週期管理

1. LangChain Agent 工作流程

下圖展示了 LangChain Agent 如何處理用户請求並調用相應的 Tools:

flowchart TB
    Start([用户發起請求<br/>例: 訪問網頁並截圖]) --> Agent[LangChain Agent<br/>分析用户意圖]
    
    Agent --> SelectTool{選擇合適的 Tool}
    
    SelectTool -->|首次使用| Tool1[create_browser_sandbox]
    SelectTool -->|導航網頁| Tool2[navigate_to_url]
    SelectTool -->|截取屏幕| Tool3[take_screenshot]
    SelectTool -->|查詢狀態| Tool4[get_sandbox_info]
    SelectTool -->|清理資源| Tool5[destroy_sandbox]
    
    Tool1 --> CallManager1[調用 SandboxManager]
    Tool2 --> CallManager2[調用 SandboxManager]
    Tool3 --> CallManager3[調用 SandboxManager]
    Tool4 --> CallManager4[調用 SandboxManager]
    Tool5 --> CallManager5[調用 SandboxManager]
    
    CallManager1 --> Manager[SandboxManager<br/>單例實例]
    CallManager2 --> Manager
    CallManager3 --> Manager
    CallManager4 --> Manager
    CallManager5 --> Manager
    
    Manager --> ToolResult[Tool 返回結果]
    ToolResult --> AgentProcess[Agent 處理結果<br/>生成響應]
    AgentProcess --> Response([返回用户友好的響應])
    
    Response -.多輪對話.-> Start
    
    style Agent fill:#667eea,color:#fff
    style Manager fill:#764ba2,color:#fff
    style Tool1 fill:#4ecdc4,color:#fff
    style Tool2 fill:#4ecdc4,color:#fff
    style Tool3 fill:#4ecdc4,color:#fff
    style Tool4 fill:#4ecdc4,color:#fff
    style Tool5 fill:#4ecdc4,color:#fff

Agent 工作流程説明:

  1. 請求接收:用户發起自然語言請求(如"訪問淘寶首頁並截圖")
  2. 意圖分析:Agent 分析用户意圖,決定需要調用哪些 Tools
  3. Tool 調用:根據任務需求,順序或組合調用多個 Tools
  4. Manager 交互:所有 Tools 都通過 SandboxManager 單例實例操作 Sandbox
  5. 結果處理:Agent 將 Tool 返回的結果整合成用户友好的響應
  6. 多輪對話:Sandbox 在整個會話中保持活躍,支持多輪對話

5 個核心 Tools 的職責:

Tool 功能 使用場景
create_browser_sandbox 創建 Sandbox 實例 首次使用或 Sandbox 已銷燬時
navigate_to_url 導航到指定 URL 需要訪問網頁時
take_screenshot 截取當前頁面 需要保存頁面快照時
get_sandbox_info 獲取 Sandbox 信息 查看狀態或獲取 VNC URL 時
destroy_sandbox 銷燬 Sandbox 實例 任務完成或需要釋放資源時
2. SandboxManager 生命週期管理

下圖展示了 SandboxManager 如何管理 Sandbox 的完整生命週期:

SandboxManager 工作流程説明:

  1. 單例管理
    • 首次調用時創建 Manager 實例
    • 後續調用複用同一個實例
    • 確保整個會話只有一個 Sandbox
  2. Sandbox 創建
    • 調用 AgentRun SDK 的 Sandbox.create()
    • SDK 通過阿里雲 API 與函數計算 FC 通信
    • FC 服務創建獨立的容器實例,包含:
      • Chromium 瀏覽器VNC 服務必要的運行環境
  3. 連接信息獲取
    • CDP URL:WebSocket 地址,用於 Playwright/Puppeteer 遠程控制瀏覽器
    • VNC URL:WebSocket 地址,用於實時查看瀏覽器畫面
  4. 瀏覽器操作
    • Playwright 通過 CDP URL 連接到遠程瀏覽器
    • 執行各種瀏覽器操作(導航、點擊、截圖等)
    • VNC 同步顯示操作過程,用户可實時監控
  5. 資源清理
    • 調用 destroy() 方法銷燬 Sandbox
    • 清理 Manager 內部狀態
    • 通過 SDK 釋放雲端資源
3. Agent 與 Manager 的協作關係

交互模式:

用户請求 → Agent → Tool → SandboxManager → AgentRun SDK → 雲端 Sandbox
                                    ↓
用户響應 ← Agent ← Tool ← SandboxManager ← 操作結果

關鍵設計理念:

  1. 分層架構
    • 用户層:自然語言交互
    • Agent 層:意圖理解和任務分解
    • Tool 層:功能封裝和參數驗證
    • Manager 層:資源管理和狀態維護
    • SDK 層:雲服務通信
    • 雲端層:實際的 Sandbox 環境
  2. 單例模式
    • SandboxManager 使用單例模式
    • 保證整個會話中只有一個 Sandbox 實例
    • 避免資源浪費和狀態衝突
  3. 狀態複用
    • Sandbox 在多輪對話中保持活躍
    • 減少創建和銷燬的開銷
    • 提供更流暢的用户體驗
  4. 雙通道設計
    • CDP 通道:Agent 通過 Playwright 控制瀏覽器
    • VNC 通道:用户通過 VNC 查看器實時監控
  5. 解耦設計
    • Tools 不直接操作 SDK,通過 Manager 統一管理
    • 便於擴展和維護
    • 統一的錯誤處理和資源管理

典型使用場景示例:

# 第 1 輪對話
用户: "創建一個 sandbox 並訪問淘寶首頁"
→ Agent 調用: create_browser_sandbox → navigate_to_url
→ Manager: 創建 Sandbox → Playwright 導航
→ 結果: "Sandbox 已創建,已訪問淘寶首頁"

# 第 2 輪對話(複用 Sandbox)
用户: "截取當前頁面"
→ Agent 調用: take_screenshot
→ Manager: 使用現有 Sandbox → Playwright 截圖
→ 結果: "截圖已保存"

# 第 3 輪對話(複用 Sandbox)
用户: "訪問京東首頁"
→ Agent 調用: navigate_to_url
→ Manager: 使用現有 Sandbox → Playwright 導航
→ 結果: "已訪問京東首頁"

通過這種設計,Agent 專注於理解用户意圖和任務編排,而 Manager 專注於 Sandbox 的生命週期管理,實現了清晰的職責分離。

工作原理總結:

  1. 工具註冊: 使用 @tool 裝飾器將 Sandbox 功能封裝為 LangChain tools
  2. 生命週期管理: SandboxManager 負責 Sandbox 的創建、管理和銷燬
  3. 狀態保持: 使用單例模式管理 Sandbox 實例,確保同一會話內複用
  4. VNC 集成: 自動獲取並返回 VNC URL,方便用户實時查看
  5. 錯誤處理: 所有工具都包含完善的錯誤處理機制

擴展和定製

添加自定義 Tools:

@tool
def extract_table_data(url: str) -> str:
    """從網頁中提取表格數據"""
    from playwright.sync_api import sync_playwright
    
    manager = get_sandbox_manager()
    cdp_url = manager.get_info()['cdp_url']
    
    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp(cdp_url)
        page = browser.contexts[0].pages[0]
        page.goto(url)
        tables = page.query_selector_all("table")
        return f"找到 {len(tables)} 個表格"

自定義提示詞:

custom_prompt = """你是一個專業的網頁數據提取助手。
在執行任務前,請先創建 sandbox,然後使用瀏覽器工具完成任務。"""

agent = create_browser_agent(system_prompt=custom_prompt)

最佳實踐

  1. 模塊化設計: 將 Sandbox 管理和 Agent 創建分離,提高代碼可維護性
  2. 錯誤處理: 所有工具都應包含完善的錯誤處理
  3. 資源清理: 使用信號處理器確保資源正確清理
  4. VNC 提示: 在工具返回中包含 VNC URL,方便用户使用
  5. 單例模式: 確保 Sandbox 實例在會話中複用,避免重複創建

前端集成可視化監控(VNC)

VNC 集成架構

下圖展示了前端如何集成 VNC 實現實時監控:

輕量級 HTML 頁面集成

創建一個簡單的 vnc-viewer.html 文件:

<!DOCTYPE html>
<html>
<head>
    <title>Browser Sandbox VNC 查看器</title>
    <style>
        body { margin: 0; padding: 0; background: #000; }
        #vnc-container { width: 100vw; height: 100vh; }
    </style>
</head>
<body>
    <div id="vnc-container"></div>
    
    <script type="module">
        const params = new URLSearchParams(window.location.search);
        const vncUrl = params.get('url');
        
        if (!vncUrl) {
            alert('請提供 VNC URL 參數');
        } else {
            const module = await import('https://cdn.jsdelivr.net/gh/novnc/noVNC@v1.4.0/core/rfb.js');
            const RFB = module.default;
            
            const rfb = new RFB(
                document.getElementById('vnc-container'),
                vncUrl,
                { shared: true, credentials: { password: '' } }
            );
            
            rfb.scaleViewport = true;
        }
    </script>
</body>
</html>

使用方式:

import webbrowser
import urllib.parse

vnc_url = sandbox.vnc_url
encoded_url = urllib.parse.quote(vnc_url, safe='')
viewer_url = f"file:///path/to/vnc-viewer.html?url={encoded_url}"
webbrowser.open(viewer_url)

React 應用集成

核心組件代碼

import React, { useEffect, useRef } from 'react';

interface VNCViewerProps {
  vncUrl: string;
  onConnect?: () => void;
  onDisconnect?: () => void;
}

export const VNCViewer: React.FC<VNCViewerProps> = ({ 
  vncUrl, 
  onConnect, 
  onDisconnect 
}) => {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    let rfb: any;

    const initVNC = async () => {
      if (!containerRef.current || !vncUrl) return;

      const { default: RFB } = await import('@novnc/novnc/core/rfb');

      rfb = new RFB(containerRef.current, vncUrl, {
        shared: true,
        credentials: { password: '' }
      });

      rfb.scaleViewport = true;

      rfb.addEventListener('connect', () => onConnect?.());
      rfb.addEventListener('disconnect', () => onDisconnect?.());
    };

    initVNC();

    return () => {
      if (rfb) rfb.disconnect();
    };
  }, [vncUrl, onConnect, onDisconnect]);

  return (
    <div 
      ref={containerRef} 
      style={{ width: '100%', height: '600px', background: '#000' }} 
    />
  );
};

使用示例:

import React, { useState, useEffect } from 'react';
import { VNCViewer } from './VNCViewer';

function App() {
  const [vncUrl, setVncUrl] = useState<string>('');

  useEffect(() => {
    fetch('/api/sandbox/create', { method: 'POST' })
      .then(res => res.json())
      .then(data => setVncUrl(data.vnc_url));
  }, []);

  return (
    <div>
      Browser Sandbox 實時監控
      {vncUrl ? (
        <VNCViewer 
          vncUrl={vncUrl}
          onConnect={() => console.log('已連接')}
          onDisconnect={() => console.log('已斷開')}
        />
      ) : (
        <p>正在初始化...</p>
      )}
    </div>
  );
}

完整示例代碼:包含完整前端集成示例和後端 API 的代碼請訪問 GitHub 倉庫。

Puppeteer 和 Playwright 直接集成

如果您更熟悉傳統的瀏覽器自動化庫,也可以直接使用 Puppeteer 或 Playwright 連接到 Browser Sandbox。

使用 Playwright

from playwright.sync_api import sync_playwright
from agentrun.sandbox import Sandbox, TemplateType

# 創建 Sandbox
sandbox = Sandbox.create(
    template_type=TemplateType.BROWSER,
    template_name="your-template-name",
    sandbox_idle_timeout_seconds=3000
)

# 使用 Playwright 連接
with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(sandbox.cdp_url)
    page = browser.contexts[0].pages[0]
    
    # 執行操作
    page.goto("https://www.example.com")
    page.screenshot(path="screenshot.png")
    content = page.content()
    
    browser.close()

# 清理
sandbox.delete()

使用 Puppeteer(Node.js)

const puppeteer = require('puppeteer-core');

// CDP URL 從 Sandbox 獲取
const cdpUrl = 'wss://your-account.funagent-data-pre.cn-hangzhou.aliyuncs.com/sandboxes/xxx/ws/automation';

(async () => {
  const browser = await puppeteer.connect({
    browserWSEndpoint: cdpUrl,
    defaultViewport: null
  });

  const page = (await browser.pages())[0];

  await page.goto('https://www.example.com');
  await page.screenshot({ path: 'screenshot.png' });

  await browser.close();
})();

總結

通過本教程,您已經學會了:

  1. AgentRun SDK 基礎: 如何使用 SDK 創建和管理 Browser Sandbox
  2. LangChain 集成: 如何將 Sandbox 封裝為 LangChain Tools
  3. VNC 可視化: 如何在前端集成 VNC 實現實時監控
  4. 直接集成: 如何使用 Puppeteer/Playwright 直接連接 Sandbox

快速瞭解函數計算 AgentRun:

​一句話介紹:​函數計算 AgentRun 是一個以高代碼為核心的一站式 Agentic AI 基礎設施平台。秉持生態開放和靈活組裝的理念,為企業級 Agent 應用提供從開發、部署到運維的全生命週期管理。

函數計算 AgentRun 架構圖

AgentRun 運行時基於阿里雲函數計算 FC 構建,繼承了 Serverless 計算極致彈性、按量付費、零運維的核心優勢。通過深度集成 AgentScope、Langchain、RAGFlow、Mem0 等主流開源生態。AgentRun 將 Serverless 的極致彈性、零運維和按量付費的特性與 AI 原生應用場景深度融合,助力企業實現成本與效率的極致優化,**平均 TCO 降低 60%**。

​讓​開發者只需專注於 Agent 的業務邏輯創新,無需關心底層基礎設施,讓 Agentic AI 真正進入企業生產環境。

歡迎加入“函數計算 AgentRun 客户羣”與我們交流,釘釘羣號:134570017218。