支持与不支持的功能
| 项目 | 状态 | 备注 |
|---|---|---|
| 聊天补全 | 是 | POST /v1/chat/completions |
| 响应 | 是 | POST /v1/responses |
| 流式输出 (SSE) | 是 | stream: true,末尾包含使用情况 |
| 函数调用 | 是 | tools、tool_choice、流式输出 tool_calls、结果以 role: "tool" 形式返回 |
| JSON 模式 | 是 | response_format: {"type":"json_object"} |
| 采样控制 | 是 | temperature、top_p、stop、seed、penalties |
| 图像、音频、视频 | 否 | 仅文本 |
| 嵌入、微调 | 否 | 不提供 |
该模型是一个开放权重文本模型,ID 为 uncensored,由独立提供商运营。它针对合法成人用途提供无内容拒绝的回答,并非 OpenAI 或其他供应商的官方 API。以下所有示例均使用基础 URL https://api.chatgptunfiltered.com/v1。
列表中未提及的功能应视为不可用,除非 文档另有说明。这包括供应商特定的托管工具,它们与下面展示的函数调用不同。
流式输出
设置 stream: true 并遍历分片。在文本分片到达时打印它们,并从最后一个分片中读取使用情况。
from openai import OpenAI
client = OpenAI(
base_url="https://api.chatgptunfiltered.com/v1",
api_key="YOUR_KEY",
)
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Write a four-line poem about a night train."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\nusage:", chunk.usage)
记住两点。首先,流式请求会占用并发槽位直到流结束,因此废弃的流会占用槽位;用户离开时请关闭它。其次,最后一个分片可能包含使用情况但没有文本,因此请像上面那样保护对 choices 的访问。
在 Web 应用中,将分片转发到浏览器,例如通过你自己的 SSE 路由,并让页面将每个文本片段追加到消息中。在流关闭后在服务器上进行使用情况记账,此时最后一个分片会告诉你请求的成本。
端到端工具调用
声明一个函数,让模型请求调用它,你自己运行它,然后通过 role: "tool" 发送结果。
import json
tools = [{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Look up the status of an order by its ID.",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
},
}]
def get_order_status(order_id):
return {"order_id": order_id, "status": "shipped"}
messages = [{"role": "user", "content": "Where is order A-1042?"}]
r = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = r.choices[0].message
if msg.tool_calls:
messages.append(msg.model_dump(exclude_none=True))
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),
})
final = client.chat.completions.create(model="uncensored", messages=messages, tools=tools)
print(final.choices[0].message.content)
else:
print(msg.content)
模型决定是否调用函数;tool_choice 让你可以控制这一点。函数本身在你的代码中运行,而不是在服务器上,因此你控制它可以访问的资源。在执行操作之前验证参数,因为模型可能会发送错误的类型或缺失字段。
流式工具调用
当您将 stream: true 与 tools 结合使用时,调用会分片到达。函数名最先到达,参数字符串以片段形式到达,您必须通过 index 将它们连接起来。
calls = {}
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Where is order A-1042?"}],
tools=tools,
stream=True,
)
for chunk in stream:
if not chunk.choices:
continue
for tc in chunk.choices[0].delta.tool_calls or []:
slot = calls.setdefault(tc.index, {"id": "", "name": "", "args": ""})
if tc.id:
slot["id"] = tc.id
if tc.function and tc.function.name:
slot["name"] = tc.function.name
if tc.function and tc.function.arguments:
slot["args"] += tc.function.arguments
for slot in calls.values():
print(slot["name"], json.loads(slot["args"]))
只有在流结束后才解析 args。接收了一半的字符串不是有效的 JSON。之后,运行函数并按非流式示例中的方式发送结果。
JSON 模式
对于结构化回复,开启 JSON 模式并在提示词中描述你需要的形状。
r = client.chat.completions.create(
model="uncensored",
temperature=0.2,
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Reply with JSON only: {\"mood\": string, \"intensity\": integer from 1 to 5}."},
{"role": "user", "content": "Rate this line: The door slammed and nobody spoke for an hour."},
],
)
try:
data = json.loads(r.choices[0].message.content)
except json.JSONDecodeError:
data = None # retry once, or fall back
print(data)
JSON 模式使生成有效 JSON 的可能性大大增加,但它不会强制执行你的 schema。请自行检查键和类型,并保留重试路径。低 temperature 有助于提取。采样设置如 temperature、top_p、stop、seed 和 penalties 会透传。
一个有用的模式是将 JSON 模式与小型验证函数结合,该函数返回清晰的错误消息,然后将该消息反馈给模型进行一次重试:“你的上次回复缺少 intensity 键。请再次回复有效的 JSON。”模型通常在第二次尝试时自行纠正,额外的请求成本不到一分钱。
处理流式传输期间的故障
流式传输以普通请求不会遇到的方式失败:连接可能在文本传输一半时断开。请为此做好准备。
import time
from openai import RateLimitError, APIConnectionError
def stream_reply(messages):
for attempt in range(3):
parts = []
try:
stream = client.chat.completions.create(
model="uncensored", messages=messages, stream=True
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
parts.append(chunk.choices[0].delta.content)
return "".join(parts)
except RateLimitError:
time.sleep(5)
except APIConnectionError:
time.sleep(1)
raise RuntimeError("stream failed after retries")
这里的 429 表示达到限制或暂时过载,正确的做法是等待 5 秒,这与 API 发送的 Retry-After: 5 一致。如果流在中间断开,您会得到一个部分答案。决定是丢弃并重试,还是保留它并让模型继续。对于聊天界面,丢弃并重试通常对用户来说更不容易混淆。
需规划的速率限制和成本
- 速率: 完整密钥每分钟 300 个请求,试用密钥每分钟 60 个。
- 并发: 完整密钥 8 个,试用密钥 2 个。记住每个打开的流都会计入。
- 大小: 请求体最大 8 MB;100,000 token 的上下文窗口,包括提示词和补全。
- 429 处理: 代码
rate_limited和concurrency表示您超出了限制;暂时过载返回 429 并附带Retry-After: 5。
成本示例:涉及两个请求的工具调用交互,第一个请求输入 1,200 token,输出 80;第二个请求输入 1,400 token,输出 150。输入成本为 2,600 x $0.15 / 1M = $0.00039。输出成本为 230 x $0.50 / 1M = $0.000115。总计 $0.000505。失败或被拒绝的请求不收费。费率见 定价,完整方法列表在 文档中。从 API 密钥页面获取 $0.50 试用额度。有关模型的背景信息,请参阅 了解无审查 ChatGPT 模型。硬性限制适用于所有模式:禁止涉及未成年人的性内容。
发布前检查清单
- 工具参数在 try 块中使用
json.loads进行解析,并在运行任何操作之前检查预期的键和类型。 - 你暴露的函数只能执行你允许匿名用户执行的操作。由模型决定何时调用它们,而不是你。
- JSON 回复经过验证,失败时重试一次。
- 当用户离开页面时,流会被关闭,因此它们不会占用并发槽位。
- 代码中的并发限制等于或低于密钥的限制:完整密钥为 8。
X-Request-Id的请求 ID 已记录。- 密钥存储在服务器上且可重新生成;旧密钥立即失效。
服务仅限文本,因此需要图像或嵌入的功能必须来自其他提供商。
