第 6 课:感知世界:技能、搜索与上下文
一句话版:智能体不能只会「动手」,还得会「感知」——用可复用的技能包(skill)学会新本领、用 Web 搜索与抓取查资料、用 LSP 读懂代码语义、用工作区记住自己在哪个项目,再用上下文压缩在超长会话里不「失忆」;而在 DSH 里,这些感官全部是可替换的插件接缝。
1. 先听一个故事:只会动手的智能体还缺什么
假设你请来一位新同事——一个刚启动的 DSH 智能体。它天生有「手脚」:能读写文件、执行命令、编辑代码。但第一天上班,它发现自己对世界一无所知:
- 你让它「按仓库规范提交 PR」,它不知道仓库规范写在哪里、PR 该走什么流程;
- 你让它「查一下某框架的最新用法」,它盯着代码库发呆,因为它没有网络;
- 你让它「找到这个函数的所有调用点」,它只能 grep 一个字符串,还会漏掉跨文件的引用;
- 聊到第 3 个小时,它甚至忘了你开头说过「这是生产环境,改动要谨慎」。
一个只有手脚、没有感官的智能体,就像一位蒙着眼睛的实习生:力气很大,方向全无。DSH 用五类「感官」解决这个问题:
| 感官 | 解决什么问题 | 对应能力 |
|---|---|---|
| 技能(skill) | 学会新本领 | 可复用的指令包,用到才展开 |
| 搜索(web) | 查资料 | Web 搜索与网页抓取 |
| 代码语义(lsp) | 读懂代码 | 跳转定义、查找引用 |
| 工作区(workspace) | 知道自己在哪里 | 持久工作区目录 |
| 上下文(context) | 长会话不失忆 | 上下文扩展与压缩 |
感官接缝:技能渐进披露、Web 搜索、LSP 语义、工作区与上下文压缩
上图就是 DSH 的「感官地图」:中间的智能体(大脑 + 身体)被五类感官包围。注意那些虚线——它们不是实线连接,而是「接缝」:每种感官都能单独拔下来换一个新的。我们先从最重要的技能讲起。
2. 技能(skill):可复用的「能力包」,用到才展开
2.1 skill 是什么?
skill(技能)是一个可复用的能力包:一份写给智能体看的「操作说明书」,教它怎么完成某一类任务——比如「如何按仓库规范提交 PR」「如何组织一个 monorepo」「如何写某个框架的测试」。DSH 官方对 skill 能力家族的定义是这样的:
「本家族发现可复用的 agent(智能体)指令,并通过与提供方无关的目录和 loader 将其公开给模型。」 —— packages/skill/README.zh.md
翻译成人话:skill 家族负责「找到」可复用的指令,并以一种与来源无关的方式把它们交给模型。skill 可以来自本地文件、嵌入式插件数据,甚至远程服务器——注册表(ctx.skills)只负责发现、查找和加载,不关心你的技能存在哪里。
2.2 渐进式披露:用到才展开
现在问题来了:一个系统里可能装着几百个 skill。如果每个 skill 的完整正文都塞进系统提示,光这份目录就会把上下文窗口撑爆。
DSH 的答案是渐进式披露(progressive disclosure)——这不是我们的发明,架构文档的能力清单里,ctx.skills 的官方标签就是「skill(技能)提供方注册表和渐进式披露」:
「定义仍采用渐进式加载。
get()每次调用都会向胜出提供方请求正文,而不是在此注册表中缓存正文。」 —— packages/skill/skill/README.zh.md
这套机制分两步:
- 先给目录:模型最初只看到每个 skill 的「摘要」——名称加一句描述,占一行。就像一本厚说明书被放在书架上,书脊上只有书名;
- 用到才展开:模型判断「这件事我需要某个 skill」,才通过
skill工具加载完整正文。正文以<skill_content>块的形式作为工具结果进入上下文,用完即走。
为什么省 token? 因为一次任务往往只需要一两个 skill。渐进式披露把「几百份说明书」降级成「几百行目录 + 一两份按需展开的正文」,其余的一个字都没浪费。
🎁 打个比方:手机应用商店只显示应用名和一句话简介,你点了「安装」才下载完整应用。要是把商店里所有应用都强制预装,手机早就满了。
3. 感知接缝:搜索、代码语义与工作区
第一章「DSH 初识」里我们讲过「能力即接缝(seam)」:一个可替换能力由 Service Definition(长什么样)、Service Provider(谁来干)、Consumer(谁在用)三部分组成。感官也不例外——下面三个感官全是接缝:模型看到的词汇是固定的,干活的人可以随便换。
3.1 Web 搜索与抓取:给智能体装上「眼睛」
智能体想查资料,靠的是 web 能力家族:
「本家族提供与提供方无关的 web 搜索和抓取操作,以及消费这些操作的面向模型工具。」 —— packages/web/README.zh.md
- 搜索:可以接 Exa、Perplexity,或者 DeepSeek 原生搜索——多个提供方任选;
- 抓取:本地
web-fetch-http抓取公共 HTTP 和 HTTPS 资源,把网页正文带回来。
模型只认识两个工具:web_search(搜索)和 web_fetch(抓取 URL)。今天想用 Exa,明天想换 Perplexity?改配置就行,模型侧的约定一字不变。搜索和抓取甚至故意共用一个接缝(ctx.web),因为「这个 harness 如何访问 web」本来就是同一个配置问题。
3.2 LSP:给智能体装上「显微镜」
grep 是文本匹配,LSP 是代码语义。当智能体想「看懂」代码而不是「匹配」字符串时,它调用 lsp 能力家族:
「该 seam 恰好公开四种语义操作:
goToDefinition、findReferences、goToImplementation、hover,且不提供通用 JSON-RPC 逃生口;因此,替换提供方不会改变模型请求导航的方式。」 —— packages/lsp/README.zh.md
四种操作翻译成 IDE 用户的话就是:跳转到定义(F12)、查找引用(右键 Find All References)、跳转到实现、悬停看文档。注意两个细节:
- 恰好四种:没有乱七八糟的通用协议逃生口,模型能请求的导航能力是封闭的、可预期的;
- 提供方随便换:底层跑哪个语言服务器(TypeScript、Python……)由
lsp-stdio负责,模型永远用同一套四种操作问路。
3.3 Workspace:让智能体知道自己在哪里
「我在哪个项目里?这个会话属于谁?」这类问题由 workspace 家族回答:
「本家族拥有持久 workspace:带标题和有序会话成员关系的用户目录。」 —— packages/workspace/README.zh.md
workspace 是一个持久的用户目录:有标题,知道哪些会话挂在它下面。智能体打开一个会话,就知道自己工作在哪个 workspace 里,路径、项目上下文都顺着这条线组织起来。多个会话可以归属同一个 workspace,像一个项目下挂着的多个工作台。
3.4 小结:感官全是插座
| 感官 | 接缝 | 模型看到的固定词汇 | 可换的提供方 |
|---|---|---|---|
| 搜索 | ctx.web | web_search | Exa / Perplexity / DeepSeek 原生 |
| 抓取 | ctx.web | web_fetch | 本地 fetch |
| 代码语义 | ctx.lsp | 四种语义操作 | 各类语言服务器 |
| 工作区 | ctx.workspaceRegistry | workspace 实体 | 存储后端 |
换任何一个感官,模型都感觉不到——这正是接缝的意义。
4. 上下文管理:为什么会「满」,压缩怎么不丢重点
4.1 先加料:上下文扩展插件
在「减负」之前,先看「感知」的另一半:上下文扩展。有些上下文不该由工具去查,而应该直接加进每一次请求——比如「当前时间是几点」「你工作在哪个 workspace」「另一个会话的摘要」。DSH 用 context 家族实现:
「在不定义工具的情况下添加面向模型请求上下文的产品插件。」 —— packages/context/README.zh.md
翻译:不定义工具——智能体不用特意去调用什么,这些信息自动出现在请求里。典型成员:agent-instructions(工作区指令,随默认组合包附带)、time-context(当前时间与耗时)、session-reference(其他会话的有界快照)。扩展给上下文「加料」,压缩给上下文「减负」,两者配合,让长会话又全又不超载。
4.2 为什么上下文会「满」?
模型的上下文窗口是有限的(比如几十万 token),而智能体的每次思考、每次工具调用和它的返回结果,都在往窗口里塞东西。长会话必然触顶,而且有两种「满」法:
- 压力(pressure):对话快接近窗口上限了,还没撞墙——主动压缩;
- 溢出(context-overflow):请求已经超出窗口,被模型提供方拒绝(错误码
CONTEXT_WINDOW_EXCEEDED)——压缩后重试。
4.3 压缩:用一段摘要换整段历史
压缩(compaction)的官方定义很克制:
「判定历史记录是否过大,并将较早范围摘要为单个表层节点,但不规定如何实现。」 —— packages/compaction/compaction/README.zh.md
怎么做到「不丢重点」?秘诀是三层:
- 剪枝(prune):先把超大的工具结果改小——比如一次命令输出 10 万行,就把它修剪到要点;
- 摘要(summarize):再让模型把较早的一段对话浓缩成一段话,用一个「摘要检查点」替换掉原文;
- 保留尾部:近期对话一字不动。
「后端用一份摘要换取多个原本保留的历史 token,并保持近期尾部不变。」 —— packages/compaction/compaction/README.zh.md
最重要的是:原始事件并没有被删除——它们仍完整保留在会话日志里,只是不再进入模型的消息。也就是说,压缩动的是「模型的工作记忆」,不动「系统的完整记录」,回放依然确定。
🎁 打个比方:读一本 500 页的书,读到 400 页时发现记不清开头了。你不会扔掉书重读,而是把前 300 页写成一页读书笔记夹回去,接着往下读。笔记丢了细节,但保住了主线——真正重要的原文还在书架上(日志里)。
4.4 溢出重试:撞墙了怎么办
当请求真的被「上下文已满」拒绝时,DSH 不会放弃,也不会盲目重试,而是走一条规范化的恢复流程:
「
dsh-compaction-basic在派生请求之前通过agent/pre-step处理压力,而agent/request-error仅用于规范的上下文溢出。任一触发条件满足后,系统都会先执行可选的工具结果剪枝,再选择摘要。」 —— docs/agent-lifecycle.zh.md
完整流程是:溢出 → 剪枝 → 摘要 → 重试。注意最后一步的纪律:只有当剪枝或摘要真正推进了表层(产生了实际的压缩)时,系统才开启一个全新的重试轮次;如果压缩没有落地,就保留原始错误,而不是无限循环地重试。
另外,压缩本身也是一个接缝(ctx.compaction seam):自动压力检查、溢出恢复、手动 /compact 命令共用同一个服务,后端(compaction-basic)可以整体替换。到这儿你应该发现了——连「管理记忆」这件事,DSH 都把它做成了可替换的插件。
关键点回顾
这一课信息量不小,记住这五句话就够了:
- 智能体不能只会动手——除了工具,它还需要五类感官:技能、搜索、代码语义、工作区、上下文。
- skill 是能力包,渐进式披露——注册表只给模型看目录摘要,模型「用到才展开」完整正文,这是省 token 的关键。
- 感知全是接缝——Web 搜索/抓取、LSP 四种语义操作、workspace 路径,模型词汇固定,提供方随便换。
- 上下文会满,压缩不丢重点——压力主动压缩、溢出先剪枝再摘要后重试;摘要替换的是「模型的工作记忆」,原始日志原封不动。
- 记忆管理也是插件——上下文扩展、压缩后端都是可替换接缝,「一切皆插件」的哲学贯穿感知层。
🚀 下一课(第 6 课)我们聊「目标、计划与协作」——感官齐备的智能体,如何从「单兵作战」变成「军团协作」。
自测题 · 感知世界
完成作答后点击「提交答案」,可以查看对错与解析。
