# OpenAI function calling: 모델을 위한 read_url 툴

> read_url 툴을 사용한 OpenAI function calling: JSON 스키마와 링크를 모델용 깔끔한 Markdown으로 바꾸는 Python 루프. KYC 없이, 암호화폐 결제.

- URL: https://webreaderapi.com/ko/docs/openai/
- 마지막 업데이트: 2026-10-22
- 발행자: WebReaderAPI (https://webreaderapi.com)

함수 하나, `read_url`를 정의하고 모델이 요청할 때 실행하세요. 모델이 어떤 페이지를 읽을지 고르고, 여러분의 코드가 API를 호출해 Markdown을 돌려줍니다. 같은 스키마는 툴 호출을 지원하는 다른 모델에서도 작동합니다.

**빠른 답변 — OpenAI 모델이 함수 호출로 웹페이지를 읽게 하려면 어떻게 하나요?**

`tools` 목록에 `read_url` 함수를 선언한 다음, 루프를 실행하세요. 모델이 툴 호출을 반환하면 URL로 **WebReaderAPI**의 `/read` endpoint를 호출하고 Markdown을 `tool` 메시지로 돌려보냅니다. 페이지당 1크레딧, 브라우저 사용 시 2크레딧이며, 2026년 10월 기준 US$0.07/1K 페이지부터 시작합니다. KYC 없이, 암호화폐로 결제합니다.

## read_url 스키마는 어떻게 생겼나요?

파라미터 두 개면 충분합니다. 읽을 `url`(필수)와, 모델이 긴 페이지를 짧게 유지할 수 있게 해주는 선택적 `max_chars`입니다. 설명이 가장 중요한 부분입니다. 모델에게 툴이 무엇을 반환하는지, 언제 써야 하는지 알려주기 때문입니다. 사실에 기반해 짧게 유지하세요.

일부 에이전트 런타임이 이미 그 이름으로 제공하는 툴과 충돌하지 않도록, 툴 이름을 `web_fetch` 대신 `read_url`로 정했습니다.

*툴 정의*

```json
{
  "type": "function",
  "function": {
    "name": "read_url",
    "description": "Read a public web page and return its main content as Markdown, with its title and source URL. Use it when the user gives a link or when a page must be read to answer.",
    "parameters": {
      "type": "object",
      "properties": {
        "url": { "type": "string", "description": "The http or https address of the page." },
        "max_chars": { "type": "integer", "description": "Most characters to return, 1000 to 500000. Default 20000." }
      },
      "required": ["url"],
      "additionalProperties": false
    }
  }
}
```

## 툴 호출 루프는 어떻게 작동하나요?

`tools` 목록과 함께 대화를 보내세요. 응답에 툴 호출이 포함되어 있으면 각각을 실행하고, role이 `tool`이고 일치하는 `tool_call_id`를 가진 메시지로 결과를 덧붙인 뒤 모델에게 다시 물어보세요. 툴 호출 없이 응답이 오면 그것이 최종 답변입니다.

라운드 수에 상한을 두세요. 몇 개의 페이지에 대한 질문이라면 세 번이나 네 번이면 충분하고, 상한은 계속 더 요청하는 모델로부터 여러분을 보호합니다.

*Python: openai 패키지로 전체 루프 구현하기*

```python
import json
import os

import requests
from openai import OpenAI

READ_URL = {
    "type": "function",
    "function": {
        "name": "read_url",
        "description": "Read a public web page and return its main content as Markdown, with its title and source URL.",
        "parameters": {
            "type": "object",
            "properties": {
                "url": {"type": "string", "description": "The http or https address of the page."},
                "max_chars": {"type": "integer", "description": "Most characters to return. Default 20000."},
            },
            "required": ["url"],
            "additionalProperties": False,
        },
    },
}


def read_url(url, max_chars=20000):
    resp = requests.get(
        "https://webreaderapi.com/api/v1/read",
        headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
        params={"url": url, "max_chars": max_chars},
        timeout=75,
    )
    body = resp.json()
    if resp.status_code != 200:
        return "Could not read the page: " + body["error"]["code"]
    return "# " + (body.get("title") or url) + "\nSource: " + body["final_url"] + "\n\n" + body["markdown"]


client = OpenAI()
model = os.environ.get("OPENAI_MODEL", "gpt-4.1-mini")
messages = [{"role": "user", "content": "What does https://example.com/blog/first-post announce? Answer in two sentences."}]

for _ in range(4):
    reply = client.chat.completions.create(model=model, messages=messages, tools=[READ_URL])
    msg = reply.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)
        messages.append({"role": "tool", "tool_call_id": call.id, "content": read_url(**args)})

```

## 토큰 사용량과 비용을 어떻게 줄이나요?

세 가지 설정이 중요합니다. `max_chars`을 페이지당 약 8,000~20,000자로 적당히 유지하세요. 반환하는 모든 문자를 모델이 읽기 때문입니다. 기본 `main_content=true`을 유지해 메뉴와 푸터가 컨텍스트에 도달하지 않게 하세요. 그리고 위에서처럼 루프에 상한을 두어 한 질문이 수십 번의 읽기를 유발하지 않게 하세요.

저희 쪽 비용은 문자당이 아니라 페이지당입니다: 1 크레딧 = 브라우저 없이 페이지 1회 읽기, 페이지가 JavaScript를 렌더링하는 데 브라우저가 필요하면 2 크레딧. 실패한 요청이나 사이트의 robots.txt가 거부한 요청은 과금되지 않습니다. 하루에 300페이지를 읽는 고객지원 어시스턴트는 한 달에 약 9,000크레딧을 사용하며, 이는 10,000페이지(US$3.75) 범위 안입니다.

## 스키마가 다른 모델에서도 작동하나요?

네, 래퍼를 조금 바꾸면 됩니다. 대부분의 툴 호출 API는 파라미터로 JSON Schema를, 툴에는 이름과 설명을 받으므로 `read_url`는 프로바이더 간에 그대로 옮겨집니다. [MCP 페이지](https://webreaderapi.com/ko/docs/mcp/)는 Model Context Protocol을 사용하는 클라이언트를 위해 같은 함수를 패키징하고, [LangChain 페이지](https://webreaderapi.com/ko/docs/langchain/)는 LangChain 및 LlamaIndex 에이전트를 위해 이를 감쌉니다.

## 페이지가 거부될 때 모델이 무엇을 보아야 하나요?

위 루프가 반환하는 것처럼 짧고 명확한 문장입니다: "Could not read the page: blocked_by_robots". 모델은 이를 잘 처리하고 다른 출처로 대체하거나 사용자에게 알립니다. 우리는 robots.txt를 준수합니다. 크롤러는 WebReaderAPI로 자신을 식별하고, 자신 또는 모든 로봇을 위해 작성된 규칙을 따르며, 사이트가 허용하지 않은 페이지는 읽지 않습니다. 거부된 페이지는 과금되지 않습니다. 사이트가 우리 요청을 거부하면 그 응답을 그대로 반환합니다. 우리는 CAPTCHA를 풀거나, 계정에 로그인하거나, 차단이나 페이월을 우회하려 시도하지 않습니다. 이러한 거부 중 어느 것도 크레딧을 사용하지 않습니다.

## 함수 호출: 자주 묻는 질문

### Responses API가 필요한가요, Chat Completions가 필요한가요?

예제는 `tools` 파라미터와 함께 Chat Completions를 사용합니다. 같은 함수 정의와 같은 `read_url` 본문은 함수 툴을 받아들이는 다른 OpenAI 인터페이스에서도 작동하며, 주변 루프만 바뀝니다.

### 모델이 한 턴에 여러 페이지를 읽을 수 있나요?

네. 응답에 여러 툴 호출이 포함될 수 있고, 루프는 각각을 실행해 모든 결과를 반환한 뒤 모델에게 다시 물어봅니다. 모델이 한 번에 많은 페이지를 읽을 때는 `max_chars`을 낮게 유지하세요.

### API 키가 모델에 노출되나요?

아니요. 키는 코드의 환경 변수에 남습니다. 모델은 툴의 이름, 설명, 그리고 여러분의 함수가 반환하는 텍스트만 봅니다.

### 거부된 페이지에는 비용이 얼마나 드나요?

아무것도 들지 않습니다. 1 크레딧 = 브라우저 없이 페이지 1회 읽기, 페이지가 JavaScript를 렌더링하는 데 브라우저가 필요하면 2 크레딧. 실패한 요청이나 사이트의 robots.txt가 거부한 요청은 과금되지 않습니다.

---

WebReaderAPI: WebReaderAPI는 AI 에이전트를 위한 웹 리더 API로, 모든 공개 웹페이지를 깔끔한 Markdown으로 변환하며 신분증 업로드 없이 암호화폐로 결제합니다. US$0.07/1K 페이지부터 시작하는 선불 크레딧은 만료되지 않으며, robots.txt를 준수합니다. KYC 없음 — 신분증도, 셀피도, 서류도 없습니다. 암호화폐로 결제하세요.
