ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

DeepSeek Harness Read Card:让 read 工具的结构化行窗口以结构化形态直达客户端

DeepSeek Harness Read Card:让 read 工具的结构化行窗口以结构化形态直达客户端 人工智能AI AgentAgent 框架DeepSeek【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址https://gitcode.com/gh_mirrors/de/deepseek-harness点击查看免费下载导读DeepSeek Harness 中read工具的规范化输出是一个结构完整、带行号的行窗口对象{ path, offset, lines, totalLines }但旧版展示层将其压平成一段行号内嵌的纯文本导致具备渲染能力的客户端无法像渲染 diff 那样渲染一次带行号栏、语法高亮的代码视图。本文依据仓库内已落地的实现笔记2026-07-30-web-read-card.md完整剖析本次Read Card设计如何通过为渲染意图联合类型新增第四个card: read标签、借助presentationMeta持久化通道把结构化行窗口投影到会话日志使实时与回放两条路径上的客户端都能拿到lines/totalLines/lang结构化数据。读完本文你将理解这套生产者投影结构化数据、消费方按能力回退的展示契约并掌握langFromPath、readMetaFromMeta的防御性收窄策略及其在 read-render.ts 中的源码级实现。背景read 工具的规范化输出与扁平化展示之间的落差在 规范化工具输出约定 落地之后DeepSeek Harness 的每个工具都必须声明一个规范化输出对象read工具返回的是{ path: string; offset: number; lines: [{ number: number; text: string }]; totalLines: number }这个结构在 read.ts 的output.schema中按 JSON Schema 逐字段声明并在执行期由buildWindow严格构造。与此同时面向模型的文本渲染由render投影器负责输出一段 OpenCode 风格的行号文本pathsrc/index.ts/path typefile/type content 10: import { foo } from ./foo 11: export function bar() { ... } (Showing lines 10-11 of 132. Use offset12 to continue.) /content问题出在规范化输出对象与面向模型文本之间的展示投影层。旧版read的展示回调是这样声明渲染意图的presentCall返回GenericCallViewkind: read一个跟随定位presentResult返回GenericResultView其唯一内容是被剥掉path/type/content信封后的文本。也就是说一个收到该视图的 UI 只看到一个压平的文本块行号以N:前缀烘焙进文本、文件的编程语言未知、totalLines丢失。有相应能力的客户端无法像渲染 diff 那样渲染一次 read——它想要的是行号栏与内容分离、支持语法高亮的代码视图。核心问题结构化数据在线上wire无法恢复为什么不能由客户端自行从文本里解析回结构化数据因为工具结果的线上形态on the wire只包含两样东西面向模型的ContentBlock[]已渲染的文本一个不透明的meta字段。规范化输出对象留在工具内部从不到达客户端也不写入会话日志。依据 规范化工具输出约定agent loop持久化tool/result事件时只记录content、error和可选的meta规范化中间值被刻意排除在会话格式之外。因此想要行数组、总数和语言提示的客户端无法从N: text文本里解析回它们——按第一个:切分存在歧义、脚注只覆盖部分分支、渲染格式一变就失效唯一的出路是工具在产生结果时把结构化窗口投影到那个会随会话日志持久化的通道上——即meta。这正是本次 Read Card 设计的出发点。决策一渲染意图联合类型新增第四个card标签read仅结果侧渲染意图 union 架构 Note 定义了一套card标签化的封闭判别联合类型工具的presentCall/presentResult各自声明一种渲染意图桥接层按card标签switch分发渲染。本次设计为这套词汇新增第四个标签// presentResult → ToolResultView新增 read 分支后 type ToolResultView GenericResultView | TerminalResultView | DiffResultView | ReadResultView interface ReadResultView { card: read title?: string path: string offset: number lines: ReadFileLine[] // ReadFileLine { number: number; text: string } totalLines: number lang?: string // 语法高亮语言提示缺失时 UI 渲染纯文本 content?: ContentBlock[] // 剥信封后的文本供无 read 能力的 UI 兜底 }几个关键设计取舍仅结果侧result-side onlyToolCallView完全不动待定状态仍是GenericCallViewkind: read跟随定位。理由很直接一次 read 调用在execute返回之前不携带任何文件内容——没有行数组、没有总数调用时没有任何可展示的结构化信息。这与 bash 终端 card 形成对比终端 card 两侧都打标签是因为终端调用在调用时已经携带命令和 cwdread 调用则两者皆无若给调用侧打标签只会平添一个空变体。调用侧展示仍由 read.ts 的presentCall负责一个以文件路径为标题、带read类型图标、offset作为跟随定位行的 generic card。ReadFileLine是共享行单元{ number, text }与规范化输出对象中的行项、面向模型文本中的N: text行一一对应保证三处数据语义一致。content兜底成功路径上presentResult在结构化字段之外总是携带剥信封后的文本。这样一个不具备 read 卡片渲染能力的 UI可以经由自己的 generic/default card 分支照常显示文件文本实现生产者一次产出、消费方按能力取用。决策二结构化窗口经由presentationMeta投影并持久化read工具通过output.presentationMeta把结构化窗口投影到meta通道——这与 write/edit 工具把应用后的 diff hunk 投影到同一通道的做法一致见 规范化工具输出约定。在 read.ts 中投影器只是对已有数据的一次轻量复制presentationMeta: (_args, value) { const lang langFromPath(value.path) return { path: value.path, offset: value.offset, lines: value.lines.map(({ number, text }) ({ number, text })), totalLines: value.totalLines, ...lang undefined ? {} : { lang }, } },执行流程presentationMeta只对一次顶层 surface 调用运行一次返回的{ path, offset, lines, totalLines, lang? }作为 JSON 被会话校验后存储在结果的meta上。随后presentResult在实时与回放两条路径上都把该meta收窄回ReadResultView——回放时原始的规范化输出对象已不在线上但持久化的meta让行数组、总数和语言提示都能被还原。为什么offset必须随 meta 持久化一个容易被忽略的细节offset窗口请求的 1-based 起始行也必须携带。原因在于字节上限与行号窗口的交互——当readMaxBytes字节上限低于首个选中行时buildWindow返回的是一个空的lines数组但totalLines为正数。此时没有持久化的offset回放出的卡片将无法报告窗口从哪一行开始续读Use offsetN to continue也无从定位应从哪行继续末行推断与文本重解析两种兜底方案都有损。换言之offset是空窗口场景下重建窗口位置的唯一可靠依据。决策三presentResult的防御性收窄与降级策略presentResult的完整逻辑在 read.ts它只在以下情况全部满足时才返回结构化卡片否则一律返回undefined即 generic 回退结果不是错误result.isError为 falsemeta 存在且结构合法——由readMetaFromMeta防御性收窄单个文本块是 read 信封——通过正则/^path[^\n]*\/path\ntypefile\/type\ncontent\n([\s\S]*)\n\/content$/u校验并提取正文。这里有一个至关重要的设计立场本 card 出现之前记录的旧日志结果——信封合法但没有持久化meta——有意走同一条undefined路径。此时客户端回退到原始result.content显示带path/type/content信封的原文而不是旧展示器返回的那种剥信封 generic card。这是项目 pre-release 立场foundation over blast radius下明确接受的降级拒绝旧的磁盘格式而不是为兼容而新增一个剥信封分支。理由有二本次变更已重录全部已发布测试 fixturefixture 即测试前置数据且会话格式本身不承诺向后兼容。语言提示推导langFromPath与LANG_BY_EXTENSIONReadResultView.lang由langFromPath从文件路径推导实现在 read-render.ts其查找表LANG_BY_EXTENSION在 read-render.ts 定义const LANG_BY_EXTENSION: ReadonlyRecordstring, string { ts: ts, tsx: tsx, mts: ts, cts: ts, js: js, jsx: jsx, mjs: js, cjs: js, json: json, jsonc: json, py: py, rb: rb, go: go, rs: rs, java: java, c: c, h: c, cc: cpp, cpp: cpp, hpp: cpp, cxx: cpp, cs: cs, kt: kotlin, swift: swift, php: php, sh: sh, bash: sh, zsh: sh, yaml: yaml, yml: yaml, toml: toml, ini: ini, md: md, markdown: md, mdx: mdx, html: html, htm: html, css: css, scss: scss, less: less, sql: sql, xml: xml, lua: lua, }推导规则可以归纳为四点取最后路径段先按/或\切出最后一个路径段同时兼容 POSIX 与 Windows 路径分隔符取最后一个点之后的扩展名且大小写不敏感.TS与.ts等价以下情况返回undefined卡片随之省略lang、UI 渲染纯文本dotfile如.gitignore开头的点不算扩展名、无扩展名如/etc/hosts、结尾的点、以及任何未知扩展名防御原型污染查找使用Object.hasOwn(LANG_BY_EXTENSION, ext)做自有属性检查——一个文件名恰好以foo.constructor、foo.__proto__结尾时绝不能命中继承成员否则函数值会流入lang并导致工具输出 JSON 校验失败。这张表不是可调项tunable它是 UI 可以忽略的展示提示而非随部署变化的选择未知扩展名优雅降级为纯文本而非报错。它刻意保持小规模而非穷尽的语言注册表——扩展它只需新增一行表项。备选方案回顾为什么是这些设计实现笔记2026-07-30-web-read-card.md明确记录了四条被否决的路径理解它们有助于把握设计的边界备选方案否决理由在presentResult中重新解析N: text文本按第一个:切分有歧义行文本自身可含:、脚注只在部分分支陈述totalLines精确总数会丢失、渲染格式一变即失效。presentationMeta直接携带已结构化数据零解析调用侧也打标签ReadCallView镜像终端 card 的两侧对称read 调用在执行前无内容、无行数组、无总数调用侧卡片只会是空变体重复GenericCallViewkind: read已表达的信息终端两侧打标签是因为调用时确有数据命令、cwd把结构化窗口放进新服务或旁路通道meta已是既定持久化展示通道write/edit 的应用 diff 也走它随会话日志免费回放无需新接线新服务等于重新发明事件日志已有的持久化与回放用 merge-extensible union 替代封闭标签与渲染意图 union 封闭的理由一致新卡片需要消费代码来渲染被消费方静默丢弃的变体比编译错误更糟。把read加入封闭 union 是受认可的扩展方式——每个在card上 switch 的消费方因新成员落入 generic default 而继续编译想要富视图的消费方自行新增分支影响评估对消费方ToolResultView从三个成员变成四个。消费方有两个选择渲染结构化形态读取lines/lang/totalLines/offset渲染成行号栏与内容分离、支持语法高亮的代码视图与 diff 卡片的渲染体验对齐路由到 generic 路径因 read card 始终携带content剥信封后的文本generic/default 分支仍能显示完整文件文本。这次生产者变更是让结构化数据可触及的后端——它不要求每个消费方在同一时间实现富视图具备能力的客户端可以先消费其余客户端不受影响。对生产者与存储read 工具现在为每次顶层 read计算presentationMeta一次lines.map加一次langFromPath调用是对已有数据的极小投影成本可忽略meta随会话日志持久化read 结果在磁盘上略大——它已渲染为文本的行数组如今也以结构化形式存在一份。这是换取回放时结构可重建的既定代价。测试与验证Read Card 的验证覆盖两层全部在仓库内可查单元测试packages/fs/tool-fs/tests/read-render.spec.ts该文件与 read-render.ts 同目录逐项钉住两个纯函数langFromPath已知扩展名的大小写不敏感匹配扩展名在最后路径段与最后一个点之后读取以及所有undefined情形dotfile、无扩展名、结尾的点、未知扩展名readMetaFromMeta含与不含lang的良构收窄以及每一种拒绝——非对象、数组、缺失或类型错误的path/totalLines/lines、畸形行项、非字符串lang更关键的是由于该函数收窄的是不透明的持久化 meta 边界它还覆盖了类型正确但语义无效的回放 JSON不是 1-based 整数的offset、小于offset的首行number、不是 1-based 整数的行number0、1.5、NaN、Infinity、不是非负整数的totalLines-1、1.5、NaN、行号重复/递减/超过totalLines以及正offset处的空窗口字节上限低于首个选中行。集成测试packages/fs/tool-fs/tests/tools.spec.ts钉住工具接线execute把结构化窗口含与不含lang提示作为meta附上presentResult把它收窄为携带剥信封content的card: read视图以及各拒绝路径错误结果、非单文本内容、meta 有效但信封畸形、信封有效但 meta 缺失或畸形一律回退到undefined。两个改动的源文件保持逐文件 100% 覆盖率。快照证据本变更携带的是持久化 meta 与扩展后联合类型的快照证据而非新渲染视图的证据重录的 ACP 会话 fixturefs-read、fs-read-window、fs-edit、fs-policy-reject、fs-write-overwrite、parallel-tool-calls、agent-instructions、workspace-edit钉住持久化的 readmeta含{{cwd}}令牌化的路径cordis-inspect-jsdoc快照钉住四成员的ToolResultView联合类型当时的终端快照还钉住消费方的 generic dim-Markdown 回退保持逐字节一致结构化卡片自身的组装应用 transcript 则归属于消费它的前端变更。相关文档导航工具调用展示的带标签渲染意图 union—— 本次以read结果分支扩展的card标签词汇含GenericCallView/TerminalCallView/DiffCallView及封闭联合的来龙去脉规范化工具输出约定—— 拥有presentationMeta持久化通道与output.schema契约是本次投影方案的底层依据Web 终端 card—— 客户端消费结构化卡片的前置先例read card 遵循相同的生产者投影、仅结果侧模式核心实现read-render.ts窗口构建、文本信封、langFromPath、readMetaFromMeta、read.tsread工具注册与三个展示回调测试read-render.spec.ts、tools.spec.ts项目立场AGENTS.mdpre-release stancefoundation over blast radius。赞分享人工智能AI AgentAgent 框架DeepSeek【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址https://gitcode.com/gh_mirrors/de/deepseek-harness点击查看免费下载相关推荐qwen-code 的 report_findings 类型化契约让代码评审发现以结构化数据直达所有客户端qwen code 的 report_findings 类型化契约让代码评审发现以结构化数据直达所有客户端 导读 在 qwen code一个运行于终端中的开人工智能AI Agent代码智能体工具调用交互助手CLIQwenDeepSeek Harness 搜索结果卡片渲染grep/glob 结构化 card: search 视图的设计与实现DeepSeek Harness 搜索结果卡片渲染grep/glob 结构化 card: search 视图的设计与实现 本篇技术指南以 DeepSeek人工智能AI AgentAgent 框架DeepSeekbash循环读取文件while read line结构bash循环读取文件while read line结构 你是否还在为处理日志文件、配置解析或数据清洗时的逐行读取需求而烦恼本文将系统讲解 while rea教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表