JellyToken 把多个大模型收进同一个入口,先弄清各模型的调用方式与输入长度

文章导读
把一个聚合了多个模型的平台当成统一入口时,先确认三件事:账号下有哪些模型可选、每个模型用哪种调用方式、每个模型能接收多长的输入。这三项通常都能在控制台页面或说明文档里查到,也能用一条最小请求加调用日志交叉验证。在确认之前,模型名不要写死在代码里,长文本也不要假定所有模型都能完整接收。
📋 目录
  1. 壹 在平台页面上把可用模型列成一张对照表
  2. 贰 用一条最小请求验证密钥与模型标识是否匹配
  3. 叁 对比不同模型的输入长度,给超长提示词定处理规则
  4. 肆 把提示词里的模型专属写法抽出来
A A

把一个聚合了多个模型的平台当成统一入口时,先确认三件事:账号下有哪些模型可选、每个模型用哪种调用方式、每个模型能接收多长的输入。这三项通常都能在控制台页面或说明文档里查到,也能用一条最小请求加调用日志交叉验证。在确认之前,模型名不要写死在代码里,长文本也不要假定所有模型都能完整接收。

接入 JellyToken 这类多模型入口,先做三件可验证的事:在控制台把模型名称、模型标识、流式支持、输入长度抄成对照表并标注来源;用一条只含单条用户消息的最小请求确认密钥与模型标识匹配;再按各模型里最小的输入长度上限,给长提示词定裁切或分段规则。判断依据只看页面显示、返回状态码和调用日志,未确认前不要写死模型名,也不要把某个模型的专属标记带进通用提示词。

在平台页面上把可用模型列成一张对照表

对照表的作用是防止凭记忆写模型名。模型名称是给人看的展示名,模型标识才是请求里 model 字段要填的值,两者经常不一致;漏掉这一条,最容易出现“页面上看得到、请求却报模型不存在”的情况。建议打开控制台逐个模型抄录,四列都要,再加一列写明这条信息是从控制台页面还是从说明文档读到的,后面排查时有据可查。

模型名称模型标识是否支持流式输入长度上限来源(控制台页面/说明文档)
控制台上的展示名请求里 model 字段要填的值是 / 否 / 未标注文档写明的数值与单位(token 或字符)控制台页面 或 说明文档
第二个模型的展示名对应模型标识是 / 否 / 未标注写明数值与单位控制台页面 或 说明文档
(继续按账号实际可用的模型补齐)

表里填不出来的格子不要猜,直接写“文档未标注”。输入长度上限没写的模型,先按第三小节的保守办法处理;控制台显示支持流式、文档却没提的,先用非流式跑通再试流式,这样出错时只需要判断一个变量。

用一条最小请求验证密钥与模型标识是否匹配

这一步只排除第一层错误:密钥环境不对、鉴权头写法不对、模型标识写错。请求体里只留模型标识和一条用户消息,不要顺手带上温度、工具、系统提示等额外参数,否则参数错误会被误读成鉴权或模型错误。下面是一段通用 HTTP 骨架,路径和字段以平台文档为准,不确定时照抄文档给出的完整地址,不要自己拼接。

POST {BASE_URL}/chat/completions
Authorization: Bearer {API_KEY}
Content-Type: application/json

{
  "model": "{MODEL_ID}",
  "messages": [{"role": "user", "content": "ping"}],
  "stream": false
}

用 SDK 时同样只保留这两项,替换项只有三处:地址前缀取控制台或文档给出的值,密钥从环境变量读取而不是粘进代码,模型标识直接用上一张表里的值。

JellyToken 把多个大模型收进同一个入口,先弄清各模型的调用方式与输入长度
import os
from openai import OpenAI  # 仅当平台兼容该客户端时;否则换成文档给出的客户端

client = OpenAI(
    base_url="{BASE_URL}",
    api_key=os.environ["JELLY_API_KEY"],
)

resp = client.chat.completions.create(
    model="{MODEL_ID}",
    messages=[{"role": "user", "content": "ping"}],
)
print(resp.choices[0].message.content)

返回结果按状态码读,不同返回说明的问题不一样:

  • 200 且返回内容非空:密钥与模型标识这一层对得上,可以继续往下测;
  • 401 或 403:先确认环境变量是否真的取到(打印长度,不打印内容)、鉴权头前缀是否写对、密钥是否属于当前环境;
  • 404 或提示模型不存在:多半是模型标识写错,或该模型没有在当前账号下开通;
  • 400:模型标识能找到,但请求字段不被接受,回头核对文档里的参数名和消息结构。

平台侧一般有调用记录,用请求发出的时间对上一条日志,可以确认请求究竟打到了哪个模型。这一步完成后,才值得去调提示词内容。

对比不同模型的输入长度,给超长提示词定处理规则

同一份长文本在一个模型上通过、换到另一个模型被截断,是换模型时最常见的翻车方式。把对照表里的输入长度上限横向排一遍,取其中最小的值作为通用阈值,超过阈值的长文本走裁切或分段,而不是每次换模型再改一遍代码。

没有精确 token 计数器时,可以先按字符数估:中文大致接近一字一 token 的量级,英文大致四字符一 token,不同分词器的差异明显,只能当作粗略估计。平台或所用库提供计数能力时以计数结果为准;两处都没有,就把字符数阈值压在页面标注值的七成左右,再留余量。

JellyToken 把多个大模型收进同一个入口,先弄清各模型的调用方式与输入长度
MAX_CHARS = 3000  # 填入对照表里最小的输入长度上限,再留出余量

def split_text(text, max_chars=MAX_CHARS):
    if len(text) <= max_chars:
        return [text]
    return [text[i:i + max_chars] for i in range(0, len(text), max_chars)]

parts = split_text(long_doc)
partials = [call(model_id, part) for part in parts]
final = call(model_id, "\n".join(partials))

选择规则看内容性质:裁切适合丢掉的只是冗余部分,比如日志尾部、重复段落;分段适合信息都重要的长文档,先分块处理再合并,代价是多花几次调用。无论哪种,超限时都不建议静默截断后继续跑,在被截断的位置打一条日志(记录原始字符数、模型标识、阈值),事后排查比靠猜省事得多。

把提示词里的模型专属写法抽出来

提示词里最容易变成“只能在一个模型上跑”的部分,通常是特殊分隔标记、系统消息的摆放位置、结构化输出的约束方式。这些写法在单一模型下很好用,换到聚合入口里的另一个模型,就可能被当作正文原样输出,或者被直接忽略。先列出当前提示词里用了哪些专属写法,再逐条改写成通用表达。

  • 特殊分隔标记:形如 <|im_start|>、<|im_end|> 这类对话分隔符,多数模型只把它当普通文本;
  • 系统消息位置:有的写法把规则塞进第一条用户消息,有的依赖单独的 system 角色,跨模型时应统一用 messages 里的 role 表达;
  • 结构化输出:依赖某家 JSON 模式、函数调用或特定参数名,换模型后不生效,需要在提示词里自带字段示例;
  • 停止词与模板尾巴:某些结束标记用在别的模型上不会触发停止,会持续生成多余内容。

改写前后的对照可以这样写。改写前是拼接字符串加专属标记:

# 改写前:依赖特定分隔符
prompt = (
    "<|im_start|>system\n只输出 JSON<|im_end|>\n"
    "<|im_start|>user\n改写下面这句话:...<|im_end|>"
)
# 改写后:只用通用的角色与消息结构
messages = [
    {"role": "system", "content": "只输出一个 JSON 对象,字段为 text,不要任何额外文字"},
    {"role": "user", "content": "改写下面这句话:..."},
]
# 返回后再用 json.loads 校验一次,解析失败就重试或提示模型

改写之后要回到第二小节的骨架,给每个模型各跑一条最小请求,确认同一段解析代码都能接住返回结果。验证方式是看日志里的解析报错类型和失败次数,而不是靠肉眼看返回像不像 JSON。提示词里保留了模型专属写法的地方,建议在对照表里单独备注,换模型时优先检查这几处。