主题
工件声明系统
本文引用的源码文件
本文引用的文件
目录
引言
本文件聚焦“工件(Artifacts)声明系统”,即 AI 助手在完成任务后,如何把 Agent 执行过程中产生的文件、目录以及 AI 显式声明的产物,统一提取、合并、校验并展示给用户的完整链路。该系统由两部分组成:
- 静态工件提取:从任务执行日志中解析
agent_exec、agent_file、file_upload等工具调用,推断写文件、创建目录、删除路径等操作。 - AI 声明工件:从助手最终回答文本中解析形如
[[artifact:路径|可选说明]]的标记,并与静态工件合并去重,补充说明信息。
该系统的实现位于侧边栏模块,核心逻辑集中在 src/side_panel/artifacts-manager.js,并在聊天消息渲染流程中被 src/side_panel/chat-manager.js 消费;背景进程中的 ReAct 执行循环则负责生成可供分析的 executionLog。
项目结构定位
工件声明系统属于扩展端 MV3 Service Worker 架构下的侧边栏功能,不直接修改 Agent 源码,而是通过读取已持久化的任务执行日志和助手回复内容完成工件发现。
graph TB subgraph "侧边栏" CM["聊天管理器<br/>chat-manager.js"] AM["工件管理器<br/>artifacts-manager.js"] end subgraph "背景进程" RL["ReAct 执行循环<br/>react-loop.js"] end RL --> |"生成 executionLog"| CM CM --> |"传入 executionLog 与原始回复"| AM AM --> |"返回工件列表"| CM CM --> |"渲染按钮/弹窗"| AM
图表来源
- src/side_panel/artifacts-manager.js#L949-L1349
- src/side_panel/chat-manager.js#L2274-L2328
- src/background/react-loop.js#L850-L850
章节来源
核心组件
工件声明系统对外暴露的关键能力可归纳为以下接口:
| 接口 | 所在文件 | 职责 | 输入 | 输出 |
|---|---|---|---|---|
extractArtifactsFromExecutionLog | artifacts-manager.js | 从执行日志中提取静态工件 | executionLog 数组 | 工件数组 |
parseDeclaredArtifactsFromText | artifacts-manager.js | 从助手回复文本解析 [[artifact:...]] | 字符串 | 声明工件数组 |
mergeDeclaredArtifacts | artifacts-manager.js | 合并静态工件与声明工件并按路径去重 | 静态工件数组、声明工件数组 | 合并后的工件数组 |
buildArtifactsForMessage | artifacts-manager.js | 一站式构建消息级工件 | 执行日志、原始回复文本 | 最终工件数组 |
preFilterDeletedArtifacts | artifacts-manager.js | 同步移除已标记删除的工件 | 工件数组 | 过滤后的数量 |
filterInvalidArtifacts | artifacts-manager.js | 异步过滤工作目录外及不存在文件 | 工件数组 | 是否发生变化的布尔值 |
validateArtifactsAsync | artifacts-manager.js | 触发异步验证并更新徽章计数 | 工件数组、按钮元素 | Promise |
showArtifactsModal | artifacts-manager.js | 打开工件弹窗,支持排序、下载、定位、预览 | 工件数组、源按钮 | 无 |
这些接口共同构成“提取 → 声明 → 合并 → 预过滤 → 异步校验 → UI 展示”的流水线。
章节来源
- src/side_panel/artifacts-manager.js#L949-L1349
- src/side_panel/artifacts-manager.js#L1661-L1783
- src/side_panel/artifacts-manager.js#L1792-L1933
架构总览
工件声明系统的数据流可以概括为三个阶段:
数据准备阶段
- 背景进程执行 Agent 工具,产生结构化执行日志。
- 聊天管理器将执行日志与助手回复文本注入到消息 DOM 的
data-*属性中。
工件构建阶段
- 聊天管理器调用静态提取函数,得到基础工件集合。
- 若存在声明工件,则调用合并函数进行去重与说明补全。
- 同步预过滤已删除工件,避免无效条目进入后续流程。
交互与校验阶段
- 点击工件按钮时打开弹窗,渲染表格行。
- 异步校验工件是否仍在工作目录内且真实存在。
- 根据校验结果更新徽章计数、表格内容与空状态提示。
sequenceDiagram participant RL as "背景执行循环" participant CM as "聊天管理器" participant AM as "工件管理器" participant UI as "侧边栏界面" RL->>CM : "提供 executionLog 与助手回复" CM->>AM : "extractArtifactsFromExecutionLog(executionLog)" AM-->>CM : "静态工件列表" CM->>AM : "parseDeclaredArtifactsFromText(回复文本)" AM-->>CM : "声明工件列表" CM->>AM : "mergeDeclaredArtifacts(静态, 声明)" AM-->>CM : "合并工件列表" CM->>AM : "preFilterDeletedArtifacts(工件)" AM-->>CM : "过滤后的工件数量" CM->>UI : "渲染工件按钮" UI->>CM : "用户点击工件按钮" CM->>AM : "showArtifactsModal(工件, 按钮)" AM->>AM : "filterInvalidArtifacts(工件)" AM-->>UI : "更新弹窗与徽章"
图表来源
- src/side_panel/artifacts-manager.js#L949-L1349
- src/side_panel/artifacts-manager.js#L1777-L1933
- src/side_panel/chat-manager.js#L2274-L2328
详细组件分析
静态工件提取器
静态工件提取是工件系统的核心引擎,负责从 executionLog 中识别三类来源:
agent_exec:解析 shell 命令链,识别写入、创建、复制、删除、脚本执行、heredoc 内联脚本等。agent_file:处理显式的 write/delete 操作。file_upload:处理上传到工作目录的文件。
其关键行为包括:
- 按时间排序执行日志,保证“先创建后删除”的时序语义正确。
- 维护
seenPaths去重,同一路径只保留最后一次操作。 - 维护
createdRoots记录本任务创建的目录根,用于限制观察输出兜底解析的范围。 - 维护
deletedPaths记录删除操作,并在后处理阶段标记工件为已删除。 - 对
ls -R、find等列表输出做兜底解析,但仅在确有写操作或路径位于已创建根下时才采信。
flowchart TD
Start(["开始提取"]) --> Sort["按时间排序执行日志"]
Sort --> ScanScripts["预扫描脚本内容映射"]
ScanScripts --> LoopEntries{"遍历执行日志"}
LoopEntries --> IsToolExec{"是否为 tool_exec?"}
IsToolExec --> |否| NextEntry["跳过"]
IsToolExec --> |是| CheckAgentExec{"是否为 agent_exec?"}
CheckAgentExec --> |是| ParseExec["解析命令链<br/>mkdir/touch/echo/cat/cp/tee/rm/heredoc"]
ParseExec --> TrackCwd["跟踪 cd 与工作目录"]
TrackCwd --> ListFallback["列表输出兜底解析 ls/find"]
ListFallback --> UpdateRoots["更新已创建根集合"]
UpdateRoots --> MarkDeleted["记录删除路径"]
MarkDeleted --> NextEntry
CheckAgentExec --> |否| CheckAgentFile{"是否为 agent_file?"}
CheckAgentFile --> |write| AddWrite["添加写入工件"]
CheckAgentFile --> |delete| MarkFileDelete["标记文件删除"]
CheckAgentFile --> |其他| NextEntry
AddWrite --> NextEntry
MarkFileDelete --> NextEntry
NextEntry --> EndLoop{"是否还有条目"}
EndLoop --> |是| LoopEntries
EndLoop --> |否| PostProcess["后处理:标记被删除工件"]
PostProcess --> Return["返回工件列表"]
图表来源
章节来源
AI 工件声明解析与合并
AI 声明工件采用固定标记格式:
- 基本语法:
[[artifact:路径|可选说明]] - 变量引用路径会被丢弃,因为无法静态确定真实文件名。
- 声明工件与静态工件按规范化路径去重;若命中已有静态工件,则优先补充说明字段。
合并策略要点:
- 规范化路径时去除前导
./,使a.xlsx与./a.xlsx视为同一文件。 - 若声明路径与静态绝对路径尾段一致,也视为同一文件,避免重复行。
- 声明工件默认类型为
file,工具名为declared,时间戳为空,状态为unknown。
flowchart TD Text["助手回复文本"] --> Parse["正则匹配 [[artifact:路径|说明]]"] Parse --> FilterVars["过滤含变量的路径"] FilterVars --> DedupKey["计算规范化去重键"] DedupKey --> DeclaredList["声明工件列表"] StaticList["静态工件列表"] --> Merge["按路径去重合并"] DeclaredList --> Merge Merge --> Result["最终工件列表"]
图表来源
章节来源
聊天消息集成
聊天管理器负责把工件系统与消息 UI 连接起来:
- 从消息元素的
dataset.executionLog恢复执行日志。 - 从
dataset.declaredArtifacts恢复声明工件。 - 调用静态提取与合并函数,得到最终工件列表。
- 同步预过滤已删除工件。
- 动态创建或复用工件按钮,绑定点击事件。
- 立即触发异步验证,更新徽章计数。
sequenceDiagram participant CM as "聊天管理器" participant AM as "工件管理器" participant DOM as "消息 DOM" CM->>DOM : "读取 dataset.executionLog" CM->>DOM : "读取 dataset.declaredArtifacts" CM->>AM : "extractArtifactsFromExecutionLog" AM-->>CM : "静态工件" CM->>AM : "mergeDeclaredArtifacts" AM-->>CM : "合并工件" CM->>AM : "preFilterDeletedArtifacts" AM-->>CM : "过滤后数量" CM->>DOM : "创建/复用工件按钮" CM->>AM : "validateArtifactsAsync" AM-->>CM : "异步校验完成"
图表来源
章节来源
工件弹窗与交互
工件弹窗提供以下交互能力:
- 按文件名、类型、时间排序。
- 双击文件名预览支持的文件。
- 单击行或定位按钮定位到工作目录。
- 复制文件名或按住 Ctrl/Cmd 复制绝对路径。
- 单文件下载或目录 Zip 下载。
- 异步校验后隐藏无效工件,全部无效时显示空状态。
弹窗的生命周期包括:
- 打开时重置排序状态。
- 渲染表头与表格行。
- 绑定行事件与表头排序事件。
- 异步校验后重新渲染或显示空状态。
- ESC 关闭、遮罩挂载位置适配嵌入模式。
flowchart TD
Open["showArtifactsModal"] --> ResetSort["重置排序状态"]
ResetSort --> BuildOverlay["构建遮罩与弹窗 DOM"]
BuildOverlay --> RenderRows["渲染排序后的工件行"]
RenderRows --> BindEvents["绑定行事件与表头排序"]
BindEvents --> AsyncValidate["filterInvalidArtifacts 异步校验"]
AsyncValidate --> ValidCount{"是否仍有有效工件?"}
ValidCount --> |否| ShowEmpty["显示空状态"]
ValidCount --> |是| UpdateRender["更新计数与表格"]
ShowEmpty --> Badge["更新徽章计数"]
UpdateRender --> Badge
Badge --> Close["ESC/关闭按钮隐藏弹窗"]
图表来源
章节来源
背景执行循环与 observation 的关系
背景进程中的 ReAct 执行循环会生成包含 observation 字段的执行日志。工件管理器会利用该字段进行两类增强:
- 从
agent_file或agent_exec的观察输出中尝试解析真实路径。 - 对
ls -R、find等列表输出做兜底解析,反推可能的新建工件。
文档注释明确指出:日志中的 observation 会被产物提取逻辑用于解析路径。
章节来源
- src/background/react-loop.js#L850-L850
- src/side_panel/artifacts-manager.js#L1180-L1183
- src/side_panel/artifacts-manager.js#L1097-L1137
依赖关系分析
工件声明系统内部依赖如下:
graph LR AM["artifacts-manager.js"] --> WM["workspace-manager.js"] AM --> WP["workspace-panel.js"] AM --> Utils["side_panel/utils.js"] AM --> I18n["shared/i18n.js"] AM --> Logger["shared/logger.js"] CM["chat-manager.js"] --> AM RL["react-loop.js"] --> CM
workspace-manager.js:提供工作目录根、家目录、文件预览支持判断、文件流下载等能力。workspace-panel.js:提供定位文件、预览工件、解析绝对路径等能力。utils.js:提供 Toast、剪贴板复制等通用 UI 辅助。i18n.js:提供中英文文案。logger.js:提供错误日志记录。
外部依赖方面,工件系统通过 Chrome Runtime 消息机制与背景进程通信,例如批量检查文件是否存在时发送 CHECK_FILES_EXIST 消息。
图表来源
章节来源
性能与复杂度
工件提取算法的时间复杂度主要受执行日志规模影响:
- 排序:O(n log n)。
- 主循环:O(n),其中每条日志可能触发命令链拆分、正则匹配、路径展开、脚本内容分析等。
- 列表输出兜底解析:对
observation字符串做 token 化与规则过滤,近似 O(m),m 为输出 token 数。 - 后处理删除标记:O(k × d),k 为工件数,d 为删除路径数。
空间复杂度方面:
seenPaths、scriptContentMap、deletedPaths、createdRoots均随日志规模增长。- 大体积
observation或长脚本内容会增加内存占用。
优化建议:
- 对超大执行日志可考虑分页或增量构建工件。
- 对频繁调用的
normalizePath、expandBraces可引入缓存。 - 对 Agent 离线场景保持降级策略,避免阻塞 UI。
[本节为通用性能讨论,不直接分析具体代码片段]
故障排查指南
工件按钮未出现
可能原因:
- 执行日志为空或格式异常。
- 没有检测到写文件、创建目录或上传文件操作。
- 所有工件均被判定为已删除、不在工作目录内或不存在。
排查步骤:
- 检查消息元素是否包含
dataset.executionLog。 - 确认
extractArtifactsFromExecutionLog能返回非空列表。 - 检查
preFilterDeletedArtifacts是否移除了全部工件。 - 检查
filterInvalidArtifacts异步校验是否因 Agent 离线而失败。
章节来源
- src/side_panel/chat-manager.js#L2274-L2328
- src/side_panel/artifacts-manager.js#L1777-L1783
- src/side_panel/artifacts-manager.js#L1661-L1733
工件数量与实际不符
可能原因:
- 声明工件与静态工件路径不一致导致重复或遗漏。
- 相对路径与绝对路径未正确归一化。
- 工作目录未知时,目录外工件未被过滤。
- Agent 离线时,存在性检查失败,系统降级保留工件。
排查步骤:
- 检查
dataset.declaredArtifacts是否正确序列化。 - 检查
mergeDeclaredArtifacts的去重键计算。 - 检查工作目录根是否可通过
getWorkspaceRoot获取。 - 检查
CHECK_FILES_EXIST响应是否成功。
章节来源
- src/side_panel/artifacts-manager.js#L1298-L1337
- src/side_panel/artifacts-manager.js#L1661-L1733
- src/side_panel/chat-manager.js#L2284-L2286
工件预览或定位失败
可能原因:
- 文件类型不支持预览。
- 工作目录面板未正确加载。
- 文件已被删除或路径失效。
- 家目录
~/未展开为绝对路径。
排查步骤:
- 检查
supportsPreview是否返回 true。 - 检查
locateFileInWorkspace是否抛出错误。 - 检查
expandHomePath是否正确展开家目录。 - 检查
isPathInsideWorkspace是否误判路径归属。
章节来源
- src/side_panel/artifacts-manager.js#L1354-L1360
- src/side_panel/artifacts-manager.js#L1621-L1644
- src/side_panel/artifacts-manager.js#L1488-L1502
测试用例参考
单元测试覆盖了工件声明链接化与历史消息恢复时的声明工件合并行为,可作为理解系统行为的参考。
章节来源
结论
工件声明系统通过“静态分析 + AI 声明 + 异步校验”的三段式架构,实现了从 Agent 执行日志到用户可见工件展示的完整闭环。其设计重点在于:
- 对 shell 命令链、脚本内容、列表输出进行多层次解析,提高工件发现覆盖率。
- 通过时序把关与根目录约束,降低误报风险。
- 通过声明工件机制,允许 AI 显式补充静态分析无法确定的工件信息。
- 通过异步校验与降级策略,在 Agent 离线或文件变化时保持用户体验稳定。
对于开发者而言,扩展工件系统时应优先关注:
- 新增工具调用类型时,补充对应的路径提取逻辑。
- 新增声明格式时,同步更新解析与合并逻辑。
- 变更路径解析规则时,确保相对路径、绝对路径、家目录、工作目录边界条件正确。
- 变更 UI 行为时,确保事件绑定、弹窗生命周期与徽章计数保持一致。