---
title: "Kobako：Handle 機制"
date: 2026-10-14T00:00:00+08:00
publishDate: 2026-10-14T00:00:00+08:00
lastmod: 2026-10-12T21:44:58+08:00
tags: ["LLM","AI","經驗","Ruby","WebAssembly","Gem","mruby"]
series: "kobako"
toc: true
permalink: "https://blog.aotoki.me/posts/2026/10/14/kobako-handle-mechanism/"
language: "zh-tw"
---


在 [Kobako：執行腳本](https://blog.aotoki.me/posts/2026/09/23/kobako-script-execution/)已經看過一個完整的運作是怎樣設計的，但是現有的設計還有一些不方便的地方，當我們想將一個 Ruby 的物件在沙盒（Sandbox）中呼叫時，卻很難直接使用到那些回傳值。


<!--more-->

## 綁定的限制{#binding-limitation}

舉例來說，我們希望讓 Agent（代理）使用 [Code Mode](https://developers.cloudflare.com/agents/tools/codemode/) 來直接對 Rails 的資料庫查詢，可能會像這樣實作。

```ruby
s = Sandbox.new
s.bind("User", User)

# Agent's tool
s.eval("User.find_by(email: 'user@example.com')")
# => ?
```

我們會發現，這個設計無法被實作，因為 `User.find_by` 會回傳一個 User 物件，而沙盒內的 mruby 不知道 `User` 是什麼，因此無法建立，也就無法回傳。

這是因為 MessagePack 只支援基本的數值類型，如果要把每一種物件都覆蓋幾乎不可能，作為替代通常會嘗試 `#to_msgpack` 看看是否有定義如何轉換成 Hash、Number、String 這種基本的數值，但我們在宿主（Host）和客體（Guest）之間就會花費大量力氣在轉換資料。

而且很可能不必要，因為 User 物件是傳進去再傳出來，這樣是很浪費的。

## 指標機制{#pointer}

要解決這個問題，已經有現成的機制可以參考，那就是「指標」的概念，在軟體中許多機制就是靠「指向記憶體某個位置」來運作的，這剛好很適合我們的需要。

在 Kobako 的設計，當我們要傳入客體給 mruby 使用時，會先檢查是不是能轉換的物件，如果不能轉換就會向 Kobako 申請一個編號，把傳入的物件轉為 `Kobako::Handle` 物件。

```ruby
def deep_wrap(value, handle_table)
  case value
  when ::Array then value.map { |element| Utils.deep_wrap(element, handle_table) }
  when ::Hash  then value.transform_values { |val| Utils.deep_wrap(val, handle_table) }
  else
    wire_representable?(value) ? value : Kobako::Handle.new(handle_table.alloc(value))
  end
end
```

因為物件也可能躲在 Array 或者 Hash 中，因此會做一個遞迴的檢查，把所有物件確認清楚後包裝成一個可以被 MessagePack 完整解析的物件。

這裡借用了 MessagePack 的「擴充（Extension）」功能，Kobako 會告知 MessagePack 遇到 `Kobako::Handle` 物件時，只需要傳遞編號即可。

如此一來，原本的 User 物件，就會變成一個 `#<Handle id=1>` 被傳入，當回傳值看到 `#<Handle id=1>` 時，只要再詢問 Kobako 編號 1 的物件是什麼，我們就能拿回 User 這個物件。

## 容易溝通{#communication-easier}

這樣的設計，是透過 Kobako 維護兩張表格達成的，透過這兩張表格不論是在宿主還是客體，都能很簡單的進行溝通跟互動。

綁定（Binding）用於把已知的物件提供到客體裡面，可以使用宿主的功能。

| Binding       | Class       |
| ------------- | ----------- |
| `"User"`      | `User`      |
| `"Workspace"` | `Workspace` |

句柄（Handle）機制則提供回傳數值，可以不受限於已知的綁定，而且是在不透明的前提下提供。

| Handle | Object              |
| ------ | ------------------- |
| 1      | `#<User id=1>`      |
| 2      | `#<Workspace id=5>` |

不論是綁定還是句柄，都具備了 Kobako 設計的 `#method_missing` 的機制，因此都能夠傳回給宿主，呼叫宿主的能力，也因此組合起來後，沙盒內能做的事情就被很大的拓展開來。

```ruby
s = Sandbox.new
s.bind("User", User)

s.eval("User.find_by(email: 'user@example.com')")
# => #<User id=1>

s.eval("User.where(role: :admin).pluck(:id)")
# => [1, 5, 9]
```

就算是 ActiveRecord 的 Method Chaining（方法鏈）也能夠很輕鬆的使用，不過這也變成 Kobako 的一大挑戰，等我們把大多數功能釐清後，再回來看這個機制，就會發現越好用的機制同時也越危險。
