Dioxus RSX 宏与 Autofmt 格式化器架构详解:从 JSX 风格语法到模板代码生成
本文基于 Dioxus 仓库内的架构文档 notes/architecture/03-RSX.md,系统讲解 dioxus-rsx 宏如何把 JSX 风格的 RSX 语法解析为 Rust AST 并展开为模板代码,以及 dioxus-autofmt 如何对 RSX 块做无损格式化。读完本文,你可以理解 rsx! {} 宏内部的解析优先级、属性合并、动态索引分配与热重载映射机制,并能基于源码定位修改 RSX 语法、新增节点类型或调整格式化启发式的切入点。
一、两个 crate 的分工与整体流水线
Dioxus 的模板系统由两个 crate 协同完成(见 03-RSX.md 开头):
dioxus-rsx:解析 JSX 风格语法并生成 Rust 代码。入口类型是CallBody,即rsx! {}宏的内容根节点。dioxus-autofmt:提供格式化能力。它复用dioxus-rsx的解析结果(CallBody),把整个 rsx! 块重写为规范格式,再转换为 IDE 可精确应用的FormattedBlock编辑集合。
两者的衔接点在于:autofmt 并不自己解析语法,而是调用 CallBody::parse_strict 完成解析,再用自己的 Writer 按 AST 重新输出文本。解析一次,两处复用——这也是"新增节点类型需要同时改解析器和 Writer"这一扩展约束的由来。
二、RSX 解析入口:CallBody
CallBody 是 rsx! 宏内容的根结构,定义于 packages/rsx/src/rsx_call.rs:
CallBody
├── body: TemplateBody (BodyNode 根节点列表)
├── template_idx: Cell<usize> (模板索引计数器)
└── span: Option<Span>
其核心行为有三个关键点:
Parse实现委托给CallBody::new:解析时先input.parse::<TemplateBody>(),再走new(),目的是"把热重载信息接线到嵌套结构中"(源码注释:Defer to the new method such that we can wire up hotreload information)。CallBody::new()做三件事(见 rsx_call.rs):- 调用
body.split_oversized_templates()预先切分超出存储上限的超大模板(见第五节); - 初始化
template_idx = 0并给根模板分配第一个索引; - 调用
cascade_hotreload_info()递归遍历所有节点,为每个嵌套的TemplateBody(组件子体、for 循环体、if 链各分支)分配顺序模板索引。
- 调用
ToTokens直接委托给body.to_tokens(out):CallBody本身不含渲染逻辑,只承载解析到的 token 信息。
BodyNode 枚举:RSX 内容的六种基本形态
BodyNode 定义于 packages/rsx/src/node.rs,架构文档列出六种变体:
Element(Element)— HTML 元素(div、span)Component(Component)— 用户组件Text(TextNode)— 带插值的字符串字面量RawExpr(ExprNode)— 花括号表达式{expr}ForLoop(ForLoop)—for pat in expr { body }IfChain(IfChain)—if cond { } else { }
需要注意,从当前源码结构看,实际还存在第七个变体 SyntheticBoundary(Box<TemplateBody>),它是宏在切分超大模板时自动插入的"动态边界"(见第五节),源码注释说明它是"包裹嵌套静态模板的动态边界"。这个变体不面向用户语法,纯粹是模板存储容量管理的产物。
解析优先级
BodyNode::parse(node.rs)按以下顺序 peek 决策:
- Peek
LitStr→TextNode - Peek
for→ForLoop - Peek
if→IfChain - Peek
match→RawExpr(match 语句没有特殊分支语法,整体作为表达式处理) - Peek
Brace→RawExpr(花括号包裹的任意表达式) - Web 组件:
Ident后紧跟-→Element(Web Components 不支持命名空间,直接按元素解析,如my-cool-el {}) - 小写 ident 且不含下划线 →
Element(下划线保留给组件名使用) - 兜底 →
Component
第 6、7 条是元素与组件区分的核心规则:div {} 是元素,Div {} 或 crate::Div {} 是组件。源码中的测试 parsing_matches 逐条验证了上述每个分支的解析归类,包括 match 表达式落为 RawExpr、some::cool::Component 落为 Component 等边界情况。
三、核心 AST 类型详解
Element 与属性合并
Element 结构(packages/rsx/src/element.rs):
Element
├── name: ElementName (Ident 或 Custom)
├── raw_attributes: Vec<Attribute> // 原始解析结果
├── merged_attributes: Vec<Attribute> // 重名属性合并后
├── spreads: Vec<Spread> // ..attr 展开
├── children: Vec<BodyNode>
├── brace: Option<Brace>
└── diagnostics: Diagnostics
两个设计细节值得展开:
- 宽松解析:解析器非常宽容地解析元素——即使缺少花括号也不会解析失败,而是往
diagnostics里推入 "Elements must be followed by braces" 诊断。这样宏可以在渲染时以诊断(而非编译错误)形式报出,保持"每个 CallBody 都应可构建"。 merge_attributes()的重名合并(element.rs):同名的多个属性会被折叠为一个。合并策略是构造IfmtInput,段之间用空格作分隔符(源码 FIXME 注释说明这是刻意的特例,期望多行字符串能以空格合并)。规则包括:key类属性(name.is_likely_key())跳过合并;- 单个同名属性直接保留;
- 字面量、
if cond { "value" }条件值都可以合并进格式化字符串; - 表达式、裸布尔等无法合并的类型会推入 "Cannot merge non-fmt literals" 诊断。
merge_all_attributes 测试 展示了完整效果:class: "foo" + class: "{bar}" + class: if true { "baz" } + class: if false { "{qux}" } else { "quux" } 四个属性合并为一个,展开为 ::std::format!("foo {0:} {1:} {2:}", bar, ...)。这解释了 Dioxus 中重复写 class 而非数组的经典用法。
ElementName 还有 Ident(Ident) 与 Custom(LitStr) 两个变体:解析时把 ident - ident - ... 序列按 - 连接成 LitStr,这就是 some-cool-element 写法能工作的原因。
Attribute 与 AttributeValue
Attribute(packages/rsx/src/attribute.rs):
Attribute
├── name: AttributeName (BuiltIn | Custom | Spread)
├── colon: Option<Token![:]> // 为无损解析保留
├── value: AttributeValue
├── comma: Option<Token![,]>
└── el_name: Option<ElementName> // 绑定元素时用于属性名/命名空间解析
AttributeValue 的五种变体(attribute.rs):
Shorthand(Ident)— 无值属性。解析规则是"ident 后不跟冒号即 shorthand",例如disabled或checked: checked(同名单 ident 表达式也可视为 shorthand);AttrLiteral(HotLiteral)— 字面量,注释明确"这些获得热重载超能力";EventTokens(PartialClosure)— 事件处理器。用专门类型是为了在闭包内提供自动补全(部分展开),并对 Rust 泛型闭包类型推断做额外包装。事件值以move或|开头时走此分支;IfExpr(IfAttributeValue)— 条件属性attr: if cond { "a" } else { "b" };AttrExpr(PartialExpr)— 任意表达式。
事件处理器还有一个值得注意的机制:event_handler_method() 会检测内联闭包,若是则改用隐藏的 onxxx_with_explicit_closure 方法,让闭包参数获得已知类型,用户无需手动标注(attribute.rs)。
HotLiteral:可热重载的字面量
HotLiteral(packages/rsx/src/literal.rs):
HotLiteral
├── Fmted(HotReloadFormattedSegment) // "{expr}" 插值格式化字符串
├── Float(LitFloat)
├── Int(LitInt)
└── Bool(LitBool)
字符串字面量一律包装为 Fmted,因为需要区分"会生成 String 的格式化串"与"会生成 &'static str 的裸串"——这个区分对组件 props 的类型推导至关重要(源码注释原话)。is_static() 判断是否全为字面段,静态串可以直接输出为 &'static str,避免运行时格式化开销。
IfmtInput:格式化字符串的段解析
IfmtInput(packages/rsx/src/ifmt.rs)把字符串内容拆为段:
Segment::Literal(String)— 纯文本Segment::Formatted(FormattedSegment)—{expr}插值
解析规则(IfmtInput::from_raw,ifmt.rs)与文档一致并有细节补充:
{{→ 字面量{;}}→ 字面量}{expr}→ 格式化段{expr:format_args}→ 带格式说明符的段。解析器还专门处理了:::两个连续冒号视为路径分隔符而非格式参数分隔符,因此{path::expr}这类写法可以正常工作- 孤立的
}会报 "unmatched closing '}' in format string" 错误
ToTokens 生成时有三级降级策略(ifmt.rs):全静态直接输出 &str;release 模式下单插值优化为 expr.to_string()(try_to_string);简单 ident 插值直接用 ::std::format!(raw) 享受 Rust 分析器的重命名展开。只有复杂表达式才走 FmtedSegments 动态池路径。
Component / ForLoop / IfChain
Component
├── name: syn::Path
├── generics: Option<AngleBracketedGenericArguments>
├── fields: Vec<Attribute>
├── component_literal_dyn_idx: Vec<DynIdx>
├── spreads: Vec<Spread>
├── children: TemplateBody
├── dyn_idx: DynIdx
└── diagnostics: Diagnostics
ForLoop
├── for_token, pat, in_token
├── expr: Box<Expr>
├── body: TemplateBody
└── dyn_idx: DynIdx
IfChain
├── if_token, cond: Box<Expr>
├── then_branch: TemplateBody
├── else_if_branch: Option<Box<IfChain>>
├── else_branch: Option<TemplateBody>
└── dyn_idx: DynIdx
for 循环在解析后改写为 (expr).into_iter().map(|pat| { body })(见 forloop.rs 的 ToTokens 与 node.rs 中 "Transform for loops into into_iter calls" 的注释)。if 链则把"未终止的 if 语句"转换为止于可选分支的终止形式。两者各自携带 TemplateBody,因此都会获得独立的模板索引(见 cascade_hotreload_info)。
DynIdx:热重载映射的索引
DynIdx 是 Cell<Option<usize>>,用于追踪动态节点/属性的索引。它刻意对 PartialEq/Eq/Hash 保持"透明"(比较时忽略内部值),这样含 DynIdx 的 AST 节点仍可做相等性比较。索引与 file!()、line!()、column!() 组合,构成每个模板在热重载系统中的全局定位。
四、代码生成:从 TemplateBody 到模板 VNode
文档描述的经典输出结构
架构文档给出的 rsx! 展开产物结构是:
__TEMPLATE_ROOTS—TemplateNode静态数组- 动态节点数组
__dynamic_nodes: [DynamicNode; N]— 组件、插值文本、循环、条件 - 动态属性数组
__dynamic_attributes— 非常量属性值 - 动态字面量池 — debug 模式下格式化后的字面量 vec
- 动态值池 — 把字面量索引映射到运行值
dioxus_core::Element::Ok({
#[cfg(debug_assertions)]
fn __original_template() -> &'static HotReloadedTemplate { ... }
let __dynamic_nodes: [DynamicNode; N] = [ ... ];
let __dynamic_attributes: [Box<[Attribute]>; M] = [ ... ];
static __TEMPLATE_ROOTS: &[TemplateNode] = &[ ... ];
// Template 引用与渲染
})
生成的 TemplateNode 有三种形态:Element { tag, namespace, attrs, children }(静态元素)、Text { text }(静态文本)、Dynamic { id }(引用动态节点池)。
当前源码中的实现演进
从 packages/rsx/src/template_body.rs 的当前实现看,代码生成已经从"直接生成节点数组"演进为类型化 View 构建器(ViewBuilder):ToTokens for TemplateBody 先调用 self.normalized(),再由 ViewBuilderPieces::from_body(&node) 单次遍历同时产出——release 用的类型化 view 表达式、模板容量统计(TemplateStatsBuilder)、debug 用的热重载表。关键的 ToTokens 展开结构(template_body.rs):
- release 路径:
dioxus_core::view::into_vnode_with_capacity::<OPS, STRINGS, DYNAMICS, _>(__view)直接以编译期预测的容量构建 VNode,模板是跨热重载稳定的const &'static Template; - debug 路径:
static __RUNTIME_TEMPLATE: OnceLock<Template>缓存运行时降级的模板,"避免每处 const 求值拖慢编译,同时产生完全相同的模板"(源码注释原话);并通过GlobalSignal::with_location(|| None, file, line, column, template_idx)建立热重载槽位——key 由归一化后的文件路径、行列号和模板索引组成。若读不到热重载后的模板,则回退到__original_template,注释特别指出"宏内嵌套模板可能因相同的 file-line-column-index 被合并,无法热重载,回退可防止错误渲染"; - 动态字面量池
__dynamic_literal_pool与动态值池DynamicValuePool::from_vnode(...).render_with(__template_read)保留了文档描述的"字面量池 + 值池"两层结构,用于 debug 下替换模板中的动态段。
ViewBuilder 的遍历规则(template_body.rs)与文档的动态节点划分一一对应:静态文本走 StaticTextBuilder(生成 impl dioxus_core::view::StaticText { const TEXT: &'static str } 标记结构体);含插值的文本、Component、RawExpr、ForLoop、IfChain、SyntheticBoundary 一律经 dynamic_node() 分配递增 id 并注册 HotReloadDynamicNode::Dynamic(id)。动态属性则经 track_dynamic_attr 注册 HotReloadDynamicAttribute::Dynamic(id),其内嵌格式化字面量会同步进入动态文本池,保证"属性、key、子节点"的填充顺序与运行时池严格对齐(源码有专门注释说明 key 段必须先行分配)。
模板 ID 分配与热重载定位
CallBody::next_template_idx()生成顺序 ID,每个嵌套结构(组件子体、循环体、if 各分支、合成边界)各得唯一 ID;- 结合
file!()、line!()、column!()组成源位置,与GlobalSignal的 key 一起实现"改哪处 rsx 就重载哪个模板"。
HotReloadFormattedSegment
它包裹 IfmtInput 并维护 dynamic_node_indexes:每个 Segment::Formatted 对应一个动态节点 id。allocate_formatted()(template_body.rs)为格式化段分配动态文本池索引,再由 quote_with_dynamic_ids() 生成 FmtSegment::Dynamic { id } 序列——这就是"格式化段 → 动态节点"映射的落地点。
超大模板切分
split_oversized_templates()(template_body.rs)在 CallBody::new 阶段运行:当某层兄弟节点的路径位数超过 TEMPLATE_SLOT_PATH_MAX_PATH_BITS(127,测试 path_bit_split_limit_matches_slot_path_payload_capacity 验证了该边界)、或 ops/strings 超过 TEMPLATE_STORAGE_MAX_CAP、动态节点/属性数超过 u16::MAX 时,把节点列表对半切分为多个 TemplateBody,包成 BodyNode::SyntheticBoundary。展开时它经 dioxus_core::IntoDynNode::into_dyn_node(...) 转成动态节点——用户语法不受影响,存储容量约束被宏静默消化。
五、Autofmt 格式化系统
入口 API
dioxus-autofmt 的三个入口(packages/autofmt/src/lib.rs):
try_fmt_file(contents, &syn::File, IndentOptions) -> syn::Result<Vec<FormattedBlock>>— 格式化完整文件,返回供 IDE 应用的块级编辑集合;fmt_block(block_str, indent_level, IndentOptions) -> Option<String>— 格式化单个 rsx! 块;write_block_out(body: &CallBody) -> Option<String>— 把已解析的 CallBody 写回字符串。
旧的 fmt_file 已被标记 #[deprecated]("错误时会 panic,请用 try_fmt_file")。FormattedBlock 携带 formatted 新内容、start/end 字节偏移,专为 VSCode 的 TextEdit API 定制;文档注释坦承"目前按整个 rsx! 块重写,而非逐行精确修改",API 设计保留向更精确编辑方式迁移的空间。
try_fmt_file 的工作流程
collect_from_file(parsed)收集文件中所有 rsx! 宏调用;- 逐个解析(
CallBody::parse_strict,宏体内有错误立即返回),跳过已被外层宏覆盖的内层宏(按 span 比较); - 用
Writer::new(contents, indent)写出格式化文本,并把 Writer 缩进对齐到宏所在行的实际缩进(count_indents); - 短块短路优化(lib.rs):
// 若格式化后 <= 80 字符、无换行、不是单一表达式、非空,则折成单行
if formatted.len() <= 80
&& !formatted.contains('\n')
&& !body_is_solo_expr
&& !formatted.trim().is_empty()
{
formatted = format!(" {formatted} "); // 折叠为 div { ... } 单行形式
}
body_is_solo_expr 特例:单根节点为 RawExpr/Text 时不折叠——源码注释解释这是为了保持 rustfmt 与宏格式化的边界(rustfmt 会处理"宏 + 单表达式"的空格,若 autofmt 也折叠会互相干扰);
5. 与原文逐字节比较,相同则跳过,不同则追加 FormattedBlock。
apply_formats() 按 start/end 拼接所有块,得到最终文件内容。
Writer 与 Buffer 状态
Writer
├── raw_src: &str
├── src: Vec<&str> // 按行切分的原文件
├── cached_formats: HashMap<LineColumn, String> // 表达式格式化缓存
├── out: Buffer
└── invalid_exprs: Vec<Span> // 记录无法格式化的不完整表达式
Buffer
├── buf: String
├── indent_level: usize
└── indent: IndentOptions
invalid_exprs 的存在对应一个实用约束:try_fmt_file 对"不完整表达式"会提前返回错误,注释说明"虽然我们可以返回部分表达式,但最终表达式格式化会交给 rustfmt,而 rustfmt 会拒绝不完整代码"——即 rsx! 内写了半个闭包时,保存动作会得到明确错误而不是坏输出。
四级优化级别
Writer::write_rsx_block(packages/autofmt/src/writer.rs)内部定义了 ShortOptimization 枚举,与文档的四级一致:
- Empty:
div {}(花括号内不加空格,直接输出}); - Oneliner:
div { class: "x", child {} }(属性与子节点全在一行,用空格分隔); - PropsOnTop:属性保持在首行,子节点换行缩进排列;
- NoOpt:一切多行——每个属性独占一行并缩进。
决策规则从源码可见:属性列表"短"的定义是 is_short_attrs 累加长度 + 当前缩进 ×4 < 80,且最多 3 个属性(超过直接返回哨兵值 100000);子节点短小判定叠加在 100 字符预算内(children_len + attr_len + indent_level * 4 < 100);此外若属性超长(attr_len > 1000,即包含换行/注释的哨兵值)或 split_line_attributes() 开启,强制降级为 NoOpt。
空白与注释保留
RSX 中空白是显著的:文本节点保留精确空白、注释必须保留、格式化不得改变文本节点内容。源码里对应的实现:
accumulate_full_line_comments()(writer.rs):从节点 span 起点向上回溯收集整行//注释(最多保留一个空行),写入时随节点一起输出,保证节点上方的注释块不丢、位置跟随节点移动;write_inline_comments()保留行尾注释,通过LineColumn(来自 Span)定位当前行剩余内容,仅当以//开头时才追加;write_attr_comments()只处理"注释属于属性自身行"的情况——比较属性 span 与左花括号是否同行,避免把上一行末尾的注释错误归属。
表达式格式化与 Marker 替换
write_partial_expr() 的策略(文档 + writer.rs):
- 用 vendored 的
prettier_please对表达式做 unparse; - 特殊处理嵌套的
rsx!宏:prettier_please不认识 rsx! 语法,所以 unparse 前把宏路径替换为 marker Unicode 字符串"𝕣𝕤𝕩"(packages/autofmt/src/prettier_please.rs 中const MARKER: &str = "𝕣𝕤𝕩"),用 rsx 宏自身格式化结果替换回 marker,避免与真实代码冲突; - 与源文本逐行比对:源中的行注释(正则
"[^"]*|(//.*)区分字符串字面量与注释)会被重新注入 pretty 输出,保证表达式内的注释不丢失。
缩进系统
IndentOptions(packages/autofmt/src/indent.rs):
IndentOptions
├── width: usize
├── indent_string: String ("\t" 或若干空格)
└── split_line_attributes: bool
IndentOptions::new(IndentType, width, split_line_attributes)构造,width == 0直接断言失败;Tabs用"\t",Spaces用width个空格;- 默认值是 4 空格、不拆行属性(
IndentType::Spaces, 4, false); line_length()把 tab 按width计宽,count_indents()对 tab 和整组空格都计数(indent.rs),测试覆盖了 tab 与空格混排的场景;split_line_attributes: true时所有属性强制独占一行(对应优化级别被压到 NoOpt)。
属性格式化
write_attributes(props_same_line):true时属性一行排列、逗号后跟空格;false时每属性换行缩进,且属性间允许插入行尾注释;write_attribute_value()的分发(writer.rs):Shorthand→ 只写 ident;AttrLiteral→ 用Display实现直接输出(HotLiteral::fmt对字符串走to_string_with_quotes(),保留转义与引号);EventTokens→write_partial_expr(),缩进层级 +1;IfExpr→write_attribute_if_chain(),按"格式化长度 ≤ 80 − 当前缩进×4"的预算决定内联(if cond { "a" } else { "b" }一行写完)还是多行展开;AttrExpr→write_partial_expr()。
六、扩展点
架构文档给出的三条扩展路径,与源码结构完全对应:
新增节点类型
- 给
BodyNode枚举加变体(node.rs); - 实现
Parsetrait 并在BodyNode::parse的优先级链中插入 peek 分支; - 实现
ToTokens完成代码生成(ViewBuilder 的visit_node也要覆盖新分支); - 在 autofmt 的
Writer::write_ident(writer.rs)中加对应write_*方法——SyntheticBoundary的write_synthetic_boundary就是一个现成例子。
新增属性值类型
- 给
AttributeValue加变体(attribute.rs); - 在
AttributeValue::parse中实现探测(如if→IfExpr、move/|→EventTokens的先例); - 视需要在
Element::merge_attributes中加合并处理或诊断; - 在
Writer::write_attribute_value加分发分支。
调整格式化启发式
- 修改
ShortOptimization的判定逻辑与降级条件; - 调整阈值常量:属性短判定 80、Oneliner 总预算 100、整块短路 80、单属性超长按 1000/哨兵 100000;
- 修改
attr_value_len/is_short_attrs等长度估算函数(注意含注释或换行的表达式统一按 100000 处理,即强制多行)。
七、小结与源码索引
| 主题 | 关键文件 |
|---|---|
| 宏入口与模板索引级联 | packages/rsx/src/rsx_call.rs |
| BodyNode 解析优先级 | packages/rsx/src/node.rs |
| 元素解析与属性合并 | packages/rsx/src/element.rs |
| 属性与属性值类型 | packages/rsx/src/attribute.rs |
| 热重载字面量 | packages/rsx/src/literal.rs |
| 格式化字符串段解析 | packages/rsx/src/ifmt.rs |
| 模板降级与代码生成 | packages/rsx/src/template_body.rs |
| 循环 / 条件节点 | packages/rsx/src/forloop.rs、packages/rsx/src/ifchain.rs |
| 格式化入口与块编辑 | packages/autofmt/src/lib.rs |
| Writer 与优化级别 | packages/autofmt/src/writer.rs |
| 缩进选项 | packages/autofmt/src/indent.rs |
| Marker 替换 | packages/autofmt/src/prettier_please.rs |
整套设计的主线是"解析一次,多处复用":dioxus-rsx 产出的 CallBody 既是 rsx! 宏展开的输入,也是 autofmt 重写的依据,还是热重载索引级联的载体。理解 template_idx/DynIdx 如何把"AST 位置"编码为"模板池下标 + 源位置",是把握 Dioxus 热重载与模板存储机制的钥匙;而 autofmt 的四级优化与 80/100 字符预算则解释了你在 IDE 中看到的每一种 rsx 排版形态从何而来。
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 StartedRust0623
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