两套语言引擎(TS-LLM 与 Python-spaCy)
上级:key-designs
词元(lemma)、词性、动词变位表、可分动词还原、语法结构——这些概念上相同的输出,是由两套并行引擎分别算出来的,而不是一条生产者/消费者式的流水线。客户端的实际路径以 LLM 为先,查词时并不会调用 Python 服务。
TS 核心(@lucerna/core)——客户端里始终在场的大脑,覆盖任意语言。
负责分词/省音(elision)、全形式匹配、动词形式的采集、原始文本与显示形式之间的几何映射,以及词性分类体系/配色/11 种语言的标签。所有内容生成都委托给一个用户自行配置的 LLM(api/llmLookup.ts):基础查词、动词变位表、完整的变形集合、句子分析(那串语法 structure 字符串)、词性配色。这里真正棘手的地方在于不能信任 LLM 的输出:cleanLLMJson 从第一个 {/[ 切到最后一个 }/],取出 JSON 子串(把代码围栏、前导文字都砍掉),再跑一遍 jsonrepair(处理多余逗号、单引号、截断),并配合宽容的数组提取(sentences|result|analysis|data|items,或任意顶层数组)以及 HTTP 状态码到本地化错误信息的映射。
Python 的 nlp-api——确定性的离线引擎,只覆盖德语和英语。
spaCy(de_core_news_lg / en_core_web_lg)负责分词加词形还原;一个依存句法分析器(infrastructure/grammar/dep_parse_analyzer.py)产出真正丰富的语法信息:从句类型(Hauptsatz 还是 Nebensatz,靠从属连词 / cp/rc 依存关系判断)、时态推断(从 Partizip II 加助动词的形态推出 Perfekt/Plusquamperfekt/Futur/Präteritum)、语序(Inversion / 动词后置)、各类特征(被动态 Passiv、虚拟式二式 Konjunktiv II、情态、否定、反身),再往下是主语/第四格/第三格成分的拆解——最终呈现为一串带中文标注的文字。再配上一个 Wiktionary/Postgres 词典(优先取有中文释义的行)和一个按语言对分发的 CompositeTranslator(NLLB / Opus-MT)。
为什么两者都要保留(设计逻辑)。 LLM 引擎能处理任意语言、给出丰富的目标语言释义,但需要用户自备 API key,还得做 JSON 修复;spaCy 引擎是确定性的、离线的、不需要 key,但被锁死在德语加英语这两种语言里。德语可分动词是这个分裂最清楚的例证:TS 这边是一个前缀剥离的启发式做法(separableVerbService.ts),Python 那边则是真正的依存句法 svp 还原(spacy_tokenizer.py——找到依存关系为 svp、其head是该动词的那个 token,重新拼出 前缀+词元,并把德语名词词元首字母大写)。同一个问题,两种保真度——客户端永远能给出答案,服务端只在它覆盖的这两种语言上给出正确答案。