Go AI Agent 实战 05|Temperature 与生成参数:让模型输出可控

本系列共 10 阶段 ,面向有 Go 后端基础的开发者,全程用 Go 手写实现。

当前进度:阶段一 大模型基础 · 第 5 课

上一篇:Prompt 与消息角色

前面我们已经学习了 LLM 的基本原理、Token、上下文窗口和 Prompt。这一课继续学习一个非常重要的主题:如何控制大模型生成答案的方式。

在实际开发中,你可能会发现,同一个问题交给同一个模型,有时回答很严谨,有时回答很有创意,甚至每次表述都不一样。这与模型的生成机制和相关参数有关。

本课先理解原理,再用 Go 代码模拟参数配置,为后续真实 API 调用做准备。

一、本课学习目标

学完之后,你应该能够:

  1. 解释 Temperature 的作用,以及它为什么会影响回答的随机性。
  2. 理解 Temperature、Top-P、最大输出 Token 数之间的区别。
  3. 根据技术问答、JSON 输出、创意写作等场景选择合适的参数。
  4. 理解为什么降低 Temperature 不能彻底消除幻觉。
  5. 使用 Go 设计一份清晰的模型生成参数配置。

二、为什么需要生成参数?

假设我们正在开发一个 Go 技术助手,用户问:

请解释 Redis 缓存穿透,并给出 Go 后端的解决方案。

模型可能用不同的方式回答:

  • 回答 A:直接给出定义、原因、解决方案和代码。
  • 回答 B:先用生活中的例子解释,再分析技术原理。
  • 回答 C:展开介绍缓存穿透、缓存击穿、缓存雪崩,以及多种架构设计。

这些回答可能都与问题有关,但它们的风格、详细程度和内容组织不同。

我们希望能够对模型的生成过程施加一定控制,而不是完全依赖默认设置。

常见的生成参数包括:

参数 主要作用
temperature 调节生成时对不同候选 Token 的选择倾向
top_p 限制采样时考虑的候选 Token 范围
max_tokens 或类似参数 限制本次生成的最大输出 Token 数
stop 指定遇到某些停止标记时结束生成

注意:不同模型服务的参数名称、取值范围和支持情况可能不同。这里先学习通用概念,后续接入真实 API 时再以对应服务的官方文档为准。

三、Temperature:控制生成的随机性

1. 先理解模型如何选择下一个 Token

假设模型已经生成了下面这句话:

Go 语言适合用于......

接下来,模型可能考虑多个候选 Token。为了方便理解,我们使用一组虚构的示意概率:

候选 Token 示意概率
后端开发 60%
网络服务 25%
游戏开发 10%
其他内容 5%

以上仅用于演示,不代表任何真实模型的概率分布。

如果模型总是选择概率最高的候选项,生成结果通常会更偏向高概率路径。

但大模型的生成并不一定每一步都选择最高概率的 Token。实际系统可以通过采样等方法,在多个候选项之间进行选择。

Temperature 就是影响这个选择过程的参数之一。

2. Temperature 的工作原理

在常见的采样实现中,模型会先得到候选 Token 的分数(通常称为 logits),然后通过 Temperature 调整这些分数的相对差异,再转换成概率分布。

可以用下面的流程理解:

text 复制代码
模型计算候选 Token 的分数(logits)
      ↓
Temperature 调整分数差异,重新转换成概率分布
      ↓
按调整后的分布采样,选出下一个 Token
      ↓
把 Token 追加到上下文,继续生成下一个

简单记忆:

  • 较低的 Temperature:分布通常更加集中,高概率候选项更占优势。
  • 较高的 Temperature:分布通常更加平缓,低概率候选项也更有机会被选中。

这不是简单地给回答添加随机字符,而是影响模型在生成过程中选择后续 Token 的倾向。

3. 不同 Temperature 有什么区别?

下面用三个典型取值理解。它们是概念示例,并不代表所有模型都支持完全相同的范围。

典型取值 随机性 说明
0.0 低 更倾向于高概率候选项。适合严谨的技术解释、分类和结构化任务,但不保证每次输出绝对相同,也不保证答案正确。
0.7 中 在稳定性与表达变化之间取得一定平衡。可以作为一般对话的实验起点,但不是所有模型的最佳默认值。
1.5 高 在支持这一范围的模型上,生成内容可能更有变化,适合探索不同创意;也可能更容易偏离要求。

这里有两个必须记住的结论:

  1. Temperature 影响的是生成倾向,不是模型的知识水平。
  2. 降低 Temperature 不会让模型自动获得最新知识,也不能保证它不产生幻觉。

例如,模型不知道某个 Go 库的新版本 API。把 Temperature 从 0.7 调成 0,并不会凭空让它知道正确的 API。

要解决这个问题,通常需要提供可靠的资料、检索官方文档、进行结果验证,或者限制模型只能使用经过确认的信息。

四、Top-P:另一种控制候选范围的方法

Temperature 调整概率分布,Top-P 则主要控制采样时考虑的候选范围。

假设候选 Token 按概率从高到低排列:

候选项 概率
A 60%
B 25%
C 10%
D 5%

如果设置 top_p = 0.85,按照常见的 nucleus sampling(核采样)方式,系统会选取累计概率达到阈值所需的最小候选集合。

这里 A 的概率是 60%,A+B 的累计概率是 85%,因此示意情况下只保留 A 和 B:

候选项 概率 累计概率 top_p = 0.85 是否纳入
A 60% 60% ✅ 纳入候选
B 25% 85% ✅ 纳入候选
C 10% 95% ❌ 排除
D 5% 100% ❌ 排除

这是简化示意:假设概率分布已经归一化,且未经过 Temperature 调整。实际采样过程通常会先结合其他生成设置,再决定最终候选集合。

有两个注意事项:

  • Top-P 是基于候选项的累计概率来限制范围,不是"只选概率最高的前 P 个 Token"。
  • Temperature 和 Top-P 都可能影响输出的多样性。实际开发中,建议先调整一个参数、观察效果,不要一开始同时调很多参数。

五、最大输出 Token 数:控制回答长度

假设你调用模型,希望它用 Go 解释 Redis 缓存穿透,并给出代码。

如果最大输出 Token 数太小,模型可能只输出定义,还没来得及给出代码就结束了。

例如:

  • 设置较小的输出上限:回答可能不完整。
  • 设置合理的输出上限:可以容纳原理、代码和解释。
  • 设置过大的输出上限:不代表模型一定会生成更多内容;但可能增加可用输出预算或成本风险,具体取决于服务的计费和限制方式。

需要区分两个概念:

概念 含义
上下文窗口 一次请求能够处理的整体 Token 容量,通常涉及输入和输出
最大输出 Token 数 对本次生成结果长度设置的上限

举个例子:某模型的上下文窗口为 16K Token,输入已经占用 12K,最大输出设置为 8K。

不能因此认为它一定能生成 8K Token。输入和输出需要共同满足模型及服务的上下文限制,实际还可能存在额外的系统开销或服务端限制。

此外,Token 数不等于中文字符数,也不等于 Go 字符串的字节数。前面课程已经讲过这个区别。

六、其他常见生成参数

Stop:遇到指定标记时停止

某些 API 支持 stop 参数,用来指定一个或多个停止字符串。

例如,你希望模型生成某种特定格式,并在遇到特定标记后停止,就可以研究服务是否支持停止序列。

但要注意:

  • stop 的具体行为依赖模型服务。
  • 不同接口可能有不同的限制。
  • 对 JSON 输出,不能随意设置停止字符串,否则可能把 JSON 截断成无法解析的内容。

不要为了"让模型尽快结束"就随便加一个停止标记。首先要确保它不会破坏输出格式。

七、Go 后端如何设计生成参数?

目前我们还没有进入真实模型 API 调用阶段,所以先不假设任何厂商的请求格式。我们用 Go 定义一份自己的配置,理解后端如何管理这些参数。

1. 完整示例

新建 main.go:

go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type GenerationConfig struct {
	Temperature     float64 `json:"temperature"`
	TopP            float64 `json:"top_p"`
	MaxOutputTokens int     `json:"max_output_tokens"`
}

func main() {
	// 技术问答场景的示例配置
	config := GenerationConfig{
		Temperature:     0.2,
		TopP:            0.9,
		MaxOutputTokens: 2048,
	}

	data, err := json.MarshalIndent(config, "", "  ")
	if err != nil {
		fmt.Println("序列化配置失败:", err)
		return
	}

	fmt.Println(string(data))
}

运行:

bash 复制代码
go run main.go

预期输出:

json 复制代码
{
  "temperature": 0.2,
  "top_p": 0.9,
  "max_output_tokens": 2048
}

这段代码可以直接运行,但要明确:它只是在 Go 中定义并序列化配置,没有调用真实的大模型。max_output_tokens 也是我们自己定义的字段名,不保证任何服务都接受这个字段。

2. 逐段理解代码

第一部分:定义配置结构体

go 复制代码
type GenerationConfig struct {
	Temperature     float64 `json:"temperature"`
	TopP            float64 `json:"top_p"`
	MaxOutputTokens int     `json:"max_output_tokens"`
}

这里使用 struct 将多个相关参数组织在一起。

  • Temperature 使用 float64,因为它可能是小数。
  • TopP 使用 float64,因为它通常也是小数。
  • MaxOutputTokens 使用 int,因为输出 Token 上限是整数。
  • JSON Tag 指定序列化时的字段名称。

这和你熟悉的 Go 后端配置设计类似:将相关配置集中管理,而不是散落在业务代码中。

第二部分:根据业务场景创建配置

go 复制代码
config := GenerationConfig{
	Temperature:     0.2,
	TopP:            0.9,
	MaxOutputTokens: 2048,
}

这份配置是针对技术问答的一个实验起点,不是普遍适用的最佳参数。

真实项目中,你还需要考虑模型自身支持的参数、业务要求、输出格式以及调用成本。

第三部分:序列化

go 复制代码
data, err := json.MarshalIndent(config, "", "  ")

json.MarshalIndent 将 Go 结构体转换成格式化的 JSON。

当我们接入真实 API 时,可以在适配层将内部配置转换成服务要求的请求格式。但不能假设不同厂商的 API 字段完全相同。

3. 在实际项目中如何选择参数?

以你后续要开发的 Go AI 技术助手为例:

业务场景 参数选择思路
Go 技术原理问答 可以先试较低 Temperature,重点保证回答结构清晰
生成严格 JSON 优先使用 API 支持的结构化输出能力,并校验结果
生成代码 适度控制随机性,同时进行编译和测试验证
技术文档总结 以稳定、准确、完整为优先目标
创意文案生成 可以尝试提高 Temperature,比较不同结果
Agent 调用工具 重点保证工具参数符合 Schema,并由 Go 后端校验;不能仅靠 Temperature 保证安全

特别注意:即使 Temperature 设置为 0,模型仍然可能给出错误的 Go 代码、错误的 API 名称或不正确的技术结论。

对于技术助手,真正可靠的设计应当是:

  1. 通过 Prompt 明确任务和输出要求。
  2. 必要时检索官方文档或可信知识库。
  3. 对 JSON 和工具参数进行程序校验。
  4. 对代码执行编译、测试等验证。
  5. 对工具调用实施权限控制。

模型参数是调节手段,不是可靠性保障机制。

八、本课总结

本课的核心知识可以归纳为:

  • Temperature:影响生成概率分布的集中程度,进而影响采样的随机性。
  • Top-P:通过累计概率阈值限制候选 Token 范围。
  • 最大输出 Token 数:限制本次生成的长度,不代表模型一定会生成这么多 Token。
  • Stop:在满足特定停止条件时结束生成,必须避免破坏输出格式。
  • 工程实践:按场景选择参数、用实验验证效果,并在 Go 后端实施校验。

九、课后练习

练习 1:Temperature

同一个模型回答 Go 技术问题时,你将 Temperature 从 0.2 调整到 1.2。通常会有什么变化?为什么不能因此认定 1.2 的答案更准确?

参考思路:调高后概率分布更平缓,低概率候选也有机会被选中,表现为措辞更多变、展开路径更发散,同时偏离要求(跑题、编造)的概率也上升。它改变的是"怎么选词",不改变模型掌握的知识,所以和"准不准确"没有因果关系------提高 Temperature 只会让答案更难复现,便于对比实验,不能提升事实正确性。

练习 2:参数选择

你正在开发一个 Go 接口,需要模型返回以下 JSON:

json 复制代码
{
  "category": "redis",
  "difficulty": "beginner",
  "summary": "缓存穿透的解释"
}

你会重点关注哪些生成参数或 API 能力?为什么即使 Prompt 写得非常明确,也仍然需要 Go 后端校验?

参考思路 :优先用服务端的结构化输出 / JSON Schema 能力,而不是靠 Prompt 描述格式;Temperature 压低到接近 0;给输出留够 max_tokens 避免截断;不要随意设 stop,容易把 JSON 切断。后端校验不能省,因为 Prompt 是"请求配合",模型仍可能多带 ```json 围栏、漏字段、把枚举值写成同义词。校验放在 json.Unmarshal 之后,检查字段非空和枚举白名单。

练习 3:Token 限制

模型上下文窗口为 16K Token,当前请求的输入约为 12K Token。你把最大输出 Token 数设置为 8K,是否意味着模型一定可以完整输出 8K Token?请说明原因。

参考思路 :不一定。输入 12K + 输出 8K = 20K,已经超过 16K 窗口,服务端可能直接拒绝、也可能截断输出。max_tokens 是上限不是承诺,实际可用输出 ≈ 窗口 − 输入 − 系统开销。正确做法是让输入与输出之和留出余量,或先裁剪历史再调大输出上限。


下一篇:Go AI Agent 实战 06|JSON 结构化输出与 Go 后端校验

相关推荐
金銀銅鐵1 小时前
[Java] 展示添加元素时 ArrayList 的内部变化
java·后端·ai编程
一只爱撸猫的程序猿1 小时前
我让 Agent 干活,自己只做审核:一个真实项目的六次"人审拦截"
人工智能
斌糖雪梨1 小时前
spring auth 核心流程详解
java·后端·spring
镜子AI1 小时前
镜子AI工程实践:用 Python 做 GEO 实体一致性审计,并自动生成 JSON-LD 结构化数据
人工智能·python·json
liliangcsdn2 小时前
DeepSeek DeepInfer开源项目的功能分析和探索
大数据·人工智能
燕落南枝2 小时前
CSS 学习:类型选择器与 AI 注释实战
css·人工智能·学习
今天的砖头有点烫手啊2 小时前
GPT-6 Pro 拉起“AI 小队”、ChatGPT 长出“双手”:OpenAI 把 Agent 干活的方式重写了
人工智能·agi
田里的水稻2 小时前
FA_CNN卷积神经网络七---优化器PyTorch
人工智能·pytorch·深度学习·神经网络·cnn
文丑颜不良啊3 小时前
LeetCode201~225题目解析
java·leetcode