AI 调用文档

这是一个 OpenAI 兼容的 AI 接口。你可以像调用 OpenAI 一样,把 base_url 换成我们的地址,就能在自己的程序、脚本或应用里使用它。

支持流式输出、多模型切换,中文表现良好。接口已开启 CORS,浏览器可以直接调用。

⚠️
免责声明

本服务由个人搭建并维护,不保证 100% 可用。请勿用于关键生产链路,也请勿滥用。

快速开始

三步就能跑起来:

  1. 拿到接口地址https://ai.lelegame.dpdns.org/v1
  2. 用任意 OpenAI 兼容客户端 / 库官方 SDK、第三方客户端、curl 都可以。
  3. 填地址 + 模型名 fast立刻就能对话。
💡

如果你用 OpenAI 官方 SDK,只需要把 base_url 改成 https://ai.lelegame.dpdns.org/v1,其余代码一行都不用动。

接口地址

用途地址
Base URLhttps://ai.lelegame.dpdns.org/v1
Chat Completionshttps://ai.lelegame.dpdns.org/v1/chat/completions
模型列表https://ai.lelegame.dpdns.org/v1/models

鉴权

✅

当前无需 API Key。直接请求即可,不需要在 Header 里带 Authorization。

很多客户端和 SDK 会强制要求填写 Key,随便填一个非空字符串即可,例如 ollama、sk-xxx、123。

🔒

未来可能会加入 API Key 校验,届时会在本页公告。请定期回来看一眼。

模型列表

通过 GET /v1/models 获取当前可用的模型。

curl https://ai.lelegame.dpdns.org/v1/models
{
  "object": "list",
  "data": [
    { "id": "fast", "object": "model", "created": 1712345678, "owned_by": "local" },
    { "id": "3b",   "object": "model", "created": 1712345678, "owned_by": "local" }
  ]
}

注意事项

  • 模型名区分大小写,必须和列表里的 id 完全一致。
  • 模型列表可能随服务端配置变化,调用前建议先请求一次 /v1/models。

请求格式

POST /v1/chat/completions Header:Content-Type: application/json

请求体字段

字段类型必填说明
modelstring是模型名,例如 fast
messagesarray是对话历史,每条包含 role 和 content
streamboolean否是否流式返回,默认 false
temperaturenumber否0 ~ 2,默认 0.7
max_tokensinteger否最大生成长度

完整请求体示例

{
  "model": "fast",
  "messages": [
    { "role": "system", "content": "你是一个乐于助人的中文助手,回答尽量简洁。" },
    { "role": "user", "content": "用一句话解释什么是 API。" }
  ],
  "stream": false,
  "temperature": 0.7,
  "max_tokens": 512
}

响应格式

当 stream 为 false(默认)时,服务端一次性返回完整的 JSON。

响应字段说明

字段说明
id本次请求的唯一 ID
object固定为 chat.completion
createdUnix 时间戳(秒)
model实际使用的模型名
choices[0].message.contentAI 回复的正文
choices[0].finish_reason结束原因,通常为 stop
usagetoken 统计

完整响应示例

{
  "id": "chatcmpl-8f3a1c2b9d4e",
  "object": "chat.completion",
  "created": 1712345678,
  "model": "fast",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "API 就是程序之间约定好的一套调用接口。"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 32, "completion_tokens": 48, "total_tokens": 80 }
}

流式输出

把 stream 设为 true 后,服务端会以 SSE(Server-Sent Events)格式逐块返回内容:

  • 每一行以 data: 开头,后面跟一个 JSON 对象;
  • 每个 JSON 里,文本片段在 choices[0].delta.content;
  • 流的最后一行是 data: [DONE]。
data: {"id":"chatcmpl-8f3a1c2b9d4e","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-8f3a1c2b9d4e","choices":[{"index":0,"delta":{"content":"API"},"finish_reason":null}]}

data: {"id":"chatcmpl-8f3a1c2b9d4e","choices":[{"index":0,"delta":{"content":" 就是"},"finish_reason":null}]}

data: {"id":"chatcmpl-8f3a1c2b9d4e","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]
⚠️

解析流时必须处理「不完整的行」。把每次读到的内容追加到一个 buffer 字符串,按 \n 切分,把最后一段留在 buffer 里,等下次数据到达再拼接。

代码示例

点击右上角标签切换语言,直接复制即可运行。通用参数:请求地址 https://ai.lelegame.dpdns.org/v1/chat/completions,模型名 fast,API Key 随意填非空字符串。

# 非流式
curl https://ai.lelegame.dpdns.org/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "fast",
    "messages": [
      {"role": "user", "content": "用一句话解释什么是 API"}
    ],
    "stream": false
  }'

# 流式(加 -N 关闭缓冲,才能逐块看到输出)
curl -N https://ai.lelegame.dpdns.org/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "fast",
    "messages": [
      {"role": "user", "content": "写一首关于秋天的短诗"}
    ],
    "stream": true
  }'
# pip install openai
from openai import OpenAI

client = OpenAI(
    base_url="https://ai.lelegame.dpdns.org/v1",
    api_key="ollama",  # 服务端当前不校验
)

# 非流式
resp = client.chat.completions.create(
    model="fast",
    messages=[
        {"role": "system", "content": "你是一个乐于助人的中文助手。"},
        {"role": "user", "content": "用一句话解释什么是 API。"},
    ],
)
print(resp.choices[0].message.content)

# 流式
stream = client.chat.completions.create(
    model="fast",
    messages=[{"role": "user", "content": "写一首关于秋天的短诗"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
print()
# pip install requests
import json
import requests

URL = "https://ai.lelegame.dpdns.org/v1/chat/completions"

payload = {
    "model": "fast",
    "messages": [{"role": "user", "content": "写一首关于秋天的短诗"}],
    "stream": True,
}
headers = {"Content-Type": "application/json"}

with requests.post(URL, json=payload, headers=headers,
                   stream=True, timeout=120) as r:
    r.raise_for_status()
    for raw in r.iter_lines(decode_unicode=True):
        if not raw:
            continue
        if not raw.startswith("data: "):
            continue
        data = raw[6:].strip()
        if data == "[DONE]":
            break
        chunk = json.loads(data)
        delta = chunk["choices"][0]["delta"].get("content")
        if delta:
            print(delta, end="", flush=True)

print()
// Node.js 18+,保存为 stream.mjs 后运行:node stream.mjs
const URL = "https://ai.lelegame.dpdns.org/v1/chat/completions";

const res = await fetch(URL, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "fast",
    messages: [{ role: "user", content: "写一首关于秋天的短诗" }],
    stream: true,
  }),
});

if (!res.ok) throw new Error("HTTP " + res.status);

const reader = res.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });

  const lines = buffer.split("\n");
  buffer = lines.pop();

  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed.startsWith("data:")) continue;

    const data = trimmed.slice(5).trim();
    if (data === "[DONE]") {
      process.stdout.write("\n");
      process.exit(0);
    }

    const chunk = JSON.parse(data);
    const delta = chunk.choices?.[0]?.delta?.content;
    if (delta) process.stdout.write(delta);
  }
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>流式对话 Demo</title>
</head>
<body>
  <pre id="out" style="white-space:pre-wrap;font-family:sans-serif"></pre>
  <script>
    const out = document.getElementById("out");

    async function chat(prompt) {
      const res = await fetch("https://ai.lelegame.dpdns.org/v1/chat/completions", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          model: "fast",
          messages: [{ role: "user", content: prompt }],
          stream: true,
        }),
      });

      if (!res.ok) throw new Error("HTTP " + res.status);

      const reader = res.body.getReader();
      const decoder = new TextDecoder("utf-8");
      let buffer = "";

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });

        const lines = buffer.split("\n");
        buffer = lines.pop();

        for (const line of lines) {
          const trimmed = line.trim();
          if (!trimmed.startsWith("data:")) continue;

          const data = trimmed.slice(5).trim();
          if (data === "[DONE]") return;

          const chunk = JSON.parse(data);
          const delta = chunk.choices?.[0]?.delta?.content;
          if (delta) out.textContent += delta;
        }
      }
    }

    chat("写一首关于秋天的短诗");
  </script>
</body>
</html>
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
)

func main() {
	url := "https://ai.lelegame.dpdns.org/v1/chat/completions"

	body := map[string]any{
		"model":  "fast",
		"stream": true,
		"messages": []map[string]string{
			{"role": "user", "content": "写一首关于秋天的短诗"},
		},
	}
	buf, _ := json.Marshal(body)

	req, _ := http.NewRequest("POST", url, bytes.NewReader(buf))
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	scanner := bufio.NewScanner(resp.Body)
	scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if !strings.HasPrefix(line, "data:") {
			continue
		}
		data := strings.TrimSpace(strings.TrimPrefix(line, "data:"))
		if data == "[DONE]" {
			break
		}

		var chunk struct {
			Choices []struct {
				Delta struct {
					Content string `json:"content"`
				} `json:"delta"`
			} `json:"choices"`
		}
		if err := json.Unmarshal([]byte(data), &chunk); err != nil {
			continue
		}
		if len(chunk.Choices) > 0 {
			fmt.Print(chunk.Choices[0].Delta.Content)
		}
	}
	fmt.Println()
}
<?php
$url = "https://ai.lelegame.dpdns.org/v1/chat/completions";

$payload = json_encode([
    "model"    => "fast",
    "messages" => [
        ["role" => "user", "content" => "用一句话解释什么是 API"],
    ],
    "stream"   => false,
], JSON_UNESCAPED_UNICODE);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_HTTPHEADER     => ["Content-Type: application/json"],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 120,
]);

$res  = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($code !== 200) {
    exit("请求失败,HTTP " . $code . "\n" . $res);
}

$data = json_decode($res, true);
echo $data["choices"][0]["message"]["content"] ?? "解析失败";
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class ChatDemo {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "model": "fast",
              "messages": [
                {"role": "user", "content": "用一句话解释什么是 API"}
              ],
              "stream": false
            }
            """;

        HttpRequest req = HttpRequest.newBuilder()
                .uri(URI.create("https://ai.lelegame.dpdns.org/v1/chat/completions"))
                .header("Content-Type", "application/json")
                .timeout(Duration.ofSeconds(120))
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpResponse<String> resp = HttpClient.newHttpClient()
                .send(req, HttpResponse.BodyHandlers.ofString());

        System.out.println(resp.statusCode());
        System.out.println(resp.body());
    }
}

错误码

状态码含义排查方向
200成功—
400请求体格式错误检查 JSON 是否合法、字段名与类型是否正确
401未授权未来加 Key 后可能出现
404路径错误确认 URL 是 /v1/chat/completions
429请求过多降低频率,稍后重试
500服务端错误服务可能未启动,稍后重试
502 / 503网关错误服务端离线或隧道中断
504超时改用流式或缩短输入

常见问题

为什么有时候回复很慢?
模型跑在本地设备上,速度取决于硬件性能。另外,第一次请求需要加载模型,会比后续请求慢很多。
为什么偶尔出现 502?
后端(本地设备)可能正在重启、更新,或者网络/隧道短暂中断。稍等片刻重试即可。
支持并发请求吗?
目前后端限制并发为 1,多个请求会排队依次处理。
支持上传文件或图片吗?
当前不支持,只接受纯文本。
能用 OpenAI 官方 SDK 吗?
可以。把 base_url 改为 https://ai.lelegame.dpdns.org/v1 即可。
有没有 API Key?
目前没有,也不需要。客户端强制要求填写的话,随便填个非空字符串即可。
浏览器直接调用会跨域失败吗?
不会。服务端已开启 CORS,浏览器可以直接用 fetch 调用。

限制与注意事项

🚫

不要滥用。这是个人搭建的免费服务,请勿高并发刷接口、批量跑数据或做压力测试。

  • 不要用于违法用途。
  • 不要上传敏感信息。对话内容会被记录到服务端日志。
  • 服务可能随时变动。接口地址、模型名、限流策略都可能调整。
  • CORS 已开启。浏览器端可以直接调用。
  • 超时建议。非流式请求建议客户端超时 60 秒以上;流式请求建议 120 秒左右。
  • 并发限制为 1。请串行发送请求。