---
title: "Kobako：從 Ruby 到 mruby"
date: 2026-08-05T00:00:00+08:00
publishDate: 2026-08-05T00:00:00+08:00
lastmod: 2026-08-03T21:29:50+08:00
tags: ["LLM","AI","經驗","Ruby","WebAssembly","Gem","mruby"]
series: "kobako"
toc: true
permalink: "https://blog.aotoki.me/posts/2026/08/05/kobako-from-ruby-to-mruby/"
language: "zh-tw"
---


雖然是受到 ruby.wasm 的限制才選擇 mruby 來作為沙盒（Sandbox）的語言，但也不代表跟 CRuby 都使用相同的語言標準（[ISO/IEC 30170:2012](https://www.iso.org/standard/59579.html)）就讓這個目標輕鬆實現，相比 CRuby 在 mruby 上有很多限制存在。

這是 mruby 為了能夠在嵌入式系統這類環境運作必要的取捨，相對的輕量的特性剛好也是一種 Embedded Sandbox 的優勢，我們把 Ruby 語言沙盒嵌入到任何語言的這種想法，變成可行的選項。

<!--more-->

## Marshal

[dRuby](https://github.com/ruby/drb) 之所以能利用非常少量的程式碼實作 RPC（Remote Procedure Call）是善用了許多語言特性，我在[上一篇文章](https://blog.aotoki.me/posts/2026/07/29/kobako-building-ai-era-sandbox/)提過，利用 `method_missing` 的機制可以在遠端沒有定義方法的前提下，正常呼叫。

當遇到資料傳輸時，就輪到 Ruby 的 [Marshal](https://docs.ruby-lang.org/en/master/language/marshal_rdoc.html) 機制出場，當我們需要傳輸一個物件時，只需要使用 `.dump` 方法就可以轉換成一個二進位的資料。

```ruby
args = [1, 2, 3]
bin = Marshal.dump(args) # => \x00 ...
```

基於這樣的特性，原本要呼叫遠端的方法會需要的參數，就能夠被傳遞到遠端。

```ruby
class Proxy
  def method_missing(method, *args, **kwargs)
     raise if @target.respond_to(method)
     
     # @server.send(method, *args, **kwargs)
     bin = Marshal.dump([method, args, kwargs])
     @server.write(bin)
     res = Marshal.load(@server.read)
  end
end
```

這樣就讓整個傳輸都解決了，換到 Ruby 和 mruby 之間的溝通，直覺會覺得像這樣，應該是很單純用 `Marshal` 來解決就好，但現實不會這麼順利。

mruby 沒有原生的 Marshal 機制，而且這個機制很可能因為 CRuby 和 mruby 在 C 語言層級的記憶體結構不同，不一定能直接使用。

> 大部分語言都有類似的標準函式庫，像是 Golang 的 [gob](https://pkg.go.dev/encoding/gob) 或者 Python 的 [pickle](https://docs.python.org/zh-tw/3/library/pickle.html) 可以使用

## JSON

既然 Ruby 原生的 Marshal 無法使用，我們只需要找到類似的機制替代，使用 JSON 很可能就是一個非常直覺的做法。

在大多數語言中，對 JSON 的處理已經非常成熟，而且 mruby 也有 [mruby-json](https://github.com/mattn/mruby-json) 這樣的 Gem 存在，理論上可以很簡單的在 Ruby 使用 `#to_json` 並且在 mruby 用 `JSON.parse` 就順利的還原回來。

將 JSON 應用在 RPC 也不是沒有先例，像是 JSON-RPC 就是一個例子，在不同語言的支持度 JSON 都是相當不錯的，未來想要跨語言也沒有問題。

然而 JSON 也有著一些限制，像是在這幾年為了讓 AI 的 Token 效率更好，大家開始想找其他格式表示，就是因為 JSON 在可讀性上的優勢，是用空間來換的，跟其他同類型工具，通常會佔用更多空間（如：`"` 包覆表示字串等等）

也因為這種結構的特性，在處理上通常也會比較慢，像是要確認 `"` 到下一個 `"` 的位置，才能區分是否為字串，如果使用二進位表示，通常只需要 `[type][len][data]` 兩個位元標記搭配資料，就不需要多記憶一個 `"` 開啟的旗標，也不一定需要回退來確認。

至少，設計初期或者考慮到通用性 JSON 是很不錯的，但是 Kobako 的定位在更底層一些，有其他選擇。

## MessagePack

[MessagePack](https://msgpack.org/) 在介紹中直接明講是一種「類似 JSON」的格式，但是更小也更快，他是使用了上面我提到的 `[type][len][data]` 的結構來處理，光是省下大量的 `{` 和 `"` 這類符號，我們就可以節省不少傳輸、編碼、解碼的時間。

然而，在 Kobako 的選項中，如果把 JSON 排除掉幾乎也只剩下 MessagePack 可以選，原因是更快的 [Protobuf](https://github.com/protocolbuffers/protobuf) 是因為有 Schema（樣式）的約定，因此連 `[type]` 都不需要，只要知道是屬於什麼的資料，就一定是特定的方式讀取，那就可以更快。

Ruby 和 mruby 都具備直譯（Interpreter）的能力，因此我們的程式在執行階段可以任意地改變資料結構，如果用 Protobuf 的方式設計，等於要求使用者每次都先自己編譯 Ruby Gem 和搭配的 mruby 才能確定如何溝通，這對 Ruby 或者 JavaScript 這類型語言的優勢來說，直接變成缺點。

也因此，在「速度」跟「彈性」之間，選擇 MessagePack 可以兼顧兩者，和 JSON 相比又更好一些，至少沒有什麼好挑剔的。

Kobako 最後選擇 MessagePack 還有一些額外的理由，主要是 [Fluent Bit](https://fluentbit.io/) 和 [mruby-engine](https://github.com/Shopify/mruby-engine) 兩個由大公司主導的 Ruby 專案，也都選擇 MessagePack 當傳輸格式，至少是被驗證過可行的做法。

接下來，我們要解決跟 MessagePack 一起被驗證過的問題，Ruby 如何跟被隔離在 WebAssembly VM 的 mruby 互相傳輸資料。

