
字节笔记本
2026年10月24日 · 约 11 分钟读完
给不确定性加边界:概率性系统工程哲学

本文是《从 0 写 Agent》系列第 42 章。这个系列基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版),用 Go 从零实现一个生产级 Agent 运行时;本章讲「概率性系统的工程哲学:给不确定性加边界」。
传统软件是确定性的,同样的输入永远产生同样的输出。但 Agent 不是。
同一个 Agent、同一个问题、同一个时间,跑两次可能得到不同答案。这种"概率性"是 Agent 工程化的核心难题,怎么让一个不确定的系统,产出可靠的结果?
引子:确定性 vs 概率性
传统软件的假设,同样的输入 → 同样的输出。这是整个软件工程的基础,单元测试能跑通,是因为"今天通过明天也通过"。
Agent 打破了这个假设:
agent := &agents.Agent{Name: "助手", Model: "gpt-4o"}
// 第一次跑
agents.Run(ctx, agent, "帮我分析这段代码的问题", opts)
// → "第 3 行有空指针风险..."
// 第二次跑(完全相同的输入)
agents.Run(ctx, agent, "帮我分析这段代码的问题", opts)
// → "这段代码的主要问题是..."(不同的措辞、可能不同的问题点)同一个输入,不同输出。 这让传统的测试、调试、质量保证方法部分失效,你怎么写断言"输出应该等于 X"?输出根本不固定。
Agent 工程化的核心,就是"给概率性系统加确定性边界"。

五个边界:驯服不确定性
边界一:结构确定性(即使内容不同,结构要对)
Agent 的输出内容可能变,但结构必须确定。这就是第 3 章的"结构化输出"(OutputType[T]):
type CodeReview struct {
Issues []Issue `json:"issues"`
Summary string `json:"summary"`
Severity string `json:"severity"`
}
agent.OutputType = agents.OutputType[CodeReview]()不管模型怎么说,输出一定是个 CodeReview 结构,有 issues 数组、有 summary、有 severity。内容变,结构不变。
这是"用类型约束概率",模型自由发挥内容,但框架强制结构。下游代码可以安全地访问 result.Issues[0].Line,不用担心"这次输出有没有这个字段"。
边界二:流程确定性(步骤可预测)
Agent 内部"想什么"不可控,但流程步骤可控。这就是第 4 章的工具调用生命周期 + 第 12 章的 HITL:
任何 Agent Run,都走这个确定流程:
循环 {
调模型(可能产生不同的 Thought)
如果调工具 → 执行工具(工具行为是确定性的)
如果危险工具 → 暂停审批(HITL 是确定性的人为边界)
如果最终输出 → guardrail 检查(确定性的安全检查)
}流程是骨架(确定),内容是血肉(概率)。 骨架固定,血肉才能在安全范围内自由变化。
边界三:验证确定性(结果可校验)
概率性输出不能"信就用",必须验证。agents-go 提供多层验证:
- 输出 guardrail,第 14 章,检测输出是否含 PII、是否超长
- 结构化输出校验,第 3 章,输出必须匹配 schema
- HITL 审批,第 12 章,人验证高风险输出
"信任但验证",允许 Agent 自由生成,但在关键节点加验证门。不验证的不能进生产。
边界四:幂等与可重放(失败可恢复)
概率性系统更容易失败(模型可能生成坏数据),所以恢复能力更重要:
- Session 持久化,第 8-10 章,状态可恢复
- RunState 序列化,第 13 章,HITL 暂停可跨进程恢复
- 工具幂等,重试不会产生副作用(比如"创建订单"工具要防重复创建)
概率性系统必须假设"会失败",并设计好恢复路径。 这是和确定性系统的根本差异,确定性系统可以"失败就报错",概率性系统要"失败可重来"。
边界五:可观察(出问题能查)
概率性系统更难调试,所以可观察性是刚需:
- Tracing,第 25-27 章,每一步都记录
- Lifecycle Hooks,第 19 章,关键节点可挂钩
- 全文日志,第 4 章的工具输出回喂,可审计
确定性系统出问题,看错误日志就够。概率性系统出问题,要看完整 trace,"模型当时想了什么、调了什么、结果是什么"。
设计权衡:温度与确定性
模型的"随机性"由 temperature 控制:
- temperature = 0,最确定(贪婪解码,每次选最高概率的 token)
- temperature = 1,较随机
- temperature > 1,非常随机(可能胡言乱语)
生产环境建议 temperature = 0(或接近 0),最大化确定性。但注意,即使 temperature=0,不同 GPU/不同版本仍可能不同(浮点精度差异)。所以 temperature=0 不是 100% 确定,只是"尽量确定"。
agent.ModelSettings = &agents.ModelSettings{
Temperature: ptr(0.0), // 生产环境用 0
}创意场景(写诗、起标题)用高温度。工程场景(分析、判断、结构化输出)用低温度。
测试概率性系统的特殊方法
传统单元测试(assertEqual)对 Agent 部分失效。替代方法:
方法一:结构断言
// 不断言具体内容,断言结构
result := agents.Run(...)
review := parseOutput(result)
assert.NotNil(t, review.Issues) // 有 issues 字段
assert.IsType(t, []Issue{}, review.Issues) // 是数组
// 不断言具体内容(因为会变)方法二:属性测试
// 断言"属性"而非具体值
result := agents.Run(...)
assert.LessOrEqual(t, len(output), maxLength) // 不超长
assert.False(t, containsPII(output)) // 不含 PII
assert.True(t, isValidJSON(output)) // 是合法 JSON方法三:LLM-as-Judge
// 用另一个模型评判输出质量
score := llmJudge("这个代码审查报告质量如何(1-10分)?", output)
assert.Greater(t, score, 7.0) // 质量分 > 7这对应前面 SkillOpt 那篇讲的"validation gate",用模型验证模型。
方法四:回归测试集
收集"已知好的输出",新版本跑同样的输入,比较输出质量有没有退化。不要求完全相同,要求"一样好或更好"。
设计权衡:什么时候该用 Agent
概率性系统的工程成本高(验证、恢复、可观察都要额外做)。所以不是所有场景都该用 Agent。
| 场景 | 该用 Agent 吗 | 理由 |
|---|---|---|
| 用户登录 | ❌ 不该 | 确定性逻辑,用传统代码 |
| 计算订单总价 | ❌ 不该 | 确定性计算 |
| 客服问答 | ✅ 该 | 需要理解和推理 |
| 数据分析 | ✅ 该 | 边查边推理 |
| 发送通知 | ❌ 不该 | 确定性触发 |
| 代码生成 | ✅ 该 | 创造性任务 |
原则,能用确定性代码解决的,别用 Agent。 Agent 是"最后手段",当问题太模糊、太复杂、需要推理时,才值得承受概率性系统的成本。
小结
读完这一章,你应该理解:
- Agent 是概率性系统,同输入可能不同输出,打破传统软件的确定性假设
- 五个边界驯服不确定性,结构确定、流程确定、验证确定、可恢复、可观察
- temperature 控制随机性,生产用低温度
- 测试方法要变,结构断言、属性测试、LLM-as-Judge、回归集
- 不是所有场景都该用 Agent,能用确定性代码就别用
核心洞察,Agent 工程化的本质,是"用确定性的边界,框住概率性的内核"。 边界(结构/流程/验证/恢复/观察)确定,内核(模型生成)才能在安全范围内自由发挥。
下一篇
第 43 章,LLM 作为大脑:为什么它适合做 Agent 的决策核心。不是所有 AI 都适合做 Agent,传统 ML 模型做不了,规则引擎做不了。LLM 的什么特性让它成为理想的"Agent 大脑"?
本文是《从 0 写 Agent》系列第 42 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。

