mcpField
J
jargon-extractor

jargon-extractor

github.com/gohypergiant文档处理2578 次安装2 周前更新
代码仓库

关于 jargon-extractor

用于从文档中提取陌生术语、缩写和行话,并维护按字母排序的 JARGON.md 词汇表的技能。它将需要判断的提取与筛选工作和确定性的文件写入分开,避免大量术语内容进入编排器上下文。

主要功能

  1. 01发现常见目录中的文档
  2. 02每批最多提取五个文件
  3. 03对候选术语进行关联和筛选
  4. 04通过确定性脚本合并词条
  5. 05在 AGENTS.md 或 CLAUDE.md 中添加词汇表链接

适用场景

  1. 01为一组文档创建术语表
  2. 02文档更新后维护现有术语表
  3. 03在 Agent 指引中添加术语表引用

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gohypergiant/agent-skills --skill jargon-extractor

安装后在 Claude Code 中输入/jargon-extractor确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CHANGELOG.md
README.md
evals/
evals.json
evals/files/
eval1-architecture.md
eval1-onboarding.md
eval2-notes.md
eval3-notes.md
scripts/
merge_jargon.py

详细介绍

术语提取器

读取一组文档,标记出新读者不熟悉的内部术语、缩略词和行话,并在多次运行之间维护一份按字母排序的 JARGON.md 词汇表。

这项工作分为三个阶段,各有不同的失败模式和不同的职责归属。提取高度依赖判断:判断某个词是否算作术语取决于对文档的理解。关联、整理和合并同样高度依赖判断:判断某个候选项是否真的应该进入词汇表,以及两个定义是否描述的是同一个概念。归档则是机械性的:排序、去重,并每次都正确地写入文件。提取和合并在一次性的子 Agent 上下文中进行,只回报一段简短摘要;归档是一个确定性脚本,直接在磁盘上读写文件,完全不需要将其内容回显到任何模型的上下文中。编排器自身的上下文只保存文件路径和小型计数,从不保存提取出的大量术语。

提取和合并都作为子 Agent 运行,但每次运行各只运行一次,而不是每波运行一次。

执行流程

wave 1 (up to 5 files)        wave 2 (up to 5 files)      ...
+---------+  +-----------+    +---------+  +-----------+
| file A  |->| extractor |    | file F  |->| extractor |
+---------+  +-----------+    +---------+  +-----------+
     ...          ...              ...          ...

each extractor writes its findings to a file and returns only a
one-line confirmation; waves run one after another, capped at 5
concurrent extractors at a time, purely to bound concurrency

once every wave has finished:

  all extraction file paths            +---------+     +--------------------+
  + the glossary path        ------->  | reducer | ---> | merge_jargon.py    |
                                        | (whole  |      | upserts into        |
                                        |  run)   |      | chosen JARGON.md    |
                                        +---------+      +--------------------+

  the reducer reads every extraction file and the current glossary
  itself, correlates and merges once across the whole run, and
  writes one entries file plus a short summary
  1. 收集输入文件并将其分组为每波最多五个,纯粹是为了限制并发。
  2. 预先一次性定位(或规划)目标词汇表路径。
  3. 提取:运行每一波,每个提取器将其发现写入文件,并只返回一段简短确认。
  4. 归约:在所有提取完成后运行一次,由一个子 Agent 将一切与当前词汇表进行关联和合并。
  5. 归档:一次 merge_jargon.py 调用将归约器的输出合并进词汇表。
  6. 链接:如果 AGENTS.md 或 CLAUDE.md 存在,则添加一个指向词汇表的引用(如果尚未存在)。
  7. 向用户报告摘要。

步骤 1:收集输入文件

采用用户提供的文件路径。如果他们指向的是一个目录而非文件列表,则展开它(遵循他们的意图:像"从 docs 文件夹提取术语"这样的请求意味着该目录下所有可读的文本或 markdown 文件,而不是整个仓库,包括 node_modules、dist、.git、锁文件和二进制资源)。

自动发现常规位置。 检查以下位置,如果存在则纳入:

  • docs/、documents/、doc/(递归——markdown 和纯文本)
  • README.md、CONTRIBUTING.md、SECURITY.md、COMPLIANCE.md、LICENSE*
  • ARCHITECTURE.md、AGENTS.md / CLAUDE.md、CONSTRAINTS.md
  • 决策记录风格的文件夹(如果存在):adr/、decisions/、rfd/、 rfcs/、notes/、spikes/ —— 任何存放 NOTE/SPIKE/RFC 风格 文档的文件夹都是高信号的

将得到的文件列表分组为每波最多五个,仅用于提取并发。五是一个起点,而非硬性上限:它保持了每个文件一个子 Agent 的设计(这正是让每次提取保持聚焦、其 JSON 输出保持整洁的原因),同时限制了同时运行的数量。如果某一波明显让环境吃紧,就调低它;如果环境能轻松处理更多并发且文件较小,就调高它。

同时为本轮运行的中间文件创建一个暂存目录,并记录其路径。以下步骤中的每个提取和归约输出都写入该目录下,从不以内联形式返回。

将其显式固定在 /tmp 下,而不是使用裸 mktemp -d(其默认位置因平台而异,在 macOS 上会解析到某个之后难以再找到的地方):

mktemp -d "/tmp/jargon-extractor.XXXXXX"

步骤 2:定位目标术语表

在做其他任何事之前,先在仓库中搜索是否已存在 JARGON.md。使用全仓库范围的搜索(find、rg、grep 或同等工具),而不是只检查根目录或 docs/。

  1. 如果仓库中任何位置恰好只有一个 JARGON.md,那么它就是本次运行后续步骤所使用的术语表路径。
  2. 如果存在多个 JARGON.md,停止并展示匹配结果。询问用户哪一个才是权威版本,而不是猜测。
  3. 如果都不存在,默认在仓库根目录新建 ./JARGON.md。
  4. 在继续之前,先公布所选的路径。

选择时该文件不需要已经存在;merge_jargon.py 会在首次写入时创建它。无论哪种情况,你在这里只需要路径,而不是文件内容:步骤 4 中的 reduce 子代理会自行在自己的临时上下文中直接从磁盘读取当前内容。编排器从不需要在自己的上下文中持有术语表的内容。

步骤 3:提取(子代理阶段,一次一波)

对于每一波,为该波中的每个文件生成一个子代理,全部在同一轮中生成,使它们并行运行,然后等待该波完成后再开始下一波。如果当前环境没有可用的子代理或 Task 工具,就完全跳过生成子代理这一步,改为自己逐个处理每个文件,在进入步骤 4 之前应用下面相同的提取说明。请注意,没有子代理就无法让这项工作不占用主上下文;如果在这样的环境中文件集很大且这很重要,可以考虑在多轮中每轮处理少量文件,而不是一次性全部处理。

提取子代理任务模板:

Read exactly one file: <path>

Flag every term in this file that a competent outside reader would not
already know: internal terminology, shorthand, abbreviations, acronyms,
and project- or domain-specific words. This includes terms that are
central to how the system works even if they are not abbreviations.

The test: would someone new to this specific project or domain need
this explained? That includes acronyms and initialisms (expanded and
defined, not just expanded), internal tool/system/process names,
shorthand the document uses without defining, and domain terms from a
specialized field the document assumes familiarity with. It excludes
plain vocabulary and terms already standard enough that defining them
would insult the reader (e.g. "function," "database," "HTTP" used in
their standard sense). When in doubt, flag it; over-flagging is cheap
to fix later, under-flagging loses information silently.

For each flagged term, give a plain English definition. Use the file's
own explanation if it states one; otherwise infer the meaning from
context. Flag any definition you are genuinely unsure about by noting
the uncertainty inline rather than presenting a guess as settled fact.
Keep each definition to one or two sentences; if a term needs more than
that to explain, give the short version anyway rather than writing an
exhaustive one.

Write your findings as strict JSON, nothing else, no markdown fences,
to this exact path: <output-path>
[
  {"term": "sidecar", "definition": "A helper process deployed
   alongside the main service to handle a supporting concern, such as
   logging or metrics, without living in the main service's codebase."},
  ...
]
If there is no jargon worth flagging, write an empty array: []

Then reply with ONLY a one-line confirmation, for example:
Wrote 6 terms to <output-path>
Do not repeat the extracted terms in your reply; they are already on
disk at <output-path>, and repeating them here only spends context
budget that the next step does not need.

将 <output-path> 替换为本次运行暂存目录下的真实路径,例如 <scratch-dir>/architecture.json,并将 <path> 替换为真实文件路径。随着各波完成,持续记录每个提取输出路径的列表;这个列表就是步骤 4 所需的全部内容,而且持有它的成本几乎为零,因为它只是文件路径。

步骤 4:归约(子代理阶段,整个运行只执行一次)

每一波都完成后,为整个运行生成一个归约子代理。将步骤 3 中收集的每个提取输出路径以及步骤 2 中的术语表路径都交给它。

归约子代理任务模板:

You are the merge step for a jargon extraction run.

Read these extraction result files, each a JSON array of
{"term", "definition"}:
  <path-1>
  <path-2>
  ...

Also read the current glossary, if it exists, at:
  <glossary-path>
Treat every term already there as part of the same pool you are
correlating against, not just the newly extracted terms. If the file
does not exist yet, there is nothing existing to correlate against.

CURATE: before correlating, drop any newly extracted candidate that
does not actually earn a glossary entry. The extraction step is
deliberately biased toward over-flagging, since catching a borderline
term there is cheap and missing one silently loses information. You
are the one point in the run with enough context, every extraction
plus the existing glossary, to narrow that list back down safely.

Keep a candidate if either is true:
- It carries a meaning specific to this repo, project, or domain that
  an outside reader could not infer from the word alone (an internal
  tool, system, process, or an acronym this project coined or redefined).
- Its words are common on their own, but this repo uses them in a
  specific way that would be easy to get wrong without the definition
  (a plain word repurposed here as a term of art).

Drop a candidate if it is any of:
- A literal filename, path, config key, or CLI flag, where the name
  itself is the explanation and nothing more needs saying.
- A generic engineering term that would mean the same thing in any
  codebase, not just this one (e.g. "database," "endpoint," "cache"),
  unless this project overloads it with a distinct meaning.
- An obvious code symbol or identifier whose name is just descriptive
  English with no special behavior behind it.
- A standard industry term or acronym any practitioner in the relevant
  field already knows, used here in its ordinary sense, not redefined
  by this project.

Do not apply this bar retroactively to terms already in the glossary;
an earlier run already decided those were worth keeping, and silently
removing them is a bigger decision than this run is making. Curation
only narrows what this run is about to add, never what is already filed.

CORRELATE: group entries that refer to the same term, case-insensitively.
Two entries are the same term if they are the same word or phrase modulo
case (API / api) and trivial punctuation or spacing differences
(sub-agent / subagent) that clearly mean the same thing. Do not merge
two different concepts just because they share a substring. Do not
merge an acronym with its own expansion unless the source treats them
as one entry (CDC and "Change Data Capture" are the same entry only if
used interchangeably; keep them separate if the expansion is a
description rather than the term itself).

MERGE: for any term with more than one definition in the pool, write
one definition at least as informative as any single source, never one
that drops information, and still no more than a couple of sentences
unless a genuine multi-sense split requires it:
- If two definitions say the same thing differently, combine them into
  the clearer, more complete phrasing.
- If two definitions describe genuinely different senses of the same
  term, keep both, numbered inside one definition: "(1) ... (2) ...".
  Do not split into separate term (context) entries; the term must
  stay one stable lookup key so future runs can find and update it.
- If you are not confident two entries describe the same underlying
  concept, do not force a merge. Keep them separate and say so in your
  summary. A wrong merge is worse than a duplicate: a duplicate is easy
  to spot later, a wrong merge quietly loses information.

OUTPUT: write a JSON array containing ONLY the terms that are new, or
whose definition changed as a result of this run, and that survived
curation above, to this exact path:
  <output-path>
[{"term": "...", "definition": "..."}, ...]
Leave out any term whose meaning is unchanged from what is already in
the glossary, and any term curation dropped. Do not reproduce the
untouched glossary into this file; list only what is new or different.

Then reply with ONLY a short plain-text summary: how many terms you are
adding, how many you are updating (and one short clause why, per term),
how many you saw but left unchanged, how many you dropped during
curation (grouped by the reason above, e.g. "3 dropped: generic
engineering terms"), and any cases where you deliberately kept entries
separate instead of merging them. Do not include the full entries list
in your reply; it is already written to <output-path>.

替换为提取文件路径的完整列表、真实的术语表路径,以及暂存目录下的真实输出路径,例如 <scratch-dir>/merged.json。

步骤 5:归档结果

针对归约子代理的输出运行合并脚本。这是一次普通的 bash 调用,不是子代理:脚本直接读取和写入文件,因此其内容永远不必经过任何模型的上下文,只有它的小结需要经过。

scripts/merge_jargon.py 随此 skill 一起提供,其路径相对于此 SKILL.md 文件,而不是相对于你正在处理的项目。bash 工具从项目的工作目录运行,因此裸写 scripts/merge_jargon.py 只有在该目录恰好是 skill 自己的文件夹时才能解析成功,而在正常使用中并不会如此。先解析出此 skill 自身目录(即加载此 SKILL.md 的同一目录)的绝对路径,然后使用该路径调用脚本:

python3 "$SKILL_DIR/scripts/merge_jargon.py" <glossary-path> <scratch-dir>/merged.json

$SKILL_DIR 代表此 skill 自身的绝对目录;替换为实际路径。术语表路径参数是步骤 2 中的那个,相对于项目(或项目内的绝对路径),而不是相对于 skill 目录。

脚本会报告新增、更新和未更改的术语数量,如果条目文件中包含重复术语,或现有术语表的条目区域中存在无法解析的行,脚本会报错退出(而不是猜测)。如果需要确切的标志,请阅读 "$SKILL_DIR/scripts/merge_jargon.py" --help;该脚本有意设计得很严格,因此请把它拒绝写入视为修正输入而非绕过它的信号。如果想在提交更改前预览,可以先加上 --dry-run。

第 6 步:从 Agent 行为文件链接术语表

术语表只有在有东西指向它时,才能对未来的 Agent 运行有用。JARGON.md 本身应保持为纯术语列表,它不应引用 Agent 行为文件,但反过来应当成立:AGENTS.md 或 CLAUDE.md(如果其中任一存在)应引用术语表,以便后续在本项目中工作的 Agent 将其作为上下文获取。

按顺序检查项目根目录下是否存在 ./AGENTS.md 或 ./CLAUDE.md。如果 ./CLAUDE.md 存在,但其全部内容只是一个指向 AGENTS.md 的导入指针(例如,文件内容实质上只是 @AGENTS.md),并且 ./AGENTS.md 也存在,则将 AGENTS.md 视为真正的目标,而不是编辑那个指针文件。如果两个文件都不存在,则跳过此步骤;不要从头创建一个,这超出了本 skill 的范围。

如果找到了目标文件,先完整读取它,然后:

  • 如果它已经有 ## Related Documentation 部分,检查术语表是否已列在其中(搜索其路径,或 JARGON.md)。如果是,则不要改动该文件。如果不是,则向该部分添加一个条目,并完全保留每一个现有条目。
  • 如果不存在这样的部分,则在文件末尾添加一个。

无论哪种方式,条目都如下所示,其中用第 2 步中术语表的实际路径替换 JARGON.md:

- **JARGON.md**: Internal terminology, acronyms, and shorthand used in this project, with plain English definitions
  _(Reference this when a term, acronym, or piece of shorthand in this project isn't self-explanatory)_

这是一次每次运行仅一次的小型单条目编辑,而不是 merge_jargon.py 旨在防范的高容量排序与合并,因此在这里直接进行是可以的。

第 7 步:向用户报告

使用 reduce 子 Agent 的简短摘要和 merge_jargon.py 的小型 JSON 输出进行总结:添加的术语总数、更新的总数、在整理过程中丢弃的总数(以及原因)、reducer 标记出来而不是强行合并的任何有歧义的合并,以及 AGENTS.md 或 CLAUDE.md 是否已更新(或已引用术语表,或两个文件都不存在)。如果用户想查看文件本身,那在此时就是一次普通的文件读取,而不是需要在运行中途发生的事情。

条目格式

merge_jargon.py 负责渲染后的形态(- **Term**: Definition、排序、缩略词大小写)。上游唯一重要的内容规则,即把编号义项保留在一个条目内,而不是拆分为单独的 term (context) 条目,已在上面的 reduce 模板中说明,因为那是它实际被应用的唯一位置。

边缘情况

  • 二进制或不可读文件:跳过并记录哪些文件被跳过以及原因,而不是让整个运行失败。

示例

给定 docs/architecture.md 和 docs/onboarding.md,两个提取器在单次波次中并行运行。每个提取器写入自己的文件,并只向 orchestrator 返回一条确认,例如 Wrote 1 term to <scratch-dir>/architecture.json。此时 orchestrator 完全看不到定义本身。

然后 reduce 子 Agent 自己读取这两个文件:

// architecture.json
[{"term": "sidecar", "definition": "A helper process deployed alongside the main service to handle logging and metrics without living in the main service's codebase."}]
// onboarding.json
[{"term": "sidecar", "definition": "Used loosely by the team for any auxiliary process that runs next to a primary one and shares its lifecycle, not just the logging/metrics helper."}]

两者描述的是同一个术语,但确实是不同的义项,一个是特定的技术含义,另一个是更宽泛的日常含义,而不只是同一概念的不同措辞。reducer 保留两者,而不是选择其中一个,将合并后的条目写入 <scratch-dir>/merged.json,并只向 orchestrator 回复一条简短摘要,例如 “1 term added (sidecar, two senses merged)”:

[{"term": "sidecar", "definition": "(1) A helper process deployed alongside the main service to handle logging and metrics without living in the main service's codebase. (2) More loosely, any auxiliary process that runs next to a primary one and shares its lifecycle."}]

然后 orchestrator 针对该文件运行 merge_jargon.py,它会将该条目按字母顺序插入选定的 JARGON.md 路径中。在整个示例中,orchestrator 自己的上下文只保留了两次简短确认、一条简短的 reduce 摘要和一条小的脚本摘要,从未保留定义本身。

评论

支持 Markdown