LLM 应用的单元测试要点 在 LLM 应用里,“对话变动大、输出不稳定、依赖外部服务”让单元测试比传统业务代码更棘手。核心是把“模型不确定性”与“业务逻辑确定性”解耦:对外用真实模型做集成/评测,对内用可控的 mock 做纯单元测试。 难点与解决思路 非确定性输出: 相同输入得到不同文本,难以断言结果。 解决: 抽象 LLM 接口并使用 mock ,对业务只断言“是否调用正确、是否走对分支、结构是否满足”,不要对真实语言内容做严格匹配。 提示词/链路易碎: Prompt 修改会击穿大量断言。 解决: 断言“包含关系/结构特征”而非完整字符串 ;对关键 prompt 片段做“黄金样本”回归测试,允许非关键部分漂移。 上下文与状态: 记忆、工具调用、RAG 依赖外部 IO。 解决: 边界外置 (向量检索、工具、存储均以接口注入),在单元测试中替换为纯内存和 mock。 流式与并发: Token 级别回调、取消、超时。 解决:为流式接口引入 回调/通道 ,在测试里用 可控的 fake 逐步推送 token,断言顺序、取消与资源回收。 评测与单测边界: 质量评测更像端到端测试或离线评估,而不是单元测试。 解决:把“是否答对/是否优于基线”放到 集成/评测 ,把“代码在给定条件下的行为”放到 单元 。 单元测试的实践策略 用接口隔离 LLM: 依赖 llms.Model (或等价)而非具体实现;在测试里注入 mock。 只测可控行为: 分支选择、参数传递、prompt 关键片段、错误传播与重试逻辑。 黄金样本回归: 对关键输出做少量 golden files(或字符串快照),配合 Review 审核变更。 流式测试: fake 模型分片发送 token,测试消费端时序、背压与取消。 并发与超时: 用 context.WithTimeout ,断言超时路径与资源释放。 RAG/工具 mock: 检索与函数调用以接口注入,测试中返回固定结果,覆盖“无结果/多结果/冲突结果”。 用 langchain-go 构建一个极简聊天机器人 下面示例基于 langchain-go( github.com/tmc/langchaingo ),构建一个无记忆的“简洁助理”聊天机器人。实际接入 OpenA...