
1. 为什么选型是个工程问题
Codex 现在能挂好几个模型,GPT 6 Astra、GPT 5.6 Sol、Terra、Luna 都在列表里。选型表面看是「挑哪个好用」,实际是一个带约束的优化:在上下文容量够用、编码能力达标的前提下,把单次成本和延迟压到最低。我这台机器每天跑几百次调用,默认把最贵的 Astra 当万能钥匙,月底账单会很难看,长上下文任务还因为排队更慢。这篇用一个 Python 小工具把这套决策固定下来,顺带把国内访问要走的代理一并接好。
2. 四个模型到底差在哪
光看名字选不出来,得看可量化的参数。下面这张表是我按文档和实测填的,数字你可以按后台真实值改,重点看四个维度:上下文长度、单次延迟、编码强度、价格档。
| 模型 | 上下文 | 单次延迟 | 编码强度 | 价格档 |
| GPT 6 Astra | 256k | 高 | 最强 | 高 |
| GPT 5.6 Sol | 128k | 中 | 强 | 中 |
| Terra | 64k | 低 | 中 | 低 |
| Luna | 32k | 很低 | 弱但稳 | 很低 |
上下文长度决定它能吃下多大的代码仓库,延迟决定交互体感,编码强度是改跨文件复杂逻辑的底气,价格档直接挂钩成本。这四个维度不是独立排序,要一起看:小任务用 Astra 是浪费算力和钱,长上下文任务用 Luna 会直接截断。选型的目标不是「选最好的」,而是在约束内选最省的。
3. 用 Python 写统一调用层
先把模型配置用 dataclass 收起来,把单价也带进去,后面选型和成本核算都从这里读,不把魔法数字散得到处都是。这样哪天调价,只动这一处。
python
from dataclasses import dataclass, field
from typing import Literal
ModelName = Literal["gpt-6-astra", "gpt-5.6-sol", "terra", "luna"]
@dataclass
class ModelProfile:
name: ModelName
context_k: int
latency: Literal["low", "mid", "high"]
coding: int # 1 到 5,越高越强
price_in: float # 每千 input token 价格(示例值,按后台填)
price_out: float # 每千 output token 价格
PROFILES: dict[ModelName, ModelProfile] = {
"gpt-6-astra": ModelProfile("gpt-6-astra", 256, "high", 5, 0.010, 0.030),
"gpt-5.6-sol": ModelProfile("gpt-5.6-sol", 128, "mid", 4, 0.005, 0.015),
"terra": ModelProfile("terra", 64, "low", 3, 0.002, 0.006),
"luna": ModelProfile("luna", 32, "low", 2, 0.0008, 0.002),
}
def estimate_cost(model: ModelName, in_tokens: int, out_tokens: int) -> float:
p = PROFILES[model]
return (in_tokens / 1000) * p.price_in + (out_tokens / 1000) * p.price_out
4. 接亿牛云隧道代理,解决国内访问
Codex 的接口在境外,服务器放在国内直连不通。我这边用亿牛云隧道代理,它的好处是给一个固定域名加端口,代码里配一次就不用管 IP 轮换,比自建代理池省心。这里要先说清一个机制:目标是 HTTPS,但代理本身用 http:// 协议,requests 会先发 CONNECT 建隧道,再和目标站做 TLS 握手。所以代理 URL 的 scheme 写 http,目标站仍是 https。
python
import socket
import requests
import certifi
from tenacity import retry, stop_after_attempt, wait_exponential
PROXY = "http://你的账号:你的密码@u12098.50.tn.16yun.cn:6233"
PROXIES = {"http": PROXY, "https": PROXY}
def probe_proxy(host: str, port: int) -> None:
try:
socket.create_connection((host, port), timeout=5)
print("代理端口可达")
except OSError as e:
print("代理不可达,先查域名解析和防火墙:", e)
@retry(stop=stop_after_attempt(4), wait=wait_exponential(multiplier=1, min=2, max=10))
def chat(model: ModelName, prompt: str) -> str:
resp = requests.post(
"https://api.codex.example/v1/chat",
json={"model": model, "messages": [{"role": "user", "content": prompt}]},
proxies=PROXIES,
verify=certifi.where(), # 代理环境下 CA 证书要指对,不然卡在 TLS 握手
timeout=30,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
两个容易踩的点。代理 URL 必须带端口,漏了会直接 ProxyError 加 Max retries,端口在亿牛云后台订单里查,别凭记忆填。verify 用 certifi 的证书包,否则有些 Python 环境会报 SSLError,表现是代理通了但握手失败。白名单也要去亿牛云后台把调用服务器的出口 IP 绑上,没绑代理会拒连,账号密码都对也没用。云服务器重装或换机房后 IP 会变,这个最容易被忘。运行前先跑一次 probe_proxy 确认端口可达,能省掉一半排查时间。
5. 写个成本感知的选型器
把任务特征映射成模型,比人肉选稳。这里不做「越难越大」的粗暴映射,而是在约束内挑单价最低的:先过滤掉上下文装不下的,再按难度卡能力下限,最后在合格项里取最便宜的。
python
def pick_model(context_tokens: int, hard: bool, max_price: float | None = None) -> ModelName:
cands = [p for p in PROFILES.values() if p.context_k * 1000 >= context_tokens]
if hard:
cands = [p for p in cands if p.coding >= 4]
if max_price is not None:
cands = [p for p in cands if p.price_in + p.price_out <= max_price]
if not cands:
raise ValueError("没有满足条件的模型,放宽上下文或难度约束")
cands.sort(key=lambda p: p.price_in + p.price_out)
return cands[0].name
# 网络/限流类错误才回退,业务 4xx 不兜底
FALLBACK: dict[ModelName, ModelName] = {
"gpt-6-astra": "gpt-5.6-sol",
"gpt-5.6-sol": "terra",
"terra": "luna",
"luna": "terra",
}
def _should_fallback(err: Exception) -> bool:
if isinstance(err, (requests.ConnectionError, requests.Timeout)):
return True
if isinstance(err, requests.HTTPError) and err.response is not None:
return err.response.status_code >= 500
return False
def chat_with_fallback(prompt: str, preferred: ModelName) -> tuple[ModelName, str]:
model: ModelName = preferred
last_err: Exception | None = None
while model:
try:
return model, chat(model, prompt)
except Exception as e:
last_err = e
model = FALLBACK.get(model, "")
if not model or not _should_fallback(e):
break
assert last_err is not None
raise last_err
回退链只接网络抖动和 5xx,业务层的 400/401 不盲目换模型,避免把鉴权失败伪装成成功。
6. 效果验证:用度量代替感觉
选型好不好,不看顺不顺眼,看度量。我用一个 Metrics 把每次调用的模型、耗时、花费记下来,隔一周拉出来算 p95 和总成本:
python
from dataclasses import dataclass, field
@dataclass
class Metrics:
calls: int = 0
cost_usd: float = 0.0
_latency: list[float] = field(default_factory=list)
def record(self, seconds: float, cost: float) -> None:
self.calls += 1
self.cost_usd += cost
self._latency.append(seconds)
def p95(self) -> float:
if not self._latency:
return 0.0
s = sorted(self._latency)
return s[min(len(s) - 1, int(len(s) * 0.95))]
m = Metrics()
t0 = time.perf_counter()
model, _ = chat_with_fallback("重构这段处理函数", pick_model(120_000, hard=True))
m.record(time.perf_counter() - t0, estimate_cost(model, 120_000, 800))
一份 200 次调用的示例压测长这样(数字为演示):
| 模型 | 调用次数 | 平均延迟(s) | p95(s) | 总成本($) | 成功率 |
| GPT 6 Astra | 12 | 8.4 | 14.2 | 0.93 | 100% |
| GPT 5.6 Sol | 45 | 4.1 | 7.0 | 0.71 | 98% |
| Terra | 88 | 2.3 | 3.9 | 0.40 | 95% |
| Luna | 55 | 1.5 | 2.6 | 0.18 | 90% |
读这张表有个反直觉的点:总成本里 Astra 占了一半多,但它只接了 12 个最重的跨文件任务。小活儿被 Terra 和 Luna 接走,整体成本压下来了。如果表里小任务频繁落到 Astra,说明选型阈值定低了,钱白花;如果长上下文任务报截断,说明该把上下文下限抬一档。
7. 常见问题和踩坑提醒
第一个坑是代理端口漏写。报错 ProxyError 加 Max retries exceeded,补上 :端口 就好了,端口在亿牛云后台订单里查。
第二个坑是证书。代理连通后冒出 SSLError,多半是 CA 包没指对,按第四节用 certifi.where() 顶上。这种错直连也会出,和代理无关,是环境缺证书。
第三个坑是白名单。账号密码都对但一连就拒,去亿牛云后台看出口 IP 有没有绑。换机器后最容易踩。
第四个坑是把选型写死。模型价格和能力会变,配置放 dataclass 里,别埋进判断逻辑,改起来才不头疼。我见过有人把模型名硬编码在业务函数里,后来降价了改起来要翻几十个文件。
第五个坑是回退无脑换模型。网络错和 5xx 才该回退,4xx 是请求本身的问题,换模型只会重复失败还掩盖真相。
8. 总结
选型不是一个拍脑袋的动作,而是一组可度量的约束。配置收进 dataclass,选型抽成带成本和能力约束的独立函数,跑批效果用 Metrics 回收,代理走亿牛云隧道解决国内访问,整套基本能自治。先把最小可用版本跑通,再拿真实的 p95 和成本往细里调,比一上来追求完美方案实在。Codex 把模型摆在你面前,怎么用省、怎么用稳,才是真功夫。