Human Feedback
将任何 HTML 页面转换为用户可评论的形式:左侧为目录,右侧为用户当前阅读内容的笔记栏,以及一个复制按钮:无笔记时显示 复制反馈,首次复制前显示 复制 N 条笔记,之后显示 复制 N 条新笔记 或 再次复制。用户将复制的文本粘贴到聊天中;你应用这些反馈。每次按键都会保存在用户的浏览器中。
此版本不发送使用统计数据。
- Claude Code 和 Codex 均能加载此文件。
allowed-tools是 Claude Code 的字段;Codex 会忽略它。 $SKILL_DIR表示加载此 SKILL.md 的文件夹路径(例如~/.claude/skills/human-feedback或~/.codex/skills/human-feedback);在命令中直接写入该字面路径。每次 shell 调用都是全新的 shell。- 在 Codex sandbox 中,
add_notes.py通过show.py打开页面,show.py通过 AppleScript 驱动 Chrome,可能需要用户批准:请求批准,而不是跳过第 3 步。 - 每个步骤中页面表现(你的光标、每轮打开的欢迎语、收到的反馈和完成状态)在
references/what-the-user-sees.md中:当用户询问页面行为时,请阅读该文件。
何时使用
- 当你要求用户审阅、批准或选择你制作的工作(报告、计划、草稿、原型、选项、拟议规则),或用户要求审阅时 → 将其放入此页面,无需被要求。
- 工作尚未形成页面 → 从它构建纯 HTML 并保留原始文件作为源文件。
- 不需要审阅页面的独立问题仍留在聊天中。
- 在笔记中回答用户的问题。 对提出问题的笔记使用
--reply,即使该笔记同时也要求修改。将修改、更长的解释和你自己的审阅问题放在页面的相关部分。只有当用户要求时,才使用--ask或--again创建包含你自己问题的气泡。 - 一旦页面准备好接收用户的笔记,并且在之后的每一轮:如果在你的可用技能列表中有
inbox-for-human(其名称,带或不带插件前缀),使用技能工具加载它(在 Codex 中,打开其技能列表指向的SKILL.md)并将该页面加入队列。如果不在列表中,说一次:“在没有 inbox-for-human 技能的情况下运行,该技能本可以将此页面保留在审阅列表中。”
首次使用
在首次设置页面之前,运行 python3 "$SKILL_DIR/scripts/self_check.py" 并将其消息以简明语言传递。在它通过后不要重新运行;脚本会记住检查已通过。--force 在需要时重新运行。
如果你在此处完全无法运行命令,则不要尝试。请精确地说:“human-feedback 无法在此模式下运行,因为我无法在此处操作你计算机上的文件。在 Claude 桌面应用程序中,打开“代码”选项卡并选择“本地会话”。在 ChatGPT 桌面应用程序中,切换到 Codex 并保持本地模式。”
退出码 2 或该消息意味着停止:不要尝试构建页面。退出码 1 提供一个用户自行打开检查的文件。
1. 标记各部分
在每个用户可能作为一个单元评论的部分上添加 data-note:卡片、章节、选项、表格行。标签来自该部分的第一个标题,因此通常只需一个裸 data-note 即可。对于没有标题或其标题与另一部分重复的部分,需要提供自己的标签:data-note="选项 B · 平静"。页面上的标签必须唯一。
对于从其自身脚本中绘制部分的页面,只要脚本立即绘制它们,而不是在稍后的事件(如 DOMContentLoaded 或点击)上绘制,这些部分也能获得笔记。不要将它们复制到 HTML 中;第 2 步的计数仅涵盖直接写在 HTML 中的部分。
标记 <iframe> 中显示的本地页面周围的部分。第 2 步会为框架页面添加标签,使其文字提供“评论”、“编辑”和“高亮”功能。审阅页面会保存反馈,并在复制的编辑中命名框架源文件。
永远不要标记要求用户填写或重写的文本,也不要要求用户编辑页面:用户可以在任何时候通过点击或选择文本后弹出的菜单中的“编辑”选项自行重写任何行(⌘Z 可以撤销编辑)。而是提出疑问(第 6 步)。
不要构建自己的注释框、输入字段或复制按钮。交互层会处理所有这些。
左侧的目录已经是页面的导航:它列出每个部分,将部分外部的章节标题显示为标题,标记当前视图中的部分,并跳转到页面自己顶部栏之外的部分。在提供自己的导航(部分选项卡、内容列表、跳转链接、带上一页/下一页的步骤计数器)之前,判断哪种方式对用户最方便,以便用户永远不会遇到两种方式来做同一件事:
- 如果它只是重复了目录 → 省略它。将部分和章节标题命名为其按钮本应显示的名称。
- 如果它做了目录无法做到的事情(暗色/亮色切换、过滤器、逐部分滚动页面的上一页/下一页按钮) → 仅保留该部分功能。
- 如果它以选项卡或其他隐藏面板显示部分 → 将其制作成一个滚动页面。用户要求选项卡 → 标记它们并仅写这些:一个
role="tablist"包含<button role="tab" aria-controls="…">,每个按钮命名一个role="tabpanel",然后照常在面板内部放置data-note部分。交互层会切换面板、绘制一个固定的粘性栏、在目录中将每个选项卡显示为其层级的一部分(它在页面上的位置)、在重新加载和版本间保持打开的选项卡、在目录行、步骤导航或#链接进入时打开对应选项卡,并保持线程直到其选项卡打开。带有自己选项卡脚本的页面保持其原有功能;带有class的 tablist 保留页面自己的样式。 - 每个页面从第二轮开始获得版本(第 5 步) → 不要绑定 ← 和 → 键;版本步进器会使用它们。
对于托管交互层的任何页面(由其他技能或手动制作的页面),提供的扩展点:
data-note部分如前述方式提供目录和笔记栏的数据。- 一旦交互层绘制完毕,
data-notes-layer会被设置在<html>上;在没有交互层的页面上则不存在。宿主页面可以使用它来隐藏自己的目录,或隐藏那些只有在没有交互层时才有意义的行,这样用户永远不会看到两个。
2. 开启注释功能
python3 "$SKILL_DIR/scripts/add_notes.py" <page.html> --agent "Claude"
该脚本在 </body> 之前添加一个标签,打印轮次和部分计数,并通过 show.py 打开页面。重新运行、--next-round 以及新版本的后续运行都会打开页面;单独的 --new-version、--received 和 --converged 则不会。如果打开失败,页面保持就绪状态,运行会打印失败原因和手动打开页面的命令。在第一轮中,它还会在页面的 <head> 中放置一个小的样式,该样式保持页面隐藏,直到交互层绘制完毕。重新运行会替换该标签,因此在重写页面后再次运行。如果出现“没有部分”或“重复标签”的警告 → 修复标记并重新运行。
-
--hold:准备好页面但不打开它,以便你构建或检查它,或当用户仅要求链接时(第 3 步)。 -
--pictures off:为与他人共享或公开构建的页面禁用图片附件;后续运行保留此设置。 -
--agent:你的众所知的名字。Claude Code 传递Claude,Codex 传递Codex,其他代理传递其自己的产品名称。一个携带该名称和你 Logo 的光标会在页面上执行你的操作:它输入你的消息,并在笔记栏的状态中点击(第 4 步中的“已收到”和“更新中”,第 8 步中的“完成”)。后续运行会保留该名称,因此在第一次运行时传递它。 -
--helper "<name>"和--owns "<name>:[Part label]":其他代理(你派出的子代理、Codex,或不是你自己的任何人)制作了页面的部分。每个辅助代理命名一次,使用其知名名称;使用--owns指定它制作的每个部分,标签格式与目录中显示的一致;两者可重复,最多三个辅助代理。你保持为主导(--agent)。在页面上,每个代理的光标只操控它拥有的部分。后续运行会保留这些设定;带任一标志的运行会替换它们;新版本从无开始,因此当这些部分仍然属于其他代理时,在后续运行中再次传递它们。如果你自己制作了所有内容 → 两者都不传递。python3 "$SKILL_DIR/scripts/add_notes.py" <page.html> --agent "Claude" --helper "Codex" --owns "Codex:[Option B · Calm]" -
--workspace <dir>:你的工作区(你工作的项目文件夹)。每次运行会在<dir>/human-feedback/attachments/下创建目录(如果缺失),并在human-feedback/下放置一个标记文件;页面会将用户附加的图片保存在那里(用户在 Chrome 中选择一次human-feedback文件夹后),页面上的提示会提到你(“在 Claude 的工作区文件夹中,选择 human-feedback 子文件夹…”)。如果不提供该参数:使用页面所在仓库,如果不在仓库中则使用当前目录,然后使用当前目录。后续运行会保留标签中命名的文件夹。 -
如果页面通过 HTTP 提供服务(它使用
fetch、模块或../资源) → 添加--copy,这会将human-feedback.js复制到页面旁边。后续运行、重写和新版本会保持该设置并说明。 -
永远不要将
human-feedback.js粘贴或内联到页面中,也不要读取它:标签就是页面所需的一切。 -
将关于页面问题放在它们所涉及的部分中(第 6 步)。
外观
- 默认跟随系统外观。当审阅的材料是共享阅读样式的散文,或当用户要求控制时,提供“自动”、“亮色”和“暗色”选项。在其他审阅中保留此控制。
- 当提供控制时,如果阅读列已有两种调色板且你可以检查两者,则使用
--appearance page。在同一个页面中重复使用它们。对于具有独立主题、没有经过测试的调色板或外观是审阅一部分的页面,使用--appearance sidebars仅切换目录和笔记栏。保持截图和嵌入内容不变。 - 在阅读列的 CSS 中,
html[data-notes-layer="light"]和html[data-notes-layer="dark"]表示显式选择。保留系统媒体查询用于“自动”模式以及页面未加载交互层时。用户的选择在此次审阅的各个轮次间保持不变。其他审阅从“自动”模式开始。 - 重新运行或通过
--new-version复制的版本会保留该设置。要移除控制,传递--appearance off;或者当从零开始重建文件时,再次传递所需的设置。
3. 显示它
对于任何类型的审阅,在每一轮中:
- 一旦审阅准备好,在每一轮中在用户浏览器中打开它。当它在同一轮中找到一个较早的页面时,打开器会重用 Chrome 中现有的标签页。在构建或检查页面时,或者当用户明确要求仅提供链接时使用
--hold。 - 对于本地审阅,使用不带
--hold的add_notes.py完成。在专用文件夹中新建审阅时,如果其资源位于该文件夹中,则所有轮次使用一个本地 web 地址,这样浏览器会保持页面缩放和笔记。在资源文件夹或其子文件夹中保留轮次和资源,并使用相对资源路径。链接打开器打印的审阅地址。 - 对于已通过 HTTP 提供的页面,使用
--hold --copy准备,并运行show.py <local page.html> --preview-url <new round's HTTP URL>。跨轮次保持相同的预览服务器,以便打开器能找到现有标签页且浏览器保留用户的笔记。 - 在每一轮回复中提供页面的链接。使用你的项目链接格式,配合打开器的结果来说明页面位置。如果打开器失败或留下页面关闭状态,请说明并给出链接。
- 如果页面之前的轮次或版本是通过
file://打开的 → 继续通过file://显示(show.py按其路径运行),并且不提供 localhost 链接,即使项目要求一个:浏览器按地址保存用户的答案,因此 localhost 副本不会显示任何答案,并且会重新打开用户已关闭的线程。在回复中说明为何没有链接。
仅在页面准备好接收用户的笔记时执行此操作。
一旦页面准备好接收用户的笔记,运行不带 --hold 的 add_notes.py。首先完成页面内容并标记每个部分。在该运行中传递任何已完成的标记和用户请求的气泡(第 5 步中新版本的后续运行)。
你告知用户正在进行的修复也会持有下一个版本。一旦修复生效,再显示它。
对于检查,使用 --hold 准备它,然后你自己检查(或让子代理截图),此时交互层已打开,因为交互层会缩小页面,然后修复你发现的问题。你的消息(问题、回复)仅在标签页获得焦点后才会出现,并且无头浏览器不会聚焦它,除非启用了焦点模拟(tests/harness.mjs 会启用它)。如果没有焦点模拟,改为检查标签数据中的消息;页面本身在其开头背后保持空白:使用 reading-page-formats 的 shoot_page.py 拍摄快照,该脚本会截取并删除一个不带标签和持有样式的裸副本。该裸副本会填满整个窗口,但在用户屏幕上,注释列每侧大约占用 375px(在 2560 宽度下测量)。因此,也要以该较窄宽度拍摄,并在显示之前修复任何换行或溢出问题。裸副本无法显示覆盖页面的列,因此还要加载带有交互层的持有页面,并检查 document.querySelector('h1').getBoundingClientRect().left 是否在目录边缘的右侧;如果页面没有 h1,使用第一个文本元素即可。只有在那之后,才使用上面的本地或 HTTP 命令打开页面。告诉用户页面已准备好接收他们的笔记、在哪里(打开器打印的行),以及让用户粘贴“复制”功能提供的文本。如果你在页面中提出了问题或留下了回复,告知用户页面中有多少等待处理。
一旦页面显示,将聊天保持在准备行、链接以及任何阻止页面打开的内容或其他技能所需的内容。将更改、发现、来源、注意事项、检查和问题放在它们涉及的部分中。当页面有“更多信息”时,使用它来提供来源和详细信息。
你框架的页面会携带框架标签,因此单独打开时也会显示注释列并被压缩,检查时会报告虚假缺陷。改为在你的草稿板中从裸副本进行检查:移除持有样式。