☰
在 Dart 中使用 xberg 进行 TextRank 抽取式摘要:基于 Rust 内核的确定性文本概括实战
2026/9/28 3:00:18 网站建设 项目流程
  • 后端
  • AI 应用
  • NLP

【免费下载链接】xberg

Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with CLI, REST API, and MCP server.

项目地址:https://gitcode.com/gh_mirrors/kr/xberg
点击查看免费下载

本文以 xberg 仓库中 Dart 端到端测试夹具 summarization_extractive_smoke.md 为核心骨架,讲解如何通过 Dart 绑定调用 Rust 内核的抽取式(extractive)摘要能力:给定一段多段落的纯文本文档,仅需配置strategy: "extractive"与max_tokens,即可在本地获得确定性的 TextRank 摘要结果,全程纯 Rust 实现、无需任何外部服务。读完本文你将掌握 xberg 摘要功能的完整调用链、SummarizationConfig配置语义、DocumentSummary输出结构,以及 TextRank 在图上的打分原理。

一、摘要能力在 xberg 中的定位

xberg 是一个以 Rust 内核为核心的跨语言文档智能引擎,支持从 106 种格式、140 种文件扩展名中抽取文本、元数据、图片、表格与结构化数据,并提供 CLI、REST API 与 MCP Server 等使用方式。文档摘要(summarization)是抽取流水线中的一项后处理(post-processor)能力:当ExtractionConfig.summarization被设置为Some(...)时,摘要后处理器会在ProcessingStage::Middle阶段运行,并把结果写入ExtractedDocument::summary字段。

从源码结构看,摘要能力分为两层:

  • 后端实现位于 crates/xberg/src/text/summarization/:textrank.rs提供纯 Rust 的 TextRank 抽取式摘要,llm.rs提供基于 LLM 的生成式摘要(受summarization-llmcargo feature 控制)。
  • 后处理器接线位于 crates/xberg/src/plugins/processor/builtin/summarization.rs:负责根据配置分发到不同策略、填充结果并记录 token 统计。

本文关联文档演示的正是第一条路径——TextRank 抽取式摘要。它不需要任何外部服务或模型下载,在 WASM、Android 等受限目标上同样可以编译运行,且结果完全确定。

二、端到端调用示例(Dart 绑定)

关联文档给出的 Dart 示例是理解整套调用方式的钥匙。该示例读取一个多段落纯文本文档(《战争与和平》节选文本),配置max_tokens: 80与strategy: "extractive",随后打印摘要结果:

import 'dart:io'; import 'package:xberg/xberg.dart'; import 'package:xberg/src/xberg_bridge_generated/frb_generated.dart' show RustLib; Future<void> main() async { await RustLib.init(); try { final input = await createExtractInputFromJson(json: '{"kind":"uri","uri":"https://example.com/text/book_war_and_peace_1p.txt"}'); final config = await createExtractionConfigFromJson(json: '{"summarization":{"max_tokens":80,"strategy":"extractive"}}'); final result = await XbergBridge.extract(input, config: config); stdout.writeln(result.results[0].summary); } finally { RustLib.dispose(); } }

逐步拆解这段代码:

  1. 初始化 Rust 运行时:await RustLib.init()加载由 flutter_rust_bridge 生成的动态库,后续所有调用都依赖它;结束时通过RustLib.dispose()释放资源(见finally块)。
  2. 构造提取输入:createExtractInputFromJson以 JSON 形式构造ExtractInput,示例中kind: "uri"表示从 URL 拉取文档。除 URI 外,还可使用kind: "bytes"直接传入文件字节。
  3. 构造提取配置:createExtractionConfigFromJson接受与 REST API 相同的 JSON 配置结构。这里的summarization对象即SummarizationConfig的序列化形式。
  4. 执行提取:XbergBridge.extract(input, config: config)返回ExtractResult,results[0].summary即DocumentSummary对象。
  5. 输出摘要:stdout.writeln打印DocumentSummary,其toString默认输出摘要文本。

配置参数说明

summarization配置对象对应 Rust 端 crates/xberg/src/core/config/summarization.rs 中的SummarizationConfig,包含三个字段:

字段类型默认值说明
strategy"extractive"|"abstractive""extractive"摘要策略。extractive为纯 Rust TextRank,确定性、无外部依赖;abstractive为 LLM 生成式摘要,需要summarization-llmfeature 且必须配置llm字段
max_tokensu32(可选)后端默认值(TextRank 为 150 词)摘要最大长度(按空白分隔的 token 计数)。不传时由后端选择默认值
llmLlmConfig(可选)None生成式摘要专用,extractive策略下被忽略;abstractive策略下必填

注意SummarizationConfig使用了#[serde(deny_unknown_fields)],意味着传入未定义的字段会直接报错,因此配置 JSON 中不要拼写多余键。

输出结构:DocumentSummary

摘要结果对应 crates/xberg/src/types/summary.rs 中的DocumentSummary:

字段类型说明
textString摘要正文(纯文本散文)
strategySummaryStrategy产生该摘要的策略,序列化为extractive或abstractive(snake_case)
token_countu32(可选)摘要的近似 token 数,按空白分词统计

对应地,SummaryStrategy枚举有两个变体:Extractive与Abstractive,其中Extractive是默认值。这也解释了夹具断言results[0].summary.strategy == "extractive"为何成立。

三、后处理器的触发条件与行为

摘要并不是每次提取都会运行,它由一个后处理器插件SummarizationProcessor驱动(crates/xberg/src/plugins/processor/builtin/summarization.rs),其行为要点如下:

  • 触发条件:should_process返回config.summarization.is_some(),即只有在配置了summarization时才执行;未配置时process直接返回,summary字段保持为None。
  • 空文档保护:若result.content.trim().is_empty(),后处理器直接跳过,不产生摘要。
  • 策略分发:根据summarization_config.strategy分发——Extractive走纯 Rust TextRank,Abstractive走 LLM。
  • 失败信号:抽取式摘要无法产出结果时(如文本过短无法成图),不会静默失败,而是向result.processing_warnings推入一条 source 为summarization_extractive的警告;生成式摘要失败则推入 source 为summarization_abstractive的警告,调用方可以据此区分"没摘要"和"摘要失败"。

该处理器的processing_stage()固定为ProcessingStage::Middle,estimated_duration_ms依据词数线性估算(word_count / 200 + 50),可用于调度与预算。相关的单元测试覆盖了"配置后填充摘要""未配置不产出""空内容跳过""无输出时告警"等场景,参见同文件的mod tests。

四、TextRank 抽取式摘要的底层原理

后端核心位于 crates/xberg/src/text/summarization/textrank.rs,算法是经典的"句子级 TextRank",全过程无外部依赖、完全确定。整体流程如下:

  1. 分句:以.、!、?、\n作为句子边界切分文本(split_sentences);若文本为空返回None,若只有一句话则原句直通返回。
  2. 停用词与分词:根据result.detected_languages的第一个语言代码选择停用词表(crates/xberg/src/stopwords/ 内置多语言词表);语言未知或未检测到时回退到英语。分词规则为按非字母数字字符切分、转小写、剔除停用词与长度小于 2 的 token(tokenize)。
  3. 建图:每个句子是一个顶点,两两计算 TF-IDF 余弦相似度作为边权;相似度低于MIN_EDGE_SIMILARITY(1e-6)的边不连(pagerank_scores)。TF-IDF 中 IDF 使用平滑公式ln((n+1)/(df+1)) + 1。
  4. PageRank 迭代:对列随机矩阵执行阻尼幂迭代(power_iteration)。关键超参数在源码中以常量定义:阻尼系数PAGERANK_DAMPING = 0.85、收敛容差PAGERANK_TOLERANCE = 1e-4、最大迭代 64 次、无出链的悬挂节点分数按均匀分布回灌。
  5. 句子选择:按 PageRank 分数从高到低挑选句子,在不超过max_tokens(默认DEFAULT_MAX_TOKENS = 150)预算的前提下尽量多选,最后按原始文档顺序重新排序拼接(select_top_sentences)。因此摘要中的句子顺序忠实于原文,可读性好。

性能上还有两个保护性上限:单次最多考虑MAX_SENTENCES = 256个句子,超过部分截断,保证算法在大文档上的时间可控。

确定性与语言回退的实证

textrank.rs 的测试给出了几个值得注意的性质:

  • 确定性:同一输入两次调用summarize输出完全一致(summarize_is_deterministic),这正是文档中"deterministic, no external services required"的依据;
  • 主题聚焦:在包含无关句子的混合段落上,摘要会命中与机器/深度学习相关的句子(summarize_picks_relevant_sentences);
  • 语言回退:传入未知语言代码"xx"时自动回退到英语停用词表(summarize_unknown_language_falls_back_to_english);
  • 预算约束:max_tokens按空白分词计数,超预算句子会被排除(summarize_respects_budget、token_count_uses_whitespace_split)。

这些测试连同后处理器测试共同构成了 summarization_extractive_smoke.json 所声明的断言基础。

五、测试夹具如何验证该能力

关联文档本质上是 alef 生成的 Dart 端到端测试夹具,其数据来源是 fixtures/summarization/extractive_smoke.json,测试代码生成于 e2e/dart/test/summarization_test.dart。理解夹具结构有助于在本地复现与扩展:

  • 输入:kind: "uri",URI 指向一个模拟服务(mock server)托管的book_war_and_peace_1p.txt纯文本文档(响应头content-type: application/octet-stream,正文来自../test_documents/text/目录下的战争与和平节选)。
  • 配置:summarization: { strategy: "extractive", max_tokens: 80 }。
  • 断言:调用extract后校验四点——不报错(not_error)、results[0].mime_type为text/plain、results[0].summary.text非空、results[0].summary.strategy等于extractive。

运行时,测试通过环境变量(MOCK_SERVER_URL/SUT_URL,默认http://localhost:8008)或本地启动 mock server 获取夹具 URL,随后以$mock_url占位符替换真实地址。若要在本地运行该测试,可参考 e2e/dart/ 目录下的pubspec.yaml与dart_test.yaml配置 Dart 环境,并先运行 mock server 脚本(e2e/run-with-mock-server.sh)。

六、REST / CLI 场景下的同一配置

SummarizationConfig与DocumentSummary都标注了utoipa::ToSchema,说明该结构直接映射到 REST API 的 OpenAPI 契约;MCP server(crates/xberg/src/mcp/server.rs)同样支持摘要后处理。因此上面 Dart 示例中的 JSON 配置可以直接平移到 REST 请求体或 CLI 参数:

{ "summarization": { "strategy": "extractive", "max_tokens": 80 } }

无论走哪条调用路径,strategy: "extractive"都代表同一份确定性、无外部依赖的本地摘要实现——这是 TextRank 后端相对生成式方案最核心的工程优势:可复现、低延迟、无网络依赖,适合批量流水线与离线环境。

七、小结与实践建议

本文围绕 xberg 的 Dart 抽取式摘要示例,串联了从ExtractionConfig配置、XbergBridge.extract调用、SummarizationProcessor后处理到 TextRank 图算法的完整链路。实践中的关键要点:

  • 抽取式摘要只需strategy: "extractive"(默认值,可省略)与可选max_tokens,纯本地运行、结果确定,适合离线与批量场景;
  • 生成式摘要需启用summarization-llmcargo feature 并配置llm字段,且max_tokens之外还涉及 token 用量统计(写入llm_usage);
  • 文本过短(单句、空内容)时抽取式摘要可能直接透传或不产出,注意读取processing_warnings以区分"无摘要"与"失败";
  • 需要进一步阅读的实现入口:配置结构、后处理器、TextRank 算法、输出类型、夹具定义与生成的 Dart 测试。
  • 后端
  • AI 应用
  • NLP

【免费下载链接】xberg

Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with CLI, REST API, and MCP server.

项目地址:https://gitcode.com/gh_mirrors/kr/xberg
点击查看免费下载

相关推荐

上一篇:G6 圆形容器 combo(CircleCombo)入门实践:用 circle-combo 搭建多层兴趣小组组合图
下一篇:USBGuard规则配置入门:3步生成安全策略避免系统锁定

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询