全书目录

第四编 系统层

第 17 章 输入契约:原始输入、清洗输入与模型可见上下文

7 分钟 2,904 字 第 18 / 161 个阅读单元

Agent 从来不是直接面对“现实世界”的。

它面对的,始终是被系统加工后的世界表征。

这一点如果不先想清楚,后面关于状态、记忆、工具、规划、多 Agent 的讨论都会失焦。因为 Agent 的第一步不是思考,而是看见;而它能看见什么、以什么形式看见、按什么顺序看见,本身就是系统设计的一部分。

所以,理解 Agent 的输入,至少要分清三层对象。

第一层,是原始输入。

原始输入来自现实世界或者业务世界本身。它可能是用户的一句话,也可能是一整个网页、一份 PDF、一个数据库查询结果、一段传感器数据、一条工单流水、一次 API 返回,甚至是一连串工具日志。原始输入最大的特点,是未经任务化处理。它保留了现实世界的丰富性,也同时携带了现实世界的噪声、冗余、格式混乱、权限差异和结构缺失。

第二层,是清洗输入。

当系统开始为某个任务准备信息时,原始输入会先经过一道预处理流水线。在真实系统里,这一步往往包括权限过滤、文本抽取、格式归一化、字段补齐、去重、切块、结构化、索引、检索、重排、压缩、摘要、拼装。也就是说,系统并不是把“现实里有什么”原样交给模型,而是先把它变成“这次任务值得看什么”。

第三层,是模型可见上下文。

这才是模型真正接触到的对象。哪怕同样完成了清洗,也未必所有信息都会进入本轮上下文。系统还会继续根据 token 预算、当前阶段、任务意图、工具调用结果、会话状态和安全策略做最后一次选择。最终进入模型视野的,通常只是整个输入世界里的一小块截面。

因此,本章要建立的第一条结论是:

text
模型看到的不是现实,而是系统挑选后的可见世界。

这不是措辞上的区别,而是工程上的区别。

如果团队把“输入”理解成用户说了什么,就会天然忽略那些真正决定结果的环节,例如:谁在做权限裁剪,谁在做文档解析,谁在决定切块粒度,谁在控制排序,谁在丢弃历史,谁在压缩工具输出,谁在把结构化字段翻译成模型更易用的语言形式。可这些步骤恰恰定义了模型的可见世界。

从官方产品与工程实践看,这已经不是理论判断,而是共同现实。今天几乎所有成熟平台都明确承认:请求本身往往是无状态的,多轮能力依赖会话对象、历史整理、状态压缩和上下文管理;真正送入模型的是经过 curated history、compaction、context engineering、retrieval orchestration 之后的输入。换句话说,模型从不自动拥有上下文,上下文是系统为它制造出来的。

这就引出第二条结论:输入契约不是“给模型多少信息”,而是“系统如何决定哪些信息以什么形式出现”。

很多人最容易犯的错误,是把输入问题理解成信息量问题。他们以为,只要把更多材料塞进去,模型自然会更聪明。但长上下文研究与大量 RAG 经验都反复表明,事实恰好相反。信息一旦过多,噪声就会积累;检索一旦泛化过宽,弱相关片段就会淹没关键证据;排序一旦失误,正确内容即便被检到,也可能因为位置不对而失去作用。模型不是摄像机,不会平等利用上下文里的每一个 token。它对顺序、位置、表达方式、冗余度和结构密度都高度敏感。

所以,输入契约真正管理的不是“有没有数据”,而是“可用性”。同样是一份一百页的报告,直接塞给模型、切成若干片段后按主题重排、再补上标题和时间等元信息,这三种做法对应的是三个完全不同的世界。对于模型来说,这不是同一份材料,只是在人类眼里它们都来自同一份原始文档。

这也是为什么文档解析、网页清洗、chunking 和 contextual retrieval 会在工程实践里变得如此关键。原始网页包含导航栏、广告、页脚、脚本与模板噪声;原始 PDF 往往把正文、页眉、页码、表格、图注和版面结构混在一起;原始数据库记录则缺乏自然语言里的关系显式化。若不先做结构抽取和语义重组,模型得到的就不是知识,而是一堆形状上像文本、功能上却不可直接推理的材料。

进一步看,输入契约并不只影响效果,也直接影响安全。

只要系统允许外部内容进入上下文,系统就在把一部分“世界”翻译给模型。而这一步如果没有边界,就会出现一个根本问题:模型无法天然分辨哪些内容是可信事实,哪些内容是恶意指令,哪些内容只是被抓取进来的污染文本。于是,间接 prompt injection、context poisoning、memory poisoning 才会成为 Agent 时代的核心风险。它们本质上都不是模型“突然变笨”,而是输入契约失守,导致不该进入决策层的内容被混入了模型可见世界。

所以,成熟系统在输入层面至少要回答五个问题。

第一,来源边界是什么。哪些数据源可以进来,哪些绝不能直接进来。

第二,权限边界是什么。当前用户、当前任务、当前工具调用到底有权看到什么。

第三,结构边界是什么。原始材料需要被解析成哪些对象、字段、片段、元数据和关系。

第四,排序边界是什么。哪些信息必须优先出现,哪些只作为候选补充。

第五,安全边界是什么。哪些内容是数据,哪些内容可能伪装成指令,哪些必须隔离或降权处理。

只要这五个问题含糊,系统就会出现表面“像是模型问题”、本质“其实是输入问题”的故障。比如模型回答不稳定,可能并不是因为模型能力差,而是每次检索到的上下文略有不同;模型经常遗漏关键条件,可能不是因为不会推理,而是关键字段根本没有进入可见上下文;模型偶尔作出危险动作,也未必是因为它天生不安全,而是工具返回或外部文档把恶意内容一起带进来了。

企业场景里,这种差异尤其明显。

如果用户问:“这个客户的续约风险高吗?”系统理论上也许能访问 CRM、客服工单、合同记录、账单状态、会议纪要、邮件摘要、产品使用日志和历史承诺。但真正决定答案质量的,从来不是“能不能把这些都接上”,而是能不能把它们变成一套任务相关、权限正确、证据分层、顺序合理的可见上下文。

例如,最近一次严重投诉、最近三个月使用率骤降、账期拖欠和即将到期的关键合同条款,可能应该进入主上下文;更久远的普通沟通记录也许只该留在候选检索层;而跨客户的敏感材料则根本不应进入这一轮观察空间。这里的关键能力不是模型“知道续约风险是什么”,而是系统“知道哪些观察值得让模型看见”。

于是,本章可以把输入契约重新定义为:

text
输入契约,就是系统把外部世界转换成模型可见世界的规则集合。

它规定数据从哪里来,如何被清洗,如何被结构化,如何被切片,如何被重排,如何被压缩,如何与当前状态拼装,如何区分数据与指令,最终又如何进入本轮上下文。

一旦这样理解,很多看似分散的工程问题就会重新连起来。RAG 不是孤立模块,而是输入契约的一部分;conversation compaction 不是节省 token 的小技巧,而是维护可见世界稳定性的机制;context engineering 也不只是“把提示词写好”,而是在设计模型这一次能看见的工作现场。

因此,一个成熟 Agent 的 observation pipeline 往往比 prompt 本身更决定结果。Prompt 当然重要,但 prompt 只能组织模型已经看见的东西,不能凭空弥补那些根本没有被送进来的事实。对生产级 Agent 来说,输入契约不是外围细节,而是第一层架构。