Agent Framework CodeActとHyperlightを試す|token・latency削減と安全なsandbox設計

多数のtool callを一つのcode blockにまとめ、安全な環境で実行したい場合、Microsoft Agent FrameworkのCodeActとHyperlightは有力な選択肢です。モデルには多数の個別ツールではなくexecute_codeを見せ、生成されたコードからcall_tool(...)で必要なツールを連続実行します。これにより、モデルとツールの往復回数を減らし、token消費とlatencyを抑えられます。

一方で、Hyperlightが隔離するのは主にモデルが生成したコードです。call_tool(...)から呼び出されるツール本体はホストプロセスで動くため、ツールの権限設計を誤ると、micro-VMを使っていても安全とはいえません。読み取り専用の小さなツールだけをCodeAct側に置き、メール送信やデータ更新などの副作用を伴う処理は、個別承認できる直接ツールとして残すのが基本です。

2026年7月時点では、Azure Update ID 563566としてCodeActとHyperlightの提供が案内され、Microsoft LearnではPythonと.NET向けのHyperlight CodeActがプレビューとして説明されています。公開プレビュー段階のため、まず非本番環境で従来方式と比較測定してから採用範囲を広げるべきです。(Microsoft Azure)

目次

Agent Framework CodeActとHyperlightで何が変わるのか

従来のtool callingでは、エージェントが一つのツールを呼び、結果をモデルへ返し、次のツールを選ばせる処理を繰り返します。

たとえば、次のタスクを考えてみましょう。

  1. ユーザー一覧を取得する
  2. ユーザーごとの注文を取得する
  3. 各明細の金額を計算する
  4. ユーザー単位で集計する
  5. 全体の合計を返す

直接tool callingでは、処理の途中で何度も「モデル→ツール→モデル」の往復が発生します。CodeActでは、モデルが処理全体を次のような一つのコードとして生成します。

users = call_tool("list_users")

results = []
grand_total = 0

for user in users:
    orders = call_tool("get_orders_for_user", user_id=user["id"])
    user_total = 0

    for order in orders:
        line_total = call_tool(
            "compute_line_total",
            unit_price=order["unit_price"],
            quantity=order["quantity"],
        )
        user_total += line_total

    results.append({
        "user": user["name"],
        "total": user_total,
    })
    grand_total += user_total

print({
    "users": results,
    "grand_total": grand_total,
})

このコード全体がexecute_codeの一回の呼び出しとしてHyperlightへ渡されます。ツールの実行回数そのものが必ず減るわけではありませんが、途中経過ごとにモデルを再呼び出す必要がなくなります。

比較項目直接tool callingCodeActとHyperlight
モデルに見せるツール複数の業務ツール主にexecute_code
処理の組み立てモデルがターンごとに決定モデルが一つのプログラムを生成
ループ・分岐・集計複数ターンになりやすいcode block内で処理できる
中間結果モデルへ何度も返るsandbox内で加工してから返せる
生成コードの実行場所実装次第Hyperlight micro-VM
個別ツールの承認呼び出し単位で設定可能原則としてexecute_code全体に適用
向いている処理少数の独立した操作多数の検索、集計、変換、軽量計算

Microsoftの説明では、Hyperlight CodeActはexecute_codeツールを追加し、sandbox内のコードからcall_tool(...)を通じてホスト側ツールを利用する構成です。Hyperlightはハイパーバイザーで隔離された軽量なmicro-VMを短時間で起動するランタイムとして提供されています。(Microsoft Learn)

tokenとlatencyを削減できる理由

CodeActの効果は、計算処理自体が突然高速になることではありません。主な削減対象は、LLMによるオーケストレーションの往復です。

直接tool callingでは、各ターンで次の情報が再びモデルへ送られる可能性があります。

  • システムプロンプト
  • 会話履歴
  • ツール定義
  • 直前までのツール結果
  • 次の操作を選ぶための追加トークン

CodeActでは、モデルが最初に処理手順をコードとしてまとめます。その後の検索、ループ、フィルター、集計はsandbox内で進むため、中間データを毎回モデルへ返す必要がありません。

特に効果が大きいのは、大きな取得結果から最終回答に必要な数件だけを抽出する処理です。取得結果をそのままモデルへ返さず、sandbox内で絞り込んでから最小限の結果だけをprint(...)すれば、入力tokenを大きく削減できます。

Microsoftが公開した代表的な測定例

MicrosoftのPythonベンチマークでは、同じモデル、同じ5個のツール、同じプロンプト、同じ出力スキーマを使い、ツールの配線方法だけを変更しています。8人分の注文を検索し、割引率や税率を取得しながら明細を集計する、数十回のツール呼び出しを含むworkloadです。

実行方式実行時間token数
従来のtool calling27.81秒6,890
CodeAct13.23秒2,489
改善率52.4%63.9%

これは代表的な一回の測定結果であり、すべてのエージェントで同じ改善率になるわけではありません。API応答時間が支配的な処理、ツールが一つしかない処理、生成コードの再試行が多い処理では、改善幅が小さくなる可能性があります。.NETについては、Microsoft Learn上でもPythonと同等の公開ベンチマークサンプルはまだないと説明されています。(Microsoft for Developers)

CodeActを使うべき処理と使わない方がよい処理

CodeActは、すべてのtool callingを置き換える機能ではありません。削減できるモデル往復のコストが、sandbox起動やコード生成の追加コストを上回る処理に向いています。

処理の特徴推奨方式理由
多数の読み取りAPIを連続実行するCodeActモデルの中間ターンを削減しやすい
データの検索、結合、フィルター、集計を行うCodeActPythonコードで処理をまとめられる
大量データから小さな結果を抽出するCodeAct中間データをモデルへ返さずに済む
一つか二つのツールだけを呼ぶ直接tool calling削減できる往復が少ない
メール送信、購入、削除、更新を行う直接tool calling操作ごとの承認と監査が必要
ツールごとに人の承認を求めたい直接tool callingCodeActではcode block全体の承認になる
ツール仕様や戻り値が不安定まずツールを改善生成コードが契約を誤解しやすい
長時間実行する重いバッチ処理別のジョブ基盤も検討エージェント内の短時間sandboxだけでは管理しにくい

実務上は、一回の要求で3~5個以上の安全な小規模ツールを連鎖させているかを最初の判断材料にするとよいでしょう。一つか二つのツール呼び出ししかない場合は、Microsoftも従来の直接tool callingを継続するよう案内しています。(Microsoft Learn)

試す前に確認する実行要件

Hyperlight CodeActは通常のPythonライブラリだけで完結する機能ではありません。ホスト側でHyperlightのバックエンドを動かせる必要があります。

確認項目内容
提供状態Python、.NETともプレビュー
Pythonパッケージagent-framework-hyperlight--pre付きで導入
.NETパッケージMicrosoft.Agents.AI.Hyperlightをprereleaseで導入
LinuxKVMを利用できること
WindowsWindows Hypervisor Platformを利用できること
macOS現行バックエンドの対応状況を実行前に確認
認証FoundryまたはAzure OpenAIへ接続できる認証情報
Python guest使用するバックエンドに応じたguestモジュール
本番利用まず非本番環境で評価する

HyperlightはLinuxではKVM、WindowsではWindows Hypervisor Platformを必要とします。仮想マシンやCI環境で試す場合、nested virtualizationが利用できるかも確認してください。バックエンドが対象プラットフォーム向けに公開されていなければ、パッケージのインストールに成功しても、sandbox作成時にexecute_codeが失敗します。(Microsoft Learn)

.NETはNuGet依存関係も確認する

Microsoft Learnでは、.NETパッケージがHyperlight.HyperlightSandbox.Apiに依存し、その依存パッケージがnuget.orgで利用できない間はrestoreに失敗する可能性があると説明されています。

Azure Update上でpublic previewが発表されていても、利用するOS、NuGetフィード、パッケージ公開状況によっては、すぐに実行できるとは限りません。最初に空のプロジェクトで次のコマンドを実行し、復元が完了することを確認してください。(Microsoft Learn)

dotnet new console -n CodeActSample
cd CodeActSample

dotnet add package Microsoft.Agents.AI.Hyperlight --prerelease
dotnet restore

PythonでCodeActとHyperlightを試す手順

パッケージをインストールする

Foundryを利用する例では、Hyperlight連携、Foundryクライアント、Azure認証を導入します。

python -m pip install --upgrade pip
python -m pip install --pre agent-framework-hyperlight agent-framework-foundry azure-identity
az login

agent-framework-hyperlightは、sandbox機能を必要なプロジェクトだけに追加できるよう、Agent Frameworkのcoreとは分離して配布されています。現行のPyPIパッケージはpre-releaseとして提供されているため、再現性を確保するには、動作確認後にlock fileやrequirementsファイルへ解決済みバージョンを固定してください。(PyPI)

続いて、次の環境変数を設定します。

FOUNDRY_PROJECT_ENDPOINT=https://<プロジェクトのエンドポイント>
FOUNDRY_MODEL=<デプロイ済みモデル名>

安全な読み取りツールを登録する

以下は、ユーザー一覧と注文一覧を取得し、各明細を計算するサンプルです。複数の小さなtool callを一つのexecute_codeへまとめる動作を確認できます。

from __future__ import annotations

import asyncio
import os
from typing import Annotated

from agent_framework import Agent, tool
from agent_framework.foundry import FoundryChatClient
from agent_framework_hyperlight import HyperlightCodeActProvider
from azure.identity import AzureCliCredential


@tool(approval_mode="never_require")
def list_users() -> list[dict[str, object]]:
    """Return users whose orders should be summarized."""
    return [
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"},
        {"id": 3, "name": "Charlie"},
    ]


@tool(approval_mode="never_require")
def get_orders_for_user(
    user_id: Annotated[int, "ID returned by list_users."],
) -> list[dict[str, object]]:
    """Return order lines for one user."""
    orders = {
        1: [
            {"unit_price": 1200.0, "quantity": 2},
            {"unit_price": 500.0, "quantity": 3},
        ],
        2: [
            {"unit_price": 800.0, "quantity": 1},
        ],
        3: [
            {"unit_price": 1500.0, "quantity": 2},
            {"unit_price": 300.0, "quantity": 4},
        ],
    }
    return orders.get(user_id, [])


@tool(approval_mode="never_require")
def compute_line_total(
    unit_price: Annotated[float, "Unit price before tax."],
    quantity: Annotated[int, "Number of units."],
) -> float:
    """Calculate one order line total."""
    if unit_price < 0:
        raise ValueError("unit_price must not be negative")
    if quantity < 0:
        raise ValueError("quantity must not be negative")

    return unit_price * quantity


async def main() -> None:
    codeact = HyperlightCodeActProvider(
        tools=[
            list_users,
            get_orders_for_user,
            compute_line_total,
        ],
        approval_mode="never_require",
    )

    agent = Agent(
        client=FoundryChatClient(
            project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
            model=os.environ["FOUNDRY_MODEL"],
            credential=AzureCliCredential(),
        ),
        name="OrderSummaryCodeActAgent",
        instructions=(
            "複数の安全な読み取りツールが必要な場合は、"
            "個別のtool callを繰り返さず、可能な限り一つのexecute_codeで処理してください。"
            "providerのツールはcall_tool(...)で呼び出し、"
            "中間データをsandbox内で集計してください。"
            "最後は必要最小限のJSONだけをprintしてください。"
        ),
        context_providers=[codeact],
    )

    result = await agent.run(
        "全ユーザーの注文を取得し、ユーザー別合計と全体合計を計算してください。"
    )
    print(result.text)


if __name__ == "__main__":
    asyncio.run(main())

PyPIのクイックスタートでは、独立したパッケージ名前空間であるagent_framework_hyperlightからimportする例が掲載されています。一方、Agent Frameworkのmeta packageなどを導入した環境では、agent_framework.hyperlightというlazy-loading名前空間も利用できます。import errorが発生した場合は、インストール済みパッケージと参照しているサンプルのバージョンを揃えてください。(PyPI)

実行結果で確認するポイント

回答が正しいだけでは、CodeActが使われたか判断できません。トレースまたはmiddlewareで、次の点を確認します。

  • モデルがexecute_codeを一回呼んでいる
  • 生成されたcode block内に複数のcall_tool(...)がある
  • ユーザー一覧や注文一覧の全データをprint(...)していない
  • 最終的な集計結果だけがモデルへ返されている
  • 失敗時に直接tool callingへ無制限に切り替わっていない

Microsoftの公式サンプルでも、middlewareからexecute_codecode引数を取得し、生成されたコードと実行時間を記録する構成が使われています。(GitHub)

.NETでHyperlightCodeActProviderを構成する手順

.NETでは、HyperlightCodeActProviderAIContextProviderとしてエージェントへ登録します。Python guestをWasmバックエンドで実行する例では、HYPERLIGHT_PYTHON_GUEST_PATHにguestモジュールの絶対パスを設定します。

AZURE_OPENAI_ENDPOINT=https://<Azure OpenAIエンドポイント>
AZURE_OPENAI_DEPLOYMENT_NAME=<モデルのデプロイ名>
HYPERLIGHT_PYTHON_GUEST_PATH=<guestモジュールの絶対パス>

以下は、値の一覧を取得し、それぞれを乗算して合計する構成例です。現在のMicrosoft Learnで説明されているCreateForWasm(...)HyperlightCodeActProviderの配線方法に沿っています。(Microsoft Learn)

using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hyperlight;
using Microsoft.Extensions.AI;
using OpenAI.Chat;

string endpoint =
    Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
    ?? throw new InvalidOperationException(
        "AZURE_OPENAI_ENDPOINT is not set.");

string deploymentName =
    Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME")
    ?? throw new InvalidOperationException(
        "AZURE_OPENAI_DEPLOYMENT_NAME is not set.");

string guestPath =
    Environment.GetEnvironmentVariable("HYPERLIGHT_PYTHON_GUEST_PATH")
    ?? throw new InvalidOperationException(
        "HYPERLIGHT_PYTHON_GUEST_PATH is not set.");

AIFunction getValues = AIFunctionFactory.Create(
    static () => new[] { 2d, 3d, 5d, 7d },
    name: "get_values",
    description: "Return numeric values to process.");

AIFunction multiply = AIFunctionFactory.Create(
    static (double value, double multiplier) => value * multiplier,
    name: "multiply",
    description: "Multiply one value by a multiplier.");

var options =
    HyperlightCodeActProviderOptions.CreateForWasm(guestPath);

options.Tools = [getValues, multiply];
options.ApprovalMode = CodeActApprovalMode.NeverRequire;

using var codeAct = new HyperlightCodeActProvider(options);

AIAgent agent =
    new AzureOpenAIClient(
        new Uri(endpoint),
        new DefaultAzureCredential())
    .GetChatClient(deploymentName)
    .AsAIAgent(
        new ChatClientAgentOptions
        {
            ChatOptions = new()
            {
                Instructions =
                    "Use one execute_code block for safe multi-step calculations. " +
                    "Call provider-owned tools through call_tool(...), " +
                    "aggregate inside the sandbox, and print only the final result.",
            },
            AIContextProviders = [codeAct],
        });

Console.WriteLine(
    await agent.RunAsync(
        "Get all values, multiply each value by 10, and return their sum."));

一つのエージェントに登録できるHyperlightCodeActProviderは一つです。また、providerはIDisposableを実装しているため、usingで確実にsandboxリソースを解放します。(Microsoft Learn)

ProviderとStandalone Toolの選び方

Hyperlight CodeActには、主に二つの配線方法があります。

構成向いているケース
HyperlightCodeActProvider実行ごとにCodeActの説明とexecute_codeを自動追加したい
HyperlightExecuteCodeTool / HyperlightExecuteCodeFunction直接ツールとCodeActを明示的に混在させたい
直接agent tool個別承認が必要な副作用操作
Provider所有tool安価、決定的、読み取り中心で連鎖可能な操作

Providerは、CodeActに必要なプロンプトとexecute_codeを自動的に注入します。Standalone方式では、CodeAct用のinstructionを自分で組み込みます。

実務では、次のように役割を分ける構成が扱いやすくなります。

Provider所有tool
├─ 顧客情報の検索
├─ 注文一覧の取得
├─ マスタ参照
├─ 税率計算
└─ 読み取り専用の社内API

直接agent tool
├─ メール送信
├─ データ更新
├─ 注文確定
├─ ファイル削除
└─ 課金を伴う処理

Hyperlightのsandbox境界を正しく理解する

最も重要なのは、call_tool(...)がツールをmicro-VM内で実行する仕組みではないことです。

モデルが生成したPythonコードはHyperlight内で動きますが、Providerに登録した関数はホストプロセスへブリッジされ、ホスト側で実行されます。そのため、ホストツールはアプリケーションが持つファイル、ネットワーク、資格情報へアクセスできます。(Microsoft Learn)

LLM
  │
  │ execute_code
  ▼
Hyperlight micro-VM
  │
  │ call_tool("search_customer", ...)
  ▼
ホストプロセス上のsearch_customer関数
  │
  ├─ データベース
  ├─ 社内API
  └─ Managed IdentityやAPI資格情報

つまり、Hyperlightを導入しても、次のようなツールは危険なままです。

@tool
def run_sql(sql: str) -> list[dict]:
    """Run any SQL supplied by the caller."""

このツールをProviderへ渡すと、生成コードは任意のSQL文字列を指定できます。micro-VMはデータベースを隔離してくれません。

代わりに、入力と操作範囲を限定します。

@tool(approval_mode="never_require")
def get_customer_orders(
    customer_id: int,
    max_rows: int = 100,
) -> list[dict]:
    """Return up to 100 readable orders for one authorized customer."""

ホストツール側では、モデルから渡された引数を信用せず、次の制御を行います。

  • 呼び出しユーザーの認可
  • テナント境界の検証
  • IDや検索条件の形式検証
  • 取得件数の上限
  • タイムアウト
  • レート制限
  • 機密項目の除去
  • 監査ログへの記録

file mountとネットワーク許可は最小限にする

Hyperlightでは、ホスト側ファイルをsandboxへmountしたり、送信先ドメインをallow listで許可したりできます。

Pythonでは、次のような構成が可能です。

codeact = HyperlightCodeActProvider(
    tools=[list_users],
    file_mounts=[
        ("/srv/codeact/input", "/input/data"),
    ],
    allowed_domains=[
        ("api.example.com", "GET"),
    ],
)

.NETでは、FileMountsAllowedDomainsをoptionsへ設定します。

var options =
    HyperlightCodeActProviderOptions.CreateForWasm(guestPath);

options.FileMounts =
[
    new FileMount(
        "/srv/codeact/input",
        "/input/data"),
];

options.AllowedDomains =
[
    new AllowedDomain(
        "https://api.example.com",
        ["GET"]),
];

入力ファイルは/input配下で読み取り、生成した大きな成果物は/output/<filename>へ保存する設計が基本です。テキスト結果を返す場合は、生成コードの最後でprint(...)する必要があります。Hyperlightは最後の式の値を自動返却しません。(Microsoft Learn)

file mountと外向き通信を同時に広げない

読み取り可能なmountと自由な外向き通信を同時に許可すると、生成コードがファイル内容を外部へ送信できる構成になります。

特に避けるべき設定は次のとおりです。

  • ホームディレクトリ全体のmount
  • ソースリポジトリ全体のmount
  • .env、SSH鍵、クラウド資格情報のmount
  • 任意ドメインへのHTTP許可
  • GETだけでよいAPIへの全HTTPメソッド許可
  • 本番データと検証用sandboxの共有

「ファイルを読める」「外部へ送れる」という二つの権限は、個別ではなく組み合わせで評価してください。

AllowedDomainsはホストツールを制限しない

allowed_domainsAllowedDomainsが制約するのは、sandbox内のコードが直接行う通信です。call_tool(...)で呼び出されたホストツールの通信先は制限しません。

したがって、機密APIへアクセスさせたい場合は、sandboxへ広いネットワーク権限を与えるより、次のような用途限定ツールをホスト側に実装する方が安全です。

@tool(approval_mode="never_require")
def get_exchange_rate(
    base_currency: str,
    quote_currency: str,
) -> dict[str, object]:
    """Return one approved currency pair from the internal rate service."""

この境界はMicrosoft Learnでも明確に説明されています。(Microsoft Learn)

承認はcode block全体に適用される

CodeActでは、同じexecute_code内に複数のcall_tool(...)が含まれていても、承認は個々のツール呼び出しではなく、原則としてexecute_code全体に適用されます。

たとえば、次のコードが一つのblockとして生成された場合を考えます。

customer = call_tool("get_customer", customer_id=100)
invoice = call_tool("create_invoice", customer_id=100)
call_tool("send_invoice_email", invoice_id=invoice["id"])
print(invoice)

ユーザーが承認画面で把握すべき操作は、顧客検索だけではありません。請求書作成とメール送信も含まれます。しかし、個別のcall_tool(...)ごとに承認を分けることはできません。

そのため、create_invoicesend_invoice_emailはProviderに入れず、直接agent toolとして登録し、操作単位で承認させる方が安全です。Microsoftも、副作用や個別承認が必要な操作を直接ツールとして残すよう案内しています。(Microsoft Learn)

ツールの説明と戻り値をCodeAct向けに整える

直接tool callingでは、モデルがツール名とJSON Schemaを見て一つの呼び出しを選びます。CodeActでは、モデルがその契約を使ってプログラムを書きます。

そのため、次の情報が曖昧だと、生成コードの失敗率が高まります。

  • ツール名
  • docstring
  • 引数名
  • 引数の型
  • 許容値
  • 戻り値のフィールド
  • エラー条件
  • 空データ時の戻り値

悪い例は、名前から用途が判断できず、戻り値も不定なツールです。

@tool
def execute(data: dict) -> object:
    """Execute data."""

CodeAct向けには、次のように契約を狭くします。

@tool(approval_mode="never_require")
def search_products(
    query: Annotated[str, "Product name keyword. Maximum 100 characters."],
    limit: Annotated[int, "Maximum number of results from 1 to 50."],
) -> list[dict[str, object]]:
    """
    Return products matching the keyword.

    Each item contains:
    - product_id: integer
    - name: string
    - unit_price: float
    - stock: integer

    Returns an empty list when no product matches.
    """

Pythonでは、Provider経由のツール結果がdictlistなどのnative valueとしてguestへ返されます。LLM向けに設定したresult_parserはsandbox経路では適用されないため、sandbox内で特定の形式が必要ならツール関数自体で整形します。(Microsoft Learn)

token削減を失敗させる出力設計に注意する

CodeActを使っても、最後に全中間データをprint(...)すればtoken削減効果は失われます。

悪い例は、取得した全レコードをそのまま出力するコードです。

records = call_tool("search_logs", hours=24)
print(records)

より適切なのは、sandbox内で絞り込みと集計を完了させる方法です。

records = call_tool("search_logs", hours=24)

errors = [
    record
    for record in records
    if record["level"] == "ERROR"
]

summary = {
    "total": len(records),
    "error_count": len(errors),
    "top_error_codes": {},
}

for record in errors:
    code = record.get("error_code", "unknown")
    summary["top_error_codes"][code] = (
        summary["top_error_codes"].get(code, 0) + 1
    )

print(summary)

大きなCSVやJSONを成果物として返したい場合は、会話テキストへ展開せず、/outputへファイルとして保存します。

execute_code間でメモリ状態は保持されない

個別のexecute_code呼び出しをまたいで、Python変数やインメモリ状態が残ることを前提にしてはいけません。

次のような会話は失敗する可能性があります。

1回目: 大量データを取得して変数dataへ保存
2回目: 先ほどのdataを使って別の集計を実行

呼び出し間でデータを残す必要がある場合は、明示的に/outputへ保存するか、ホスト側の状態管理やストレージを利用します。Hyperlight CodeActの現在の制約として、インメモリのinterpreter stateは別のexecute_codeへ引き継がれないと説明されています。(Microsoft Learn)

自環境でtokenとlatencyを比較する方法

公開された改善率をそのまま採用判断に使わず、直接tool calling版とCodeAct版を同じ条件で比較します。

比較条件を固定する

少なくとも、次の条件を揃えます。

項目固定する内容
モデル同じモデルとデプロイ
temperature同じ設定
ツール同じ処理内容
データ同じテストデータ
プロンプト同じ要求
出力同じJSON Schema
認証・接続先同じ環境
タイムアウト同じ上限
再試行同じポリシー

違いは、ツールをAgent(tools=...)へ直接渡すか、HyperlightCodeActProviderの背後へ渡すかだけにします。Microsoft Learnの比較方法も、この条件を前提としています。(Microsoft Learn)

記録すべき指標

latencyとtokenだけでなく、成功率も測定します。

  • end-to-end latency
  • p50、p95 latency
  • input token
  • output token
  • total token
  • モデル呼び出し回数
  • execute_code回数
  • call_tool(...)回数
  • 正しい結果を返した割合
  • 構文エラー率
  • ツール引数エラー率
  • 再試行回数
  • sandbox作成エラー率
  • 一件当たりの推定モデル費用

CodeActでlatencyが下がっても、計算結果の誤りや再試行が増えれば、本番品質は改善していません。

Pythonでの簡易計測例

from dataclasses import dataclass
from time import perf_counter
from typing import Any


@dataclass
class Measurement:
    elapsed_seconds: float
    usage: Any
    text: str


async def measure(agent, prompt: str) -> Measurement:
    started = perf_counter()

    result = await agent.run(prompt)

    elapsed = perf_counter() - started
    usage = getattr(result, "usage", None)

    return Measurement(
        elapsed_seconds=elapsed,
        usage=usage,
        text=result.text,
    )

最初の一回には初期化や認証、接続確立の影響が含まれることがあります。cold startとwarm runを分け、同一プロンプトを20~30回程度実行して分布を比較すると判断しやすくなります。

また、モデルのキャッシュやAPI側の混雑による偏りを避けるため、直接方式をすべて実行してからCodeAct方式を実行するのではなく、実行順を交互にする方法も有効です。

よくあるエラーと対処方法

症状主な原因対処
execute_code作成時に失敗するHyperlightバックエンド非対応OS、CPU仮想化、KVMまたはWHPを確認する
Pythonのimportに失敗する名前空間と導入パッケージの不一致agent_framework_hyperlightagent_framework.hyperlightを公式サンプルに合わせる
.NETのrestoreに失敗するprerelease依存パッケージが取得できないNuGetソースと公開状況を確認する
結果が空になる生成コードにprint(...)がないinstructionに最終結果をprintするよう明記する
HTTPアクセスが拒否されるallowed_domains未登録必要なドメインとHTTPメソッドだけを追加する
ファイルが見つからないsandbox内のパスが異なるhost pathとmount pathを分けて確認する
前回の変数が消える呼び出し間で状態が保持されない/outputまたは外部ストレージへ保存する
tokenが減らない中間データを全件printしているsandbox内で絞り込み、要約結果だけを返す
依然として直接tool callが多いinstructionまたはProvider登録が不十分ツールをProvider側へ移し、execute_code優先を明記する
危険な処理が一括承認される副作用ツールをProviderへ登録している直接agent toolへ戻して個別承認させる

公開プレビュー段階での安全な導入順序

Azure Updatesでは、previewを非本番での利用とテストを想定した状態として区別しています。APIやパッケージ構成が変わる可能性もあるため、最初から本番の更新処理へ組み込むべきではありません。(Microsoft Azure)

読み取り専用の検証タスクを選ぶ

最初の対象には、次のような処理が適しています。

  • 複数APIから情報を検索する
  • ログを集計する
  • 在庫や注文を読み取ってレポート化する
  • 複数のマスタを結合する
  • 数値を計算してJSONを返す

削除、更新、送信、課金は含めません。

直接方式とCodeAct方式を並行実装する

同じツールを使って、次の二つのエージェントを用意します。

Agent A
└─ tools=[list_users, get_orders, compute_total]

Agent B
└─ context_providers=[
     HyperlightCodeActProvider(
       tools=[list_users, get_orders, compute_total]
     )
   ]

両方へ同じ評価データを入力し、latency、token、成功率、出力差分を記録します。

sandboxの権限をゼロから追加する

最初は次の状態から始めます。

file mount: なし
outbound network: なし
provider tools: 読み取り専用のみ
direct tools: なし

必要性が確認できた権限だけを、一つずつ追加します。最初から広いファイルアクセスやネットワークアクセスを許可すると、どの権限が本当に必要なのか判断できません。

バージョンを固定して段階展開する

動作確認後はpre-releaseパッケージのバージョンを固定し、更新時には同じ評価セットを再実行します。

本番相当環境へ進める場合も、対象ユーザーや処理件数を限定し、従来方式へ戻せる切り替えを残してください。

CodeAct導入で最初に実施すべきこと

Agent Framework CodeActとHyperlightは、多数の小さなtool callを一つの実行可能code blockへまとめ、モデル往復に伴うtokenとlatencyを削減する仕組みです。特に、検索、ループ、フィルター、集計、軽量計算を組み合わせるタスクで効果を期待できます。

ただし、micro-VMが隔離するのはモデル生成コードであり、call_tool(...)先の関数はホストプロセスで動きます。安全性を確保するには、読み取り専用で入力範囲の狭いツールだけをProviderへ登録し、副作用のある処理は個別承認可能な直接ツールとして残す必要があります。

まずはPythonで読み取り専用の実データに近いworkloadを用意し、従来方式とCodeAct方式を同じ条件で測定してください。latencyとtokenだけでなく、成功率、再試行、生成コード、権限境界も確認します。.NETはNuGet依存関係とHyperlightバックエンドの利用可否を先に検証し、復元とsandbox作成に成功してから同じ比較へ進むのが確実です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次