Nodaro ドキュメント
ドキュメントノードリファレンスモデルAI エージェント(MCP)開発者向けセルフホスティングリサーチ
TypeScript SDK

LLM と Reduce

client.llm で言語モデルから検証済みの JSON を取得し、client.reduce で複数の結果からベストを選んだり、多数決を取ったり、結合や統合をしたりします。

client.llm は、言語モデルに構造化出力を依頼します。システムプロンプトと入力、JSON Schema を送ると、そのスキーマに一致するオブジェクトが返ります。client.reduce は、Reduce のファンインの処理を単体で実行します。複数の結果からベストを選ぶ、件数を数える、多数決を取る、テキストを結合する、JSON をマージするといった、ワークフロー内のベストを選択(Choose Best)ノードが行う処理です。どちらも、モデルのティアに応じてクレジットがかかります。

メソッド

メソッド内容
llm.structured(input)言語モデルから、検証済みのオブジェクトを 1 回のリクエストで取得します
llm.structuredJob(input)同じ呼び出しを、ポーリングするジョブとして実行します
reduce.run(input)多数の入力を、選択、カウント、多数決、結合、マージのいずれかで 1 つにまとめます

client.llm

構造化出力:システムプロンプトと JSON Schema を渡すと、検証済みのオブジェクトが返ります。プラットフォームがモデルのレーンを選び、JSON 出力を強制し、指定したスキーマに沿って検証し、無効な答えは、あきらめる前にモデルへ差し戻します。課金は llm-structured として、モデルのティアごとに行われます。

llm.structured(input)

モデルに問い合わせ、答えを待ちます(POST /v1/llm/structured)。1 回の呼び出しに数分かかることがあり、クライアントのデフォルトの 60 秒のタイムアウトより長くなる場合があります。大きめの timeoutMs を指定してクライアントを作成するか、structuredJob() を使ってください。

structured<T>(input: LlmStructuredInput): Promise<{
  jobId: string
  output: T
  usage: { inputTokens: number; outputTokens: number }
}>

Prop

Type

type Plan = { title: string; scenes: string[] }

const { output } = await client.llm.structured<Plan>({
  system: "You write production plans for short films.",
  input: "A rainy chase through Rome, 60 seconds.",
  jsonSchema: {
    type: "object",
    properties: {
      title: { type: "string" },
      scenes: { type: "array", items: { type: "string" } },
    },
    required: ["title", "scenes"],
  },
  schemaName: "production_plan",
})
console.log(output.title, output.scenes.length)

llm.structuredJob(input)

同じ呼び出しを、ジョブとして実行します(POST /v1/llm/structured/jobs)。jobId がすぐに返るので、client.jobs.getStatus() でポーリングします。ジョブは、動画から下書きを作ることもできます。その場合、プラットフォームはまず動画を分析し、その分析結果を入力に加えます。

structuredJob(input: LlmStructuredJobInput): Promise<{ jobId: string }>

Prop

Type

const { jobId } = await client.llm.structuredJob({
  system: "You write production plans.",
  input: "A rainy chase through Rome.",
  jsonSchema: { type: "object", properties: { title: { type: "string" } }, required: ["title"] },
  origin: "my-app",
  label: "Rome chase",
})

// later, even from another session
const { data } = await client.jobs.getStatus(jobId)
if (data.status === "completed") {
  console.log((data.output_data as { output: { title: string } }).output.title)
}
const { data: runs } = await client.jobs.list({ type: "llm-structured", origin: "my-app" })

ジョブの実行中、output_data には stage が入り、値は analyzing(動画からの下書きの場合)または drafting です。完了すると、output_data には output、inputTokens、outputTokens が入り、動画からの下書きの場合はさらに analysisJobId と analysisCredits も入ります。

  • analysisJobId が指すジョブが自分のものではない、存在しない、または完了した動画分析ではない場合、422 で失敗します。コードは analysis_not_found、not_analysis、analysis_failed、analysis_not_ready、invalid_analysis のいずれかです。
  • このルートがないプラットフォームでは、NotFoundError がスローされます。
  • 言語モデルの呼び出しを Nodaro Cloud に送るセルフホスティング環境では、503 provider_unavailable が返ります。これは一時的なエラーではなく、そのインスタンスでは利用できないものとして扱ってください。

client.reduce

reduce.run(input)

多数の入力を 1 つに集約します。MCP の reduce ツールや、ベストを選択ノードと同じ処理です。

run(input: ReduceInput): Promise<ReduceResult>

Prop

Type

戦略strategyConfig返す内容
pick-best-llm{ criteria, inputKind?, llmModel? }。inputKind は "text" または "image-url" です。llmModel は判定に使うモデルを選び、そのモデルのクレジットのティアが適用されます。言語モデルが最も優れていると判断した入力で、そのインデックスと判断理由を含みます
concat{ separator? }。デフォルトは空行です。すべての入力を 1 つのテキストに結合したもの
first-non-emptyなし空でない最初の入力
countなし入力の件数
vote{ caseSensitive? }。デフォルトは false です。最も頻度の高い入力です。同数の場合は最初のものが選ばれます。
merge-json{ strategy? }:"deep"(デフォルト)または "shallow"JSON の入力を 1 つのオブジェクトにマージしたもの
const result = await client.reduce.run({
  strategyId: "pick-best-llm",
  strategyConfig: { criteria: "The sharpest image with no artifacts", inputKind: "image-url" },
  inputs: [url1, url2, url3, url4, url5],
})
console.log(result.output)             // the chosen URL
console.log(result.meta.selectedIndex) // 0 to 4
console.log(result.meta.reasoning)     // why the model chose it

結果は { jobId, output, meta } です。output は、選ばれた値または結合された値で、文字列です。meta.summary は常に設定されます。pick-best-llm と vote は meta.selectedIndex を設定し、pick-best-llm はさらに meta.reasoning も設定します。

// Majority vote
const winner = await client.reduce.run({ strategyId: "vote", inputs: ["red", "blue", "red"] })

// Deep-merge JSON fragments
const merged = await client.reduce.run({
  strategyId: "merge-json",
  inputs: [JSON.stringify({ a: 1, nested: { x: 1 } }), JSON.stringify({ b: 2, nested: { y: 2 } })],
})
JSON.parse(merged.output) // { a: 1, b: 2, nested: { x: 1, y: 2 } }

すべての入力が空か空白だけの場合、呼び出しは失敗し、ステータスが 400 で code が no_valid_inputs の NodaroError になります。クレジットは、ほかの生成と同じように確保されるため、残高が足りない場合は InsufficientCreditsError がスローされます。

よくある質問

最終更新

目次