从死钩子到框架感知路由图:codegraph 的 Framework Resolver `extract()` 接线设计与实现解析
本文以 codegraph 仓库中的实现计划 2026-04-24-framework-resolver-extract.md 为主线,拆解一个具体的工程问题:如何把一个从未被调用的 extractNodes 死钩子,替换为统一的 extract(filePath, content): { nodes, references } 接口,让 Django、Flask、Express、Rails、Spring 等 13+ 个框架的路由文件都能向代码知识图谱贡献 route 节点和 route→handler 边。读完本文,你将掌握框架提取器的接口设计、按语言分发的检测机制、各框架正则提取的迁移模式,以及提取器接入 tree-sitter 解析管道(含 worker 线程边界)的关键实现细节。
一、问题起点:一个从未被调用的钩子
计划文档的 Background 一节开宗明义地指出了两个叠加的缺陷:
- 死钩子。当时每个
FrameworkResolver都自带一个extractNodes?(filePath, content)方法(覆盖 express、laravel、python/django、python/flask、python/fastapi、ruby/rails、java/spring、go、rust、csharp、swift × 3、react、svelte),但整个src/中对该符号的引用只有一处——src/resolution/types.ts 中的接口定义本身。钩子从未被编排器调用,结果就是图谱中实际不存在任何route类型的节点,路由文件里一条 URL 与其 view/controller/handler 之间的链路完全缺失,codegraph_callers(MyView)会悄无声息地漏掉它最重要的调用方。 - 形状 bug。即使钩子"活着"也没用:Django 提取器的正则虽然把 view 名捕获在第 2 组,但 src/resolution/frameworks/python.ts 中的解构把它直接丢弃了。类似形状的问题遍布多数框架。
计划的 Goal 因此被定义为:把死钩子换成单一的 extract?() 方法,在提取阶段(tree-sitter 解析该文件之后)对每个"语言匹配"的已检出框架各调用一次;提取出的节点与 tree-sitter 节点一起入库,提取出的引用进入既有的 unresolved-references 管道,由 name-matcher / import-resolver / 框架自带 resolve() 这套既有机制生成最终边。净效果是:
path('/users', UserListView.as_view())
在图谱中产生一个 route 节点,并通过一条 references 边链接到 UserListView 类节点——且该性质对 Flask、FastAPI、Express、Rails、Laravel、Spring、Gin、Axum、ASP.NET、Vapor、React Router、SvelteKit 同样成立。
技术栈:TypeScript、vitest、tree-sitter(已有)、better-sqlite3(已有),不引入任何新依赖。
文档开头还注明了面向 agentic workers 的执行方式建议(使用 subagent-driven-development 或 executing-plans 逐任务执行,checkbox 跟踪进度)。下文按计划的 Task 1–9 顺序展开,并在每一节标注当前仓库中落地后的真实代码证据。
二、接口改造(Task 1):FrameworkExtractionResult 与 extract()
Task 1 遵循严格的 TDD 流程:先写失败测试(一个 extract() 返回 { nodes, references } 的最小 FrameworkResolver 对象,期望 toEqual({ nodes: [], references: [] })),运行 npx vitest run __tests__/frameworks.test.ts 确认失败(extract 不是 FrameworkResolver 的属性、languages 也不是),然后替换接口,再跑 typecheck——预期 typecheck 失败,所有 src/resolution/frameworks/*.ts 会因 extractNodes 不存在于接口上而报错,这是刻意的,由后续任务逐个修复。
计划给出的目标接口是:
/**
* Result of framework-specific file extraction.
*/
export interface FrameworkExtractionResult {
/** Framework-specific nodes (e.g. routes) */
nodes: Node[];
/** Framework-specific unresolved references (e.g. route -> handler) */
references: UnresolvedRef[];
}
export interface FrameworkResolver {
name: string;
/** Languages this framework applies to. If omitted, applies to all languages. */
languages?: Language[];
/** Detect if project uses this framework (project-level, called once at startup) */
detect(context: ResolutionContext): boolean;
/** Resolve a reference using framework-specific patterns */
resolve(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null;
/**
* Extract framework-specific nodes and references from a file.
* Returns route nodes, middleware nodes, etc., plus unresolved references
* that link those nodes to handlers (view classes, controller methods,
* included modules). Unresolved references flow into the normal resolution
* pipeline; the framework's own `resolve()` is one of the strategies tried.
*/
extract?(filePath: string, content: string): FrameworkExtractionResult;
}
仓库源码佐证:当前 src/resolution/types.ts#L186-L236 中 FrameworkExtractionResult 与 FrameworkResolver.extract?() 均已按上述形状落地。从源码结构看,接口在计划之后还演化出两个可选钩子,值得注意:
claimsReference?(name):让"即使图谱中没有同名节点也要放行"的引用名通过解析器的 name-exists 预过滤(典型场景是 Django ORM 的self._iterable_class(...)这类属性作为可调用对象的动态分发);postExtract?(context):跨文件收尾通道,在所有 per-file 提取完成后(以及每次增量同步时)调用一次,用于 NestJSRouterModule.register([...])这类"符号最终形态取决于兄弟文件"的场景。实现方返回带变更字段的节点(通常是name),且必须保留节点id以使既有的 route→handler 边不被破坏。
这两个钩子的存在说明 extract() 接口设计为"每文件、纯函数、无跨文件状态"是刻意的:所有需要全局视角的逻辑被隔离到 postExtract,保持主提取路径简单可测试。
三、语言分发(Task 2):detectFrameworks 与 getApplicableFrameworks
Task 2 在保持 detectFrameworks 签名不变的前提下,新增一个按语言过滤的辅助函数:
/**
* Filter a list of detected frameworks down to ones that apply to a given language.
* Frameworks without an explicit `languages` list are treated as universal.
*/
export function getApplicableFrameworks(
detected: FrameworkResolver[],
language: Language
): FrameworkResolver[] {
return detected.filter(
(fw) => !fw.languages || fw.languages.includes(language)
);
}
对应的失败→通过测试验证了两种行为:['python'] 文件只匹配 python 框架与"无 languages 字段"的通用框架;当语言无任何专属框架时只返回通用框架。
仓库源码佐证:当前实现位于 src/resolution/frameworks/index.ts#L98-L119。其中 detectFrameworks 对每个 resolver 的 detect(context) 调用做了 try/catch 包裹——检测失败按"未检出"处理,保证单个框架的 detect 异常不会炸掉整个索引。同一个文件中的注册表(FRAMEWORK_RESOLVERS,L36-L79)从源码结构看已扩展为 20+ 个 resolver:在计划覆盖的 13 个之外,还纳入了 Drupal、NestJS、Vue、Astro、Play、GoFrame、Swift-ObjC 桥、React Native 桥、Expo Modules、Fabric、CICS、Terraform 等,并提供了 registerFrameworkResolver 允许外部同名覆盖注册。这说明该机制已从一个一次性接线计划沉淀为可扩展的框架注册表。
四、Django 迁移(Task 3):动机案例的完整实现
Django 是整个计划的动机案例(motivating case),因此拥有最富的测试覆盖。计划中的目标测试覆盖了 6 种形状:
| 输入形状 | 期望产出 |
|---|---|
path('users/', UserListView.as_view(), name='user-list') |
route 节点 users/ + reference UserListView(kind=references,fromNodeId 指向 route 节点 id) |
path('api/', api_v1_views.UserListView.as_view()) |
reference 取点分路径最后一段 UserListView |
path('home/', home_view) |
reference home_view |
path('api/', include('api.urls')) |
reference api.urls,kind=imports |
re_path(r'^users/$', UserView)、url(r'^old/$', OldView) |
两个 route 节点,name 分别为 ^users$/、^old/$ |
非 urls.py 的普通 python 文件 |
空 {nodes: [], references: []} |
计划给出的 extract() 核心实现(含路由正则与 handler 解析函数):
extract(filePath, content) {
if (!filePath.endsWith('.py')) return { nodes: [], references: [] };
const nodes: Node[] = [];
const references: UnresolvedRef[] = [];
const now = Date.now();
// path('url', handler, name=...) / re_path(r'...', handler) / url(r'...', handler)
const routeRegex = /\b(path|re_path|url)\s*\(\s*r?'"['"]\s*,\s*([^)]*?)(?:\)|,\s*name=)/g;
let match: RegExpExecArray | null;
while ((match = routeRegex.exec(content)) !== null) {
const [, _fn, urlPath, handlerExpr] = match;
const line = content.slice(0, match.index).split('\n').length;
const routeNode: Node = {
id: `route:${filePath}:${line}:${urlPath}`,
kind: 'route',
name: urlPath,
qualifiedName: `${filePath}::route:${urlPath}`,
filePath, startLine: line, endLine: line,
startColumn: 0, endColumn: match[0].length,
language: 'python', updatedAt: now,
};
nodes.push(routeNode);
const handler = handlerExpr.trim();
const target = resolveHandlerName(handler);
if (target) {
references.push({
fromNodeId: routeNode.id,
referenceName: target.name,
referenceKind: target.kind,
line, column: 0, filePath, language: 'python',
});
}
}
return { nodes, references };
}
其中 resolveHandlerName 的归一化规则是理解该机制的关键:
function resolveHandlerName(expr: string): { name: string; kind: 'references' | 'imports' } | null {
// include('module.path') → 整条模块路径,kind = imports
const includeMatch = expr.match(/^include\s*\(\s*'"['"]/);
if (includeMatch) return { name: includeMatch[1], kind: 'imports' };
// 剥掉尾部 .as_view(...) / .as_view
let head = expr.replace(/\.as_view\s*\([^)]*\)\s*$/, '');
// 再剥掉任意尾部方法调用 .some_method()
head = head.replace(/\.\w+\s*\([^)]*\)\s*$/, '');
// head 要么是裸名要么是点分路径,取最后一段
const dotted = head.split('.').filter(Boolean);
if (dotted.length === 0) return null;
const last = dotted[dotted.length - 1];
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(last)) return null;
return { name: last, kind: 'references' };
}
注意 include() 走的是 imports 而非 references:它把 URLconf 之间的包含关系建模为"模块依赖",交给 import 解析链路去落到 api/urls.py 文件;而 .as_view() 剥壳后取尾标识符走 references,交给名字匹配链路去落到 view 类节点。同一次正则扫描,两种引用类型分流到既有的两条解析通路。
仓库源码佐证与演化:当前 src/resolution/frameworks/python.ts#L61-L138 的 django 实现相比计划有三处增强:
- 提取前先过
stripCommentsForRegex(content, 'python')(src/resolution/strip-comments.ts),避免注释里的path(...)字面量产生幽灵 route 节点; - handler 捕获组从"任意非
)字符"收紧为[\w.]+(?:\s*\([^)]*\))?,即"点分标识符 + 至多一层平衡括号",注释里明确说明这是为了匹配View.as_view()、include('x.y')这两类真实形状; - 计划中列为已知 gap 的 DRF
router.register已被后续实现:python.ts#L109-L135 用.register(r'prefix', SomeViewSet)正则产生VIEWSET /prefixroute 节点,用"第一参是字符串 + 第二参以 View/ViewSet 结尾"两个条件把它与admin.site.register(Model, Admin)区分开。
五、Flask 与 FastAPI(Task 4):装饰器路由的共享抽象
Flask/FastAPI 的路由写在装饰器里,且 handler 是装饰器下方"下一个 def",与 Django 的"调用参数即 handler"不同。计划为此抽象了一个 extractDecoratorRoutes 共享助手,用一组配置参数区分两个框架:
interface DecoratorRouteOpts {
decoratorRegex: RegExp;
defaultMethod: string;
methodGroup?: number; // @x.post(...) 的方法动词组
methodFromGroup?: number; // methods=[...] 列表里提取方法
pathGroup: number;
handlerGroup?: number;
findHandler?: boolean; // handler 不在正则里,向后找下一个 def
language: 'python';
}
Flask 配置:
decoratorRegex: /@(\w+)\.route\s*\(\s*'"'"\])?\s*\)\s*\n\s*(?:async\s+)?def\s+(\w+)/g,
defaultMethod: 'GET',
methodFromGroup: 3,
pathGroup: 2,
handlerGroup: 4,
FastAPI 配置:
decoratorRegex: /@(\w+)\.(get|post|put|patch|delete|options|head)\s*\(\s*'"['"]/g,
defaultMethod: '',
methodGroup: 2,
pathGroup: 3,
handlerGroup: undefined,
findHandler: true,
findHandler 分支的核心是在装饰器匹配结束之后向后扫描 \n\s*(?:async\s+)?def\s+(\w+)。两个框架的测试目标分别是:
- Flask:
@app.route('/users')→ route 节点GET /users+ referencelist_users;@users_bp.route('/<id>', methods=['POST'])→POST /<id>+create_user; - FastAPI:
@app.get('/users')→GET /users+list_users;@router.post('/items')→POST /items+create_item。
仓库源码佐证与演化:当前 python.ts#L210-L229 的 flask extract() 显示装饰器正则已改为"只匹配装饰器头 + 向后找 def"的两段式:注释明确解释动机——"handler 是下一个 def,允许中间夹着 @login_required 和叠层 @x.route() 行"。这正是 tests/frameworks-integration.test.ts#L68-L90 中的端到端用例所验证的回归:两个叠层 @bp.route 装饰器 + 一个 @login_required 之下,GET / 与 GET /index 两个 route 节点都必须被提取出来(旧假设"装饰器后必须紧跟 def"会丢掉第一个叠层装饰器)。Flask 检测逻辑同样被加固为"入口文件(app/main/wsgi/__init__.py,限前 50 个)中同时出现 import flask 与 Flask("的 app-factory 友好判定。
六、Express(Task 5):中间件约定与尾标识符
Express 的形态是 (app|router).METHOD('/path', handler-expr),计划的三条测试规则:
app.get('/users', listUsers)→ routeGET /users+ referencelistUsers;router.post('/items', auth, createItem)→ reference 取最后一个 handlercreateItem(约定:中间件在前,handler 在后);app.get('/x', userController.list)→ reference 取尾标识符list。
计划实现要点:用 handlers.split(',') 取最后一段,再经 extractTailIdent 归一化:
function extractTailIdent(expr: string): string | null {
const cleaned = expr.replace(/\s+/g, '').replace(/\(\)$/, '');
const m = cleaned.match(/(?:\.|^)([A-Za-z_][A-Za-z0-9_]*)$/);
return m ? m[1] : null;
}
另外,method === 'use' 且路径不以 / 开头的匹配被跳过(app.use(mw) 无路径、app.use('/api', mw) 才算前缀路由)。
仓库源码佐证与演化:当前 src/resolution/frameworks/express.ts#L134-L160 中,单条全匹配正则被替换为"头正则 + 括号配平"两段式:头正则只匹配到 (app|router).METHOD('/path', 为止,随后用字符串感知的括号配平函数 matchDelim(L21-L35,逐字符跳过 "、'、` 字符串字面量再数括号深度)圈出完整参数列表。注释直说了动机:handler 经常是内联箭头函数,旧的正则 [^)]+ 无法跨过箭头函数体里的 ) 与 {},导致"内联 handler 的路由连不到任何东西"。这是 Task 5 计划在实战仓库中留下的最典型的一处"形状扩展"。
七、其余框架迁移(Task 6):同一模式,逐框架提交
Task 6 把 Laravel、Rails、Spring、Gin、Axum、ASP.NET、Swift/Vapor、React、Svelte 归入同一迁移模式(加 languages 字段、extractNodes 改名 extract 并返回双元组、每个 route 匹配同时产出节点与 UnresolvedRef、每框架至少一条单测),并强调每个框架独立提交,便于任一框架回归时单独回滚。各框架的提取规则要点:
| 框架(文件) | 路由形状 | handler 引用取法 | languages |
|---|---|---|---|
| Laravel(laravel.ts) | Route::get('/x', [Ctrl::class, 'method'])、'Ctrl@method'、Route::resource('users', UserController::class) |
数组取第二元 / @ 后段 / 类名本身 |
['php'] |
| Rails(ruby.ts) | get '/x', to: 'users#index';resources :users 展开为每个 CRUD action 一个节点 |
按 # 拆分取方法名,controller 段用于限定作用域 |
['ruby'] |
| Spring(java.ts) | 方法上的 @GetMapping("/x") |
向后扫描到下一个 public/private 方法名 |
['java'] |
| Gin/chi/gorilla(go.ts) | r.GET("/x", handler) |
最后一个参数里的最后一个标识符 | ['go'] |
| Axum/actix(rust.ts) | .route("/x", get(handler)) |
get(...) 内部的标识符 |
['rust'] |
| ASP.NET(csharp.ts) | [HttpGet("/x")] + 同类 action 方法 |
向前扫描到首个访问修饰符方法名 | ['csharp'] |
| Vapor(swift.ts) | app.get("/x", use: handler) |
use: 之后的标识符末段 |
['swift'] |
| React / Svelte(react.ts、svelte.ts) | UI 框架,路由映射到组件而非服务端 handler | 保持只产节点、references: [] 的迁移,<Route element={<Page/>}/> → Page 的边留作后续 |
['javascript','typescript'] / ['svelte'] |
八、管道接线(Task 7):主线程与 worker 线程的双覆盖
这是整个计划的核心接线改动,发生在每个文件被 tree-sitter 解析之后。计划给出的主线程侧片段(在 extractFromSource 返回结果前追加):
// Framework-specific extraction (routes, etc.)
if (detectedFrameworks && detectedFrameworks.length > 0) {
const applicable = getApplicableFrameworks(detectedFrameworks, language);
for (const fw of applicable) {
if (!fw.extract) continue;
try {
const fwResult = fw.extract(filePath, content);
result.nodes.push(...fwResult.nodes);
result.unresolvedReferences.push(...fwResult.references);
} catch (err) {
result.errors.push({
message: `Framework extractor '${fw.name}' failed: ${err instanceof Error ? err.message : String(err)}`,
filePath,
severity: 'warning',
});
}
}
}
三个设计细节值得强调:
- 单框架失败不拖垮整文件:每个
fw.extract独立 try/catch,失败降级为一条severity: 'warning'的提取错误记录; - 检出只做一次:在
indexAll启动解析 worker 之前,调用detectFrameworks(buildResolutionContext(rootDir, queries))完成项目级检测("项目级信号,启动时调一次"),随后每个文件只做廉价的按语言过滤; - worker 线程边界:这是计划中最容易被忽略的坑——解析在 worker 线程中执行,而"带函数的对象无法穿越
worker_threads的postMessage边界"。因此跨线程只传框架名字符串数组,worker 内部用getAllFrameworkResolvers().filter(f => detectedNames.includes(f.name))重新解析出 resolver 对象。
仓库源码佐证:这条双覆盖路径在现行代码中完整可追:
- 主线程侧:src/extraction/tree-sitter.ts#L6788-L6811 的
extractFromSource在 kernel/wasm 提取完成后,依据frameworkNames过滤getAllFrameworkResolvers()再经getApplicableFrameworks(..., detectedLanguage)二次过滤,逐个调用fw.extract(filePath, source)并把nodes/references合入同一ExtractionResult——与计划片段逐行对应,且对"文件级语言"(如 CFML 之外只做 file-record 的语言)同样生效,注释指出 Drupal 路由 yml、Spring@Value对application.yml的解析就依赖这一通道; - 编排侧:src/extraction/index.ts#L1634 的
ensureDetectedFrameworks(files)把检出框架名随每个解析任务下发(L1741-L1742 的extractFromSource/pool.requestParse两处都携带frameworkNames),src/extraction/parse-pool.ts#L53 的 job 类型带frameworkNames?: string[]; - worker 侧:src/extraction/parse-worker.ts#L68-L114 从
msg.frameworkNames取出名字数组,按计划所述在 worker 内 filter 出 resolver 对象,最终同样汇入extractFromSource(filePath, content, language, frameworkNames)的统一路径。
九、测试分层与端到端验证
计划把测试文件刻意一分为二,理由写在 File Structure 一节:单元测试是确定性的 string-in/array-out,毫秒级;集成测试要启动一个 CodeGraph DB,慢但给出最强的行为保证。
单元测试 tests/frameworks.test.ts(当前 1800+ 行)从源码结构看完整继承了计划中的骨架:接口契约测试(extract() 返回 {nodes, references})、getApplicableFrameworks 的语言过滤测试,以及 django/flask/fastapi/express 等每个 resolver 的形状测试。
集成测试 tests/frameworks-integration.test.ts#L20-L58 中的 Django 用例与计划 Task 7 Step 1 几乎逐行一致:
- 在临时目录落一个微型 Django 项目(
manage.py标记文件 +requirements.txt含django==4.2+users/views.py定义UserListView+users/urls.py含path("users/", UserListView.as_view())); CodeGraph.initSync(tmpDir)后indexAll();- 断言三层:
route节点存在且 name 为users/;UserListView类节点存在;getOutgoingEdges(route.id)中存在一条target === view.id且kind === 'references'的边。
这个断言链正是全计划的验收标准:它验证的不是"正则匹配成功",而是 route 节点经 unresolved-reference 管道、经既有解析机制最终落成了图上的真实边。
十、收尾(Task 8)与验收标准
Task 8 定义了迁移完成的两条硬性判据:
grep -rn "extractNodes" src/ __tests__/零匹配——死钩子彻底移除;npm run build && npm test全绿。
并规定了 README 需要补充的 "Framework-aware Routes" 章节内容(Django urlpatterns 的 path()/re_path()/url()/include()、Flask/FastAPI 装饰器、Express、Laravel、Rails、Spring、Gin、Axum、ASP.NET、Vapor 的覆盖清单,以及一句使用提示:Query codegraph_callers(YourView) and the route pattern will appear as an incoming edge)。计划同时要求在 PR 描述中记录生产代码/测试/文档的三行行数统计(占位 X/Y/Z 在 PR 阶段填写,不在计划阶段虚构)。
十一、范围边界与已知缺口
计划的 Scope Note 与 Known gaps 一节刻意划清了"本次不做"的边界,理解这些边界有助于判断当前实现的能力上限:
- 不迁 AST:Django 提取保持正则而非 tree-sitter AST 遍历。正则对目标形状(
path/re_path/url/include/DRF register/CBV.as_view/点分模块路径)够用;AST 化是更大的独立变更,不阻塞本接线。同理,Laravel/Spring 等框架的正则都只承诺覆盖常见声明形状; - DRF router 展开:计划时代
router.register只产单个指向 ViewSet 的 route 节点,展开成 6 个 CRUD action 节点列为后续——从源码看,router.register的 route 节点 + ViewSet 引用已在现行 python.ts 中实现,六 action 展开仍是未做项; - React Router handler 边:
<Route element={<Page/>}/>目前只产 route 节点,route -> Page引用留作后续; - Spring 类级作用域:方法级 mapping 可用,类级
@RequestMapping基础路径的组合是后续工作。
十二、小结:这套接线设计可复用的工程经验
回顾整份计划及其在仓库中的落地,有四条经验对任何"给静态分析管线加领域感知提取器"的工作都有参考价值:
- 死代码的实证定位:计划用"全仓 grep 只有一处接口定义"这种可复核的证据确立问题真实性,而不是凭印象断言;
- 接口收敛 + 引用复用既有管道:
extract()只产出FrameworkExtractionResult,不自己造边——节点入库、引用走既有 unresolved-refs 解析链,让框架提取器成为"数据生产者"而非"图写入者",职责边界干净; - 跨线程只传数据:resolver 对象含函数,
postMessage传不过去;约定"传名字、两端各自 filter 注册表"是低成本且对注册表演化(resolver 数量从 13 扩到 20+)保持稳定的做法; - 逐框架独立提交 + 双层测试:13 个框架各占一个可回滚的 commit,形状测试毫秒级兜底、Django e2e 用例作为最强行为保证,使"正则这类脆弱实现"的回归面被压到最小。
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 StartedRust0624
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