Voyager 业务线学习:Agent 多图生成的参考图契约与像素继承
这条业务线解决的不是“如何把 prompt 写得更长”,而是图片生成调用之间哪些信息能可靠地跨调用传递。每次 image_generation 只看到本次 prompt 和 refs,不会自动看到上一轮对话、用户上传文件或 brand kit。形状、字体、精确布局和角色轮廓一旦只被改写成文字,就会在每次独立生成中…
source_automation: voyager-merged-pr run_date: 2026-08-05 anchor_pr_number: 5591 pr_number: 5591 pr_title: “fix(agent): reserve a set’s pixel inheritance for what only pixels can hold” pr_url: https://github.com/adastralab-ai/voyager/pull/5591 ↗ author: “Horcrux / magicismight” merged_at: “2026-08-05T02:29:00Z” modules:
- backend/workers/agent/src/skills/image-set/SKILL.md
- backend/workers/agent/src/tools/imageGenerationTool.ts
- backend/workers/agent/src/tools/imageSource.ts
- backend/workers/agent/src/tools/assetResolver.ts
- backend/workers/agent/src/routes/agentEval.ts
- agent-eval/cases/image
- agent-eval/asserts/trajectory.ts files_changed: 4 changed_files:
- agent-eval/asserts/trajectory.ts
- agent-eval/cases/image/prompt-set.yaml
- backend/workers/agent/src/gen/skillsBundle.ts
- backend/workers/agent/src/skills/image-set/SKILL.md learning_tags:
- agent-runtime
- image-generation
- image-set
- reference-assets
- pixel-inheritance
- prompt-contract
- asset-id-boundary
- attachment-promotion
- agent-eval
- trajectory-assertion
- provider-context
- cost-control
- test-strategy business_line: “Agent 多图生成中的参考图契约、像素继承与评测边界” related_prs: [5533, 5562, 5573, 5581] related_prior_prs: [4720, 5520] line_stage: “模型自行维护 prompt (#5533) -> image id 显式分型与共享 source resolver (#5562) -> 每次 set generation 携带必须复现的图片 (#5573) -> uniform set 才允许 master pixel inheritance (#5591) -> eval 让 assetId 背后的真实像素进入回放 (#5581)” open_questions:
- “image-set skill 是模型可读规则,不是运行时强制器;#5591 的 refs 选择仍可能被模型漏传或误传,轨迹 eval 也不能证明最终图像质量。”
- “resolveImageRefs 用 Promise.all 并行 promotion;一个 ref 被拒绝可以阻止 task submit,但其他合法 upload 可能已经 uploadAsset,上传 promotion 本身没有事务回滚。”
- “imageSource.ts 的 in-flight Map 只覆盖一轮 turn,uploadAsset 与 attachmentAssetStore.set 之间跨重启仍没有原子提交证明。”
- “uniform set 把 master 和原始 refs 一起传入,受 MAX_REFERENCE_ASSETS=10、最多 8 张以及 advanced tier 成本约束;多参考图的 provider 行为仍需部署级验证。”
- “agent-eval 的 assets/primeInline 路径让 fixture 真正携带像素,但 eval route 仍 stub Go/R2/task persistence,不能替代真实 upload -> task -> provider 链路。”
- “裸 id 为兼容历史仍被视为 workspace asset;跨 workspace、删除资产或 wrapped GoApiError 的组合是否都能稳定落入 image_not_found,仍需真实接口覆盖。”
- “同一条业务线后续的 snapshot 缺失/过期处理(#5579/#5607)在本次刷新时尚未进入 origin/main,不能当作本报告的正式后续证据。” feishu_doc_url: null github_repository: joyehuang/ai-agent-field-notes github_path: outputs/voyager-daily-pr-study/2026-08-05-pr-5591-agent-image-pixel-inheritance.md voyager_merge_commit: 78767d844b27ff321ee99e24e527ae78ea4f98e0 voyager_origin_main_snapshot: fb59f20192af82234201622927e52d0f56f271ad
业务线概览#
这条业务线解决的不是“如何把 prompt 写得更长”,而是图片生成调用之间哪些信息能可靠地跨调用传递。每次 image_generation 只看到本次 prompt 和 refs,不会自动看到上一轮对话、用户上传文件或 brand kit。形状、字体、精确布局和角色轮廓一旦只被改写成文字,就会在每次独立生成中重新猜测;反过来,如果把一张 master 图无条件传给一个本来需要各自构图的 carousel,后续图片会继承不该继承的版式、标题或主体。
当前范围包含四个相互咬合的边界:
- 模型如何区分“要写进每个 prompt 的文字不变量”和“必须作为图片传入的像素不变量”;
- chat upload、workspace asset 和历史产物如何在 image tool 入口归一化为可提交的 asset id;
- uniform set 与 merely related set 如何选择 master pixel inheritance 或独立生成;
- agent-eval 如何让 asset id 背后的真实像素进入测试,而不是只断言 refs 字符串出现过。
昨日 #5520 已经学习了 chat upload 到 workspace asset、task gate 和 canvas delivery 的边界。本次继续向上游推进到模型上下文:上传图和历史产物怎样成为每次生成真正能看到的参考图,以及什么时候不能把上一张结果当成下一张的模板。
今日锚点#
| 项目 | 内容 |
|---|---|
| PR | #5591 fix(agent): reserve a set’s pixel inheritance for what only pixels can hold ↗ |
| 作者 | Horcrux / magicismight |
| 合并时间 | 2026-08-05 12:29:00(Australia/Melbourne,02:29:00Z) |
| Voyager merge commit | 78767d844b27ff321ee99e24e527ae78ea4f98e0 |
| origin/main 快照 | fb59f20192af82234201622927e52d0f56f271ad |
| 改动范围 | image-set skill、bundled skill、prompt-set trajectory assertions |
| 选择理由 | #5591 是当天已合入当前 origin/main、尚未作为锚点学习、且把“参考图继承”从一句笼统规则收窄成 uniform/related 两种不同策略的高杠杆 PR。它能用真实代码自然串起 #5533、#5562、#5573 与 #5581,而不是只按标题拼接图片相关 PR。 |
代码事实与推断分开看:代码证明了 refs 选择规则、显式 id 解析、eval 的像素注入和新的轨迹断言;它不能证明所有真实 provider 都会按 refs 预期保留像素,也不能证明上传 promotion 和 task 提交跨故障原子完成。
演进时间线#
| 阶段 | PR 与真实代码变化 | 改变的层 | 新边界 |
|---|---|---|---|
| prompt ownership | #5533 ↗,2026-08-04,merge ed098a74ecfd6a28ec3879cc37caf52291529a3b | Agent image tool / prompt / image-set skill | 删除 imagePromptComposer.ts,移除 imageGenerationTool 的 composeFinalPrompt;模型在 tool args 中写出的 prompt 直接送入 submit,跨调用的一致性改由 skill 和 refs 规则承担。 |
| image id 分型 | #5562 ↗,2026-08-04,merge 948e11a0f17216123a080d2141e3a915c80dcb63 | imageSource / attachment promotion / Go error boundary | upload: 前缀才触发 chat attachment promotion,asset: 或裸 id 走 workspace asset;解析失败成为 rejected result,不能再靠“查找失败后猜它是哪一种 id”。 |
| 每次生成携带必须复现的图片 | #5573 ↗,2026-08-04,merge 6a72bca17cd8bbc6ca88ad49338b5a8d4585dd6c | image-set skill / prompt-set eval | 把用户上传的主体、已 settled 的图片和 brand image 放进每一次 generation 的 refs;仅用文字描述一个角色不再被视为等价方案。 |
| 今日锚点:pixel inheritance 收窄 | #5591 ↗,2026-08-05,merge 78767d844b27ff321ee99e24e527ae78ea4f98e0 | image-set policy / trajectory assertions | 只有 uniform set 才以首张图作为 master;只需要“属于同一系列”的 carousel 不共享 master pixels。uniform set 还要把用户主体和 master 一起带入每个 refs。 |
| eval 与生产上下文对齐 | #5581 ↗,2026-08-05,merge cd37074a21aec25f061b96ea51050c72a2da84dc | AssetResolver / eval route / fixture provider | eval 的 asset id 不再只是标签:provider 读取 fixture bytes,agentEval 用 primeInline 把它放进模型可见的 image-data;reference-carry case 因此能测真实像素上下文。 |
这条线的演进是“先把 prompt 重写权交还给模型,再把输入类型和像素来源变成显式契约,最后限制像素继承的适用面”。#5591 不是简单地把更多 refs 加进去,而是在识别出过度继承会污染整组输出后,主动减少 refs 的使用范围。
当前架构与数据流#
flowchart LR
U["用户上传 / 历史 asset / brand image"] --> C["模型 tool args: prompt + refs"]
C --> D["image-set: locked words or pixel refs"]
D --> R["resolveImageRefs"]
R --> I["createImageIdResolver"]
I -->|upload:| P["R2 attachment -> uploadAsset -> workspace assetId"]
I -->|asset: / bare| A["workspace assetId"]
P --> G["imageGenerationTool"]
A --> G
G --> T["Go image task: referenceAssetIds"]
T --> W["resumable task / provider"]
W --> O["result assetId"]
O --> H["AssetResolver: fresh URL or inline fixture"]
H --> C
E["agent-eval assets fixture"] --> Hplaintext1. 模型上下文与 image-set 决策#
image-set/SKILL.md:39-51 明确写出每次 generation 只看到本次 call;要复现的图片进入 refs,brand 仍以 brand kit 的值传递。#5591 又在 :62-85 将 set 分为:
- uniform:同一张图的 layout、type treatment 或主体只做小范围变化,首张结果作为 master,后续 prompt 只写每张真正改变的 subject/text;
- related:每张图有自己的视觉结构,locked block 负责共同风格,refs 不应被 master 强行绑定;
- 不确定时选 related:一次局部漂移可以重做,错误的 master inheritance 会污染首张之后的所有输出。
这里的“uniform”不是输出数量的同义词。#5591 的重点是判断差异维度是否小于共同像素维度:两张不同主题但各自有独立构图的 carousel 不应走 master;同版式只换标题的卡片可以走 master。
2. image id 归一化与副作用边界#
PR #5562 新增的 imageSource.ts 把模型命名的图片分成两个来源:
- upload:…:从 attachmentsById 找 chat upload,限制 PNG/JPEG/WebP;命中 attachmentAssetStore 直接复用,否则读 workspace/chat/attachment R2,调用 uploadAsset,写入 asset store;
- asset:… 或裸 id:去掉显式 asset: 别名后按 workspace asset 处理,不触发 attachment 读取;
- getAsset 返回 404 或非 image 时,loadImageAsset 返回 rejected message;未知 reference 不应被包装成一条不可行动的 server fault。
createImageIdResolver 的 inFlight Map 让同一 turn 内并发 tool call 共用 upload promotion promise;attachmentAssetStore 负责跨 turn 的复用。imageGenerationTool 在 image decision、aspect/resolution 和 tier rejection 之后调用 resolveImageRefs,随后才把 assetIds 填入 referenceAssetIds 并进入 runGeneration。对模型而言,这条路径返回 image_not_found result;对系统而言,task submit 与 credits 不会因为一个被拒绝的 reference 继续启动。
有一个需要保持警惕的细节:resolveImageRefs 对多个 refs 使用 Promise.all。它能保证 task 不提交,但不能把多个 upload promotion 回滚成事务;合法 upload 可能在另一个 ref 被拒绝前已经被提升。
3. task 与历史回放的图片通道#
生成任务仍沿用 resumable task 和内容式 idempotency key;runGeneration 将已解析的 refs 作为 referenceAssetIds 交给 Go。任务结果的 assetId 由 AssetResolver 重新物化为当前 turn 可用的 URL,或在 eval 中物化为 inline image-data。这样历史 transcript 可以保存 durable id,而不必保存易过期的 signed URL。
这与 #4720 的 replay-time asset refresh 是同一条长期原则,但本次新增视角是:assetId 不只是“未来能重新签 URL 的句柄”,它还是多图生成必须显式带回的像素来源;#5581 则把这条原则带进 eval harness。
4. eval 数据流与验证范围#
agent-eval/provider.ts:40-59 读取 assets 映射指向的 fixture bytes,并在 :115-144 将它们随请求发送到 worker。agentEval.ts:122-134 接受 assetId/mediaType/base64,:203-205 调用 AssetResolver.primeInline。buildAssetImagePart 在 assetResolver.ts:54-67 优先返回 image-url 或 image-data,只有没有像素时才降级成“Preview unavailable”文字。
因此 #5581 修复的是测试环境的语义缺口:之前 case 里有 EVAL_ASSET_PREV1 这个 id,却没有它背后的图片,模型只能看到一个字符串或 unavailable note;现在 reference-carry 可以判断“模型是否真的以像素看到上一张图”。但该 route 明确是本地 eval,Go/R2/task 仍是 stub,不能当作部署级链路证明。
关键代码#
1. 显式 id 类型阻止“查找失败后猜来源”#
来源:PR #5562 diff ↗,backend/workers/agent/src/tools/imageSource.ts:22-24、110-141。
export const UPLOAD_ID_PREFIX = "upload:";
const ASSET_ID_PREFIX = "asset:";
if (!rawId.startsWith(UPLOAD_ID_PREFIX)) {
const assetId = rawId.startsWith(ASSET_ID_PREFIX)
? rawId.slice(ASSET_ID_PREFIX.length)
: rawId;
return assetId
? { kind: "ok", assetId }
: { kind: "rejected", message: "An image id is required." };
}plaintext代码事实是 source kind 由输入本身决定。这样一个模型编造的 id 不会因为“刚好没在本 chat upload 里找到”就被当成 upload 或触发 R2 promotion;裸 id 继续作为 workspace asset 是兼容历史 transcript 的折衷。
2. rejection 在 image task submit 前收束#
来源:PR #5562 之后的 origin/main,backend/workers/agent/src/tools/imageGenerationTool.ts:187-203。
const refs = await resolveRefs();
if (refs.kind === "rejected") return refsRejected(refs.message);
return runGeneration(
options,
{ ...shaped, refs: refs.assetIds },
resolvedModelId,
abortSignal,
);plaintext这个 seam 将“模型给了不可用图片”与“已经提交并计费的生成任务”分开。这里的断言范围要准确:它保证 task submit 不会由这一调用继续发生,不保证此前并发的 upload promotion 已回滚。
3. #5591 只让 uniform set 继承 master pixels#
来源:PR #5591 diff ↗,backend/workers/agent/src/skills/image-set/SKILL.md:62-90。
Uniform ... is a master and its edits, and only pixels hold it.
Belonging together is the locked block's work: each image is rendered on its own.
Then issue the rest ... with the settled image as its first refs entry,
followed by the pictures the set has to reproduce.plaintext这段规则把 refs 从“越多越可靠”的直觉改成按差异维度选择。首张 master 也不再是唯一 ref:如果 uniform set 还有用户上传主体,后续 generation 必须同时拿到 master 的 layout 和上传图的 subject pixels。
4. 轨迹测试同时锁定“每次都带”与“变化各自生成”#
来源:PR #5591 diff ↗,agent-eval/cases/image/prompt-set.yaml:56-84、agent-eval/asserts/trajectory.ts:92-116。
- description: "uniform set ... subject ... in every call"
assert:
- type: javascript
value: file://asserts/trajectory.ts:argIncludes
config: { tool: image_generation, field: refs, contains: mascot.png, every: true }
- type: javascript
value: file://asserts/trajectory.ts:argIncludes
config: { tool: image_generation, field: refs, contains: EVAL_ASSET_0001 }plaintext同一份 prompt-set 还新增了每张各自有 look 的 trend carousel,并用 argAbsent 断言 image_generation 不携带 refs。#5591 对 argIncludes 增加 every 参数,让“至少一次带过”升级为“所有生成调用都带过”,这是从存在性测试到全局不变量测试的实际变化。
5. eval 让 asset id 背后的像素可见#
来源:PR #5581 diff ↗,backend/workers/agent/src/tools/assetResolver.ts:21-66、backend/workers/agent/src/routes/agentEval.ts:122-134、203-205。
primeInline(assetId, { data: base64, mediaType });
const inline = resolver.getInline(assetId);
if (inline) return { type: "image-data", ...inline };plaintext这不是把测试夹具挂到描述文本上,而是沿着模型 image part 的实际输出路径交付 bytes。它使 reference-carry 的 ref 断言有了语义前提:模型能看到图,才有资格判断它是否在下一次 generation 中复用了该图的形状与风格。
工程取舍#
边界与复用#
- #5533 删除 prompt composer,把“用户想要什么”留在模型决策,把“哪些信息必须跨调用保留”放进 image-set skill 和 tool schema。好处是不会在提交前再次把用户 prompt 改写成另一种语义;代价是 skill 规则不具备类型系统同等强度。
- #5562 用一个 createImageIdResolver 服务所有 image tools,并把同一 turn 的 in-flight promotion 共享起来。复用的是 source boundary,不是把各工具的业务行为合并成一个大工具。
- #5591 将同一业务目标拆成两个执行策略:related set 复用 locked words,uniform set 才复用 master pixels。这避免用一个“多图一致性”开关覆盖互相冲突的用户意图。
兼容性与迁移#
- upload: 是新增的显式来源标记;asset: 是可读的显式别名;裸 id 仍表示 workspace asset,因此历史 transcript 不需要批量迁移。
- #5562 同时让 Go 的 IsNotFound 和 Gin error middleware 使用 errors.As,wrapped APIError 也能保持 4xx/可行动错误,而不是降级成 500。它修的是通用错误边界,但直接影响错误 reference 的可诊断性。
- #5591 的 refs 规则依赖现有 tool schema 和 asset id 输出形状,没有引入新的持久化 transcript schema;这是低迁移成本的选择,也意味着 runtime 仍需继续兼容模型不遵守规则的输入。
性能、成本与可靠性#
- uniform set 最多 8 张,reference schema 总上限是 10;advanced nano-banana-pro 用来承担 pixel reproduction,用户拒绝升级时退回独立生成而不是丢掉 deliverable。
- resolveImageRefs 保留 refs 顺序,因为 prompt 以 image1、image2 命名;同时对相同 upload 做 turn-local dedupe,避免并行 tool call 重复铸造 workspace asset。
- 生成任务仍使用 resumable store 和 idempotency key;本次只改变 prompt/ref 输入契约,没有把任务恢复责任塞进 image-set skill。
测试策略#
- #5533/#5573/#5591 用 prompt-set 和 reference-carry 做行为回归,验证 tool sequence、refs presence、refs contents 以及多图策略,而不是只测 UI 文案。
- #5591 的 argAbsent 覆盖“各自有 look 的 set 不应继承 master”,argIncludes(every=true) 覆盖“uniform set 每一次都要带用户主体”,两者对应两个不同的错误方向。
- #5581 给 eval asset id 配 fixture bytes,再由 AssetResolver 走 image-data;它补的是 harness 真实性,不是一个新的生产存储机制。
- 这些测试仍主要是本地 agent worker + promptfoo/real model + stubbed Go/R2 的轨迹验证。没有在本次任务中运行测试,也没有把 PR 描述中的通过数字当作本轮独立验证结果。
和最近学习记录的关系#
最近一篇是 #5520 chat upload attachment promotion ↗:它学习了 chat-scoped attachment 在 task、image delivery 和 canvas placement 前如何变成 workspace asset,并强调 side effect ordering、billing gate 与 turn-local dedupe。
本次新增的视角有三点:
- #5520 的 asset promotion 是输入归一化,本次的 refs 是模型语义归一化;前者解决“API 能不能读”,后者解决“模型能不能复现”;
- #5562 将 #5520 中的“promote if upload, otherwise assume asset”收紧为显式 id source contract,并把拒绝结果变成模型可行动的输出;
- #5591/#5581 将 #4720 的 replay-time asset refresh 原则延伸到多图生成和 eval:durable assetId 只有在每轮重新变成可见像素时才真正支撑连续创作。
因此本篇不是重复 #5520 的 canvas delivery,而是追踪同一资产从 chat 输入跨到 provider reference context 后,如何避免错误的像素继承。
我会怎么吸收#
- 先列出每个跨调用不变量的表示:能用文字稳定表达的放进 locked block,不能用文字表达的直接传 pixels/refs;不要让“prompt 很详细”替代真实媒体。
- 在边界把来源写进 id,而不是用一次失败的查找结果推断来源;兼容历史裸 id 时要明确这是兼容层,不要把它误当成强类型完成。
- 把“相关但不相同”和“必须像素一致”分成两个状态机分支;默认向独立生成降级,因为错误 master 会污染整组输出。
- 测试模型轨迹时同时测 some 与 every,并把 asset id 映射到真实 fixture bytes;否则 refs 字符串可以通过,模型仍然没有看到图。
- 把可计费 task submit、不可逆 upload promotion、模型可纠正拒绝分别设为不同边界,并为每一个边界记录幂等和回滚能力。
边界、风险与未解问题#
已由代码确认#
- #5591 的策略改变了 image-set skill 和 eval trajectory:相关 set 断言 refs 缺失,uniform set 断言每次 refs 都含上传图和 master asset。
- #5562 的 resolver 只对 upload: 触发 promotion,并将缺失 upload、非 image 和 Go 404 映射成 rejected message;imageGenerationTool 在 rejection 后不会调用 runGeneration。
- #5581 的 eval request 接收 assetId/mediaType/base64,并由 AssetResolver 输出 image-data;它不是只在 prompt 里写“有一张图”。
合理推断#
- 显式 source id + shared resolver 会减少 upload id、workspace asset id、historical asset id 在不同工具间漂移的机会;但它不能消除模型传错 id 的概率。
- 将 uniform set 的 master 与原始 subject refs 一起传入,是为了解决“master 保留了版式却吞掉用户主体”的特定回归;多 ref 的 provider 解释仍是外部边界。
- 以 related 作为不确定时的默认路径,是把重试成本限制在单张图片,而不是让一张错误 master 传播到整个 batch 的成本取舍。
尚待确认#
- uploadAsset 成功后 store.set 失败、worker 重启、重复提交的组合是否可能产生多个 workspace asset;当前代码没有跨重启事务证明。
- resolveImageRefs 的 Promise.all 是否应该改为先完成所有无副作用验证,再按顺序 promotion,以避免一个坏 ref 让其他 upload 变成孤儿 asset。
- upload:/asset: 与旧 transcript、跨 workspace asset、删除资产、wrapped 404 的全组合是否都能稳定返回 image_not_found,而不是 500 或重复 retry。
- 真实 Anthropic/Gemini/provider 在 master + subject 多 refs、aspectRatio、advanced tier 和 8 张上限下的视觉结果是否符合 skill 预期。
- agent-eval 的 stochastic rubric、mocked image_generation 输出和真实部署中的 R2/Go/task/provider 之间还缺一条端到端回归。
- #5579/#5607 处理 snapshot 缺失与 lifecycle 的后续变更尚未进入本次 origin/main;进入后应检查它们是否改变历史 image part 与本线的复用假设。
候选说明#
候选窗口优先使用 Australia/Melbourne 2026-08-05 当天的 merged PR。#5591、#5581、#5597、#5599、#5606 等均是当天候选;其中 #5591 与 #5581 直接落在 image refs、image-set 和 AssetResolver 同一代码边界,最适合形成连续业务线。#5607 与 #5579 虽然 GitHub 已显示 merged,但在刷新时其 merge commit 尚未成为当前 origin/main 的祖先,因此按仓库边界排除;它们没有被当作正式 related PR。
昨日 2026-08-04 的 #5533、#5562、#5573 是必要前置,均已验证 merge commit 可达当前 origin/main。所有正式 PR 的关联依据来自同一 image tool、image-set skill、asset resolver、eval route 或明确的前置/后续关系;没有为了凑数量加入无关的 editor UI、infra 或 export PR。
验证记录:
- Voyager 初始/结束工作区保持只读,远端刷新使用 git fetch —prune origin;
- origin/main 快照为 fb59f20192af82234201622927e52d0f56f271ad;
- #5533 ed098a74ecfd6a28ec3879cc37caf52291529a3b、#5562 948e11a0f17216123a080d2141e3a915c80dcb63、#5573 6a72bca17cd8bbc6ca88ad49338b5a8d4585dd6c、#5591 78767d844b27ff321ee99e24e527ae78ea4f98e0、#5581 cd37074a21aec25f061b96ea51050c72a2da84dc 均通过 git merge-base —is-ancestor origin/main;
- 真实代码证据来自各 PR merge commit 的 git show diff 与 origin/main 文件上下文,未修改 Voyager 工作区、未运行格式化/生成/安装命令。
GitHub 文档#
- 报告文件:outputs/voyager-daily-pr-study/2026-08-05-pr-5591-agent-image-pixel-inheritance.md
- 锚点 PR:https://github.com/adastralab-ai/voyager/pull/5591 ↗
- 关联 PR:5533 ↗、5562 ↗、5573 ↗、5581 ↗
- Voyager 锚点 commit:78767d844b27ff321ee99e24e527ae78ea4f98e0
- Voyager origin/main snapshot:fb59f20192af82234201622927e52d0f56f271ad
- 目标仓库:joyehuang/ai-agent-field-notes;本次推送后的目标仓库 commit 记录在自动化 study-log 中。
本报告的持久化来源只有 GitHub Markdown;metadata 中的 feishu_doc_url 固定为 null。