作者说明:本文由FlowBarCN团队提供,涉及自家服务,经AI辅助整理。代码仅在本地离线验证,没有发起真实模型请求;演示模型和能力配置均为虚构。
配置里写了temprature,服务端才报错;调用方塞进extra_body,绕开了你在外层定义的字段;stream写成字符串"false",在Python里却是真值。这些都适合在发请求前检查。
本篇从零做一个纯文本、非流式、固定演示模型的请求构造器,交付独立UTF-8 JSON字节。只生成请求,不发送请求,不需要Key,也不会扣模型调用费用。
1. 范围与环境
Python 3.11,标准库即可。把实现保存为request_guard.py。输入是Python字典,不是原始HTTP文本;如果你的入口是原始JSON,还要单独处理请求体大小、重复键和解析失败。本文不提供Web服务,也不把字符限制当Token预算。
2026年10月25日检索了近24—72小时官方动态;已打开10月23日的OpenAI存储与训练环境文章,两者都不直接提供本课需要的模型参数兼容结论。因此本篇采用常青工程教学,不把新闻、厂商能力或文档更新时间写成FlowBarCN新功能。
官方Chat Completions文档说明参数支持因模型而异。下面的demo-text-v1完全虚构,2048输出上限、20条消息、每条10000字符是教学项目的本地政策,不是平台或模型上限。真实接入前需按目标服务逐项确认。
2. 设计允许列表
| 字段 | 本例处理 |
|---|---|
| model | 只允许精确演示ID |
| messages | 1—20条;只接受role/content两个键和文本内容 |
| temperature | 可省略;有限数值0—2;拒绝bool、字符串、NaN |
| max_completion_tokens | 可省略;1—2048整数;bool不算整数 |
| stream | 可省略或显式false;true报未实现 |
| extra_body及其余字段 | 拒绝,不直接透传 |
省略字段就不写入最终JSON,显式null则拒绝,避免把“没有设置”和“发送null”混在一起。对话角色只保留system/user/assistant是本例的教学范围,不表示其他角色普遍无效;工具消息、图片、多模态和完整对话顺序检查不在本例实现范围内。
3. 完整实现
"""Strict, narrow request builder for a fictional text-only profile."""
import json
import math
MODEL = "demo-text-v1"
ALLOWED = frozenset({"model", "messages", "temperature", "max_completion_tokens", "stream"})
def build_request(raw):
if type(raw) is not dict or any(type(k) is not str for k in raw):
raise ValueError("request_must_be_string_keyed_object")
if set(raw) - ALLOWED:
raise ValueError("unknown_parameter")
if raw.get("model") != MODEL:
raise ValueError("unapproved_model")
messages = raw.get("messages")
if type(messages) is not list or not 1 <= len(messages) <= 20:
raise ValueError("invalid_messages")
copied = []
for message in messages:
if type(message) is not dict or set(message) != {"role", "content"}:
raise ValueError("invalid_message_shape")
if message["role"] not in ("system", "user", "assistant"):
raise ValueError("unsupported_role")
text = message["content"]
if type(text) is not str or not text.strip() or len(text) > 10000:
raise ValueError("invalid_content")
copied.append({"role": message["role"], "content": text})
out = {"model": MODEL, "messages": copied}
if "temperature" in raw:
value = raw["temperature"]
if type(value) not in (int, float) or not 0 <= value <= 2 or not math.isfinite(value):
raise ValueError("invalid_temperature")
out["temperature"] = value
if "max_completion_tokens" in raw:
value = raw["max_completion_tokens"]
if type(value) is not int or not 1 <= value <= 2048:
raise ValueError("invalid_output_limit")
out["max_completion_tokens"] = value
if "stream" in raw:
if type(raw["stream"]) is not bool:
raise ValueError("invalid_stream")
if raw["stream"]:
raise ValueError("stream_not_implemented")
out["stream"] = False
# Return independent bytes, not a mutable view of the caller's objects.
return json.dumps(out, ensure_ascii=False, allow_nan=False).encode("utf-8")
if __name__ == "__main__":
request = {"model": MODEL, "messages": [{"role": "user", "content": "Hello"}],
"temperature": 0.2, "max_completion_tokens": 128, "stream": False}
print(build_request(request).decode("utf-8"))
try:
build_request({**request, "extra_body": {"temperature": 9}})
except ValueError as exc:
print(str(exc))
为什么用type(value)而不是只用isinstance(value,int)?因为Python的bool是int子类。预算字段收到True时,让它当成1继续走会掩盖输入错误。temperature还检查有限数值,序列化再次用allow_nan=False,避免生成非标准JSON数值。
为什么返回bytes?请求构建结束后,调用方再修改原字典,不会改变已经构造好的请求。本文不返回对原messages列表的共享引用,也不打印生产数据。
4. 运行与输入输出
执行:
python request_guard.py
实际本地演示输出:
{"model": "demo-text-v1", "messages": [{"role": "user", "content": "Hello"}], "temperature": 0.2, "max_completion_tokens": 128, "stream": false}
unknown_parameter
第二条请求带了extra_body,即使里面的字段看起来合理也拒绝。在一个受控入口里,如果允许任意额外请求体、请求头或URL覆盖,外层允许列表就失去意义。本文构造器不接受这些入口;后续发送层同样不能在构造后重新混入未校验数据。
5. 复制即可运行的边界回归
新建test_request_guard.py,与实现放在同一个目录:
import json
import unittest
from request_guard import MODEL, build_request
class RequestTests(unittest.TestCase):
def setUp(self):
self.raw = {"model": MODEL, "messages": [{"role": "user", "content": "Hi"}]}
def test_valid_utf8(self):
self.assertEqual(json.loads(build_request(self.raw)), self.raw)
def test_unknown_and_bypass(self):
for key in ("temprature", "extra_body", "extra_headers", "tools"):
with self.subTest(key=key), self.assertRaisesRegex(ValueError, "unknown_parameter"):
build_request({**self.raw, key: {}})
def test_bool_and_nonfinite(self):
for value in (True, "0.2", None, float("nan"), float("inf")):
with self.subTest(value=value), self.assertRaises(ValueError):
build_request({**self.raw, "temperature": value})
def test_stream_and_limit(self):
for key, value in (("stream", True), ("stream", "false"),
("max_completion_tokens", True), ("max_completion_tokens", 2049)):
with self.subTest(key=key, value=value), self.assertRaises(ValueError):
build_request({**self.raw, key: value})
if __name__ == "__main__":
unittest.main()
执行python -m unittest -v test_request_guard。本地还检查了空消息、未知角色、额外嵌套键、模型不匹配、允许范围边界和序列化后独立性。上述测试只证明本地规则与预期一致,不能证明demo配置适用于真实模型。
6. 排错时先找哪一层
- unknown_parameter:看拼写与入口规范;不要写一个“删掉所有不认识字段”的补丁,让需求无声失效。
- invalid_temperature:检查是否把文本、null或bool传入;不要自动float转换吞掉调用方类型错误。
- invalid_output_limit:本例只接受整数1—2048。此数字是项目预算,不能用它推断模型上下文长度。
- stream_not_implemented:这个实现只做非流式请求,不能简单删除检查后假装已经支持SSE;接收与结束状态需另外实现。
- 服务端仍返回400:离线允许列表不是服务端协议校验器,重新核对端点、模型、参数模式、服务文档;先去掉未经核实的能力假设,不连续重试同一非法请求。
真实项目应把校验放在唯一发送入口前,并按已核实的模型能力拆分配置。不要给前端任意透传SDK参数。错误日志只留分类和必要请求编号,不记录Key、Authorization或完整提示词。
FlowBarCN文档提供OpenAI兼容接入说明,模型目录也说明实时可调用列表以控制台为准。本文没有核验账户权限、没有调用线上模型,也没有证明任何本站模型支持本例的全部参数。当前计费说明可供以后获授权接入时自行核对,不要求为完成练习充值。
今日练习:增加top_p之前,先写三行规则——目标模型证据、允许类型和范围、与temperature同时出现时的项目策略。补失败样本,再决定是否纳入允许列表。
下一篇将补messages顺序与边界测试。欢迎在评论区留下不含私密信息的输入类型问题;FlowBarCN官网可查看接入资料,不要提交密钥或用户原文。
本文基于Python 3.11标准库,实现一个离线、纯文本、非流式的大模型请求参数校验器,支持允许字段列表、类型严格检查(如区分bool/int)、数值边界验证(如temperature、output_limit)及JSON序列化安全控制。代码不发起真实请求,仅生成UTF-8 JSON字节,强调输入字典预校验、避免隐式类型转换、拒绝extra_body等绕过行为,并配套完整单元测试与分层排错指南。
435

被折叠的 条评论
为什么被折叠?



