综合小项目:用 Python 构建可解释的 AI 模型路由器
前面的系列已经介绍了模型调用、提示词、上下文、成本、错误处理、评估和工作流。实际应用中,所有请求都交给同一个模型并不一定合理:简单改写可能不需要高成本模型,复杂分析则可能需要更强的模型。本篇做一个综合小项目:先用确定性的规则判断任务类型,再根据配置选择模型,最后记录路由理由和调用结果。重点不是“自动猜得多聪明”,而是让选择过程可解释、可测试、可替换。
先定义路由边界
路由器接收一份任务,任务包含 kind 和 text 两个字段。kind 只允许 simple、analysis 和 code 三种值,分别代表简单文本处理、分析任务和代码问题。程序根据类型读取环境变量中的模型名:
MODEL_SIMPLE:处理短文本改写等低复杂度任务。MODEL_ANALYSIS:处理需要较长上下文的分析任务。MODEL_CODE:处理代码解释或调试问题。
这不是让模型自己决定调用哪个模型,而是把路由规则放在 Python 中。规则越明确,越容易写测试、估算成本,也越容易在出现异常时解释“为什么用了这个模型”。生产系统还可以把规则改成配置文件,加入长度、租户、预算或人工指定的覆盖项。
准备环境变量
在独立虚拟环境中安装官方 Python SDK:
1 | python -m venv .venv |
然后配置密钥和模型名。下面只是占位符,真实密钥只能通过环境变量提供:
1 | export OPENAI_API_KEY="替换为你的真实密钥" |
模型名称必须以你实际使用的服务商文档为准,不要因为模型名看起来相似就假定它一定可用。
编写最小路由器
新建 router.py:
1 | import json |
创建输入文件 task.json:
1 | { |
运行:
1 | python router.py task.json |
程序会实际调用所选模型,并输出模型名、路由理由和本次返回的文本。这里不能预先写死模型回答,因为结果取决于你的服务、模型版本和请求状态;可验证的事实是路由函数选择了配置中的对应模型,网络调用成功后才会有 output。
为什么把路由和调用分开
choose_route 是纯函数式的确定性逻辑,不需要网络,也不需要密钥。它可以单独测试:
1 | assert choose_route("simple").reason == "简单任务使用轻量模型" |
网络请求只集中在 run 中。这样做有两个好处:第一,路由规则错误不会被 API 异常掩盖;第二,测试时可以替换客户端,避免每次测试都消耗额度。更进一步,可以让 run 接收一个客户端参数,生产环境传入真实客户端,测试环境传入返回固定文本的假客户端。
加上长度与预算保护
类型路由只是第一层规则。用户把一篇很长的文档标成 simple,仍可能造成上下文超限或费用异常。因此,调用前应做输入长度检查,并为每类任务设置预算。例如,先限制字符数:
1 | MAX_CHARS = {"simple": 4000, "analysis": 20000, "code": 12000} |
这不是精确的 token 计算,中文、代码和不同 tokenizer 的比例都可能不同;它只是一个便宜的第一道保护。涉及严格上下文窗口时,应按实际模型文档和 tokenizer 估算,并在日志中记录输入长度、模型、耗时和错误类型,不要记录密钥或未经脱敏的敏感正文。
常见问题
为什么不直接让模型选择模型? 模型选择本身也要消耗一次调用,而且结果不稳定、难审计。可以把模型建议作为候选信息,但最终选择应受代码规则、预算和权限约束。
模型名称配置错误怎么办? SDK 通常会返回认证、请求或模型不存在等错误。不要无限重试这类配置错误;启动时可以检查必需环境变量是否存在,并把错误写到标准错误。实际项目还应设置请求超时和有限重试。
多个模型的提示词可以完全相同吗? 不一定。不同模型支持的输入格式、参数和能力可能不同。示例使用 Responses API 的共同字段,但接入其他服务时必须查阅其官方文档确认兼容性。
路由后如何证明成本下降? 不能凭一次调用下结论。应准备固定任务集,记录每次的模型、输入输出 token、延迟、错误和质量评分,再与统一使用单一模型的基线比较。模型更便宜不代表总成本更低,如果返工和失败更多,整体成本可能上升。
小结
本项目把模型路由拆成了三个清晰环节:用输入类型确定规则,用环境变量提供模型配置,用独立函数记录选择理由,再由统一调用层取得结果。它没有把模型能力差异包装成绝对结论,也没有伪造 API 输出。继续工程化时,可以加入基于评测集的路由规则、预算熔断、人工覆盖和指标看板,但每一步都应保留可解释的决策记录,并为失败设计明确的降级路径。