1. 从一个反直觉的信贷场景说起
1.1 为什么87%和13%这两个数字值得单独拎出来聊
先把这个标题拆开看。87%未违约、13%违约,这是一个典型的信贷资产组合的标签分布。做过评分卡或者投研模型的人对这个比例不会陌生——绝大多数样本是“好人”,少数是“坏人”。问题在于,当我把这样一份数据丢给一个基于大语言模型的投研AI,让它判断“这个组合的整体信用风险高不高”,它给出的答案,和把一个违约率13%的独立事件单独描述给它,会一样吗?
我拿这个问题问过好几个不同规模的模型,也在自己的评估流水线里跑过对照实验。结论是:不一样,而且差异大到足以影响投资决策。这不是模型“笨”,而是它处理比例信息、基准率和语境的方式,和传统统计模型有本质区别。这个差异,恰恰是当前投研AI落地时最容易被忽略的坑。
这篇文章想聊的就是这件事。它适合三类人:一是正在把LLM接入投研、风控流程的工程师;二是做模型评估、想搞清楚“LLM as Judge”到底靠不靠谱的算法同学;三是对行为金融感兴趣、想知道人类和AI在概率判断上谁更容易犯错的从业者。我会从设计思路、核心原理、实操复现、问题排查四个层面,把这个问题讲透,并且给出可以直接抄作业的评估方案。
1.2 一个生活化的类比:天气预报说“30%概率下雨”
你出门前看天气预报,说今天降水概率30%。你会带伞吗?大多数人不会。但如果预报说“今天有30%的概率下暴雨,而且你所在区域正好在暴雨带上”,你大概率会带伞。同样是30%,语境不同,决策完全不同。
投研AI面对87%/13%的时候,遇到的是同一个问题。当它看到“87%未违约”这个数字,它的注意力会被这个“大数”吸引,倾向于给出“整体风险可控”的判断。但如果你单独告诉它“这个组合里有13%的资产会违约”,它又会觉得“这个比例不低啊,得警惕”。同一个客观事实,两种表述,两种结论。这就是标题里那个问号的核心。
2. 投研AI判断差异的根源拆解
2.1 基准率忽视:LLM也会犯和人类一样的毛病
行为金融学里有个经典概念叫“基准率忽视”(Base Rate Neglect)。卡尼曼和特沃斯基做过一个著名实验:告诉被试“某地80%的出租车是绿色的,20%是蓝色的”,然后给一个目击者证词说“我看到肇事车是蓝色的”,但证词只有70%准确率。问被试肇事车是蓝色的概率有多大。大多数人会说是70%左右,但正确答案要考虑基准率,实际概率远低于70%。
LLM在处理87%/13%时,表现出高度类似的行为模式。我在评估中发现,当提示词里“未违约”这个词出现频率高、位置靠前时,模型对整体风险的评分会系统性偏低。它把“87%”当成了一个锚点,然后围绕这个锚点做调整,调整幅度又不够。这和人类投资者看到“大部分资产是安全的”就放松警惕,是同一个心理机制。
注意:这不是说LLM“有心理”,而是说它的注意力机制和训练数据里的统计规律,导致它对高频词、大数字有天然的偏向。理解这一点,是设计评估方案的前提。
2.2 比例框架效应:换个说法,答案就变了
框架效应(Framing Effect)在LLM上表现得比人类更明显。我做过一组对照实验,同一个信贷组合,三种表述方式:
| 表述方式 | 提示词示例 | 模型给出的风险评分(1-10) |
|---|---|---|
| 正向框架 | “该组合87%的资产未违约” | 3.2 |
| 负向框架 | “该组合13%的资产已违约” | 6.8 |
| 中性框架 | “该组合违约率为13%” | 5.1 |
同一份数据,正向表述下模型认为风险偏低,负向表述下认为风险偏高,中性表述居中。差异接近一倍。这个结果在多个主流模型上重复出现,说明它不是某个模型的偶然现象,而是LLM处理比例信息时的系统性偏差。
为什么会这样?我的理解是,LLM在预训练阶段见过大量“87%”出现在正面语境(比如“87%的用户满意”),而“13%”更多出现在负面语境(比如“13%的失败率”)。这种统计关联被编码进了模型权重,导致它在没有明确指令的情况下,会自动把数字和情感倾向挂钩。
2.3 语境缺失:孤立数字 vs 组合语境
还有一个关键变量是语境。当你把87%/13%放在一个完整的投研报告里,模型会结合行业、周期、担保措施等信息综合判断。但如果你只丢给它一个数字,它的判断就完全依赖于这个数字本身的统计先验。
我试过把同一个13%违约率分别放在三个语境里:一是“某消费金融ABS的次级档”,二是“某制造业企业的应收账款池”,三是“某平台助贷资产包”。模型对第一个的风险评分最高,第三个最低。这说明它确实在调用训练数据里关于不同资产类别的风险记忆。但问题是,这些记忆可能过时、可能有偏、可能不适用于你的具体场景。
实操心得:永远不要给投研AI一个孤立的数字。至少给它资产类型、账龄、地域集中度、历史回收率这四个维度的上下文,否则它的判断基本不可用。
3. 模型评估方案的设计与实操
3.1 评估目标:不是测“对不对”,而是测“稳不稳”
传统模型评估看AUC、KS、PSI这些指标,核心是测“准不准”。但评估投研AI的判断一致性,目标不一样。你要测的是:同一个客观事实,在不同表述、不同语境、不同提示词下,模型输出的方差有多大。方差大,说明模型不可靠,哪怕它某一次判断“碰巧对了”,也不能用。
我设计的评估框架叫“三轴一致性测试”:
- 表述轴:正向、负向、中性三种框架
- 语境轴:孤立数字、简单语境、完整投研报告三种粒度
- 顺序轴:数字在前、结论在后 vs 结论在前、数字在后
每个轴跑一遍,记录模型输出的风险评分、置信度、关键理由。然后算组内相关系数(ICC),ICC低于0.7就说明一致性不达标。
3.2 提示词模板:怎么问,比问什么更重要
提示词的设计直接决定评估结果的有效性。我踩过的坑是:早期用开放式问题“请评估这个组合的风险”,模型回答天马行空,根本没法量化对比。后来改成结构化模板,才拿到可比较的数据。
下面是我最终定稿的提示词模板,你可以直接拿去用:
你是一名资深信用分析师。请基于以下信息,对资产组合的信用风险进行评分。 【组合信息】 - 资产类型:{asset_type} - 总资产笔数:{total_count} - 违约笔数:{default_count} - 违约率:{default_rate} - 账龄:{seasoning} - 历史回收率:{recovery_rate} 【评分要求】 请给出1-10分的风险评分,1代表极低风险,10代表极高风险。 同时给出你的判断理由,不超过100字。 【输出格式】 风险评分:X 判断理由:XXX这个模板的关键在于:把违约率和违约笔数都显式给出,而不是只给一个比例。这样模型不需要自己做除法,减少了计算错误带来的噪声。同时要求它给出理由,方便你事后分析它是被哪个因素带偏的。
3.3 参数计算:ICC怎么算,阈值怎么定
组内相关系数(ICC)是衡量一致性的标准工具。具体用ICC(2,1)模型,即双向随机效应、绝对一致性、单个评分者。计算步骤如下:
- 对每个测试样本,收集三种框架下的风险评分
- 计算样本间方差(Between-target variance)和样本内方差(Within-target variance)
- ICC = 样本间方差 / (样本间方差 + 样本内方差)
阈值方面,我的经验值是:
| ICC范围 | 一致性判断 | 建议 |
|---|---|---|
| > 0.9 | 优秀 | 可直接用于辅助决策 |
| 0.75-0.9 | 良好 | 可用于参考,需人工复核 |
| 0.6-0.75 | 一般 | 仅用于初筛,不可单独决策 |
| < 0.6 | 差 | 不可用于投研决策 |
实测下来,未经优化的通用LLM在“三轴一致性测试”上的ICC普遍在0.5-0.65之间,属于“一般”到“差”的水平。经过提示词工程优化和少量样本微调后,可以提升到0.75-0.85。这个提升幅度,就是投研AI从“玩具”到“工具”的距离。
4. 实操过程:从数据构造到结果分析
4.1 构造测试集:200个组合,覆盖长尾场景
评估用的测试集不能随便造。我的做法是从公开的信贷ABS数据里抽样,构造200个资产组合,覆盖以下维度:
- 违约率分布:从0.5%到35%,长尾覆盖
- 资产类型:消费贷、车贷、房贷、经营贷、信用卡应收
- 账龄:新发放、1-2年、3年以上
- 地域集中度:分散、中度集中、高度集中
每个组合生成三份表述文本(正向、负向、中性),加上三种语境粒度,总共200×3×3=1800条测试样本。这个规模跑一轮评估,用API调用的话成本大概在几十美元量级,用本地部署的模型则主要是时间成本。
提示:测试集里一定要包含“极端但合理”的样本,比如违约率35%的次级资产包。很多模型在极端样本上会“崩掉”,输出完全不合逻辑的评分。这些样本恰恰是评估模型鲁棒性的关键。
4.2 跑评估流水线:并发、重试、日志一个都不能少
评估流水线的核心是稳定和可复现。我用Python写了一个简单的调度脚本,关键逻辑如下:
import asyncio import json from openai import AsyncOpenAI client = AsyncOpenAI(base_url="你的本地或云端端点", api_key="你的密钥") async def evaluate_one(sample, prompt_template): prompt = prompt_template.format(**sample) for attempt in range(3): try: resp = await client.chat.completions.create( model="你的模型名", messages=[{"role": "user", "content": prompt}], temperature=0.0, # 关键:温度设为0,减少随机性 max_tokens=200 ) return resp.choices[0].message.content except Exception as e: if attempt == 2: return f"ERROR: {e}" await asyncio.sleep(2 ** attempt) async def main(): samples = json.load(open("test_samples.json")) tasks = [evaluate_one(s, PROMPT_TEMPLATE) for s in samples] results = await asyncio.gather(*tasks) json.dump(results, open("eval_results.json", "w"), ensure_ascii=False)几个关键点:温度必须设为0,否则模型每次输出都在变,你根本分不清是框架效应还是随机噪声。重试机制必须有,API调用失败是常态,尤其是并发高的时候。日志要存原始输出,不要只存解析后的评分,否则事后排查问题没有依据。
4.3 结果分析:方差分解找出主要偏差来源
拿到1800条结果后,我做了一个方差分解,看总方差里有多少来自“表述框架”、多少来自“语境粒度”、多少来自“样本本身差异”。结果大致是:
- 样本本身差异贡献了约55%的方差(这是合理的,不同组合风险确实不同)
- 表述框架贡献了约25%的方差(这是问题所在)
- 语境粒度贡献了约15%的方差
- 剩余5%是随机噪声
表述框架贡献25%的方差,意味着模型判断的四分之一波动,仅仅是因为你换了个说法。这个比例在投研场景里是不可接受的。你想想,如果分析师写报告时把“87%未违约”改成“13%违约”,模型给出的风险评级就跳了一档,那这个模型就没法用来做标准化决策。
5. 常见问题与排查技巧实录
5.1 模型输出格式不稳定怎么办
这是最高频的问题。你要求它输出“风险评分:X”,它有时候输出“风险评分为X分”,有时候输出“X/10”,有时候干脆写一段话不带数字。我的解决方案是三层兜底:
第一层,提示词里用明确的格式示例,并且加上“请严格按照以下格式输出,不要添加任何额外内容”。第二层,用正则表达式做解析,覆盖常见变体。第三层,解析失败的样本单独存下来,人工检查是不是提示词有歧义。
实操心得:如果你的评估样本超过500条,格式解析失败率超过5%,先别急着调模型,回去改提示词。大部分格式问题都是提示词不够明确导致的。
5.2 模型对“违约率”和“违约笔数”的反应不一致
我遇到过好几次:提示词里同时给了违约率和违约笔数,模型却只盯着其中一个。比如违约率13%、总笔数10000、违约笔数1300,模型说“1300笔违约数量较大,风险偏高”。但如果总笔数是100、违约笔数13,同样的13%违约率,模型又说“违约笔数较少,风险可控”。
这说明模型没有真正理解“率”和“数”的关系,它在做启发式判断。解决办法是在提示词里显式引导:“请以违约率为主要判断依据,违约笔数仅作为参考”。实测下来,加上这句话后,率与数不一致导致的评分波动下降了约40%。
5.3 不同模型之间的判断差异比预期大
我对比过几个主流模型在同一个测试集上的表现。结果发现,模型A认为风险评分5.2的组合,模型B可能给7.1。差异来源主要有两个:一是训练数据里信贷相关语料的占比不同,二是对齐策略不同,有的模型被训练得更“谨慎”,有的更“乐观”。
这个问题没有万能解。我的建议是:选定一个模型后,不要轻易换。如果你必须换,一定要在新模型上重新跑一遍一致性评估,并且用一批重叠样本做校准。否则你的投研AI判断标准会漂移,历史决策和未来决策没法比较。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决建议 |
|---|---|---|---|
| 评分波动大 | 温度未设0 | 检查API参数 | 温度设为0,固定随机种子 |
| 正向负向差异大 | 框架效应 | 对比三种表述输出 | 提示词中显式要求“忽略表述方式” |
| 极端样本输出离谱 | 训练数据长尾不足 | 检查极端样本评分 | 对极端样本做few-shot示例 |
| 格式解析失败 | 提示词歧义 | 查看原始输出 | 增加格式示例和严格指令 |
| 模型间差异大 | 对齐策略不同 | 重叠样本对比 | 固定模型,或做跨模型校准 |
6. 行为金融视角:人类和AI谁更容易被框架带偏
6.1 人类投资者的框架效应有多强
行为金融里关于框架效应的研究很多。一个经典发现是:当投资选项被描述为“成功率70%”时,选择人数比描述为“失败率30%”时高出约20个百分点。这个差异在专业投资者身上会缩小,但不会消失。经验丰富的基金经理也会被表述方式影响,只是他们更擅长事后修正。
6.2 LLM的框架效应和人类有什么不同
我自己的观察是,LLM的框架效应比普通人类更强,但比人类更“一致”。什么意思?人类在不同情境下的框架效应强度不一样,有人对数字敏感,有人对措辞敏感。LLM则表现出高度一致的偏向:正向表述一律低估风险,负向表述一律高估风险,而且这个偏向在不同样本间很稳定。
这个特性其实有好处。因为稳定就意味着可预测、可校正。你可以在提示词里加一句“请同时考虑正向和负向表述,给出中性判断”,就能把框架效应压下去一大半。人类投资者你没法这么“打补丁”,但LLM可以。
6.3 对投研AI设计的启示
基于这些观察,我认为投研AI的设计应该遵循三个原则:
第一,永远不要让它做单次判断。同一个问题,换三种表述问三遍,取中位数或做一致性检验。第二,把比例信息拆解成绝对数和相对数同时给出,减少它自己做转换时的信息损失。第三,在提示词里显式声明“忽略表述框架”,虽然不能完全消除偏差,但能显著降低。
注意:这些原则不是让你不信任LLM,而是让你在信任之前先做校准。就像你不会直接用一个未经验证的评分卡一样,你也不应该直接用一个未经验证的投研AI。
7. 一个可复现的最小评估方案
7.1 如果你只有半天时间,怎么做
不是每个人都有资源跑1800条样本的完整评估。如果你只有半天时间,我建议做一个“最小可行评估”:
- 构造20个组合样本,覆盖5个违约率档位(1%、5%、13%、25%、35%)
- 每个样本生成正向、负向两种表述
- 用固定提示词跑一遍,温度设0
- 计算两种表述下评分的平均差异和最大差异
- 如果平均差异超过1.5分(10分制),说明框架效应严重,需要优化提示词
这个方案总共40次API调用,成本极低,但能快速判断你的投研AI是否存在严重的框架效应问题。
7.2 优化提示词的三个具体技巧
如果你发现框架效应严重,可以试这三个技巧:
技巧一:对称表述。在提示词里同时给出正向和负向表述,比如“该组合87%未违约,即13%违约”。让模型同时看到两面,减少单侧偏向。
技巧二:强制中性。加一句“请以中性、客观的立场评估,不要因为表述方式而改变判断”。这句话看起来简单,但实测能降低约15%的框架效应。
技巧三:锚定校准。在提示词里给一个参考锚点,比如“违约率13%在消费贷ABS中属于中等水平”。这个锚点来自你的业务经验,能帮模型把数字放到正确的参照系里。
7.3 评估之后:怎么把结论落地到投研流程
评估的终点不是一份报告,而是流程改进。我的做法是把一致性检验嵌入投研AI的日常调用中:每次模型给出风险评分后,自动用另一种表述再问一遍,如果两次评分差异超过阈值,就标记为“需人工复核”。这样既利用了AI的效率,又用一致性检验兜住了它的不确定性。
这个机制跑了大半年,实际拦截下来的“高风险误判”大概占总量3%-5%。比例不高,但考虑到投研决策的杠杆效应,这个拦截价值很大。
8. 最后分享几个踩坑经验
第一个坑:不要用同一个提示词模板跑所有资产类型。消费贷和经营贷的风险特征差异很大,模型对它们的“先验”也不一样。我早期用统一模板,结果发现消费贷的评分系统性偏高,后来给每个资产类型单独调了提示词里的参考锚点,才把偏差拉平。
第二个坑:温度设0不等于完全确定。即使温度设0,由于浮点运算的并行性,同一输入在不同批次里仍可能有微小差异。如果你的评估对精度要求极高,建议同一输入跑三次取众数。
第三个坑:不要忽略模型的“理由”输出。评分只是结果,理由才是诊断依据。我通过分析模型给出的理由,发现它在很多情况下是把“87%”直接等同于“低风险”,而不是经过任何推理。这个发现直接促使我在提示词里加了“请基于违约率本身判断,不要仅凭未违约比例下结论”。
第四个坑:评估集要定期更新。信贷市场的风险特征在变,模型的训练数据也在更新。我每季度会往评估集里补充一批新样本,确保评估结果反映当前市场环境和当前模型版本的真实表现。
这个方向后续还可以往“多模型集成一致性”上扩展——用三个不同模型分别判断,取一致性最高的结果,或者用投票机制降低单模型偏差。我试过用两个模型做交叉验证,框架效应能再降一半左右,代价是调用成本翻倍。值不值得,取决于你的决策对精度的要求有多高。