<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jambo</title>
    <description>The latest articles on DEV Community by Jambo (@jambochen).</description>
    <link>https://dev.to/jambochen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1051989%2Fdaaf1fa4-eefb-4cc8-8b20-416f85ff3c31.jpeg</url>
      <title>DEV Community: Jambo</title>
      <link>https://dev.to/jambochen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jambochen"/>
    <language>en</language>
    <item>
      <title>在 Jetson 運行 Live VLM WebUI</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Tue, 30 Dec 2025 11:42:44 +0000</pubDate>
      <link>https://dev.to/jambochen/zai-jetson-yun-xing-live-vlm-webui-3f5a</link>
      <guid>https://dev.to/jambochen/zai-jetson-yun-xing-live-vlm-webui-3f5a</guid>
      <description>&lt;h1&gt;
  
  
  Live VLM WebUI
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8myzywzn75u6ed9twm00.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8myzywzn75u6ed9twm00.jpg" alt="Live VLM WebUI" width="800" height="719"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/NVIDIA-AI-IOT/live-vlm-webui" rel="noopener noreferrer"&gt;Live VLM WebUI&lt;/a&gt; 是一個方便的介面,用於即時評估視覺語言模型:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎥 &lt;strong&gt;多來源視訊輸入&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;WebRTC 網路攝影機串流(穩定)&lt;/li&gt;
&lt;li&gt;🧪 RTSP IP 攝影機支援(測試版)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;🔌 &lt;strong&gt;OpenAI 相容 API&lt;/strong&gt; - 與 Ollama、vLLM、NIM 或任何視覺 API 相容

&lt;ul&gt;
&lt;li&gt;🔧 靈活部署 - VLM 後端:本地推理或雲端 API&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✍️ &lt;strong&gt;互動式提示編輯器&lt;/strong&gt; - 8 個預設提示 + 自訂提示&lt;/li&gt;

&lt;li&gt;⚡ &lt;strong&gt;非同步處理&lt;/strong&gt; - 影片流暢播放,同時 VLM 在背景處理影格&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  先決條件
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;支援的設備:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jetson AGX Thor 開發者套件&lt;/li&gt;
&lt;li&gt;Jetson AGX Orin (64GB)&lt;/li&gt;
&lt;li&gt;Jetson AGX Orin (32GB)&lt;/li&gt;
&lt;li&gt;Jetson Orin Nano (8GB)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;JetPack 版本:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JetPack 6 (L4T r36.x)&lt;/li&gt;
&lt;li&gt;JetPack 7 (L4T r38.x)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;儲存空間:&lt;/strong&gt; &lt;strong&gt;強烈建議&lt;/strong&gt;使用 NVMe SSD&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;live-vlm-webui&lt;/code&gt; 容器約需 &lt;code&gt;4 GB&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;在 Ollama 上拉取(下載)模型所需的空間(如果在本地運行)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  概述
&lt;/h2&gt;

&lt;p&gt;視覺語言模型現在可以在適合邊緣部署的大小中使用,從 4b 到 90b+ 參數。開放權重模型包括 Qwen 2.5/3 VL、Gemma 3、Llama 3.2/4 Vision、Phi-3.5-vision,以及 NVIDIA 的 Cosmos-Reason1 和 Nemotron Nano VL。&lt;/p&gt;

&lt;h3&gt;
  
  
  可用的開放權重 VLM 模型
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;創建者&lt;/th&gt;
&lt;th&gt;模型名稱&lt;/th&gt;
&lt;th&gt;大小&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alibaba&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Qwen 2.5 VL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3b, 7b, 32b, 72b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alibaba&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Qwen 3 VL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2b, 4b, 8b, 30b, 32b, 235b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Gemma 3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4b, 12b, 27b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Llama 3.2-Vision&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;11b, 90b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Llama 4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;16x17b, 128x17b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Phi-3.5-vision&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.2b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVIDIA&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cosmos-Reason1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVIDIA&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Nemotron Nano 12B V2 VL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12b&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;即時測試 VLM 存在特定挑戰:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open WebUI 等網頁介面需要手動上傳圖片(無串流)&lt;/li&gt;
&lt;li&gt;缺乏跨平台統一的基準測試工具&lt;/li&gt;
&lt;li&gt;有限的即時 GPU 監控整合&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Live VLM WebUI&lt;/strong&gt; 通過提供基於 WebRTC 的介面來解決這些差距,用於即時視覺推理測試並整合系統監控。&lt;/p&gt;

&lt;h2&gt;
  
  
  入門指南
&lt;/h2&gt;

&lt;h3&gt;
  
  
  步驟 1: 準備 VLM 後端 (Ollama)
&lt;/h3&gt;

&lt;p&gt;首先,安裝 Ollama 作為後端來服務 VLM。Ollama 官方安裝程式支援 Jetson。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux/Mac 的 Ollama 安裝程式&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh

&lt;span class="c"&gt;# 下載推薦模型(輕量級)&lt;/span&gt;
ollama pull gemma3:4b

&lt;span class="c"&gt;# 或其他視覺模型&lt;/span&gt;
&lt;span class="c"&gt;# ollama pull llama3.2-vision:11b&lt;/span&gt;
&lt;span class="c"&gt;# ollama pull qwen2.5-vl:7b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;警告 - Jetson Thor (JetPack 7.0) 使用者:&lt;/strong&gt; Ollama 0.12.10 在 Jetson Thor 上存在 GPU 推理問題。請使用版本 0.12.9:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | &lt;span class="nv"&gt;OLLAMA_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.12.9 sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  步驟 2: 安裝 Live VLM WebUI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/nvidia-ai-iot/live-vlm-webui.git
&lt;span class="nb"&gt;cd &lt;/span&gt;live-vlm-webui
./scripts/start_container.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  步驟 3: 訪問網頁介面
&lt;/h3&gt;

&lt;p&gt;打開您的網頁瀏覽器並導航至:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;本地 URL&lt;/strong&gt;: &lt;code&gt;https://localhost:8090&lt;/code&gt; (如果在同一台機器上運行瀏覽器)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;網路 URL&lt;/strong&gt;: &lt;code&gt;https://&amp;lt;IP_ADDRESS&amp;gt;:8090&lt;/code&gt; (從同一網路上的另一台設備訪問)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;本地 URL: https://localhost:8090
網路 URL: https://10.110.50.252:8090
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;給 Jetson Orin Nano 使用者的提示:&lt;/strong&gt; 建議使用連接在同一網路上的 PC,並遠端訪問網頁 UI,以獲得更好的效能。&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  如何使用 Live VLM WebUI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  步驟 4: 配置並啟動
&lt;/h3&gt;

&lt;h4&gt;
  
  
  接受自簽名 SSL 證書
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;點擊 "&lt;strong&gt;進階&lt;/strong&gt;" 按鈕&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjlgtg9faw9g36ut4sok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjlgtg9faw9g36ut4sok.png" alt="Chrome 進階" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點擊 "&lt;strong&gt;繼續前往  (不安全)&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpv3iszcwq0ulr107url1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpv3iszcwq0ulr107url1.png" alt="Chrome 繼續" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;提示時允許相機訪問&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5q8q51ckadvjooapgvo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5q8q51ckadvjooapgvo.png" alt="Chrome 網路攝影機訪問" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  驗證 VLM API 配置
&lt;/h4&gt;

&lt;p&gt;介面會自動檢測本地 VLM 後端。在 "&lt;strong&gt;VLM API 配置&lt;/strong&gt;" 部分驗證:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API 端點&lt;/strong&gt;: &lt;code&gt;http://localhost:11434/v1&lt;/code&gt; (Ollama) 或 &lt;code&gt;http://localhost:8000/v1&lt;/code&gt; (vLLM)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;模型&lt;/strong&gt;: 已選擇的模型名稱(例如 &lt;code&gt;gemma3:4b&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;對於雲端 API,手動輸入端點和 API 密鑰。&lt;/p&gt;

&lt;h4&gt;
  
  
  啟動相機和 VLM 分析
&lt;/h4&gt;

&lt;p&gt;點擊 "&lt;strong&gt;啟動相機並開始 VLM 分析&lt;/strong&gt;" 並授予相機權限。介面開始根據配置的間隔串流和分析影格。&lt;/p&gt;

&lt;h2&gt;
  
  
  主要功能
&lt;/h2&gt;

&lt;h3&gt;
  
  
  即時顯示
&lt;/h3&gt;

&lt;p&gt;右側主窗格顯示以下資訊:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. 即時 AI 分析結果
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;VLM 輸出資訊&lt;/strong&gt; 部分顯示:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;右上角&lt;/strong&gt;: 推理延遲、平均延遲、總分析次數&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;當前使用的 VLM 模型&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;當前提示&lt;/strong&gt; 文字&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;分析結果文字&lt;/strong&gt;(支援 Markdown)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. 即時視訊串流
&lt;/h4&gt;

&lt;p&gt;WebRTC 串流您的網路攝影機視訊,具有:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;鏡像按鈕&lt;/strong&gt;(左上角) - 面對相機時方便的鏡像顯示&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;覆蓋文字&lt;/strong&gt; - 分析結果覆蓋在視訊上(如果在設定中啟用)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. GPU/CPU 監控
&lt;/h4&gt;

&lt;p&gt;即時硬體使用監控:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GPU 使用率&lt;/strong&gt;(使用 &lt;code&gt;jtop&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VRAM 使用率&lt;/strong&gt;(使用 &lt;code&gt;jtop&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU 使用率&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系統 RAM&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  詳細設定
&lt;/h3&gt;

&lt;p&gt;左側設定選單允許您自訂配置和行為。&lt;/p&gt;

&lt;h4&gt;
  
  
  1. 提示自訂
&lt;/h4&gt;

&lt;p&gt;最重要的設定是左側選單底部的 &lt;strong&gt;提示編輯器&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;快速提示&lt;/strong&gt; - 8 個可立即使用的預設提示:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;場景描述&lt;/strong&gt;: "用一句話描述您在這張圖片中看到的內容。"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;物體檢測&lt;/strong&gt;: "列出您在這張圖片中看到的所有物體,用逗號分隔。"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;活動識別&lt;/strong&gt;: "描述人物的活動以及他們正在做什麼。"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;安全監控&lt;/strong&gt;: "是否有任何可見的安全隱患?用'警報:描述'或'安全'回答。"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;情緒檢測&lt;/strong&gt;: "描述可見人物的面部表情和情緒。"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;無障礙&lt;/strong&gt;: "為視障人士提供場景的詳細描述。"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OCR / 文字識別&lt;/strong&gt;: "閱讀並轉錄圖片中可見的任何文字。"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;是/否問題&lt;/strong&gt;: "僅用是或否回答:是否有可見的人?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;自訂提示&lt;/strong&gt; - 在自訂提示欄位中輸入您自己的獨特提示。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示:&lt;/strong&gt; 許多模型,包括 &lt;code&gt;gemma3:4b&lt;/code&gt;,支援多種語言。嘗試用不同語言指示模型並以該語言輸出。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;即時提示工程&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;您可以執行我們可能稱之為 "即時視覺提示工程" 的操作。&lt;/p&gt;

&lt;p&gt;例如,按原樣使用物體檢測提示可能會產生:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"這是我在圖片中看到的物體列表:人、桌子、顯示器..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;模型貼心地包含了序言。如果您想直接將其作為 CSV 饋送到下游應用程式,這不太方便,因此您可以自訂提示:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;列出您在這張圖片中看到的所有物體,用逗號分隔。
不要以 "這是一個列表..." 開頭
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這會抑制序言。這種即時評估能力是該工具的獨特優勢之一。&lt;/p&gt;

&lt;h4&gt;
  
  
  2. 後端配置
&lt;/h4&gt;

&lt;p&gt;左側選單頂部的 &lt;strong&gt;VLM API 配置&lt;/strong&gt; 支援多個 VLM 後端。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API 基礎 URL&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;如果 Ollama、vLLM 或 SGLang 在同一台機器上本地運行,會自動檢測&lt;/li&gt;
&lt;li&gt;如果未檢測到,您可以指定雲端 API(預設設定為 NVIDIA API Catalog)&lt;/li&gt;
&lt;li&gt;使用雲端 API 時,會出現 &lt;strong&gt;API 密鑰&lt;/strong&gt; 欄位供您輸入憑證&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;模型選擇&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;查詢指定的 API 並列出可用模型。&lt;/p&gt;

&lt;h4&gt;
  
  
  3. 相機設定
&lt;/h4&gt;

&lt;p&gt;左側選單中間的 &lt;strong&gt;相機和應用程式控制&lt;/strong&gt; 部分處理相機選擇和相關設定。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;相機選擇&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;列出客戶端 PC 上瀏覽器檢測到的所有相機。大多數現代筆記型電腦應該有可用的前置相機。如果直接在 Jetson 上使用桌面瀏覽器,請連接 USB 相機。&lt;/p&gt;

&lt;p&gt;即使在 VLM 分析期間,您也可以切換相機。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;影格處理間隔&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WebRTC 相機影格處理(簡單地將影格從瀏覽器回傳到 UI 伺服器)和 VLM 分析處理非同步運行。&lt;/p&gt;

&lt;p&gt;預設情況下,VLM 分析完成後,它會等待下一個可被 30 整除的影格編號,然後再開始下一次分析。&lt;/p&gt;

&lt;p&gt;如果您想放慢分析速度以有更多時間閱讀每個結果,請增加這個數字。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RTSP 串流 (測試版)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;暫時支援來自 IP 監控攝影機的 RTSP 串流。&lt;/p&gt;

&lt;p&gt;測試尚未涵蓋許多相機,所以如果遇到問題,請在 GitHub 上回報。&lt;/p&gt;




&lt;h2&gt;
  
  
  已驗證的平台
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;平台&lt;/th&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;狀態&lt;/th&gt;
&lt;th&gt;推理速度&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PC (RTX 6000 Ada)&lt;/td&gt;
&lt;td&gt;RTX 6000 Ada&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&amp;lt;1 秒/影格 (gemma3:4b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jetson Orin Nano 8GB&lt;/td&gt;
&lt;td&gt;1024 核心 Ampere&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;7-8 秒/影格 (gemma3:4b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jetson Thor 128GB&lt;/td&gt;
&lt;td&gt;2560 核心 Blackwell&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;1-2 秒/影格 (llama3.2-vision:11b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DGX Spark&lt;/td&gt;
&lt;td&gt;6144 核心 Blackwell&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;1-2 秒/影格 (llama3.2-vision:11b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mac (M3)&lt;/td&gt;
&lt;td&gt;Apple Silicon&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;2-4 秒/影格 (gemma3:4b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows (WSL2)&lt;/td&gt;
&lt;td&gt;RTX A3000&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;2-4 秒/影格 (gemma3:4b)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Jetson Orin Nano 效能
&lt;/h3&gt;

&lt;p&gt;即使是最實惠的 Jetson,&lt;strong&gt;Jetson Orin Nano 開發者套件&lt;/strong&gt;,也能透過 Ollama 成功運行 &lt;code&gt;gemma3:4b&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;雖然每個影格需要 7-8 秒處理,但它持續運行,為即時系統開啟了可能性。&lt;/p&gt;

&lt;h2&gt;
  
  
  故障排除
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Jetson Thor (JetPack 7.0) 上的 Ollama GPU 錯誤
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;問題:&lt;/strong&gt; Ollama 0.12.10 在 Jetson Thor 上的 GPU 推理失敗&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;解決方案:&lt;/strong&gt; 降級到 Ollama 0.12.9:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 停止並移除當前的 Ollama&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop ollama
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; /usr/local/bin/ollama

&lt;span class="c"&gt;# 安裝 Ollama 0.12.9&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | &lt;span class="nv"&gt;OLLAMA_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.12.9 sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  無法訪問相機
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;問題:&lt;/strong&gt; 瀏覽器無法訪問網路攝影機或顯示 "權限被拒絕"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;解決方案:&lt;/strong&gt; 確保您使用的是 HTTPS(而非 HTTP):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;start_container.sh&lt;/code&gt; 腳本預設啟用 HTTPS&lt;/li&gt;
&lt;li&gt;在瀏覽器中接受自簽名證書警告(點擊 "進階" → "繼續")&lt;/li&gt;
&lt;li&gt;現代瀏覽器需要 HTTPS 才能訪問網路攝影機&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  無法連接到 VLM 後端
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;問題:&lt;/strong&gt; "連接失敗" 或 "連接被拒絕" 錯誤&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;解決方案:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;驗證 VLM 是否正在運行:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 對於 Ollama&lt;/span&gt;
curl http://localhost:11434/v1/models

&lt;span class="c"&gt;# 對於 vLLM&lt;/span&gt;
curl http://localhost:8000/v1/models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;檢查防火牆設定:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 11434  &lt;span class="c"&gt;# Ollama&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 8000   &lt;span class="c"&gt;# vLLM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;如果使用 Docker,確保網路模式:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 使用主機網路訪問本地服務&lt;/span&gt;
docker run &lt;span class="nt"&gt;--network&lt;/span&gt; host ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GPU 統計顯示 "N/A"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;問題:&lt;/strong&gt; GPU 監控對所有指標顯示 "N/A"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jetson 的解決方案:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;確保已安裝 jetson-stats:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; jetson-stats
&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;授予容器訪問 jtop socket:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 已包含在 start_container.sh 中&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; /run/jtop.sock:/run/jtop.sock:ro ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;對於 Jetson Thor,從 GitHub 安裝:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--break-system-packages&lt;/span&gt; git+https://github.com/rbonghi/jetson_stats.git
&lt;span class="nb"&gt;sudo &lt;/span&gt;jtop &lt;span class="nt"&gt;--install-service&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  效能緩慢 / 低 FPS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;問題:&lt;/strong&gt; 視訊卡頓或 VLM 回應緩慢&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;解決方案:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;使用較小的模型:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma3:4b  &lt;span class="c"&gt;# 而非 gemma3:11b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;增加影格處理間隔:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;在設定中,將 "影格處理間隔" 設為 60+ 影格&lt;/li&gt;
&lt;li&gt;這會減少影格分析的頻率&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;減少最大 Token:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;將 "Max tokens" 設為 50-100 而非 512&lt;/li&gt;
&lt;li&gt;較短的回應 = 更快的推理&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;檢查系統資源:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jtop  &lt;span class="c"&gt;# 監控 GPU/CPU 使用率&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  不支援 JetPack 5.x
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;問題:&lt;/strong&gt; JetPack 5.x 上的 Python 3.8 相容性問題&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;解決方案:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;升級到 JetPack 6.x 或 JetPack 7.0&lt;/li&gt;
&lt;li&gt;或使用自動處理 Python 環境的 Docker 方法&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如需更多故障排除提示,請參閱&lt;a href="https://github.com/NVIDIA-AI-IOT/live-vlm-webui/blob/main/docs/troubleshooting.md" rel="noopener noreferrer"&gt;官方故障排除指南&lt;/a&gt;。&lt;/p&gt;

</description>
      <category>nvidia</category>
      <category>jetson</category>
      <category>vlm</category>
    </item>
    <item>
      <title>Using Rust WebAssembly in Vite + React: A Modern Game of Life Example</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Tue, 09 Dec 2025 15:25:31 +0000</pubDate>
      <link>https://dev.to/jambochen/using-rust-webassembly-in-vite-react-a-modern-game-of-life-example-hde</link>
      <guid>https://dev.to/jambochen/using-rust-webassembly-in-vite-react-a-modern-game-of-life-example-hde</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This project is based on the official Rust + WebAssembly tutorial for &lt;strong&gt;Game of Life&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
&lt;a href="https://rustwasm.github.io/docs/book/game-of-life/introduction.html" rel="noopener noreferrer"&gt;https://rustwasm.github.io/docs/book/game-of-life/introduction.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All Rust code is taken directly from the tutorial. The focus here is on integrating Rust-generated WebAssembly into a &lt;strong&gt;Vite + React + TypeScript&lt;/strong&gt; project. The official tutorial still uses webpack and JavaScript, which makes the documentation a bit outdated for Vite users. This guide shows how to adapt the workflow to Vite while keeping everything compatible with wasm-bindgen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Project Structure Overview
&lt;/h2&gt;

&lt;p&gt;After setup, the project looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── backend                # Rust wasm backend
│   ├── Cargo.toml
│   └── src/lib.rs
├── pkg                    # wasm-bindgen output directory
├── src                    # React frontend
│   ├── App.tsx
│   └── main.tsx
├── vite.config.ts
├── package.json
└── index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure separates the Rust logic (backend) from the React frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Create the Vite + React + TypeScript Project
&lt;/h2&gt;

&lt;p&gt;In the &lt;strong&gt;project root&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Framework: &lt;code&gt;React&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Variant: &lt;code&gt;TypeScript&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;wasm-lifegame
pnpm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Create Rust WebAssembly Backend
&lt;/h2&gt;

&lt;p&gt;From the &lt;strong&gt;project root&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo new backend &lt;span class="nt"&gt;--lib&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
├── Cargo.toml
└── src/lib.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure &lt;code&gt;Cargo.toml&lt;/code&gt; for wasm&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"backend"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2024"&lt;/span&gt;

&lt;span class="nn"&gt;[lib]&lt;/span&gt;
&lt;span class="py"&gt;crate-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"cdylib"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;wasm-bindgen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2.106"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;crate-type = ["cdylib"]&lt;/code&gt; makes Rust compile to a dynamic library compatible with wasm&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wasm-bindgen&lt;/code&gt; is required for JS interop
### Rust Code: Game of Life&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open &lt;code&gt;backend/src/lib.rs&lt;/code&gt; and replace it with the &lt;a href="https://rustwasm.github.io/docs/book/game-of-life/introduction.html" rel="noopener noreferrer"&gt;&lt;strong&gt;official Game of Life Rust code&lt;/strong&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;wasm_bindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="nd"&gt;#[repr(u8)]&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Clone,&lt;/span&gt; &lt;span class="nd"&gt;Copy,&lt;/span&gt; &lt;span class="nd"&gt;Debug,&lt;/span&gt; &lt;span class="nd"&gt;PartialEq,&lt;/span&gt; &lt;span class="nd"&gt;Eq)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Dead&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Alive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Universe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Universe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;live_neighbor_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;delta_row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.cloned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;delta_col&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.cloned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delta_row&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;delta_col&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;neighbor_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;delta_row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;neighbor_col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;delta_col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.get_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neighbor_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;neighbor_col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.cells&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Universe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.cells&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.height&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.width&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.get_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.cells&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;live_neighbors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.live_neighbor_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;next_cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;live_neighbors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otherwise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;otherwise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;

                &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_cell&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.cells&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Universe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dead&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;Universe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Display&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Universe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.cells&lt;/span&gt;&lt;span class="nf"&gt;.as_slice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.chunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.width&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dead&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="sc"&gt;'◻'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="sc"&gt;'◼'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
                &lt;span class="nd"&gt;write!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nd"&gt;write!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Add the wasm compilation target
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustup target add wasm32-unknown-unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Build Rust to WebAssembly
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;backend
cargo build &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-unknown-unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/target/wasm32-unknown-unknown/debug/backend.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Generate JavaScript Bindings with wasm-bindgen
&lt;/h2&gt;

&lt;p&gt;Install &lt;code&gt;wasm-bindgen-cli&lt;/code&gt; by &lt;code&gt;cargo&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;wasm-bindgen-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Return to the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
wasm-bindgen backend/target/wasm32-unknown-unknown/debug/backend.wasm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out-dir&lt;/span&gt; pkg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Note that &lt;code&gt;--target&lt;/code&gt; is "web" and not "bundle".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkg
├── backend_bg.wasm
├── backend_bg.wasm.d.ts
├── backend.d.ts
└── backend.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;pkg&lt;/code&gt; folder contains everything the frontend needs to import the wasm module.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Install Vite WebAssembly Plugins
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; vite-plugin-wasm vite-plugin-top-level-await
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These plugins allow Vite to handle &lt;code&gt;.wasm&lt;/code&gt; imports and top-level &lt;code&gt;await&lt;/code&gt;. Edit &lt;code&gt;vite.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;wasm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite-plugin-wasm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;topLevelAwait&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite-plugin-top-level-await&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;wasm&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;topLevelAwait&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  React Frontend: Loading Rust wasm (src/App.tsx)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Universe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../pkg/backend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Not `backend_bg.wasm`&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLPreElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;universeRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Universe&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;universe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Universe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;universeRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;universe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderLoop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;universeRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;preRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;universeRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nx"&gt;universeRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderLoop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderLoop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;pre&lt;/span&gt;
      &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;preRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"game-of-life-canvas"&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;monospace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run the Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5173
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the &lt;strong&gt;Game of Life running in real time in the browser&lt;/strong&gt;, driven by Rust + wasm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate wasm Build (package.json)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"wasm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd backend &amp;amp;&amp;amp; cargo build --target wasm32-unknown-unknown &amp;amp;&amp;amp; wasm-bindgen target/wasm32-unknown-unknown/debug/backend.wasm --out-dir ../pkg --target web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"predev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm run wasm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prebuild"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm run wasm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc -b &amp;amp;&amp;amp; vite build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite preview"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures Rust is compiled to wasm automatically before dev or build.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>typescript</category>
      <category>vite</category>
      <category>webdev</category>
    </item>
    <item>
      <title>OpenAI 開源 GPT-OSS：重塑 AI 開發生態的里程碑之舉</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Wed, 06 Aug 2025 03:31:09 +0000</pubDate>
      <link>https://dev.to/jambochen/openai-kai-yuan-gpt-osszhong-su-ai-kai-fa-sheng-tai-de-li-cheng-bei-zhi-ju-3474</link>
      <guid>https://dev.to/jambochen/openai-kai-yuan-gpt-osszhong-su-ai-kai-fa-sheng-tai-de-li-cheng-bei-zhi-ju-3474</guid>
      <description>&lt;p&gt;2025年8月5日，OpenAI 正式發布了 GPT-OSS-120B 和 GPT-OSS-20B 兩款開源語言模型，這標誌著自 GPT-2 以來，OpenAI 首次向社群開放大型語言模型的完整權重。這一決定不僅展現了 OpenAI 對開源生態的重新審視，更預示著人工智慧發展模式的深刻變革。&lt;/p&gt;

&lt;h2&gt;
  
  
  技術突破與架構創新
&lt;/h2&gt;

&lt;p&gt;GPT-OSS 系列模型在技術架構上體現了 OpenAI 的最新研究成果。兩個模型都採用了專家混合（MoE）架構，這一設計顯著降低了推論時的運算開銷。具體而言，GPT-OSS-120B 擁有1170億總參數，但每個 token 僅啟動51億參數；而 GPT-OSS-20B 雖然總參數為210億，但啟動參數僅為36億。這種稀疏啟動的設計使得模型在保持強大效能的同時，大幅降低了運行成本。&lt;/p&gt;

&lt;p&gt;在注意力機制方面，兩個模型都採用了交替的密集和局部帶狀稀疏注意力模式，結合分組多查詢注意力（分組大小為8），有效提升了推論和記憶體效率。值得注意的是，這些模型原生支援128k的上下文長度，並使用旋轉位置嵌入（RoPE）進行位置編碼，展現了在長文本處理方面的優勢。&lt;/p&gt;

&lt;p&gt;從效能表現來看，GPT-OSS-120B 在核心推理基準測試中與 OpenAI O1-mini 模型幾乎持平，同時能在單個80GB GPU上高效運行。更令人驚喜的是，GPT-OSS-20B 在常見基準測試中達到了與 O1‑mini 相似的結果，卻可以在僅配備16GB記憶體的邊緣裝置上運行。這種硬體友好性使得高品質的AI推論能夠真正走向一般開發者和中小企業。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frafecnn0wijhgdvmqnd1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frafecnn0wijhgdvmqnd1.png" alt=" " width="622" height="786"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在實際應用能力方面，兩個模型在工具使用、少樣本函式呼叫、以及鏈式思維推理等方面表現突出。特別是在 Tau-Bench 智慧代理評估套件和 HealthBench 測試中，它們甚至超越了包括 OpenAI O1 和 GPT-4o 在內的專有模型。這種表現證明了開源模型完全有能力在關鍵應用場景中與閉源方案競爭。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63yeg8f9bjacqdx58ar8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63yeg8f9bjacqdx58ar8.png" alt=" " width="638" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  開源策略的深遠影響
&lt;/h2&gt;

&lt;p&gt;OpenAI 選擇開源 GPT-OSS 系列模型，背後蘊含著多層次的策略考量。首先，這一舉措直接回應了業界對AI技術透明度和可控性的呼聲。在Apache 2.0授權條款下發布的模型允許開發者進行自由修改和商業化使用，這為建構多樣化的AI應用生態奠定了基礎。&lt;/p&gt;

&lt;p&gt;更為重要的是，GPT-OSS 模型的發布體現了 OpenAI 對AI安全的全新思考。與傳統的閉源模型不同，GPT-OSS 提供了完整的思維鏈（CoT）過程，這使得研究人員和開發者能夠深入理解模型的推理過程。OpenAI 明確表示，他們在訓練過程中未對模型的 CoT 進行任何直接監督，這種設計理念為監測模型的不當行為、欺騙和濫用提供了可能。&lt;/p&gt;

&lt;p&gt;在安全性方面，OpenAI 採用了創新的「最壞情況微調」評估方法。透過在專門的生物學和網路安全資料上對模型進行惡意微調，模擬攻擊者可能採用的手段，研究團隊發現即使經過廣泛的惡意微調，這些模型仍無法達到其「防範準備框架」所定義的高能力水準。這一發現為開源模型的安全性提供了重要的實證支撐。&lt;/p&gt;

&lt;p&gt;對於廣大開發者而言，GPT-OSS 的開源意味著前所未有的自主權。企業可以在本地部署模型，確保資料隱私和安全；研究機構可以基於這些模型進行深度客製化和學術研究；新創公司則無需承擔昂貴的API呼叫費用即可獲得世界級的AI能力。這種模式的轉變有望加速AI技術在各行各業的落地應用。&lt;/p&gt;

&lt;h2&gt;
  
  
  NVIDIA 的生態賦能
&lt;/h2&gt;

&lt;p&gt;在 GPT-OSS 模型的部署和最佳化方面，NVIDIA 展現出了卓越的技術支援能力。透過 TensorRT-LLM 框架，NVIDIA 為 GPT-OSS 系列模型提供了高效的推論最佳化方案。這套工具不僅支援模型的自動最佳化，還能夠根據不同的GPU架構產生專門的推論引擎。&lt;/p&gt;

&lt;p&gt;TensorRT-LLM 的Python API設計體現了易用性和高效能的完美結合。開發者只需幾行程式碼即可從Hugging Face下載模型權重，系統會自動為特定的GPU架構建構最佳化引擎。對於 GPT-OSS-20B 模型，在配備至少20GB VRAM的NVIDIA GPU上，開發者可以輕鬆實現高效能推論。推薦的硬體包括NVIDIA Hopper（如H100、H200）、NVIDIA Blackwell（如B100、B200）以及最新的RTX 50系列GPU。&lt;/p&gt;

&lt;p&gt;NVIDIA 還透過預建構的Docker容器簡化了部署流程。透過 NVIDIA NGC 平台，開發者可以快速獲取包含所有相依性的容器映像，大大降低了環境設定的複雜性。這種標準化的部署方案確保了模型在不同硬體環境中的一致性表現。&lt;/p&gt;

&lt;p&gt;更值得關注的是，TensorRT-LLM 支援多種量化技術，如 INT8 和 FP8 量化，這些技術能夠在保持模型精度的同時顯著減小模型規模並加速推論過程。對於資源受限的硬體環境，這些最佳化技術尤為重要。結合 NVIDIA 的硬體優勢，開發者可以在從資料中心到邊緣裝置的全場景中部署 GPT-OSS 模型。&lt;/p&gt;

&lt;p&gt;在生產環境部署方面，NVIDIA 還提供了 Dynamo 平台支援，實現了強大、可擴展的多模型服務架構。這種企業級的部署方案為 GPT-OSS 模型的商業化應用提供了可靠保障。&lt;/p&gt;

&lt;h2&gt;
  
  
  結語
&lt;/h2&gt;

&lt;p&gt;GPT-OSS 的發布標誌著 AI 發展進入了一個新階段，開源模型與閉源模型的效能差距正在快速縮小。這一轉變將推動 AI 技術的民主化進程，讓更多組織和個人能夠參與 AI 創新。&lt;/p&gt;

&lt;p&gt;然而，開放性也帶來了新的挑戰，包括如何確保負責任使用、維護安全性，以及建立有效的治理機制。OpenAI 透過舉辦紅隊測試挑戰賽等方式，展現了開放協作解決安全問題的新模式。&lt;/p&gt;

&lt;h2&gt;
  
  
  參考資料與延伸閱讀
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OpenAI 官方文件&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openai.com/index/introducing-gpt-oss/" rel="noopener noreferrer"&gt;GPT-OSS 模型發布公告&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdn.openai.com/pdf/419b6906-9da6-406c-a19d-1bb078ac7637/oai_gpt-oss_model_card.pdf" rel="noopener noreferrer"&gt;GPT-OSS 模型卡片與技術規格&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/openai/gpt-oss-120b" rel="noopener noreferrer"&gt;GPT-OSS 模型權重（Hugging Face）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NVIDIA 相關資源&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nvidia.github.io/TensorRT-LLM/" rel="noopener noreferrer"&gt;TensorRT-LLM 官方文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://catalog.ngc.nvidia.com/" rel="noopener noreferrer"&gt;NVIDIA NGC 容器平台&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cookbook.openai.com/articles/run-nvidia" rel="noopener noreferrer"&gt;GPT-OSS 部署指南&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Isaac Sim 5 与 ROS2 机械臂仿真教程</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Thu, 17 Jul 2025 12:51:10 +0000</pubDate>
      <link>https://dev.to/jambochen/isaac-sim-5-yu-ros2-ji-jie-bi-fang-zhen-jiao-cheng-1lcp</link>
      <guid>https://dev.to/jambochen/isaac-sim-5-yu-ros2-ji-jie-bi-fang-zhen-jiao-cheng-1lcp</guid>
      <description>&lt;p&gt;本教程將指導您在 Isaac Sim 5 中導入機械臂模型並與 ROS2 整合，實現機械臂的仿真控制。&lt;/p&gt;

&lt;h2&gt;
  
  
  資料來源
&lt;/h2&gt;

&lt;p&gt;本教程基於以下資源製作：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;影片教程：&lt;a href="https://www.bilibili.com/video/BV1sZKfzgEAG" rel="noopener noreferrer"&gt;B站影片教程&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;開源專案：&lt;a href="https://github.com/TheRobotStudio/SO-ARM100" rel="noopener noreferrer"&gt;TheRobotStudio SO-ARM100 機械臂模型&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;官方文件：&lt;a href="https://docs.isaacsim.omniverse.nvidia.com/5.0.0/index.html" rel="noopener noreferrer"&gt;Isaac Sim 5&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  前提條件
&lt;/h2&gt;

&lt;p&gt;開始前，請確保已安裝以下軟體：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Isaac Sim 5&lt;/strong&gt;：參考 &lt;a href="https://github.com/isaac-sim/IsaacSim" rel="noopener noreferrer"&gt;官方安裝指南&lt;/a&gt; 安裝&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROS2&lt;/strong&gt;：參考 &lt;a href="https://docs.isaacsim.omniverse.nvidia.com/5.0.0/installation/install_ros.html" rel="noopener noreferrer"&gt;Isaac Sim ROS2 安裝文件&lt;/a&gt; 配置 ROS2 環境&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  第一步：取得機械臂模型
&lt;/h2&gt;

&lt;p&gt;TheRobotStudio 開源 SO-ARM101 機械臂，提供完整 URDF 文件：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/TheRobotStudio/SO-ARM100.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;：URDF（統一機器人描述格式）包含機械臂的幾何結構、關節資訊和物理屬性。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  第二步：啟動 Isaac Sim
&lt;/h2&gt;

&lt;p&gt;開啟終端，導航至 Isaac Sim 安裝目錄並啟動：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;Isaac Sim 路徑&amp;gt;
./isaac-sim.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：請將 &lt;code&gt;&amp;lt;Isaac Sim 路徑&amp;gt;&lt;/code&gt; 替換為實際安裝路徑。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  第三步：導入機械臂模型
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;在 Isaac Sim 介面點擊 &lt;code&gt;File&lt;/code&gt; -&amp;gt; &lt;code&gt;Import&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;在文件選擇對話框中，導航至 &lt;code&gt;&amp;lt;SO-ARM100 路徑&amp;gt;/Simulation/SO101/so101_new_calib.urdf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;選擇 URDF 文件並點擊導入&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;導入成功後，應可在 Isaac Sim 的 3D 視窗中看到完整機械臂模型。&lt;/p&gt;

&lt;h2&gt;
  
  
  第四步：添加仿真環境
&lt;/h2&gt;

&lt;p&gt;為進行仿真，需為機械臂添加地面：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;點擊 &lt;code&gt;Create&lt;/code&gt; -&amp;gt; &lt;code&gt;Physics&lt;/code&gt; -&amp;gt; &lt;code&gt;Ground Plane&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;將在場景中添加物理地面&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  第五步：配置 ROS2 介面
&lt;/h2&gt;

&lt;p&gt;為機械臂添加 ROS2 通信介面，實現外部控制。&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 建立 Joint States 節點
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;點擊 &lt;code&gt;Tools&lt;/code&gt; -&amp;gt; &lt;code&gt;Robotics&lt;/code&gt; -&amp;gt; &lt;code&gt;ROS 2 OmniGraphs&lt;/code&gt; -&amp;gt; &lt;code&gt;Joint States&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5.2 配置 Articulation Root
&lt;/h3&gt;

&lt;p&gt;在對話框中：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;點擊 &lt;code&gt;Articulation Root&lt;/code&gt; 輸入框右側選擇按鈕&lt;/li&gt;
&lt;li&gt;在場景樹中選擇 &lt;code&gt;so101_new_calib&lt;/code&gt; 下的 &lt;code&gt;root_joint&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Articulation Root&lt;/code&gt; 是機械臂的根關節，定義基礎座標系與物理屬性。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.3 啟用發布與訂閱
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;勾選 &lt;code&gt;Publisher&lt;/code&gt;（發布關節狀態至 ROS2）&lt;/li&gt;
&lt;li&gt;勾選 &lt;code&gt;Subscriber&lt;/code&gt;（接收 ROS2 控制命令）&lt;/li&gt;
&lt;li&gt;點擊 &lt;code&gt;OK&lt;/code&gt; 確認&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  第六步：完善控制圖
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 開啟 Action Graph
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;在 &lt;code&gt;Stage&lt;/code&gt; 視窗找到 &lt;code&gt;ROS_JointStates&lt;/code&gt; 節點&lt;/li&gt;
&lt;li&gt;右鍵點擊，選擇 &lt;code&gt;Open Graph&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;檢查是否有 &lt;code&gt;Articulation Controller&lt;/code&gt; 節點。若已自動建立，跳至第七步；若無，依以下步驟手動建立。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  6.2 添加 Articulation Controller
&lt;/h3&gt;

&lt;p&gt;在 &lt;code&gt;Action Graph&lt;/code&gt; 視窗中：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;在 &lt;code&gt;Nodes&lt;/code&gt; 面板搜索 &lt;code&gt;Articulation Controller&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;將節點拖曳至畫布&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Articulation Controller&lt;/code&gt; 是控制機械臂關節運動的核心組件。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  6.3 建立節點連線
&lt;/h3&gt;

&lt;p&gt;將 &lt;code&gt;ROS2 Subscribe Joint State&lt;/code&gt; 的以下輸出埠連接到 &lt;code&gt;Articulation Controller&lt;/code&gt; 的對應輸入埠：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Joint Names&lt;/code&gt;（關節名稱）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Position Command&lt;/code&gt;（位置命令）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Exec Out&lt;/code&gt; -&amp;gt; &lt;code&gt;Exec In&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  第七步：啟動仿真
&lt;/h2&gt;

&lt;p&gt;點擊 Isaac Sim 介面左側 &lt;code&gt;Play&lt;/code&gt; 按鈕開始仿真，機械臂將發布狀態資訊至 ROS2，並準備接收控制命令。&lt;/p&gt;

&lt;h2&gt;
  
  
  第八步：驗證 ROS2 通信
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8.1 啟動 ROS2 環境
&lt;/h3&gt;

&lt;p&gt;開啟新終端並啟動 ROS2 環境：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; /opt/ros/jazzy/setup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：請根據您的 ROS2 版本調整路徑，如 &lt;code&gt;humble&lt;/code&gt;、&lt;code&gt;iron&lt;/code&gt; 等。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8.2 查看 ROS2 主題
&lt;/h3&gt;

&lt;p&gt;使用以下命令查看可用 ROS2 主題：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ros2 topic list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;應看到以下輸出：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/joint_command
/joint_states
/parameter_events
/rosout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8.3 監控關節狀態
&lt;/h3&gt;

&lt;p&gt;使用以下命令即時查看機械臂關節狀態：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ros2 topic &lt;span class="nb"&gt;echo&lt;/span&gt; /joint_states
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;將顯示每個關節的位置、速度和力矩資訊。&lt;/p&gt;

&lt;h2&gt;
  
  
  第九步：編寫控制腳本
&lt;/h2&gt;

&lt;p&gt;以下為基礎鍵盤控制範例：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;rclpy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rclpy.node&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sensor_msgs.msg&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JointState&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;termios&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tty&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JointKeyboardControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;joint_keyboard_control&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publisher_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_publisher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JointState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/joint_command&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# 關節名稱（與 /joint_states 一致）
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shoulder_pan&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shoulder_lift&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;elbow_flex&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wrist_flex&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wrist_roll&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gripper&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# 當前關節位置（初始為0）
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# 當前選中關節索引（預設第一個）
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="c1"&gt;# 關節調整步長
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;

        &lt;span class="c1"&gt;# 保存終端設定
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;old_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;termios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcgetattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# 操作提示
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_instructions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_instructions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;=== 機械臂鍵盤控制 ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1-6: 選擇關節&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a/d: 調整關節位置（a=減小, d=增大）&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q: 退出程式&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;當前關節:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;當前位置:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# 非阻塞讀取鍵盤輸入
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# 設置終端為非阻塞模式
&lt;/span&gt;            &lt;span class="n"&gt;tty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setcbreak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fileno&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;rclpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_key&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="c1"&gt;# 選擇關節（1-6）
&lt;/span&gt;                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;6&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;當前關節:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;當前位置:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="c1"&gt;# 調整關節位置（a=減小, d=增大）
&lt;/span&gt;                    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step_size&lt;/span&gt;
                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish_joint_command&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;位置 -:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;d&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step_size&lt;/span&gt;
                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish_joint_command&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;位置 +:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_joint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="c1"&gt;# 退出程式（q）
&lt;/span&gt;                    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;退出程式...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="k"&gt;break&lt;/span&gt;

                &lt;span class="c1"&gt;# 短暫休眠，避免CPU過高
&lt;/span&gt;                &lt;span class="n"&gt;rclpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spin_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout_sec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# 恢復終端設定
&lt;/span&gt;            &lt;span class="n"&gt;termios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcsetattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;termios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TCSADRAIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;old_settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;publish_joint_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JointState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_clock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;to_msg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_names&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joint_positions&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publisher_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rclpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;joint_keyboard_control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JointKeyboardControl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;joint_keyboard_control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;joint_keyboard_control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy_node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;rclpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  執行控制腳本
&lt;/h3&gt;

&lt;p&gt;將程式碼儲存為 &lt;code&gt;joint_keyboard_control.py&lt;/code&gt;，然後執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 joint_keyboard_control.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;您可在終端使用鍵盤控制機械臂運動，同時觀察 Isaac Sim 中機械臂的即時回應。&lt;/p&gt;

</description>
    </item>
    <item>
      <title>LeRobot 機械手臂操作教程</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Fri, 11 Jul 2025 07:47:08 +0000</pubDate>
      <link>https://dev.to/jambochen/lerobot-ji-jie-bi-cao-zuo-jiao-cheng-78i</link>
      <guid>https://dev.to/jambochen/lerobot-ji-jie-bi-cao-zuo-jiao-cheng-78i</guid>
      <description>&lt;p&gt;本教程基於 Linux 環境編寫，假設用戶已完成環境配置、機械臂組裝與校準。教程中將 Leader 稱為主臂，Follower 稱為從臂。&lt;/p&gt;

&lt;p&gt;訓練環境：Ubuntu 22.10，RTX 4060 TI (16G)&lt;/p&gt;

&lt;h2&gt;
  
  
  準備工作
&lt;/h2&gt;

&lt;p&gt;由於 LeRobot 迭代速度快，建議切換到教程編寫時的版本：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout d2645cb19fc521e5b117fe03d90a84f698d3d3f6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. 遙控操作
&lt;/h2&gt;

&lt;p&gt;完成主從臂校準後，可用以下腳本控制主臂遙控從臂，顯示相機畫面與馬達資訊：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; lerobot.teleoperate &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--robot&lt;/span&gt;.type&lt;span class="o"&gt;=&lt;/span&gt;so101_follower &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--robot&lt;/span&gt;.port&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM1 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--robot&lt;/span&gt;.id&lt;span class="o"&gt;=&lt;/span&gt;follower &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--robot&lt;/span&gt;.cameras&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{ front: {type: opencv, index_or_path: /dev/video2, width: 640, height: 480, fps: 30}}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--teleop&lt;/span&gt;.type&lt;span class="o"&gt;=&lt;/span&gt;so101_leader &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--teleop&lt;/span&gt;.port&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM0 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--teleop&lt;/span&gt;.id&lt;span class="o"&gt;=&lt;/span&gt;leader &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--display_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  參數說明：
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;robot.id&lt;/code&gt; 與 &lt;code&gt;teleop.id&lt;/code&gt;：應與校準時的機械臂唯一 ID 一致，用於讀取校準時儲存的馬達資訊&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;robot.cameras&lt;/code&gt;：相機配置資訊，可執行 &lt;code&gt;python -m lerobot.find_cameras opencv&lt;/code&gt; 查找可用相機。支援多機位配置，透過字典鍵區分與記錄不同相機&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. 資料收集
&lt;/h2&gt;

&lt;h3&gt;
  
  
  基本收集命令
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; lerobot.record &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.type&lt;span class="o"&gt;=&lt;/span&gt;so101_follower &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.port&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.id&lt;span class="o"&gt;=&lt;/span&gt;follower &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.cameras&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{ front: {type: opencv, index_or_path: /dev/video2, width: 640, height: 480, fps: 30}}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--teleop&lt;/span&gt;.type&lt;span class="o"&gt;=&lt;/span&gt;so101_leader &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--teleop&lt;/span&gt;.port&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--teleop&lt;/span&gt;.id&lt;span class="o"&gt;=&lt;/span&gt;leader &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--display_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt;.num_episodes&lt;span class="o"&gt;=&lt;/span&gt;20 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt;.push_to_hub&lt;span class="o"&gt;=&lt;/span&gt;False &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt;.repo_id&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HF_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/record-test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt;.single_task&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Grab the black cube"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  資料儲存位置
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;預設儲存路徑：&lt;code&gt;~/.cache/huggingface/lerobot/{repo-id}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;自訂儲存：設定 &lt;code&gt;HF_LEROBOT_HOME&lt;/code&gt; 環境變數，資料儲存於 &lt;code&gt;HF_LEROBOT_HOME/repo_id&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  資料收集控制
&lt;/h3&gt;

&lt;p&gt;收集過程可透過鍵盤控制：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;右箭頭 (→)&lt;/strong&gt;：錄製階段完成當前錄製並進入恢復階段；恢復階段進入下一輪錄製&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;左箭頭 (←)&lt;/strong&gt;：重新錄製當前資料&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESC&lt;/strong&gt;：完成當前錄製並退出&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;注意：腳本使用 pynput 監聽按鍵，SSH 或 Wayland 下可能無法運作。詳見：&lt;a href="https://pynput.readthedocs.io/en/latest/limitations.html#linux" rel="noopener noreferrer"&gt;pynput 平台限制&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  恢復中斷的收集
&lt;/h3&gt;

&lt;p&gt;若收集過程出錯，可在原命令加 &lt;code&gt;--resume=true&lt;/code&gt; 參數恢復。&lt;/p&gt;

&lt;h3&gt;
  
  
  資料上傳
&lt;/h3&gt;

&lt;p&gt;手動上傳資料至 HuggingFace：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;huggingface-cli upload &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HF_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/record-test ~/.cache/huggingface/lerobot/&lt;span class="o"&gt;{&lt;/span&gt;repo-id&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--repo-type&lt;/span&gt; dataset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;注意：若未設定 &lt;code&gt;--dataset.push_to_hub=False&lt;/code&gt;，收集完成時會自動上傳資料。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 模型訓練
&lt;/h2&gt;

&lt;h3&gt;
  
  
  基本訓練命令
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; lerobot.scripts.train &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt;.repo_id&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HF_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/record-test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt;.type&lt;span class="o"&gt;=&lt;/span&gt;act &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;outputs/train/act_so101_test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--job_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;act_so101_test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt;.device&lt;span class="o"&gt;=&lt;/span&gt;cuda &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wandb&lt;/span&gt;.enable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt;.repo_id&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HF_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/my_policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt;.push_to_hub&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--steps&lt;/span&gt; 50000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--batch_size&lt;/span&gt; 32 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--save_freq&lt;/span&gt; 10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;訓練腳本從 &lt;code&gt;HF_LEROBOT_HOME/dataset.repo_id&lt;/code&gt; 路徑讀取資料進行訓練。&lt;/p&gt;

&lt;h3&gt;
  
  
  恢復訓練
&lt;/h3&gt;

&lt;p&gt;從最新檢查點恢復訓練：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; lerobot.scripts.train &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;outputs/train/act_so101_test/checkpoints/last/pretrained_model/train_config.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. 模型推理與評估
&lt;/h2&gt;

&lt;p&gt;使用訓練好的模型進行推理，同時記錄評估片段：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; lerobot.record &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.type&lt;span class="o"&gt;=&lt;/span&gt;so101_follower &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.port&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.cameras&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{ front: {type: opencv, index_or_path: /dev/video2, width: 640, height: 480, fps: 30}}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--robot&lt;/span&gt;.id&lt;span class="o"&gt;=&lt;/span&gt;follower &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--display_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt;.repo_id&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HF_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/eval_so101 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt;.single_task&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Put lego brick into the transparent box"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt;.path&lt;span class="o"&gt;=&lt;/span&gt;outputs/train/act_so101_test/checkpoints/last/pretrained_model/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>用 Ollama？其實你在跑 llama.cpp！學會直接使用它，發揮更強性能！</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Wed, 05 Mar 2025 23:00:00 +0000</pubDate>
      <link>https://dev.to/jambochen/yong-ollamaqi-shi-ni-zai-pao-llamacppxue-hui-zhi-jie-shi-yong-ta-fa-hui-geng-qiang-xing-neng--1m38</link>
      <guid>https://dev.to/jambochen/yong-ollamaqi-shi-ni-zai-pao-llamacppxue-hui-zhi-jie-shi-yong-ta-fa-hui-geng-qiang-xing-neng--1m38</guid>
      <description>&lt;h1&gt;
  
  
  前言
&lt;/h1&gt;

&lt;p&gt;Ollama 在大語言模型社群中擁有極高的關注度，因為它讓使用者能夠輕鬆地在本地環境運行大型語言模型，即使對相關參數不熟悉，也能獲得良好的使用體驗。此外，Ollama 還提供模型託管服務，使得部署與管理變得更加便捷。&lt;/p&gt;

&lt;p&gt;然而，在 Ollama 背後執行推理的核心技術其實是 llama.cpp，而 GGUF 模型格式也是由 llama.cpp 的作者所開發。雖然 Ollama 已經足以應對日常使用，但如果追求極致的推理效能，或希望探索尚未正式發布的實驗性功能，那麼深入理解與使用 llama.cpp 便是必要的。&lt;/p&gt;

&lt;p&gt;本指南將介紹如何在特定硬體環境下編譯 llama.cpp，轉換與量化 GGUF 模型，以及進行高效的推理測試。&lt;/p&gt;

&lt;h2&gt;
  
  
  試驗平台
&lt;/h2&gt;

&lt;p&gt;硬體環境：Jetson Orin Nano&lt;/p&gt;

&lt;p&gt;軟體環境：JetPack 6.2 (Ubuntu 22.04, CUDA 12.6)&lt;/p&gt;

&lt;p&gt;測試模型：&lt;a href="https://huggingface.co/microsoft/Phi-4-mini-instruct" rel="noopener noreferrer"&gt;microsoft/Phi-4-mini-instruct&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  1. 編譯 llama.cpp
&lt;/h1&gt;

&lt;h2&gt;
  
  
  安裝必要套件
&lt;/h2&gt;

&lt;p&gt;在 Jetson Orin Nano 上編譯 llama.cpp 需要確保系統具備必要的依賴項，包括 CUDA 支援、CMake 以及 g++ 編譯器。如果你使用的是 Jetson 平台，這些工具應該已經預裝。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; build-essential cmake git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  下載並編譯 llama.cpp
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ggerganov/llama.cpp.git
&lt;span class="nb"&gt;cd &lt;/span&gt;llama.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jetson 平台具備 CUDA 核心，因此我們可以啟用 CUDA 加速 GPU 計算。若使用其他平台，請參考&lt;a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md" rel="noopener noreferrer"&gt;官方文檔&lt;/a&gt;以啟用對應功能。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cmake &lt;span class="nt"&gt;-B&lt;/span&gt; build &lt;span class="nt"&gt;-DGGML_CUDA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ON
make &lt;span class="nt"&gt;-j&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;執行完上述步驟後，llama.cpp 應已成功編譯，編譯的可執行文件會儲存在 &lt;code&gt;build/bin&lt;/code&gt; 目錄下。&lt;/p&gt;

&lt;h1&gt;
  
  
  2. 轉換 GGUF 模型
&lt;/h1&gt;

&lt;p&gt;llama.cpp 使用 GGUF 格式的模型。你可以在 Hugging Face 或 Ollama 的儲存庫上直接下載別人轉換好的 GUFF 模型。但如果暫時還沒有現成的 GUFF，你可以自己將 Hugging Face 上發佈的模型轉換從 GUFF 模型，llama.cpp 有提供轉換的 Python 腳本。&lt;/p&gt;

&lt;h2&gt;
  
  
  下載原始 Hugging Face 模型
&lt;/h2&gt;

&lt;p&gt;可以通過 &lt;a href="https://huggingface.co/docs/huggingface_hub/en/guides/cli" rel="noopener noreferrer"&gt;huggingface-cli&lt;/a&gt; 快速下載 Hugging Face 上的模型:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;huggingface-cli download microsoft/Phi-4-mini-instruct --local-dir ./phi4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這將把模型下載到本地的 &lt;code&gt;./phi4&lt;/code&gt; 目錄中。&lt;/p&gt;

&lt;h2&gt;
  
  
  轉換為 GGUF 格式
&lt;/h2&gt;

&lt;p&gt;轉換模型的腳本在 llama.cpp 目錄中。在運行腳本之前，你應該要安裝 python 的依賴&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; pyproject.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接下來就可以用 &lt;code&gt;convert_hf_to_gguf.py&lt;/code&gt;轉換模型。你可以用命令 &lt;code&gt;python3 convert_hf_to_gguf.py -h&lt;/code&gt; 查看該腳本的可用參數。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 convert_hf_to_gguf.py &lt;span class="nt"&gt;--model-dir&lt;/span&gt; ./phi4 &lt;span class="nt"&gt;--outfile&lt;/span&gt; ./models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這將在 &lt;code&gt;./models&lt;/code&gt; 目錄下生成 GGUF 格式模型。到這裏 llama.cpp 就可以推理模型了。&lt;/p&gt;

&lt;h1&gt;
  
  
  3. 量化模型
&lt;/h1&gt;

&lt;p&gt;為了提高推理速度並減少記憶體佔用，我們可以對 GGUF 模型進行量化處理。讓原本需要 9G 現存的模型，只需要 5G 就能運行。&lt;/p&gt;

&lt;p&gt;./build/bin/llama-quantize ./models/Phi-4-mini-instruct.gguf ./models/Phi-4-mini-instruct-q4.gguf q4_0&lt;/p&gt;

&lt;p&gt;這會將模型從浮點格式轉換為 Q4_0 量化格式，從而提升推理速度並降低硬體需求。&lt;/p&gt;

&lt;p&gt;目前量化格式種類繁多，各自具備不同程度的加速與壓縮效果，相對應的準確度也會有所犧牲。不同模型與應用場景適用不同的量化格式，因此建議嘗試多種量化方案，以尋找最適合的配置。&lt;/p&gt;

&lt;h1&gt;
  
  
  4. 推理
&lt;/h1&gt;

&lt;p&gt;當完成模型準備後，即可開始執行推理。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./build/bin/llama-cli &lt;span class="nt"&gt;-m&lt;/span&gt; ./models/Phi-4-mini-instruct-q4.gguf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;如果一切順利，你應該能成功加載模型並進入對話模型。&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Running Phi-3-vision via ONNX on Jetson Platform</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Fri, 19 Jul 2024 09:54:58 +0000</pubDate>
      <link>https://dev.to/jambochen/running-phi-3-vision-via-onnx-on-jetson-platform-35m1</link>
      <guid>https://dev.to/jambochen/running-phi-3-vision-via-onnx-on-jetson-platform-35m1</guid>
      <description>&lt;p&gt;This article aims to run the quantized Phi-3-vision model in ONNX format on the Jetson platform and successfully perform inference for image+text dialogue tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Jetson?
&lt;/h2&gt;

&lt;p&gt;Jetson is a series of small arm64 devices launched by NVIDIA, equipped with powerful GPU computing capabilities. It is designed for edge computing and AI applications. Running on a Linux system, Jetson can handle complex computing tasks with low power consumption, making it ideal for developing embedded AI and machine learning projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why ONNX Runtime?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://onnxruntime.ai/" rel="noopener noreferrer"&gt;ONNX Runtime&lt;/a&gt; is a high-performance inference engine for executing ONNX (Open Neural Network Exchange) models. It provides a simple way to run large language models like Llama, Phi, Gemma, and Mistral via the &lt;a href="https://github.com/microsoft/onnxruntime-genai" rel="noopener noreferrer"&gt;onnxruntime-genai&lt;/a&gt; API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Phi-3-vision Model
&lt;/h2&gt;

&lt;p&gt;In this project, I ran the Phi-3-vision model on the Jetson platform using ONNX Runtime. Here’s a sneak peek at the results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inference Speed and Resource Utilization
&lt;/h3&gt;

&lt;p&gt;By running the Phi-3-vision model in ONNX format on the Jetson Orin Nano with the Int4 quantized model, I achieved remarkable performance metrics. The Python process utilized 5.4 GB of GPU memory while keeping the CPU load minimal and almost fully utilizing the GPU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6a260wdwhok2aeawff4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6a260wdwhok2aeawff4d.png" alt="Resource Utilization" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The inference speed was impressively fast, even on a device with just a 15W power consumption.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48nhnv742pnue8m1gl33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48nhnv742pnue8m1gl33.png" alt="Inference Speed" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Output
&lt;/h3&gt;

&lt;p&gt;Here’s an example of running the Phi-3-vision model on the Jetson platform. Given an image and a prompt, the model successfully converted the image to markdown format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Input Image:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymau9fjbkdtcl149hvd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymau9fjbkdtcl149hvd8.png" alt="table.png" width="487" height="206"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Convert this image to markdown format
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Product             | Qtr 1    | Qtr 2    | Grand Total |
|---------------------|----------|----------|-------------|
| Chocolade           | $744.60  | $162.56  | $907.16     |
| Gummibarchen        | $5,079.60| $1,249.20| $6,328.80   |
| Scottish Longbreads | $1,267.50| $1,062.50| $2,330.00   |
| Sir Rodney's Scones | $1,418.00| $756.00  | $2,174.00   |
| Tarte au sucre      | $4,728.00| $4,547.92| $9,275.92   |
| Chocolate Biscuits  | $943.89  | $349.60  | $1,293.49   |
| Total               | $14,181.59| $8,127.78| $22,309.37  |
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The table lists various products along with their sales figures for Qtr 1, Qtr 2, and the Grand Total. The products include Chocolade, Gummibarchen, Scottish Longbreads, Sir Rodney's Scones, Tarte au sucre, and Chocolate Biscuits. The Grand Total column sums up the sales for each product across the two quarters.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Running the Phi-3-vision model in ONNX format on the Jetson platform demonstrates the incredible potential of combining powerful AI models with efficient edge computing devices. The results are impressive, and the resource utilization is optimized for low-power devices.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;For a detailed step-by-step guide on setting up and running the Phi-3-vision model on Jetson, including preparation and installation, please visit the complete article here: &lt;a href="https://techcommunity.microsoft.com/t5/educator-developer-blog/running-phi-3-vision-via-onnx-on-jetson-platform/ba-p/4195041?WT.mc_id=studentamb_228125" rel="noopener noreferrer"&gt;Running Phi-3-vision via ONNX on Jetson Platform&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're interested in AI and edge computing, don't miss out on this comprehensive tutorial!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Profiling Rust with VS on Windows</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Thu, 23 May 2024 10:00:00 +0000</pubDate>
      <link>https://dev.to/jambochen/profiling-rust-with-vs-on-windows-3m4l</link>
      <guid>https://dev.to/jambochen/profiling-rust-with-vs-on-windows-3m4l</guid>
      <description>&lt;h1&gt;
  
  
  Preface
&lt;/h1&gt;

&lt;p&gt;This all began while I was developing a tool in Rust for reading from a database, processing data, and writing back to it. The program was taking too long to run, and I couldn't figure out where the bottleneck was. Online searches for 'how to profile Rust on Windows' mostly pointed to using Visual Studio (with some mentions of WPA), yet I found no straightforward guides for beginners, &lt;strong&gt;especially as a VS Code user&lt;/strong&gt;. After some trial and error, I've put together this article.&lt;/p&gt;

&lt;p&gt;It's not a full-fledged profiling tutorial, but more a record of my own experiences. I've also experimented with Intel Vtune, which I quite like, but it occupies a hefty 2GB of space. Considering that installing Rust on Windows seems to inevitably involve downloading Visual Studio (I'm not aware of how to avoid it), I've decided to document the profiling process using VS.&lt;/p&gt;

&lt;p&gt;This is just a simple tutorial. For more detailed information, please refer to the Visual Studio documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/visualstudio/profiling/profiling-feature-tour?view=vs-2022&amp;amp;WT.mc_id=studentamb_228125" rel="noopener noreferrer"&gt;First look at profiling tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/visualstudio/profiling/cpu-usage?view=vs-2022&amp;amp;WT.mc_id=studentamb_228125" rel="noopener noreferrer"&gt;Analyze performance by using CPU profiling in the Performance Profiler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/visualstudio/profiling/flame-graph?view=vs-2022&amp;amp;WT.mc_id=studentamb_228125" rel="noopener noreferrer"&gt;Identify hot paths with a flame graph&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Profiling
&lt;/h1&gt;

&lt;p&gt;First and foremost, add the following lines to your project's &lt;code&gt;Cargo.toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile.release]&lt;/span&gt;
&lt;span class="py"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving, building the release will also include debug information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cargo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--release&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Finished&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;release&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;optimized&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;debuginfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0.10s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, open Visual Studio (I'm using VS 2022). Since I'm only planning to use VS as a performance analysis tool, click "Continue without code".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx5a4nacf6tj086t851o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx5a4nacf6tj086t851o.png" alt=" " width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, go to the 'Debug' menu and open 'Performance Profiler'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mh3y6swzxh1vw599c04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mh3y6swzxh1vw599c04.png" alt=" " width="607" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the executable file (exe) as the target for analysis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwkay5azkll74vzglih3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwkay5azkll74vzglih3.png" alt=" " width="357" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the working directory to the project's directory, and locate the compiled executable file in the project's &lt;code&gt;target/release&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ec0g3sk2m5pev798dx0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ec0g3sk2m5pev798dx0.png" alt=" " width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose 'CPU Usage' and then click 'Start' to begin executing the file and recording. You can wait for the file to finish running or interrupt it yourself. After completion, Visual Studio will automatically generate a report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9emqez9xor4ma0sc2st.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9emqez9xor4ma0sc2st.png" alt=" " width="402" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generated report includes information on the execution time of functions and their sub-functions, and it also lists the functions that consume the most time. Clicking 'Open details' will open a more detailed report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25bwch7pi71fo49silmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25bwch7pi71fo49silmd.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the upper left corner, you can switch the displayed view. After clicking on a function, Visual Studio will also display the specific code and the time consumed by each line of code. The flame graph can also be used to display code performance details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6ktlina0hzsrzo0dmam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6ktlina0hzsrzo0dmam.png" alt=" " width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0iisgbkg3151v8mlzc5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0iisgbkg3151v8mlzc5f.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Reference
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/visualstudio/profiling/profiling-feature-tour?view=vs-2022&amp;amp;WT.mc_id=studentamb_228125" rel="noopener noreferrer"&gt;First look at profiling tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/visualstudio/profiling/cpu-usage?view=vs-2022&amp;amp;WT.mc_id=studentamb_228125" rel="noopener noreferrer"&gt;Analyze performance by using CPU profiling in the Performance Profiler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/visualstudio/profiling/flame-graph?view=vs-2022&amp;amp;WT.mc_id=studentamb_228125" rel="noopener noreferrer"&gt;Identify hot paths with a flame graph&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>用 LangChain 構建基於資料庫的問答機器人（四）：通過代理使用外部工具</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/jambochen/yong-langchain-gou-jian-ji-yu-zi-liao-ku-de-wen-da-ji-qi-ren-si-tong-guo-dai-li-shi-yong-wai-bu-gong-ju-45cb</link>
      <guid>https://dev.to/jambochen/yong-langchain-gou-jian-ji-yu-zi-liao-ku-de-wen-da-ji-qi-ren-si-tong-guo-dai-li-shi-yong-wai-bu-gong-ju-45cb</guid>
      <description>&lt;p&gt;上一篇教程我們介紹了 ReAct 系統，這是一個非常強大的行為模式，但它需要編寫大量的示例來告訴 LLM 如何思考、行動，並且為了遵循這個模式，還需要編寫代碼來分析生成文字、調用函數、拼接 prompt 等，這些工作都是十分繁瑣且冗長的。而 LangChain 幫我們把這些步驟都隱藏起來了，將這一系列動作都封裝成 “代理”，我們只需要提供有那些工具可以使用，以及這些工具的功能，就可以讓 LLM 自動完成這些工作了。這樣我們的代碼就會變得簡潔、易讀，並且擁有更高的靈活性。&lt;/p&gt;

&lt;h1&gt;
  
  
  代理（Agent）
&lt;/h1&gt;

&lt;p&gt;所謂代理，就是將一系列的行為和可以使用的工具封裝起來，讓我們可以通過一個簡單的接口來調用這些動作，而不需要關心這些動作是如何完成的。這樣可以讓我們的代碼更簡潔、易讀，並且擁有更高的靈活性。接下來我們就結合 ReAct 和向量數據庫來介紹代理的使用。&lt;/p&gt;

&lt;h2&gt;
  
  
  準備工作
&lt;/h2&gt;

&lt;p&gt;和之前一樣，我們先設置環境變量。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# set the environment variables needed for openai package to know to reach out to azure
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_TYPE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;azure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_BASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&amp;lt;your-endpoint.openai.azure.com/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your AzureOpenAI key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_VERSION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2023-03-15-preview&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接下來我們先把 llm 類創建好。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AzureChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.embeddings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIEmbeddin&lt;/span&gt; &lt;span class="n"&gt;Whether&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="n"&gt;generated&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;
                &lt;span class="n"&gt;returned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;both&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="n"&gt;generated&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt;
                &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Defaults&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gs&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AzureChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deployment_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-35-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-ada-002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;因為我們的向量數據庫已經在之前的教程中創建好了，所以我們用 &lt;code&gt;from_existing_index&lt;/code&gt; 直接連接到數據庫。這裡 &lt;code&gt;index_name&lt;/code&gt; 是我們之前創建數據庫時指定的名字。另外不同的數據庫所使用的鏈接方法可能不同，具體還是要參考各個數據庫的&lt;a href="https://python.langchain.com/docs/modules/data_connection/vectorstores/" rel="noopener noreferrer"&gt;文檔&lt;/a&gt;。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.vectorstores.redis&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;

&lt;span class="n"&gt;rds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_existing_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redis_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis://localhost:6379&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_retriever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  創建工具
&lt;/h2&gt;

&lt;p&gt;雖然代理可以讓 llm 使用外部工具，但我們首先要創建工具，並為工具編寫描述。我們的目的是為了要能夠讓 llm 根據問題從數據庫中檢索出答案，因此我們創建的工具一定是與此相關的。我們可以將檢索器的搜索功能包裝成一個函數，然後以此作為工具，但我們完全可以用之前教程中所介紹的 &lt;code&gt;RetrievalQA&lt;/code&gt; Chain ，它的輸出本就是字符串，而且已經先將相關的資料進行了整理，這顯然比檢索器更高效。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chains&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RetrievalQA&lt;/span&gt;

&lt;span class="n"&gt;qa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RetrievalQA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_chain_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stuff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;然後用 &lt;code&gt;Tool&lt;/code&gt; 將上面創建的 Chain 包裝起來。其中 &lt;code&gt;func&lt;/code&gt; 參數是告訴代理系統要調用哪個函數，&lt;code&gt;description&lt;/code&gt; 是對這個工具的描述。我們將包裝好的工具放在一個列表中，這樣就是告訴系統他可以使用的工具有哪些了。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Tool&lt;/span&gt;

&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Contract QA System&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;qa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;useful for when you need to answer questions about contracts.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  創建代理
&lt;/h2&gt;

&lt;p&gt;我們用 &lt;code&gt;initialize_agent&lt;/code&gt; 函數來創建代理。這裡 &lt;code&gt;agent&lt;/code&gt; 參數是告訴系統我們要創建的代理類型，這裡我們創建的是 &lt;code&gt;CHAT_ZERO_SHOT_REACT_DESCRIPTION&lt;/code&gt;，名字中的 &lt;code&gt;CHAT&lt;/code&gt; 表示這是一個聊天代理，因為我們用的是聊天模型；&lt;code&gt;ZERO_SHOT&lt;/code&gt; 表示它的 prompt 沒有編寫示例，而是僅通過描述來引導 llm 的行為。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initialize_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AgentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CHAT_ZERO_SHOT_REACT_DESCRIPTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;然後用 &lt;code&gt;run&lt;/code&gt; 方法來運行代理。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent_chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;在什麼情況下可以單方面解除合同？&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Entering new  chain...
Thought: This question is asking about the circumstances under which a contract can be unilaterally terminated.

Action:
\```


{
  "action": "Contract QA System",
  "action_input": "在什麼情況下可以單方面解除合同？"
}
\

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Observation: 根據文本，可以單方面解除合同的情況包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在承擔國家重大科研項目期間；&lt;/li&gt;
&lt;li&gt;掌握重大科技成果關鍵技術和資料，未脫離保密期；&lt;/li&gt;
&lt;li&gt;被審查期間或因經濟問題未作結案處理之前；&lt;/li&gt;
&lt;li&gt;合同訂立時所依據的客觀情況發生重大變化，致使合同無法履行，經甲乙雙方協商不能就變更合同達成協議的；&lt;/li&gt;
&lt;li&gt;甲方提出解除本合同，乙方同意解除的；&lt;/li&gt;
&lt;li&gt;乙方患病或者非因工負傷，醫療期滿後，不能從事原工作也不能從事由甲方安排的其他工作，甲方單方面解除本合同的；&lt;/li&gt;
&lt;li&gt;乙方年度考核或者聘期考核不合格，又不同意甲方調整其工作崗位的；&lt;/li&gt;
&lt;li&gt;在試用期內被證明不符合本崗位要求；&lt;/li&gt;
&lt;li&gt;連續曠工超過 10 個工作日或者 1 年內累計曠工超過 20 個工作日的；
...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final Answer: 根據文本，可以單方面解除合同的情況有很多種，包括在承擔國家重大科研項目期間、合同訂立時所依據的客觀情況發生重大變化、甲方提出解除本合同且乙方同意解除等。此外，乙方在試用期內、考入普通高等院校、被錄用或者選調為公務員、依法服兵役的情況下也可以隨時單方面解除合同。如果乙方提出解除合同未能與甲方協商一致，乙方應當堅持正常工作，繼續履行合同；6個月後再次提出解除合同仍未能與甲方協商一致的，即可單方面解除合同。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Finished chain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;'根據文本，可以單方面解除合同的情況有很多種，包括在承擔國家重大科研項目期間、合同訂立時所依據的客觀情況發生重大變化、甲方提出解除本合同且乙方同意解除等。此外，乙方在試用期內、考入普通高等院校、被錄用或者選調為公務員、依法服兵役的情況下也可以隨時單方面解除合同。如果乙方提出解除合同未能與甲方協商一致，乙方應當堅持正常工作，繼續履行合同；6個月後再次提出解除合同仍未能與甲方協商一致的，即可單方面解除合同。 '&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


這樣我們很容易的完成 ReAct 的流程，並且它確實可以從資料源中檢索出答案。用 LangChain 我們就可以用簡潔的代碼完成如此復雜的任務，並且也不需要編寫 prompt，這樣我們就可以更專注於業務的邏輯了。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
    </item>
    <item>
      <title>用 LangChain 構建基於資料庫的問答機器人（三）：ReAct</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Mon, 24 Jul 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/jambochen/yong-langchain-gou-jian-ji-yu-zi-liao-ku-de-wen-da-ji-qi-ren-san-react-14bf</link>
      <guid>https://dev.to/jambochen/yong-langchain-gou-jian-ji-yu-zi-liao-ku-de-wen-da-ji-qi-ren-san-react-14bf</guid>
      <description>&lt;p&gt;大家好，我是 Jambo。我們已經學習瞭如何使用 LangChain 的一些基本功能，解下我們就應該要結合這些功能來做一些複雜的東西了。但在這之前，為了讓同學們更好的理解 LangChain 在這其中做了什麼，我想先介紹一下關於 GPT 使用方面的一些知識。&lt;/p&gt;

&lt;p&gt;在 ChatGPT 開放之初，除了各大公司在 AI 算法方面競爭，還有許多人在研究如何僅通過修改 prompt 就能讓 GPT-3 做出更好的回答，這種方法被稱為“提示工程（Prompt Engineering）”。如果把 LLM 比喻成一個擁有一般常識的大腦，那麼提示工程就是在教它如何思考，從而更有效的結合知識得出答案。像 AutoGPT 就是這樣，他通過精心設計的 prompt，就能讓 GPT-4 自行完成各種任務。為了讓同學們了解這其中的思想，我們先從“思維鏈”開始介紹。&lt;/p&gt;

&lt;h1&gt;
  
  
  思維鏈（Chain of Thought）
&lt;/h1&gt;

&lt;p&gt;思維鏈（Chain of Thought）在 ChatGPT 推出後不久就被提出，具體來說就是通過手動編寫示例的方式讓 GPT-3 將問題的思考過程也生成出來，通過這種方式 GPT-3 回答的效果會有大幅提升。就像我們在寫比較複雜的計算題，將過程一步一步寫出來的正確率會比直接寫出答案要高。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4bnfcm3usqtffl2jndx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4bnfcm3usqtffl2jndx.png" alt="Alt text" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;後來有人發現，只需要加上 “Let's think step by step.” 這一魔法提示，就能達到一樣的效果，還不需要寫示例。並且他還在這基礎上，額外讓 GPT 根據它前面附帶思考過程的回答，再總結出一個更簡潔的答案，相當於把思考過程隱藏起來。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsr7rrdi4li4kyy4nwa8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsr7rrdi4li4kyy4nwa8t.png" alt="Alt text" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我這裡想強調的是，我們用 LLM 構建應用時，完全可以在輸出最終答案前多次用 LLM 分析、拆解、解決問題。就像深度學習網絡中的介於輸入和輸出之間的隱藏層，我們可以在這裡加入更多的邏輯，讓 LLM 能夠更好的理解問題，從而得出更好的答案。&lt;/p&gt;

&lt;h1&gt;
  
  
  ReAct
&lt;/h1&gt;

&lt;p&gt;無論我們如何調整 prompt，但 LLM 仍然存在一個重大缺陷：它始終基於訓練時的數據集和 prompt 中提供的信息生成回答。如果提問有關天氣、新聞或內部文件等問題，LLM 將無法回答或會產生虛假回答（"幻覺"）。&lt;/p&gt;

&lt;p&gt;當然，我們可以在用戶提出問題後，先使用搜索引擎或內部資料庫來搜索問題，然後將搜索結果作為 prompt 的一部分。但在實際應用中，這種方法的效果並不總是好的，甚至在某些情況下會產生誤導。例如，如果我們想讓 LLM 幫助我們修改一篇文章，在這種流程下，程序會首先使用搜索引擎搜索文章，但很可能搜索不到相關結果。這樣一來，LLM 就會直接罷工，因為我們通常要求 LLM 僅根據 prompt 提供的信息生成內容，以確保准確性。&lt;/p&gt;

&lt;p&gt;為了解決這個問題，我們可以讓 LLM 自己決定是否要進行搜索，要搜索什麼內容，就和上一篇教程中提到的用 LLM 優化搜索語句一樣。於是 ReAct 系統就被提出來了，通過 prompt 中的示例告訴 LLM 可以使用哪些工具，然後讓 LLM 自己決定是否要使用這些工具，以及如何使用這些工具。 ReAct 總共有三個部分：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;思考：根據當前的信息思考需要做什麼動作。&lt;/li&gt;
&lt;li&gt;行動：根據思考結果做出相應的行動，如調用工具。程序可以分析這一步生成的字符串，來調用相應的工具，類似 Python 的 &lt;code&gt;eval&lt;/code&gt; 函數。&lt;/li&gt;
&lt;li&gt;觀察：存放行動的結果，如搜索的內容，以便下一次思考時使用。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;這三個步驟會不斷循環，直到思考步驟判斷已經找到答案，並在下面的行動中給出最終答案。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftj1jtuilgfyua9i3y1o0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftj1jtuilgfyua9i3y1o0.png" alt="Alt text" width="800" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;其實你仔細回想一下，這個過程和我們人類思考的過程是類似的。我們在思考時，會根據當前的信息判斷是否需要做出某些行動，然後根據行動的結果來判斷是否已經找到答案，如果沒有就繼續思考，直到找到答案為止。&lt;/p&gt;

&lt;p&gt;AutoGPT 的行為模式實際上和 ReAct 一樣，只是相比提出 ReAct 的論文中的步驟，AutoGPT 的 prompt 更加詳細，並且提供了更多的工具給 LLM 使用。但不管哪種方式，都需要編寫大量的示例來告訴 LLM 如何思考、行動，並且根據提供工具的不同，效果也會有所不同，並且 ReAct 的思考流程會有很多步驟。而 LangChain 幫我們把這些步驟都隱藏起來了，將這一系列動作都封裝成 “代理”，而這就是我們下一章要介紹的內容了。&lt;/p&gt;

</description>
    </item>
    <item>
      <title>用 LangChain 構建基於資料庫的問答機器人（二）：從資料源中提取文本信息</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Mon, 17 Jul 2023 23:00:00 +0000</pubDate>
      <link>https://dev.to/jambochen/yong-langchain-gou-jian-ji-yu-zi-liao-ku-de-wen-da-ji-qi-ren-er-cong-zi-liao-yuan-zhong-ti-qu-wen-ben-xin-xi-13ka</link>
      <guid>https://dev.to/jambochen/yong-langchain-gou-jian-ji-yu-zi-liao-ku-de-wen-da-ji-qi-ren-er-cong-zi-liao-yuan-zhong-ti-qu-wen-ben-xin-xi-13ka</guid>
      <description>&lt;p&gt;大家好，我是學生大使 Jambo。這個系列的目標是做出一個根據資料回答問題的機器人，那麼從資料源中提取文本信息就是一件必要的事。但我們的資料源格式是多樣的，比如 PDF、Word、HTML、PPT 等等，甚至有的資料源來自於網絡，這些格式都不能直接提取出文本，但好在 Python 有很多第三方庫可以幫助我們提取文本信息，並且 LangChain 也幫我們整合到了一起，我們只需要調用 LangChain 的接口就可以了。&lt;/p&gt;

&lt;p&gt;我在公開網絡上找到了一份 PDF 格式的合同，我們就以這份合同為例，介紹一下如何提取文本信息。&lt;/p&gt;

&lt;h1&gt;
  
  
  文本提取
&lt;/h1&gt;

&lt;p&gt;LangChain 針對 PDF 包含了許多第三方庫，比如 &lt;code&gt;PyPDF2&lt;/code&gt;、&lt;code&gt;PyPDFium2&lt;/code&gt;、&lt;code&gt;PDFMiner&lt;/code&gt; 等等，這裡我們以 &lt;code&gt;PyPDF2&lt;/code&gt; 為例，介紹一下如何提取文本信息。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pypdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;我們使用 &lt;code&gt;PyPDFLoader&lt;/code&gt; 來加載 PDF 文件，然後調用 &lt;code&gt;load&lt;/code&gt; 方法就可以得到文本信息了。 PDF 的讀取器會將 PDF 文件中的每一頁轉換成一段文本，然後將這些文本段組成一個列表返回。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.document_loaders&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PyPDFLoader&lt;/span&gt;
&lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PyPDFLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./contract.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  文本分割
&lt;/h1&gt;

&lt;p&gt;但僅僅是按頁進行分割，仍然是不夠的，因為一頁中仍然包含了太多的字數，如果將來將這些文本放在 prompt 的上下文中，會佔據大量的 token，而這些文本對於當下所問的問題不一定都是有用的，所以我們需要將這些文本再進行分割。&lt;/p&gt;

&lt;p&gt;LangChain 提供了幾種分割器，可以分割代碼、段落、Markdown 標題。這裡我們使用 &lt;code&gt;RecursiveCharacterTextSplitter&lt;/code&gt; ，他會把文本按照字符進行分割，直到每個文本段的長度足夠小。它默認的分割列表是 &lt;code&gt;["\n\n", "\n", " ", ""]&lt;/code&gt;，這樣它可以盡可能把段落、句子或單詞放在一起。&lt;/p&gt;

&lt;p&gt;其中 &lt;code&gt;chunk_size&lt;/code&gt; 限制了每段文本的最大長度。 &lt;code&gt;chunk_overlap&lt;/code&gt; 則是兩個文本段之間的重疊長度，如果一個段落實在太長而被強制分割成了兩段，那麼這兩段之間就會有一段重疊的文本，這樣可以保證上下文的連貫性。&lt;/p&gt;

&lt;p&gt;它還有個參數 &lt;code&gt;length_function&lt;/code&gt;，用來計算文本的長度，默認是 &lt;code&gt;len&lt;/code&gt;。如果你想按照 token 的數量來分割，那麼可以結合 &lt;code&gt;tiktoken&lt;/code&gt; 庫來使用。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.text_splitter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;

&lt;span class="n"&gt;text_splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text_splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這樣我們就很容易的得到了一個文本列表，而不用考慮具體的分割細節了。&lt;/p&gt;

&lt;p&gt;當然，這裡只是簡單的帶大家了解一下 LangChain 的文本提取功能，還有其他很多格式和對應的細節沒有介紹到，比如 Word、HTML、PPT 等等，如果你想了解更多，還是要自己去看看&lt;a href="https://python.langchain.com/docs/modules/data_connection/document_loaders/" rel="noopener noreferrer"&gt;官方的文檔&lt;/a&gt;。&lt;/p&gt;

</description>
    </item>
    <item>
      <title>我的微軟學生大使成長經歷- 從 Alpha 到 Beta 的升級經驗分享</title>
      <dc:creator>Jambo</dc:creator>
      <pubDate>Tue, 04 Jul 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/jambochen/wo-de-wei-ruan-xue-sheng-da-shi-cheng-chang-jing-li-cong-alpha-dao-beta-de-sheng-ji-jing-yan-fen-xiang-e3k</link>
      <guid>https://dev.to/jambochen/wo-de-wei-ruan-xue-sheng-da-shi-cheng-chang-jing-li-cong-alpha-dao-beta-de-sheng-ji-jing-yan-fen-xiang-e3k</guid>
      <description>&lt;p&gt;大家好，我是微軟學生大使 Jambo。前幾天，我從微軟學生大使 Alpha 升級到了 Beta，離最高的 Gold 又進了一步！我非常開心能在暑假到來前升級到微軟學生大使 Beta ，這不僅是對我過去幾個月努力的回報，更是對我未來的鼓勵。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyye09pz7juj47xwl72q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyye09pz7juj47xwl72q.png" alt="Alt text" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;回想起最初申請學生大使時，我對這個計劃的認識還很模糊，只是覺得這是一個很好的鍛煉機會，抱著試試看的心情申請，當然更多的也是被福利所吸引。而申請通過後，我才真正意識到這個計劃的意義和價值，它的核心並不只是技術，更多的是分享和傳播，是一種精神，是一種對技術的熱愛和對社區的貢獻。這不僅僅要求過硬的技術，還要能有足夠的社交能力。當然，這對我而言也是一種挑戰，因為我在此之前一直都是自己一個人埋頭學習，很少與人分享技術，更不用說參與社區、組織活動了。但是，我做到了，我參與了很多活動，分享了很多技術，認識了很多志同道合的朋友，這些都是我以前從未有過的經歷，我也從中學到了很多，收穫了很多，升級到 Beta 便是證明。&lt;/p&gt;

&lt;p&gt;為了讓更多的人了解微軟學生大使計劃，也是讓申請者們能夠少走彎路，接下來我將介紹下學生大使計劃和分享下我的經歷，也希望有更多學生大使能盡快升級。&lt;/p&gt;

&lt;p&gt;學生大使計劃由Microsoft發起，旨在培養和支持全球範圍內的年輕領導者。作為學生大使，我們有機會代表 Microsoft 在學校和社區中扮演積極的角色，與其他有著相同激情的學生共同學習、合作和成長。&lt;/p&gt;

&lt;p&gt;學生大使分為 4 個階段：New、Alpha、Beta 和 Gold，每個階段都有對應的福利和要求。當然，除了學生大使的福利以外，你還能用 edu 郵箱去申請 Azure、Github 的學生福利。&lt;/p&gt;

&lt;p&gt;當你成功申請成為學生大使並成功入職後，便會進入 New 階段，同時你也能&lt;strong&gt;獲得 Microsoft 365 作為福利&lt;/strong&gt;。在這個階段，你只需要完成一些很簡單的任務，比如完成一個 Microsoft Learn 路徑，就能進入到下一階段。&lt;/p&gt;

&lt;p&gt;在晉升到 Alpha 後，你將獲得 &lt;strong&gt;Visual Studio 企業版訂閱，以及每月 150 美元的 Azure 額度等&lt;/strong&gt;福利。而想要升級到 Beta，你需要完成一些更具挑戰性的任務，比如舉辦活動，以證明你的影響力。在升級到 Beta 後，你將收到 Microsoft 的 &lt;strong&gt;Beta 禮品包&lt;/strong&gt;。最後，如果你想要升級到 Gold，你需要保持活躍度，舉辦更多的活動，進行更多的社區貢獻。 Gold 的申請是在每年的 12 月和 6 月進行審查，如果你成功升級到 Gold，你將獲得更多的福利，比如 Gold 禮品包、微軟最有價值專家提名等。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famay6mq28rqkko1dsra1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famay6mq28rqkko1dsra1.png" alt="Alt text" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我最初是在某個技術交流群中看到有人分享學生大使計劃的鏈接，我就點進行想說看看，結果發現福利還不錯，便想說申請看看。但是發現申請還蠻複雜，除了個人信息外，還要寫英語小作文介紹自己的經歷，以及參與技術社區的經歷，並且還要錄一段視頻。我看到這裡確實是有放棄的想法，因為我此前參與的活動確實不多，並且我非常不擅長寫作。但那些福利又讓我心動，再加上我認為有這一經歷對我以後的發展也是有好處的，便決定硬著頭皮試一下。&lt;/p&gt;

&lt;p&gt;雖然我英文也非常糟糕，但這部分&lt;strong&gt;完全可以藉助翻譯軟件&lt;/strong&gt;，視頻也允許用母語，只是要加英文字幕而已，因此對我而言，最大的難點就在於寫作了。此前我有參加的活動，僅僅是學校的幾個比賽，以及在 Nvidia 舉辦的活動裡幫忙回答其他選手的問題，雖然也有寫一些博客，但也只是簡單的記錄而已。我認為這些經歷微不足道，但我還是把它們添油加醋的寫了出來。而視頻的內容，也是把小作文的內容再提煉整理了一下，然後用中文錄製，最後加上英文字幕，當然，我也錄製了好幾遍才錄出了相對流暢的版本。我將這些內容提交後，便等待結果了。&lt;/p&gt;

&lt;p&gt;1 月多的時候，我收到了申請通過的郵件，我有些意外但也很開心。我照著郵件裡的鏈接進行入職程序，然後便進入了 New 階段，也收到了學生大使的賬號。在這個階段，我需要完成一個 Microsoft Learn 路徑，只需要幾個小時就能完成，沒過幾天我便自動升級到 Alpha 了。&lt;/p&gt;

&lt;p&gt;在 Alpha 階段，我獲得了 Visual Studio 企業版訂閱，以及每月 150 美元的 Azure 額度等福利。我也開始了解微軟提供的一些服務，比如 Github Codespaces、Azure ML 等，並將我的收穫寫成教程發在 &lt;a href="https://blog.csdn.net/chenjambo" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt; 和 &lt;a href="https://dev.to/jambochen"&gt;Dev.To&lt;/a&gt; 上。同時，我也了解到微軟有推出 Azure OpenAI Service，但需要填表申請才能使用，於是我便用 edu 郵箱進行申請，沒想到很快就通過了。我也將這個過程寫成了教程，當然也包括申請後的使用教程，做成了一個 Azure OpenAI Service 的系列：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/jambochen/xue-sheng-dang-kuai-lai-azure-qi-xue-xi-openai--1pe6"&gt;同學們，快來Azure 一起學習 OpenAI（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jambochen/lai-azure-xue-xi-openai-er-huan-jing-gou-jian-3m05"&gt;來 Azure 學習 OpenAI 二 - 環境構建&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jambochen/lai-azure-xue-xi-openai-san-yong-python-diao-yong-azure-openai-api-5ddn"&gt;來 Azure 學習 OpenAI 三 - 用 Python 調用 Azure OpenAi API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jambochen/lai-azure-xue-xi-openai-si-yong-embedding-jia-qiang-gpt-462i"&gt;來 Azure 學習 OpenAI 四 - 用 Embedding 加強 GPT&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;但是僅僅是寫教程是不夠的，升級到 Beta 的要求是能夠舉辦活動，需要 15 人參與，並且至少要有 20 分鐘時間是專注於分享微軟相關的技術。可以是線上或者線下，但一定要是學生大使自己舉辦的，並且一次活動只能作為一位學生大使的申請條件。如果你舉辦了活動，那麼就可以到&lt;a href="https://studentambassadors.microsoft.com/studentambassadors/account/" rel="noopener noreferrer"&gt;學生大使的頁面&lt;/a&gt;，在 "Events" 中的 "Add Events" 進行申請。在申請時，你需要填寫活動的信息，以及參與的人數，以及活動的照片或者視頻。如果你的活動通過審核，那麼你就可以升級到 Beta 了。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6cz71dvudsjw3bf0rryl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6cz71dvudsjw3bf0rryl.png" alt="Alt text" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;舉辦活動的要求對我一個社恐是十分困難的。我也曾想過舉辦線上活動，但是我認為我沒有足夠的影響力，而且我也不知道該如何宣傳，因此我便放棄了這個想法。但後來我在某個技術群看到微軟的 Reactor 活動，那是個線上活動，以直播的方式分享技術。我也認為我在 Alpha 待了這麼久，也該做點什麼了，於是我硬著頭皮到微軟 Reactor 上直播分享，而分享的內容正是我之前寫的 Azure OpenAI Service 的教程。雖然我在直播前很緊張，但還是勉強完成了分享，而且也有人參與了我的直播。雖然我只是參加微軟的活動，但我作為演講者也應該能算是“舉辦”吧。雖然不太確定，但我還是將這個活動的信息、錄像都提交了上去，沒想到 2 天后我就收到了晉升 Beta 的郵件。這對我而言是個驚喜，也是鼓舞，也讓我對沖擊 Gold 有了信心。&lt;/p&gt;

&lt;p&gt;（如果有人想看我的直播回放，可以點擊以下圖片↓）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bilibili.com/video/BV1Uo4y1b7GE/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa99aee6s6o2nu68w4vuh.png" alt="Alt text" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我分享這些不僅僅是對我前一階段的總結，更是學生大使這條路的開門磚，希望有更多大中華地區的同學加入到微軟學生大使的行列，也期待有更多人加入到微軟學生大使計劃，申請鏈接在&lt;a href="https://studentambassadors.microsoft.com/" rel="noopener noreferrer"&gt;這裡&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;與我相關的鏈接：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Github：&lt;a href="https://github.com/JamboChen" rel="noopener noreferrer"&gt;https://github.com/JamboChen&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CSDN：&lt;a href="https://blog.csdn.net/chenjambo" rel="noopener noreferrer"&gt;https://blog.csdn.net/chenjambo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dev.To：&lt;a href="https://dev.to/jambochen"&gt;https://dev.to/jambochen&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
