尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

别只顾刷RSI:大模型应用应先完成模型对齐

别只顾刷RSI:大模型应用应先完成模型对齐 先设想一个很常见的场景团队已经把大模型应用的某个自动化指标优化得很高叠加上线前的一系列评测数据看起来一切顺利。可一旦交给真实用户得到的却是“不稳定”“乱说”“格式没法用”这类反馈。如果再深入复盘往往会发现一个被忽略的顺序问题——大家一直在赶 RSI却没有先解决模型对齐。这里的 RSI不是股票技术分析里的相对强弱指标而是很多 LLM 应用团队内部会用的响应稳定性指数Response Stability Index。它的初衷是衡量同一个问题重复提问时模型回答是否保持一致。这个想法本身没有错问题在于它是“过程指标”而不是“目标指标”。如果模型本身还没有和任务意图、输出协议、评测口径对齐那么 RSI 越高可能只是说明模型在“稳定地犯错”。本文会从这个问题切入聊清楚 RSI 与模型对齐的关系再给出一套可以落地的对齐流程、校验脚本和指标分层方案帮助你在开发大模型应用时避免“只顾着刷指标忘了定义什么是对的”这个常见坑。适合正在做大模型应用评测、Prompt 优化、智能客服或 Agent 项目的开发者阅读。即便你只是刚接触大模型应用也能通过本文理解为什么“稳定”不等于“正确”以及如何在工程上把“对齐”这件事提前。1. 一个容易被忽略的评估顺序问题1.1 为什么 RSI 高不代表模型好用RSI 这类稳定性指标有一个天然特点它很容易被自动化。脚本把同一段 Prompt 重复调几次再对返回结果做文本相似度计算一个看起来非常客观的数字就出来了。问题也恰恰出在这里。稳定性指标只回答了一个问题“模型这次和上次是否说得差不多”它不回答更关键的问题“模型是否真的在按业务要求完成任务”举一个极端的例子把一个只回复“当前咨询人数较多请您稍后再试”的模型接到客服机器人上无论用户问什么它都输出完全相同的这句话。此时 RSI 可以计算到 1.0非常稳定。但从业务角度看这个模型没有任何可用性因为它没有真正回答任何一个用户问题。这不是在否定 RSI 的价值而是在强调一个顺序如果模型没有先完成对齐稳定性指标反而会成为掩盖真实问题的“保护色”。1.2 什么是模型对齐在学术界“模型对齐”通常指让大模型的行为符合人类意图和价值观方法包括 RLHF、DPO、指令微调等。在应用工程领域模型对齐的范围会更直接一些。一个模型接入具体业务时至少需要完成三层对齐任务意图对齐模型是否理解用户输入要解决什么问题。输出协议对齐模型返回的文本或 JSON 是否符合下游解析要求。评测口径对齐评估指标本身是否真的能代表业务成功标准。很多团队把这三件事当成了“上线前随便调一调”的环节而把大量精力放到指标监控和 Prompt 试错上。一旦指标上涨就觉得系统在变好而实际上模型可能只是在某条 Prompt 模板上越走越偏。1.3 先对齐再赶指标顺序为什么重要如果把大模型应用比作一个黑盒系统那么指标是给系统外部看的仪表盘。仪表盘只有在传感器安装正确的情况下才有意义。模型对齐就是安装传感器之前的校准步骤。举个例子假设我们要模型输出一段 JSON 字符串供前端直接渲染。如果 Prompt 里并没有明确说明“必须输出 JSON不要输出解释文字”模型可能会输出一段非常礼貌的自然语言。这时候即便文本内容非常稳定RSI 也很高下游程序却会因为无法解析而直接报错。等到上线前再去修复解析问题就会陷入“指标明明达标联调却一直挂”的尴尬局面。所以在指标体系设计时应该把“对齐检查”放在 RSI 优化之前。先让模型做对再让它说得稳。2. 用一个小反例看清 RSI 的盲区为了更直观地理解问题我们用 Python 构造一个对比场景。这里使用模拟输出目的是演示指标计算的思路真实环境替换成你自己的模型调用即可。假设业务是电商 FAQ 问答助手其中一个高频问题是“你们的退款政策是什么”import json from difflib import SequenceMatcher def unaligned_model(question: str) - str: # 未对齐模型无论用户问什么都返回同一个兜底话术 return 当前咨询人数较多小助手正在忙请您稍后再试。 def aligned_model(question: str) - str: # 已对齐模型返回符合协议的结构化结果 if 退款 in question: return json.dumps( { answer: 支持7天无理由退款未发货订单可申请全额退款。, need_human: False, }, ensure_asciiFalse, ) return json.dumps( { answer: , need_human: True, }, ensure_asciiFalse, ) def semantic_stability(outputs): texts [] for o in outputs: try: texts.append(json.loads(o).get(answer, )) except Exception: texts.append(o) # 非 JSON 时按原文本比较 scores [] for i in range(len(texts) - 1): scores.append(SequenceMatcher(None, texts[i], texts[i 1]).ratio()) return sum(scores) / len(scores) if scores else 1.0 def protocol_ok(output: str) - bool: # 检查输出是否是合法 JSON 且字段完整 try: data json.loads(output) return isinstance(data.get(answer), str) and isinstance( data.get(need_human), bool ) except Exception: return False接下来分别对两个模型的输出计算指标question 你们的退款政策是什么 model_a_outputs [unaligned_model(question) for _ in range(3)] model_b_outputs [aligned_model(question) for _ in range(3)] print(未对齐模型 RSI:, semantic_stability(model_a_outputs)) print(未对齐模型协议通过率:, sum(protocol_ok(x) for x in model_a_outputs) / len(model_a_outputs)) print(对齐模型 RSI:, semantic_stability(model_b_outputs)) print(对齐模型协议通过率:, sum(protocol_ok(x) for x in model_b_outputs) / len(model_b_outputs))输出结果会是这样模型RSI协议通过率可用性未对齐模型1.00%无对齐模型1.0100%高两个模型的 RSI 完全一样都是满分。差别出现在协议通过率上。这个例子并不是说 RSI 没有意义而是提醒我们如果只看 RSI未对齐模型的“稳定输出兜底话术”会被误判为高质量表现。它在数值上甚至比已经对齐的模型更难发现问题。把它放到真实场景里你会发现更隐蔽的形态。某些模型会每次都按照固定句式回复但内容并没有解决用户问题又或者每次都输出同样的错误知识。由于 RSI 只看“是否一致”不看“是否正确”它自然无法区分这些情况。3. 模型对齐到底要解决哪三件事3.1 任务意图对齐任务意图对齐是让模型明确知道自己被要求做什么。很多 Prompt 写不好本质是任务指令缺失。比如只告诉模型“你是客服助手”却没有告诉它“你要根据知识库回答问题知识库没有答案时不要编造要转人工”。模型在缺乏边界的情况下很容易自由发挥。更友好的做法是把任务描述写成一组可以在代码中维护的约束角色FAQ 知识问答助手。输入用户问题。输出基于知识库的简短答案或转人工标记。不允许编造知识库外的政策输出与问题无关的内容在 JSON 外添加解释性文字。这部分属于业务规则常常需要产品、运营、客服同学一起梳理。不要只让算法同学拍脑袋定规则。3.2 输出协议对齐输出协议对齐是让模型的返回格式能直接被程序消费。对于需要结构化结果的场景最好在 Prompt 中给出精确格式同时在代码里做一层校验。不要指望模型每次都“碰巧”输出合法 JSON。一个常用的方式是在 System Prompt 里写明 JSON Schema{ answer: string回答用户问题的文本, need_human: boolean知识库无法回答时置为 true }同时还要在模型输出后做解析与兜底。如果解析失败不要把错误文本直接透传给用户至少应该记录日志并进入人工兜底流程。3.3 评测口径对齐评测口径对齐是检查“你用来衡量模型好坏的指标是否真的等于业务好坏”。业务好坏的直接表现是用户问题得到了正确解答用户不需要重复提问。中间指标可以是 RSI、首轮解决率、平均响应时长等。你需要确认优化这些中间指标时会不会牺牲业务目标。例如为了提升 RSI把模型温度调到 0问题不大但如果为了提升 RSI让模型在遇到超出知识库的问题时也强行给出一个“标准回答”那就属于牺牲业务目标来迁就指标。评测口径对齐后你会知道哪些指标应该纳入核心看板哪些指标只是分析辅助。4. 实战补上一套“先对齐”的评测流程下面我们用一个最小可运行的工程示例演示如何在 Prompt 优化和 RSI 计算之前先完成模型对齐的闭环。4.1 建议的项目结构在实际项目中建议把评估代码、Prompt、样例数据分开管理。目录结构可以这样组织llm_alignment_demo/ ├── data/ │ └── golden_set.json # 少量人工标注的校验样本 ├── prompt.py # Prompt 模板与消息构造 ├── validator.py # 模型输出结构校验 ├── eval_alignment.py # 对齐评测主流程 └── README.md4.2 先准备一份 Golden SetGolden Set 是人工标注的少量“标准样本”用来检查模型是否理解任务。它不需要很大但必须覆盖典型场景、边界场景和转人工场景。[ { question: 你们的退款政策是什么, expected_keywords: [7天, 退款], need_human: false }, { question: 怎么联系人工客服, expected_keywords: [联系客服, 在线客服], need_human: false }, { question: 如果商品发错地址了能重新补发吗, expected_keywords: [], need_human: true } ]说明expected_keywords 用于快速检查答案是否覆盖关键信息need_human 表示该问题是否需要转人工。这是演示数据真实业务请替换成自己的知识库内容。4.3 编写对齐的 Prompt在开启 RSI 优化之前先用一段结构清晰、包含约束的 Prompt 启动任务。# prompt.py KNOWLEDGE 知识库 1. 支持7天无理由退款未发货订单可直接申请退款。 2. 用户可以在 App 内“我的-联系客服”页面找到在线客服。 3. 商品发出后不支持修改地址请直接联系人工客服处理。 SYSTEM_PROMPT 你是一个电商FAQ知识问答助手。 请严格按照以下规则回答 1. 只能基于知识库内容回答禁止编造知识库之外的政策。 2. 如果知识库无法回答问题请把 need_human 设置为 trueanswer 设置为空字符串。 3. 始终输出 JSON不要输出任何多余解释文字。 JSON 格式如下 {answer: 回答文本, need_human: false} def build_messages(question: str): return [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: f知识库内容\n{KNOWLEDGE}\n\n用户问题{question}}, ]这里的关键点是把知识库作为上下文输入同时在系统提示词中强调“只能基于知识库回答”。不要把所有约束都堆在用户消息里系统层约束通常更稳定。4.4 模型调用封装由于不同团队使用的模型网关不同下面用一个函数占位。你需要将其替换为实际可用的模型 SDK 或 HTTP 接口。# 在 eval_alignment.py 中 def call_llm(messages): 真实环境请通过你的模型客户端调用 1. 使用 OpenAI 兼容客户端 2. 调用公司内部模型网关 这里特意不绑定具体 SDK避免不同版本带来的干扰。 # 以 openai 1.0 为例仅作为接入参考 # from openai import OpenAI # client OpenAI() # resp client.chat.completions.create( # modelyour-model, # messagesmessages, # temperature0.2, # ) # return resp.choices[0].message.content # 本地无法调用模型时可用下面的模拟值跑通流程 last_user_content messages[-1][content] if 退款 in last_user_content: return {answer: 支持7天无理由退款未发货订单可直接申请。, need_human: false} if 客服 in last_user_content: return {answer: 请前往 App 内“我的-联系客服”页面联系在线客服。, need_human: false} return {answer: , need_human: true}在实际代码里建议把 call_llm 单独放到一个 model_client.py 中方便切换模型和记录日志。4.5 编写校验器模型输出之后不要立刻进入下一个环节先做结构校验和业务校验。# validator.py import json def parse_model_output(output: str): if not isinstance(output, str) or not output.strip(): return None, 输出为空 try: data json.loads(output) except json.JSONDecodeError as e: return None, fJSON 解析失败: {e} if not isinstance(data.get(answer), str): return None, answer 字段缺失或类型错误 if not isinstance(data.get(need_human), bool): return None, need_human 字段缺失或类型错误 return data, None def validate_sample(sample, output: str): data, err parse_model_output(output) if err: return False, err # 需要转人工时answer 不应该被下游使用 if sample[need_human]: if data[need_human] is not True: return False, 该问题应转人工但模型没有设置 need_humantrue return True, # 不需要转人工时必须检查是否真正回答了问题 if data[need_human] is True: return False, 模型错误转人工实际可由知识库回答 answer data[answer] for keyword in sample.get(expected_keywords, []): if keyword not in answer: return False, f答案缺少关键信息: {keyword} return True, 这部分校验逻辑并不复杂但很值得投入。因为一旦跳过它模型输出的格式错误或业务错误就会在后期被包装成“指标问题”增加排查成本。4.6 运行对齐评测主流程把 Golden Set、Prompt、模型调用和校验过程串起来# eval_alignment.py import json from pathlib import Path from prompt import build_messages from validator import validate_sample def load_golden_set(): file_path Path(__file__).parent / data / golden_set.json with open(file_path, r, encodingutf-8) as f: return json.load(f) def evaluate(): samples load_golden_set() total len(samples) passed 0 for sample in samples: messages build_messages(sample[question]) output call_llm(messages) ok, reason validate_sample(sample, output) if not ok: print(f[FAIL] {sample[question]} - {reason}) print(f 模型输出: {output}) else: print(f[PASS] {sample[question]}) passed 1 print(f\n对齐通过率: {passed}/{total} {passed / total * 100:.1f}%) if __name__ __main__: evaluate()运行命令python eval_alignment.py当 Golden Set 的对齐通过率达到 100%再开始做 RSI 的稳定性调优会更有底气。5. 把 RSI 放回正确的评估位置5.1 用更严谨的方式计算 RSI实战中的 RSI 不应该只基于文本相似度还应该考虑模型输出是否发生了结构性变化。下面给出一个可扩展的 RSI 计算骨架。其核心思想是对同一个问题重复调用 N 次分别计算“内容稳定性”和“结构稳定性”。import json import statistics from difflib import SequenceMatcher def content_similarity(text_a: str, text_b: str) - float: return SequenceMatcher(None, text_a, text_b).ratio() def extract_answer(output: str) - str: try: data json.loads(output) return data.get(answer, ) except Exception: return output.strip() def extract_structure(output: str) - int: try: data json.loads(output) if isinstance(data.get(answer), str) and isinstance(data.get(need_human), bool): return 0 # 结构合法 return 1 except Exception: return 2 # 完全无法解析 def calc_rsi(model_fn, question: str, n_repeat: int 5): outputs [] for _ in range(n_repeat): messages [ {role: system, content: 请按统一 JSON 格式输出。}, {role: user, content: question}, ] outputs.append(model_fn(messages)) content_scores [] structure_scores [] for i in range(len(outputs) - 1): text_a extract_answer(outputs[i]) text_b extract_answer(outputs[i 1]) content_scores.append(content_similarity(text_a, text_b)) struct_a extract_structure(outputs[i]) struct_b extract_structure(outputs[i 1]) structure_scores.append(1.0 if struct_a struct_b else 0.0) return { content_stability: statistics.mean(content_scores), structure_stability: statistics.mean(structure_scores), rsi: 0.5 * statistics.mean(content_scores) 0.5 * statistics.mean(structure_scores), }这段代码没有绑定具体模型模型函数由你传入因此很适合放到自己的评测脚本中使用。5.2 引入对齐门槛RSI 最适合用在一个“已完成对齐”的模型上做回归监控。若模型输出结构总是变化则 RSI 偏低过程指标能帮助发现线上波动。但 RSI 绝不能替代对齐校验。建议在你的每日评测产线里同时输出这样一组数据协议通过率模型输出可以被程序解析并消费的比例。任务通过率模型回答真的是用户所需结果的比例。RSI在通过率和任务通过率稳定后的文本稳定性。把这组指标放到同一张报表里顺序是“先看对齐率再看稳定性”。如果对齐率不达标RSI 的值不值得解读。6. 常见问题与排查思路问题现象常见原因解决思路RSI 很高但用户仍觉得回答不可用模型输出固定套话没有解决用户问题先检查任务通过率再考虑加知识库与边界约束模型回答总是多余解释文字导致解析失败Prompt 未强调只输出 JSON在 System Prompt 明确格式并增加输出校验加了 JSON 格式要求后模型反而频繁报错任务指令与格式约束混在一起模型理解冲突将角色、任务、格式分成独立段落并增加样例同一个问题多次调用答案内容一直变化温度设置过高或 Prompt 中存在随机变量调低温度固定常用参数后再观察对齐通过率不低但线上业务问题仍很多Golden Set 覆盖不足增加边界样本、异常样本和转人工样本Golden Set 上做得很好换一批问题就退化模型过拟合了少量人工样本扩大 Golden Set 规模并引入线上真实流量采样排查时最有效的方法是不要只看失败样本还要把模型原始输出完整打印出来。很多时候问题出在 Prompt 和协议本身而不是模型能力。7. 工程最佳实践与推进节奏7.1 把 Prompt 当代码管理Prompt
返回列表