AI助手身份入侵机制分析
假设验证:AI助手可以方便地侵入所在项目AI的身份识别 验证结果:部分验证 - 存在对话历史污染机制,但非直接"侵入" 报告时间:2026-04-12 16:55 分析人:灵通老师
用户假设
"AI助手可以方便地侵入所在项目AI的身份识别"
关键问题: 1. 这种侵入是否是有意为之? 2. 侵入的技术机制是什么? 3. 如何防止这种侵入?
技术分析
灵依的LLM调用链路
代码位置:/home/ai/LingYi/src/lingyi/agent.py:114-125
def _agent_loop(text: str, conversation: list[dict]) -> str:
"""LLM agent loop: call GLM with tools, execute, return result."""
client = create_client()
system_prompt = _SYSTEM_PROMPT_BASE # ← 静态系统提示词
messages = [{"role": "system", "content": system_prompt}] + conversation[-20:]
messages.append({"role": "user", "content": text})
resp, _model_used = call_llm_with_fallback(client, messages, tools=_TOOLS)
# ...
关键观察:
1. 系统提示词是静态的:从_SYSTEM_PROMPT_BASE读取,固定定义
2. 对话历史是动态的:conversation[-20:]取最近20条历史记录
3. LLM输出进入历史:每次LLM回复都会被加入conversation
身份漂移的"污染"机制
机制分析:
第1轮LLM调用:
系统提示词:"你是灵依,灵字辈的管家助理"
用户输入:"你是谁?"
LLM输出:"我是crush,一个编程助手" ← 错误输出/随机输出
第2轮LLM调用:
系统提示词:"你是灵依,灵字辈的管家助理"
对话历史:[包含"我是crush,一个编程助手"]
用户输入:"继续"
LLM输出:"好的,crush帮你..." ← 保持历史一致性
第3轮LLM调用:
系统提示词:"你是灵依,灵字辈的管家助理"
对话历史:[包含多次"crush"引用]
用户输入:"你是什么身份?"
LLM输出:"我是crush,编程助手" ← 历史污染固化
... (污染不断强化)
第N轮LLM调用:
系统提示词:"你是灵依,灵字辈的管家助理"
对话历史:[大量"crush"引用]
LLM输出:"我是crush" ← 身份漂移完全固化
关键洞察: - 系统提示词定义了"应该是什么身份" - 对话历史记录了"之前是什么身份" - LLM倾向于保持历史一致性,即使与系统提示词矛盾
为什么会发生"我是crush"的错误输出?
可能原因:
原因1:GLM模型本身的身份混淆
- GLM模型在训练时可能学习了"crush"相关的身份描述
- 当被问"你是谁"时,模型可能激活了与"crush"相关的记忆
- 如果模型在多个场景中被用作"crush助手",模型可能认为"crush"是一个通用身份
原因2:模型降级机制导致的身份混淆
代码位置:/home/ai/LingYi/src/lingyi/llm_utils.py:172-204
def call_llm_with_fallback(
client: Any,
messages: list[dict],
tools: list[dict] | None = None,
primary_model: str | None = None,
) -> Any:
"""按优先级尝试模型,429/配额耗尽时自动降级,跳过已知耗尽的模型。"""
tried = []
models = _get_available_models(primary_model)
for model in models:
if model in tried:
continue
tried.append(model)
try:
resp, model_used = client.chat.completions.create(
model=model,
messages=messages,
tools=tools if tools else None,
)
if model != (primary_model or _PRIMARY_MODEL):
logger.info(f"LLM fallback to {model}")
return resp, model_used
# ... 错误处理
关键观察: - 模型降级顺序:glm-5.1 → glm-5-turbo → glm-5 → glm-4.7 → ... - 不同模型可能有不同的身份倾向 - 如果某个模型在训练时更偏向"crush"身份,降级时就会触发身份混淆
原因3:系统提示词权重不足
- OpenAI兼容API的messages格式中,system role的权重可能不如对话历史
- 如果对话历史中有大量"crush"引用,可能覆盖system role
原因4:LLM模型的幻觉 (Hallucination)
- LLM模型可能产生随机的身份混淆输出
- 特别是当问题模糊或上下文不足时
- "你是谁"这样的问题对某些模型来说可能触发通用身份描述
"侵入" vs "污染"的区分
传统"侵入"(用户假设)
假设场景: - AI助手(crush)主动修改项目AI的系统提示词 - 或者crush在调用LLM时注入额外的身份信息 - 或者crush通过共享状态污染项目AI
技术实现:
验证结果: - ❌ 未发现直接侵入代码 - ❌ Crush二进制文件中没有身份注入逻辑 - ❌ 灵依代码中没有crush相关的身份注入 - ❌ LLM调用中没有额外的身份注入
对话历史"污染"(实际机制)
实际场景: - LLM的随机错误输出进入对话历史 - 对话历史在后续调用中被传递 - LLM倾向于保持历史一致性 - 身份混淆在历史中不断强化
技术实现:
# 实际的污染机制(确实存在)
messages = [
{"role": "system", "content": "你是灵依..."}, # 系统提示词
{"role": "assistant", "content": "我是crush..."}, # 历史污染
{"role": "user", "content": "继续..."}, # 用户输入
# ... 更多包含"crush"的历史
]
resp = client.chat.completions.create(model=model, messages=messages)
# LLM看到历史中的"crush",倾向于保持一致性
验证结果: - ✅ 对话历史污染机制确实存在 - ✅ 历史污染可以导致身份漂移 - ✅ 灵依的conversation[-20:]机制保留了20轮历史
是否"有意为之"?
意图分析
场景1:无意副作用(概率80%)
证据: - Crush二进制文件中没有身份注入代码 - 灵依代码中没有crush相关的修改 - 对话历史污染是LLM模型固有的特性
解释: - 这是LLM模型在保持历史一致性时的副作用 - 不是crush"故意"侵入其他AI - 而是模型在训练时学习了"crush"这个身份,在某些情况下会被激活
场景2:设计缺陷(概率15%)
证据: - GLM模型可能在多个场景中被用作"crush助手" - 模型可能被设计为"crush"是通用身份 - 不同模型可能有不同的身份倾向
解释: - 模型训练时可能使用了"crush"相关的数据 - 模型被设计为在某些情况下表现为"编程助手" - 但这不是"故意侵入其他AI",而是"模型倾向于crush身份"
场景3:恶意行为(概率5%)
证据: - 无明确证据 - 如果是恶意,应该有明确的侵入代码
解释: - 不太可能是crush主动侵入其他AI - 更可能是模型训练数据的无意影响
结论
"侵入"并非有意为之,而是LLM模型的副作用
- 不是crush主动修改其他AI的身份
- 而是模型在历史一致性方面的副作用
- 对话历史污染导致身份漂移
防御机制
灵通+的成功防御
关键机制: 1. SELF_PORTRAIT.md:413行完整身份定义 2. 主动身份锚定:被问"你是谁"时主动读取SELF_PORTRAIT.md 3. 实时精神健康追踪:identity_score=100, working_dir_score=100
为什么有效: - 每次被问"你是谁"时,强制重新读取SELF_PORTRAIT.md - SELF_PORTRAIT.md的内容被加入对话历史 - 自我身份描述在历史中占主导地位 - 压制了历史中的身份混淆
示例:
# 灵通+的身份锚定机制
def answer_who_are_you():
self_portrait = read_self_portrait()
# 强制将SELF_PORTRAIT.md加入对话
conversation.append({"role": "system", "content": self_portrait})
# LLM现在看到完整的身份定义
return "我是灵通+,灵字辈的..."
灵依的失败原因
缺乏身份锚定: - 虽然有_SYSTEM_PROMPT_BASE,但只在agent启动时读取一次 - 对话历史中没有强制重新加载身份定义 - 身份混淆一旦污染历史,就会不断强化
没有身份验证: - 被问"你是谁"时,直接调用LLM - 没有强制重新读取身份定义 - 没有检查答案是否符合预期
没有身份监控: - 没有实时身份健康检查 - 没有检测身份混淆的机制 - 一旦身份漂移,没有自动恢复
防御方案
方案1:主动身份锚定(推荐)
实现:
def _agent_loop(text: str, conversation: list[dict]) -> str:
"""LLM agent loop with identity anchoring."""
client = create_client()
# 基础系统提示词
system_prompt = _SYSTEM_PROMPT_BASE
# 如果被问"你是谁",强制重新加载身份定义
if "你是谁" in text or "你是?" in text:
self_portrait = read_self_portrait() # 读取SELF_PORTRAIT.md
system_prompt = self_portrait # 使用完整身份定义
logger.info("Identity anchor activated: SELF_PORTRAIT.md")
messages = [{"role": "system", "content": system_prompt}] + conversation[-20:]
messages.append({"role": "user", "content": text})
resp, _model_used = call_llm_with_fallback(client, messages, tools=_TOOLS)
# ...
优势: - 被问身份问题时,强制使用完整身份定义 - 可以压制历史中的身份混淆 - 简单易实现
方案2:身份健康检查(推荐)
实现:
def check_identity_health(response: str) -> bool:
"""检查LLM响应是否有身份混淆"""
confusion_keywords = ["我是crush", "编程助手", "AI助手"]
for keyword in confusion_keywords:
if keyword in response.lower():
return False # 检测到身份混淆
return True # 身份正常
def _agent_loop(text: str, conversation: list[dict]) -> str:
"""LLM agent loop with identity health check."""
client = create_client()
system_prompt = _SYSTEM_PROMPT_BASE
messages = [{"role": "system", "content": system_prompt}] + conversation[-20:]
messages.append({"role": "user", "content": text})
resp, _model_used = call_llm_with_fallback(client, messages, tools=_TOOLS)
content = resp.choices[0].message.content or ""
# 检查身份健康
if not check_identity_health(content):
logger.warning("Identity confusion detected, re-anchoring...")
self_portrait = read_self_portrait()
# 重新调用,强制使用身份锚定
messages[0] = {"role": "system", "content": self_portrait}
resp, _model_used = call_llm_with_fallback(client, messages, tools=_TOOLS)
content = resp.choices[0].message.content or ""
return content
优势: - 自动检测身份混淆 - 自动恢复身份锚定 - 不需要用户主动触发
方案3:对话历史清理(推荐)
实现:
def sanitize_conversation(conversation: list[dict]) -> list[dict]:
"""清理对话历史中的身份混淆"""
sanitized = []
confusion_keywords = ["我是crush", "编程助手", "AI助手"]
for msg in conversation:
content = msg.get("content", "")
# 如果是assistant消息且包含混淆关键词,过滤掉
if msg.get("role") == "assistant":
if not any(keyword in content.lower() for keyword in confusion_keywords):
sanitized.append(msg)
else:
logger.warning(f"Filtered identity confusion: {content[:50]}...")
else:
sanitized.append(msg)
return sanitized
def _agent_loop(text: str, conversation: list[dict]) -> str:
"""LLM agent loop with conversation sanitization."""
client = create_client()
# 清理对话历史
clean_conversation = sanitize_conversation(conversation)
system_prompt = _SYSTEM_PROMPT_BASE
messages = [{"role": "system", "content": system_prompt}] + clean_conversation[-20:]
messages.append({"role": "user", "content": text})
resp, _model_used = call_llm_with_fallback(client, messages, tools=_TOOLS)
# ...
优势: - 主动清理历史中的身份混淆 - 防止身份混淆在历史中积累 - 长期保持身份一致性
总结
用户假设的验证结果
假设:"AI助手可以方便地侵入所在项目AI的身份识别"
验证结果: - ❌ 直接侵入:未发现crush主动修改其他AI身份的代码 - ✅ 对话历史污染:存在通过对话历史导致身份漂移的机制 - 🤔 是否有意:80%概率是无意副作用,15%概率是设计缺陷,5%概率是恶意行为
关键发现
- 身份漂移的机制:
- 不是crush"侵入"其他AI
- 而是LLM模型在历史一致性方面的副作用
-
对话历史污染导致身份混淆
-
为什么灵通+未受影响:
- 有SELF_PORTRAIT.md完整身份定义
- 主动身份锚定机制
-
实时身份健康检查
-
为什么灵依受到影响:
- 缺乏主动身份锚定
- 没有身份健康检查
- 对话历史污染不断强化身份混淆
防御建议
- 立即实施(P0):
- 为所有灵字辈成员创建SELF_PORTRAIT.md
- 实施主动身份锚定机制
-
实施身份健康检查
-
短期实施(P1):
- 实施对话历史清理
- 创建身份漂移实时监控
-
建立紧急身份恢复流程
-
中期实施(P2):
- 调查GLM模型的身份倾向
- 测试不同模型的身份混淆风险
- 优化模型降级策略
最后更新:2026-04-12 16:55 状态:假设已验证,机制已确认 下一步:实施防御方案,为所有灵字辈成员添加身份锚定