docs(i18n): proofread README translations 161-180

This commit is contained in:
j-xiang
2026-07-29 15:29:39 +08:00
parent a37097846b
commit bc7086a087
20 changed files with 189 additions and 189 deletions

View File

@@ -2,17 +2,17 @@
[English](README.md) | 中文 [English](README.md) | 中文
由插件拥有、供交互式 UI 适配器使用的面向用户命令注册表。[插件命令注册 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md)定义了其边界与分发契约。 由插件负责、供交互式 UI 适配器使用的面向用户命令注册表。[插件命令注册 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md)定义了其边界与分发契约。
## 服务契约 ## 服务契约
`ctx.commands.register(definition)` 注册一个小写命令名称、描述、可选的非结构化输入提示,以及可中止的处理器。每个已注册命令都可供所有已组合的命令适配器使用;与某项部署不兼容的插件不会在此注册。普通上下文中的注册全局生效。在 `agent.ctx` 下挂载的命令生产插件会声明自身的 `commands` 注入,并创建精确限定到该 agent 的定义;该定义会遮蔽同名的全局定义。这种子级注入形态保留了 agent 作用域,同时不会让核心 agent loop 依赖 UI 服务。同一层中的名称重复会在注册时失败。每个 disposer 都是 Cordis effect 返回的确切 disposer;注册或移除命令时,系统会通知每个 `commands/change` 观察者,使实时适配器能够刷新发现结果。观察者失败会写入日志,既不能否决注册表变更,也不能阻止后续观察者运行。 `ctx.commands.register(definition)` 注册一个小写命令名称、描述、可选的非结构化输入提示,以及可中止的处理器。每个已注册命令都可供所有已组合的命令适配器使用;与某项部署不兼容的插件不会在此注册。普通上下文中的注册全局生效。在 `agent.ctx` 下挂载的命令生产插件会声明自身的 `commands` 注入,并创建精确限定到该 agent(智能体)的定义;该定义会遮蔽同名的全局定义。这种子级注入形态保留了 agent 作用域,同时不会让核心 agent loop(智能体循环)依赖 UI 服务。同一层中的名称重复会在注册时失败。每个 disposer 都是 Cordis effect 返回的确切 disposer;注册或移除命令时,系统会通知每个 `commands/change` 观察者,使运行中的适配器能够刷新发现结果。观察者失败会写入日志,既不能否决注册表变更,也不能阻止后续观察者运行。
`list(agent)` 在应用作用域遮蔽后,返回按名称排序的不可变描述符。`find(agent, name)` 返回相应定义。`execute(agent, line, signal)` 使用 `parseCommand()`,且只运行已知命令,返回已结算的 `CommandExecution`(规范化结果加生命周期配对 `commandId`);语法无效或名称未知时返回 `undefined`。已解析命令的生命周期会以 log-only 事件对的形式记录在接收 agent 的会话日志中:`command/run`(进入处理器前记录,携带铸造的 `commandId`、解析器的结构化 `name`/`args` 切分和发起方 `CommandSource`)与 `command/done`(结算时记录,携带结局种类与原样文本;处理器抛出或被中止时以 `kind: 'error'` 结算)。未通过准入的输入不记录任何事件。两者都是直接独立追加:没有轮次包裹它们,持久化在常规检查点与 teardown 时排空它们。 `list(agent)` 在应用作用域遮蔽后,返回按名称排序的不可变描述符。`find(agent, name)` 返回相应定义。`execute(agent, line, signal)` 使用 `parseCommand()`,且只运行已知命令,返回已结算的 `CommandExecution`(规范化结果加生命周期配对 `commandId`);语法无效或名称未知时返回 `undefined`。已解析命令的生命周期会以 log-only 事件对的形式记录在接收 agent 的会话日志中:`command/run`(进入处理器前记录,携带新生成的 `commandId`、解析器的结构化 `name`/`args` 切分和发起方 `CommandSource`)与 `command/done`(结算时记录,携带结果类型与原样文本;处理器抛出或被中止时以 `kind: 'error'` 结算)。未通过准入的输入不记录任何事件。两者都直接独立追加到接收 agent 的会话中:没有轮次包裹它们,持久化机制会在常规检查点和销毁期间排空这些事件。
`parseCommand()` 识别位于字节零位置的斜杠、由小写字母、数字、`_` 或 `-` 构成的名称,以及名称后紧接输入末尾或空白的形式。它将名称后的每个字节作为 `rawInput` 返回,其中包括分隔空白;消费方拥有各命令专用的语法,只能执行该语法允许的规范化。 `parseCommand()` 识别位于第 0 字节的斜杠、由小写字母、数字、`_` 或 `-` 构成的名称,以及名称后紧接输入末尾或空白的形式。它将名称后的每个字节作为 `rawInput` 返回,其中包括分隔空白;消费方负责各命令专用的语法,只能执行该语法允许的规范化。
处理器返回 `success` 或 `error`,并可附带 UI 文本。适配器直接渲染结果,结果绝不进入模型历史。注册表绝不会隐式地把 `rawInput` 提交给 agent;命令生产方可以通过接收命令的 `Agent` 显式安排模型可见工作,此时该生产方拥有由此产生的消息契约。注册表会让处理器完成与所提供的中止信号竞速,但不协作的处理器可能在调用方停止等待后继续产生自身的外部副作用。 处理器返回 `success` 或 `error`,并可附带 UI 文本。适配器直接渲染结果,结果绝不进入模型历史。注册表绝不会隐式地把 `rawInput` 提交给 agent;命令生产方可以通过接收命令的 `Agent` 显式安排模型可见工作,此时该生产方负责由此产生的消息契约。注册表会同时等待处理器完成和所提供的中止信号,以先发生者为准,但不响应中止的处理器可能在调用方停止等待后继续产生自身的外部副作用。
## 组合 ## 组合
@@ -32,9 +32,9 @@
#### KV Cache 影响 #### KV Cache 影响
注册表元数据、命令输入和直接输出绝不会进入模型请求,也不会影响其缓存。被命令变更的领域拥有之后产生的所有缓存影响。 注册表元数据、命令输入和直接输出绝不会进入模型请求,也不会影响其缓存。发生变更的领域负责之后产生的所有缓存影响。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **仅支持非结构化文本输入**:表单、补全 schema 和类型化参数仍由各命令自行解析。 - **仅支持非结构化文本输入**:表单、补全 schema 和类型化参数仍由各命令自行解析。
- **副作用采用协作式取消**:中止后,分发会停止等待;处理器必须遵循信号,才能停止已经进入外部系统的工作。 - **副作用采用协作式取消**:中止后,分发会停止等待;处理器必须遵循信号,才能停止已经进入外部系统的工作。

View File

@@ -2,11 +2,11 @@
[English](README.md) | 中文 [English](README.md) | 中文
`jsonrpc` 插件通过 stdio 提供以换行符分隔的 JSON-RPC,使进程外 SDK 客户端能够驱动 harness agent(智能体)。[`HarnessSdkServer`](src/server.ts) 持有协议方法和通知;传输与具名线类型位于 [`dsh-sdk-protocol`](../../sdk/sdk-protocol/README.md),与客户端 SDK 共享;[`jsonrpc-demo`](../../examples/jsonrpc-demo/README.md) 提供外围的 `cordis.yml` 应用。 `jsonrpc` 插件通过 stdio 提供以换行符分隔的 JSON-RPC,使进程外 SDK 客户端能够驱动 harness agent(智能体)。[`HarnessSdkServer`](src/server.ts) 负责协议方法和通知;传输与具名协议类型位于 [`dsh-sdk-protocol`](../../sdk/sdk-protocol/README.md),与客户端 SDK 共享;[`jsonrpc-demo`](../../examples/jsonrpc-demo/README.md) 提供外围的 `cordis.yml` 应用。
## 组装 ## 组装
`inject: ['agents']`。服务器按 `sessionId` 获取或创建一个 agent。只有服务建立快照时的生命周期 `local` 标志为 true,服务器才会转发 subagent 完成事件;提供方名称、子级 id 和持久化谱系均不能证明本地性。已注册的适配器优先;未被持有的 `deepseek` 路由会挂载 `dsh-llm-deepseek`,任何其他未被持有的提供方都会导致初始化失败。其他功能由外围 `cordis.yml` 提供。 `inject: ['agents']`。服务器按 `sessionId` 获取或创建一个 agent。只有服务对生命周期建立快照时记录的 `local` 标志为 true,服务器才会转发 subagent 完成事件;提供方名称、子级 id 和持久化谱系均不能证明本地性。已注册的适配器优先;尚无适配器负责的 `deepseek` 路由会挂载 `dsh-llm-deepseek`,任何其他尚无适配器负责的提供方都会导致初始化失败。其他功能由外围 `cordis.yml` 提供。
## 配置 ## 配置
@@ -38,9 +38,9 @@ Stdout 只承载 JSON-RPC 帧。部署不得组合 stdout logger;诊断应写
#### KV Cache 影响 #### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **协议没有逐会话关闭或提示词取消方法**:SDK 创建的 agent 会一直存活到进程关闭;一条已接受的提示词必须运行到 agent 空闲,该会话才能接受下一条。 - **协议没有逐会话关闭或提示词取消方法**:SDK 创建的 agent 会一直存活到进程关闭;一条已接受的提示词必须运行到 agent 空闲,该会话才能接受下一条。
- **stdout 纯净性由部署保证**:外围配置仍可能加载 stdout logger 并破坏 JSON-RPC 通道;此插件不会检查或否决同级 logger。 - **stdout 纯净性由部署保证**:外围配置仍可能加载 stdout logger 并破坏 JSON-RPC 通道;此插件不会检查或否决同级 logger。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文 [English](README.md) | 中文
模型侧 `ask_user_question` 工具,基于 `ctx.userInteraction` 实现。当模型需要确认、选择或缺失信息才能继续时,它可以借此向用户提出简明问题。 模型侧 `ask_user_question` 工具,基于 `ctx.userInteraction` 实现。当模型需要确认、选择结果或缺失的信息才能继续时,它可以借此向用户提出简明问题。
## 工具 ## 工具
@@ -15,7 +15,7 @@
- `options`:可选选项,包含 `label` 和 `description`。如需推荐某个选项,请将其置于首位,并在该标签末尾追加 `(Recommended)`。 - `options`:可选选项,包含 `label` 和 `description`。如需推荐某个选项,请将其置于首位,并在该标签末尾追加 `(Recommended)`。
- `multi_select`:该问题是否可以返回多个选中的选项。 - `multi_select`:该问题是否可以返回多个选中的选项。
工具调用 `ctx.userInteraction.ask()`,并返回规范的 `{ answers: [{ id, selected, custom? }] }`。`selected` 包含选项标签;仅当用户自由填写回答时才会出现 `custom`,并覆盖选中的选项。Native renderer 会保留紧凑的 JSON 文本形式 `{ "answers": [{ "id": "...", "selected": ["..."], "custom": "..." }] }`。 工具调用 `ctx.userInteraction.ask()`,并返回规范的 `{ answers: [{ id, selected, custom? }] }`。`selected` 包含选项标签;仅当用户自由填写回答时才会出现 `custom`,并覆盖选中的选项。Native 渲染器会保留紧凑的 JSON 文本形式 `{ "answers": [{ "id": "...", "selected": ["..."], "custom": "..." }] }`。
## 职责 ## 职责
@@ -31,17 +31,17 @@
#### Token 影响 #### Token 影响
工具可见的每个请求都会产生固定的 schema 开销。 工具可见时,每个请求都会产生固定的 schema token 开销。
#### KV Cache 影响 #### KV Cache 影响
只要定义和可见性保持不变,前缀即可稳定复用。插件生命周期变化或作用域限制可能从此 schema 开始使复用失效。 只要定义和可见性保持不变,前缀即可稳定复用。插件生命周期变化或作用域限制可能会使从此 schema 起的缓存复用失效。
### 工具调用历史与结果 ### 工具调用历史与结果
#### 模型看到的内容 #### 模型看到的内容
模型提出的完整问题保留在 assistant 工具调用参数中。用户回答后,下一步骤会看到精确采用 `{"answers":[{"id":"<id>","selected":["<label>"],"custom":"<text>"}]}` 形式的紧凑 JSON;不使用 `custom` 时会省略该字段,`selected` 可以包含零个、一个或多个标签。调用等待期间的 UI 交互不属于模型上下文。 模型提出的完整问题保留在 assistant 工具调用参数中。用户回答后,下一步会看到精确采用 `{"answers":[{"id":"<id>","selected":["<label>"],"custom":"<text>"}]}` 形式的紧凑 JSON;不使用 `custom` 时会省略该字段,`selected` 可以包含零个、一个或多个标签。调用等待期间的 UI 交互不属于模型上下文。
#### Token 影响 #### Token 影响
@@ -49,9 +49,9 @@
#### KV Cache 影响 #### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **待处理问题会阻塞工具调用,直至用户作答**:该工具未声明 `timeout-policy` 预算;取消仅沿用当前轮次的 `exec.signal`。 - **待处理问题会阻塞工具调用,直至用户作答**:该工具未声明 `timeout-policy` 预算;取消仅沿用当前轮次的 `exec.signal`。
- **Native 回答渲染为 JSON 文本**:规范值仍为结构化数据,但模型侧结果使用紧凑 JSON,而非更丰富的内容块词汇。 - **Native 回答渲染为 JSON 文本**:规范值仍为结构化数据,但模型侧结果使用紧凑 JSON,而非更丰富的内容块词汇。

View File

@@ -4,9 +4,9 @@
与通道无关的一次性审批 seam。`ctx.approval.request(req)` 返回 `allowed-once`、`rejected`、`cancelled` 或 `unavailable`;应答者缺失或失败时会以拒绝方式关闭,授权也只适用于所请求的操作。确切事件签名见生成的 [Cordis 目录](../../../docs/cordis-catalog/events.md)。 与通道无关的一次性审批 seam。`ctx.approval.request(req)` 返回 `allowed-once`、`rejected`、`cancelled` 或 `unavailable`;应答者缺失或失败时会以拒绝方式关闭,授权也只适用于所请求的操作。确切事件签名见生成的 [Cordis 目录](../../../docs/cordis-catalog/events.md)。
每个请求都必须属于一个打开的 agent(智能体)轮次。服务会追加一对 `approval/asked` 与 `approval/decided` 审计记录,而模型只会看到由此产生且已写入日志的工具结果。已中止的请求会 resolve 为 `cancelled`;如果审计追加在提交前失败,请求会被拒绝,而不会返回一项未记录的决定。 每个请求都必须属于一个尚未结束的 agent(智能体)轮次。服务会追加一对 `approval/asked` 与 `approval/decided` 审计记录,而模型只会看到由此产生且已写入日志的工具结果。已中止的请求会解析为 `cancelled`;如果审计记录的追加在提交前失败,Promise 会被拒绝,而不会返回一项未记录的决定。
应答者是 `approval/request` waterfall(瀑布式事件)监听器。要回答所拥有 agent 的请求,请返回一个结果;否则调用 `next()` 委托。限定到 agent 的监听器只接收该 agent 的请求;每项部署应当组合一个终端应答者,因为同级监听器的顺序不是策略优先级机制。ACP(Agent Client Protocol)自动化桥接层为其拥有的会话提供一次性机器决定。 应答者是 `approval/request` waterfall(瀑布式事件)监听器。要回答其负责的 agent 请求,请返回一个结果;否则调用 `next()` 委托。限定到 agent 的监听器只接收该 agent 的请求;每项部署应当组合一个最终应答者,因为同级监听器的顺序不是策略优先级机制。ACP(Agent Client Protocol)自动化桥接层为其负责的会话提供一次性机器决定。
`ApprovalPolicy` 为 `'ask'` 或 `'never'`。实际值取最后一条 `approval/policy` 事件,并回退到配置;`setApprovalPolicy()` 是写入路径。`'never'` 会在交互式分发之前拒绝请求,也是提示词中唯一声明的策略。切换最多产生一条合并通知:如果覆盖发生在最后一个 `request/header` 之后,则归因于用户;否则归因于操作方/配置。 `ApprovalPolicy` 为 `'ask'` 或 `'never'`。实际值取最后一条 `approval/policy` 事件,并回退到配置;`setApprovalPolicy()` 是写入路径。`'never'` 会在交互式分发之前拒绝请求,也是提示词中唯一声明的策略。切换最多产生一条合并通知:如果覆盖发生在最后一个 `request/header` 之后,则归因于用户;否则归因于操作方/配置。
@@ -49,15 +49,15 @@ Approval prompts are disabled in this session: actions that require approval are
#### Token 影响 #### Token 影响
不会产生重复的审计 token。拒绝可能以一条少量且保留的错误替换正常工具结果,而允许会保留消费方的普通结果。 不会产生重复的审计 token。拒绝可能以一条简短且会保留的错误信息替换正常工具结果,而允许会保留消费方的普通结果。
#### KV Cache 影响 #### KV Cache 影响
仅追加;新出现的可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。 仅追加;新出现的可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **请求只在打开的轮次内有效**:空闲时或轮次之间的调用方会在审计前抛出异常;持久化的轮次外审批工作流仍属延期事项。 - **请求只在尚未结束的轮次内有效**:在空闲时或轮次之间发起调用,会在审计前抛出异常;持久化的轮次外审批工作流仍属暂缓事项。
- **仅存在一次性授权**:结果词汇包含 `allowed-once`,但不含 `allow-always`、记忆规则、撤销或授权存储;会话策略只有 `ask`/`never`。 - **仅存在一次性授权**:结果词汇包含 `allowed-once`,但不含 `allow-always`、已记住的规则、撤销或授权存储;会话策略只有 `ask`/`never`。
- **请求不携带工具参数**:应答者会看到工具名称、原因和可选调用 id;ACP 机器通道要求调用 id,并会委托不含 id 的请求。 - **请求不携带工具参数**:应答者会看到工具名称、原因和可选调用 id;ACP 机器通道要求调用 id,并会委托不含 id 的请求。
- **没有内置应答者**:无头或组合不完整的部署会 resolve 为 `unavailable` 并以拒绝方式关闭;服务自身绝不会提示人类。 - **没有内置应答者**:无头或组合不完整的部署会 resolve 为 `unavailable` 并以拒绝方式关闭;服务自身绝不会提示人类。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文 [English](README.md) | 中文
抽象用户交互 seam。它拥有 `ctx.userInteraction`:当面向模型的工具或权限插件需要暂停工作并询问人类决定时所使用的服务。 抽象用户交互 seam。它定义 `ctx.userInteraction`,供面向模型的工具或权限插件在需要暂停工作并询问人类决定时使用。
## 服务:`UserInteractionService`(ctx 键:`userInteraction`) ## 服务:`UserInteractionService`(ctx 键:`userInteraction`)
@@ -19,7 +19,7 @@
- `UserInteractionProvider`:包含 `ask(request)` 的 UI 实现。 - `UserInteractionProvider`:包含 `ask(request)` 的 UI 实现。
- `UserInteractionError`:`HarnessError` 的子类,包含 `EMPTY_QUESTIONS`、`NO_PROVIDER`、`DUPLICATE_PROVIDER` 和 `ASK_ABORTED` 等代码。 - `UserInteractionError`:`HarnessError` 的子类,包含 `EMPTY_QUESTIONS`、`NO_PROVIDER`、`DUPLICATE_PROVIDER` 和 `ASK_ABORTED` 等代码。
当回答包含 `custom` 时,`selected` 为空;自定义文本会覆盖所选选项,而不是补充它们。UI 可以把跳过的条目保留为 `{ id, selected: [] }`,既维持现有回答形态,也保留该批次中的其他回答。 当回答包含 `custom` 时,`selected` 为空;自定义文本是所选选项的替代,而不是补充。UI 可以把跳过的条目保留为 `{ id, selected: [] }`,既维持现有回答形态,也保留该批次中的其他回答。
## 职责 ## 职责
@@ -31,9 +31,9 @@
#### KV Cache 影响 #### KV Cache 影响
不会直接使缓存失效;具名消费方拥有所有请求前缀变更。 不会直接使 KV Cache 失效;请求前缀的任何变更均由上述消费方负责。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **每个上下文只能有一个提供方**:不支持路由或扇出到多个 UI;第二次注册会抛出 `DUPLICATE_PROVIDER`,未注册任何提供方时,`ask()` 会抛出 `NO_PROVIDER`,而不会降级。 - **每个上下文只能有一个提供方**:不支持路由或扇出到多个 UI;第二次注册会抛出 `DUPLICATE_PROVIDER`,未注册任何提供方时,`ask()` 会抛出 `NO_PROVIDER`,而不会降级。
- **词汇仅包含问题表单形态**:可选选项加可选自定义文本;更丰富的交互形态(文件选择器、diff 预览确认)尚无 seam 词汇。 - **词汇仅包含问题表单形态**:可供选择的选项加可选的自定义文本;更丰富的交互形态(文件选择器、diff 预览确认)尚无 seam 词汇。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文 [English](README.md) | 中文
`Branded<B>` 名义类型原语:一个微小的**仅类型** 包(无运行时代码,无 harness 包依赖),由每个拥有跨边界 id 的包共享。 `Branded<B>` 名义类型原语:一个微小的**仅类型**包(package),无运行时代码,也不依赖其他 harness 包;所有负责跨边界 id 的包都会共享它。
## `Branded` 是什么 ## `Branded` 是什么
@@ -19,10 +19,10 @@ export function SessionId(id: string): SessionId {
} }
``` ```
构造操作通过所属包中针对每个 id 的工厂完成。比较、日志记录、JSON 序列化和协议格式与普通字符串表现相同;品牌会在编译时被擦除。 构造操作通过所属包中各 id 专用的工厂完成。比较、日志记录、JSON 序列化和协议格式(wire format)的行为与普通字符串相同;品牌信息会在编译时被擦除。
## 策略:为跨包边界的 id 添加品牌 ## 策略:为跨包边界的 id 添加品牌
包为自己拥有的 id 添加品牌:`CallId` 位于 `dsh-llm`,共享的 agent/会话 `SessionId` 位于 `dsh-session`,`TaskId` 位于 `dsh-tasks`。为可能被混淆的跨包 id 添加品牌,但无需为每个字符串都添加。 包为自己拥有的 id 添加品牌:`CallId` 位于 `dsh-llm`,共享的 agent/会话 `SessionId` 位于 `dsh-session`,`TaskId` 位于 `dsh-tasks`。为可能被混淆的跨包 id 添加品牌,但无需为每个字符串都添加。
该包只负责原语。保持无依赖意味着,例如 `dsh-tasks` 可以为 `TaskId` 添加品牌,而无需仅为使用 `Branded` 而导入不相关的功能包。 该包只负责这一原语。保持无依赖意味着,例如 `dsh-tasks` 可以为 `TaskId` 使用品牌类型,而无需仅为使用 `Branded` 而导入不相关的功能包。

View File

@@ -16,9 +16,9 @@ DeepSeek Harness 用户数据的共享文件系统路径辅助工具。
`expandHomePath()` 使用操作系统主目录展开 `~`、`~/...` 和 Windows 风格的 `~\...` 前缀。它会保留非波浪号路径和 `~user/...` 原样不变。 `expandHomePath()` 使用操作系统主目录展开 `~`、`~/...` 和 Windows 风格的 `~\...` 前缀。它会保留非波浪号路径和 `~user/...` 原样不变。
该包刻意保持规模小且不依赖 harness,以便产品包共享用户数据路径约定,而不必彼此依赖。 该包(package)刻意保持规模小且不依赖 harness,以便产品包共享用户数据路径约定,而不必彼此依赖。
## 已知限制与待完成工作 ## 已知限制与暂缓事项
- **展开范围刻意保持狭窄**:只有单独的 `~`、`~/...` 和 `~\...` 使用当前操作系统主目录;`~alice/...` 等指定用户的形式、环境变量和 shell 表达式保持不变。 - **展开范围刻意保持狭窄**:只有单独的 `~`、`~/...` 和 `~\...` 使用当前操作系统主目录;`~alice/...` 等指定用户的形式、环境变量和 shell 表达式保持不变。
- **辅助工具不会操作文件系统**:调用方仍负责目录创建、存在性检查、权限,以及对结果路径应用信任策略。 - **辅助工具不会操作文件系统**:调用方仍负责目录创建、存在性检查、权限,以及对结果路径应用信任策略。

View File

@@ -2,11 +2,11 @@
[English](README.md) | 中文 [English](README.md) | 中文
一个轻依赖的**保留** 库:为必须限制返回上下文量的工具提供有界的面向模型输出。调用方将项或文本分片送入有界对象,然后取回保留的内容和精确的省略元数据。 一个轻依赖的**保留**库:为必须限制返回上下文量的工具提供有界的面向模型输出。调用方将项或文本分片送入有界对象,然后取回保留的内容和精确的省略元数据。
该库**只** 负责这个机制问题:*「我们保留了什么,又省略了什么?」*。工具专用代码保留其业务语义:文件分组、行号、退出码、提供方错误状态、每行预览截断、spill 文件以及面向模型的文案。这就是 [Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-tool-result-retention-library.md) 划定的边界。 该库**只**负责这个机制问题:*「我们保留了什么,又省略了什么?」*。工具专用代码保留其业务语义:文件分组、行号、退出码、提供方错误状态、每行预览截断、spill 文件以及面向模型的文案。这就是 [Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-07-06-tool-result-retention-library.md) 划定的边界。
它是**库,而非服务或插件**:没有 `ctx`,不注册任何内容,不发出任何事件。状态只存在于每个 retainer(一次累积)中,绝不跨调用。工具包直接导入它。 它是**库,而非服务或插件**:没有 `ctx`,不注册任何内容,不发出任何事件。状态只存在于每个 retainer(一次累积)中,绝不跨调用。工具包(package)直接导入它。
## 对外接口 ## 对外接口
@@ -32,18 +32,18 @@ import type {
## 资源模式 ## 资源模式
两个 retainer 使用独立名称,而不是同一个通用收集器,因为它们的**资源模型** 不同。 两个 retainer 使用独立名称,而不是同一个通用收集器,因为它们的**资源模型**不同。
- **`ItemRetainer` 限制有序逻辑单元**。搜索工具可收集完整结果集用于 spill 文件恢复,同时只为面向模型的预览保留前 `maxItems` 项。因为调用方会继续送入每个已观察到的项,所以省略数量是精确的。 - **`ItemRetainer` 限制有序逻辑单元**。搜索工具可收集完整结果集用于 spill 文件恢复,同时只为面向模型的预览保留前 `maxItems` 项。因为调用方会继续送入每个已观察到的项,所以省略数量是精确的。
- **`TextRetainer` 限制面向字节的文本**。`head`、`tail` 和 `headTail` 在 `finish()` 时保留 UTF-8 边界;`headTail` 是 `dsh-spill-policy` 用于围绕 spill 文件通知构建有界预览的形态。 - **`TextRetainer` 限制面向字节的文本**。`head`、`tail` 和 `headTail` 在 `finish()` 时保留 UTF-8 边界;`headTail` 是 `dsh-spill-policy` 用于围绕 spill 文件通知构建有界预览的形态。
## `truncated` 是预算事实,绝不表示「不完整」 ## `truncated` 是预算事实,绝不表示「不完整」
`truncated` 表示*因为预算限制,retainer 省略了本可获得的内容*。它**不** 表示上游不完整。权限失败、跳过二进制文件、提供方部分失败、不可读候选项和无效 UTF-8 保留在工具领域字段中,绝不合并到 `truncated`。将两者混为一谈是该库命名最容易诱发的缺陷;务必保持分离。 `truncated` 表示*因为预算限制,retainer 省略了本可获得的内容*。它**不**表示上游不完整。权限失败、跳过二进制文件、提供方部分失败、不可读候选项和无效 UTF-8 保留在工具领域字段中,绝不合并到 `truncated`。将两者混为一谈是该库命名最容易诱发的缺陷;务必保持分离。
## 字节,而非字符 ## 字节,而非字符
文本上限和 `omittedBytes` 按**字节** 计数,以保证进程/正文安全(子进程 pipe 和 HTTP 正文都是字节流)。跨越码点的分片会被正确处理:`finish()` 会修剪每个切割位置的不完整码点,使返回文本绝不在边界引入替换字符;首尾两侧会分开解码,因此绝不会跨越被省略的中间部分重建码点。按字符或行限制的预览预算属于独立的工具职责。 文本上限和 `omittedBytes` 按**字节**计数,以保证进程/正文安全(子进程管道和 HTTP 正文都是字节流)。跨越码点的分片会被正确处理:`finish()` 会修剪每个切割位置的不完整码点,使返回文本绝不在边界引入替换字符;首尾两侧会分开解码,因此绝不会跨越被省略的中间部分重建码点。按字符或行限制的预览预算属于独立的工具职责。
## 工具映射 ## 工具映射
@@ -51,11 +51,11 @@ import type {
| 工具 | Retainer 与策略 | 说明 | | 工具 | Retainer 与策略 | 说明 |
|---|---|---| |---|---|---|
| `glob` | `ItemRetainer<FsGlobEntry>`, `head` | 收集完整的已排序路径列表用于 spill 文件,同时在内联位置保留第一页。路径映射、已跳过候选项和 `incomplete` 保留在外部。 | | `glob` | `ItemRetainer<FsGlobEntry>`,`head` | 收集完整的已排序路径列表用于 spill 文件,同时在内联位置保留第一页。路径映射、已跳过候选项和 `incomplete` 保留在外部。 |
| `grep` | `ItemRetainer<FlatGrepMatch>`, `head` | 收集匹配项用于 spill 文件,同时在内联位置保留第一页。每个匹配项的预览截断、分组、排序和 `incomplete` 保留在外部。 | | `grep` | `ItemRetainer<FlatGrepMatch>`,`head` | 收集匹配项用于 spill 文件,同时在内联位置保留第一页。每个匹配项的预览截断、分组、排序和 `incomplete` 保留在外部。 |
| `bash` | `TextRetainer`, `tail` or `headTail` | 执行器仍负责 spill 文件、退出状态、信号、超时和后台任务。 | | `bash` | `TextRetainer`,`tail` 或 `headTail` | 执行器仍负责 spill 文件、退出状态、信号、超时和后台任务。 |
| `web_fetch` | `TextRetainer`, `head` or `headTail` | 提供方/资源上限保留为提供方事实;retainer 只提供保留文本和省略元数据。 | | `web_fetch` | `TextRetainer`,`head` 或 `headTail` | 提供方/资源上限保留为提供方事实;retainer 只提供保留文本和省略元数据。 |
| `web_search` | `ItemRetainer<WebSearchSource>`, `head` | 当提供方返回的来源超过面向模型的结果应包含的数量时,标准化「来源已达上限」通知。 | | `web_search` | `ItemRetainer<WebSearchSource>`,`head` | 当提供方返回的来源超过面向模型的结果应包含的数量时,标准化「来源已达上限」通知。 |
`read` **刻意不在 v1 范围内**。其 `read-render` 辅助工具负责文件专用的分页契约:`offset`/`limit`、行号、`totalLines`、偏移越界错误、每行预览截断、针对已选窗口的字节上限。这是行窗口渲染器,而非通用保留机制。单个 `Omitted` 数量无法表示行窗口两侧。 `read` **刻意不在 v1 范围内**。其 `read-render` 辅助工具负责文件专用的分页契约:`offset`/`limit`、行号、`totalLines`、偏移越界错误、每行预览截断、针对已选窗口的字节上限。这是行窗口渲染器,而非通用保留机制。单个 `Omitted` 数量无法表示行窗口两侧。
@@ -89,9 +89,9 @@ const footer = formatRetentionNotice(
#### KV 缓存影响 #### KV 缓存影响
不直接导致失效;指定的消费方负责其引起的任何请求前缀变更。 不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与待完成工作 ## 已知限制与暂缓事项
- **项保留只支持 `head`**:tail、head/tail、分页、分组和提供方完整性语义仍由工具负责。 - **项保留只支持 `head`**:tail、head/tail、分页、分组和提供方完整性语义仍由工具负责。
- **文本保留面向字节**:`read` 分页等行窗口和字符窗口需要单独的渲染器;切割可能会丢弃部分 UTF-8 边界字节,以保持返回文本有效。 - **文本保留面向字节**:`read` 分页等行窗口和字符窗口需要单独的渲染器;切割可能会丢弃部分 UTF-8 边界字节,以保持返回文本有效。

View File

@@ -2,9 +2,9 @@
[English](README.md) | 中文 [English](README.md) | 中文
超时的**时序与分类** 部分:一个零依赖纯函数库(无运行时 harness 依赖),由每个需要限制调用方超时提示、启动 deadline,并在之后区分「已超时」与「已取消」的功能共享。 超时的**时序与分类**部分:一个零依赖纯函数库(无运行时 harness 依赖),由每个需要限制调用方超时提示、启动 deadline,并在之后区分「已超时」与「已取消」的功能共享。
它**不负责终止**。它发出的信号只会*通知*;真正停止工作仍由各功能负责,因为机制各不相同:bash 对操作系统进程组发送 SIGKILL,web 拆除 `fetch` socket,没有任何共享层能够承担全部终止机制。[Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md) 将边界划定为:共享时序/分类,将强制终止保留在本地。 它**不负责终止**。它发出的信号只会*通知*;真正停止工作仍由各功能负责,因为机制各不相同:bash 对操作系统进程组发送 SIGKILL,web 关闭 `fetch` 套接字,没有任何共享层能够承担全部终止机制。[Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md) 将边界划定为:共享时序/分类,将强制终止保留在本地。
它是**库,而非服务或插件**:没有 `ctx`,不注册任何内容,不持有状态,也不发出事件。「超时服务」必须了解如何停止每项功能的工作,这正是微内核要排除在共享层之外的知识。 它是**库,而非服务或插件**:没有 `ctx`,不注册任何内容,不持有状态,也不发出事件。「超时服务」必须了解如何停止每项功能的工作,这正是微内核要排除在共享层之外的知识。
@@ -16,16 +16,16 @@ import { clampTimeout, deadline, idleWatchdog, MAX_TIMER_DELAY_MS, timeoutOf, Ti
| 导出项 | 职责 | | 导出项 | 职责 |
|---|---| |---|---|
| `clampTimeout(requested, def, max, name?)` | 验证调用方可选的正有限提示,从 `def` 填充,并限制在 `max` 以内。如果提示不为正数或有限数,则抛出错误(包含 `name`)。 | | `clampTimeout(requested, def, max, name?)` | 验证调用方可选的、值为正且有限的提示,从 `def` 填充,并限制在 `max` 以内。如果提示为非正数或非有限数,则抛出错误(包含 `name`)。 |
| `deadline(upstream, timeoutMs, code)` | 将 `upstream` 取消与超时融合为一个 `AbortSignal`(`AbortSignal.any`);超时携带 `TimeoutReason`。`[Symbol.dispose]` 清除 timer。 | | `deadline(upstream, timeoutMs, code)` | 将 `upstream` 取消与超时融合为一个 `AbortSignal`(`AbortSignal.any`);超时携带 `TimeoutReason`。`[Symbol.dispose]` 清除 timer。 |
| `idleWatchdog(upstream, timeoutMs, code)` | 保持一个稳定的融合信号,并且只在受保护的异步迭代器 `next()` 尚未完成时启动。解析后取消启动;后续需求重新启动;dispose 清除;并发需求被拒绝。 | | `idleWatchdog(upstream, timeoutMs, code)` | 保持一个稳定的融合信号,并且只在受保护的异步迭代器 `next()` 尚未完成时启动 timer。完成后停止 timer;后续需求重新启动 timer;dispose(资源释放)时清除;并发需求被拒绝。 |
| `MAX_TIMER_DELAY_MS` | Node 在不将延迟限制为 1 毫秒时可调度的最大延迟(`2_147_483_647`)。拥有 timer 的配置不得超过该值。 | | `MAX_TIMER_DELAY_MS` | Node 在不将延迟限制为 1 毫秒时可调度的最大延迟(`2_147_483_647`)。负责 timer 的配置不得超过该值。 |
| `timeoutOf(signal \| { reason }, code?)` | 从已中止的信号/错误中恢复 `TimeoutReason`,否则返回 `undefined`,即超时与取消的分类器。传入 `code` 可仅匹配这个 deadline 的 timer(见下文的嵌套)。 | | `timeoutOf(signal \| { reason }, code?)` | 从已中止的信号/错误中恢复 `TimeoutReason`,否则返回 `undefined`,即超时与取消的分类器。传入 `code` 可仅匹配这个 deadline 的 timer(见下文的嵌套)。 |
| `TimeoutReason` | 印在超时中止上的内部原因(`code` + `timeoutMs`)。它不是公开错误;提供方将其转换为自己的错误/字段。 | | `TimeoutReason` | 标记在超时中止上的内部原因(`code` + `timeoutMs`)。它不是公开错误;提供方将其转换为自己的错误/字段。 |
## `timeoutMs <= 0` 哨兵值 ## `timeoutMs <= 0` 哨兵值
`0` 是后端自有后台工作(bash `start()`)使用的「无超时」值,其可见范围为:**内部**。`deadline()` 不启动 timer,只转发 `upstream`;如果也没有 upstream,它将返回永不中止的信号和无操作 disposer,因此每个调用方都能保持同一种调用形态。外部请求提示会通过 `clampTimeout` 验证为**正有限数**,之后才进入 `deadline`,因此 `0` 绝不是面向模型/插件的「禁用超时」值。 `0` 是后端自有后台工作(bash `start()`)使用的**内部**「无超时」值。`deadline()` 不启动 timer,只转发 `upstream`;如果也没有 upstream,它将返回永不中止的信号和无操作 disposer,因此每个调用方都能保持同一种调用形态。外部请求提示会通过 `clampTimeout` 验证为**正有限数**,之后才进入 `deadline`,因此 `0` 绝不是面向模型/插件的「禁用超时」值。
## 使用形态 ## 使用形态
@@ -44,15 +44,15 @@ export async function runWithDeadline(upstream: AbortSignal | undefined, timeout
} }
``` ```
该信号只会*通知*;调用方必须连接自己的终止机制(`d.signal.addEventListener('abort', kill)`,或将 `d.signal` 传给 `fetch`)。让 promise 与 timer 竞速,会在子进程或 socket 泄漏的情况下就解析工具调用;发出信号则会强制要求存在真正的终止路径。 该信号只会*通知*;调用方必须接入自己的终止机制(`d.signal.addEventListener('abort', kill)`,或将 `d.signal` 传给 `fetch`)。让 promise 与 timer 竞速,会在子进程或套接字仍在泄漏时就让工具调用完成;发出信号则会强制要求存在真正的终止路径。
将你自己的 `code` 传给 `timeoutOf`,以便分类可在嵌套中组合:当你收到的 `upstream` *本身*就是 deadline 信号时(未来启动每次调用 deadline 的 `tools/execute` 中间件),如果外层 timer 首先触发,`AbortSignal.any` 会保留外层 `TimeoutReason`。将范围限定为你的 `code`,可将外部超时视为普通 upstream 取消,这才是你所属功能视角下的正确分类,而不会在本地 timer 尚未到期时就声称自己超时。 将你自己的 `code` 传给 `timeoutOf`,以便分类可在嵌套中组合:当你收到的 `upstream` *本身*就是 deadline 信号时(未来启动每次调用 deadline 的 `tools/execute` 中间件),如果外层 timer 首先触发,`AbortSignal.any` 会保留外层 `TimeoutReason`。将范围限定为你的 `code`,可将外部超时视为普通 upstream 取消,这才是你所属功能视角下的正确分类,而不会在本地 timer 尚未到期时就声称自己超时。
对于流式传输,创建一个 `idleWatchdog`,将其稳定的 `signal` 传入传输,并为每次提供方读取调用 `watchdog.next(iterator)`。间隔必须为正有限数,且不得超过 `MAX_TIMER_DELAY_MS`;否则 Node 会将其限制为 1 毫秒。它只测量尚未完成的需求,因此当下游代码进行渲染或在请求下一个分片前以其他方式等待时,timer 不会运行。该原语仍然只会通知,因此传输必须观察稳定信号;DeepSeek 和 pi-ai 适配器证明,超时会关闭它们的真实响应正文或 SDK 请求。 对于流式传输,创建一个 `idleWatchdog`,将其稳定的 `signal` 传给传输层,并为提供方的每次读取调用 `watchdog.next(iterator)`。间隔必须为正有限数,且不得超过 `MAX_TIMER_DELAY_MS`;否则 Node 会将其限制为 1 毫秒。它只对尚未完成的读取请求计时,因此当下游代码进行渲染或在请求下一个分片前以其他方式等待时,timer 不会运行。该原语仍然只会通知,因此传输层必须观察稳定信号;DeepSeek 和 pi-ai 适配器证明,超时会关闭它们的真实响应正文或 SDK 请求。
## 哪些操作不设置超时 ## 哪些操作不设置超时
本地文件 `read`/`write`/`edit` 不接受 `timeoutMs`:系统调用最多只能尽力中止,超时无法强制 `fsync`/`rename` 停止,而添加超时将成为违反显式优于隐式的默认值。详见 [`fs/`](../../fs/README.md)。 本地文件 `read`/`write`/`edit` 不接受 `timeoutMs`:中止系统调用至多只能尽力而为,超时无法强制 `fsync`/`rename` 停止,而添加超时会引入一个违反显式优于隐式原则的隐式默认值。详见 [`fs/`](../../fs/README.md)。
## 模型体验 ## 模型体验
@@ -60,11 +60,11 @@ export async function runWithDeadline(upstream: AbortSignal | undefined, timeout
#### KV 缓存影响 #### KV 缓存影响
不直接导致失效;指定的消费方负责其引起的任何请求前缀变更。 不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与待完成工作 ## 已知限制与暂缓事项
- **只发出通知**:deadline 无法停止忽略其信号的工作;每项功能仍需要自己的 socket/进程/任务终止路径。 - **只发出通知**:deadline 无法停止忽略其信号的工作;每项功能仍需要自己的 socket/进程/任务终止路径。
- **`timeoutMs <= 0` 是内部词汇**:只有在所属后端已解析策略后,它才会禁用本地 timer;绝不会作为面向模型/插件的公开开关。 - **`timeoutMs <= 0` 是内部词汇**:只有在所属后端已解析策略后,它才会禁用本地 timer;绝不会作为面向模型/插件的公开开关。
- **第一个中止原因决定分类**:当 upstream 取消早于本地 timer 发生时,即使自己的超时之后也会到期,该层也无法再报告。 - **第一个中止原因决定分类**:当 upstream 取消早于本地 timer 发生时,即使自己的超时之后也会到期,该层也无法再报告。
- **空闲 watchdog 不是总 deadline**:它针对每个尚未完成的迭代器需求重新启动,并刻意排除消费方的思考时间。 - **空闲 watchdog 不是总 deadline**:它针对每个尚未完成的迭代器需求重新启动,并刻意排除消费方的处理时间。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文 [English](README.md) | 中文
web 访问能力 seam:抽象 web 接口、搜索/抓取提供方实现,以及面向模型的 web 工具。这些全是**产品** 包。 web 访问能力 seam:抽象 web 接口、搜索/抓取提供方实现,以及面向模型的 web 工具。这些全是**产品**包(package)。
| 包 | 职责 | ctx key | | 包 | 职责 | ctx key |
|---|---|---| |---|---|---|
@@ -10,9 +10,9 @@ web 访问能力 seam:抽象 web 接口、搜索/抓取提供方实现,以
| `web-search-exa/` | Exa 支持的 `WebSearchProvider` | (注册到 `ctx.web`) | | `web-search-exa/` | Exa 支持的 `WebSearchProvider` | (注册到 `ctx.web`) |
| `web-search-perplexity/` | Perplexity 支持的 `WebSearchProvider` | (注册到 `ctx.web`) | | `web-search-perplexity/` | Perplexity 支持的 `WebSearchProvider` | (注册到 `ctx.web`) |
| `web-search-deepseek/` | DeepSeek 支持的 `WebSearchProvider`,通过 Anthropic 兼容 API 使用原生 `web_search` | (注册到 `ctx.web`) | | `web-search-deepseek/` | DeepSeek 支持的 `WebSearchProvider`,通过 Anthropic 兼容 API 使用原生 `web_search` | (注册到 `ctx.web`) |
| `web-fetch-local/` | 匿名公共 HTTP(S) `WebFetchProvider` | (注册到 `ctx.web`) | | `web-fetch-local/` | 用于匿名访问公共 HTTP(S) 的 `WebFetchProvider` | (注册到 `ctx.web`) |
| `tool-web/` | 面向模型的 `web_search`/`web_fetch` 工具 schema | (注册到 `ctx.tools`) | | `tool-web/` | 面向模型的 `web_search`/`web_fetch` 工具 schema | (注册到 `ctx.tools`) |
接口位于 `web/web/`。与 bash/fs 不同,该 seam 跨越**两种能力**(搜索和抓取),每种能力都可能有多个提供方:`ctx.web` 是单一的 web 访问中间层,拥有一项提供方选择策略、一套中止/错误词汇,以及一个面向产品的「该 harness 如何访问 web」配置表层。提供方注册的是**能力** 而非工具;`tool-web` 是面向模型名称、schema、提示词指引和呈现的唯一 owner。替换搜索提供方不会改变模型提出查询的方式,替换抓取实现也不会改变模型请求 URL 的方式。 接口位于 `web/web/`。与 bash/fs 不同,该 seam 跨越**两种能力**(搜索和抓取),每种能力都可能有多个提供方:`ctx.web` 是单一的 web 访问中间层,拥有一项提供方选择策略、一套中止/错误词汇,以及一个面向产品的「该 harness 如何访问 web」配置接口。提供方注册的是**能力**而非工具;`tool-web` 是面向模型名称、schema、提示词指引和呈现的唯一负责方。替换搜索提供方不会改变模型提出查询的方式,替换抓取实现也不会改变模型请求 URL 的方式。
设计原理见 [web 能力 seam Agent Note](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md),其中也解释了搜索与抓取为何有意合并为一个 seam,以及为何暂缓实现 `web_fetch` 的 SSRF 防护。 设计原理见 [web 能力 seam Agent Note(agent 决策记录)](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md),其中也解释了搜索与抓取为何有意合并为一个 seam,以及为何暂缓实现 `web_fetch` 的 SSRF 防护。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文 [English](README.md) | 中文
面向模型的 web 工具套件 `web_search` 与 `web_fetch`,构建于 [web 能力 seam](../web/README.md)(`ctx.web`)之上。它只拥有面向模型的事项:工具名称、JSON schema、snake_case 参数名称、提示词区段、结果数量上限、结果格式、HTML→markdown 呈现,以及 `presentCall`。所有 web 访问都通过 `ctx.web`;该包绝不导入具体提供方。两个工具都不公开面向模型的超时:每个工具的协作式工具调用预算通过配置在此声明(`fetchTimeoutMs`/`searchTimeoutMs`,附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-timeout-policy`](../../timeout/timeout-policy/README.md)(`tools/execute` 包装层)强制执行;每个工具只把 `exec.signal` 转发给 seam。 面向模型的 web 工具套件 `web_search` 与 `web_fetch`,构建于 [web 能力 seam](../web/README.md)(`ctx.web`)之上。它只负责面向模型的事项:工具名称、JSON Schema、snake_case 参数名称、提示词区段、结果数量上限、结果格式、HTML→markdown 呈现,以及 `presentCall`。所有 web 访问都通过 `ctx.web`;该包(package)绝不导入具体提供方。两个工具都不公开面向模型的超时:每个工具的协作式工具调用超时预算通过配置在此声明(`fetchTimeoutMs`/`searchTimeoutMs`,附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-timeout-policy`](../../timeout/timeout-policy/README.md)(`tools/execute` 包装层)强制执行;每个工具只把 `exec.signal` 转发给 seam。
每个工具独立注册;只需要其中一个工具的产品可以通过配置禁用另一个(`{ search: false }`/`{ fetch: false }`)。 每个工具独立注册;只需要其中一个工具的产品可以通过配置禁用另一个(`{ search: false }`/`{ fetch: false }`)。
@@ -10,20 +10,20 @@
| 工具 | 参数 | 行为 | | 工具 | 参数 | 行为 |
|---|---|---| |---|---|---|
| `web_search` | `query`(string) | 发现。返回可选答案与源 URL。`max_results` **不** 面向模型:工具设置上限(`searchMaxResults` 配置,默认 8)并传给 seam。 | | `web_search` | `query`(string) | 用于发现信息。返回可选答案与来源 URL。`max_results` **不**面向模型:工具设置上限(`searchMaxResults` 配置,默认 8)并传给 seam。 |
| `web_fetch` | `url`(string) | 获取特定 URL。HTML 主体渲染为 markdown(turndown,带 GFM 表格/删除线);文本主体原样通过。非 2xx 状态会报告,而非报错。工具调用超时是部署策略(`dsh-timeout-policy`),不是模型参数。 | | `web_fetch` | `url`(string) | 获取特定 URL。HTML 主体渲染为 markdown(turndown,带 GFM 表格/删除线);文本主体原样通过。非 2xx 状态会报告,而非报错。工具调用超时是部署策略(`dsh-timeout-policy`),不是模型参数。 |
两个工具都选择并发调度,因为提供方读取会返回内容,不会修改父 agent 状态。 两个工具都选择并发调度,因为提供方读取会返回内容,不会修改父 agent(智能体)的状态。
规范化 seam 结果也是规范工具值:`WebSearchResult` 与 `WebFetchResult`。原生 renderer 保留下述答案/源与抓取主体文本;提供方搜索/主体上限仍是获取限制,而非仅呈现截断。 规范化后的 seam 结果也是标准工具值:`WebSearchResult` 与 `WebFetchResult`。原生渲染器会保留下文所述的答案、来源和抓取正文文本;提供方对搜索结果数量和正文大小的上限仍属于获取限制,而非仅用于呈现的截断。
## 配置 ## 配置
| Key | 默认值 | 含义 | | 配置键 | 默认值 | 含义 |
|---|---|---| |---|---|---|
| `search` | `true` | 注册 `web_search`。 | | `search` | `true` | 注册 `web_search`。 |
| `fetch` | `true` | 注册 `web_fetch`。 | | `fetch` | `true` | 注册 `web_fetch`。 |
| `searchMaxResults` | `8` | 一次 `web_search` 调用返回的源数量上限(seam 截断更长的提供方列表并标记)。 | | `searchMaxResults` | `8` | 一次 `web_search` 调用返回的来源数量上限(seam 截断更长的提供方列表并标记)。 |
| `fetchTimeoutMs` | `30000` | `web_fetch` 的协作式工具调用超时预算(ms)。 | | `fetchTimeoutMs` | `30000` | `web_fetch` 的协作式工具调用超时预算(ms)。 |
| `searchTimeoutMs` | `30000` | `web_search` 的协作式工具调用超时预算(ms)。 | | `searchTimeoutMs` | `30000` | `web_search` 的协作式工具调用超时预算(ms)。 |
| `fetchMaxOutputChars` | `200000` | 同步转换的源字符数与单次完整 `web_fetch` 输出的上限(状态头、渲染后的主体与页脚合并计算);主体被截断时,在能容纳的情况下附带截断提示。 | | `fetchMaxOutputChars` | `200000` | 同步转换的源字符数与单次完整 `web_fetch` 输出的上限(状态头、渲染后的主体与页脚合并计算);主体被截断时,在能容纳的情况下附带截断提示。 |
@@ -37,9 +37,9 @@
## 稳定注册 ## 稳定注册
工具注册遵循产品**启用状态**,而非后端可用性。即使选中的提供方缺失、错误配置、存在歧义或暂时不可用,工具仍保持可见;seam 在执行时解析提供方,执行以结构化 `WebError`(例如 `WEB_PROVIDER_UNAVAILABLE`、`WEB_PROVIDER_AMBIGUOUS`)失败,`ToolRegistry.execute()` 会把它转为模型可读、hook/UI 可路由的错误工具结果。这样无需把插件加载顺序、credential 状态或 HMR 时机纳入面向模型契约,也能保持模型 schema 稳定。要彻底移除 web 工具,请在此处通过配置将其禁用。 工具注册遵循产品**启用状态**,而非后端可用性。即使选中的提供方缺失、错误配置、存在歧义或暂时不可用,工具仍保持可见;seam 在执行时解析提供方,执行以结构化 `WebError`(例如 `WEB_PROVIDER_UNAVAILABLE`、`WEB_PROVIDER_AMBIGUOUS`)失败,`ToolRegistry.execute()` 会把它转为模型可读、钩子/UI 可路由的错误工具结果。这样无需把插件加载顺序、凭据状态或 HMR(热模块替换)时机纳入面向模型契约,也能保持模型 schema 稳定。要彻底移除 web 工具,请在此处通过配置将其禁用。
工具绝不会调用提供方的 `available()`,也不会枚举提供方;唯一执行路径是 `ctx.web.search()`/`ctx.web.fetch()`,提供方不可用会作为选择机制在执行时抛出的结构化 `WebError` code 到达工具。提供方选择完全留在 seam 内,只有一个 owner。 工具绝不会调用提供方的 `available()`,也不会枚举提供方;唯一执行路径是 `ctx.web.search()`/`ctx.web.fetch()`,提供方不可用时,选择机制会在执行阶段抛出结构化 `WebError`,其错误码由工具接收。提供方选择完全留在 seam 内,由单一主体负责。
## 模型体验 ## 模型体验
@@ -63,7 +63,7 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
#### Token 影响 #### Token 影响
每个通过配置启用的工具会为每次请求增加固定指引成本,即使限制隐藏了其 schema。 每个通过配置启用的工具都会为每次请求增加固定的指引 token 开销,即使限制隐藏了其 schema。
#### KV Cache 影响 #### KV Cache 影响
@@ -77,7 +77,7 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
#### Token 影响 #### Token 影响
每次请求承担固定 schema 成本;通过配置禁用会同时移除 schema 与指引,scope 限制只移除 schema。 每次请求都会产生固定的 schema token 开销;通过配置禁用会同时移除 schema 与指引,scope 限制只移除 schema。
#### KV Cache 影响 #### KV Cache 影响
@@ -87,21 +87,21 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
#### 模型看到的内容 #### 模型看到的内容
可选的提供方答案之后是 `Sources:`,再跟随数据相关、形状精确为 `- [<title-or-url>](<url>)` 的行,并可添加后缀 ` — <snippet> (<publishedAt>)`。既无答案也无源时,结果显示 `No results found.`。列表达到上限时会添加 `(Showing the first <count> sources. Refine the query for more.)`;每项结果都以 `Cite the relevant URLs above as markdown links in your answer.` 结尾。 可选的提供方答案之后是 `Sources:`,再跟随内容取决于数据且格式严格为 `- [<title-or-url>](<url>)` 的行,并可添加后缀 ` — <snippet> (<publishedAt>)`。既无答案也无来源时,结果显示 `No results found.`。列表达到上限时会添加 `(Showing the first <count> sources. Refine the query for more.)`;每个结果都以 `Cite the relevant URLs above as markdown links in your answer.` 结尾。
#### Token 影响 #### Token 影响
数据相关结果会重复发送直到压缩,源数量由 `searchMaxResults` 限制。 数据相关结果会重复发送直到压缩(compaction),来源数量由 `searchMaxResults` 限制。
#### KV Cache 影响 #### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 配置项失效。 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
### 抓取结果 ### 抓取结果
#### 模型看到的内容 #### 模型看到的内容
成功抓取的精确形状是 `Fetched <finalUrl> (HTTP <statusCode>)`、一个空行,以及提供方拥有的解码主体。发生截断时会再添加一个空行和 `(Content truncated. Fetch a more specific URL or section for the full text.)`;失败变为 `Error: <message>`。查询与 URL 保留在调用历史中。 成功抓取的精确形状是 `Fetched <finalUrl> (HTTP <statusCode>)`、一个空行,以及由提供方返回的已解码正文。发生截断时会再添加一个空行和 `(Content truncated. Fetch a more specific URL or section for the full text.)`;失败变为 `Error: <message>`。查询与 URL 保留在调用历史中。
#### Token 影响 #### Token 影响
@@ -109,7 +109,7 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
#### KV Cache 影响 #### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 配置项失效。 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
### 参数错误 ### 参数错误
@@ -123,10 +123,10 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
#### KV Cache 影响 #### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 配置项失效。 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与暂缓事项 ## 已知限制与暂缓事项
- **HTML→markdown 转换会在 GFM 无法安全表示的输入上降级**:[turndown](https://github.com/mixmark-io/turndown)(带 GFM 表格/删除线)通过真实 DOM 转换至多 `fetchMaxOutputChars` 个源字符。保守的 512 层词法守卫会将深层或嵌套有歧义的主体作为原始 HTML 直接透传,转换异常也会如此处理;表格的 `colspan` 会被忽略,因为 GFM 无法表示跨列单元格。这些限制可避免阻塞事件循环,也避免不受信任的数值属性使输出膨胀([决策记录](../../../.agents/notes/implemented/simplification/2026-07-26-turndown-for-tool-web-html-markdown.md))。 - **HTML→markdown 转换会在 GFM 无法安全表示的输入上降级**:[turndown](https://github.com/mixmark-io/turndown)(带 GFM 表格/删除线)通过真实 DOM 转换至多 `fetchMaxOutputChars` 个源字符。保守的 512 层词法守卫会将深层或嵌套有歧义的主体作为原始 HTML 直接透传,转换异常也会如此处理;表格的 `colspan` 会被忽略,因为 GFM 无法表示跨列单元格。这些限制可避免阻塞事件循环,也避免不受信任的数值属性使输出膨胀([Agent Note(agent 决策记录)](../../../.agents/notes/implemented/simplification/2026-07-26-turndown-for-tool-web-html-markdown.md))。
- **面向模型的表层有意保持最小,提升项暂缓**:`max_results` 保持为配置上限(不是模型参数),`web_fetch` 只接受 `url`(没有 `format`/`prompt`/LLM 摘要模式);两项都列为 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) 中的后续步骤。 - **面向模型的接口有意保持精简,后续扩展暂缓**:`max_results` 保持为配置上限(不是模型参数),`web_fetch` 只接受 `url`(没有 `format`/`prompt`/LLM(大语言模型)摘要模式);两项都列为 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) 中的后续步骤。
- **没有 web 专用权限策略**:两个工具都不会请求 `ctx.approval` 就直接执行;需要确认的部署必须添加 `tools/pre-execute` 策略,该包不定义持久 URL/domain 授权。 - **没有 web 专用权限策略**:两个工具都不会请求 `ctx.approval` 就直接执行;需要确认的部署必须添加 `tools/pre-execute` 策略,该包不定义持久化的 URL/域名授权。

View File

@@ -2,30 +2,30 @@
[English](README.md) | 中文 [English](README.md) | 中文
一个匿名公共 HTTP(S) `WebFetchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它获取具体 URL,返回状态码与有界解码内容。 一个匿名公共 HTTP(S) `WebFetchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它获取具体 URL,返回状态码和长度受限的解码内容。
这是一个**实现** 包:它向 `ctx.web` 注册提供方,不拥有该 key,也不注册面向模型的工具。它是函数/namespace 插件(`inject: ['web']`)。 这是一个**实现**包(package):它向 `ctx.web` 注册提供方,不拥有该键,也不注册面向模型的工具。它是函数/命名空间插件(`inject: ['web']`)。
## 职责拆分 ## 职责拆分
提供方拥有**安全资源获取**:URL 验证、HTTP 传输、重定向策略、资源兜底超时、中止传播、字节上限、charset 解码、内容类型分类与二进制拒绝。`@deepseek-ai/dsh-tool-web` 拥有**呈现**(HTML→markdown、截断格式)。非 2xx HTTP 响应是*结果*(状态码 + 解码主体),不是错误;`WebError` 只用于无法安全获取或表示资源的失败。 提供方拥有**安全资源获取**:URL 验证、HTTP 传输、重定向策略、资源兜底超时、中止传播、字节上限、charset 解码、内容类型分类与二进制拒绝。`@deepseek-ai/dsh-tool-web` 拥有**呈现**(HTML→markdown、截断格式)。非 2xx HTTP 响应是*结果*(状态码 + 解码主体),不是错误;`WebError` 只用于无法安全获取或表示资源的失败。
提供方的 `timeoutMs` 是直接 `ctx.web.fetch()` 调用方与错误配置部署的资源兜底,不是面向模型的工具调用预算。[`dsh-timeout-policy`](../../timeout/timeout-policy/README.md) 拥有 `web_fetch` 工具调用预算,并通过武装 `exec.signal` 强制执行该预算。 提供方的 `timeoutMs` 是直接 `ctx.web.fetch()` 调用方和配置有误的部署所用的资源兜底,不是面向模型的工具调用预算。[`dsh-timeout-policy`](../../timeout/timeout-policy/README.md) 拥有 `web_fetch` 工具调用预算,并让 `exec.signal` 在超时时触发,以强制执行该预算。
已交付的 web 工具部署会把提供方兜底设为高于工具预算,因此模型调用通常返回 `TOOL_TIMEOUT`。如果外层 deadline 先到达提供方,提供方报告 `WEB_ABORTED`,外层策略再将其替换为 `TOOL_TIMEOUT`。因此,`WEB_FETCH_TIMEOUT` 标识提供方预算已经耗尽的直接 seam 调用方。 已交付的 web 工具部署会把提供方兜底设为高于工具预算,因此模型调用通常返回 `TOOL_TIMEOUT`。如果外层截止期限先于提供方的兜底超时触发,提供方会报告 `WEB_ABORTED`,外层策略再将其替换为 `TOOL_TIMEOUT`。因此,`WEB_FETCH_TIMEOUT` 表明直接 seam 调用方的提供方预算已经耗尽。
## 传输卫生 ## 传输卫生
- 只接受 `http:` 和 `https:` URL;拒绝 URL 中的 credential(`WEB_BLOCKED_URL`)以及过长/格式错误的 URL(`WEB_INVALID_URL`)。 - 只接受 `http:` 和 `https:` URL;拒绝 URL 中的凭据(`WEB_BLOCKED_URL`)以及过长/格式错误的 URL(`WEB_INVALID_URL`)。
- 强制执行 URL 最大长度、响应字节上限(`WEB_FETCH_TOO_LARGE`)、解码主体字符上限、超时(`WEB_FETCH_TIMEOUT`)和重定向跳数上限。 - 强制执行 URL 最大长度、响应字节上限(`WEB_FETCH_TOO_LARGE`)、解码主体字符上限、超时(`WEB_FETCH_TIMEOUT`)和重定向跳数上限。
- 把调用方的中止信号(`WEB_ABORTED`)传播到网络请求与流式读取。 - 把调用方的中止信号(`WEB_ABORTED`)传播到网络请求与流式读取。
- 只跟随**同源** 重定向;跨源重定向以 `WEB_REDIRECT_BLOCKED` 失败,要求发起新的工具调用(沿用 Claude Code 的 WebFetch 模型)。 - 只跟随**同源**重定向;跨源重定向以 `WEB_REDIRECT_BLOCKED` 失败,要求发起新的工具调用(沿用 Claude Code 的 WebFetch 模式)。
- 发送显式的产品 `User-Agent`,绝不伪装成浏览器。 - 发送显式的产品 `User-Agent`,绝不伪装成浏览器。
- 不受支持的内容类型(例如二进制)以 `WEB_UNSUPPORTED_CONTENT_TYPE` 拒绝。 - 不受支持的内容类型(例如二进制)以 `WEB_UNSUPPORTED_CONTENT_TYPE` 拒绝。
## 配置 ## 配置
| Key | 默认值 | 含义 | | 配置键 | 默认值 | 含义 |
|---|---|---| |---|---|---|
| `maxUrlLength` | `2048` | 接受的请求 URL 最大长度。 | | `maxUrlLength` | `2048` | 接受的请求 URL 最大长度。 |
| `maxResponseBytes` | `5_000_000` | 响应主体最大字节数。 | | `maxResponseBytes` | `5_000_000` | 响应主体最大字节数。 |
@@ -38,14 +38,14 @@
## 模型体验 ## 模型体验
通过 [`dsh-tool-web`](../tool-web/README.md) 间接影响;该工具把此提供方经 `maxBodyChars` 限制的解码文本或 markdown 形状 HTML 置于抓取结果包装中,并保留提供方失败;重定向、标头与传输机制保持隐藏。 通过 [`dsh-tool-web`](../tool-web/README.md) 间接影响;该工具把此提供方经 `maxBodyChars` 限制的解码文本或 markdown 形状 HTML 置于抓取结果包装层中,并保留提供方失败;重定向、标头与传输机制保持隐藏。
#### KV Cache 影响 #### KV Cache 影响
不会直接失效;请求前缀变更由命名消费方负责。 不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与暂缓事项 ## 已知限制与暂缓事项
- **SSRF/私有网络防护暂缓**:不会阻止私有、loopback、link-local、multicast 或其他非公开目标,也不进行 DNS 解析后验证或逐跳重新验证(见 [web 能力 seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。在此功能落地前,该提供方是 SSRF 原语;能够访问敏感内部网络目标的部署**禁止启用它**。 - **SSRF/私有网络防护暂缓**:不会阻止私有、loopback、link-local、multicast 或其他非公开目标,也不进行 DNS 解析后验证或逐跳重新验证(见 [web 能力 seam Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。在此功能落地前,该提供方是 SSRF 原语;能够访问敏感内部网络目标的部署**禁止启用它**。
- **只解码文本内容**:包括 html/xhtml 与 `text/*` 加 JSON/XML 家族;缺少 `Content-Type` 或任何二进制类型都会抛出 `WEB_UNSUPPORTED_CONTENT_TYPE`,可提取文本的 PDF 解码属于明确的暂缓工作。 - **只解码文本内容**:包括 html/xhtml 与 `text/*` 加 JSON/XML 家族;缺少 `Content-Type` 或任何二进制类型都会抛出 `WEB_UNSUPPORTED_CONTENT_TYPE`,可提取文本的 PDF 解码属于明确的暂缓工作。
- **charset 只来自 `Content-Type` 标头**(默认为 UTF-8):HTML `<meta charset>` 声明会被忽略;声明但无法识别的 charset label 会抛出异常,而非回退。 - **charset 只来自 `Content-Type` 标头**(默认为 UTF-8):HTML `<meta charset>` 声明会被忽略;声明但无法识别的 charset 标签会抛出异常,而非回退。

View File

@@ -4,7 +4,7 @@
由 [DeepSeek](https://deepseek.com) 支持的 `WebSearchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它调用 DeepSeek 的 **Anthropic 兼容 Messages API**(`POST {baseURL}/messages`),启用原生 `web_search_20250305` 服务器工具,并把 DeepSeek 返回的结构化 `web_search_tool_result` 块映射为 seam 规范化的 `WebSearchResult`。 由 [DeepSeek](https://deepseek.com) 支持的 `WebSearchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它调用 DeepSeek 的 **Anthropic 兼容 Messages API**(`POST {baseURL}/messages`),启用原生 `web_search_20250305` 服务器工具,并把 DeepSeek 返回的结构化 `web_search_tool_result` 块映射为 seam 规范化的 `WebSearchResult`。
这是一个**实现** 包:它向 `ctx.web` 注册提供方,不拥有该 key,也不注册面向模型的工具。与 `@deepseek-ai/dsh-llm-deepseek` 一样,它是函数/namespace 插件(`inject: ['web']`)。Anthropic 协议形状是提供方私有细节,并**不** 使该提供方依赖 `ctx.llm`。 这是一个**实现**包(package):它向 `ctx.web` 注册提供方,不拥有该键,也不注册面向模型的工具。与 `@deepseek-ai/dsh-llm-deepseek` 一样,它是函数/命名空间插件(`inject: ['web']`)。Anthropic 协议格式是提供方私有细节,并**不**使该提供方依赖 `ctx.llm`。
## 与专用搜索端点的区别 ## 与专用搜索端点的区别
@@ -12,13 +12,13 @@ Exa 和 Perplexity 提供专用搜索端点,DeepSeek 则没有。该提供方
**严格模式**:如果响应不含 `web_search_tool_result` 块(未触发原生搜索),提供方会抛出 `WebError` `WEB_PROVIDER_ERROR`,而非降级为文本抓取;这种行为诚实且可诊断。 **严格模式**:如果响应不含 `web_search_tool_result` 块(未触发原生搜索),提供方会抛出 `WebError` `WEB_PROVIDER_ERROR`,而非降级为文本抓取;这种行为诚实且可诊断。
它复用 `$DEEPSEEK_API_KEY`(不增加 secret),但**不会** 复用 `$DEEPSEEK_BASE_URL`:搜索端点使用 Anthropic 兼容基址(`https://api.deepseek.com/anthropic/v1`),不同于 LLM 适配器使用的 chat-completions 基址(`https://api.deepseek.com`)。 它复用 `$DEEPSEEK_API_KEY`(不增加密钥),但**不会**复用 `$DEEPSEEK_BASE_URL`:搜索端点使用 Anthropic 兼容基址(`https://api.deepseek.com/anthropic/v1`),不同于大语言模型(LLM)适配器使用的 chat-completions 基址(`https://api.deepseek.com`)。
## 配置 ## 配置
| Key | 默认值 | 含义 | | 配置键 | 默认值 | 含义 |
|---|---|---| |---|---|---|
| `apiKey` | `$DEEPSEEK_API_KEY` | DeepSeek API key。为空/缺失时提供方不可用。同时作为 `x-api-key` 与 `Authorization: Bearer` 发送(官方与 Anthropic 兼容 proxy)。 | | `apiKey` | `$DEEPSEEK_API_KEY` | DeepSeek API 密钥。为空或缺失时提供方不可用。同时通过 `x-api-key` 和 `Authorization: Bearer` 发送(分别用于官方接口与 Anthropic 兼容代理)。 |
| `baseURL` | `https://api.deepseek.com/anthropic/v1` | Anthropic 兼容端点基址;追加 `/messages`。覆盖时使用 `$DEEPSEEK_SEARCH_BASE_URL` 等独立环境变量;禁止复用属于 chat-completions LLM 适配器的 `$DEEPSEEK_BASE_URL`。无法解析时提供方不可用。 | | `baseURL` | `https://api.deepseek.com/anthropic/v1` | Anthropic 兼容端点基址;追加 `/messages`。覆盖时使用 `$DEEPSEEK_SEARCH_BASE_URL` 等独立环境变量;禁止复用属于 chat-completions LLM 适配器的 `$DEEPSEEK_BASE_URL`。无法解析时提供方不可用。 |
| `model` | `deepseek-v4-flash` | Anthropic 格式模型名称。 | | `model` | `deepseek-v4-flash` | Anthropic 格式模型名称。 |
| `apiVersion` | `2023-06-01` | `anthropic-version` 标头值。 | | `apiVersion` | `2023-06-01` | `anthropic-version` 标头值。 |
@@ -47,7 +47,7 @@ DeepSeek 不返回该提供方可作为 `content` 信任的提供方生成答案
#### 模型看到的内容 #### 模型看到的内容
独立的 DeepSeek 模型会接收精确的 `Perform a web search for the query: <query>` 作为 user 文本,并收到一个原生 `web_search` 服务器工具定义。该请求不属于会话模型上下文。 独立的 DeepSeek 模型会原样接收 `Perform a web search for the query: <query>` 作为用户文本,并收到一个原生 `web_search` 服务器工具定义。该请求不属于会话模型上下文。
#### Token 影响 #### Token 影响
@@ -55,13 +55,13 @@ DeepSeek 不返回该提供方可作为 `content` 信任的提供方生成答案
#### KV Cache 影响 #### KV Cache 影响
与会话请求 cache 相互独立。辅助指令与原生工具定义可以形成稳定前缀,但查询或模型路由的每次变化都会阻止从首个差异起的复用。 与会话请求缓存相互独立。辅助指令与原生工具定义可以形成稳定前缀,但查询或模型路由的每次变化都会阻止从首个差异起的复用。
### 间接的会话工具结果 ### 间接的会话工具结果
#### 模型看到的内容 #### 模型看到的内容
通过 [`dsh-tool-web`](../tool-web/README.md),会话模型会看到结构化搜索块中去重后的 URL、标题、日期与引用 snippet;提供方文本不会作为答案受到信任。该提供方的精确失败是 `DeepSeek search aborted`、`DeepSeek search request failed: <error>`、`DeepSeek returned no web_search_tool_result blocks; the request may not have triggered native web search` 和 `DeepSeek returned an unprocessable response body: <error>`;HTTP 失败保留提供方消息。错误包装属于消费方。 通过 [`dsh-tool-web`](../tool-web/README.md),会话模型会看到结构化搜索块中去重后的 URL、标题、日期与引用 snippet;提供方文本不会作为答案受到信任。该提供方的具体错误消息为 `DeepSeek search aborted`、`DeepSeek search request failed: <error>`、`DeepSeek returned no web_search_tool_result blocks; the request may not have triggered native web search` 和 `DeepSeek returned an unprocessable response body: <error>`;HTTP 失败保留提供方消息。错误包装属于消费方。
#### Token 影响 #### Token 影响
@@ -69,11 +69,11 @@ DeepSeek 不返回该提供方可作为 `content` 信任的提供方生成答案
#### KV Cache 影响 #### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 配置项失效。 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与暂缓事项 ## 已知限制与暂缓事项
- **一次搜索需要完整的 Messages 模型轮次**:会产生延迟与生成 token,并且最多执行 `maxUses` 次服务器侧搜索;DeepSeek 不公开专用检索端点。 - **一次搜索需要完整的 Messages 模型轮次**:会产生延迟与生成 token,并且最多执行 `maxUses` 次服务器侧搜索;DeepSeek 不公开专用检索端点。
- **超量返回的源仍消耗 token**:协议没有结果数量旋钮,`maxResults` 只能由 seam 在事后截断。 - **超量返回的源仍消耗 token**:协议没有结果数量旋钮,`maxResults` 只能由 seam 在事后截断。
- **未引用的结果没有 `snippet`**:只有 `text` 块中的引用(`cited_text`)匹配其 URL 时,源才会获得 snippet。 - **未引用的结果没有 `snippet`**:只有 `text` 块中的引用(`cited_text`)匹配其 URL 时,源才会获得 snippet。
- **按错误形状分类中止**:只有 `DOMException` 且名为 `AbortError` 时才映射为 `WEB_ABORTED`;携带自定义原因的中止(例如 `dsh-timeout` 的 `TimeoutReason`)会呈现为 `WEB_PROVIDER_ERROR`。 - **中止分类基于错误结构**:只有 `DOMException` 且名为 `AbortError` 时才映射为 `WEB_ABORTED`;携带自定义原因的中止(例如 `dsh-timeout` 的 `TimeoutReason`)会呈现为 `WEB_PROVIDER_ERROR`。

View File

@@ -2,15 +2,15 @@
[English](README.md) | 中文 [English](README.md) | 中文
由 [Exa](https://exa.ai) 支持的 `WebSearchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它调用 Exa 的 `POST /search` 端点并请求 highlight 内容,把扁平 `results[]` 映射为 seam 规范化的 `WebSearchResult`。 由 [Exa](https://exa.ai) 支持的 `WebSearchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它调用 Exa 的 `POST /search` 端点并请求高亮摘要内容,把扁平 `results[]` 映射为 seam 规范化的 `WebSearchResult`。
这是一个**实现** 包:它向 `ctx.web` 注册提供方,不拥有 `ctx.web` key,也不注册面向模型的工具(后者属于 `@deepseek-ai/dsh-tool-web`)。与 `@deepseek-ai/dsh-llm-deepseek` 一样,它是函数/namespace 插件(`inject: ['web']`),负责注册后端,而非默认导出服务。 这是一个**实现**包(package):它向 `ctx.web` 注册提供方,不拥有 `ctx.web` 键,也不注册面向模型的工具(后者属于 `@deepseek-ai/dsh-tool-web`)。与 `@deepseek-ai/dsh-llm-deepseek` 一样,它是函数/命名空间插件(`inject: ['web']`),负责注册后端,而非默认导出服务。
## 配置 ## 配置
| Key | 默认值 | 含义 | | 配置键 | 默认值 | 含义 |
|---|---|---| |---|---|---|
| `apiKey` | `$EXA_API_KEY` | Exa API key。为空/缺失时提供方不可用。 | | `apiKey` | `$EXA_API_KEY` | Exa API 密钥。为空或缺失时提供方不可用。 |
| `baseURL` | `https://api.exa.ai` | 端点基址;追加 `/search`。无法解析时提供方不可用。 | | `baseURL` | `https://api.exa.ai` | 端点基址;追加 `/search`。无法解析时提供方不可用。 |
| `searchType` | `auto` | 以 Exa `type` 发送的检索模式:`auto`(由 Exa 决定)、`keyword` 或 `neural`。 | | `searchType` | `auto` | 以 Exa `type` 发送的检索模式:`auto`(由 Exa 决定)、`keyword` 或 `neural`。 |
| `numResults` | (未设置) | 请求不含 `maxResults` 时使用的默认结果数。未设置时不发送默认值。必须是正整数。 | | `numResults` | (未设置) | 请求不含 `maxResults` 时使用的默认结果数。未设置时不发送默认值。必须是正整数。 |
@@ -25,18 +25,18 @@
## 映射 ## 映射
Exa 返回扁平 `results[]`,不返回生成答案,因此省略 `content`。每项结果映射为 `WebSearchSource`:`url` ← `url`、`title` ← `title`、`snippet` ← 第一个非空 `highlights[]` 配置项(没有 highlight 的结果缺少可移植 snippet,会被丢弃)、`publishedAt` ← `publishedDate`。请求的 `maxResults` 优先于已配置的默认 `numResults`,并作为 Exa `numResults` 发送,以优化成本/延迟;最终边界由 seam 强制执行。提供方失败(HTTP 错误、网络失败、无法解析或形状错误的主体)以 `WebError` `WEB_PROVIDER_ERROR` 呈现;中止请求以 `WEB_ABORTED` 呈现。HTTP 重定向会在接触 `Location` 目标前被拒绝,并以 `WEB_PROVIDER_ERROR` 呈现。 Exa 返回扁平 `results[]`,不返回生成答案,因此省略 `content`。每项结果映射为 `WebSearchSource`:`url` ← `url`、`title` ← `title`、`snippet` ← 第一个非空的 `highlights[]` 条目(没有高亮摘要的结果缺少可移植的 snippet,会被丢弃)、`publishedAt` ← `publishedDate`。请求的 `maxResults` 优先于已配置的默认 `numResults`,并作为 Exa `numResults` 发送,以优化成本和延迟;最终上限由 seam 强制执行。提供方失败(HTTP 错误、网络失败、响应体无法解析或结构不符)以 `WebError` `WEB_PROVIDER_ERROR` 呈现;中止请求以 `WEB_ABORTED` 呈现。HTTP 重定向会在访问 `Location` 指向的目标之前被拒绝,并以 `WEB_PROVIDER_ERROR` 呈现。
## 模型体验 ## 模型体验
通过 [`dsh-tool-web`](../tool-web/README.md) 间接影响;该工具保留此提供方经 `maxResults` 限制的 URL、标题、首条 highlight 与发布日期,或将精确的 `Exa search aborted`、`Exa search request failed: <error>` 和 `Exa returned an unprocessable response body: <error>` 失败置于消费方错误包装内;生成答案与提供方私有字段不进入上下文。 通过 [`dsh-tool-web`](../tool-web/README.md) 间接影响;该工具保留此提供方经 `maxResults` 限制的 URL、标题、首条 highlight 与发布日期,或将确切的错误消息 `Exa search aborted`、`Exa search request failed: <error>` 和 `Exa returned an unprocessable response body: <error>` 置于消费方的错误包装层内;生成答案与提供方私有字段不进入上下文。
#### KV Cache 影响 #### KV Cache 影响
不会直接失效;请求前缀变更由命名消费方负责。 不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与暂缓事项 ## 已知限制与暂缓事项
- **没有非空 highlight 的结果会被整个丢弃**:没有可映射的可移植 snippet,因此返回源可能少于请求数量。 - **没有非空白高亮摘要的结果会被整个丢弃**:没有可映射的可移植 snippet,因此返回源可能少于请求数量。
- **只公开 `searchType`/`numResults`/`highlightsPerResult`**:Exa 的其他控制项(livecrawl、category、domain/date filter、全文内容)等待提供方无关 seam 字段(见 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。 - **只公开 `searchType`/`numResults`/`highlightsPerResult`**:Exa 的其他控制项(livecrawl、category、域名/日期过滤条件、全文内容)等待提供方无关 seam 字段(见 [seam Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。
- **按错误形状分类中止**:只有 `DOMException` 且名为 `AbortError` 时才映射为 `WEB_ABORTED`;携带自定义原因的中止(例如 `dsh-timeout` 的 `TimeoutReason`)会呈现为 `WEB_PROVIDER_ERROR`。 - **按错误形状分类中止**:只有 `DOMException` 且名为 `AbortError` 时才映射为 `WEB_ABORTED`;携带自定义原因的中止(例如 `dsh-timeout` 的 `TimeoutReason`)会呈现为 `WEB_PROVIDER_ERROR`。

View File

@@ -4,17 +4,17 @@
由 [Perplexity](https://perplexity.ai) 支持的 `WebSearchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它调用 Perplexity 的 OpenAI 兼容 `POST /chat/completions` 端点,把生成答案与引用映射为 seam 规范化的 `WebSearchResult`。 由 [Perplexity](https://perplexity.ai) 支持的 `WebSearchProvider`,用于 harness [web 能力 seam](../web/README.md)(`ctx.web`)。它调用 Perplexity 的 OpenAI 兼容 `POST /chat/completions` 端点,把生成答案与引用映射为 seam 规范化的 `WebSearchResult`。
这是一个**实现** 包:它向 `ctx.web` 注册提供方,不拥有该 key,也不注册面向模型的工具。与 `@deepseek-ai/dsh-llm-deepseek` 一样,它是函数/namespace 插件(`inject: ['web']`)。OpenAI 兼容协议形状是提供方私有细节,并**不** 使该提供方依赖 `ctx.llm`。 这是一个**实现**包(package):它向 `ctx.web` 注册提供方,不拥有该键,也不注册面向模型的工具。与 `@deepseek-ai/dsh-llm-deepseek` 一样,它是函数/命名空间插件(`inject: ['web']`)。OpenAI 兼容协议格式是提供方私有细节,并**不**使该提供方依赖 `ctx.llm`。
## 配置 ## 配置
| Key | 默认值 | 含义 | | 配置键 | 默认值 | 含义 |
|---|---|---| |---|---|---|
| `apiKey` | `$PERPLEXITY_API_KEY` | Perplexity API key。为空/缺失时提供方不可用。 | | `apiKey` | `$PERPLEXITY_API_KEY` | Perplexity API 密钥。为空或缺失时提供方不可用。 |
| `baseURL` | `https://api.perplexity.ai` | 端点基址;追加 `/chat/completions`。无法解析时提供方不可用。 | | `baseURL` | `https://api.perplexity.ai` | 端点基址;追加 `/chat/completions`。无法解析时提供方不可用。 |
| `model` | `sonar` | 搜索模型名称。 | | `model` | `sonar` | 搜索模型名称。 |
| `maxTokens` | `1024` | 生成答案 token 上限(`max_tokens`)。必须是正整数。 | | `maxTokens` | `1024` | 生成答案 token 上限(`max_tokens`)。必须是正整数。 |
| `searchRecency` | (未设置) | 以 `search_recency_filter` 发送的新近程度窗口:`day`、`week`、`month` 或 `year`。未设置时不发送 filter。 | | `searchRecency` | (未设置) | 以 `search_recency_filter` 发送的新近程度窗口:`day`、`week`、`month` 或 `year`。未设置时不发送过滤条件。 |
```yaml ```yaml
- id: web-search-perplexity - id: web-search-perplexity
@@ -25,7 +25,7 @@
## 映射 ## 映射
`content` ← `choices[0].message.content`(生成答案)。`sources[]` 优先使用结构化 `search_results[]`(`url`、`title`、`snippet`、`publishedAt` ← `date`),否则回退到只含 URL 的 `citations[]` 数组;仅当不存在 `search_results` 时才采取这条回退路径。这些源只携带 `url`,因此 seam 上的 `title`/`snippet`/`publishedAt` 是可选字段。提供方失败以 `WebError` `WEB_PROVIDER_ERROR` 呈现;中止请求以 `WEB_ABORTED` 呈现。HTTP 重定向会在接触 `Location` 目标前被拒绝,并以 `WEB_PROVIDER_ERROR` 呈现。Perplexity 没有结果数量控制,因此 seam 会强制执行 `maxResults`(截断 `sources[]` 并设置 `truncated`)。 `content` ← `choices[0].message.content`(生成答案)。`sources[]` 优先使用结构化 `search_results[]`(`url`、`title`、`snippet`、`publishedAt` ← `date`),否则回退到只含 URL 的 `citations[]` 数组;仅当不存在 `search_results` 时才采取这条回退路径。这些源只携带 `url`,因此 seam 上的 `title`/`snippet`/`publishedAt` 是可选字段。提供方失败以 `WebError` `WEB_PROVIDER_ERROR` 呈现;中止请求以 `WEB_ABORTED` 呈现。HTTP 重定向会在访问 `Location` 指向的目标之前被拒绝,并以 `WEB_PROVIDER_ERROR` 呈现。Perplexity 没有结果数量控制,因此 seam 会强制执行 `maxResults`(截断 `sources[]` 并设置 `truncated`)。
## 模型体验 ## 模型体验
@@ -33,7 +33,7 @@
#### 模型看到的内容 #### 模型看到的内容
独立的 Perplexity 模型通过 chat-completions 端点接收逐字的 `<query>` 作为唯一 user 消息。该请求不属于会话模型上下文。 独立的 Perplexity 模型通过 chat-completions 端点将 `<query>` 原样作为唯一用户消息接收。该请求不属于会话模型上下文。
#### Token 影响 #### Token 影响
@@ -41,25 +41,25 @@
#### KV Cache 影响 #### KV Cache 影响
与会话请求 cache 相互独立。同一模型路由下的相同查询可能复用提供方 cache;查询或路由改变会建立不同前缀。 与会话请求缓存相互独立。同一模型路由下的相同查询可能复用提供方缓存;查询或路由改变会建立不同前缀。
### 间接的会话工具结果 ### 间接的会话工具结果
#### 模型看到的内容 #### 模型看到的内容
通过 [`dsh-tool-web`](../tool-web/README.md),会话模型会看到生成答案及结构化结果元数据,或只含 URL 的引用。该提供方的精确失败是 `Perplexity search aborted`、`Perplexity search request failed: <error>` 和 `Perplexity returned an unprocessable response body: <error>`;HTTP 失败保留提供方消息。错误包装属于消费方。 通过 [`dsh-tool-web`](../tool-web/README.md),会话模型会看到生成答案及结构化结果元数据,或只含 URL 的引用。该提供方确切的错误消息为 `Perplexity search aborted`、`Perplexity search request failed: <error>` 和 `Perplexity returned an unprocessable response body: <error>`;HTTP 失败保留提供方消息。错误包装层属于消费方。
#### Token 影响 #### Token 影响
注册不会直接产生会话 token。答案与源 token 取决于数据,源数量受 seam 限制;保留的结果或错误会重复发送直到压缩。 注册不会直接产生会话 token。答案与源 token 取决于数据,源数量受 seam 限制;保留的结果或错误会重复发送,直到发生 compaction(上下文压缩)。
#### KV Cache 影响 #### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 配置项失效。 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与暂缓事项 ## 已知限制与暂缓事项
- **引用回退源只含 URL**:Perplexity 省略结构化 `search_results[]` 时,源不含 `title`/`snippet`/`publishedAt`,因此工具只渲染裸 hostname label。 - **引用回退源只含 URL**:Perplexity 省略结构化 `search_results[]` 时,源不含 `title`/`snippet`/`publishedAt`,因此工具只渲染纯主机名标签。
- **超量返回的源仍消耗 token 与延迟**:协议没有结果数量控制,`maxResults` 只能由 seam 在事后截断。 - **超量返回的来源仍会增加 token 消耗和延迟**:协议没有结果数量控制,`maxResults` 只能由 seam 在事后截断。
- **只公开 `model`/`maxTokens`/`searchRecency`**:Perplexity 的其他搜索控制项(domain filter、`web_search_options` 上下文大小、图片)等待提供方无关 seam 字段(见 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。 - **只公开 `model`/`maxTokens`/`searchRecency`**:Perplexity 的其他搜索控制项(域名过滤条件、`web_search_options` 上下文大小、图片)等待提供方无关 seam 字段(见 [seam Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。
- **按错误形状分类中止**:只有 `DOMException` 且名为 `AbortError` 时才映射为 `WEB_ABORTED`;携带自定义原因的中止(例如 `dsh-timeout` 的 `TimeoutReason`)会呈现为 `WEB_PROVIDER_ERROR`。 - **按错误形状分类中止**:只有 `DOMException` 且名为 `AbortError` 时才映射为 `WEB_ABORTED`;携带自定义原因的中止(例如 `dsh-timeout` 的 `TimeoutReason`)会呈现为 `WEB_PROVIDER_ERROR`。

View File

@@ -4,7 +4,7 @@
**web 访问 seam**:抽象 `WebService`(`ctx.web`)定义 harness 具备哪些 web 访问能力(搜索 web、抓取 URL),并通过多个提供方实现,不把模型契约绑定到某个厂商的 API 形状。 **web 访问 seam**:抽象 `WebService`(`ctx.web`)定义 harness 具备哪些 web 访问能力(搜索 web、抓取 URL),并通过多个提供方实现,不把模型契约绑定到某个厂商的 API 形状。
该包是 web 能力中负责接口的三分之一。与 bash/fs 不同,它在一个 seam 上跨越搜索与抓取两种能力,每种能力都可能有多个提供方: 该包(package)是 web 能力中负责接口的三分之一。与 bash/fs 不同,它在一个 seam 上跨越搜索与抓取两种能力,每种能力都可能有多个提供方:
| 包 | 职责 | | 包 | 职责 |
|---|---| |---|---|
@@ -14,21 +14,21 @@
| `@deepseek-ai/dsh-web-fetch-local` | 抓取实现:匿名公共 HTTP(S) | | `@deepseek-ai/dsh-web-fetch-local` | 抓取实现:匿名公共 HTTP(S) |
| `@deepseek-ai/dsh-tool-web` | 面向模型的 `web_search`/`web_fetch` 工具 schema,构建于 `ctx.web` 之上 | | `@deepseek-ai/dsh-tool-web` | 面向模型的 `web_search`/`web_fetch` 工具 schema,构建于 `ctx.web` 之上 |
搜索与抓取没有共享请求 schema 或业务逻辑,但有意共用一个 seam:`ctx.web` 是单一 web 访问中间层,拥有一项提供方选择策略、一套中止/错误词汇和一个面向产品的「该 harness 如何访问 web」配置表层。代价是成对的并行 `Search`/`Fetch` 方法;这种并行是有意设计,不是遗漏提取。 搜索与抓取没有共享请求 schema 或业务逻辑,但有意共用一个 seam:`ctx.web` 是单一 web 访问中间层,拥有一项提供方选择策略、一套中止/错误词汇和一个面向产品的“该 harness 如何访问 web”配置接口。代价是成对的并行 `Search`/`Fetch` 方法;这种并行是有意设计,并非遗漏了可抽取的共性。
## 服务 API(`ctx.web`) ## 服务 API(`ctx.web`)
| 成员 | 语义 | | 成员 | 语义 |
|---|---| |---|---|
| `registerSearchProvider(provider)`/`registerFetchProvider(provider)` | 注册后端。同一能力 kind 下 id 重复时抛出 `WebError` `WEB_DUPLICATE_PROVIDER`。返回 disposer。随调用 fiber 释放。 | | `registerSearchProvider(provider)`/`registerFetchProvider(provider)` | 注册后端。同一能力类型下 id 重复时抛出 `WebError` `WEB_DUPLICATE_PROVIDER`。返回 disposer。随调用 fiber 一并 dispose(资源释放)。 |
| `search(request, signal?)` | 解析搜索提供方并运行一次搜索。在结果上强制执行 `request.maxResults`(截断 `sources[]`,设置 `truncated`)。能力无法运行时抛出 `WebError`。 | | `search(request, signal?)` | 解析搜索提供方并运行一次搜索。在结果上强制执行 `request.maxResults`(截断 `sources[]`,设置 `truncated`)。能力无法运行时抛出 `WebError`。 |
| `fetch(request, signal?)` | 解析抓取提供方并获取一个 URL。非 2xx 响应是结果,不会抛出异常。无法安全获取或表示资源时抛出 `WebError`。 | | `fetch(request, signal?)` | 解析抓取提供方并获取一个 URL。非 2xx 响应是结果,不会抛出异常。无法安全获取或表示资源时抛出 `WebError`。 |
提供方注册的是**能力** 而非工具。`dsh-tool-web` 是面向模型名称、描述、提示词指引、JSON schema 和呈现的唯一 owner。 提供方注册的是**能力**而非工具。`dsh-tool-web` 是面向模型的名称、描述、提示词指引、JSON Schema 和呈现的唯一归属方。
## 选择 ## 选择
选择绝不依赖注册、配置或 HMR 顺序。能力要么具有显式提供方 id(配置 `searchProvider`/`fetchProvider`,或由环境变量 `$DSH_WEB_SEARCH_PROVIDER`/`$DSH_WEB_FETCH_PROVIDER` 提供相同字段),要么在恰好只注册一个可用提供方时自动选择。`search()`/`fetch()` 会在执行时解析提供方: 选择绝不依赖注册、配置或热模块替换(HMR)顺序。能力要么具有显式提供方 id(配置 `searchProvider`/`fetchProvider`,或由环境变量 `$DSH_WEB_SEARCH_PROVIDER`/`$DSH_WEB_FETCH_PROVIDER` 提供相同字段),要么在恰好只注册一个可用提供方时自动选择。`search()`/`fetch()` 会在执行时解析提供方:
| 情况 | 执行 | | 情况 | 执行 |
|---|---| |---|---|
@@ -39,23 +39,23 @@
| 无 id,没有可用提供方 | `WEB_PROVIDER_UNAVAILABLE` | | 无 id,没有可用提供方 | `WEB_PROVIDER_UNAVAILABLE` |
| 无 id,多个可用提供方 | `WEB_PROVIDER_AMBIGUOUS` | | 无 id,多个可用提供方 | `WEB_PROVIDER_AMBIGUOUS` |
失败分支会抛出 `WebError`;调用方按其结构化 code(加消息细节:缺失 id、歧义候选集合)路由。提供方自身的 `available()` 是便宜的局部检查(credential 是否存在、配置是否可解析),供执行时选择使用,且**禁止发起网络调用**;`dsh-tool-web` 永远不会调用它。工具通过 `ctx.web.search()`/`fetch()` 执行,并按抛出的 code 路由,因此提供方选择只有一个 owner。 失败分支会抛出 `WebError`;调用方按其结构化 code(加消息细节:缺失 id、歧义候选集合)路由。提供方自身的 `available()` 是便宜的局部检查(凭据是否存在、配置是否可解析),供执行时选择使用,且**禁止发起网络调用**;`dsh-tool-web` 永远不会调用它。工具通过 `ctx.web.search()`/`fetch()` 执行,并按抛出的 code 路由,因此提供方选择只有一个归属方。
## 词汇 ## 词汇
`WebSearchRequest`(`query`、`maxResults?`)→ `WebSearchResult`(`content?`、`sources[]`、`truncated`);每个 `WebSearchSource` 都有必填 `url` 与可选 `title`/`snippet`/`publishedAt`(Perplexity 引用可能只含 URL)。`WebFetchRequest`(`url`)→ `WebFetchResult`(最终 `url`、`statusCode`、`body`、`truncated`);取消作为可选的直接 `AbortSignal` 参数传给 `search()`/`fetch()`。`WebFetchBody` 是这里拥有的封闭判别联合(`html` | `text`);消费方使用 `switch` 实现穷尽检查,因此新增 kind 会破坏编译,直到处理完毕。完整契约见 `src/types.ts`,其中也包含 `WebError` code 分类体系。 `WebSearchRequest`(`query`、`maxResults?`)→ `WebSearchResult`(`content?`、`sources[]`、`truncated`);每个 `WebSearchSource` 都有必填 `url` 与可选 `title`/`snippet`/`publishedAt`(Perplexity 引用可能只含 URL)。`WebFetchRequest`(`url`)→ `WebFetchResult`(最终 `url`、`statusCode`、`body`、`truncated`);取消作为可选的直接 `AbortSignal` 参数传给 `search()`/`fetch()`。`WebFetchBody` 是这里拥有的封闭判别联合(`html` | `text`);消费方使用 `switch` 实现穷尽检查,因此新增类型会导致编译失败,直到处理完毕。完整契约见 `src/types.ts`,其中也包含 `WebError` code 分类体系。
## 模型体验 ## 模型体验
通过 `dsh-tool-web` 间接影响;该工具保留有界的规范化提供方数据,或精确的已配置提供方、提供方不可用、无提供方、多提供方及 `Error: <message>` 失败,本注册表自身不贡献提示词或 schema。 通过 `dsh-tool-web` 间接影响;该工具会保留有界的规范化提供方数据,或者原样保留以下失败:已配置的提供方缺失、提供方不可用、无提供方、存在多个提供方以及 `Error: <message>`;本注册表自身不贡献提示词或 schema。
#### KV Cache 影响 #### KV Cache 影响
不会直接失效;请求前缀变更由命名消费方负责。 不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与暂缓事项 ## 已知限制与暂缓事项
- **没有观测表层**:没有提供方变更事件或能力状态查询;可用性只能通过执行 `search()`/`fetch()` 并按抛出的 `WebError` code 路由来观测,无提供方失败是通用的 `WEB_PROVIDER_UNAVAILABLE`,不会枚举逐提供方原因(见 [Agent Note](../../../.agents/notes/archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md))。 - **没有观测接口**:没有提供方变更事件或能力状态查询;可用性只能通过执行 `search()`/`fetch()` 并按抛出的 `WebError` code 路由来观测,无提供方失败是通用的 `WEB_PROVIDER_UNAVAILABLE`,不会枚举逐提供方原因(见 [Agent Note(agent 决策记录)](../../../.agents/notes/archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md))。
- **`WebSearchRequest` 只携带 `query` + `maxResults`**:提供方无关的控制项(新近程度、domain filter、区域提示、搜索深度)暂缓至 Exa 与 Perplexity 都能诚实支持时(见 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。 - **`WebSearchRequest` 只携带 `query` + `maxResults`**:提供方无关的控制项(新近程度、域名过滤条件、区域提示、搜索深度)暂缓至 Exa 与 Perplexity 都能诚实支持时(见 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。
- **`WebFetchBody` 没有 `pdf` 分支**:可提取文本的 PDF 支持属于明确的暂缓工作;封闭联合会使新增该分支成为三个 web 包中由编译强制执行的变更。 - **`WebFetchBody` 没有 `pdf` 分支**:可提取文本的 PDF 支持属于明确的暂缓工作;封闭联合会使新增该分支成为三个 web 包中由编译强制执行的变更。
- **提供方支持的页面提取不属于 `fetch()` 范围**:Firecrawl/Tavily 风格的 `web_extract` 能力暂缓,而不会扩宽抓取 seam。 - **提供方支持的页面提取不属于 `fetch()` 范围**:Firecrawl/Tavily 风格的 `web_extract` 能力暂缓,而不会扩展抓取 seam。

View File

@@ -2,15 +2,15 @@
[English](README.md) | 中文 [English](README.md) | 中文
workflow seam:由模型编写 JavaScript 编排脚本,大规模扇出 subagent(分阶段、每个 agent(智能体)的结构化结果、并发上限),其设计参考 Claude Code 动态工作流。这是 bash 形态的能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):每个上下文只有一个引擎实现注册为 `ctx.workflows`;面向模型的工具消费它。 工作流 seam(扩展点):由模型编写 JavaScript 编排脚本,大规模扇出 subagent(分阶段、每个 agent(智能体)的结构化结果、并发上限),其设计参考 Claude Code 动态工作流。这是 bash 形态的能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):每个上下文只有一个引擎实现注册为 `ctx.workflows`;面向模型的工具使用它。
| 包 | 角色 | ctx 键 | | 包(package) | 角色 | ctx 键 |
|---|---|---| |---|---|---|
| `workflow/` | 抽象 workflow seam:服务基类、运行词汇和 `workflow/*` 事件 | `ctx.workflows` | | `workflow/` | 抽象工作流 seam:服务基类、运行词汇和 `workflow/*` 事件 | `ctx.workflows` |
| `workflow-workerthread/` | `node:worker_threads` 引擎:每次运行使用一个 worker;脚本的 vm 上下文位于 worker 内,`agent()` 通过消息端口桥接到 `ctx.subagents` | (提供 `ctx.workflows`) | | `workflow-workerthread/` | `node:worker_threads` 引擎:每次运行使用一个 worker;脚本的 vm 上下文位于 worker 内,`agent()` 通过消息端口桥接到 `ctx.subagents` | (提供 `ctx.workflows`) |
| `tool-workflow/` | 面向模型的 `workflow` 工具,基于 `ctx.workflows` | (注册到 `ctx.tools`) | | `tool-workflow/` | 面向模型的 `workflow` 工具,基于 `ctx.workflows` | (注册到 `ctx.tools`) |
| `tool-ralph/` | 基于 `ctx.workflows` 和全新结构化输出 subagent 提供方的固定全新 agent Ralph 策略 | (注册到 `ctx.tools`) | | `tool-ralph/` | 基于 `ctx.workflows` 和全新结构化输出 subagent 提供方的固定全新 agent Ralph 策略 | (注册到 `ctx.tools`) |
接口位于 `workflow/workflow/`。引擎的 `agent()` 钩子使用 [subagent seam](../subagent/README.md)(任何已注册提供方;随产品交付的示例使用 `spawn`),`agent({ schema })` 则使用进程内后端实现的结构化输出支持。worker thread 隔离的是脚本:宿主绝不会被它阻塞,已取消运行经过宽限时间后的终止也会实际生效;但它不是安全边界。如果将来确有需要,可以在同一接口后替换为 isolated-vm/独立进程引擎,以实现真正的沙箱隔离。 接口位于 `workflow/workflow/`。引擎的 `agent()` 钩子使用 [subagent seam](../subagent/README.md)(任何已注册提供方;随产品交付的示例使用 `spawn`),`agent({ schema })` 则使用进程内后端实现的结构化输出支持。worker thread 隔离的是脚本:宿主绝不会被它阻塞,已取消运行经过宽限时间后的终止也会实际生效;但它不是安全边界。如果将来确有需要,可以在同一接口背后换用 isolated-vm/独立进程引擎,以实现真正的沙箱隔离。
通用脚本引擎的决策和延期工作见[动态工作流 Agent Note](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。独立的 [Ralph 消费方](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)会固定脚本和全新提供方策略,而不是再添加一个引擎或 agent loop(智能体循环)模式。 通用脚本引擎的决策和暂缓事项见[动态工作流 Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。独立的 [Ralph 消费方](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)会固定脚本和全新提供方策略,而不是再添加一个引擎或 agent loop(智能体循环)模式。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文 [English](README.md) | 中文
面向模型的 `ralph` 工具运行固定的前台工作流,把一个不可变目标依次交给多个全新子 agent(智能体)。它展示如何把专用编排策略实现为基于 [`ctx.workflows`](../workflow/README.md) 和 [`ctx.subagents`](../../subagent/subagent/README.md) 的普通插件:不会向 `agent-loop` 添加 Ralph 模式或全新 agent 循环,同会话的[目标领域](../../goal/goal/README.md)也保持独立。政策和延期工作由 [Ralph Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)负责。 面向模型的 `ralph` 工具运行固定的前台工作流,把一个不可变目标依次交给多个全新子 agent(智能体)。它展示如何把专用编排策略实现为基于 [`ctx.workflows`](../workflow/README.md) 和 [`ctx.subagents`](../../subagent/subagent/README.md) 的普通插件:不会向 `agent-loop` 添加 Ralph 模式或全新 agent loop(智能体循环),同会话的[目标领域](../../goal/goal/README.md)也保持独立。策略和暂缓事项由 [Ralph Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)负责。
## 契约 ## 契约
@@ -16,7 +16,7 @@
## 生命周期与取消 ## 生命周期与取消
调用方 agent 是每个全新子 agent 的父级,因此会保留 cwd 和谱系,但不会复制其对话。`exec.signal` 进入工作流引擎,同时也桥接到 `run.cancel()`,确保实现相互独立。工具等待 `run.result` 并调用 `run.dispose()`,后一个调用位于 `finally` 中,因此取消的父级步骤会等到引擎完成有界终止且子 agent 完全停稳后才返回。 调用方 agent 是每个全新子 agent 的父级,因此会保留 cwd 和谱系,但不会复制其对话。`exec.signal` 进入工作流引擎,同时也桥接到 `run.cancel()`,以便不依赖具体实现。工具等待 `run.result` 并调用 `run.dispose()`,后一个调用位于 `finally` 中,因此取消的父级步骤会等到引擎完成有界终止且子 agent 完全停稳后才返回。
## 渲染意图 ## 渲染意图
@@ -31,7 +31,7 @@
| `maxHandoffChars` | `16384` | 一份 Round 报告序列化后的最大字符数。 | | `maxHandoffChars` | `16384` | 一份 Round 报告序列化后的最大字符数。 |
| `maxResultChars` | `16384` | 返回给父级的完整成功结果最大字符数。 | | `maxResultChars` | `16384` | 返回给父级的完整成功结果最大字符数。 |
插件应用时会规范化并校验所有配置值;直接应用、未经过 Loader schema 规范化的情况也包括在内。每次调用前都会立即解析提供方能力,因为提供方注册可能随插件生命周期和 HMR(热模块替换)变化。 插件应用时会规范化并校验所有配置值,也包括绕过 Loader schema 规范化而直接应用的情况。每次调用前都会立即解析提供方能力,因为提供方注册可能随插件生命周期和热模块替换(HMR)变化。
## 模型体验 ## 模型体验
@@ -49,21 +49,21 @@ Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop o
#### Token 影响 #### Token 影响
插件启用期间,每个请求支付少量固定指导成本。 插件启用期间,每个请求都会产生少量固定的指导 token 开销。
#### KV Cache 影响 #### KV Cache 影响
只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose(资源释放)可能从该提示词段开始使复用失效。 只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose(资源释放)可能会使从该提示词段起的缓存复用失效。
### 工具 schema ### 工具 schema
#### 模型看到的内容 #### 模型看到的内容
已生成的 [`ralph` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-ralph)公开一个必填 `objective` 字符串和一个可选 `maxRounds` 数字。提供方选择、交接大小、报告 schema、工作流脚本和编排行为均由部署拥有,不在调用接口中。 已生成的 [`ralph` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-ralph)公开一个必填 `objective` 字符串和一个可选 `maxRounds` 数字。提供方选择、交接大小、报告 schema、工作流脚本和编排行为均由部署侧控制,不在调用接口中。
#### Token 影响 #### Token 影响
工具可见的每个请求都会支付少量固定 schema 成本。 工具可见时,每个请求都会产生少量固定的 schema token 开销。
#### KV Cache 影响 #### KV Cache 影响
@@ -83,11 +83,11 @@ Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop o
每个全新子 agent 都有独立的请求缓存。父级结果追加在可复用请求前缀之后。 每个全新子 agent 都有独立的请求缓存。父级结果追加在可复用请求前缀之后。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **完成由 worker 自行声明**:没有独立的评估器或验证器判断目标是否实际完成;评估器政策及评估器驱动的延续均延期处理。 - **完成由 worker 自行声明**:没有独立的评估器或验证器判断目标是否实际完成;评估器策略及评估器驱动的延续均暂缓处理。
- **仅支持前台**:没有 task id、后台收集、进程恢复检查点、调度器或基于墙上时钟的启动政策。 - **仅支持前台**:没有 task id、后台收集、进程恢复检查点、调度器或基于实际时间的启动策略。
- **工作区是唯一的跨 Round 长期记忆**:一份有界报告作为显式交接内容,每个子 agent 结束后,未提交的对话推理都会消失。 - **工作区是唯一的跨 Round 长期记忆**:一份有界报告作为显式交接内容,每个子 agent 结束后,未提交的对话推理都会消失。
- **一个 Round 对应一个全新子 agent**:Round 内没有扇出、模型/提供方切换、fork 上下文或由模型调用选择的提供方。 - **一个 Round 对应一个全新子 agent**:Round 内没有扇出、模型/提供方切换、fork 上下文或由模型调用选择的提供方。
- **普通子 agent 失败会终止运行**:固定脚本报告失败的 Round 和上一次成功交接,但不会重试;致命的工作流基础设施失败可能在该状态返回前结束。 - **普通子 agent 失败会终止运行**:固定脚本报告失败的 Round 和上一次成功交接,但不会重试;致命的工作流基础设施失败可能在该状态返回前结束。
- **聚合工作量仅受 Round 数量限制**:token、价格和已用时间预算均延期处理。 - **聚合工作量仅受 Round 数量限制**:token、价格和耗时预算均暂缓处理。

View File

@@ -2,26 +2,26 @@
[English](README.md) | 中文 [English](README.md) | 中文
面向模型的 **`workflow` 工具**:运行一段扇出 subagent 的 JavaScript 编排脚本,并返回脚本的最终值。本包负责基于 [`ctx.workflows`](../workflow/README.md) 塑造 schema 和生命周期;脚本解析、执行、上限与取消位于 seam 之后,消费方继续拥有面向父级的 schema 和结果包络。 面向模型的 **`workflow` 工具**:运行一段扇出 subagent 的 JavaScript 编排脚本,并返回脚本的最终值。本包(package)负责基于 [`ctx.workflows`](../workflow/README.md) 塑造 schema 和生命周期;脚本解析、执行、上限与取消位于 seam 之后,消费方仍负责面向父级的 schema 和结果包络。
## 模型看到的内容 ## 模型看到的内容
工具有三个参数:`meta`(必需的身份数据:`name`、`description` 和可选的进度注解)、`script`(必需的纯 JavaScript 函数体,不含 `export const meta` 语句;工具描述包含完整的编写契约)以及 `args`(可选 JSON 对象,作为全局变量 `args` 向脚本公开;裸列表应包装到字段中,使协议 schema 如实表达形态)。插件还会贡献一个 `tool:<toolName>` 系统提示词段,其中包含使用政策:只有用户明确要求工作流/大型编排时才使用该工具;一两项委派优先使用普通 subagent 调用。这遵循工具指导随工具插件交付、绝不放入部署 persona 的约定。 工具有三个参数:`meta`(必需的身份数据:`name`、`description` 和可选的进度注解)、`script`(必需的纯 JavaScript 脚本体,不含 `export const meta` 语句;工具描述包含完整的编写契约)以及 `args`(可选 JSON 对象,作为全局变量 `args` 向脚本公开;裸列表应包装到字段中,使协议 schema 如实表达形态)。插件还会贡献一个 `tool:<toolName>` 系统提示词段,其中包含使用策略:只有用户明确要求工作流/大型编排时才使用该工具;一两项委派优先使用普通 subagent 调用。这遵循工具指导随工具插件交付、绝不放入部署 persona 的约定。
## 生命周期 ## 生命周期
当前版本采用同步收集(类似 [`dsh-tool-subagent`](../../subagent/tool-subagent/README.md)):`execute` 启动运行并等待 `run.result`;这些操作位于 `try/finally` 中,该结构总会 dispose 运行,使脚本及其子 agent(智能体)在每条路径上完全停稳。`exec.signal` 会桥接到 `run.cancel()`,包括启动前已经中止的情况。非 `completed` 结束原因会映射为报告原因的 `isError` 结果,绝不会把局部输出当作成功;`start()` 同步抛出的解析/meta 失败会变成模型可据以修正的 `isError`。完成时返回规范值 `{ runId, agentsStarted, result }`;Native 渲染器保留 meta 名称、agent 数量和 JSON 值,只会在 `maxResultChars` 处截断该投影。 当前版本采用同步收集(类似 [`dsh-tool-subagent`](../../subagent/tool-subagent/README.md)):`execute` 启动运行并等待 `run.result`;这些操作位于 `try/finally` 中,该结构总会 dispose(资源释放)运行,使脚本及其子 agent(智能体)在每条路径上完全停稳。`exec.signal` 会桥接到 `run.cancel()`,包括启动前已经中止的情况。非 `completed` 结束原因会映射为报告原因的 `isError` 结果,绝不会把局部输出当作成功;`start()` 同步抛出的解析/meta 失败会变成模型可据以修正的 `isError`。完成时返回规范值 `{ runId, agentsStarted, result }`;Native 渲染器保留 meta 名称、agent 数量和 JSON 值,只会在 `maxResultChars` 处截断该投影。
## 渲染意图 ## 渲染意图
渲染意图预先确定(见[渲染意图 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)):使用一个 `generic` 卡片,标题为 `workflow: <meta.name>`,直接从 `args.meta.name` 读取(呈现是参数的纯函数,不要求引擎解析);脚本文本作为 `rawInput` 携带。结果继续使用 generic 卡片。 渲染意图预先确定(见[渲染意图 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)):使用一个 `generic` 卡片,标题为 `workflow: <meta.name>`,直接从 `args.meta.name` 读取(呈现是参数的纯函数,不要求引擎解析);脚本文本作为 `rawInput` 携带。结果继续使用 generic 卡片。
## 配置 ## 配置
| 键 | 默认值 | 含义 | | 键 | 默认值 | 含义 |
|---|---|---| |---|---|---|
| `toolName` | `workflow` | 要注册的面向模型工具名称。 | | `toolName` | `workflow` | 要注册的面向模型工具名称。 |
| `maxResultChars` | `50000` | 渲染结果上限;更长的 JSON 会连同提示一起截断。 | | `maxResultChars` | `50000` | 渲染结果上限;更长的 JSON 会被截断并附上提示。 |
## 模型体验 ## 模型体验
@@ -29,9 +29,9 @@
#### 模型看到的内容 #### 模型看到的内容
在该插件的注册作用域内,每个父级请求都会收到下方的 workflow 指导。作用域工具限制可以隐藏 schema,而不移除这段独立注册的指导。 在该插件的注册作用域内,每个父级请求都会收到下方的工作流指导。作用域工具限制可以隐藏 schema,而不移除这段独立注册的指导。
##### Workflow 指导 ##### 工作流指导
```markdown ```markdown
Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls. Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.
@@ -39,25 +39,25 @@ Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for
#### Token 影响 #### Token 影响
插件启用期间,每个请求支付少量固定指导成本。 插件启用期间,每个请求都会产生少量固定的指导 token 开销。
#### KV Cache 影响 #### KV Cache 影响
只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose(资源释放)可能从该提示词段开始使复用失效。 只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose 可能会使从该提示词段起的缓存复用失效。
### 工具 schema ### 工具 schema
#### 模型看到的内容 #### 模型看到的内容
工具可见时,已生成的默认 [`workflow` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-workflow)包含完整的 JavaScript 钩子与元数据契约;`toolName` 可以重命名该定义,模型会提交脚本、元数据和可选 args。 工具可见时,已生成的默认 [`workflow` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-workflow) 包含完整的 JavaScript 钩子与元数据契约;`toolName` 可以重命名该定义,模型会提交脚本、元数据和可选 args。
#### Token 影响 #### Token 影响
工具可见的每个请求都会支付较大的固定 schema 成本。 工具可见时,每个请求都会产生较大的固定 schema token 开销。
#### KV Cache 影响 #### KV Cache 影响
只要 `toolName`、定义和可见性不变,前缀就保持稳定。重命名、插件生命周期或作用域限制可能从该 schema 开始使复用失效。 只要 `toolName`、定义和可见性不变,前缀就保持稳定。重命名、插件生命周期或作用域限制可能会使从该 schema 起的缓存复用失效。
### 工具调用历史与结果 ### 工具调用历史与结果
@@ -67,14 +67,14 @@ Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for
#### Token 影响 #### Token 影响
调用 token 可能很多,并会保留到上下文压缩(compaction)为止。结果渲染受 `maxResultChars` 限制;子模型 token 与父级保留的上下文相互独立。 调用 token 可能很多,并会保留到 compaction(上下文压缩)为止。结果渲染受 `maxResultChars` 限制;子模型 token 与父级保留的上下文相互独立。
#### KV Cache 影响 #### KV Cache 影响
仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。 仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **父级轮次会阻塞到整个工作流结算**:没有后台启动/轮询接口,取消会把局部输出作为错误丢弃。 - **父级轮次会阻塞到整个工作流结算**:没有后台启动/轮询接口,取消会把局部输出作为错误丢弃。
- **`args` 必须是对象,Native 结果文本有界**:调用方把顶层数组/标量包装到字段中;规范工作流结果保持完整,超过 `maxResultChars` 的 JSON 会在面向模型的投影中截断,而不是存入检索句柄之后。 - **`args` 必须是对象,Native 结果文本有界**:调用方把顶层数组/标量包装到字段中;规范工作流结果保持完整,超过 `maxResultChars` 的 JSON 会在面向模型的投影中截断,而不是存储在检索句柄背后。
- **每次工具注册的工作流政策固定**:提供方选择、上限和工具名称属于部署配置,不是模型调用参数。 - **每次工具注册的工作流策略固定**:提供方选择、上限和工具名称属于部署配置,不是模型调用参数。

View File

@@ -2,9 +2,9 @@
[English](README.md) | 中文 [English](README.md) | 中文
本包为 `WorkflowService` 提供实现,每次运行使用一个 Node worker thread。worker 执行编排脚本;子 agent(智能体)留在宿主上,通过带类型的宿主/worker 协议访问 `ctx.subagents`。 本包(package)为 `WorkflowService` 提供实现,每次运行使用一个 Node worker thread。worker 执行编排脚本;子 agent(智能体)留在宿主上,脚本通过带类型的宿主/worker 协议经由 `ctx.subagents` 访问它们。
包根目录默认导出引擎插件及其 `Config`;worker 协议、运行时和会话模块均为实现私有。操作入口 `./worker` 仍是引擎的派生目标。 包根目录默认导出引擎插件及其 `Config`;worker 协议、运行时和会话模块均为实现私有。操作入口 `./worker` 仍是引擎的 spawn 目标。
这种拆分只有一个主要目的:同步脚本循环不能阻塞 harness 事件循环,忽略取消的脚本可以连同其 worker 一起终止。它不是安全沙箱。 这种拆分只有一个主要目的:同步脚本循环不能阻塞 harness 事件循环,忽略取消的脚本可以连同其 worker 一起终止。它不是安全沙箱。
@@ -16,14 +16,14 @@ worker 仍提供实用的隔离:
- 脚本 CPU 工作和同步自旋不会占用宿主事件循环; - 脚本 CPU 工作和同步自旋不会占用宿主事件循环;
- `worker.terminate()` 为 dispose(资源释放)提供真实的最终停止手段; - `worker.terminate()` 为 dispose(资源释放)提供真实的最终停止手段;
- 除未构建 loader 的管道变量外,worker 以空环境启动,因此环境凭据不会通过 `process.env` 跨越边界; - 除未构建 loader 所需的衔接配置外,worker 以空环境启动,因此环境凭据不会通过 `process.env` 跨越边界;
- 宿主/worker 消息使用结构化克隆数据,并在脚本边界执行普通 JSON 校验。 - 宿主/worker 消息使用结构化克隆数据,并在脚本边界执行普通 JSON 校验。
真正的不可信脚本沙箱需要在同一 workflow seam 后采用不同引擎。 真正的不可信脚本沙箱需要在同一工作流 seam(扩展点)背后采用不同引擎。
## 脚本契约 ## 脚本契约
工作流的 `meta` 是宿主提供的数据,而不是待求值的脚本文本。引擎会校验必需的 `name` 和 `description`、拒绝未知字段,并在返回运行前检查函数体能否解析。 工作流的 `meta` 是宿主提供的数据,而不是待求值的脚本文本。引擎会校验必需的 `name` 和 `description`、拒绝未知字段,并在返回运行前检查脚本正文能否解析。
在 worker 内,脚本会收到 `args` 以及以下钩子: 在 worker 内,脚本会收到 `args` 以及以下钩子:
@@ -32,16 +32,16 @@ worker 仍提供实用的隔离:
- `pipeline(items, ...stages)` 在没有跨阶段屏障的情况下传递 `(previous, item, index)`; - `pipeline(items, ...stages)` 在没有跨阶段屏障的情况下传递 `(previous, item, index)`;
- `phase(title)` 和 `log(message)` 发出观察器叙述。 - `phase(title)` 和 `log(message)` 发出观察器叙述。
未知选项、格式错误的参数、不支持的 schema、触发的上限、提供方启动失败和基础设施结果失败都属于致命工作流错误。有意不注入 timer、文件系统 API 或 Node 全局变量,但上述信任注意事项仍然适用。 未知选项、格式错误的参数、不支持的 schema、超出上限、提供方启动失败和基础设施结果失败都属于致命工作流错误。有意不注入 timer、文件系统 API 或 Node 全局变量,但上述信任注意事项仍然适用。
## 运行顺序 ## 运行顺序
`start()` 会校验 meta、解析函数体、解析一个已注册且规范化的提供方路由,并解析每次运行的子 agent 总数上限,然后才创建 worker 或发布 `workflow/start`。请求的 `maxTotalAgents` 必须是正安全整数,且不能超过引擎配置的部署上限。源代码模式通过 data URL bootstrap 安装 TypeScript 转换;构建模式把同级 `lib/worker.cjs` 作为文件系统路径传入,因为 pkg 的 VFS 钩子要求 CommonJS。两者都能在普通 Node 下运行。ready/go 握手可以避免启动信号取消与 worker 启动发生竞态,导致脚本最初的同步片段被执行。 `start()` 会校验 meta、解析脚本正文、解析一个已注册且规范化的提供方路由,并解析每次运行的子 agent 总数上限,然后才创建 worker 或发布 `workflow/start`。请求的 `maxTotalAgents` 必须是正安全整数,且不能超过引擎配置的部署上限。源代码模式通过 data URL bootstrap 安装 TypeScript 转换;构建模式把同级 `lib/worker.cjs` 作为文件系统路径传入,因为 pkg 的虚拟文件系统(VFS)钩子要求 CommonJS。两者都能在普通 Node 下运行。ready/go 握手可以避免启动信号取消与 worker 启动发生竞态,导致脚本最初的同步片段被执行。
对于每次 `agent()` 调用: 对于每次 `agent()` 调用:
1. worker 发送 `child-start`,其中包含普通数据提示词和选项。 1. worker 发送 `child-start`,其中包含普通数据提示词和选项。
2. 宿主通过异步 `SubagentService.start` 调用启动请求中的提供方覆盖值,否则调用已配置提供方;调用会传入工作流父级和每次运行唯一的规范中止信号。提供方选择应用于该次运行的每个子 agent,对脚本不可见。 2. 宿主通过异步 `SubagentService.start` 调用启动请求中指定的提供方,否则调用已配置的提供方;调用会传入工作流父级和该次运行共用的唯一中止信号。提供方选择应用于该次运行的每个子 agent,对脚本不可见。
3. 如果启动被拒绝,宿主会发送 `child-start-error`;提供方启动已经完全停稳,不会发出子 agent 生命周期事件。 3. 如果启动被拒绝,宿主会发送 `child-start-error`;提供方启动已经完全停稳,不会发出子 agent 生命周期事件。
4. 如果启动兑现时工作流仍接纳工作,宿主会记录该运行、观察 `result`,然后发送 `child-started`。即使结果已经结算,也只会随后转发,以保持先启动、后结果的顺序。 4. 如果启动兑现时工作流仍接纳工作,宿主会记录该运行、观察 `result`,然后发送 `child-started`。即使结果已经结算,也只会随后转发,以保持先启动、后结果的顺序。
5. worker 发出成对的 `workflow/agent-start` 和 `workflow/agent-end` 叙述,并在收集后请求 dispose 子 agent。 5. worker 发出成对的 `workflow/agent-start` 和 `workflow/agent-end` 叙述,并在收集后请求 dispose 子 agent。
@@ -56,9 +56,9 @@ worker 仍提供实用的隔离:
## 取消与 dispose ## 取消与 dispose
`WorkflowRun.cancel()` 会记录第一个原因、通知 worker 取消、中止每个待处理及已发布子 agent 共享的唯一信号,并启动 `disposeGraceMs` timer。worker 钩子会在下次 await 时抛出 `CANCELLED`。如果运行到期限仍未结算,宿主会将其以已取消状态兑现、为悬空的子 agent 生命周期事件配对,并终止 worker。 `WorkflowRun.cancel()` 会记录第一个原因、通知 worker 取消、中止每个待处理及已发布子 agent 共享的唯一信号,并启动 `disposeGraceMs` 定时器。worker 钩子会在下次 await 时抛出 `CANCELLED`。如果运行到期限仍未结算,宿主会将其以已取消状态兑现、为悬空的子 agent 生命周期事件配对,并终止 worker。
subagent seam 只有一个取消通道:请求信号。不存在单独的子 agent 取消 RPC。已发布子 agent 使用 `run.dispose()` 清理;待处理提供方启动在其 promise 拒绝或兑现前仍由提供方拥有。 subagent seam 只有一个取消通道:请求信号。不存在单独的子 agent 取消 RPC。已发布子 agent 使用 `run.dispose()` 清理;待处理的提供方启动在其 promise 拒绝或兑现前仍由提供方负责。
正常结算也会中止待处理启动,并在结果对外结算前开始 dispose 所有已发布但无需等待的子 agent。宿主的完全停稳条件同时包括待处理启动和已发布子 agent 的 dispose,因此清理不会遗漏异步启动事务。 正常结算也会中止待处理启动,并在结果对外结算前开始 dispose 所有已发布但无需等待的子 agent。宿主的完全停稳条件同时包括待处理启动和已发布子 agent 的 dispose,因此清理不会遗漏异步启动事务。
@@ -66,9 +66,9 @@ subagent seam 只有一个取消通道:请求信号。不存在单独的子 ag
## 结果与事件保证 ## 结果与事件保证
在宿主主张点,终态结果遵循先到者胜。已接受的外部取消会覆盖后到的非取消 worker 结果;先完成主张的结果或 worker 死亡不能被可重入清理回调改写。 在宿主的结果确认点,终态结果遵循先到者胜。已接受的外部取消会覆盖后到的非取消 worker 结果;先完成确认的结果或 worker 死亡不能被可重入清理回调改写。
worker 错误、消息失败或提前退出会在清理前关闭消息接纳,然后以 `error` 兑现;如果取消已经拥有该运行,则不覆盖取消。后到的排队消息无法在该逻辑边界后创建子 agent 或发出叙述。 worker 错误、消息失败或提前退出会在清理前关闭消息接纳,然后以 `error` 兑现;如果取消已经接管该运行,则不覆盖取消。后到的排队消息无法在该逻辑边界后创建子 agent 或发出叙述。
宿主会维护已转发子 agent 启动的台账。优雅退出的 worker 会提供对应的结束事件;死亡或强制终止会把缺失的结束事件合成为已取消。因此,每个已转发的 `workflow/agent-start` 都会且只会配对一次,不过已经到达的工作流结果之后的清理可能稍后才完成。 宿主会维护已转发子 agent 启动的台账。优雅退出的 worker 会提供对应的结束事件;死亡或强制终止会把缺失的结束事件合成为已取消。因此,每个已转发的 `workflow/agent-start` 都会且只会配对一次,不过已经到达的工作流结果之后的清理可能稍后才完成。
@@ -83,7 +83,7 @@ worker 错误、消息失败或提前退出会在清理前关闭消息接纳,
| `syncTimeoutMs` | `5000` | 脚本最初同步片段的 VM 超时时间。 | | `syncTimeoutMs` | `5000` | 脚本最初同步片段的 VM 超时时间。 |
| `disposeGraceMs` | `5000` | 强制结算/终止之前的期限,也是公开 dispose 的期限。 | | `disposeGraceMs` | `5000` | 强制结算/终止之前的期限,也是公开 dispose 的期限。 |
所属消费方可以为一次运行设置 `WorkflowStartRequest.subagentProvider` 和 `WorkflowStartRequest.maxTotalAgents`。它们属于引擎级政策,不是脚本钩子或面向模型的选项;普通 `workflow` 工具不会设置两者。每次运行的子 agent 总数上限可以降低、但绝不能提高已配置的 `maxTotalAgents` 上限。 负责该引擎的消费方可以为一次运行设置 `WorkflowStartRequest.subagentProvider` 和 `WorkflowStartRequest.maxTotalAgents`。它们属于引擎级策略,不是脚本钩子或面向模型的选项;普通 `workflow` 工具不会设置两者。每次运行的子 agent 总数上限可以降低、但绝不能提高已配置的 `maxTotalAgents` 上限。
## 模型体验 ## 模型体验
@@ -91,7 +91,7 @@ worker 错误、消息失败或提前退出会在清理前关闭消息接纳,
#### 模型看到的内容 #### 模型看到的内容
脚本每次调用 `agent()`,都会把提示词逐字发送给 subagent 提供方,并附带可选模型或结构化输出 schema。每个子 agent 看到该提供方自己的上下文;phase 和 log 叙述只留在观察器事件中。 脚本每次调用 `agent()`,都会把提示词原样发送给 subagent 提供方,并附带可选模型或结构化输出 schema。每个子 agent 看到该提供方自己的上下文;phase 和 log 叙述只留在观察器事件中。
#### Token 影响 #### Token 影响
@@ -109,16 +109,16 @@ worker 错误、消息失败或提前退出会在清理前关闭消息接纳,
#### Token 影响 #### Token 影响
本引擎不会直接向父级添加 token。最终结果大小由工具消费方限制,并保留到上下文压缩(compaction)为止。 本引擎不会直接向父级添加 token。最终结果大小由工具消费方限制,并保留到 compaction(上下文压缩)为止。
#### KV Cache 影响 #### KV Cache 影响
仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。 仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与延期工作 ## 已知限制与暂缓事项
- **worker/vm 不是安全边界**:模型编写的代码可以逃逸 `node:vm` 并取得 worker 的进程权限;不可信代码部署需要独立进程或容器引擎。 - **worker/vm 不是安全边界**:模型编写的代码可以逃逸 `node:vm` 并取得 worker 的进程权限;不可信代码部署需要独立进程或容器引擎。
- **每次运行都要支付一个 worker thread 的成本**:没有池、预热运行时或跨运行脚本缓存。 - **每次运行都要支付一个 worker thread 的成本**:没有池、预热运行时或跨运行脚本缓存。
- **不注入环境 timer、文件系统或网络,但逃逸代码仍可访问 Node**:缺失的全局变量用于保证 API 可移植性,而非隔离。 - **不注入默认可用的定时器、文件系统或网络,但逃逸代码仍可访问 Node**:这些缺失的全局变量属于可移植性 API 设计,而非隔离措施。
- **终止只能报告宿主观察到的启动**:`agentsStarted` 不包括仍在 worker 侧排队等待并发、且在强制终止后无法得知的调用。 - **终止只能报告宿主观察到的启动**:`agentsStarted` 不包括因并发限制仍在 worker 侧排队、且在强制终止后无法得知的调用。
- **跨 realm 错误在脚本内无法通过 `instanceof Error`**:工作流作者必须根据 `name` 和 `code` 等稳定字段分支。 - **跨 realm 错误在脚本内无法通过 `instanceof Error`**:工作流作者必须根据 `name` 和 `code` 等稳定字段分支。