# open-weight 모델로 하는 tool calling과 JSON 출력

> OpenAI 형식의 open-weight 모델로 하는 function calling: tools, tool_choice, 호출-응답 루프, 그리고 유효한 JSON 출력을 위한 response_format.

- URL: https://llmwithcrypto.com/ko/docs/tool-calling/
- 마지막 업데이트: 2026-10-23
- 발행자: LLMwithCrypto (https://llmwithcrypto.com)

모델은 여러분의 코드에 함수 실행을 요청하고 그 결과를 답변에 사용할 수 있으며, 또는 프로그램이 직접 읽는 JSON 객체로 응답할 수도 있습니다. 둘 다 OpenAI 요청 형식을 사용하고, 둘 다 선택한 모델에 따라 달라집니다.

**빠른 답변 — LLMwithCrypto에서 tool calling은 어떻게 동작하나요?**

함수를 `tools`에 OpenAI 형식으로 보내세요. 모델이 함수를 원하면 **LLMwithCrypto**이 함수 이름과 JSON arguments가 담긴 `finish_reason: "tool_calls"`를 반환합니다. 여러분의 코드가 이를 실행하고 결과를 `tool` 메시지로 다시 보냅니다. 구조화된 응답에는 `response_format: {"type": "json_object"}`로 유효한 JSON을 요청합니다. 둘 다 이를 지원하는 모델에서 동작하며, 2026년 10월 23일 문서에 기재된 대로입니다.

## 어떤 모델이 tools와 JSON을 지원하나요?

스트리밍은 모든 모델에서 동작하며, tool calling과 JSON 출력은 각 모델이 자체적으로 지원하는 범위를 따릅니다.

의존하기 전에 사용할 모델에서 직접 스키마를 테스트하세요. tools를 지원하지 않는 모델은 함수를 호출하는 대신 일반 텍스트로 응답합니다. JSON 모드를 지원하지 않는 모델은 `response_format`에 대해 `400` 오류를 반환합니다.

## 루프란 무엇인가요?

1. **함수 설명하기** — 각 tool에는 name, 모델이 언제 사용할지 판단하기 위해 읽는 description, 그리고 JSON Schema 형식의 parameters가 있습니다.
2. **tools와 함께 대화 보내기** — 모델은 텍스트로 응답하거나, finish_reason: "tool_calls"로 멈추고 message.tool_calls에 하나 이상의 호출을 담아 반환합니다.
3. **각 호출 실행하기** — function.arguments(JSON 문자열)을 파싱하고 함수를 실행하세요. arguments를 맹신하지 말고, 다른 사용자 입력처럼 검증하세요.
4. **결과 다시 보내기** — assistant 메시지를 추가한 뒤, 각 호출마다 tool_call_id와 결과를 텍스트로 담은 tool 메시지를 하나씩 추가하세요. API를 다시 호출하고, 모델이 텍스트로 응답할 때까지 반복하세요.

*Python: gpt-oss-120b로 완성하는 tool 루프*

```python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url="https://llmwithcrypto.com/api/v1",
    api_key=os.environ["LLMWITHCRYPTO_API_KEY"],
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_order_status",
            "description": "Return the delivery status of an order.",
            "parameters": {
                "type": "object",
                "properties": {"order_id": {"type": "string"}},
                "required": ["order_id"],
            },
        },
    }
]


def get_order_status(order_id):
    return {"order_id": order_id, "status": "shipped", "eta": "2026-10-25"}


messages = [{"role": "user", "content": "Where is my order A-1042?"}]
while True:
    resp = client.chat.completions.create(model="gpt-oss-120b", messages=messages, tools=tools, max_tokens=300)
    msg = resp.choices[0].message
    if not msg.tool_calls:
        print(msg.content)
        break
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_order_status(**args)
        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})

```

## 응답에서 tool 호출은 어떻게 보이나요?

assistant 메시지에는 텍스트가 없고 `tool_calls` 배열이 있으며, choice는 `finish_reason: "tool_calls"`로 끝납니다. arguments는 객체가 아니라 JSON 문자열로 전달됩니다. 아래의 id와 수치는 예시입니다.

*함수 호출을 요청하는 응답 (예시)*

```json
{
  "id": "chatcmpl-3c9d27e1",
  "object": "chat.completion",
  "created": 1792742400,
  "model": "gpt-oss-120b",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_7f31",
            "type": "function",
            "function": {
              "name": "get_order_status",
              "arguments": "{\"order_id\": \"A-1042\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 118,
    "completion_tokens": 22,
    "total_tokens": 140
  }
}

```

## tools가 언제 사용되는지 어떻게 제어하나요?

- **`"tool_choice": "auto"`** (`tools`가 설정된 경우 기본값): 모델이 결정합니다.
- **`"tool_choice": "none"`**: tools가 나열되어 있어도 모델이 텍스트로 응답합니다.
- **`"tool_choice": "required"`**: 모델이 최소 하나의 tool을 호출해야 합니다.
- **`{"type": "function", "function": {"name": "get_order_status"}}`**: 모델이 그 하나를 반드시 호출해야 합니다.

모든 tool 정의는 매 호출마다 입력으로 전송되므로, 긴 설명과 많은 tools는 입력 토큰을 늘립니다. 설명은 짧게 유지하고 해당 단계에 필요한 tools만 보내세요.

## 유효한 JSON을 돌려받으려면 어떻게 하나요?

`response_format`를 `{"type": "json_object"}`로 설정하고 시스템 메시지에 예상되는 키를 설명하세요. JSON 모드는 응답이 파싱된다는 것만 보장하며, 원하는 키가 있다는 것은 보장하지 않습니다. 안정적인 출력을 위해 `temperature`를 낮게 유지하고, 사용하기 전에 코드에서 파싱된 객체를 확인하세요.

*Node.js: response_format으로 JSON 응답 받기*

```javascript
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://llmwithcrypto.com/api/v1',
  apiKey: process.env.LLMWITHCRYPTO_API_KEY,
});

const resp = await client.chat.completions.create({
  model: 'gpt-oss-120b',
  messages: [
    { role: 'system', content: 'Reply with a JSON object {"sentiment": "positive" | "negative" | "neutral", "reason": string}.' },
    { role: 'user', content: 'The delivery was late but support fixed it in an hour.' },
  ],
  response_format: { type: 'json_object' },
  temperature: 0.2,
  max_tokens: 150,
});
const result = JSON.parse(resp.choices[0].message.content);
console.log(result.sentiment, '-', result.reason);

```

## tools와 JSON: 자주 묻는 질문

### tool 호출에 추가 비용이 드나요?

자체 요금은 없습니다. tool 정의, 호출, 다시 보내는 결과는 모두 토큰이며 다른 것과 동일하게 과금됩니다. 잔액에서 토큰 단위로 결제합니다. 입력 및 출력 토큰은 모델 자체의 tokenizer로 계산되며, 해당 모델의 1M 토큰당 가격으로 청구됩니다. 오류로 끝난 호출은 비용이 들지 않습니다.

### 모델이 여러 tools를 한 번에 호출할 수 있나요?

일부 모델은 `tool_calls`에 둘 이상의 호출을 반환합니다. 모두 실행하고, 다음 요청 전에 각 호출마다 `tool` 메시지를 하나씩 보내세요.

### API가 내 함수를 실행하나요?

아니요. 모델은 어떤 함수를 어떤 arguments로 호출할지만 알려주며, 실행은 여러분의 코드가 합니다. 저희 쪽에서는 아무것도 실행되지 않고, 주고받은 내용도 저장되지 않습니다: 프롬프트와 모델의 답변은 저장하지 않습니다. 응답이 전송되면 그 텍스트는 사라집니다. 각 호출은 시간, 계정, 모델, 토큰 수를 담은 로그 한 줄을 남기며, 이는 30일 후 삭제됩니다.

---

LLMwithCrypto: LLMwithCrypto는 오픈 웨이트 모델을 위한 OpenAI 호환 LLM API입니다 — DeepSeek V4 Pro, DeepSeek V4.1 Flash, DeepSeek R1, Qwen3.5 397B A17B, Qwen3 235B A22B Instruct, gpt-oss-120b, gpt-oss-20b, GLM-4.7, Llama 3.3 70B Instruct, Gemma 4 31B, Mistral Large 3, Mistral Small 4 — 충전해 둔 암호화폐 잔액에서 토큰 단위로 결제하며, 공개 호스트 가격 중앙값보다 5% 저렴합니다. KYC 없이: 신분증도, 셀피도, 서류도 필요하지 않습니다. 프롬프트와 출력은 저장되지 않습니다.
