最近很多技术群里都在聊自定义 GPT,也就是 CustomGPTs。不少团队把它当成了一个“给 ChatGPT 写段人设”的小玩意,结果做了几天发现两个问题:一是回答质量不稳定,二是成本比想象中高。其实这两个问题指向的是同一个误区:CustomGPTs 的价值从来不是换皮,而是把指令、知识库、工具调用这些能力压缩成一套可配置的工程方案。一个真正“Affordable”的自定义 GPT,不是靠选小模型省出来的,而是靠设计省出来的。
这篇文章回到标题里的那句话:Make A GPT: Building Affordable CustomGPTs。我会从实际构建的角度出发,讲清楚 CustomGPTs 到底解决了什么问题,成本主要消耗在哪些环节,以及如何用较低的 token 成本构建一个可用的自定义 GPT。文章会覆盖 GPT Builder 的配置思路、Assistants API 的代码接入、知识库和 Actions 的瘦身方法,以及上线前后最容易踩的坑。
如果你正准备在公司内部做一个问答机器人、行业顾问或团队知识助手,这篇文章值得认真读完。它不会帮你绕开所有开发工作,但能让你少走一段用钱和时间换教训的路。
1. 为什么 CustomGPTs 值得开发者认真对待
1.1 从“调用模型”到“配置一个 AI 助手”
传统大模型应用开发的基本路径是:选定模型,准备 prompt,写代码搭 RAG 流程,再用 LangChain 或类似框架去链知识库、工具和对话记忆。这套流程本身没有问题,但它对工程能力有要求。你需要处理文本切分、向量检索、上下文拼接、Agent 调度、会话状态管理,任何一个环节不仔细,最终效果都会打折。
CustomGPTs 提供了一个更轻的入口:不需要从零搭建 RAG 管线,不需要维护一套复杂的 Agent 调度代码,你可以在官方提供的 Builder 界面里,通过指令、知识库文件、Actions 三个能力拼出一个能回答专业问题的助手。对于原型验证、内部工具、垂直场景小团队来说,这种配置化开发的效率优势是明显的。
但这里有一个关键判断:CustomGPTs 不是要替代完整的大模型应用工程,而是把“70 分可用”的场景用最低成本先跑起来。它适合已经清楚任务边界、数据量不大、工具调用链路简单的场景。如果业务复杂度上去了,再回到代码工程也不迟。
1.2 它真正降低的是哪类成本
很多团队会把 CustomGPTs 理解为“省了写代码的时间”,这只是表面。它真正降低的是三类成本:
- 认知成本:不需要理解向量数据库、Embedding、Agent 框架,也能做出带知识库的问答对话。
- 迭代成本:改指令、传新文档、调工具说明都可以在界面里快速完成,不用重新部署服务。
- 维护成本:会话状态、文件解析、基础检索由平台托管,团队不用自己维护一套检索服务。
但这不意味着它就适合所有场景。如果业务需要完全私有化部署,或者需要复杂的多步业务编排,还是应该回到代码方案。用一个简单的标准判断:任务能不能被描述成“给定背景材料 + 固定规则 + 有限工具”的问答?如果能,就非常适合 CustomGPTs;如果不能,它的优势就会变成束缚。
2. CustomGPTs 核心概念
2.1 它到底是什么
CustomGPTs 可以理解为一个“预配置的 AI 助手”。它仍然基于底层大模型,但多了一套静态配置:系统指令、知识文件、动作声明、开场白和推荐问题。当用户和它对话时,平台会把配置中的指令、检索到的知识块、以及动作返回的结果一起交给模型去组织回答。
理解这个机制很重要。CustomGPTs 不是一个新的独立模型,它是一层“固定上下文 + 行为规则”的壳。底层的模型能力当然是基础,但最终回答质量高度依赖这层壳设计得好不好。同样的模型,配置得当和配置粗糙,效果可以差出一个量级。
2.2 四个核心组成
一个自定义 GPT 通常可以拆成四部分:
| 组成 | 作用 | 容易踩的坑 |
|---|---|---|
| 指令(Instructions) | 定义角色、目标、回答风格、限制条件 | 写得过长,模型注意力被稀释 |
| 知识库文件 | 提供私有资料,按需检索 | 文件过大、检索块杂乱,回答被噪声带偏 |
| Actions | 允许调用外部 API 获取实时数据或执行操作 | 权限过大,接口无鉴权,调用频率失控 |
| 对话配置 | 开场白、推荐问题、联网开关等 | 与业务场景不匹配,开关配置过宽 |
这四部分并不是非黑即白。如果不需要外部数据,Actions 就可以不配;如果所有答案都能写在指令里,知识库文件也可以省略。做得越少,成本越低,这是后文的核心原则。
2.3 和普通 ChatGPT、API 应用的区别
为了不让自己陷入“CustomGPTs 到底是什么”的迷茫里,建议先做一个简单对比:
| 方案 | 例子 | 适合人群 | 主要成本 |
|---|---|---|---|
| 普通 ChatGPT 对话 | 直接使用通用对话框 | 普通用户 | 订阅费或按次计费 |
| CustomGPTs | 在 GPT Builder 里配置私有助手 | 想快速搭建垂直助手的开发者 | 对话 token + 使用量 |
| 自研 API 应用 | 自己写代码调用模型接口 | 需要深度定制的团队 | 开发 + 运维 + API 费用 |
CustomGPTs 处于中间地带。它不是最自由的方案,也不是最基础的方案。它的设计哲学是“让配置代替代码”,前提是你愿意接受平台提供的默认检索、默认记忆和默认工具框架。理解了这一点,就知道后续优化围绕的核心是:把配置做得更精确,把模型的无效计算降到最低。
3. 成本到底从哪来
3.1 推理、检索与工具调用的三重消耗
如果只把 CustomGPTs 当对话框,你会觉得成本就是“一次回答消耗多少 token”。实际上,一次多轮对话中,模型可能做了不止一次推理。
第一是推理成本。每一次回答都是一次模型生成,输入的一部分是指令、历史对话、检索结果,输出是模型生成的内容。输出 token 通常比输入 token 更贵,所以让模型保持简洁回答是省钱的第一手段。
第二是知识库检索成本。自定义 GPT 在回答用户问题时,会把用户问题转换为检索任务,从上传的知识文件中找出相关片段,拼进上下文。这个检索过程本身有算力开销,更重要的是,检索出来的片段会成为输入的一部分。如果知识库质量差,检索出的片段多而杂,输入 token 就会飙升,而且回答质量还会下降。
第三是工具调用成本。Actions 是自定义 GPT 最容易被忽视的“隐形支出”。一次工具调用等于让模型多了一次内部推理和外部请求,每次调用都会消耗 token。一个设计不佳的 Agent 可能会为了一个简单问题连续调用三四次工具,成本自然成倍增加。
3.2 用“请人办事”来理解 token 消耗
我习惯用一个类比来解释 token 消耗:你请了一位顾问,你的每一次提问和对方的每一次回答都按字数计费,而且顾问在回答问题前,还会把参考书翻一遍、把需要核实的资料重新读一遍,这些阅读也要收费。所以你要做的不是让顾问少说话,而是让他少翻无关的书、少打不必要的电话、少写冗长的报告。
这也解释了为什么很多人觉得“模型回答质量差,但 token 消耗却很快”——大概率不是模型的问题,而是你让模型在回答前读了一堆不相关的内容,或者在一次问答中安排了太多不必要的工具调用。
3.3 成本优化判断:先瘦设计,再换模型
看到成本上涨,很多人的第一反应是换更便宜的模型。正确的顺序是先看设计是否臃肿,再看模型成本。
瘦设计的顺序应该是:
- 指令是否精简到只保留必要规则?
- 知识库文件是否只保留当下业务所需的资料?
- Actions 是否只暴露必要接口,且设置了明确的调用条件?
- 回答是否被要求直接、简短,避免无意义的扩展?
这些全部做完之后,才是模型选择层面的优化。用低成本模型承接高频简单问题,把复杂任务留给强模型,这种分层策略,比单一依赖某一颗“灵丹妙药”更可靠。
4. 构建一个低成本 CustomGPT 的完整流程
4.1 明确任务边界
构建 CustomGPT 之前,先回答三个问题:这个助手解决什么类型的问题?回答这类问题需要哪些资料?需要访问哪些外部数据?
这里我用一个常见场景做演示:做一个“团队项目问答助手”。它需要回答某个项目的背景、进度、技术方案和常见问题,数据来源是项目文档和 Wiki 页面。这个定位清晰,知识边界明确,就是一个典型的最小 CustomGPT。
如果任务边界模糊,比如“帮我回答一切技术问题”,那抱歉,这不是 CustomGPT 能解决的问题,而且会非常耗 token。边界模糊意味着你要塞入海量知识库,模型面对开放问题时也会不知所措。
4.2 编写精简指令
指令是成本控制的第一关。一个常见的误区是把所有想得到的规则都写进去,结果指令膨胀到几千字,模型在每个请求里都要读完这些内容,输入成本更高,而且指令越长,模型越容易丢失关键约束。
好的指令应该只包含四类信息:
- 角色:它是谁,面向什么用户。
- 目标:它的核心职责是什么。
- 约束:哪些不能做、回答风格是什么。
- 资源:遇到哪类问题应该查知识库,哪类问题可以直接回答。
下面是一个项目问答助手的指令示例,可以保存为INSTRUCTIONS.md文件用于后续版本管理:
你是一个项目助手,服务对象是项目组的研发和运营人员。 你的职责: 1. 回答项目背景、版本计划、责任人、技术方案相关问题。 2. 当问题涉及具体文档、指标、排期时,必须优先搜索知识库,不要凭记忆回答。 3. 如果知识库中没有相关信息,直接说明“未见相关资料”,不要编造。 回答风格: - 中文回答,语言简洁。 - 涉及步骤时使用编号列表。 - 如果问题无法处理,建议用户联系项目负责人。 禁止事项: - 不回答与项目无关的问题。 - 不透露凭推测得出的结论。这条指令只有约 150 字,但已经把角色、任务、约束、资源边界都说清楚了。比 500 字的“全角色扮演”文案更实用,输入 token 也更少。
4.3 知识库文件瘦身
知识库是最容易膨胀的部分。常见的错误是把整个 Wiki、几十个 PDF 一股脑传进去,以为资料越全效果越好。实际结果往往相反:检索命中率下降,回答时被无关碎片干扰,成本却直线上升。
知识库瘦身可以按下面步骤做:
- 只保留“回答高频问题”必需的文档,删掉历史归档、重复文件。
- 文件尽量拆成独立的小主题文档,一个文件只讲一个主题。比如“部署手册.md”“排期流程.md”“接口设计说明.md”,而不是一个大而全的“项目总文档.md”。
- 文档内使用清晰的小节标题,便于检索系统定位。
- 无法拆分时,优先选择格式干净的 Markdown 或文本,替代扫描版 PDF。
知识库文件不是越多越好,而是“越精确越好”。让检索系统每次都能快速找到最合适的一小块内容,模型拿到的上下文就更干净,回答质量和成本都更可控。
4.4 Actions 设计:克制地接入外部接口
Actions 是自定义 GPT 接入外部系统的窗口,也是成本失控的高发区。很多人一上来就把十几个接口全接进去,结果是模型在简单问题上也要反复“思考该调用哪个接口”,消耗大量时间片和 token。
设计 Actions 的原则是:能不加就不加,加了也要限制触发条件。
下面是一个最小 Action 配置示例,用于查询项目排期。这里只暴露一个查询接口,并通过 OpenAPI Schema 描述给模型:
{ "openapi": "3.1.0", "info": { "title": "Project Schedule API", "version": "1.0.0" }, "paths": { "/schedule": { "get": { "operationId": "getSchedule", "summary": "获取项目排期信息", "responses": { "200": { "description": "返回排期列表" } } } } } }这段配置告诉模型:存在一个获取排期信息的接口,但没有给它过多的额外描述。在实际项目里,你应该在summary和description中明确说明“仅当用户询问排期时调用”,避免模型在无关话题上调用接口。
Actions 的调优思路是:接口描述越精确,模型误调用的概率越低;接口返回的数据越精简,模型需要处理的输入 token 越少。让接口返回“排期表 + 当前状态”就够了,不要返回一长串与问题无关的日志字段。
4.5 发布与分享
配置完成后,需要通过发布流程启用。根据官方界面,一般可以选择仅自己可见、链接分享、或公开发布到 GPT Store。从工程角度看,稳妥的顺序是先“仅自己可见”做内部测试,再“链接分享”给少量同事,最后再考虑更广范围发布。
发布到不同范围会影响使用量,也间接影响成本。如果只用于团队内部,公开上架反而会带来无谓的消耗。很多团队最终的教训是:一个内部工具被外部用户发现后,token 消耗一夜之间翻了十几倍。所以发布范围要克制,用完即关,不上不必要的话。
5. 用 Assistants API 在代码中管理自定义 GPT
5.1 什么时候需要代码管理
GPT Builder 做交互配置很顺手,但有两个痛点:一是配置没有真正的 Git 历史,二是无法自动化测试、批量创建、集成到业务系统。如果团队需要把“自定义 GPT”作为产品能力交付,而不是做一个内部原型,就应该考虑用 Assistants API 在代码中创建和管理 Assistant。
这里要提醒一点:API 的实际用法会随官方 SDK 版本变化。下面给出的代码是通用的最小流程,运行前请查阅你所用 SDK 版本的官方文档。如果你的 SDK 版本较新,类名和模块路径可能略有不同,但整体结构是稳定的。
5.2 最小 Python 示例
首先安装依赖:
pip install openai python-dotenv准备一个.env文件,内容如下:
OPENAI_API_KEY=your_api_key_here ASSISTANT_ID=接下来创建一个 Python 脚本create_assistant.py,用于创建你的自定义助手:
import os from openai import OpenAI # 加载环境变量 from dotenv import load_dotenv load_dotenv() # 初始化客户端 client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # 创建一个助手 assistant = client.beta.assistants.create( name="Project Q&A Assistant", instructions=( "你是一个项目助手,服务对象是项目组的研发和运营人员。" "回答项目背景、版本计划、责任人和技术方案相关问题。" "如果知识库中没有相关信息,直接说明未见相关资料,不要编造。" ), model="gpt-4o-mini", tools=[{"type": "file_search"}], ) # 输出助手 ID,后续调用会用到 print(assistant.id)这段代码做了几件事:初始化客户端,把指令写死在instructions参数里,指定使用一个低成本模型,并声明启用文件搜索工具。运行后会返回一个 Assistant ID,把它记录下来,更新到.env的ASSISTANT_ID字段。
5.3 多轮对话流程
创建 Assistant 之后,一次典型对话分三步:创建 Thread(会话)、向 Thread 中追加用户消息、运行 Assistant 并获取回复。
import os import time from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) assistant_id = os.getenv("ASSISTANT_ID") # 创建会话 thread = client.beta.threads.create() # 添加用户消息 client.beta.threads.messages.create( thread_id=thread.id, role="user", content="这个项目下一个版本计划什么时候发布?", ) # 运行助手 run = client.beta.threads.runs.create( thread_id=thread.id, assistant_id=assistant_id, ) # 轮询运行状态 while run.status in ["queued", "in_progress", "requires_action"]: time.sleep(2) run = client.beta.threads.runs.retrieve( thread_id=thread.id, run_id=run.id, ) if run.status in ["queued", "in_progress"]: continue # 如果模型触发了工具调用,这里需要根据实际情况处理。 # 对于 file_search,平台会自行完成检索,无需额外干预。 break # 获取回复 if run.status == "completed": messages = client.beta.threads.messages.list(thread_id=thread.id) for message in messages.data: if message.role == "assistant": print(message.content[0].text.value)这段代码展示了最核心的对话闭环。重点理解:Thread 是会话容器,每条消息都追加在里面;Run 是模型的一次执行,会读取完整会话上下文;工具调用发生在 Run 过程中,对于文件检索类工具,平台会内部处理,代码里通常只需要关注最终结果。
5.4 把配置纳入版本管理
代码方案相比 GPT Builder 的一大优势,就是配置文件可以进入 Git。建议把指令单独抽成文件:
assistant/ ├── .env ├── create_assistant.py ├── chat.py ├── INSTRUCTIONS.md └── knowledge/ └── project_wiki.mdcreate_assistant.py读取INSTRUCTIONS.md的内容来创建 Assistant,这样指令的每次修改都会有历史记录。知识库目录里的文档也建议与代码同仓库管理,发布前用一个脚本上传到 Assistant。这样团队协作时不会出现“只有一个人的本地配置能跑”的问题。
6. 运行验证与成本观测
6.1 功能验证
构建完成后,不能只在界面里用一两个问题测试。建议准备一组固定的测试用例,覆盖以下类型:
- 正向问题:应该能准确回答的常见问题。
- 边界问题:超出知识范围的问题,模型应当说“未见相关资料”。
- 误导问题:与业务相关但知识库中无答案的问题,模型应当拒绝编造。
- 多余问题:与项目无关的问题,模型应当拒绝回答。
这四类用例跑完,你才能判断这个自定义 GPT 是真的可用,还是只是对测试问题“表现良好”。
6.2 成本观测指标
代码方案中,每次 Run 完成后,可以通过结果对象查看 token 使用情况。下面的代码片段演示了如何读取 usage 信息:
if run.status == "completed": print("完整对话 token 消耗:") print("输入 token:", run.usage.prompt_tokens) print("输出 token:", run.usage.completion_tokens) print("总计 token:", run.usage.total_tokens)在实际项目中,建议把每次对话的total_tokens、线程 ID、问题类型、用户 ID 记录到日志系统。成本要可观测,才能做优化。如果发现某个业务场景单次对话的 token 消耗明显偏高,说明该场景的配置或指令还有调整空间。
6.3 优化前后对比测试
做成本优化时,要建立一个简单的对比实验。固定同一组 20 道测试题,记录两个指标:回答准确率和总 token 消耗。先跑一遍原始配置,再按瘦身方案调整指令、知识库和 Actions,然后再跑一遍。对比数据会直观告诉你,到底哪些改动真正降低了成本,哪些改动只是心理安慰。
这套方法特别适合团队内部做“自定义 GPT 优化评审”。与其靠感觉争论,不如跑数据说话。
7. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 知识库内容检索不到 | 文件过大、主题混杂 | 问一个明确问题,观察回答是否“查无此内容” | 拆分文档,保持单一主题,使用清晰的标题 |
| 回答偏离指令 | 指令过长或前后矛盾 | 重新检查指令文本,删除互相冲突的规则 | 精简指令,只保留必要规则,按优先级排列 |
| token 消耗异常快 | 知识库检索块过多、Actions 被频繁触发 | 查看日志中的 usage 和工具调用记录 | 裁剪知识库,收紧 Actions 触发条件 |
| Actions 调用失败 | 鉴权配置错误、接口超时 | 检查接口日志,确认 API Key 和权限 | 最小化授权,先手动调用接口验证 |
| 发布后无法访问 | 权限范围配置错误 | 检查发布设置 | 先恢复为内部链接,确认后再放开 |
| 模型回答存在编造 | 没有明确“未见资料时怎么办” | 询问难例,观察模型是否使用不确定措辞 | 在指令中强制加入“未见相关资料时直接说明” |
| 对话历史太长导致成本上涨 | 未控制历史长度 | 观察多次交互后的 usage | 定期清理 Thread 消息或控制多轮轮数 |
遇到问题时,第一步永远是看日志。自定义 GPT 的问题大多是配置层问题,而不是模型本身有问题。先确认输入侧(指令、知识库、工具返回)是否干净,再考虑换模型或改提示词策略。
8. 最佳实践与工程建议
8.1 指令要像代码一样管理
指令不是一句话,而是一份配置。建议把指令当作代码看待:
- 独立成文件,写入 Git。
- 每次修改都提交,记录变更原因。
- 多个助手共享的指令抽成公共模板。
- 关键规则写测试用例,换指令后自动或半自动回归。
很多人只会“改一版看看效果”,结果过了两个月,谁也说不清当前版本为什么长这样。指令版本化可以避免这种混乱。
8.2 知识库文件要持续治理
知识库不是一次性上传就结束了。项目文档更新后,旧文档会成为噪声;重复文件会让检索结果变乱;过期指标会被模型当作当前状态引用。建议每周或每个迭代周期做一次知识库审查,删除过时文件,更新已变更内容。
如果知识和代码在一个仓库里,可以顺便在 CI 里增加“文件大小、文件数量变动”的提示。知识库的膨胀往往是渐进的,直到成本报表出来你才意识到。
8.3 Actions 权限最小化
Actions 对外部接口的权限遵循最小化原则。只暴露当前业务确实需要的接口,并且每个接口都要确认:
- 模型能否通过返回信息完成任务?
- 接口是否使用了临时、限权的凭证?
- 是否需要对调用频率设置上限?
在企业内部场景中,一个自定义 GPT 如果可以调用内部核心系统,那么权限管控就是安全底线,不能因为“它只是一个 GPT”而放松。生产环境的接口必须考虑身份认证、审计日志和调用配额。
8.4 发布前灰度
即使是在团队内部,也不要一上来就把链接群发。建议按“个人 -> 核心同事 -> 团队 -> 更大范围”的节奏灰度。灰度期间重点观察:回答是否出现不该出现的内容、是否调用过不该调用的接口、每次会话的平均 token 是否在预算范围内。
如果灰度阶段就出现成本飙升或答非所问,这时候回滚配置的成本很低。一旦面向更广范围发布,问题影响面就大了。
8.5 日志与监控
代码方案中,务必记录三个维度的日志:
- 用户侧:谁在什么时间问了什么问题。
- 成本侧:每次会话的 token 消耗。
- 质量侧:回答是否被用户点踩、是否有“未见资料”等兜底回复。
有了日志,后续优化才有依据。没有日志的 CustomGPTs,就像一个没有测试的黑盒,出问题时只能靠猜。
9. 从配置到工程:真正的门槛在设计
回到标题,Building Affordable CustomGPTs 这件事,真正的难点不在“会点按钮”,也不在“会调 API”,而在于能对任务边界、知识规模、工具必要性做出准确判断。一个便宜好用的自定义 GPT,通常配置都很简单:指令精简、知识库干净、Actions 克制、模型选择务实。
如果你想动手实践,我建议按这个顺序来:先选一个非常具体的场景,用 GPT Builder 配一版,用 20 个测试问题跑一遍效果;然后把指令和知识库纳入 Git,用代码执行同样的流程;最后再考虑加 Actions 做外部集成。每一步都控制变量,别一次加太多能力。
对于那些想继续深入的人来说,下一步值得研究的方向包括:多自定义 GPT 之间的职责拆分、知识库文件的自动更新机制、复杂 Action 工作流的稳定调优,以及如何在保证质量的前提下,把高频场景逐步迁移到更经济的模型上。技术方向很多,但核心原则始终不变:给模型越干净的任务,它就越能给你稳定而低成本的回报。