CPython C 扩展参数解析与返回值构建:PyArg_Parse 系列与 Py_BuildValue 格式串全解
本文基于 CPython 官方文档 Doc/c-api/arg.rst 展开,系统讲解 C 扩展开发中“参数解析”(PyArg_Parse* 家族)与“返回值构建”(Py_BuildValue)两大核心机制。读完本文,你将理解格式串(format string)的完整语法、字符串/缓冲区的三种内存语义(Py_buffer 借用、分配缓冲、裸指针借用)、所有数字与对象格式单元、|、$、:、; 等特殊控制字符的用法,并能结合 Python/getargs.c 与 Python/modsupport.c 的源码印证底层实现,写出可复制、可运行的 C 扩展代码。
一、总体模型:格式串驱动的参数解析
在编写 C 扩展函数时,Python 调用层会把参数打包传递给你:传统 METH_VARARGS 调用约定传一个 PyObject *args 元组;METH_VARARGS | METH_KEYWORDS 额外传关键字字典;METH_FASTCALL 则传一个参数指针数组加计数。PyArg_Parse* 系列函数的职责,就是把这堆 PyObject* 按你指定的“格式串”逐个转换、存进 C 局部变量。
文档明确了三个入口都使用同一套格式串语法:
PyArg_ParseTuple—— 解析只有位置参数的函数参数(args元组);PyArg_ParseTupleAndKeywords—— 解析位置 + 关键字参数;PyArg_Parse—— 解析单个位置参数(配合METH_O调用约定)。
格式串由零个或多个“格式单元”(format unit)组成。每个格式单元描述一个 Python 对象,通常是一个单独字符,也可以是括号括起来的格式单元序列。除少数例外,一个非嵌套括号的格式单元对应一个 C 侧的“地址参数”——即你传入的局部变量的地址。文档约定:引号形式是格式单元,圆括号内是匹配的 Python 对象类型,方括号内是应传入地址的 C 变量类型。
从源码结构看,这三个函数最终都汇聚到 Python/getargs.c 中的 vgetargs1_impl(变参被展平为 va_list),再由 convertsimple 逐字符分发处理(见 Python/getargs.c#L712)。PyArg_Parse 走 vgetargs1(args, format, &va, FLAG_COMPAT) 分支(Python/getargs.c#L78-L87),PyArg_ParseTuple 走 vgetargs1(..., 0)(Python/getargs.c#L103-L112)。
转换成功/失败的语义:转换成功要求 arg 对象与格式完全匹配,且格式串必须被完整耗尽。成功时函数返回非零(true),失败时返回 0 并抛出相应异常。当某个格式单元转换失败时,该单元及其后所有格式单元对应的 C 变量都保持原值不变——你无需手动清理已写入的变量。
二、字符串与缓冲区:三种内存语义
文档将“字符串/缓冲区转 C”归为三类,这是 C 扩展中最容易踩内存坑的地方,必须分清各自的释放责任:
y*、s*等填充Py_buffer的格式:它们会锁定(lock)底层缓冲区,使你在Py_BEGIN_ALLOW_THREADS代码块中使用时也不会遇到可变数据被扩容或销毁的风险。作为代价,你必须在处理完毕后(包括任何提前退出路径)调用PyBuffer_Release释放。es、es#、et、et#:由PyArg_ParseTuple负责分配结果缓冲区。你必须在处理完毕后调用PyMem_Free释放。- “借用”缓冲(borrowed buffer):其余格式(如
s、s#、y、y#)接收str或只读 bytes-like 对象,直接给出const char *裸指针。该缓冲区由对应 Python 对象管理,生命周期与对象一致,你不需要释放任何内存。
第 3 类“借用”有两层安全约束,文档说得很明确:
- 对象的
PyBufferProcs.bf_releasebuffer字段必须为NULL。这排除了常见的可变对象如bytearray,也排除了某些只读对象(例如指向bytes的memoryview); - 除此之外,CPython 不检查输入对象是否真的不可变(例如它是否会响应可写缓冲请求,或另一个线程是否可能修改数据)。
注意:在 Python 3.12 及更早版本中,若要使用所有
#变体格式(s#、y#等),必须在#include "Python.h"之前定义宏PY_SSIZE_T_CLEAN;Python 3.13 及之后不再需要。
2.1 字符串/缓冲区格式单元速查
| 格式单元 | 接受的 Python 类型 | C 变量类型 | 说明 |
|---|---|---|---|
s |
str |
const char * |
转换为 NUL 结尾的 UTF-8 C 字符串;含内嵌 NUL 时抛 ValueError;编码失败抛 UnicodeError。不接受 bytes-like 对象 |
s* |
str 或 bytes-like |
Py_buffer |
接受 Unicode 与 bytes-like,可含内嵌 NUL,需 PyBuffer_Release |
s# |
str、只读 bytes-like |
const char *、Py_ssize_t |
借用缓冲,指针 + 长度两个变量,可含内嵌 NUL |
z |
str 或 None |
const char * |
同 s,但 None 时指针置 NULL |
z* |
str、bytes-like 或 None |
Py_buffer |
同 s*,None 时 buf 成员为 NULL |
z# |
str、只读 bytes-like 或 None |
const char *、Py_ssize_t |
同 s#,None 时指针为 NULL |
y |
只读 bytes-like | const char * |
不接受 Unicode;含内嵌 NUL 抛 ValueError |
y* |
bytes-like | Py_buffer |
官方推荐接收二进制数据的方式 |
y# |
只读 bytes-like | const char *、Py_ssize_t |
同 s# 但仅限 bytes-like |
S |
bytes |
PyBytesObject *(或 PyObject *) |
严格类型检查,不做转换,非 bytes 抛 TypeError |
Y |
bytearray |
PyByteArrayObject *(或 PyObject *) |
同上,严格 bytearray |
U |
str |
PyObject * |
严格 Unicode 检查,不做转换 |
w* |
可读写 bytes-like | Py_buffer |
接受实现可读写缓冲接口的对象,需 PyBuffer_Release |
S、Y、U 的共同点是只验证类型、不做任何转换,因此 C 变量直接拿到对应对象指针,C 侧也可直接声明为 PyObject*。
关于 s 的补充(来自文档的 note):s 不接受 bytes-like 对象。如果你要接收文件系统路径并转成 C 字符串,更合适的是用 O& 格式配合 PyUnicode_FSConverter 作为 converter(3.5 之前的版本对内嵌 NUL 抛 TypeError,3.5 起改为 ValueError)。
2.2 es / et / es# / et#:显式指定编码
es(str → const char *encoding, char **buffer):把 Unicode 编码成字符缓冲,只支持不含内嵌 NUL 的编码结果。它需要两个 C 参数:
- 第一个仅作输入:指向编码名的 NUL 结尾 C 字符串,或
NULL(表示用'utf-8');指定了 Python 不认识的编码会抛异常; - 第二个必须是
char **:解析后指向编码结果的缓冲区。PyArg_ParseTuple会分配恰好需要的空间、拷贝数据并调整指针——调用方负责用PyMem_Free释放。
et(str/bytes/bytearray → 同上):与 es 相同,但字节串对象不经重新编码直接透传,实现上假定该字节串对象已经使用了你传入的参数编码。
es#(str → const char *encoding, char **buffer, Py_ssize_t *buffer_length):与 es 的区别是允许输入含 NUL 字符。第三个参数是指向整数的指针,被设置为输出缓冲的字节数。它有两种工作模式:
- 若
*buffer初始为NULL:函数分配所需缓冲区并拷贝,调用方须PyMem_Free; - 若
*buffer指向已分配的缓冲区:直接使用该内存,并把*buffer_length的初始值解释为缓冲区容量,拷贝并 NUL 结尾;容量不足时抛ValueError。
两种模式下 *buffer_length 最终都设置为编码数据的长度(不含结尾 NUL 字节)。et# 与 es# 相同,只是字节串对象直接透传不重编码。
从源码实现印证:这些“需要清理”的分配(Py_buffer 与 char **)在 Python/getargs.c 中通过 cleanup_ptr(内部调 PyMem_Free,Python/getargs.c#L202-L209)与 cleanup_buffer(内部调 PyBuffer_Release,Python/getargs.c#L211-L219)登记到 freelist;一旦解析中途失败,cleanreturn(Python/getargs.c#L235-L252)会自动执行已登记的清理函数,避免异常路径下的内存泄漏——这正是文档所说“或任何提前退出情形”背后有兜底的原因。
此外,3.12 起 u、u#、Z、Z# 已被移除,因为它们依赖遗留的 Py_UNICODE* 表示。
2.3 借用引用的通用规则
文档专门强调:传给调用方的任何 Python 对象引用都是借用引用(borrowed reference),不要释放它们(即不要减少引用计数)。同样,额外传入这些函数的参数必须是“类型由格式串决定的变量”的地址,用来存放输入元组中的值;只有少数格式单元(上文的 es、es# 等)把额外参数当输入用,此时必须与文档对应条目匹配。
三、数字格式:整数、字符与浮点
数字格式把 Python 数字(或单字符)表示为 C 数字。要求 int、float、complex 的格式也可以调用对象对应的 __index__、__float__ 或 __complex__ 方法完成转换。范围语义上:
- 有符号整数格式:值超出 C 类型范围抛
OverflowError; - 无符号整数格式:接收域太小时最高位静默截断,当值大于 C 类型最大值或小于同尺寸有符号类型的最小值时发出
DeprecationWarning。
完整对照表(引号格式单元 / 接受类型 / C 变量类型):
| 格式单元 | Python 类型 | C 变量类型 | 说明 |
|---|---|---|---|
b |
int |
unsigned char |
非负整数转无符号 tiny int |
B |
int |
unsigned char |
不做溢出检查 |
h |
int |
short int |
|
H |
int |
unsigned short int |
|
i |
int |
int |
|
I |
int |
unsigned int |
|
l |
int |
long int |
|
k |
int |
unsigned long |
3.14 起可用 __index__ |
L |
int |
long long |
|
K |
int |
unsigned long long |
3.14 起可用 __index__ |
n |
int |
Py_ssize_t |
首选的“平台指针尺寸整数” |
c |
长度为 1 的 bytes 或 bytearray |
char |
3.3 起允许 bytearray |
C |
长度为 1 的 str |
int |
|
f |
float |
float |
|
d |
float |
double |
|
D |
complex |
Py_complex |
注意 3.15 起,对无符号格式 B、H、I、k、K,当值超出范围时会发出 DeprecationWarning(文档标记为 deprecated 行为预警)。源码印证:Python/getargs.c 的 convertsimple 中,b 用 PyLong_AsLong 后手动检查 < 0 与 > UCHAR_MAX 抛 OverflowError,而 B 走 PyLong_AsNativeBytes 且对超宽值调 PyErr_WarnEx(PyExc_DeprecationWarning, "integer value out of range", 1)(Python/getargs.c#L712-L780),与文档描述逐字对应。
四、其他对象格式:O、O!、O&、p 与嵌套元组
4.1 O 与 O!
O(object →PyObject *):把 Python 对象原样存入 C 对象指针,不创建新强引用(引用计数不增加),存入的指针非NULL。O!(object → typeobject,PyObject *):与O类似但取两个 C 参数:第一个是 Python 类型对象的地址,第二个是存放对象指针的PyObject*变量地址;类型不符抛TypeError。
4.2 O&:自定义转换器
O& 通过 converter 函数把 Python 对象转成任意类型的 C 变量。它取两个参数:converter 函数本身,以及目标 C 变量地址(转成 void *)。converter 的调用协议是:
status = converter(object, address);
object 是待转换对象,address 是传给 PyArg_Parse* 的 void*。成功返回 1,失败返回 0(且 converter 应抛出异常并保持 address 内容不变)。官方示例 converter:PyUnicode_FSConverter 与 PyUnicode_FSDecoder。
Py_CLEANUP_SUPPORTED 机制:如果 converter 返回 Py_CLEANUP_SUPPORTED 标记,当参数解析最终失败时,它可能被第二次调用以释放已分配的内存——第二次调用时 object 参数为 NULL,address 与第一次相同。
4.3 p 与 (items)
-
p(bool→int,3.3 加入):对传入值做真值测试(predicate),结果为真置 1、假置 0。接受任何合法的 Python 值(真值语义见文档 truth 一节)。 -
(items)(sequence → 对应 matching-items):对象必须是 Python 序列(str、bytes、bytearray除外),长度必须等于 items 中格式单元的数量,C 参数须与 items 的单元一一对应,且允许嵌套。两条安全约束:若 items 内含存借用缓冲的单元(
s、s#、z、z#、y、y#)或借用引用的单元(S、Y、U、O、O!),则该对象必须是 tuple(3.14 起str与bytearray不再被接受为序列;3.14 同时把“含借用单元时用非 tuple 序列”标记为 deprecated)。items 中O&的 converter 不得存储借用缓冲或借用引用。
4.4 特殊控制字符:|、$、:、;
这些字符不能出现在嵌套括号内:
|:其后的参数变为可选。可选参数对应的 C 变量必须预先初始化为默认值——当调用方没有提供该参数时,PyArg_ParseTuple不会触碰这些变量。例如"OO|OO"对应 Python 签名f(a, b, c=None, d=None)。$(仅PyArg_ParseTupleAndKeywords,3.3 加入):其后的参数变为 keyword-only。若$之前出现过|则它们是可选的,否则是必需的;|不能出现在$之后。例如"O|O$O"对应f(a, b=None, *, c=None),"OO$OO"对应f(a, b, *, c, d)。::格式单元列表到此结束,冒号后的字符串用作错误信息中的函数名(即PyArg_ParseTuple抛出异常的“关联值”)。实践中强烈建议总是加上,例如"i:my_function",这样报错信息会写成my_function() argument must be ...。;:格式单元列表到此结束,分号后的字符串整体替代默认错误信息。:与;互斥。
五、API 函数族一览
5.1 解析函数
int PyArg_ParseTuple(PyObject *args, const char *format, ...);
int PyArg_VaParse(PyObject *args, const char *format, va_list vargs);
int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char * const *keywords, ...);
int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char * const *keywords, va_list vargs);
int PyArg_ValidateKeywordArguments(PyObject *);
int PyArg_Parse(PyObject *args, const char *format, ...);
int PyArg_ParseArray(PyObject *const *args, Py_ssize_t nargs, const char *format, ...); // 3.15+
int PyArg_ParseArrayAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames,
const char *format, const char * const *kwlist, ...); // 3.15+
PyArg_VaParse/PyArg_VaParseTupleAndKeywords与变参版本功能相同,只是接受va_list。PyArg_ParseTupleAndKeywords的keywords参数是 NULL 结尾的关键字参数名数组(NUL 结尾的 ASCII/UTF-8 C 字符串);空字符串名表示 positional-only 参数(3.6 加入)。- 版本变化要点:3.13 起
keywords参数类型在 C 中是char * const *、C++ 中是const char * const *(不再是char **),并支持非 ASCII 关键字参数名;可用PY_CXX_CONST宏覆盖该前缀(C 默认为空、C++ 默认为const,在包含Python.h前定义即可覆盖,3.13 加入)。 PyArg_ValidateKeywordArguments(3.2 加入):确保关键字字典的键都是字符串。只有在你不用PyArg_ParseTupleAndKeywords(它已内置该检查)时才需要。PyArg_Parse:解析单个位置参数,面向METH_O调用约定。文档给出的官方示例:
// Function using METH_O calling convention
static PyObject*
my_function(PyObject *module, PyObject *arg)
{
int value;
if (!PyArg_Parse(arg, "i:my_function", &value)) {
return NULL;
}
// ... use value ...
}
PyArg_ParseArray/PyArg_ParseArrayAndKeywords(均为 3.15 加入):分别解析METH_FASTCALL与METH_FASTCALL | METH_KEYWORDS约定下的数组参数(PyObject *const *args+nargs)以及关键字参数(kwnames+kwlist)。从源码看,它们都收敛到vgetargs1_impl/vgetargskeywords_impl(Python/getargs.c#L139-L170),与元组版本共享同一套格式单元语义。
5.2 PyArg_UnpackTuple:不用格式串的简单取参
int PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...);
这是一种“简单取参”形式:不使用格式串指定类型。使用它的函数应在函数/方法表中声明为 METH_VARARGS;args 必须是元组,长度至少 min、至多 max(两者可以相等)。额外参数各是一个指向 PyObject* 变量的指针,将被填入 args 中对应的值——注意是借用引用。未提供的可选参数对应的变量不会被写入,应由调用方预初始化。args 不是元组或元素数量不对时返回 false 并设置异常。
文档引用自 _weakref 辅助模块的源码示例:
static PyObject *
weakref_ref(PyObject *self, PyObject *args)
{
PyObject *object;
PyObject *callback = NULL;
PyObject *result = NULL;
if (PyArg_UnpackTuple(args, "ref", 1, 2, &object, &callback)) {
result = PyWeakref_NewRef(object, callback);
}
return result;
}
文档指出,这个调用与下面的 PyArg_ParseTuple 调用完全等价:
PyArg_ParseTuple(args, "O|O:ref", &object, &callback)
源码印证:PyArg_UnpackTuple 实现于 Python/getargs.c#L2898,失败分支会设置 "PyArg_UnpackTuple() argument list is not a tuple" 错误,与文档描述一致。
六、构建返回值:Py_BuildValue 与 Py_VaBuildValue
6.1 基本契约
PyObject* Py_BuildValue(const char *format, ...);
PyObject* Py_VaBuildValue(const char *format, va_list vargs);
Py_BuildValue 用与 PyArg_Parse* 相似的格式串 + 一组值创建新的 Python 值;出错返回 NULL 且异常已置位。Py_VaBuildValue 与之相同,只是接受 va_list。从源码看,Py_BuildValue 直接转发到 va_build_value(Python/modsupport.c#L496-L503),非法格式字符会触发 "bad format char passed to Py_BuildValue" 的 SystemError 路径(Python/modsupport.c#L487)。
三条易被忽略的契约:
- 不总是返回 tuple:只有格式串含两个及以上格式单元时才构建 tuple;空格式串返回
None;恰好一个单元时返回该单元描述的对象本身。要强制得到 0 元或 1 元 tuple,请给格式串加括号,如"(i)"。 - 缓冲区是拷贝而非引用:以
s、s#等格式提供的内存缓冲,其数据会被拷贝;Py_BuildValue创建的对象从不引用调用方的缓冲。换言之,若你malloc后把内存传给Py_BuildValue,Py_BuildValue返回后由你负责free该内存。 - 格式串中的空格、制表符、冒号和逗号被忽略(
s#这类单元内部除外),可用来提高长格式串的可读性。
6.2 构建格式单元对照表
| 格式单元 | 返回的 Python 类型 | C 参数类型 | 说明 |
|---|---|---|---|
s |
str 或 None |
const char * |
NUL 结尾 C 串按 'utf-8' 解码;指针为 NULL 时得 None |
s# |
str 或 None |
const char *、Py_ssize_t |
串 + 长度;NULL 时忽略长度得 None |
y |
bytes |
const char * |
C 串转 bytes;NULL 得 None |
y# |
bytes |
const char *、Py_ssize_t |
C 串 + 长度;NULL 得 None |
z / z# |
str 或 None |
同 s / s# |
与 s / s# 相同 |
u / u# |
str |
const wchar_t *(+ 长度) |
宽字符缓冲(UTF-16 或 UCS-4)转 Unicode;NULL 得 None |
U / U# |
str 或 None |
同 s / s# |
与 s / s# 相同 |
i |
int |
int |
|
b |
int |
char |
|
h |
int |
short int |
|
l |
int |
long int |
|
B |
int |
unsigned char |
|
H |
int |
unsigned short int |
|
I |
int |
unsigned int |
|
k |
int |
unsigned long |
|
L |
int |
long long |
|
K |
int |
unsigned long long |
|
n |
int |
Py_ssize_t |
|
p |
bool |
int |
必须传 int;变参不做自动类型收缩,其他类型可用 (x) ? 1 : 0 或 !!x 转换(3.14 加入) |
c |
长度 1 的 bytes |
char |
表示一个字节 |
C |
长度 1 的 str |
int |
表示一个字符 |
d / f |
float |
double / float |
|
D |
complex |
Py_complex * |
注意传结构体地址 |
O |
object | PyObject * |
原样传递但创建新强引用(引用计数 +1);传入 NULL 时假定上游出错并已置异常——Py_BuildValue 返回 NULL 但不抛新异常;若尚无异常则置 SystemError |
S |
object | PyObject * |
同 O |
N |
object | PyObject * |
同 O 但不创建新强引用;适合对象由参数列表中的构造器调用创建的情形(如 Py_BuildValue("N", obj) 把所有权交给返回值) |
O& |
object | converter, anything | 通过 converter 把 anything(应与 void* 兼容)转为新 Python 对象或 NULL |
(items) |
tuple |
对应 C 值 | 构建等长元组 |
[items] |
list |
对应 C 值 | 构建等长列表 |
{items} |
dict |
成对 C 值 | 每连续两个值构成一对键值 |
格式串本身有语法错误时,置 SystemError 并返回 NULL。
七、实战要点小结(对应文档结论)
- 选对函数:只有位置参数用
PyArg_ParseTuple;位置 + 关键字用PyArg_ParseTupleAndKeywords(记住|/$语义与空名 positional-only);METH_O单参数用PyArg_Parse;METH_FASTCALL用 3.15 的PyArg_ParseArray/PyArg_ParseArrayAndKeywords;不想引入类型转换就用PyArg_UnpackTuple。 - 格式串尾随
:函数名几乎总是值得写,它直接决定用户看到什么报错;需要完全自定义错误文案时用;message替代(二者互斥)。 - 可选参数先赋默认值:
|之后的变量在调用方省略时不会被写入。 - 分清三种内存语义:
Py_buffer系(s*/y*/w*/z*)用PyBuffer_Release收尾;es/es#/et/et#用PyMem_Free收尾;借用指针(s/s#/y/y#/z系)不释放但生命周期依赖源对象;解析器自身的失败路径会自动走 freelist 兜底清理(见 Python/getargs.c#L200-L252)。 Py_BuildValue的O/N选择:已有引用、想移交所有权用N;普通对象引用、需要 +1 强引用用O;NULL参数的语义是“上游已出错”的哨兵。- 更多扩展函数与方法的上下文示例可参阅 Doc/extending/index.rst;格式串的公共声明位于 Include/modsupport.h(如
PyArg_ParseTuple的原型声明),实现主体在 Python/getargs.c(解析)与 Python/modsupport.c(构建)。
适用前提:以上版本行为(3.13 的
keywords类型变化、3.14 的k/K支持__index__、3.15 的PyArg_ParseArray*与无符号溢出DeprecationWarning等)均以当前仓库(CPython 主干,对应 3.15+ 开发版本)文档与源码为准,移植到 3.13/3.14 及以下运行时请核对对应版本的差异。
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