compromise
纯JS轻量NLP库,无需API和GPU,浏览器内直接处理英文文本
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
纯JS轻量NLP库,无需API和GPU,浏览器内直接处理英文文本
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。

「你会不会觉得,教会一台机器'听懂人话'这件事,总是要么重如泰山(部署重型模型),要么轻如鸿毛却什么也干不了?Compromise 给出了一个中间解:轻到可以跑在浏览器里,但功能足够处理真实业务。」
Compromise 的作者 Spencer Kelly(GitHub @spencermountain)是一位加拿大独立开发者,长期活跃于 npm 生态。这个项目的诞生,源于他对当时 NLP 工具生态的不满:当时的自然语言处理,要么是依赖重型服务端 API(调用 OpenAI、百度等),要么是过于学术化的科研库(需要 PhD 才能看懂文档)。他想要一个"普通人也能用"的方案——不需要服务器,不需要 API Key,不需要懂机器学习,只要会 JavaScript 就能跑起来。
从 2016 年发出第一个 commit 至今,Compromise 经历了 14 个大版本迭代(当前 v14.15.1),积累了超过 12,100 GitHub Stars,npm 周下载量超过 180 万次,成为 JavaScript 生态中下载量最高的纯客户端 NLP 库。值得注意的是,它并不是靠"大模型"做到的,而是靠词典驱动的方法——一个 ~14,000 词的词表,配合 83 个词性标签,构成了整个系统的核心。
想象你手里有一把瑞士军刀,Compromise 就是专门处理文字的版本。你丢给它一段英文,它能告诉你:
它的体积只有 ~250KB(压缩后),比一张高清图片还小,速度却能达到每秒处理约 1MB 文本(相当于 10 个 Wikipedia 页面),大多数句子分析只需 0.1 毫秒——快到可以绑在键盘输入事件上实时运行。
Compromise 采用了独特的三层模块化设计,每一层解决一个层次的语言问题:
/one — 分词层(Tokenization)负责把一段文字拆成最小语言单元。比如输入 "Wayne's World, party time",它会输出每个词的原形和归一化形式。这个模块极快(每秒 1MB 文本),适合需要快速扫描大量文本的场景。
/two — 词性标注层(Part-of-Speech Tagging)在分词基础上,给每个词打上语法标签。它有 83 个词性标签,组成一个有向图结构:#FirstName → #Person → #ProperNoun → #Noun。这意味着当你写 .match('#Possessive #Noun') 时,它能准确匹配"Wayne's World"(所有格 + 名词)。
/three — 短语工具层(Phrase & Sentence Tooling)在知道词性的基础上,提供更高层次的操作:提取人名、 normalize 大小写、处理数字和金钱、识别日期、修改动词时态等。
Compromise 还有丰富的插件体系:
此外还有多语言扩展:法语(fr-compromise)、德语(de-compromise)、意大利语(it-compromise)、西班牙语(es-compromise)等,覆盖欧洲主要语言。
这是理解 Compromise 最关键的一点:它不依赖任何机器学习模型。
传统 NLP(如 spaCy、BERT)通过大量标注语料训练神经网络,模型体积从几百 MB 到几十 GB 不等。Compromise 的思路完全不同——它维护一个约 14,000 词的词典,每个词条记录其所有变形的词性和语义标签。当你输入一个词,它查词典、找规则、做变形,而不是"猜"。
这带来了几个有意思的特性:
但代价也很明显:泛化能力有限。对于词典里没有的生僻词、网络用语、新兴表达,效果会大打折扣。它更适合结构相对规范的英语文本,而非高度创造性的写作。
<script src="https://unpkg.com/compromise/builds/compromise.js"></script>
<script>
var doc = nlp('The dog ate the apple')
console.log(doc.nouns().toPlural().text())
// 输出: "The dogs ate the apples"
</script>
npm install compromise
import nlp from 'compromise'
var doc = nlp('London is calling')
doc.verbs().toNegative()
console.log(doc.text())
// 输出: "London is not calling"
Compromise 同时支持 CommonJS 和 ES Module,并提供完整的 TypeScript 类型定义(types/three.d.ts),IDE 智能提示开箱即用。
从代码结构来看,Compromise 采用了清晰的模块化设计:src/one(分词)、src/two(词性标注)、src/three(短语工具)、plugins/(插件)四大块。核心逻辑围绕词表(data/)和标注规则(src/2-two/)展开。
测试覆盖有 codecov 集成,CI/CD 流程完善。但需要指出的是,核心代码从 2016 年开始编写,部分早期代码风格较旧(var 声明、回调模式),虽然经过多次重构,仍有一定历史包袱。对于习惯现代 TypeScript 的开发者,初期阅读源码可能会有一定门槛。
局限一:只能处理英语(及少量欧洲语言)
词典驱动的方法天然受制于词典规模。英语词表相对完整,但中文、日文等字符语言无法直接支持。项目虽有多语言扩展,但覆盖广度和质量远不及英语。
局限二:无法处理复杂语义
词性标注、实体识别这些任务,Compromise 能做到 80-90% 的准确率,但对于需要深层理解的场景(如情感分析的细微差别、讽刺识别、复杂推理),词典方法天然存在瓶颈。
局限三:文档分散
由于 API 非常丰富(83 个标签、数百个方法),官方的可观测性笔记本(Observable)提供了大量示例,但静态文档相对薄弱,很多用法需要通过示例代码反推。
随着 GPT-4、Claude 等大模型的崛起,有人认为"小 NLP 库没有存在价值了"。但 Compromise 用数据反驳了这个观点:2023-2024 年,它的 npm 下载量不降反升。
原因很朴素:不是所有场景都需要大模型。一个在用户浏览器内运行的表单验证、聊天机器人的前端预处理、快速原型验证——这些场景引入一个 API 调用(延迟、费用、隐私风险)反而是过度设计。Compromise 在这些"轻量 NLP"场景中,仍然是最佳选择。
总结:Compromise 是一个"工程味"很重的项目——没有炫目的 AI 技术词汇,没有万亿参数的大模型,但它用最朴素的词典驱动方法,解决了成千上万开发者"想让应用理解点文字"时的真实痛点。在 AI 工具越来越重的今天,它的"轻"本身就是一种价值。
图1:Compromise 词性标注示意 — 输入句子经过 one/two/three 三层处理,逐层添加语言结构信息。