コンテンツにスキップ
Subako Docs
日本語
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

クイックスタート

Package「anydoc」を使って、Agent が Word や PDF を読めるようにします。

誰かが Session に q3-review.docx をアップロードし、自分が何を決めるべきかを Agent に尋ねたとします。答えはファイルの中にあります。このページでは、その答えを取り出す方法と、それに Function が最適な理由を説明します。

課題: Agent はファイルを読めない

.docx はテキストではありません。複数の XML ファイルを ZIP でまとめたもので、段落はあるファイルに、スタイルは別のファイルに入り、表や番号付けは両方にまたがっています。バイト列をそのまま渡されても、モデルにはノイズにしか見えません。.pdf、.xlsx、.pptx も同様で、実際の仕事で扱うドキュメントはほとんどがバイナリ形式です。

すぐに思いつく回避策は、どれもモデルのコンテキストを経由します。

  • ファイルを会話に貼り付ける。 2 MB のドキュメントを Base64 にしてメッセージに入れると、たいていの Context window に収まらないほどのトークンを消費します。しかも、モデルはそのバイト列をどのみち解析できません。
  • MCP サーバーに渡す。 MCP サーバーは別のサービスなので、Session のファイルを見られません。ドキュメントを届けるには、結局モデルを通して Tool 呼び出しの引数として渡すしかなく、コストは変わらないうえ、扱えるサイズは Context window で頭打ちになります。
  • ユーザーに先に変換してもらう。 Agent が手助けするはずだった相手に、作業を押し戻すことになります。

どれも望む形ではありません。そもそもドキュメントがモデルを通らないようにしたいのです。

発想: Tool の呼び出しをファイルへの操作にする

Session にはすでにファイルシステムがあります。Session Volume です。アップロードしたドキュメントは /volume/data/uploads/q3-review.docx に置かれています。それなら、Tool にそこで作業させればよいのです。

モデルはファイルを指定する

モデルはドキュメントそのものではなく、パスを渡して Tool を呼び出します。かかるのは数十トークンです。

Tool はファイルのそばで処理する

Tool は Volume からバイト列を読み、ZIP の展開、XML の解析、表の Markdown への整形といった、モデルにはできない処理を行います。

結果はファイルか、小さな回答になる

Tool は副作用として、次の Tool がそのまま使える q3-review.md のような出力を Volume に書き戻せます。モデルに返すのは、モデルに必要なものだけです。短いドキュメントなら Markdown そのもの、長いドキュメントならパスとサイズです。

何をするかを決めるのはモデルで、計算とバイト列はファイルシステムの側にとどまります。これが Subako Function です。Subako がホストする環境で、Session の Volume のそばで動くコードであり、自分でサーバーを用意する必要はありません。

Function としての anydoc

anydoc は、まさに必要な変換を行うオープンソースの Rust ライブラリです。ドキュメントのバイト列を渡すと、形式 (Word、PowerPoint、Excel、OpenDocument、RTF、EPUB、CSV、テキストベースの PDF) を判別し、テキストを GitHub-Flavored Markdown にして返します。ただし、単体ではただのライブラリです。ファイルのそばでそれを実行する仕組みが要ります。

anydoc のサンプル は、Subako の Rust SDK を使って、これを 1 つの関数として包んだものです。以下はその中心部分を、サンプルのソースから簡略化して示したものです。

#[derive(Deserialize, JsonSchema)]
struct ConvertInput {
    /// Path of the document on the session's Volume, under `/volume/tmp` or `/volume/data`.
    path: String,
    /// Where the Markdown goes: back in the reply (the default), or into a Volume file.
    #[serde(default)]
    output: MarkdownOutput,
}

/// Convert a document on the Volume to GitHub-Flavored Markdown. Reads Word
/// (doc, docx), PowerPoint (ppt, pptx), Excel (xls, xlsx, xlsb), OpenDocument
/// (odt, ods, odp), RTF, EPUB, CSV and text-based PDF. …
#[function]
fn convert_to_markdown(input: ConvertInput) -> Result<Converted, ToolError> {
    // Read the document from the session's volume…
    let bytes = std::fs::read(&input.path)?;
    // …let anydoc do the conversion…
    let format = Format::from_bytes(&bytes).ok_or("unrecognized file")?;
    let markdown = anydoc::to_markdown_bytes(&bytes, format)?;
    // …and answer with the text, or write it to a file and answer with where it is.
    let markdown = match input.output {
        MarkdownOutput::Inline => Markdown::Inline { text: markdown },
        MarkdownOutput::File { path } => {
            std::fs::write(&path, &markdown)?;
            Markdown::File { bytes: markdown.len(), path }
        }
    };
    Ok(Converted { format: format_name(format).into(), markdown })
}

package! {
    name: "anydoc",
    description: "Converts office documents, EPUB, CSV and text-based PDFs to Markdown with anydoc.",
    functions: [convert_to_markdown],
}

組み込みに必要なのはこれだけです。

  • #[function] は普通の Rust の関数を Tool にします。doc コメントはモデルが読む説明に、ConvertInput はモデルが呼び出しに使う入力 Schema になります。
  • ファイルへのアクセスは普通の std::fs で済みます。Volume が Function のファイルシステムにマウントされているからです。
  • package! は crate を Function package にします。WebAssembly 向けにビルドすると 1 つの module.wasm になり、Subako はそれをどこでも、どの Session のためにも実行できます。デプロイするものは何もありません。

ここからは、この Package をビルドしてアップロードし、Agent に使わせるまでの手順です。

anydoc をビルドする

Package のビルドには Rust が必要です。

git clone https://github.com/subako-ai/subako-function-examples
cd subako-function-examples/rust/anydoc
./package.sh        # dist/anydoc-function.tar.gz に書き出します

package.sh は Module を WASI preview 1 向けにビルドし (このターゲットは、サンプルの rust-toolchain.toml に従って rustup がインストールします)、subako-package.json と一緒に Bundle にまとめます。Bundle は、Subako が受け付ける形式の Archive です。

アップロードする

コンソールで Workspace の Function ページを開き、Package をアップロード を選びます。Archive を選んで dist/anydoc-function.tar.gz を指定し、アップロード を押します。

Function package のアップロード: anydoc の Archive を選択した状態

Subako は Module を一度実行して提供される Tool を問い合わせ、Package のページを開きます。ページには、Tool とその説明、Parameter が、モデルから見えるとおりに表示されます。

Package「anydoc」のページ: バージョン 1、そのビルド、Parameter 付きの Tool convert_to_markdown

Agent に付与する

Agent を開き (まだなければ クイックスタート のとおりに作成し)、新しいバージョンを公開 を選びます。Tool › Function package で Package を追加 を押し、この Workspace を選んで anydoc を指定します。

Function package の Grant: Workspace の Package「anydoc」をバージョン 1 に固定

anydoc は既定値のままで使えます。ネットワークは不要で、1 回の呼び出しは 60 秒よりずっと短く終わるからです。続いて Session Volume の Function に Data (/volume/data) を用意する をオンにします。これで、Session にアップロードしたファイルが、Session がある限り保持されます。

Session Volume:「Function に Data (/volume/data) を用意する」をオンにした状態

あわせて Platform package の files も付与しておくと、Agent はパスを知らされていないドキュメントを探したり、長い変換結果を 1 ページずつ読んだりできます。設定が終わったら バージョン N を公開 を押します。

ドキュメントを渡す

その Agent で Session を開始し、Session Volume タブで ファイルをアップロード を選びます。ドキュメントは Data (/volume/data) の uploads などのフォルダーにアップロードします。

ファイルのアップロード: q3-review.docx を Data (/volume/data) のフォルダー uploads にアップロード

Volume は、Agent の Function がファイルを読み書きする場所です。これでファイルは /volume/data/uploads/q3-review.docx に置かれました。

Session Volume: uploads/q3-review.docx がある Data の領域

自分のアプリから PUT /v1/sessions/{session_id}/volume/data/{path} でファイルを置くこともできます。詳しくは API で Session を扱う を参照してください。

質問する

会話 に戻り、ドキュメントを読まなければ答えられないことを質問します。Agent はファイルを対象に convert_to_markdown を呼び出し、得られた Markdown を読んで回答します。

会話: 質問、完了した Function「anydoc」の convert_to_markdown の呼び出し、3 つの決定事項を挙げた Agent の回答

呼び出しを開くと、何が起きたかを正確に確認できます。モデルが送った引数と、anydoc が返した Markdown です。表もそのまま含まれています。

convert_to_markdown の呼び出しを開いたところ: 引数 path と、Markdown になったドキュメント

短いドキュメントなら、この例のように Markdown がそのまま返ってきます。300 ページのマニュアルのような長いドキュメントでは、モデルは代わりに "output": {"type": "file", "path": "/volume/data/uploads/manual.md"} を指定します。anydoc は Markdown を Volume に書き込み、そのパスとサイズだけを返します。Agent は必要な部分だけを Platform package の files で読みます。どちらの場合も、ドキュメントのバイト列がモデルを通ることはありません。

これで一連の流れは完了です。Package は一度アップロードして Agent に付与すれば、必要とするすべての Session から呼び出されます。自分で運用するサーバーは要りません。

次のステップ

このページは役に立ちましたか?