做 Agent 的时候碰到一个问题:模型"说什么"可以通过 prompt 控制,但"怎么说"很难靠提示词稳定。语气太冲了你让它温柔,下一轮它又恢复原样。我想让模型自己从交互反馈里学会调整说话风格,而不是每次都靠人工调 prompt。

研究了一下导向向量(Activation Steering),发现现有方法都是离线提取、推理时固定的——没有"边聊边学"的能力。于是尝试做了一套在线自适应方案。先说清楚:这套东西还比较早期,没跑过严格评测,分享的是设计思路和工程方案。

背景

导向向量近两年进展很快(CAA、RepE 等),操作就一件事:从中间层激活差分里提取一个方向向量,推理时加上去,模型输出就会往那个方向偏。

但有两个问题困扰了我:

第一,向量提完就定死了。 100对语料做CAA,提取一个"温柔方向"的向量,之后一直用。如果用户在不同情境下偏好不同风格,系统没有学习能力。

第二,大家把 prompt 注入和导向向量当成了竞争方案。 多数讨论是在对比"用哪个更好",但我觉得这搞错了。

想想人在愤怒时的表现:一方面,你知道自己在生气,可以用语言描述这种状态(“我很生气”);另一方面,你的声音不自觉地变大、语速加快,这不是你有意控制的。

前者是显意识,后者是潜意识。对应到 Agent 系统里:

  • Prompt 注入(mood 文字)= 显意识通道:模型能推理自己的情绪状态,能说"我有点难过"
  • 导向向量注入 = 潜意识通道:模型的用词和语气不自觉地偏移,不需要理解为什么

这两个不冲突。 它们作用在不同层面——文字通过 attention 影响所有层,向量只注入在中间层。同时开启的时候,模型既知道自己情绪不好,说话方式也无意识地变柔了。跟人既知道自己在生气、声音又不自觉变大一样。


核心方法

用矩阵替代向量

传统方法维护一个固定的 d 维导向向量。我换成了一个 (d, n) 维的映射矩阵 W,让导向向量变成情境状态的函数:

v = v̂₀ + W · S_norm

v̂₀ 是基线(一次性提取的默认风格),W 负责把 n 维情境状态映射成行为偏移。不同情境自动产生不同方向的导向。

这里有个容易忽视的细节:提取 W 的初始列向量后,需要做 Gram-Schmidt 正交化。如果不正交化,“更开心”的方向可能跟“更自信”有投影分量——调情感愉悦度的时候自信度也跟着串了。正交化保证各维度独立可控,调一个不会意外影响另一个。

注入强度 α 的设计也有个反直觉的地方:情绪越极端,α 反而越低。人在极端压力下会变得“不像自己”——平时温柔的人可能突然变凶,平时冒失的人可能突然沉默。导向向量的“人设维护力”应该在此时减弱,而不是增强。所以公式里唤醒度的系数是负的——绝对值越大,α 越小。

学习问题就变成了:怎么更新 W?

Hebbian 外积更新

收到反馈时(不好的表达 vs 好的表达),做一次在线学习。这里的反馈对不是人类手工标注的——是模型自己生成的。模型在反思时产生“我刚才太冷淡了,应该更温暖”这样的结论,然后自己写出一对冷淡/温暖的例句。整个学习回路是自驱动的闭环,不依赖人工标注。

dv = normalize(activation(good) - activation(bad))
S_norm = normalize(current_state)
W += importance * eta * outer(dv, S_norm)

直觉上就是:“在当前情境下,记住行为应该往这个方向偏”。importance 是上游给的重要度,不重要的反馈直接跳过。

还有一个设计选择值得说:正反馈不触发 W 更新。"做得好"只说明当前 v 已经在合理方向上了,没有新的方向信息可学。如果正反馈也更新 W,等于在强化随机噪声——因为你无法从"这次没问题"中提取一个有意义的 Δv。只有负反馈(“刚才太冷淡了,应该更温暖”)才能给出明确的偏移方向。

而且不是所有负反馈都会触发更新。系统会过滤:只有"风格"相关的反馈才更新 W。如果模型犯的是事实错误(“说错了朝代”),那不是"怎么说"的问题,是"说了什么"的问题——导向向量只负责前者。通过关键词过滤(“语气”“态度”“温柔”"生硬"等)判断反思是否涉及风格,不涉及就跳过。

为什么改 W 不改 v

一开始我想过直接 v += η × Δv,后来发现不行。v 是 W·S 的结果,S 每轮交互都在变——你这一秒改了 v,下一秒 S 变了 v 就被重新算了,刚才的修改白费了。

改 W 改的是映射本身。下次遇到类似的情境 S,系统自动产生学过的偏移方向。

单次反馈很嘈杂?没关系

这里有一个一开始困扰我的问题:传统 CAA 要 100 对语料取平均才能得到可靠的方向向量。但在线学习时,每次反馈只有一对例句——方向肯定是嘈杂的。

后来想了想,觉得这个问题不像看起来那么严重。多次外积累加时,噪声方向因为每次不一样而互相抵消,而真正共同的偏好方向会被反复强化。这和传统 CAA 多对语料取平均的思路是类似的,只不过不是严格等价——因为每次的情境状态 S 不同,外积的结果会分布在 W 的不同列上,实际上比纯平均更细粒度——它区分了“在什么情境下该往哪个方向偏”。


防失控

双时标正则化

每轮交互后微衰减: W *= (1 - 1e-4)。相当于一条遗忘曲线,不被反复强化的偏好慢慢淡掉。

周期性大回拉: W = 0.95·W + 0.05·W_init。把 W 往初始矩阵方向拉回一部分,类似"睡一觉醒来,还是那个自己,但多了些新倾向"。

W 的天然约束

W 的形状是 (d, n),秩最多为 n。哪怕 d = 4096,n 只有个位数的时候,W 只能在整个激活空间中划出一个 n 维子空间来施加影响。它不可能在 4096 维空间里乱搞。这是架构层面自带的安全性。

三层工程安全

  • 模型哈希校验——换模型自动拒绝旧 W
  • Frobenius 漂移上限——超了就回滚 checkpoint
  • 注入比例兜底——||α·v|| 不能超过 hidden states 范数的 10%
  • 行为空间回读——把 W^T · v 投影回 n 维,得到一个“行为空间的状态读数”。跟真实状态对比,如果偏差大,说明 W 的学习已经导致行为偏离了状态的预期方向

降级方案的一个反直觉设计

导向向量只能在本地模型用(需要摸中间层)。切到云端 API 时怎么办?

一开始想的方案是:“把 v 从激活空间投影回语义空间,转成文字描述注入 prompt”——听起来很优雅对吧?

后来发现这是走了一圈回到原点。 v = W·S,把 v 投影回去约等于恢复 S……而 S 本身已经在 mood 文字里了。等于你把导向向量转换成了一段跟已有 mood 文字重复的描述。

正确的降级方式是换一个信息源:不从 v 投影,而是从 W 的学习日志里做统计——“过去这段时间,哪些风格关键词被反复强化了?”——然后把统计结果转成自然语言补进 prompt。比如"你近期倾向于更温柔的表达"。这个信息在 mood 文字中是没有的,是 W 的学习历史独有的。

精度肯定丢了,但至少补的是增量信息,而不是重复信息。


伪代码

class AdaptiveSteering:
    """
    W:      (d, n) 映射矩阵,可在线学习
    W_init: (d, n) 初始矩阵,正则化锚点
    v0:     (d,)   基线向量
    """

    def compute_steering(self, context_state):
        S_norm = S / max(norm(S), 1.0)
        v = normalize(v0) + W @ S_norm
        alpha = clamp(alpha_base + f(S), alpha_min, alpha_max)
        return v, alpha

    def learn(self, model, layer, bad_text, good_text,
              context_state, importance, eta=0.01):
        if importance < threshold: return
        dv = normalize(activation(good_text) - activation(bad_text))
        S_norm = normalize(context_state)
        W += importance * eta * outer(dv, S_norm)

    def micro_regularize(self):
        W *= (1 - 1e-4)

    def macro_regularize(self):
        W = 0.95 * W + 0.05 * W_init

注入就是标准的前向钩子:

def hook_fn(module, input, output):
    h = output[0]
    max_perturb = 0.1 * h.norm(dim=-1, keepdim=True).mean()
    effective = steering * min(1.0, max_perturb / steering.norm())
    return h + effective

哪些确定,哪些还不确定

环节 状态
往 activation 加向量→改变输出分布 ✅ 数学确定
CAA 对比提取能得到语义方向 ✅ 有文献(CAA 2024, RepE 2023)
W外积更新的长期稳定性 ⚠️ 有理论约束但没实测
单次反馈的方向精度 ⚠️ 单次嘈杂,靠多次叠加收敛
所有状态维度都线性可提取 ⚠️ 常见维度有文献,特殊维度没验证
整体效果 ⚠️ 没跑过定量评测

线性假设也不一定成立——MLP 替代 W 可能更有表达力,但正则化也更难做。


跟现有工作的关系

截至 2026 年初,导向向量领域的主流方向是:更好的离线提取方法(CAA 等)、动态强度调制(Adaptive ACT、AMPS 等)、安全性分析。共同点是向量方向固定,只有强度动态化

2026年的 GSS 论文甚至在 future work 里写了"在线更新导向向量"——说明这个方向被认为有价值,但目前没有公开的实现。

我的方法是把方向本身也变成可学习的,用 Hebbian 外积建立情境→行为偏移的映射,配合双时标正则化防漂移。在我查到的文献里没见过完全相同的方案,如果有了解相关工作的读者,欢迎指正。


正在开发中的系统设计,代码已实现但还没做严格评估。欢迎交流。

声明:开发过程中使用了 AI 编程辅助工具。


相关工作

  • Turner et al., Steering Llama 2 via Contrastive Activation Addition, ACL 2024
  • Zou et al., Representation Engineering: A Top-Down Approach to AI Transparency, arXiv 2023
  • Adaptive ACT: Adaptive Activation Steering for LLMs, WWW 2025
  • AMPS: Adaptive Modality Preference Steering via Functional Entropy, ICLR 2026 submitted
  • GSS: Gated Subspace Steering (memorization mitigation, 提及在线学习为 future work)
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐