本系列共 10 阶段 ,面向有 Go 后端基础的开发者,全程用 Go 手写实现。
当前进度:阶段一 大模型基础 · 第 7 课
这一课完成后,第一阶段「AI 与大语言模型基础」就正式结束了。
前面我们已经学过 LLM 的基本原理、Token、上下文窗口、Prompt、Temperature 和 JSON 结构化输出。现在需要把这些知识整合起来,回答三个工程问题:
- 为什么模型有时会一本正经地说错话?
- 哪些事情适合交给模型,哪些必须由 Go 后端负责?
- 如何控制真实项目中的模型调用成本?
这些问题会直接影响后续 AI 应用的可靠性、架构和开发成本。
一、本课学习目标
- 理解模型幻觉的常见原因与应对措施。
- 区分模型生成能力和传统后端的确定性逻辑。
- 理解模型 API 的输入、输出成本,以及 Agent 多轮调用带来的额外开销。
- 设计一套基本的 Go AI 服务可靠性控制方案。
- 完成第一阶段的知识整合,为真实 API 调用做好准备。
二、模型为什么会产生幻觉?
所谓模型幻觉(Hallucination),是指模型生成了看似合理、实际却不准确或缺乏依据的内容。
例如,你问:
Go 某个最新版本中的某个函数应该怎么调用?
模型可能生成一个看起来非常专业的函数名、参数列表和代码示例,但这个 API 实际上根本不存在。
为什么会发生这种情况?
1. 模型的核心任务是生成内容,不是自动核实事实
大语言模型根据输入上下文和学到的模式生成后续 Token。它并不会因为某句话写得流畅,就自动确认这句话一定是真的。
可以把它理解为:
- 模型负责根据上下文生成一个可能合适的回答。
- 检索系统负责查找外部资料。
- Go 后端负责执行程序规则和校验。
- 测试与业务验证负责发现实际错误。
这也是为什么模型能够写出语法看似合理、但无法编译的代码。
2. 常见的幻觉原因
| 原因 | 例子 |
|---|---|
| 训练知识不完整 | 不知道某个新版本库的 API |
| 信息已经过时 | 根据旧版本文档回答新版本问题 |
| 用户提供的信息不足 | 缺少报错日志,却直接推断出唯一原因 |
| 上下文太长或关键信息缺失 | 忽略前面提到的版本约束 |
| 生成机制的局限 | 生成了合理但不真实的函数名或事实 |
这里需要注意:并非所有错误都属于严格意义上的幻觉。有些错误源于信息不足、上下文管理不当或工具调用失败。但对应用工程而言,它们都需要被妥善处理。
三、Go 后端如何降低幻觉风险?
我们用一个技术助手的例子说明。用户问:
请告诉我 Eino 最新版本中,某个组件的初始化函数怎么写。
如果直接让模型回答,它可能依赖已有知识生成代码。更可靠的系统可以增加资料检索和验证环节:
text
用户提出技术问题
↓
Go 后端识别问题与约束 ------ 库版本、问题类型、权限与预算
↓
检索可信资料 ------ 官方文档、项目源码、指定版本说明
↓
模型根据问题和资料生成答案
↓
Go 后端验证与返回 ------ 结构校验、必要的代码测试、来源信息
这套流程不意味着回答一定正确,但它比单纯依赖模型的已有知识更容易验证。
实际开发中,可以采用以下策略:
- 给出明确上下文:例如 Go 版本、库版本、错误日志和目标。
- 检索可信资料:优先使用官方文档、源码和经过审核的知识库。
- 要求引用来源:让用户能够检查关键结论依据;但要注意,模型生成的引用本身也可能是假的,必须核实。
- 执行程序验证:对 Go 代码进行编译、测试或静态检查。
- 允许模型表达不确定性:资料不足时,应允许它说明无法确定,而不是强迫它给出答案。
- 设置后端约束:对权限、资金、库存、数据库写入等关键操作,必须由程序执行确定性检查。
一个重要的工程原则
不要让模型成为唯一的事实来源,更不要让模型成为唯一的安全边界。
例如,在你熟悉的电商项目中,库存是否充足应由数据库或可靠的库存服务判断,而不是让模型根据对话内容决定库存数量。
模型可以解释库存规则、生成操作建议,甚至提出工具调用请求,但实际扣库存仍需要由 Go 后端完成校验和原子操作。
四、模型的能力边界:什么该交给 LLM?
这是设计 AI 应用时最重要的架构判断之一。
你已经熟悉传统 Go 后端。可以把 LLM 看作一个新增的能力组件,而不是整个系统的替代品。
| 任务 | 更适合谁负责? | 原因 |
|---|---|---|
| 理解用户的自然语言问题 | LLM | 擅长语言理解与表达 |
| 将问题分类为 Go、Redis 或 MySQL | LLM 或规则系统 | 模型灵活,规则更确定 |
| 校验请求参数是否为空 | Go 后端 | 规则明确、结果确定 |
| 判断用户是否有权限删除文章 | Go 后端 | 必须执行可信的权限校验 |
| 查询数据库中的真实文章 | Go 后端或受控工具 | 需要访问真实数据 |
| 总结检索到的文章 | LLM | 擅长归纳和自然语言生成 |
| 原子扣减库存 | 数据库或库存服务 | 需要事务或原子操作保证一致性 |
| 根据文档回答技术问题 | LLM + 检索 | 模型负责组织答案,资料提供依据 |
| 决定是否执行高风险操作 | Go 后端权限策略 + 必要的人工确认 | 不能只相信模型的判断 |
记住这条分工原则:
- LLM:处理模糊的语言任务。
- Go 后端:执行确定的业务规则。
- 数据库与基础设施:保存事实并保障一致性。
- 检索、测试与监控:为模型输出提供证据和验证。
这也解释了为什么 AI 应用开发仍然需要扎实的后端工程能力。接入大模型不会让 Gin、MySQL、Redis、Context、错误处理和权限控制变得不重要,反而会增加新的边界需要管理。
五、模型调用成本:钱究竟花在哪里?
真实模型 API 通常会根据输入 Token、输出 Token、模型档位以及具体服务的计费规则收费。
常见的两个指标是:
- 输入 Token:System Prompt、用户消息、历史对话、检索资料以及其他随请求发送的内容。
- 输出 Token:模型生成的回答,以及某些服务计费口径中包含的其他输出内容。
注意:缓存 Token、推理 Token、工具调用等是否单独计费,取决于具体服务。后续接入 API 时必须查阅对应的官方价格说明。
1. 计算单次调用成本
假设某模型的价格为:
- 输入:每 100 万 Token 收费 1 美元。
- 输出:每 100 万 Token 收费 4 美元。
假设一次请求:
- 输入 5,000 Token。
- 输出 1,000 Token。
那么:
text
输入成本 = 5,000 ÷ 1,000,000 × 1 = 0.005 美元
输出成本 = 1,000 ÷ 1,000,000 × 4 = 0.004 美元
总成本 = 0.005 + 0.004 = 0.009 美元
也就是一次调用约 0.009 美元。
这些价格是用于练习的假设值,并非任何模型当前的真实报价。
2. 估算调用量增长后的成本
以同样的价格,把调用次数放大到 1,000 次:
| 参数 | 取值 |
|---|---|
| 调用次数 | 1,000 |
| 每次输入 Token | 5,000 |
| 每次输出 Token | 1,000 |
| 输入价格 | $1 / 100 万 Token |
| 输出价格 | $4 / 100 万 Token |
| 输入总量 | 5,000,000 Token |
| 输出总量 | 1,000,000 Token |
| 估算总成本 | 约 9.00 美元 |
这个估算没有考虑缓存折扣、额外计费项目、重试和其他服务费用,属于基础估算,而不是账单预测。
3. 为什么 Agent 可能比普通聊天贵?
普通问答可能只调用模型一次,而 Agent 为完成任务可能调用多次。
例如,用户提出:
帮我查一下项目中 Redis 连接失败的原因,分析日志并给出修改方案。
一个 Agent 可能执行以下步骤:
- 模型分析任务。
- 模型请求调用日志查询工具。
- Go 后端执行工具并返回日志。
- 模型分析日志。
- 模型请求读取配置文件。
- Go 后端返回经过授权的配置内容。
- 模型综合结果并生成最终回答。
这里的工具执行本身未必产生模型 Token 费用,但模型每次调用的输入和输出可能都会产生费用。后续请求也可能重复携带部分历史上下文。
所以,Agent 成本不仅取决于最终回答的长度,还取决于中间调用次数和每次请求携带的数据量。
六、如何在 Go 后端控制成本和风险?
结合你熟悉的 Go 服务架构,建议从以下几个方向入手。
1. 限制请求时间
使用 context.WithTimeout 为模型请求设置时间上限,避免请求长时间占用资源。
go
ctx, cancel := context.WithTimeout(
context.Background(),
30*time.Second,
)
defer cancel()
这段代码只是创建了一个带超时的 Context。要真正限制 HTTP 请求,还必须把这个 ctx 传给支持 Context 的 HTTP 请求或模型客户端。如果模型客户端不使用它,超时取消就不会自动生效。
2. 限制输出长度
根据业务设置合理的最大输出 Token 数。
例如,分类接口只需要少量字段,就不应该允许模型生成一篇几千字的解释。
3. 控制重试次数
遇到网络临时故障时,可以进行有限重试,但需要:
- 设置最大重试次数。
- 设置总耗时上限。
- 对限流错误采用合理的退避策略。
- 避免对所有错误无差别重试。
- 考虑请求是否可能重复执行,以及是否存在重复扣费。
4. 管理上下文长度
不需要每次都把完整聊天历史传给模型。
可以根据业务选择:
- 保留最近的若干轮对话。
- 将旧对话压缩成摘要。
- 只检索与当前问题相关的历史信息。
- 对过长的用户输入和检索结果设置上限。
5. 设置预算与调用上限
在 Agent 中设置最大循环次数、工具调用次数和任务总预算。即使模型持续提出新任务,也不能让循环无限进行。
对于涉及用户配额的系统,还应在服务端记录实际使用量,并在并发请求场景中保证配额扣减的一致性。
6. 记录必要的指标
建议至少监控:
- 模型请求次数。
- 请求成功率和错误类型。
- 请求耗时。
- 输入、输出 Token 数。
- 重试次数。
- Agent 执行轮数。
- 每个用户或业务模块的成本。
这些指标能帮助你定位:到底是 Prompt 太长、历史管理不合理、模型响应慢,还是 Agent 进行了过多的调用。
七、把第一阶段知识串起来
到这里,我们已经掌握了构建 AI 应用所需要的第一批基础知识。接下来用一个完整流程,把它们联系起来。
假设你正在为博客系统开发一个 AI 技术问答功能。
步骤 1:接收用户问题
Gin 接口接收问题,校验用户身份、输入长度和请求参数。
步骤 2:组织 Prompt 和上下文
System 设定助手规则,User 提供问题,必要时携带历史对话和检索资料。
步骤 3:调用 LLM
模型根据输入逐步生成 Token,生成参数影响采样过程和输出长度。
步骤 4:处理结构化结果
如果业务需要 JSON,使用结构化输出能力,并在 Go 中解析和校验。
步骤 5:执行可靠性与安全检查
处理错误、超时、成本限制和权限控制;需要时通过检索与程序测试验证答案。
步骤 6:返回用户
Gin 返回答案,并根据需要保存会话、记录使用量和监控指标。
你可以发现,AI 应用并不是把传统后端换成大模型,而是在原有后端架构中加入模型调用、上下文管理和结果验证等能力。
八、第一阶段学习进度总结
| 课程 | 已学习的内容 |
|---|---|
| 第 1 课 | AI 应用与传统后端的区别,LLM、Tool Calling、Agent、RAG、MCP、Eino 的关系 |
| 第 2 课 | LLM 的训练与推理、Token 生成机制、幻觉的基本概念 |
| 第 3 课 | Token、上下文窗口、历史管理和调用成本 |
| 第 4 课 | Prompt、System/User/Assistant 消息角色、多轮上下文 |
| 第 5 课 | Temperature、Top-P、最大输出 Token 数和 Stop |
| 第 6 课 | JSON 结构化输出、Go 解析与业务校验 |
| 第 7 课 | 幻觉与能力边界、可靠性设计、成本控制 |
目前已经完成的代码实验有:
- 使用 Go 结构体定义生成参数并序列化为 JSON。
- 使用
encoding/json解析模型响应。 - 对结构化结果进行字段校验和业务规则校验。
这些实验都是本地模拟,没有调用真实大模型 API。建议你也亲手跑一遍,再进入第二阶段。
九、阶段复习:检查是否真正掌握
先回答下面 3 道综合题。如果某一题卡住,说明对应的知识点还需要回看。
练习 1:架构设计
你要为博客系统增加 AI 文章问答功能。用户提出自然语言问题,系统需要查询已有文章、组织答案并返回结果。请简要说明 LLM 和 Go 后端各自负责什么。
参考思路:LLM 负责"理解与表达"------把用户口语化的问题变成检索意图,再根据检索到的文章内容组织成通顺答案;Go 后端负责"确定性与边界"------鉴权、参数校验、按意图查 MySQL 拿到文章、把文章内容拼进上下文、控制 Token 预算、解析与校验模型返回、记录日志和用量。一句话:LLM 决定"话怎么说",Go 后端决定"能看什么、能给什么"。
练习 2:可靠性
模型返回了语法正确的 JSON,但其中的技术结论是错误的。为什么 json.Unmarshal 无法解决这个问题?你会增加哪些措施?
参考思路 :
json.Unmarshal只做"字节 → 结构体"的语法与类型转换,它对内容真假一无所知,所以它能挡住{category: redis}这种坏格式,但挡不住{"category":"mysql","summary":"Redis 是关系型数据库"}。补的措施:RAG 提供可核查来源、规则交叉校验、关键结论人工确认、以及把失败样本沉淀成评测集持续回归。
练习 3:成本估算
假设输入价格为每百万 Token 1 美元,输出价格为每百万 Token 4 美元。某次调用输入 5,000 Token、输出 1,000 Token。如果调用 1,000 次,总成本是多少?Agent 为什么可能比单轮问答更贵?
参考思路 :单次 0.005 + 0.004 = 0.009 美元,1,000 次 = 9 美元。Agent 更贵是因为它把一次问答拆成多轮模型调用,每一轮输入里往往还要重复携带历史与工具结果,调用次数和单次输入长度同时上升,成本是乘法关系而非加法。所以 Agent 必须配最大迭代次数、Token 预算和超时上限。
十、下一阶段:Go 调用真实大模型 API
第二阶段将从概念进入实战,课程顺序是:
- 通过 HTTP 理解模型 API 的请求与响应。
- 使用 Go 发起真实模型调用。
- 通过环境变量管理 API Key。
- 设计模型请求与响应结构。
- 实现流式输出与多轮对话。
- 使用 Context 管理超时和取消。
- 实现有限重试、错误处理、日志与模型适配层。
我们会优先用标准库完成最小可运行的调用,再根据需要引入 SDK 或 Eino,不会一开始就引入一堆框架。
进入真实调用前有一个实际问题:如果暂时没有可用的大模型 API Key,也可以先通过模拟客户端把请求、响应和错误处理设计好,再接入实际服务,不需要公开或发送你的密钥。
下一篇:Go AI Agent 实战 08|HTTP 模型 API 的工作原理与请求流程