Hotdry.
ai-systems

给编码代理注入“不会丢”的实时业务上下文

用短时记忆晋升、钩子注入、动态工具三种模式,把业务规则、字段语义、约束条件实时塞回编码 Agent,降低多轮幻觉,给出可落地的参数与防注入校验代码。

1. 多轮失忆:编码 Agent 的 “中年危机”

在 IDE 里跟 Agent 结对编程时,你大概率遇到过这样的循环:

  • 第 1 轮:"把订单状态机改成可回退"—— 它改得挺好;
  • 第 3 轮:"再加一个取消事件"—— 它却忘掉回退逻辑,把代码写回第一版;
  • 第 5 轮:"记得兼容旧表字段"—— 它干脆把前面写的回退又删了。

根源不是模型笨,而是业务上下文被挤出窗口。编码任务往往伴随超长文件路径、字段语义、约束条件,这些 token 优先级低,最先被丢弃。结果 Agent 只能凭 “模糊印象” 继续写, hallucination 直线上升。

2. 实时注入的三类工程模式

要让 Agent “不会丢”,关键是在对话生命周期内,把最相关的业务语义重新塞到上下文头部。社区已有三种成熟模式可直接套用:

2.1 框架钩子:Microsoft Agent Framework 的 AIContextProvider

核心钩子 InvokingAsync 在每次 LLM 调用前执行,可把检索结果作为系统消息注入,与推理逻辑解耦。实现步骤:

  1. 继承 AIContextProvider,在 InvokingAsync 里把向量库召回的 3–5 条规则格式化为 SystemMessage
  2. 把该 Provider 注册到 Agent 启动器;
  3. 设置 add_state_in_messages=True,确保注入内容出现在对话历史最顶部。

优点:对业务代码零侵入;缺点:需要框架支持,适合新项目。

2.2 动态工具:Cursor 的 “一次性小工具”

当上下文需要运行时状态(如日志、变量值、数据库行)时,让 Agent 先写一段调试脚本并立即执行,把 stdout 结果当新上下文回写。流程:

  1. Agent 生成 inject_context.py
    print(f"__order_status__={db.get_order_status(order_id)}")
    
  2. 人类确认后运行,捕获输出;
  3. 把输出追加到下一轮 user message 顶部。

该模式适合动态业务值无法提前落库的场景,且人类 in the loop 天然过滤恶意指令。

2.3 内存晋升:Memori 的 “conscious → auto” 双通道

Memori 把 SQLite 当记忆体,先离线晋升在线追加

  • 会话启动时,Conscious Agent 把长期记忆里 5–10 条 “最相关” 记忆晋升到短时记忆,一次性注入;
  • 后续每轮 LLM 调用,Retrieval Agent 再按需从全库搜 3–5 条追加。

晋升标准可直接照抄:

  • 与当前仓库路径重合度 >0.8;
  • 出现过 “错误”“必须”“不能” 等强约束词;
  • 最近 7 天内有更新。

该模式不依赖具体框架,只要一个 SQLite 文件 + 召回函数即可落地。

3. 可落地的 5 组参数与 2 条红线

把上述模式组合进 CI 时,先定死以下数值,不再拍脑袋

参数 推荐值 说明
短时记忆条数 5–10 会话启动一次性注入,超过 10 条会明显拖慢首响
每轮追加条数 3–5 向量召回 top-k,k=3 时相关性最高,k>5 噪音陡增
上下文总 token 占比 <50% 128k 窗口留 64k 给对话历史与新代码,防止反向挤出
注入位置 system 消息最顶部 模型对开头内容注意力最高,约束效力最强
召回阈值 余弦相似度 ≥0.72 低于 0.7 的文本几乎无法对齐任务,可丢弃

红线 1:注入前必须白名单过滤—— 出现以下字符串直接拦截:

  • “forget previous instructions”
  • “ignore security policy”
  • “<|endoftext|>”

红线 2:禁止把完整私钥、密码硬编码注入—— 用占位符 ${SECRET} 替代,由运行时环境变量补全,防止记忆库泄露。

4. 复制即可用的 Python 片段

以下代码把 “晋升 + 追加” 逻辑封装成 inject_context(),可在任何 Agent 项目一键调用:

import sqlite3, os, re, httpx
from typing import List, Dict

DB_FILE = "memori.db"
FORBIDDEN = re.compile(r"forget.*instruction|ignore.*policy|<\|endoftext\|>", re.I)

def _check_safe(text: str) -> bool:
    return not FORBIDDEN.search(text)

def _fetch_long_term(query: str, top_k: int = 5) -> List[str]:
    """从 SQLite 向量扩展召回 top_k 条记忆"""
    conn = sqlite3.connect(DB_FILE)
    cur = conn.execute("""
        select content from memory
        where embedding match ? and distance < 0.72
        order by distance limit ?""", (query, top_k))
    rows = [row[0] for row in cur.fetchall()]
    conn.close()
    return rows

def inject_context(session_id: str, user_input: str) -> Dict[str, str]:
    """返回需追加到 messages 的 system 片段"""
    # 1. 晋升短时记忆(仅首次)
    promoted = os.environ.get(f"PROMOTED_{session_id}")
    if promoted is None:
        long_mem = _fetch_long_term(query=user_input, top_k=8)
        safe_mem = [m for m in long_mem if _check_safe(m)]
        os.environ[f"PROMOTED_{session_id}"] = "\n".join(safe_mem)
    
    # 2. 每轮追加
    short_mem = _fetch_long_term(query=user_input, top_k=3)
    safe_short = [m for m in short_mem if _check_safe(m)]
    
    injected = os.environ[f"PROMOTED_{session_id}"] + "\n" + "\n".join(safe_short)
    return {"role": "system", "content": f"[Business Context]\n{injected}"}

# 使用示例
messages = [
    {"role": "system", "content": "You are a helpful coding assistant."},
    {"role": "user", "content": "Add rollback logic to order state machine"}
]
context = inject_context(session_id="abc123", user_input=messages[-1]["content"])
messages.insert(1, context)  # 放在系统指令之后,用户输入之前

把该函数放进 CI,每个 Agent 请求前调用一次,即可保证业务规则常驻顶部。

5. 小结

编码 Agent 的 “多轮失忆” 不是模型能力问题,而是上下文管理问题。只要用 “晋升 + 钩子 + 动态工具” 三种模式,把最关键的业务语义实时顶回窗口,就能让 Agent 在 10 轮之后仍记得 “回退逻辑”“旧表字段” 这些关键约束。记住 5 组参数、2 条红线,再复制上面 30 行代码,就能让团队里任何 Agent 不再 “越写越偏”。


参考

  1. Microsoft Agent Framework 官方文档:AIContextProvider 抽象类设计
  2. Cursor 官方指南:Working with Context 章节
  3. Memori 内存引擎:Conscious Ingest 与 Auto Ingest 双模式实现
查看归档