iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
AI Engineering

30天拆Agent:從Repo看設計系列 第 23 篇

Day 23|OpenDots:讓 AI 助理整理文件,也能操作獨立的瀏覽器與執行環境

  • 分享至 

  • xImage
  •  

OpenDots 是 CopilotKit 開源的一套 AI 助理網站。部署後,你可以透過網頁建立不同職責的助理,請它們研究資料、撰寫內容,並把成果保存成可繼續編輯的文件。需要操作網站或執行程式時,也能替助理啟用獨立的 Docker 容器。

例如,先建立一位研究助理 Scout,請它蒐集資料;等你審閱報告後,再把內容存進「技術研究」這個文件區。接著,切換到寫作助理 Writer,請它讀取報告並整理成文章。

OpenDots 把助理稱為 Dot,文件區稱為 Space,助理操作瀏覽器、檔案與命令的容器稱為 Computer。

要理解它怎麼使用,可以先分清楚三種操作:

事情 在哪裡設定 什麼時候做
模型、對話服務、Docker 管理服務的連線 伺服器的 .env 部署時設定,修改後重啟應用程式
Dot 名稱、角色、Space 權限、記憶開關、Computer 權限 OpenDots 網頁上的表單與面板 部署後隨時調整
研究什麼、文章怎麼寫、是否先審閱 對話框中的自然語言 每次交付任務時

接下來就沿著這個順序,從啟動網站走到完成一份報告。

一、先啟動網站,再連接 Agent 模型與對話服務

OpenDots 包含 React 前端與 Node.js 後端。依官方本機開發方式,準備 Node.js 24,下載專案後執行:

git clone https://github.com/CopilotKit/OpenDots.git
cd OpenDots
npm ci
cp .env.example .env
npm run dev

接著開啟 http://127.0.0.1:5173,就會看到 OpenDots 網頁。這個網頁本身就提供管理 Dot、Space、偏好與 Computer 的介面。

即使還沒接上模型與對話服務,你仍可建立 Space、編輯文件、設定 Dot;要開始與助理對話,才需要完成下面兩項連線。

第一項:設定 Agent 使用的 LLM

在伺服器的 .env 設定:

OPENAI_API_KEY=<模型服務的API Key>
OPENAI_MODEL=<模型名稱>
OPENAI_BASE_URL=<模型服務的API位址>

雖然變數名稱以 OPENAI_ 開頭,程式實際使用的是 OpenAI-compatible Chat Completions 介接方式。若自己的 LLM 服務提供相容 API,並支援所需的串流與工具呼叫,可以調整上述設定;若介面不相容,則需要修改模型轉接程式。

目前各個 Dot 共用這組模型設定。建立 Scout 或 Writer,主要是在改角色指示與權限,沒有在 Dot 表單裡替每位助理選不同模型。

第二項:連接 CopilotKit Intelligence

CopilotKit Intelligence 是獨立的後端服務。在 OpenDots 中,它主要負責保存對話訊息、工具事件,以及讓前端重新連線後載入歷史內容。平台另外提供 Channels、Automatic Learning 等能力。

把相關元件放在一起看,分工會比較清楚:

元件 在 OpenDots 中負責什麼
你設定的 LLM 理解要求、產生回答、提出工具呼叫
OpenDots 的 Agent 程式與 TanStack AI 組合角色指示、執行工具、把結果交回模型
CopilotKit Runtime 接收前端請求,銜接 Agent 與事件傳遞
CopilotKit Intelligence 保存與重播對話事件、提供對話管理及同步能力
OpenDots 的 SQLite 保存 Dot 設定、文件、權限與手動加入的偏好

CopilotKit Intelligence 並不是 OpenDots MIT 原始碼的一部分。 OpenDots 本身雖然是 MIT 開源,但目前的聊天、Thread 保存與重新連線流程直接整合 Intelligence。開發測試可以使用 CopilotKit 提供的免費 cloud-hosted Developer 方案;若要把 Intelligence 正式部署在自己的 VPC 或 Kubernetes,則需要 Team self-hosted 或 Enterprise 授權。若要替換它,需要修改程式,接管對話建立、歷史載入、事件保存與前端恢復等功能,也要調整目前直接呼叫 Intelligence 的地方。

「使用者偏好」,本來就存放在 OpenDots 的 SQLite。跟 Intelligence 無關

二、在網頁建立 Dot,設定它能讀什麼、記得什麼

https://ithelp.ithome.com.tw/upload/images/20261006/20178568lfRTIftGjy.png

建立 Space,再建立助理

先在GUI左側 SPACES 標題旁按下「+」,建立一個名為「技術研究」的 Space。

Space 是 OpenDots 裡的文件集合。它的頁面與內文存在資料庫,不會在作業系統中自動建立同名資料夾。

接著,在左側 DOTS 標題旁按下「+」,建立 Scout。表單裡可以設定:

表單欄位 這次範例填什麼
Name Scout
Role instructions 負責研究技術專案,整理架構與限制,附上資料來源
Space access 勾選「技術研究」
Default destination for saved pages 選擇「技術研究」
Public-page research 勾選,允許研究公開網頁
Use saved memories 視需求勾選,允許使用已保存的偏好

按下 Save 後,前端把表單送到後端,後端產生 Dot ID,將名稱、角色與開關存入 SQLite 的 dots 表;Space 存取關聯則記錄在 dot_spaces 表。

這是部署後的網頁操作。建立第二位助理時,不必修改部署檔或重新啟動服務。日後要調整設定,可以點選 Dot 名稱旁的「⋯」。

Writer 如何取得同一個 Space 的權限?

用同樣方式建立 Writer,在它的 Space access 裡勾選「技術研究」即可。

後端會保存類似下面的關聯:

Dot 授權存取的 Space
Scout 的 ID 技術研究的 ID
Writer 的 ID 技術研究的 ID

之後,Writer 讀寫這個 Space 的文件時,後端會檢查這筆授權。

角色指示裡可以寫「你負責閱讀研究報告」,但真正讓它取得文件存取權的,是表單中的 Space 勾選與後端保存的授權資料。

使用者偏好是在 Memories 頁面手動加入

假設希望兩位助理都使用繁體中文,可以:

  1. 點左側 Memories。
  2. 按 Add memory。
  3. 在 Preference or context 輸入「請使用繁體中文,研究報告需要附上來源」。
  4. 按 Save。

這裡輸入的是自然語言文字,但保存流程很直接:前端送出文字,後端檢查長度,再保存到 SQLite 的:

memories(id, text, createdAt)

目前這個功能沒有從聊天自動辨識偏好、抽取欄位,再寫入記憶的步驟。 在對話中說「請記住我偏好繁體中文」,也不能直接當作已經建立了一筆長期偏好。

要讓 Dot 使用這些文字,需確認兩個地方都有勾選 Use saved memories:

  • 左側 Settings & setup 的全域設定。
  • 該 Dot 的設定表單。

下一輪執行時,後端確認兩個開關都允許,才會讀取已保存的偏好,加入 Agent 的提示詞。這份偏好目前沒有依 Dot 分開,所以 Scout 和 Writer 都開啟權限時,會使用同一份偏好資料。

介面另外提供的 Automatic Learning 是另一套功能,需要另外配置 Intelligence;它和這個手動輸入偏好的 Memories 功能應分開理解。

三、從一句研究要求,走到一份保存的文件

完成設定後,選擇 Scout,在對話框輸入:

請研究這個 repo 的架構與限制,整理成附來源的報告。先讓我審閱,核准後再存進「技術研究」。

這裡的「先審閱,再保存」就是使用者以自然語言提出的要求,沒有另外的部署參數。

後端如何知道這次使用 Scout?

前端已知道使用者選的是哪個 Dot,請求會帶上 Dot ID 與對話 ID。

後端提供給 Runtime 的 Agent 清單,是從 SQLite 中的 Dot 設定建立的。Runtime 依 Dot ID 選擇對應的 Agent;Agent 再讀取 Scout 的角色、偏好與授權資料,開始執行。

模型不需要猜測「Scout 是誰」,也不會因為聊天文字出現 Writer,就自動切換到另一位助理。前端與後端之間,則透過 CopilotKit 整合的 AG-UI 事件傳遞訊息與工具結果。

「先審閱」如何變成一張可以按核准的卡片?

https://ithelp.ithome.com.tw/upload/images/20261006/20178568nV8Tykmsc6.png

OpenDots 預先實作了一個文件審閱工具,負責把模型提出的標題、內文與目標 Space,呈現成前端卡片。

Agent 的系統提示詞會要求模型:使用者提出先審閱的需求時,呼叫這個工具,並等待結果。

這裡可以分成兩部分:

  • 模型判斷:理解自然語言要求,選擇審閱工具,提出草稿。
  • 應用程式執行:顯示草稿、接收按鈕操作、保存文件、回傳結果。

使用者按下 Approve & save 後,前端先把卡片上的草稿送到 OpenDots 後端。後端保存成功,前端才把頁面資訊交回 Agent,讓它繼續回答。

這樣保存的就是使用者核准的草稿,不必再請模型重新生成一次內文。後端還會用「對話 ID+工具呼叫 ID」記錄這次保存,讓同一份草稿在網路失敗後重試時,能找回已建立的頁面,避免重複存檔。

但目前也有直接建立文件的工具。因此,「請先審閱」包含模型遵循提示詞的部分,不能視為所有文件寫入都必經的強制關卡。若產品要求每次寫入都必須核准,需要再修改後端的寫入規則。

文件實際存在哪裡?Schema 長什麼樣子?

OpenDots 預設使用應用程式工作目錄下的:

data/opendots.sqlite

可以透過 .env 的 DATABASE_PATH 改變位置。Dot 設定、Space、文件與手動偏好,都使用這個 SQLite 檔案。[資料庫路徑設定]

文件保存在 pages 表,建立表格的結構如下,僅調整排版:

CREATE TABLE IF NOT EXISTS pages (
  id             TEXT PRIMARY KEY,
  spaceId        TEXT NOT NULL,
  parentId       TEXT,
  title          TEXT NOT NULL,
  content        TEXT NOT NULL,
  revision       INTEGER NOT NULL,
  createdAt      INTEGER NOT NULL,
  updatedAt      INTEGER NOT NULL,
  sourceThreadId TEXT
);

其中:

  • spaceId:文件屬於哪個 Space。
  • parentId:父頁面的 ID,用來組織子頁面。
  • content:文件內文,介面提供視覺編輯與 Markdown 原始內容編輯。
  • revision:版本號,用於拒絕拿舊版本覆蓋新內容的寫入。
  • sourceThreadId:文件來源對話的 ID,可為空。

因此,在 Space 裡編輯報告,主要是在更新資料庫中的頁面紀錄。文件 Schema 與版本檢查

同一份文件,如何保留不同 Dot 的對話?

文件旁的聊天使用另一張 SQLite 表 page_threads 保存對應關係,主要欄位是:

pageId、dotId、threadId、ready、leaseUntil

其中 (pageId, dotId) 是複合主鍵。以下用簡化 ID 示意:

pageId dotId threadId
report-1 scout-1 thread-a
report-1 writer-1 thread-b

當你在同一份報告旁選擇 Scout,後端查找或建立 thread-a;改成 Writer,則使用 thread-b。SQLite 保存這些 ID 的關係,完整對話內容仍由 Intelligence 保存。

ready 與 leaseUntil 用來協調對話建立狀態,降低重複建立的問題;它們不是文件內文。

所以,讓 Writer 接手的方法很具體:先確認它有「技術研究」的權限,再開啟已保存的報告,選擇 Writer,請它依據文件改寫。兩位助理可以讀同一份成果,但不會自動共享彼此的完整聊天紀錄。

四、需要操作網站或執行命令時,再啟用 Computer

前面的研究與文件整理,可以先使用公開網頁研究工具完成。

如果任務需要登入網站、互動式瀏覽器操作,或在 Linux 環境處理檔案與執行命令,就需要啟用 Computer。

OpenDots 的 Computer 是每個 Dot 對應的一個 Docker 容器。Agent 程式仍在 OpenDots 後端執行,瀏覽器、檔案與命令操作則發生在容器裡。

部署時先準備 supervisor

Supervisor 是管理這些容器的後端服務。它接收「替這個 Dot 準備 Computer」的請求,再操作 Docker。

沿用前面「OpenDots 在本機以 Node.js 執行」的方式,在 .env 加入:

COMPUTER_SUPERVISOR_URL=http://127.0.0.1:4312
COMPUTER_SUPERVISOR_TOKEN=<第一組隨機密鑰>
COMPUTER_TOKEN=<第二組不同的隨機密鑰>
COMPUTER_NAMESPACE=opendots
COMPUTER_MEMORY_BYTES=2147483648

兩組密鑰各至少 24 個字元。可以在本機分別執行兩次:

openssl rand -hex 32

每次會產生一組 64 個十六進位字元,將兩次結果分別填入上述欄位。

接著執行:

docker compose -f compose.computers.yml build computer-image computer-supervisor
docker compose -f compose.computers.yml up -d computer-supervisor

第一行建立映像檔,第二行只啟動 supervisor。各 Dot 的 Computer 還沒建立。

這兩行不會額外啟動一個 Agent 管理 GUI。 管理介面就是前面已啟動的 OpenDots 網頁;修改 .env 後,需重新啟動 npm run dev,讓後端讀取 Computer 連線設定。

在網頁授權,再按 Start computer

回到 OpenDots 的 Computer 面板,確認 Computer for 選的是 Scout,展開 Computer settings → Computer permissions:

  • Enable this computer:啟用這個 Dot 的 Computer。
  • Browser:允許瀏覽器操作。
  • Workspace files:允許工作檔案操作。
  • Terminal commands:允許執行命令。

這些權限預設關閉,勾選後會保存到 SQLite 的 computer_permissions 表,以 Dot ID 對應一組設定。

再按 Start computer,才會向 supervisor 發出啟動請求。

目前 Agent 可用的 Computer 工具包含瀏覽器、檔案與命令操作,但沒有修改權限或啟動容器的工具。因此,直接在聊天說「幫我啟動 Computer」,不會取代這個面板操作。容器啟動並取得授權後,才可以用自然語言要求 Scout「打開這個網站」或「在工作目錄建立檔案」。

Supervisor 如何知道要啟動哪個容器?

建立 Dot 時,只會在 OpenDots 的 SQLite 建立設定,不會同步替它建立 Computer,也不需要先到 supervisor 登錄一份角色設定。

按下 Start computer 後,流程是:

  1. 前端把選定的 Dot ID 送到 OpenDots 後端。
  2. 後端確認 Dot 存在,而且 Computer 已被使用者啟用。
  3. 後端呼叫 supervisor 的 POST /computers/{Dot ID}/ensure。
  4. Supervisor 使用部署時指定的 Computer 映像檔,依 namespace 與 Dot ID 決定容器名稱。
  5. 若容器不存在,就建立容器與 volumes;若已存在且配置仍適用,就重用並視需要啟動。
  6. 等服務可回應後,回傳容器位址,供 OpenDots 後端操作。

例如,namespace 是 opendots,Dot ID 是 abc123,名稱就會是:

容器:opendots-computer-abc123
瀏覽器資料:opendots-profile-abc123
工作檔案:opendots-workspace-abc123

Supervisor 不需要知道 Scout 的角色提示詞,也不需要讀取 OpenDots 的 SQLite。它收到 Dot ID 後,依固定規則與部署設定建立環境,並透過 Docker labels 辨識自己管理的容器。

兩组 token 分別用在哪裡?

第一組 COMPUTER_SUPERVISOR_TOKEN 用於 OpenDots 後端 → supervisor。

Compose 會把同一組值交给 supervisor,後端呼叫它時放進 Authorization: Bearer …。Supervisor 比對成功,才接受啟動、停止等管理請求。

第二組 COMPUTER_TOKEN 是用來衍生 每個 Dot 的 Computer 操作憑證。計算方式可簡化表示為:

Dot 的操作 token =
HMAC-SHA256(
  主密鑰,
  "opendots-computer:" + Dot ID
)

實際運作時:

  1. OpenDots 後端與 supervisor 持有相同的主密鑰。
  2. Supervisor 建立 Scout 的容器時,先算出 Scout 專用的 token,再把它放進該容器。
  3. 後端要操作 Scout 的瀏覽器或檔案時,用相同算法算出 token,隨 HTTP 請求送出。
  4. Scout 的 Computer 驗證這個 token,再接受操作。

Writer 有不同的 Dot ID,因而得到不同的操作 token。相同主密鑰與相同 Dot ID 會得到相同結果,不必手動替每位助理配置一組密鑰;主密鑰也不會直接交給各個 Computer。

OpenDots 在建置 supervisor 時,加入了這段每個 Dot 各自衍生憑證的修改。

檔案保存與資源配置

各 Computer 的工作檔案與瀏覽器設定檔使用獨立的 Docker volumes。COMPUTER_MEMORY_BYTES=2147483648 表示每個 Computer 的容器記憶體上限為 2 GiB。這是執行資源,與 Memories 裡的使用者偏好無關。多個 Computer 同時運作時,主機仍需有足夠的 RAM、CPU 與磁碟空間。

在提供的部署配置中,只有 supervisor 取得 Docker socket。各 Dot 的命令在自己的容器內執行,並不直接落到 OpenDots 主機上;預設使用一般 Docker 隔離,容器仍共用主機核心。

五、什麼情況適合用 OpenDots?

讀到這裡,可以把 OpenDots 的使用方式整理成一條完整路徑:

部署網站並接好模型與 Intelligence → 在網頁建立 Dot 和 Space → 勾選權限、加入偏好 → 用自然語言交付任務 → 審閱並保存文件 → 必要時再啟用 Computer。

如果需要的是個人研究、寫作與文件整理,這套流程已提供可修改的起點。你可以先用 Scout 完成研究,再手動切換 Writer 接續處理。

目前主要以單一擁有者使用為前提,多人共同編輯、多個 Dot 群聊與自動委派工作仍需要擴充。兩位 Dot 取得同一個 Space 的權限,表示它們可以讀寫同一組文件,不代表已建立自動協作流程。

與 OpenBot、OpenMuse 的差別:同一個底座,解不同問題

OpenDots、OpenBot 與 OpenMuse 都是 CopilotKit 公開的 MIT 開源專案,但它們並不是三套完全獨立的 Agent 平台。

三者都使用 CopilotKit Runtime、AG-UI,並把 CopilotKit Intelligence 當成 Conversation / Thread 的持久化服務。也就是說,GitHub 上的應用程式雖然開源,完整的對話功能仍依賴另一個獨立的 Intelligence 服務。

專案 主要著重的情境 例子
OpenDots 不同職責的助理,加上研究、審閱與文件管理 研究專案、保存報告,再請另一位助理改寫文章
OpenBot 公司內部的 Agent 整合、工具權限與操作紀錄 接入既有 Agent,操作內部系統,集中管理允許與拒絕的操作
OpenMuse 跨手機與網頁的個人事務處理、任務追蹤 處理郵件、行事曆、PDF 表單,或追蹤指定網頁的變化

共同的對話層則大致是:

OpenDots / OpenBot / OpenMuse
            ↓
CopilotKit Runtime / AG-UI
            ↓
CopilotKit Intelligence
            ↓
Threads / Conversation Persistence

因此,這三個 Repo 更適合看成 CopilotKit 在不同產品情境下的三套開源參考應用。

這也帶來一個部署上的限制:應用程式本身雖然採 MIT License,但 CopilotKit Intelligence 是另一個服務,不包含在這些 Repo 的 MIT 授權中。

OpenDots 另外重用了 OpenBot 的 Computer 與 supervisor,而 OpenMuse、OpenBot 也有部分架構與整合概念可以互相參考。因此三者真正值得比較的,是它們如何在同一套 CopilotKit 基礎設施上,分別處理文件協作、企業治理與個人自動化。

References


上一篇
Day 22|Rakazo:Agent 工作到一半,電腦、登入狀態與權限怎麼留下來?
系列文
30天拆Agent:從Repo看設計 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言