把 LLM 接到生产链路里,先解决上下文污染

# 把 LLM 接到生产链路里,先解决上下文污染

 

这半年和团队聊 AI 落地,最容易高估的不是模型能力,而是“把一个能聊天的模型接进系统之后,它会不会稳定地产生你要的结果”。很多 PoC 卡住,并不是 prompt 写得不够花,而是上下文管理太粗糙,最后把模型变成了一个对历史噪音过度敏感的黑盒。

 

我最近比较认同一个判断:做 LLM 应用,第一层工程问题不是检索,也不是 agent,而是上下文污染控制。这个事如果没处理好,后面你叠再多流程,成本和波动都会一起上来。

 

先说一个常见场景。很多团队做客服、代码助手或者内部知识问答,最初会把“用户问题 + 最近若干轮对话 + 检索到的文档片段 + 系统提示词”一股脑塞进模型。Demo 阶段看起来没问题,线上一跑就开始出现下面几类毛病:

 

1. 历史对话里一句无关的话,把当前任务带偏。

2. 检索召回了 10 段文档,真正有用的只有 2 段,模型却把低相关内容也当依据。

3. 工具调用的中间结果被原样塞回上下文,token 花了不少,增益不明显。

4. 同一类问题在不同会话里表现不一致,因为前文状态不一样。

 

这些问题背后,都是“上下文预算”没有被当成硬资源管理。LLM 的上下文窗口再大,也不代表你应该把什么都喂进去。现实里更有效的做法,是把上下文拆成几层,并且让每一层都带着明确职责。

 

我现在更倾向于这个结构:

 

```python

from dataclasses import dataclass

 

@dataclass

class PromptContext:

    system_rules: str

    task_input: str

    short_memory: list[str]

    retrieved_facts: list[str]

    tool_state: list[str]

 

 

def build_prompt(ctx: PromptContext) -> str:

    parts = [

        "[SYSTEM]",

        ctx.system_rules,

        "[TASK]",

        ctx.task_input,

    ]

 

    if ctx.short_memory:

        parts += ["[RECENT DIALOGUE]", "\n".join(ctx.short_memory[-4:])]

 

    if ctx.retrieved_facts:

        parts += ["[FACTS]", "\n\n".join(ctx.retrieved_facts[:4])]

 

    if ctx.tool_state:

        parts += ["[TOOL STATE]", "\n".join(ctx.tool_state[:3])]

 

    return "\n\n".join(parts)

```

 

这段代码没什么炫技的地方,但核心思想很重要:不同来源的信息不要混在一起,进入 prompt 之前先分层、限量、裁剪。尤其是 `retrieved_facts` 和 `tool_state`,很多系统最大的问题就是不做筛选,默认“多给一点总没错”。实际上模型在长上下文里并不会自动帮你做最优注意力分配,给多了反而会稀释主任务。

 

另一个容易被忽略的点,是把“摘要”当成长期记忆。摘要当然有用,但摘要不是越长越好,也不是每轮都该更新。更稳的做法是把记忆分成两类:一类是会影响后续行为的稳定事实,比如用户偏好、任务约束、代码库约定;另一类只是本轮推理过程中的临时状态,这些状态应该随任务结束被丢掉。

 

如果不区分这两类,系统会出现一种很典型的退化:它表面上“记住了很多”,实际上是把大量短期噪音变成长期负担。后面每次推理都要背着这些历史包袱走,延迟、费用和错误率一起涨。

 

在线上链路里,我建议至少做三件很朴素的事。

 

第一,给每种上下文来源设硬上限,而不是交给模型自己消化。比如最近对话最多保留 4 轮,检索片段最多 4 段,工具状态只保留最终结果,不要塞调试日志。

 

第二,做检索后重排。哪怕先用一个简单的 cross-encoder,或者用规则先按时间、权限、命中字段过滤一遍,效果都比“向量检索完直接全塞进去”稳定。

 

第三,对工具结果做结构化摘要。命令执行、SQL 查询、网页抓取,这些原始输出通常很长,但真正有用的信息往往只是几行。与其让模型在 200 行日志里找重点,不如前面先做一次压缩。

 

例如把 shell 输出整理成固定格式:

 

```bash

status: success

changed_files: 3

key_result: service restarted on port 8080

warnings:

  - config key x is deprecated

```

 

模型读这种结构化结果,比读一整段混杂日志更稳,后续也更容易做审计和回放。

 

还有一个工程上很现实的取舍:不要太早迷信多 agent。很多问题单 agent 加上明确的上下文边界就够了。多 agent 真正适合的是职责天然可拆的流程,比如“检索 -> 计划 -> 执行 -> 复核”。如果只是因为主 agent 表现不稳定就盲目拆成三个 agent,最后常见结果不是质量提升,而是上下文在多个节点之间来回复制,问题变得更难定位。

 

我现在看一个 LLM 系统是否靠谱,往往不先看模型名字,而是先问三个问题:

 

1. 哪些信息能进上下文,谁来决定?

2. 过期信息何时清理?

3. 工具输出是原样透传,还是已经被压缩成可消费的状态?

 

这三个问题答不清楚,系统大概率只是“看起来很聪明”。答清楚了,即便模型不是最新一代,整体体验通常也不会差。

 

LLM 工程到最后,越来越像传统系统设计:你要管理边界、状态、成本和不确定性。模型确实重要,但真正把产品做稳的,往往是那些不那么性感的上下文治理工作。这个活干得细,AI 才不容易从助手变成噪音源。

 

posted @ 2026-07-22 09:04  fitch_liu  阅读(3)  评论(0)    收藏  举报