LLM과 Reduce
client.llm으로 언어 모델에서 검증된 JSON을 받고, client.reduce로 여러 결과 중 가장 좋은 것을 고르거나, 투표하거나, 이어 붙이거나, 병합합니다.
client.llm은 언어 모델에 구조화된 출력을 요청합니다. 시스템 프롬프트, 입력, JSON Schema를 보내면 스키마에 맞는 객체를 돌려받습니다. client.reduce는 Reduce 팬인 단계를 단독으로 실행합니다. 여러 결과 중 가장 좋은 것을 고르고, 개수를 세고, 투표하고, 텍스트를 이어 붙이거나 JSON을 병합합니다. 워크플로에서 최적 선택 노드가 하는 일입니다. 둘 다 모델 등급에 따라 크레딧이 듭니다.
메서드
| 메서드 | 기능 |
|---|---|
llm.structured(input) | 요청 한 번으로 언어 모델에서 검증된 객체 받기 |
llm.structuredJob(input) | 같은 호출을 폴링하는 작업으로 실행 |
reduce.run(input) | 여러 입력을 고르기, 세기, 투표, 이어 붙이기, 병합으로 하나로 줄이기 |
client.llm
구조화된 출력입니다. 내 시스템 프롬프트와 JSON Schema를 넣으면 검증된 객체가 나옵니다. 플랫폼이 모델 경로를 고르고, JSON 출력을 강제하고, 내 스키마로 검증하며, 포기하기 전에 유효하지 않은 답을 모델에 다시 보냅니다. 모델 등급에 따라 llm-structured로 청구됩니다.
llm.structured(input)
모델에 요청하고 답을 기다립니다(POST /v1/llm/structured). 호출 한 번에 몇 분이 걸릴 수 있으며, 이는 클라이언트의 기본 타임아웃 60초보다 깁니다. 이 호출에는 더 큰 timeoutMs로 클라이언트를 만들거나 structuredJob()을 사용하세요.
structured<T>(input: LlmStructuredInput): Promise<{
jobId: string
output: T
usage: { inputTokens: number; outputTokens: number }
}>속성
타입
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 }>속성
타입
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)
여러 입력을 하나로 줄입니다. MCP reduce 도구와 최적 선택 노드와 같은 방식으로 동작합니다.
run(input: ReduceInput): Promise<ReduceResult>속성
타입
| 전략 | strategyConfig | 반환값 |
|---|---|---|
pick-best-llm | { criteria, inputKind?, llmModel? }. inputKind는 "text" 또는 "image-url"입니다. llmModel은 판단할 모델을 고르며, 그 모델의 크레딧 등급이 적용됩니다. | 언어 모델이 가장 좋다고 판단한 입력과 그 인덱스, 판단 이유 |
concat | { separator? }, 기본값은 빈 줄 | 모든 입력을 이어 붙인 텍스트 하나 |
first-non-empty | 없음 | 비어 있지 않은 첫 입력 |
count | 없음 | 입력의 개수 |
vote | { caseSensitive? }, 기본값은 false | 가장 많이 나온 입력. 동률이면 먼저 나온 입력입니다. |
merge-json | { strategy? }: "deep"(기본값) 또는 "shallow" | JSON 입력을 병합한 객체 하나 |
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가 발생합니다.
자주 묻는 질문
언어 모델에서 내 스키마에 맞는 JSON을 받으려면 어떻게 하나요?
시스템 프롬프트, 입력, JSON Schema 객체를 넣어 client.llm.structured를 호출하세요. 플랫폼이 모델이 그 형태로 답하도록 강제하고, 답을 검증하고, 유효하지 않은 답은 다시 시도한 다음, 파싱된 객체를 반환합니다.
structured 대신 structuredJob은 언제 사용하나요?
오래 걸리는 호출에는 structuredJob을 사용하세요. structured는 요청 한 번으로 답을 기다리며, 이는 기본 타임아웃 60초보다 긴 몇 분이 걸릴 수 있습니다. structuredJob은 jobId를 바로 반환합니다.
생성한 이미지 여러 장 중 가장 좋은 것을 고르려면 어떻게 하나요?
strategyId를 pick-best-llm으로, 내 기준을 criteria로, inputKind를 image-url로 지정해 client.reduce.run을 호출하고, 이미지 URL을 inputs로 전달하세요. 결과에 선택된 URL, 그 인덱스, 모델의 판단 이유가 담깁니다.
언어 모델이 필요 없는 Reduce 전략은 무엇인가요?
concat, first-non-empty, count, vote, merge-json은 모델 없이 실행됩니다. 언어 모델에 판단을 맡기는 전략은 pick-best-llm뿐입니다.
관련 문서
최적 선택
노드 실행
작업과 실행
피커, 프리셋, 프롬프트
최종 업데이트