Caloogy 文章
English

用因果分析减少 Agent 的"金融幻觉"

日期
2025 年 10 月 21 日
系列
量化 Agent
阅读
4 分钟
主题
Ai agentQuantCausalityConfounding

一句话版本

大模型 Agent 非常擅长"讲故事":给它一段数据,它能流畅地解释"因为新闻情绪转好,所以股价上涨"。问题在于,金融数据里到处是共同原因造成的相关性,一个听起来合理的因果故事,可能只是两个变量同时被第三个因素驱动。 这类"金融幻觉"比编造事实更隐蔽:数据是真的,相关也是真的,只有因果解释是错的。本文给 Agent 配备三件因果工具:画出因果图、控制混杂变量、做安慰剂与负对照检验,并说明它们各自能排除什么、不能排除什么。

免责

本文仅为技术科普,不构成任何投资建议。文中代码使用模拟数据。


第一层:通俗理解

1.1 一个很有说服力的错误

Agent 分析了三年数据,得出结论:

"新闻情绪指数与次日收益显著正相关(t = 13)。情绪改善会推动投资者买入,从而推高股价。建议构建情绪因子。"

听起来无懈可击。但实际情况可能是:市场整体的风险偏好同时推高了新闻情绪(记者更乐观)和股价,情绪本身对股价毫无作用。一旦风险偏好的驱动方式变了,这个"因子"就会失效。

1.2 两种幻觉

类型 例子 检测难度
事实幻觉 编造一个不存在的财报数字 较容易:核对数据源
因果幻觉 数据和相关都是真的,但因果解释错了 难:需要因果推理

大模型从大量财经文本中学会了"因为……所以……"的叙事方式,而财经媒体本身就充满事后归因。Agent 的因果幻觉,很大程度上是继承来的。

1.3 三层防线

Agent 提出因果结论

1. 画因果图
有哪些可能的共同原因?

2. 控制混杂
控制后效应还在吗?

3. 安慰剂 / 负对照
不该有效果的地方也有效果吗?

保留 / 降级 / 拒绝


第二层:三件工具

2.1 因果图(DAG)

要求 Agent 在给出结论前,先画出相关变量之间可能的因果关系:

?待检验

市场风险偏好
(混杂变量)

新闻情绪

股票收益

一旦画出来,"情绪 → 收益"之外的其他路径就一目了然。Pearl 的因果推断理论给出了判断"需要控制哪些变量"的规则(后门准则)。

2.2 控制混杂变量

在回归中加入混杂变量(或它的代理变量),看原来的效应是否还在:

2.3 安慰剂检验与负对照

方法 做法 能排除什么
安慰剂(置换)检验 把因子的时间顺序打乱,重复多次,得到"没有任何关系时"的分布 排除"纯属偶然"
负对照 找一个不可能是原因的变量(比如在收益之后才公布的数据),看它是否也和收益"相关" 如果它也相关,说明相关来自共同原因或数据问题
时间上的负对照 检验因子和过去收益的关系;因子不可能影响过去 排除反向因果和前视偏差

关键区分:置换检验只能证明"不是偶然",不能证明"是因果"。一个由混杂造成的相关,在置换检验下照样"显著"。


第三层:代码演示

3.1 模拟:收益只由风险偏好驱动,情绪毫无作用

import numpy as np, pandas as pd, statsmodels.api as sm
rng = np.random.default_rng(21)
T = 1000
# 混杂变量:市场风险偏好(不可直接观测,但可以用代理变量)
risk_on = rng.normal(size=T)
sentiment = 0.8 * risk_on + rng.normal(scale=0.6, size=T)     # Agent 发现的"新闻情绪因子"
ret = 0.5 * risk_on + rng.normal(scale=1.0, size=T)            # 收益真正只由风险偏好驱动
df = pd.DataFrame({"sentiment": sentiment, "ret": ret,
                   "risk_proxy": risk_on + rng.normal(scale=0.3, size=T)})

naive = sm.OLS(df["ret"], sm.add_constant(df[["sentiment"]])).fit()
print("朴素回归:情绪系数", round(naive.params["sentiment"], 3), " t =", round(naive.tvalues["sentiment"], 1))
ctrl = sm.OLS(df["ret"], sm.add_constant(df[["sentiment", "risk_proxy"]])).fit()
print("控制混杂后:情绪系数", round(ctrl.params["sentiment"], 3), " t =", round(ctrl.tvalues["sentiment"], 1))

null = []
for _ in range(500):
    s = rng.permutation(df["sentiment"].values)
    null.append(np.corrcoef(s, df["ret"])[0, 1])
obs = df["sentiment"].corr(df["ret"])
print("观测相关", round(obs, 3), " 安慰剂 95% 分位", round(np.quantile(np.abs(null), 0.95), 3))

future_only = 0.8 * risk_on + rng.normal(scale=0.6, size=T)   # 和情绪同源但时间上不可能是原因
print("负对照变量与收益的相关", round(np.corrcoef(future_only, ret)[0, 1], 3))

输出:

朴素回归:情绪系数 0.45  t = 13.0
控制混杂后:情绪系数 0.099  t = 1.8
观测相关 0.381  安慰剂 95% 分位 0.06
负对照变量与收益的相关 0.384

3.2 解读

检验 结果 结论
朴素回归 t = 13,非常"显著" Agent 的原始结论就是这么来的
控制混杂 系数从 0.45 降到 0.10,t = 1.8,不再显著 绝大部分效应来自风险偏好
置换检验 观测相关 0.38 远超安慰剂分布 只说明"不是偶然",不能证明因果
负对照 一个不可能是原因的变量,相关同样是 0.38 强烈提示相关来自共同原因

控制后系数没有完全归零(0.10),是因为代理变量 risk_proxy 带有测量误差,没能完全吸收混杂。这也是现实中的常态:控制混杂只能做到"尽量"。

3.3 把它变成 Agent 的工作规范

给研究 Agent 的系统提示词和工具中加入以下要求:

  1. 结论必须附因果图:列出至少三个可能的混杂变量;
  2. 必须运行的检验:控制主要混杂(市场、行业、市值、风险偏好代理)、置换检验、至少一个负对照;
  3. 结论分级:
等级 条件 措辞
相关 只通过置换检验 "X 与 Y 相关,原因不明"
稳健相关 控制主要混杂后仍显著,负对照不显著 "控制已知因素后,X 仍与 Y 相关"
因果证据 有自然实验、外生冲击或可干预的证据 "有证据表明 X 影响 Y"
  1. 禁止的措辞:在只有相关证据时使用"导致""推动""因为";
  2. 反方 Agent:由另一个 Agent 专门提出替代解释。

3.4 局限


总结

层次 要点
问题 金融幻觉:数据真、相关真、因果解释错
工具 因果图、控制混杂、置换检验、负对照
关键区分 置换检验排除偶然,不排除混杂;负对照可以暴露共同原因
规范 结论附因果图、强制检验、结论分级、禁止因果措辞、反方 Agent