Windows Terminal 缓冲区导出与会话日志设计详解:从 exportBuffer 动作到 toggleLogging 路线图
本文围绕 Windows Terminal(microsoft/terminal 仓库)的设计草案 "Buffer Exporting and Logging"(对应 issue #642)展开,系统梳理其解决方案设计——exportBuffer() 与 toggleLogging() 动作、profile 级 logSettings/logAutomatically 设置、以及路径格式化字符串的选型讨论;并结合仓库当前源码印证缓冲区导出功能(Story A/B/C 路线)的实际实现链路,包括文件选择器、环境变量展开与原子写文件,帮助读者既理解设计蓝图,也掌握今天就能上手的导出用法。
ConEmu 的日志设置页面:可配置日志输出文件与命名规则(原 spec 图 3)
PuTTY 的日志设置页面:可指定日志类型与文件名(原 spec 图 1)
1. 背景:会话历史导出是终端模拟器的普遍需求
Spec 原文指出,一个常见的用户诉求是把终端会话的历史导出到文件,以便事后审查或验证。这有两种形态:
- 手动导出:在需要时把当前缓冲区内容导出到文件;
- 自动日志:终端自动把会话输出记录到文件,使历史始终被捕获。
在设计灵感方面,作者对比了 PuTTY、SecureCRT、ConEmu 三款工具的设置页面(原文图 1、图 2、图 3,其中 SecureCRT 截图见 [SecureCRT-logging-settings.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/drafts/?utm_source=gitcode_repo_files#642 - Buffer Exporting and Logging/SecureCRT-logging-settings.png))。三者的共性是:允许指定日志文件路径,且路径支持特殊格式化字符串,从而可以按会话的时间/日期或会话名拆分不同的日志文件。这一共性直接影响了 Windows Terminal 中 path 参数的设计。
2. 用户故事:用户到底想做什么
Spec 定义了七个用户故事,覆盖"手动/自动" × "显式路径/运行时提示" × "覆盖/追加"的组合,构成整个功能设计的验收标准:
- Story A:用户能通过 tab 的上下文菜单项,把缓冲区内容导出到文件(保存位置由弹窗提示)。这正是 issue #642 的原始诉求。
- Story B:用户可以绑定一个动作,导出缓冲区内容到文件,运行时提示保存位置。与 A 类似,但经由快捷键或命令面板触发。
- Story C:用户能通过动作导出到显式指定的文件——与 B 类似,但在设置中声明文件路径,不在运行时提示。
- Story D:导出时可以选择追加到文件,而非覆盖。
- Story E:用户可以在路径中写格式化字符串,由 Terminal 自动替换时间、日期、profile 名等变量。
- Story F:打开某个特定 profile 时,可以自动记录日志到文件。
- Story G:用户能执行一个动作来开始/停止向给定文件写日志。
3. 解决方案设计:两个动作与两个 profile 设置
这是 spec 的核心骨架,各参数语义均继承原文。
3.1 新动作:exportBuffer()
把缓冲区内容导出到文件,支持参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
string | "" |
为空时弹出文件选择器让用户选择导出文件。路径支持特殊的格式化字符串,会被替换为特定变量(见 §5) |
append |
boolean | false |
为 false 时覆盖文件内容;为 true 时把缓冲区内容追加到文件末尾 |
3.2 新 profile 设置对象:logSettings
描述记录某个 profile 日志的一组行为:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
string | — | 同 exportBuffer() 的 path |
append |
boolean | — | 同 exportBuffer() 的 append |
captureAllOutput |
boolean | false |
为 true 时不仅记录可打印字符,还记录写入终端的非打印转义序列 |
captureInput |
boolean | false |
额外把输入到终端的内容记入文件。输入会以传统 VT 序列格式记录,而不是完整的 win32-input 编码 |
newFileEveryDay |
boolean | false |
要求路径格式化串中包含 day 元素。启用该设置记录日志时,午夜打开新文件并开始写入新文件 |
原文还保留了一个被注释的规划参数 flushFrequently(默认 true):控制是否频繁把输出冲刷到文件,而不是只在关闭或换行时冲刷。作者自注"需要更多咖啡",因为尚未弄清 PuTTY 在关闭该选项时的具体冲刷时机。
3.3 新 profile 设置与动作:logAutomatically 与 toggleLogging()
logAutomatically(boolean,默认false):为 true 时,使用该 profile 的终端打开后会自动开始记录日志。toggleLogging()动作:开始或停止向配置的文件写日志。行为规则:- 若终端已在以不同于本动作的设置记录日志,则先停止(而不是直接改记到新文件);
- 接受与 profile
logSettings对象完全相同的参数集; - 提供了任何参数就用这些参数;一个都没提供则回退到 profile 中的日志设置(如果有的话);
- 若动作参数与 profile 中都未提供路径,则提示用户选择日志文件。
3.4 完整 JSON 配置示例
Spec 给出了一个完整配置示例(原文照录并补充注释),一次性演示七个故事:
{
"actions": [
{ "keys": "f1", "command": "exportBuffer" },
{ "keys": "f2", "command": { "action": "exportBuffer", "path": "c:\\logs\\${year}-${month}-${date}\\{profile}.txt" } },
{ "keys": "f3", "command": "toggleLogging" },
{ "keys": "f4", "command": { "action": "toggleLogging", "path": "c:\\logs\\${profile}.log", "append": true } }
],
"profiles": [
{
"name": "foo",
"logging": {
"path": "c:\\foo.txt",
"append": true
},
"automaticallyLog": false
},
{
"name": "bar",
"logging": {
"path": "c:\\logs\\${date}\\bar.txt",
"append": false
},
"automaticallyLog": true
}
]
}
参数逐项对照 §3.1–3.3:
- F1:无参
exportBuffer→ 弹出文件选择器(Story B); - F2:带
path的exportBuffer,路径含格式化字符串 → 按一键直接导出到固定路径(Story C/E); - F3:无参
toggleLogging→ 使用 profile 的logging设置开始/停止日志(Story G;在 profile "foo" 下即回退到 foo 的设置); - F4:显式参数的
toggleLogging→ 忽略 profile 设置,以追加方式记到c:\logs\{profile}.log(Story D/G); - profile "foo":配置了日志设置但
automaticallyLog为 false,打开时不会自动记录; - profile "bar":
automaticallyLog为 true,打开即自动记录日志(Story F)。
用该示例复核七个故事:
- Story A:已由 PR #11062 实现(即 tab 上下文菜单中现成的"导出文本"入口);
- Story B:F1 绑定的动作;
- Story C:F2 绑定的动作;
- Story D:动作与 profile 设置中的
append属性; - Story E:F2、F4 动作与 profile "bar" 的 logging 设置;
- Story F:profile "bar" 配置为打开时自动记录;
- Story G:F4 绑定的动作。
补充行为:打开 profile "foo" 时不会自动记录日志;按 F3 开始记录到 c:\foo.txt,按 F4 开始记录到 c:\logs\foo.log。
4. 对照仓库当前源码:缓冲区导出的实现现状
Spec 并非纯蓝图。在当前仓库中,"Buffer exporting"部分已部分落地(Story A/B/C 路线),而自动记录日志(toggleLogging、logSettings、logAutomatically)仍停留在草案阶段。以下用源码证据还原 exportBuffer 动作的真实实现链。
4.1 动作注册与参数定义
- 动作字符串标识
exportBuffer定义于 ActionAndArgs.cpp:static constexpr std::string_view ExportBufferKey{ "exportBuffer" };。 - 参数类
ExportBufferArgs在 WinRT IDL ActionArgs.idl 中声明:
[default_interface] runtimeclass ExportBufferArgs : IActionArgs, IActionArgsDescriptorAccess
{
ExportBufferArgs(String path);
String Path { get; };
}
注意与 spec 的差异:当前参数类只有 Path,尚无 append 布尔参数,即导出目前只支持覆盖式写入,spec 中的 Story D"追加"部分尚待补齐。
- 内置动作清单中,
exportBuffer映射到Terminal.ExportBuffer命令 id,见 defaults.json;显示名取自 ActionMap.cpp 中ShortcutAction::ExportBuffer对应的ExportBuffer资源。 - 一个值得注意的细节:ActionArgs.cpp 中
ExportBufferArgs::GenerateName会依据Path是否为空动态生成命令面板里的显示名——带路径时用ExportBufferToPathCommandKey模板,无路径时用ExportBufferCommandKey。用户在命令面板里因此能直观区分"导出到指定路径"与"弹出选择器"两种形态。
4.2 处理入口:_HandleExportBuffer
动作分发入口在 AppActionHandlers.cpp:
void TerminalPage::_HandleExportBuffer(const IInspectable& sender,
const ActionEventArgs& args)
{
if (const auto activeTab{ _senderOrFocusedTab(sender) })
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ExportBufferArgs>())
{
_ExportTab(*activeTab, realArgs.Path());
args.Handled(true);
return;
}
}
// If we didn't have args, or the args weren't ExportBufferArgs (somehow)
_ExportTab(*activeTab, L"");
if (args)
{
args.Handled(true);
}
}
}
逻辑与 spec 一一对应:动作携带 ExportBufferArgs 时取其 Path();否则(例如命令面板里的裸 exportBuffer)传空字符串,进入文件选择器分支。
4.3 导出实现:文件选择器、环境变量展开、原子写文件
核心实现在 TabManagement.cpp 的 TerminalPage::_ExportTab,可拆为三段:
第一段:确定保存位置。 若 path 为空,通过 SaveFilePicker 构造 shell32 文件保存对话框(源码注释指明 GH#11356:UWP 的文件写入 API 在提权场景下不可用,故直接改用 shell32 选择器)。对话框细节值得留意:
- 默认保存到下载目录(
FOLDERID_Downloads); - 过滤器为
Text Files (*.txt)与All Files (*.*),默认扩展名txt; - 默认文件名取自 tab 标题,经
til::clean_filename清洗后追加.txt; - 弹出对话框前会
co_await wil::resume_foreground(...)让出一次(GH#20188),防止命令面板的回车键泄漏进终端; SetClientGuid(clientGuidExportFile)让该对话框的所有实例记住上次使用的目录。
第二段:展开路径中的环境变量。 若路径来自设置而非选择器,用户可能在其中写了环境变量,代码执行:
path = winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(path.c_str()) };
即当前已实现的"格式化"就是 Windows 原生的 %VAR% 语法;spec §5 设想的 ${year}/${profile} 式格式化串仍属 TODO。这是今天使用该功能时最重要的"现状"事实:路径里可以写 %USERPROFILE%\.wt\logs\mylog.txt,但还不能写 ${date}。
第三段:读缓冲区并写文件。
const auto buffer = control.ReadEntireBuffer();
til::io::write_utf8_string_to_file_atomic(std::filesystem::path{ std::wstring_view{ path } }, til::u16u8(buffer));
这正对应 spec 中"导出是较容易的部分:PR #11062 已经把 TerminalApp 与 TermControl 的缓冲区内容打通,按需写文件很容易"。数据流为:Tab → TerminalControl::ReadEntireBuffer()(读取控件缓冲区全部内容)→ til::u16u8 宽字符转 UTF-8 → til::io::write_utf8_string_to_file_atomic 原子写入(保证文件要么完整写出、要么不写,避免写一半的坏文件)。
4.4 multipleActions 组合法
Spec 提到用 multipleActions(PR #11045)组合多个动作,实现"导出既有缓冲区 + 开始持续记录"(见 §6.3)。该动作在当前仓库已落地,可在 AppActionHandlers.cpp 的 _HandleMultipleActions 中验证顺序执行实现:遍历 MultipleActionsArgs.Actions(),逐个调用 _actionDispatch->DoAction(action)。因此"先 exportBuffer 到某路径、再对同一路径以追加方式开启记录"的组合在动作框架层面已可行(后半段依赖尚未实现的 logging 能力)。
4.5 实现现状小结
把 spec 各项与当前源码对照:
| Spec 条目 | 现状(以当前仓库为准) | 依据 |
|---|---|---|
| Story A:上下文菜单"导出文本" | ✅ 已实现 | PR #11062,见 Tab.cpp |
exportBuffer()(无参 / 带 path) |
✅ 已实现 | §4.2、§4.3 |
exportBuffer 的 append 参数 |
❌ 未实现 | ExportBufferArgs 仅有 Path |
路径中 ${year} 等格式化串 |
❌ 未实现 | 仅 wil::ExpandEnvironmentStringsW 的 %VAR% 展开 |
路径中 %VAR% 环境变量 |
✅ 已实现 | TabManagement.cpp |
logSettings / logAutomatically / toggleLogging() |
❌ 草案,未实现 | 仓库中无对应 IDL 或设置字段 |
5. 路径格式化字符串:语法选型讨论
Spec 的 "Path formatting" 一节标注 TODO,列出了三款产品的语法对照:
- PuTTY:
&Y、&M、&D、&T、&H、&P分别表示年、月、日、时间、主机、端口; - SecureCRT:
%H(主机名)、%S(会话名)、%Y(四位年)、%M(两位月)、%D(两位日)、%h(两位时)、%m(两位分)、%s(两位秒)、%t(三位毫秒)、%%(字面百分号)、%envvar%(环境变量,如%USERNAME%)。
Windows Terminal 自身有 ${braces} 语法先例:命令面板的"iterable command"已使用 ${profile.name} 形式;issue #9287 又已实现 ${env:VARIABLE} 环境变量语法。因此作者最初的倾向是采用如下 ${braces} 变量集:
| 变量 | 含义 |
|---|---|
${profile} |
profile 名称 |
${year} |
四位年 |
${month} |
两位月 |
${day} |
两位日 |
${hour} |
两位时 |
${minute} |
两位分 |
${second} |
两位秒 |
${ms} |
三位毫秒 |
${env:variable} |
环境变量(如 ${env:USERPROFILE},受 #9287 启发) |
Spec 留下的开放问题:要暴露哪些变量、用户以何种方式格式化?实施计划中还列了候选变量 WT_SESSION(每个会话的 uuid)、profile 名、命令行等,并注明需要先决定格式方案(yyyy-mm-dd?%Y-%m-%D?&Y-&m-&D?${year}-${month}-${day}?)。这部分在草案中保持"For discussion"状态,当前实现仅支持 %VAR% 也印证该讨论未定案。
6. 导出 vs 日志:架构与机制的差异
6.1 架构分层:为什么"日志"更难做
Spec 在 "Exporting vs Logging" 中划出关键界线:导出容易,日志难。
根本原因在于进程架构。经历 "Process Model 2.0"(issue #5000)改造后,Windows Terminal 拆分为 UI 进程(TerminalApp/TermControl)与内容进程(ControlCore/ControlInteractivity)。若在 TermControl 一侧做日志、把每一段输出都通知 TerminalApp,将引入大量跨进程跳转开销。Spec 的提议:让内容进程里的 ControlCore/ControlInteractivity 自行完成日志写入(这也顺带解决了 §7 提到的性能顾虑——自动记录只发生在内容进程,不必担心文件写入落在 UI 线程上)。
6.2 何时记录:反回车的两难
Spec 提出了一个看似细节、实则触及日志设计核心的问题:用户在一个 pwsh/bash 提示符上输入 what,再按两次 Backspace 变成 wh,日志里应该是什么?what^h ^h^h ^h?还是 wh?
作者的顾虑是 conpty 有时过度使用 ^H(Backspace)作为光标定位序列,日志会"噪声"很大。备选方案:只在光标换行或离开当前行时按行记录。原文脚注解释了 ^H 语义:^H 是非破坏性的,序列 what^h ^h^h ^h 依次表示——打印 "what" → 光标左移一格 → 打印空格(覆盖 't')→ 光标左移一格 → 光标再左移一格 → 打印空格(覆盖 'a')→ 光标左移一格。作者表示需要实测 PuTTY 在 "Printable output" 选项下的输出行为。
这个问题直接关系到 captureAllOutput 参数的意义:"可打印输出"与"全部转义序列"是两种截然不同的日志风格,而写入触发点(即时写、还是换行写)决定了日志的可读性。
6.3 开始记录时的行为
若终端启动时未开启记录,用户随后用 toggleLogging 手动开始,该记录什么?所有未来输出?还是也包含当前缓冲区内容?
作者倾向只记录"所有未来输出",忽略既有缓冲区内容。若用户确实想"导出当前缓冲区 + 开始记录",可用 multipleActions 动作先 exportBuffer 到某文件,再对该文件以 "append":true 执行 toggleLogging。这正是 §4.4 所述 multipleActions 组合的典型用途。
7. 潜在问题:性能与"命令时间戳"
Spec 的 "Potential Issues" 一节有两段值得完整继承的讨论。
性能、功耗与效率:记录日志预计会带来可测量的性能开销,缓解手段是在后台线程上写文件,与连接线程、渲染线程分离;由于自动记录只发生在内容进程,无需担心文件写入落在 UI 线程。
要不要记录"命令执行时间戳"? 这是另一个高频诉求,作者结论是 Terminal 自身不应实现,理由链:
- Windows Terminal 本质是"终端模拟器",不知道所连客户端应用(
cmd、powershell、bash、vim)内部在发生什么——无法区分用户是在 shell 里敲命令,还是正在emacs里打字; - 保存命令历史通常是客户端应用自己的职责:
bash与powershell都能把历史存到文件、跨会话恢复,而cmd.exe不能; - Windows 控制台世界格外复杂:
cmd.exe实际上完全不管理自己的命令历史,conhost在替客户端应用做这件事。很久以前有人决定把readline功能直接做进控制台宿主,这让python.exe这类 REPL 更容易实现(无需自维护历史缓冲),但也使这类行为与控制台本身难以解耦; - 更棘手的是,Terminal 可能根本不在 conhost 会话里(例如未来考虑过的"直接连 WSL"场景)——进程树里没有
conhost,"向控制台要命令历史"就会"莫名其妙"失效; - 最合理的路线其实最简单:让 shell 在提示符中自己打印时间戳。Terminal 不知道命令何时输入,但 shell 知道。配置 shell 输出时间戳后,Terminal 会把它连同其他一切输出如实地记下来。
8. 实施计划与未来考虑
8.1 实施计划清单
Spec 给出粗略实施大纲(作者注:"每个顶层条目都可以是独立的 PR,紧随 #11062")。原文照录并对照现状标注:
缓冲区导出(Buffer exporting):
- [x] 添加
exportBuffer()动作,打开文件选择器(已实现) - [x] 给
exportBuffer()添加字符串path参数,允许按键即导到完整路径;默认""表示"打开文件选择器"(已实现) - [ ] 给
exportBuffer添加布尔append(默认false)参数:为 true 时以追加而非覆盖方式导出 - [ ] 在
path参数中启用字符串格式化。开放问题:要哪种格式?要哪些变量?(年/月/日/时/分容易;WT_SESSION或许作为每会话 uuid?profile 名?命令行?)
自动记录(Automatic logging):
- [ ]
toggleLogging()动作用于开始/停止记录,带path、append属性(同exportBuffer())。ToggleLoggingArgs包含单个成员LoggingSettings,后者含path与append属性(分层原因见下) - [ ] 给
LoggingSettings添加"记录全部输出"属性(默认为"仅记录可打印输出") - [ ] 给
LoggingSettings添加"记录输入"属性(输入大概率以普通 VT 编码记录,而非win32-input编码) - [ ] 每 profile 的
logSettings设置,可包含完整LoggingSettings(同ToggleLoggingArgs)。toggleLogging无参时回退使用该 profile 的loggingSettings - [ ] 每 profile 的
automaticallyLog设置,使该 profile 打开时默认记录 - [ ] 给
LoggingSettings添加"每天一个新文件"属性,仅当路径串中含{day}时生效;开启该属性的自动记录时,午夜打开新文件并写入新文件 - (被注释)给
LoggingSettings添加"频繁冲刷日志"属性,默认true(?):把全部输出冲刷到文件,而非仅在关闭或换行时冲刷。PuTTY 关闭该选项时的冲刷时机尚不明确,需要更多研究
8.2 未来考虑(Future Considerations)
Spec 末尾列出几个值得关注的方向:
- Toast 通知:记录开始时弹 "Logging to {filename}",停止时弹 "Stopped logging to {filename}";
- 分格内的状态指示:目前没有合适的 UI 元素能表明某分格正在向文件记录。PuTTY 不显示任何指示器;SecureCRT 只在应用自身的上下文菜单里放一个复选框(见原文 [SecureCRT-context-menu.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/drafts/?utm_source=gitcode_repo_files#642 - Buffer Exporting and Logging/SecureCRT-context-menu.png))。作者建议:正在记录时,把上下文菜单里的 "Export Text" 条目替换为 "Stop Logging";
- Alt 缓冲区日志开关:可增加一项设置,禁用来自备用屏幕缓冲区(alt buffer)的记录。这对使用
vim等全屏应用的用户有价值——这类应用频繁重绘整个视口,日志会不必要地嘈杂。禁用 alt buffer 记录后,日志至少能体现"用户打开了 vim,退出后又做了一些事"; - 调试价值:"记录全部输出"对将来复现"用户能复现、我们却复现不了"的 bug 会非常有帮助。
9. 小结:今天能用什么,期待什么
回看这份 spec,它勾勒出 Windows Terminal "会话历史保全"的完整版图,价值分三层:
- 今天可用:
exportBuffer动作覆盖了手动导出路线——actions里给exportBuffer绑一个不带参数的键,就得到文件选择器(默认下载目录、tab 标题作默认文件名);绑带path的exportBuffer,则一键导出到固定路径,且路径支持%VAR%环境变量。底层链路(_HandleExportBuffer→_ExportTab→ReadEntireBuffer→write_utf8_string_to_file_atomic)保证了 UTF-8 输出与原子写入。 - 设计中待落地:
append参数、${braces}格式化串、以及整套自动记录体系(logSettings/logAutomatically/toggleLogging)仍在清单上。阅读本 spec 时需留意它是 draft 阶段的文档(位于 [drafts 目录](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/drafts/?utm_source=gitcode_repo_files#642 - Buffer Exporting and Logging/#642 - Buffer Exporting and Logging.md),成文于 2021 年),参数实况请以当前源码为准。 - 设计权衡值得借鉴:"日志职责放在内容进程"避免跨进程通知开销;"开始记录时只记未来输出"保持语义简单、复杂需求交给
multipleActions组合;"不记命令时间戳、让 shell 自己打印"守住终端模拟器的边界——这些取舍对同类功能设计都是良好参照。
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