SocialCoach 装完能跑通示例,只说明依赖装上了、链路通了;换成你自己的对话就报错或答非所问,通常不是模型突然变差,而是两件事叠在一起:输入格式没和示例对齐,以及你给的场景本来就超出了示例演示的范围。要分清这两者,可行的顺序是先固定一条已知可用的对照基线,再用同一套记录方式逐步换输入、加难度,最后把确认可用的输入固化成模板。
示例跑通不等于你的场景跑通。建议先原样复现示例并打印完整请求体与响应体作为基线;再逐字段把自己的对话改写成示例格式,排除角色标记、轮次顺序、文本长度这类低级失败;最后用单一情绪、双方冲突、信息不全三组场景递增压测。判断依据放在日志里的原始输入输出,而不是页面上的最终回答。模板只固化格式,不代表能力上限,换配置后建议重跑第一组场景。
先复现示例并完整打印输入输出
这一步的用途是拿到一份对照基线:示例那一条请求,入参长什么样、返回长什么样,都要能原样打印出来。放在你调用 SocialCoach 的那一层做包裹最省事,不要先去改内部逻辑。
# 在你自己的调用层加打印
import json, logging
log = logging.getLogger('socialcoach.trace')
def call_socialcoach(payload: dict):
log.info('REQ %s', json.dumps(payload, ensure_ascii=False))
resp = client.chat(payload) # 替换成你实际的调用方式
log.info('RESP %s', json.dumps(resp, ensure_ascii=False, default=str))
return resp
请求体建议保留:角色字段、每条消息的原文、轮次先后、被调用的模型或引擎标识。响应体建议保留:返回文本全文、结束原因一类字段、错误码与错误原文。另外单独记下超时设置和重试次数,否则很难判断一次失败是内容问题还是被切断。落盘时一行一条 JSON,后续用 diff 对比示例和自己的请求,比在终端里翻屏可靠。验证方式是:重放示例请求两次,两次打印的请求体应当基本一致。
按示例格式改写自己的第一段对话
这一步的目的很窄,只排除格式层面的低级失败。做法是把示例输入和你的输入逐字段摆在一起看,而不是凭印象觉得“差不多”。最容易出错的三处:角色标记的大小写和取值(例如 User 与 user、是否接受 system)、轮次顺序(是否必须以 user 开头、能否出现连续同角色)、文本长度(示例是一句话,你贴的是一整段聊天记录)。
sample = {'messages': [{'role': 'user', 'content': '示例里的那句话'}]}
mine = {'messages': [{'role': 'User', 'content': '我自己的对话……'}]}
for i, (a, b) in enumerate(zip(sample['messages'], mine['messages'])):
print(i, a['role'] == b['role'], len(a['content']), len(b['content']))
print('多出的字段:', set(mine) - set(sample))
print('缺失的字段:', set(sample) - set(mine))
建议的改写顺序是:先把你的场景压缩成一句和示例长度接近的单轮输入,跑通;再逐步加长文本、增加轮次。每加一个变量只改一处,这样出错时能立刻知道是哪一处引入的。如果压到一句话仍然报格式错误,基本可以判定是字段结构问题,而不是场景太复杂。
准备三组难度递增的压测场景
三组场景按复杂度递增,用来观察它在哪一档开始不稳定。每组建议准备 3 到 5 条样本,同一组内只改文本内容,不改字段结构。
- 单一情绪表达:一句话、一个情绪词、没有背景信息。观察点是能否识别情绪,以及是直接回应还是一上来就连环追问。
- 双方冲突对话:两个说话人多轮交替,包含指责和辩解。观察点是会不会只站一方、会不会把说话人张冠李戴。
- 信息不全需要追问:故意缺关键前提,比如时间、双方关系、想要的结果。观察点是先追问还是硬编一个答案。
场景清单可以按下面这个模板落在文件里,方便逐条勾选:
scene: single_emotion
messages:
- role: user
content: 用户原话,保持一段,不改写
expect: 先识别情绪,再问一句是否需要展开
notes: 只改 content,其余字段与示例一致
执行时把每组场景的原始请求和原始响应都按第一步的方式记录下来。判定标准写在跑之前,比如“信息不全时必须先追问”,跑完再对照,避免事后改判。
记录失败样本并归类原因
散落的报错只有归了类才能复用。建议每条失败样本都记:样本 ID、原始输入、原始响应、所属场景、复现命令或参数、是否稳定复现。
| 类别 | 典型现象 | 对应的验证动作 |
|---|---|---|
| 格式错误 | 请求直接失败,报校验或字段相关错误 | 用示例 payload 替换字段逐个重放,确认是角色、顺序还是多余字段导致 |
| 超长 | 内容被截断、后半段要求没被响应 | 对同一条输入做二分缩短,找到开始稳定返回的文本量级 |
| 拒答 | 返回明确拒绝或空回复 | 换成中性措辞重放同一条,判断是措辞触发还是格式触发 |
| 答非所问 | 回答与最后一轮无关,像在回应更早的内容 | 检查轮次顺序和上下文是否完整传入,去掉中间轮次再跑一次 |
需要留意的边界是:一次失败不构成结论。同一类别至少复现两次,才值得写进经验记录;偶发一次的先标为待观察,不要急着改配置。
把可用输入格式固化成模板
把已经确认能稳定触发的输入结构抽成模板,下次只填内容,减少反复试错。模板里保留必要的角色、顺序和长度约束,去掉示例里没有、你也不确定是否被接受的字段。
scene: <场景名>
messages:
- role: user # 与示例大小写保持一致
content: | # 一段文本,不混入额外结构
在这里替换成你的对话原文
expect: <你期望的输出方向>
notes: <只改了哪一个变量>
提交前按这份清单自查:角色取值与示例一致;第一条是 user;最后一条是要回应的那轮;单条文本长度在示例量级以内;没有示例里不存在的字段;文本里没有多余的不可见字符;时间、人物关系这类信息写在正文里而不是新增字段。
模板固化的是输入格式,不等于场景能力的上限;压测结论也只对当前这套配置和这批样本成立。换模型、换提示词或改参数之后,建议从第一组单一情绪场景重新跑一轮,确认基线仍然成立。