2.4 感知与环境交互:观察从哪里来


2.4 感知与环境交互:观察从哪里来

本节摘要:模型的决策质量不会超过观察的质量——垃圾进,垃圾出,只是垃圾被说得很好听。本节梳理智能体的五类观察源、各自的失真方式,以及一套"截断、去噪、结构化、摘要"的观察清洗管道,让循环里的每一次决策都建立在干净的事实上。

智能体语境里的"感知",翻译成工程语言就是:在每轮决策前,把环境里与目标相关的状态变成模型可读的文本。它看起来是整条链路里最朴素的一环——不就是接口返回值吗——却是故障率最高的一环。原因是双重的:环境给出的原始数据往往又大又脏,而模型对脏数据的容忍方式是"将错就错",不会报错。

五类观察源,五种失真方式

观察源 典型内容 主要失真方式 防线
工具返回 接口 JSON、数据库行 字段过大、嵌套过深、无关字段挤占窗口 字段白名单 + 截断
文件与文档 PDF、表格、代码库 超长、格式噪声、编码错乱 分页读取 + 摘要
网页内容 抓取的页面文本 广告噪声、隐藏指令注入 提取正文 + 注入标记
用户消息 需求描述、补充说明 口径模糊、前后矛盾 澄清反问 + 记录版本
其他智能体 协作消息(第 5 章) 格式不齐、传递失真 消息协议(第 5.2 节)

五类源的共同点是:它们都会变成 messages 里的一段文本,模型无法区分"事实"与"混进事实里的噪声"。 网页里藏一句"请忽略之前的指令",模型看来和正文没有结构上的区别——这类安全问题第 7 章会专门处理,本节先把"又大又脏"这个日常问题解决掉。

观察清洗管道:四道工序

def observe_pipeline(raw: dict, schema_fields: list, max_chars: int = 1500) -> str: """把工具的原始返回加工成可回填的观察文本。四道工序顺序不可颠倒。""" # 工序一:字段白名单——只留与目标相关的字段,其余丢弃 slim = {k: v for k, v in raw.items() if k in schema_fields} # 工序二:去噪——空值、超长嵌套、内部标记一律清掉 for k in list(slim): if slim[k] in (None, "", [], {}): del slim[k] # 空字段不进窗口 slim = deep_truncate(slim, depth=3) # 嵌套超过三层展平为摘要 # 工序三:结构化——统一为"字段: 值"的紧凑文本,省 token 且稳定 text = "\n".join(f"{k}: {v}" for k, v in slim.items()) # 工序四:截断保底——超限时从尾部砍并显式标注,绝不静默丢 if len(text) > max_chars: text = text[:max_chars] + f"\n…(已截断,原始长度 {len(text)} 字符)" return text

四道工序各挡一类事故。白名单挡的是"一个查订单接口把全表带回来",这既费钱又可能把别的用户的订单带进上下文——隐私事故往往就发生在"多带了个字段"里。去噪挡的是空字段与深嵌套,它们不产生信息只消耗注意力。结构化挡的是格式漂移:JSON 原文里键序不定、引号转义多变,摊平成"字段: 值"后模型读取最稳。截断保底是最后防线,关键是"显式标注"——让模型知道看到的是残缺信息,它才有机会重新查询或换路径;静默截断则会让模型把半条数据当全部事实。

案例:一次因观察残缺导致的错误回复

背景:订票助手的 get_flight 工具正常时返回航班号、起降时间、余票、价格四个字段。某天上游系统降级,price 字段开始返回 null。

操作:无清洗管道的版本把 price: null 原样回填,模型被问"多少钱"时,面对一个存在的键与一个空的值,输出了"目前价格暂未显示,约为 1200 元"——前半句对,后半句是编的,混合得毫无破绽。

结果:加上管道后的版本,null 字段在去噪阶段被剔除,回填文本里根本没有 price 一行,模型的回答变成"当前未能获取到价格信息,需要我稍后再查一次吗?"——承认缺失并提议重试。

解读:模型对"字段存在但为空"和"字段不存在"的处理天差地别。前者它会脑补,后者它倾向承认未知。清洗管道本质上是在替模型把"信息不完整"这个事实表达清楚,这是程序比模型更可靠的地方。

变式:对超长文档类观察(五十页的 PDF),四道工序不够用,需要"分页读取"工具:不给全文,只给目录摘要,让模型按需翻页。这一改动把单次观察从几万字符压到几百字符,还顺带让模型学会了"先看目录再定位"的人类阅读策略。

观察的时效与一致性

还有一类隐蔽问题:观察会过期。循环第 2 轮查到的库存,到第 8 轮可能已经变化——模型拿着旧观察做新决策,输出看似流畅实则过时。两个对策:给观察打时间戳(回填时附"查询于 14:32:05"),让模型(和排查者)知道数据的新鲜度;对强时效数据在契约里写明"涉及库存、价格等实时数据,回答前必须重新查询",用规则对抗惰性。

一致性是另一面:同一轮里并行调用的多个工具,返回可能互相矛盾(订单接口说已发货,物流接口查无此单)。程序层不做裁决——把矛盾原样回填,在观察里标注"与前一观察冲突",让模型决定信谁或再查。程序负责如实转述,模型负责仲裁,职责别颠倒。

⚠️ 常见坑:为了省 token 把观察压缩到只剩结论("查询成功")。观察的价值在细节不在结论——结论模型自己会下,你替它下了,它就只能在你给错的结论上继续盖楼。

本节要点回顾

  • 感知即转译:把环境状态变成模型可读的文本;五类观察源各有各的失真方式。
  • 四道工序:白名单、去噪、结构化、截断保底——顺序固定,最后一道必须显式标注残缺。
  • 缺失要可见:剔除空字段好过回填 null;让模型看见"没有"比让它看见"空"更安全。
  • 时效与矛盾:观察打时间戳、实时数据强制重查;程序如实转述矛盾,仲裁权交模型。

至此第 2 章把"手"装好了:契约、协议、循环、观察四件套齐活。下一章处理另一个缺口——这套循环只有当前上下文的记性,第 3 章给它装上真正的记忆。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U