> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# リモート関数

> 関数デコレーターを使用して、サンドボックス内で Python 関数を実行します。

ここで紹介する例では、`CWSANDBOX_API_KEY` に設定した CoreWeave API アクセストークンを使用します。Forge APIキーによる認証については、[はじめに](/ja/products/sandboxes/get-started)を参照してください。CKS の要件については、[配置モード](/ja/products/sandboxes/placement)を参照してください。

このガイドでは、サンドボックス内で Python 関数を実行するための `@session.function()` デコレーターについて説明します。対象読者は、サンドボックスのライフサイクルを直接管理することなく、個々の Python の計算処理を分離された環境にオフロードしたい開発者です。リモート関数の定義方法、API がサポートする引数と戻り値のタイプ、およびこの API が適しているユースケースについて解説します。

<h2 id="overview">
  概要
</h2>

関数デコレーター API を使用すると、隔離されたサンドボックスコンテナー内で Python 関数を実行できます。呼び出し側のコードをシンプルに保ちつつ、個別の Python 処理をリモートで実行したい場合に使用します：

```python theme={"system"}
import cwsandbox
from cwsandbox import SandboxDefaults

with cwsandbox.Session(SandboxDefaults(container_image="python:3.11")) as session:
    @session.function()
    def compute(x: int, y: int) -> int:
        return x + y

    # サンドボックス内で実行
    result = compute.remote(2, 3).result()
    print(result)  # 5
```

<h2 id="basic-usage">
  基本的な使用方法
</h2>

<h3 id="define-functions">
  関数を定義する
</h3>

次のように、`@session.function()` で関数をデコレートします。

```python theme={"system"}
@session.function()
def process_data(data: dict) -> dict:
    # コードはサンドボックス内で実行されます
    import pandas as pd
    df = pd.DataFrame(data)
    return {"mean": df["value"].mean()}
```

<h3 id="call-functions">
  関数を呼び出す
</h3>

デコレートした関数で `.remote()` を呼び出すと、サンドボックス内で実行されます。

```python theme={"system"}
# OperationRef をすぐに返します
ref = compute.remote(2, 3)

# 結果が返るまでブロックします
result = ref.result()

# 1 行で記述する場合
result = compute.remote(2, 3).result()
```

<h2 id="execution-methods">
  実行メソッド
</h2>

<h3 id="run-in-parallel-with-map">
  map() で並列実行する
</h3>

複数の入力に対して実行します：

```python theme={"system"}
@session.function()
def square(x: int) -> int:
    return x * x

# 各入力に対して実行
refs = square.map((x,) for x in range(10))

# すべての結果を収集
from cwsandbox import results
all_results = results(refs)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
```

複数の引数をタプルで渡す場合:

```python theme={"system"}
@session.function()
def add(x: int, y: int) -> int:
    return x + y

# 各タプルは引数として展開されます
refs = add.map([(1, 2), (3, 4), (5, 6)])
all_results = results(refs)  # [3, 7, 11]
```

<h3 id="run-locally-with-local">
  local() でローカル実行する
</h3>

サンドボックスを使用せずにローカルで実行します。テスト時に便利です。

```python theme={"system"}
# サンドボックスは作成せず、現在のプロセスで実行します
result = compute.local(2, 3)
print(result)  # 5
```

<h2 id="argument-and-return-types">
  引数と戻り値のタイプ
</h2>

リモート関数は、引数と戻り値を JSON として受け渡します。そのため、どちらも JSON シリアライズ可能な値 (辞書、リスト、strings、数値、ブール値、`None`) である必要があります。

```python theme={"system"}
@session.function()
def process(data: dict) -> dict:
    return {"result": data["value"] * 2}
```

JSON で表現できない引数を渡すと、サンドボックスが起動する前に呼び出しから `TypeError` が送出されます。

```text title="Example output" theme={"system"}
TypeError: Function arguments are not JSON-serializable: Object of type ndarray is not JSON serializable
```

JSON で表現できない戻り値も同様に失敗し、`return value is not JSON-serializable` というメッセージが表示されます。

JSON オブジェクトのキーには文字列しか使用できません。整数、float、ブール値をキーとする辞書は、変換されずに拒否されます。そのため、サンドボックス内で関数が受け取る値のタイプは、渡したタイプと常に一致します。

NumPy 配列、pandas のデータフレーム、カスタムクラスのインスタンスを扱う場合は、関数を呼び出す前に JSON 互換の値に変換し、関数内で元の形式に再構築してください。

```python theme={"system"}
@session.function()
def array_mean(values: list[float]) -> float:
    from statistics import fmean
    return fmean(values)

result = array_mean.remote([1.0, 2.0, 3.0, 4.0, 5.0]).result()  # 3.0
```

クライアントは、関数を実行する前にパッケージを一切インストールしません。NumPy など、関数でインポートするライブラリは、あらかじめコンテナーイメージに含めておく必要があります。

JSON として渡すには大きすぎるデータは、サンドボックスからアクセスできるストレージに書き込み、代わりにその参照を渡してください。詳しくは、[ファイル操作](/ja/products/sandboxes/client/guides/file-operations)を参照してください。

<h2 id="closures-and-globals">
  クロージャとグローバル変数
</h2>

<h3 id="closure-variables">
  クロージャ変数
</h3>

関数は、外側のスコープにある変数を取得します。

```python theme={"system"}
multiplier = 10

@session.function()
def multiply(x: int) -> int:
    return x * multiplier  # 'multiplier' をキャプチャ

result = multiply.remote(5).result()  # 50
```

<h3 id="global-variables">
  グローバル変数
</h3>

デコレーターは、関数から参照されているグローバル変数を関数と一緒にシリアライズします。

```python theme={"system"}
CONFIG = {"threshold": 0.5}

@session.function()
def check_value(x: float) -> bool:
    return x > CONFIG["threshold"]

result = check_value.remote(0.7).result()  # True
```

キャプチャされたクロージャ変数とグローバル変数は、引数や戻り値と同様に JSON 形式でサンドボックスに転送されます。そのため、関数が JSON で表現できない値をキャプチャしている場合は、渡す引数がすべて有効であっても呼び出しは失敗します。

<h2 id="container-image">
  コンテナーイメージ
</h2>

特定の関数で使用するコンテナーイメージを上書きします。

```python theme={"system"}
@session.function(container_image="pytorch/pytorch:latest")
def train_model(data: dict) -> dict:
    import torch
    # GPU を使ったトレーニングのコード
    return {"loss": 0.01}
```

<h2 id="error-handling">
  エラー処理
</h2>

関数で発生した例外は、呼び出し元に伝播します。

```python theme={"system"}
@session.function()
def failing_function() -> None:
    raise ValueError("Something went wrong")

try:
    failing_function.remote().result()
except Exception as e:
    print(f"Function failed: {e}")
```

<h2 id="when-to-use-functions-compared-to-sandboxes">
  関数とサンドボックスの使い分け
</h2>

次の表を参考に、ワークロードに適しているのが関数デコレーター API とサンドボックス API のどちらであるかを判断してください。

| ユースケース | 推奨 API |
| - | - |
| シンプルな Python の計算 | 関数デコレーター |
| データに対する Map/Reduce | 関数デコレーター |
| 対話型のワークフロー、複数のコマンドの実行 | サンドボックス |
| ストリーミング出力 | サンドボックス |
| ファイル操作 | サンドボックス |
| 長時間実行されるプロセス | サンドボックス |

<h2 id="limitations">
  制限事項
</h2>

関数 API は意図的にシンプルな設計になっています。複雑なワークフローには、次の方法で対応してください。

* **リトライとバックオフ**: 呼び出し側のコードで実装します。
* **タスクの依存関係と DAG**: Airflow や Prefect などのワークフローオーケストレーターを使用します。
* **複雑なスケジューリング**: サンドボックス API を直接使用します。

<h2 id="complete-example">
  完全な設定例
</h2>

次の例では、このガイドで説明した概念を組み合わせています。単一実行と並列実行に加えて、引数と戻り値を JSON 形式で受け渡す計算を示します。

```python theme={"system"}
import cwsandbox
from cwsandbox import SandboxDefaults, results

defaults = SandboxDefaults(
    container_image="python:3.11",
    tags=("remote-functions-demo",),
)

with cwsandbox.Session(defaults=defaults) as session:
    @session.function()
    def square(x: int) -> int:
        return x * x

    # 引数はリスト、戻り値は float なので、どちらも JSON として受け渡しできます
    @session.function()
    def array_mean(values: list[float]) -> float:
        from statistics import fmean
        return fmean(values)

    # 単一実行
    result = square.remote(7).result()
    print(f"7 squared: {result}")

    # 並列実行
    refs = square.map((x,) for x in range(5))
    all_results = results(refs)
    print(f"Squares: {all_results}")

    # JSON 互換の計算
    mean = array_mean.remote([1.0, 2.0, 3.0, 4.0, 5.0]).result()
    print(f"Array mean: {mean}")
```
