Joye Dev

Back

Voyager 业务线学习:Agent 图片任务的源资产解析、可恢复执行与终态错误契约

这条业务线解决的是 Agent 在聊天中处理图片时的“输入资产不是普通 workspace asset”问题,同时把一次图片任务变成可恢复、可去重、可正确反馈的运行时流程


source_automation: voyager-merged-pr run_date: 2026-08-03 anchor_pr_number: 5507 pr_number: 5507 pr_title: “Handle missing source assets in image tasks gracefully” pr_url: https://github.com/adastralab-ai/voyager/pull/5507 author: “Jason Tan / banchichen” merged_at: “2026-08-03T03:20:58Z” modules:

  • backend/workers/agent/src/goApi.ts
  • backend/workers/agent/src/goApi.test.ts
  • backend/workers/agent/src/tools/imageTaskTool.ts
  • backend/workers/agent/src/tools/imageTaskTool.test.ts files_changed: 4 changed_files:
  • backend/workers/agent/src/goApi.test.ts
  • backend/workers/agent/src/goApi.ts
  • backend/workers/agent/src/tools/imageTaskTool.test.ts
  • backend/workers/agent/src/tools/imageTaskTool.ts learning_tags:
  • agent-runtime
  • image-task
  • source-asset-lifecycle
  • attachment-promotion
  • terminal-result
  • no-retry
  • idempotency
  • billing-safety
  • api-error-contract
  • image-delivery
  • test-strategy business_line: “Agent 图片任务的源资产解析、可恢复执行与终态错误契约” related_prs: [4318, 4516, 5422, 5461, 5506] related_prior_prs: [4516] line_stage: “请求内容哈希重挂接 (#4318) -> 终态失败由 throw 改为结构化 result (#4516) -> 图片结果统一 delivery seam (#5422) -> refs/source op 统一分类 (#5461) -> remove_background 补 attachment promotion (#5506) -> 缺失 source asset 在提交门禁处可纠正终止 (#5507)” open_questions:
  • “toSubmitResult 是多个 image endpoint 共用的 notFound 映射;当前依赖坏 reference 会被 Go 包装成 500 的实现细节,未来若 reference 直接返回 code 102,可能被误标成 source_asset_not_found。”
  • “未知 referenceAssetId 当前仍可能走 wrapped APIError -> 500 并记录 server fault;需把 reference lookup 前置到 Go submit 之前,并避免 debit + refund 循环。”
  • “source asset 在任务已 submitted 后被删除或权限变化时,当前主要靠 wait 的 terminal error 或 resolve failure 处理;需要验证 delivery slot、pending record 和用户提示的组合。”
  • “#5506 的 attachment promoter 在 uploadAsset 成功与 attachmentAssetStore.set 之间缺少跨重启的原子性证明;重复同一上传是否可能铸造多个 workspace asset 仍待运行时验证。”
  • “#5507 单测覆盖 Go 404 映射与 shared gate,但没有 workerd/Go deployed contract、真实 Agent tool -> 404/503、或 reference 404 的集成覆盖。”
  • “promoteAttachment 只支持 PNG/JPEG/WebP;用户上传 SVG/GIF/AVIF 或附件丢失时,模型提示与产品 UI 是否都能稳定给出可操作降级仍待验证。” feishu_doc_url: null github_repository: joyehuang/ai-agent-field-notes github_path: outputs/voyager-daily-pr-study/2026-08-03-pr-5507-agent-image-source-asset-recovery.md voyager_merge_commit: 5d8c482006df6b21535c76653f516115946e3446 voyager_origin_main_snapshot: 26c7c2c4b81511e3bced4da1c3b0438fb41150d9

业务线概览#

这条业务线解决的是 Agent 在聊天中处理图片时的“输入资产不是普通 workspace asset”问题,同时把一次图片任务变成可恢复、可去重、可正确反馈的运行时流程。

用户看到的是一个图片工具调用,但代码里至少存在三个不同的 ID 域:

  1. 聊天上传附件的 ID,模型通过 /mnt/attachments/ 路径看到;
  2. workspace asset ID,Go 图片 API 的 assetId 以 UUID 形式消费;
  3. 图片任务 ID,提交后用于轮询、解析结果并交给统一的 delivery seam。

如果把第 1 种 ID 直接送给第 2 种接口,Go API 返回 404/code 102。此前这类异常会以裸异常逃出 tool execute,AI SDK 可能把它视为可重试的 tool error;模型既拿不到“这个 ID 不存在”的可纠正信息,也可能重复调用甚至触发额外的扣费/退款路径。

本次锚点 #5507 没有扩大到新的图片能力,而是补上提交阶段的最后一个错误边界:把“source asset 不存在”从服务故障改成结构化终态结果,在任务尚未创建之前结束本次调用,并向模型提供如何修正 ID 的明确提示。它依赖 #5506 刚完成的附件提升,也继承 #4318、#4516、#5422、#5461 建立的任务恢复、结果契约、输出交付和统一工具形状。

今日锚点#

项目内容
PR#5507
标题Handle missing source assets in image tasks gracefully
作者Jason Tan / banchichen
合并时间2026-08-03 13:20:58(Australia/Melbourne,UTC 03:20:58)
Voyager merge commit5d8c482006df6b21535c76653f516115946e3446
改动文件4 个:Go API 结果映射、Go API 测试、图片任务执行器及其测试
选择理由它是当天 Melbourne 窗口内尚未学习的业务线 PR,并且直接承接前一天 #5506 的 attachment promotion;改动同时触及 API 错误契约、任务生命周期和模型纠正提示,能自然串起至少 5 个有实质关系的 PR。

PR body 给出的验证结果是 pnpm vitest run --project agent-worker 通过 19 个文件、163 个测试。本报告把它作为 PR 提供的验证证据,不把它冒充为本次运行重新执行的结果;本次运行只读检查了已合入 origin/main 的真实代码和 diff。

演进时间线:输入域、任务生命周期和输出交付逐层收敛#

阶段PR 与合并时间代码证据改变的层
前置基础#4318,2026-07-04backend/workers/agent/src/tools/imageTaskTool.ts 引入基于 task type + payload 的 SHA-256 key;PendingTaskStore 按 key 保存、读取和删除 task ID。从 tool call 标识切换到业务请求内容标识,使 Agent 重试能重新挂接 orphaned task,避免重复提交和重复扣费。该 PR 距锚点已超过默认 30 天,但它是当前“submit 前不写 pending、submit 后才能复用”的直接底层前提。
结果契约#4516,2026-07-08runResumableImageTask 对 terminal failure 删除 pending 后返回 { status: "failed" },不再抛出 ImageTaskFailedError;tool schema 增加 credits/resolution/no_image/error reason。把用户/模型可处理的失败从异常通道移到结构化业务结果,阻断 AI SDK 将最终失败误当成 retryable tool error。
输出交付#5422,2026-07-30backend/workers/agent/src/imageDelivery.tswithImageDelivery 统一 hold slot、运行任务、成功后放置结果、失败时 fail slot。生成、编辑、去背景共享结果落位和模型可见输出的单一路径,避免各工具分别放置导致重复放置或状态不一致。
工具形状#5461,2026-07-31imageMode.ts 用一次 image_generation 调用的 prompt/refs/size 形状决定 generate、edit 或 upscale;resolveImageRefsrunSourceOp 统一 source operation。用户入口和运行时分派收敛;同一套 attachment/reference 解析、幂等 key 和 delivery seam 可以覆盖多个图片操作,但 remove_background 仍保留独立的 API 语义。
输入修复#5506,2026-08-02removeBackgroundTool.ts 在计算幂等 key、建立 canvas slot 前调用共享 promoteAttachment;工具 schema 明确接受 workspace asset ID 或 /mnt/attachments/<id>把聊天附件先提升为 workspace asset,修复 raw attachment ID 直接进入 Go remove-background API 的 404。
今日锚点#5507,2026-08-03goApi.ts:727-733 把未找到 source asset 的 code 102 映射为 source_asset_not_foundimageTaskTool.ts:179-186 返回可纠正的 failed result,并明确不要原样重发 ID。在“提交任务”这个 runtime boundary 上完成错误分类:没有任务就没有 pending record、wait 或 resolve,也不会把模型输入错误包装成服务故障。

当前架构与数据流#

flowchart LR
  U["Chat upload"] --> R["Agent attachment R2 + attachmentsById"]
  R --> P["<submitted> /mnt/attachments/<id>"]
  P --> T["image tool call"]
  T --> D["decideImageRequest / remove_background"]
  D --> N["shared promoteAttachment"]
  N --> A["workspace assetId"]
  A --> K["SHA-256 task idempotency key"]
  K --> S["runResumableImageTask"]
  S --> G["Go image API"]
  G -->|404 code 102| F["source_asset_not_found result"]
  G -->|submitted| W["pending store + wait"]
  W --> Q["resolve result asset"]
  Q --> E["withImageDelivery: source node or new slot"]
  F --> M["model gets correction; no same-id retry"]
plaintext

按代码路径拆开,当前流程是:

  • 用户入口与输入上下文。 Agent prompt 把聊天上传放在 <submitted>,并以 /mnt/attachments/<id> 暴露给模型;它和用户当前查看的 <context> 场景是不同的数据域。模型可把上传 ID 传给图片工具,但工具不能假定它已经是 workspace asset。
  • 统一提升。 tools/index.ts 为一次 Agent turn 创建一个共享的 promoteAttachment,传给 image_generationcutout_generationremove_backgroundattachmentAssets.ts 对已是 workspace asset 的值返回原 ID,对支持的上传格式把附件写入/提升为 workspace asset,并用 in-flight map 和跨 turn store 避免同一附件在同一执行链中重复提升。#5506 将这个 seam 接入 remove_background。
  • API 契约边界。 TypeSpec 的 RemoveBackgroundRequest 仍只声明 assetId: string;Go handler 将其按 UUID 解析,再创建 image task。因此 /mnt/attachments/<id> 不应穿透到 Go 层,真正的附件 ID 到 workspace asset ID 的转换必须在 Agent worker 完成。
  • 幂等与可恢复执行。 提升后的 assetId 和 operation payload 进入 imageTaskIdempotencyKey,用请求内容的 SHA-256 作为 pending key。#4318 的设计让提交前没有旧 task 时正常 submit,提交后才写 pending;轮询临时失败时保留记录,终态失败/取消/成功解析后删除。
  • 提交阶段错误。 #5507 的 toSubmitResult 是多个 image submit endpoint 共用的结果映射。Go 返回 404/code 102 时,当前 source-asset 语义被转成 source_asset_not_foundrunResumableImageTask 在此处立即返回 failed result,既不 store.set,也不 wait/resolve。
  • 结果交付。 正常 submitted 任务通过 withImageDelivery 统一处理:成功结果放入预留 slot 或复用 source node,失败则使 slot 进入失败状态。#5507 的 submit-before-task 失败会走同一层的失败收口,但不会产生远端 task 或 pending 状态。

这里有一个必须保留的边界:PR #5507 的 comment 明确指出,坏的 referenceAssetId 会在 resolveExtraSourceSpec 中包裹 Go API 错误,之后 Gin middleware 的 concrete-type 判断会把它作为 500;因此当前 code 102 映射只安全覆盖“source asset ID 直接在 submit API 返回 not found”的路径,不等价于所有引用资产错误都已治理。

关键代码与设计点#

以下行号均基于本次只读抓取后的 Voyager origin/main snapshot 26c7c2c4b81511e3bced4da1c3b0438fb41150d9;PR 编号和 diff 位置同时给出,避免把工作分支的旧行号当成主线证据。

1. #4318:pending key 绑定业务请求,而不是绑定一次 tool call#

代码事实: #4318 diffbackend/workers/agent/src/tools/imageTaskTool.ts 的 diff 在新增 imageTaskIdempotencyKeyrunResumableImageTask 附近。

const imageTaskIdempotencyKey = (taskType, payload) =>
  taskType + ":" + sha256(JSON.stringify(payload));

const pendingTaskId = await store.get(idempotencyKey);
if (pendingTaskId) {
  return waitAndResolve(pendingTaskId);
}
const taskId = await submit();
await store.set(idempotencyKey, taskId);
plaintext

设计点: 重试时相同的业务请求仍能找到 orphaned task;而 #5507 在 submit 之前发现 source asset 不存在时不会写 pending。这个顺序是 billing safety 的关键:未提交的错误不应该伪造一个可恢复 task,也不应该让下一次重试挂到不存在的 task 上。

2. #4516:终态业务失败改走 result,而不是异常#

代码事实: #4516 diff,当前 backend/workers/agent/src/tools/imageTaskTool.ts:211-221 对 terminal failure 删除 pending 后返回失败结果;对应的 tool schema 在 toolDefs.ts 使用 succeeded | failed discriminated union。

if (outcome.status === "failed") {
  await store.delete(idempotencyKey);
  return {
    status: "failed",
    reason: "error",
    ...(outcome.errorCode != null ? { errorCode: outcome.errorCode } : {}),
  };
}
plaintext

设计点: “模型输入导致的不可恢复失败”和“服务/网络异常”不再共用 throw 通道。#5507 不是另起一个异常类型,而是把 source asset 缺失继续落到这条已存在的结构化失败协议上。

3. #5506:先把附件提升为 workspace asset,再计算幂等 key#

代码事实: #5506 diff,当前 backend/workers/agent/src/tools/removeBackgroundTool.ts:48-73 的顺序是 promotion -> abort check -> idempotency key -> delivery/task execution。

const assetId = (await promoteAttachment(sourceId, abortSignal)) ?? sourceId;
abortSignal?.throwIfAborted();
const idempotencyKey = await imageTaskIdempotencyKey("remove_background", {
  assetId,
});
return withImageDelivery(
  go,
  delivery,
  { slotId: idempotencyKey, source: { assetId } },
  () => runResumableImageTask({
    signal: abortSignal,
    idempotencyKey,
    store: pendingStore,
    submit: () => submitRemoveBackground(go, { assetId }, abortSignal),
    wait: ...,
    resolve: ...,
  }),
  abortSignal,
);
plaintext

设计点: 幂等身份必须描述 Go API 实际消费的资源,而不是模型最初给出的路径字符串。这样同一个上传在提升前后的 ID 变化不会造成两个语义上相同但 key 不同的任务;如果提升失败,错误也在远端任务创建前结束。

4. #5507:Go 的 404/code 102 变成可纠正的 source-specific result#

代码事实: #5507 diffbackend/workers/agent/src/goApi.ts:662-664 的 shared toSubmitResult 说明 billing/plan gate 或“source ID 指向不存在对象”属于终态业务结果,goApi.ts:727-733 实际映射:

// A wrong id is the model's to correct, not a service fault. Only a
// source-asset id reaches here: a bad reference arrives wrapped, as a 500.
if (result.error.code === GO_API_ERROR_CODE.notFound) {
  return { status: "source_asset_not_found" };
}
throwGoApiFailure(result);
plaintext

这里的 SubmitImageTaskResult union(goApi.ts:174-186)新增了 { status: "source_asset_not_found" },所以调用方不需要从 error message 中猜测业务含义。

5. #5507:在 pending、wait、resolve 之前给模型可操作的纠正#

代码事实: 当前 backend/workers/agent/src/tools/imageTaskTool.ts:179-186

if (submitResult.status === "source_asset_not_found") {
  return {
    status: "failed",
    reason: "error",
    errorCode: "source_asset_not_found",
    message:
      "No image asset has that id, and it names no upload in this chat either. " +
      "A chat upload's id is the file name in its /mnt/attachments/<id> path, " +
      "and the image tools take that id as it is. Do not re-send the same id.",
  };
}
plaintext

测试 tools/imageTaskTool.test.ts:10-51 验证了四个重要负面断言:wait 不调用、resolve 不调用、store.set 不调用,模型输出包含 “Do not re-send the same id”。这比只断言错误字符串更接近 runtime contract。

6. #5422:所有成功/失败结果最终穿过同一 delivery seam#

代码事实: 当前 backend/workers/agent/src/imageDelivery.ts:295-332withImageDelivery 先 hold slot、运行任务;若结果是带 assetId 的 succeeded 才放置,否则 fail slot。

const slot = await holdImageSlot(delivery, slotId, source);
const result = await run();
if (!slot) return result;

if (result.status === "succeeded" && result.assetId) {
  await placeImageInSlot(delivery, slot, result.assetId);
} else {
  await failImageSlot(delivery, slot);
}
return result;
plaintext

推断: #5506 通过这个 seam 把 remove_background 纳入统一交付;#5507 的早期失败会让 slot 失败收口,但因为没有 submitted task,所以不会留下需要恢复的远端状态。这里的 slot 状态仍应通过真实 Agent/workerd 场景验证,当前新增测试只覆盖 runResumableImageTask 的 gate。

工程取舍#

边界和复用#

  • 错误归属清晰。 一个模型生成的错误 source ID 是输入可纠正问题,不是 Go/服务不可用;因此在共享 Go submit 映射处转成 union result,再由 Agent tool 给出修正建议。
  • 复用既有协议。 #5507 复用 #4516 的 failed + reason + errorCode,没有新增异常传播、retry policy 或第二套 UI 协议。#5506 复用 promoteAttachment,没有让 remove_background 单独解析 R2 附件。
  • 边界仍依赖隐含前提。 toSubmitResult 被多个 image endpoint 共用,但 notFoundsource_asset_not_found 的语义依赖“这里只有 source asset 的未包装 404”。这是当前代码 comment 已明确写出的约束,不应被理解为通用 404 分类器。

兼容性与数据模型#

  • Go API 的 TypeSpec/handler 仍消费 workspace asset ID,兼容性修复发生在 Agent worker,不改变后端接口的输入模型。
  • 工具 schema 同时允许 workspace asset ID 和 /mnt/attachments/<id>,把“用户/模型可见的输入域”与“后端可执行的资源域”隔开。
  • promotion 当前只覆盖 PNG、JPEG、WebP;其他格式不应静默假定可用。未知附件格式、附件已删除、workspace asset 不存在都需要产品层面的降级文案与观测。

性能、可靠性与计费安全#

  • 业务请求哈希会让重试复用已提交任务,避免重复执行;它也意味着 payload 的字段变化会形成新任务,后续扩展字段时要评估兼容语义。
  • #5506 在算 key 和 hold slot 前做 promotion,避免把同一附件的临时 ID 当成任务身份;共享 promoter 的 in-flight map 降低同一 turn 的重复上传。
  • #5507 在远端 submit 前结束,不轮询、不 resolve、不写 pending,减少无意义 I/O,并阻断由输入错误引发的自动 retry/重复扣费。
  • 代价是错误分类必须精确。若把真正的服务错误归为终态,Agent 会停止恢复;若仍把模型 ID 错误当异常,AI SDK 可能继续重试。当前实现把 source direct 404 和 wrapped reference 404 分开,但后者仍是缺口。

测试策略#

覆盖证据尚未覆盖
提交 API 的 404/code 102 映射#5507 goApi.test.ts:130-148 模拟 404 JSON,并断言 source_asset_not_found真实 Go 服务/部署契约和不同 image endpoint 的 code 102 语义
tool gate 的运行时顺序#5507 imageTaskTool.test.ts:10-51 断言不 wait、不 resolve、不 set,并检查模型提示真实 Agent tool、delivery slot 和 workerd 路由组合
terminal failure 不抛异常#4516 的 unit/eval 变更客户端/模型是否始终停止同 ID 重试
orphaned task 恢复#4318 的 pending store/idempotency 测试进程重启、promotion store 与 pending store 跨重启的一致性
共享结果交付#5422 的 image delivery 测试/调用方接入submit-before-task 失败时实际 canvas slot 的状态转移

PR body 报告的 19 files/163 tests 是合并前的 agent-worker 验证;本次没有在 Voyager 工作区运行命令,也没有修改依赖或生成文件。

和最近学习记录的关系#

  • 目标仓库已有 #4516 的报告,重点是“图片任务终态失败不再 throw,避免 AI SDK 重试”。本次不是重复该结论,而是继续向前追一层:具体的 source asset 404 如何在 submit boundary 变成这个结构化协议,以及在任务尚未创建时哪些副作用必须为零。
  • #4344 的报告 已讨论 Agent reference image 与附件上下文。本次新增的视角是 ID 域分离:附件路径不是 workspace asset;#5506 的 promotion 修复输入转换,#5507 处理转换后仍指向不存在 source 的终态。
  • 最近一次自动化报告是 #5496 的 Durable Object transient/503 恢复线。两者共享“错误分类决定是否重试”的可靠性原则,但本次边界更窄:#5507 专门把模型可纠正的资源 ID 错误从 transient/service-fault 通道移出,避免沿用 DO/网络错误的恢复策略。

我会怎么吸收#

  1. 先画清 ID/数据域,再定义接口。 让 prompt 中的附件路径、工具 schema 的输入、Go API 的 workspace asset UUID 和任务 ID 在命名与转换点上显式可见;不要让一个 assetId 变量跨域隐式复用。
  2. 用业务请求内容做幂等身份。 tool call 是一次交互事件,{taskType, payload} 才是重试时需要恢复的业务意图;对图片任务尤其要在资源规范化之后计算 key。
  3. 把可纠正输入错误设计成终态 result。 result 应携带稳定的 errorCode 和模型可执行的下一步,并且测试“不会 submit 后续副作用”,而不只是测试错误文本。
  4. 集中收口副作用。 任务提交/轮询/解析和 canvas delivery 分层;生成、编辑、去背景共享 delivery seam,单个工具只负责描述 source 与 operation。
  5. 在测试中断言负面路径。 对错误分支同时断言不写 pending、不 wait、不 resolve、不重复调用;这类断言直接保护可靠性和计费边界。

边界、风险与未解问题#

已由代码验证的边界#

  • #5507 只处理 submit 结果中未包装的 Go 404/code 102;PR comment 明确说坏 reference 会被包装并当前走 500。
  • #5507 只在 runResumableImageTask gate 返回失败;它不处理任务已经 submitted 后 source asset 被删除、权限变化或 resolve 失败的所有组合。
  • #5506 的 promoter 支持 PNG/JPEG/WebP;工具 schema 接受两类 ID 不代表所有附件格式都能提升。
  • 本次锚点和相关 PR 的 merge commits 均已进入 origin/main;没有把仍在分支上的 PR 当作演进证据。

仍待确认的问题#

  1. reference asset 的 404 是否应被前置分类。 当前 wrapped error 会被当作 500 并记录 server fault;正确修复可能需要把 reference lookup 放在 task submit 前,这还涉及 debit + refund 的顺序和一致性。
  2. 共享 notFound 映射的未来漂移。 若另一个 image endpoint 直接返回同一 code 102,shared toSubmitResult 可能把它误命名为 source asset not found;应考虑让 submit API 返回更具体的错误域,或在调用方传入 operation context。
  3. 提升的持久化一致性。 uploadAsset 成功和 attachmentAssetStore.set 之间若进程重启,重试是否可能创建多个 workspace asset,需通过真实存储/重启测试确认。
  4. 已提交任务的资源失效。 需要验证 wait 期间资源删除时 pending 清理、delivery slot 失败、模型消息和 billing 状态是否一致。
  5. 生产契约与可观测性。 当前新增单测没有覆盖 workerd 到 Go 的真实 404 响应、完整 Agent tool 调用或 reference 404;应补一个不付费/可清理的集成测试,并区分模型输入错误与服务 fault 的 Sentry 统计。

候选说明#

本次先按 Australia/Melbourne 的当天合并窗口查找候选。#5507 在 2026-08-03 13:20 合并,未出现在既有锚点学习日志中;#5503 同日合并但属于聊天 reconnect 失败策略,和上一轮 #5496 的 Durable Object transient 可靠性线更接近,新增工程视角较少。#5501 的语言提示、#5482 的 billing、#5372 的登录账号预劫持和 #5385 的文档侧改动都不能自然串起本次图片任务链,因此没有拼入报告。

#5506 是昨天的直接前置,作为相关 PR 保留而没有重复选作锚点。#4318 超过默认 30 天,但它定义了当前 PendingTaskStore 与内容哈希恢复 seam;如果去掉它,#5507 的“不写 pending、只在 submitted 后保存 task”就失去代码上的前置解释,因此报告明确标注了保留原因。其余关联 PR 均在近 30 天内,且通过相同目录、相同任务生命周期、相同 API/resource boundary 或统一 delivery path 进行代码关联,而不是只依据标题关键词。

GitHub 文档#

  • 报告文件:outputs/voyager-daily-pr-study/2026-08-03-pr-5507-agent-image-source-asset-recovery.md
  • 目标仓库:joyehuang/ai-agent-field-notes
  • Voyager PR: #5507 — Handle missing source assets in image tasks gracefully
  • Voyager merge commit:5d8c482006df6b21535c76653f516115946e3446
  • 本报告随本次目标仓库 main 推送提交;最终目标仓库 commit 以本次自动化日志的 githubCommit 字段和任务回复为准。
  • feishu_doc_url: null;本任务不创建外部学习文档。

🗂️ 这是知识库中的🔬 研究。

内容可能仍在补充或修订中。

← Back