Graphify 增量更新与仅聚类模式全解析:`/graphify --update` 与 `--cluster-only` 实战指南
本指南面向在 Claude Code / Cursor / Codex / Gemini CLI 等 Agent 环境中通过
/graphify技能维护知识图谱的开发者。当你已经完成过一次全量构建,之后只对仓库做了增删改时,--update模式只重抽取变化文件、只重跑必要的语义步骤,从而大幅节省 Token 与时间;当图的抽取内容不变、只需重新划分社区时,--cluster-only模式可在秒级刷新报告与可视化。读完本文你将掌握:增量检测的状态文件契约(.graphify_detect.json/.graphify_incremental.json)、"代码-only 免 LLM"快速路径、build_merge的 replace-on-re-extract 剪枝语义、增量后的图谱 diff 展示,以及cluster-only的自包含重聚类流程,并能在仓库源码中找到每一步的对应实现。
这张参考卡何时被加载
本文对应的原文档是 Agent 技能树中的一张"参考卡"(reference),原文明确写了它的加载条件:
Load this only when the user passed
--updateor--cluster-only. A first-time full build never reads this file.
也就是说,首次全量构建永远不读取本文件——它只服务于两种后续操作:
--update:增量重抽取,只处理上次运行之后新增或修改过的文件;--cluster-only:跳过抽取,直接在既有图上重新做社区聚类。
在仓库中,该参考卡以多种形态存在:技能源文件位于 graphify/skills/codex/references/update.md(agents、claude、kiro、opencode、windows 等各 Agent 平台的 graphify/skills 目录下各有一份等价副本),同时由 skillgen 生成"期望产物"快照存放在 tools/skillgen/expected/graphify__skills__codex__references__update.md;而调用它的主技能文档是 graphify/skill-codex.md。
参考卡中的两处占位符需要先解释清楚:
INPUT_PATH:本次扫描/构建的目标路径(无参数时通常是.);$(cat graphify-out/.graphify_python):每次运行前由技能 Step 1 探测出的、真正能import graphify的 Python 解释器路径,并持久化到graphify-out/.graphify_python,后续所有代码块都通过它调用解释器,避免 uv tool / pipx / venv / 系统 Python 环境不一致。
参考卡中出现的 "Step 3A / 3B / 3C / Steps 4–8 / Step 9" 均指 graphify/skill-codex.md 主流程中的步骤编号:3A 是确定性 AST 抽取,3B 是语义(LLM/subagent)抽取,4 起为聚类分析、报告生成、导出与清理。
--update 增量重建全流程
第一步:detect_incremental —— 找出真正变化过的文件
增量更新的起点不是重新全量扫描语义,而是调用 detect_incremental 做差异检测。参考卡中的第一段代码把结果写入 graphify-out/.graphify_incremental.json:
$(cat graphify-out/.graphify_python) -c "
import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path
result = detect_incremental(Path('INPUT_PATH'))
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
deleted = list(result.get('deleted_files', []))
if new_total == 0 and not deleted:
print('No files changed since last run. Nothing to update.')
raise SystemExit(0)
if deleted:
print(f'{len(deleted)} deleted file(s) to prune.')
if new_total > 0:
print(f'{new_total} new/changed file(s) to re-extract.')
"
实现层面的语义:detect_incremental 如何判定"变了"
detect_incremental 定义在 graphify/detect.py。它的判定模型值得精读:
- 快速路径:mtime 未变 + 内容哈希匹配 → 视为未变化(免费,无磁盘 IO 开销);
- 慢速路径:mtime 有变化 → 计算 MD5 与 manifest 中记录的哈希比对,一致则仍不算变化,避免"只 touch 不改内容"造成无效重抽取;
- mtime 同 tick 回写保护(源码注释中的
_mtime_may_hide_a_rewrite):mtime 被记录在与文件写入相同的文件系统 tick 内时,一次等长度的后续编辑可能不移动 mtime,此时强制做内容哈希校验(对应 issue #1859); - kind 区分:
kind="semantic"(extract 用)比对semantic_hash;kind="ast"(update 用)比对ast_hash。语义缺失的哈希视为"变了"。
返回结果的关键字段:
| 字段 | 含义 |
|---|---|
files |
当前全量扫描语料,按文件类型分组(document/paper/image/video/代码扩展名等) |
new_files |
本次新增/变更文件的子集,同样按类型分组 |
unchanged_files |
与上次一致、无需处理的文件 |
new_total |
需要重抽取的文件总数 |
deleted_files |
manifest 中有记录、但磁盘上已不存在的文件(真正的删除,其节点是"幽灵") |
excluded_files |
文件仍在磁盘但已退出扫描范围(.graphifyignore/.gitignore/--exclude 变化所致),不算删除 |
源码里对 deleted 与 excluded 的区分处理在 [#1908] 对应逻辑中明确注释:manifest 中离开语料的行按"磁盘是否存在"分叉——磁盘上没了才算删除,磁盘上还在只是被忽略规则排除的不得上报为删除,这与 watch 侧的区分(#1795)保持一致。
当 new_total == 0 且无删除时,参考卡直接 raise SystemExit(0) 提前退出并提示 "No files changed since last run. Nothing to update."——这是增量模式最常见的廉价路径:一次 mtime+hash 的比对开销,零抽取开销。
第二步:回填 .graphify_detect.json —— files 与 all_files 的分工
detect_incremental 的结果不能直接交给下游。主流程的 Steps 3A–6 是无条件读取 graphify-out/.graphify_detect.json 的,所以必须把增量状态重新组织成它们认识的形态:
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\"))
Path('graphify-out/.graphify_detect.json').write_text(json.dumps({
'files': r.get('new_files', {}),
'all_files': r.get('files', {}),
'total_files': r.get('new_total', 0),
'total_words': r.get('total_words', 0),
'skipped_sensitive': r.get('skipped_sensitive', []),
'needs_graph': True,
}, ensure_ascii=False), encoding=\"utf-8\")
"
关键契约(参考卡原文)是双字段语义:
files携带变更子集——驱动 Step 3A(AST)与 Step 3B0(缓存检查)只针对"真正变了"的文件工作;all_files携带完整语料——供任何需要全语料上下文的步骤使用。
skipped_sensitive 与 total_words 原样透传,保证后续敏感文件过滤、Token 预算与成本统计等步骤拿到与全量一致的元数据。
第三步:代码-only 快速路径 —— 语义抽取整个跳过
增量更新最大的优化机会在于:如果所有变更文件都是代码文件,那么 Step 3B(LLM/subagent 语义抽取)完全不需要运行,因为代码的结构信息由确定性的 AST 抽取(3A)负责,语义层(文档/论文/图片的"概念化")只服务于非代码内容。参考卡先做一次扩展名判定:
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
result = json.loads(open('graphify-out/.graphify_incremental.json', encoding='utf-8').read()) if Path('graphify-out/.graphify_incremental.json').exists() else {}
code_exts = {'.py','.ts','.js','.go','.rs','.java','.cpp','.c','.rb','.swift','.kt','.cs','.scala','.php','.cc','.cxx','.hpp','.h','.kts','.lua','.toc','.f','.F','.f90','.F90','.f95','.F95','.f03','.F03','.f08','.F08'}
new_files = result.get('new_files', {})
all_changed = [f for files in new_files.values() for f in files]
code_only = all(Path(f).suffix.lower() in code_exts for f in all_changed)
print('code_only:', code_only)
"
该扩展名集合覆盖主流语言(Python/TS/JS/Go/Rust/Java/C/C++/Ruby/Swift/Kotlin/C#/Scala/PHP 与 Fortran 各方言等)。执行分支(参考卡原文规则):
code_only为 True:打印[graphify update] Code-only changes detected - skipping semantic extraction (no LLM needed),只对变更文件运行 Step 3A(AST),完全跳过 Step 3B(不派生任何 subagent),直接进入 merge 与 Steps 4–8。这意味着"只改代码"的更新是纯本地、零 LLM Token 的;code_only为 False(任一变更文件是 doc/paper/image/video):走下面的完整语义管线。
第四步:非代码文件的语义重抽取与视频转写
只要有任何变更文件不是代码,就需要完整重跑 Steps 3A–3C。但其中有一个容易踩的坑,参考卡专门给出了规则(对应 issue #1392):
if any changed file is in
new_files['video'], runreferences/transcribe.md(Step 2.5) on those files, then rewrite.graphify_detect.jsonto move the resulting transcript paths intofiles['document']and dropfiles['video']— otherwise raw.mp4/.mp3paths are fed to semantic subagents as unreadable media (#1392).
也就是说,视频/音频文件必须先经转写管线变成文本(完整步骤见技能参考卡 graphify/skills/codex/references/transcribe.md,其中会使用 Whisper 模型做语音转写),再把转写文本路径从 new_files['video'] 挪到 files['document'],彻底删掉 files['video'],否则裸 .mp4/.mp3 路径会被当作不可读媒体喂给语义 subagent。这与 graphify/skill-codex.md Usage 中 --whisper-model medium 等转写选项对应。
第五步:纯删除场景 —— 为空 merge 准备空抽取
如果 new_total == 0 而只有删除(deleted_files 非空),仍然要进入 merge 步骤做剪枝。merge 需要一份抽取产物作为输入,因此参考卡先创建一个空抽取文件(如果尚不存在):
if [ ! -f graphify-out/.graphify_extract.json ]; then
echo '[graphify update] Only deletions -- creating empty extraction for merge.'
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8')
"
fi
这样下游 merge 步骤就能拿着"零节点零边"的新抽取,仅凭 prune_sources 把已删除文件的陈旧节点清出图谱。
第六步:核心合并 —— build_merge 的 replace-on-re-extract 语义
增量更新中"最容易出幽灵节点/幽灵边"的环节是合并。参考卡给出完整合并代码块,并逐行注释了背后的设计决策:
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
from graphify.build import build_merge
from graphify.detect import save_manifest
# Load new extraction and incremental state
new_extraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
incremental = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\"))
deleted = list(incremental.get('deleted_files', []))
# prune_sources is ONLY for genuinely DELETED files. Changed/re-extracted files are
# handled by build_merge's replace-on-re-extract (#1344): every source_file in
# new_chunks is dropped from the base before merge, so old/stale nodes don't survive.
# Do NOT add `changed` here: with root= passed, prune_set relativizes to the same base
# as the freshly merged nodes and would DELETE the re-extracted content (#1178 is moot
# now that replace — not the dedup pass — reconciles changed files).
prune = list(deleted) or None
# Use build_merge() — reads graph.json directly without NetworkX round-trip
# so edge direction (calls, implements, imports) is always preserved (#801).
# Pass root= so prune_sources (absolute paths from detect_incremental) are
# relativized to match the graph's relative source_file values; without it
# nothing is pruned and stale nodes accumulate on every update (#1361).
# directed=IS_DIRECTED: replace IS_DIRECTED with True if --directed was given, else
# False. Without it a --directed --update silently rebuilds undirected and collapses
# reciprocal A<->B edges (#1392).
G = build_merge(
[new_extraction],
graph_path='graphify-out/graph.json',
prune_sources=prune,
root='INPUT_PATH',
directed=IS_DIRECTED,
)
print(f'[graphify update] Merged: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges')
# Write merged result back to .graphify_extract.json so Step 4 sees the full graph
merged_out = {
'nodes': [{'id': n, **d} for n, d in G.nodes(data=True)],
'edges': [
# Explicit source/target last so they win over any stale attrs in d.
{**{k: val for k, val in d.items() if k not in ('_src', '_tgt', 'source', 'target')},
'source': d.get('_src', u), 'target': d.get('_tgt', v)}
for u, v, d in G.edges(data=True)
],
# G.graph[\"hyperedges\"] holds hyperedges from both existing graph.json
# and new_extraction (build_merge combines them). Falling back to
# new_extraction only would silently drop prior-run hyperedges (#801).
'hyperedges': list(G.graph.get('hyperedges', [])),
'input_tokens': new_extraction.get('input_tokens', 0),
'output_tokens': new_extraction.get('output_tokens', 0),
}
Path('graphify-out/.graphify_extract.json').write_text(json.dumps(merged_out, ensure_ascii=False), encoding=\"utf-8\")
print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])} nodes, {len(merged_out[\"edges\"])} edges)')
# Save manifest so next --update diffs against today's state, not the
# prior run's baseline (prevents ghost-node reports on subsequent updates).
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
#
# Only stamp semantic files (docs/papers/images) that ACTUALLY produced output
# THIS run (new_extraction is this run's fresh extraction, read above before the
# merge overwrote the file): a changed doc whose chunk failed must stay unstamped
# so the next --update re-queues it, otherwise it is marked done and its content
# is lost forever (#2015). Mirrors the library extract path
# (cli._stamped_manifest_files + clear_semantic + scan_corpus).
from graphify.cli import _stamped_manifest_files
_manifest_files = _stamped_manifest_files(incremental['files'], new_extraction, Path('INPUT_PATH'))
# Changed semantic files dispatched this run but NOT stamped had their chunk fail
# or be omitted; clear any stale semantic_hash so they are re-queued (#1948).
_sem_types = ('document', 'paper', 'image')
_dispatched = {f for t, fl in incremental.get('new_files', {}).items() if t in _sem_types for f in fl}
_stamped = {f for fl in _manifest_files.values() for f in fl}
_cleared = _dispatched - _stamped
# scan_corpus = the RAW full corpus so in-root files newly excluded since last run
# are dropped rather than masquerading as deletions; untouched rows preserved (#1908).
_scan = {f for fl in incremental['files'].values() for f in fl}
save_manifest(_manifest_files, root='INPUT_PATH', scan_corpus=_scan, clear_semantic=_cleared or None)
print('[graphify update] Manifest saved.')
"
这段代码浓缩了合并阶段最重要的四条设计原则,均可对照源码验证:
- prune 只针对真删除,变更文件靠 replace 而非 prune。
build_merge定义在 graphify/build.py,其 replace-on-re-extract 逻辑(#1344)会把new_chunks中出现过的每个source_file先从事先加载的旧图中按层(AST tier / semantic tier,见_is_ast_tier)剔除对应旧节点/旧边,再合并新内容——因此"修改过的文件"即使节点标签不变、边变少,陈旧边也绝不会残存(这正是 tests/test_incremental.py 中test_update_prunes_a_removed_imports_edge断言的行为:删除a.py里的一行 import 后执行 update,a.py拥有的旧 import 边必须消失)。若把changed文件也塞进prune_sources,在传入root=时会被相对化到与新鲜节点同一基准,从而误删刚重抽取出来的内容(#1178 的旧陷阱)。 root='INPUT_PATH'是剪枝生效的前提。detect_incremental返回的是绝对路径,而图内source_file是相对扫描根的键;build_merge通过_norm_source_file做双向归一化(绝对形式与相对形式都参与匹配),不传root时任何文件都删不掉,陈旧节点会在每次 update 上累积(#1361)。新版源码中还增加了"从图自身推导 merge root"的兜底(#1571),并在_build_prune_sets内处理了"replace 优先于矛盾 delete"的守卫(#1796/#2012)。directed=必须显式传入。主流程里--directed决定图是否保留方向(calls/implements/imports 的方向);若不把IS_DIRECTED(True/False 占位)传给build_merge,一次--directed --update会静默地按无向重建并把互反的 A↔B 边折叠掉(#1392)。当前build_merge源码默认行为是:directed=None时继承磁盘上旧图自己的 directed 标志(#2342),避免合并意外翻转方向。- 超边必须从
G.graph['hyperedges']回读,而不是只信新抽取。build_merge会把既有 graph.json 与新抽取的超边合并放进图对象属性里;若回写merged_out时只取new_extraction的超边,会静默丢弃历次运行累积的超边(#801)。
合并结果写回 .graphify_extract.json 后,主流程 Step 4(社区分析)看到的就是"完整新图",可以无缝继续 Steps 4–8。
第七步:合并后向用户展示图谱 diff
参考卡要求在主流程 Step 4 之后展示增量 diff,把"这次更新改了什么"显式呈现给用户。前置动作是合并前先备份旧图:
cp graphify-out/graph.json graphify-out/.graphify_old.json
然后运行:
$(cat graphify-out/.graphify_python) -c "
import json
from graphify.analyze import graph_diff
from graphify.build import build_from_json
from networkx.readwrite import json_graph
import networkx as nx
from pathlib import Path
# Load old graph (before update) from backup written before merge
old_data = json.loads(Path('graphify-out/.graphify_old.json').read_text(encoding=\"utf-8\")) if Path('graphify-out/.graphify_old.json').exists() else None
new_extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
G_new = build_from_json(new_extract, directed=IS_DIRECTED)
if old_data:
G_old = json_graph.node_link_graph(old_data, edges='links')
diff = graph_diff(G_old, G_new)
print(diff['summary'])
if diff['new_nodes']:
print('New nodes:', ', '.join(n['label'] for n in diff['new_nodes'][:5]))
if diff['new_edges']:
print('New edges:', len(diff['new_edges']))
"
graph_diff 实现在 graphify/analyze.py:按节点 ID 集合求差得到 new_nodes/removed_nodes,按 (端点, relation) 三元组求差得到 new_edges/removed_edges(有向图保留顺序、无向图对端点排序后去重),并汇总成 "3 new nodes, 5 new edges, 1 node removed" 风格的 summary 字符串。展示结束后清理备份文件:
rm -f graphify-out/.graphify_old.json
说明:参考卡中的
rm是 Agent 技能运行时的清理命令;本仓库为只读参考仓库,读者不应在本仓库内执行这些破坏性操作,仅在自有项目运行时使用。
第八步:只给"真正产出"的文件盖 manifest 戳
合并块的收尾是 save_manifest(定义于 graphify/detect.py),它决定了下一次 --update 的 diff 基准。参考卡里这条规则最为关键:
- 语义文件(
document/paper/image)只有当本次运行确实产出了节点或超边时才盖章——chunk 失败或缺失的文件必须保持未盖章状态,否则它会被标记为"已完成",内容将永远丢失(#2015/#933)。这正是_stamped_manifest_files(graphify/cli.py)的职责:它只把sem_result中nodes与hyperedges集合里出现过source_file的语义文件放行,纯边结果(无实体表示)也会被排除在盖章之外(#2927)。 - 本 run 被派发但未盖章的语义文件,要用
clear_semantic=强制清空其历史semantic_hash,防止旧的语义哈希被原样继承而让下次检测误判为"未变化"(#1948)。 scan_corpus=必须传原始全量语料(而非盖章过滤后的子集):这样"上次在范围内、本次因 ignore 规则新被排除的 in-root 文件"会被正确丢弃而不是伪装成删除;未触碰文件的旧行则原样保留(#1908)。另外root=使 manifest 键以扫描根为基准做相对化(posix 风格),跨机器/换克隆位置依然可移植,避免仓库移动后每次--update全部缓存失配(#1417)。
参考卡的 Step 9(在 graphify/skill-codex.md 完整出现)还会执行同样的 _stamped_manifest_files + clear_semantic + scan_corpus 逻辑并顺带更新 graphify-out/cost.json 的累计成本账,然后清理所有中间文件(.graphify_detect.json、.graphify_extract.json、.graphify_ast.json、.graphify_semantic.json、.graphify_analysis.json 与 .graphify_chunk_*.json)。
--cluster-only:在既有图上重新聚类
当仓库内容没有变化、只是想重新划分社区(例如调整了聚类参数、希望刷新社区标签与可视化)时,增量抽取也是多余的。参考卡给出的用法极其简单:
graphify cluster-only .
参考卡对此模式的定位强调两点:
- 完全自包含(self-contained):
graphify cluster-only .会读取既有graph.json,一次性完成重新聚类、社区命名,并从既有图重新生成GRAPH_REPORT.md、graph.json与graph.html三个产物; - 严禁重跑 Steps 5–9:那些步骤依赖的中间文件(
.graphify_extract.json、.graphify_detect.json、.graphify_analysis.json)已被上一次构建的 Step 9 清理删除,重跑会直接抛FileNotFoundError(#1392)。完成后只需像往常一样向用户呈现刷新后的GRAPH_REPORT.md摘要。
从 graphify/cli.py 的 cluster-only/label 分支实现可以印证"自包含"的工程细节:该路径从既有图上重新运行聚类与社区标签生成,不重新派生代码内容(报告里明确标注 cluster-only mode — file stats not available);提交戳沿用抽取时记录的 built_at_commit 而不是从当前 shell 的 cwd 重新推导(避免在另一个仓库目录下运行导致把该仓库的 HEAD 错误盖到图上,#2534);写 graph.json 前先 touch HTML 陈旧标记以防范"旧 HTML 冒充最新";标签侧写的 .sig 成员签名让下一次 cluster-only 能检测社区成员是否变化、避免复用过期标签。默认不含 --no-viz 时三个产物同步更新,含 --no-viz 时只保留 GRAPH_REPORT.md 与 graph.json 并移除 graph.html。
与全量构建的边界:一次全量,之后全靠增量
把参考卡的规则还原到日常使用节奏上,可以总结为一张决策表:
| 场景 | 正确操作 | 读取本参考卡? |
|---|---|---|
| 首次构建 / 没有 manifest | /graphify <path> 全量 |
否 |
| 只新增/修改/删除了代码文件 | /graphify <path> --update |
是,走 code-only 快速路径(免 LLM) |
| 涉及 doc/paper/image/video 变化 | /graphify <path> --update |
是,视频先转写再走完整 3A–3C |
| 内容没变,只想重聚类/换标签 | /graphify <path> --cluster-only |
是,但绝不再跑 Steps 5–9 |
| 图已存在,只想查询 | graphify query ... |
否,走 query 快速路径 |
这条"全量一次 + 增量维护"的形态之所以成立,底层依赖的是 manifest 文件(保存每个文件的 mtime 与 ast_hash/semantic_hash 双哈希),detect_incremental 每次与它比对;而 build_merge 的 replace-on-re-extract 保证"改了就有新、删了就移除、没动就原样保留",配合 graphify/cli.py 中 _stamped_manifest_files 只给真实产出盖章的纪律,让图既不会累积幽灵数据、也不会遗漏应当重抽取的文件。
常见陷阱与避坑清单
--directed必须透传给build_merge:漏传会让一次--directed --update静默退化为无向图并折叠互反边(#1392)。- 不要把
changed文件塞进prune_sources:配合root=时它会相对化到与新鲜节点相同基准,反过来把刚重抽取的内容删掉;变更文件统一交给 replace-on-re-extract(#1344/#1178)。 - manifest 只盖"真实产出"的戳:语义文件 chunk 失败必须保持未盖章,否则下次
--update不再重排它、内容永久丢失(#2015);clear_semantic负责清理上次残留的假哈希(#1948)。 scan_corpus传原始全量语料:传盖章后的子集会误删"仅被过滤而未失败"的行(#1908)。cluster-only后不要再跑 Steps 5–9:中间文件已随上次构建清理,硬跑必然FileNotFoundError(#1392)。- video 变更先转写再喂语义管线:裸
.mp4/.mp3不可读,需经 graphify/skills/codex/references/transcribe.md 转文本并挪入files['document'](#1392)。
若想追踪这些机制在库级 CLI(graphify update / graphify extract --incremental)中的对应实现,可继续阅读 graphify/cli.py、graphify/detect.py 与 graphify/build.py;增量剪枝正确性的回归测试可参考 tests/test_incremental.py(如 test_update_prunes_a_removed_imports_edge、test_incremental_md_reference_target_canonicalizes),并结合 graphify/skills/codex/references 目录下的 query.md、exports.md 等其余参考卡理解完整技能体系。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00