ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

本地LLM驱动书目超级作品聚类:从规则归一化到语义审核的混合管线

本地LLM驱动书目超级作品聚类:从规则归一化到语义审核的混合管线 做本地 LLM 应用真正难的不是跑一个模型而是找到一个“值得用 LLM 来做”的任务。书目超级作品聚类就是一个很典型的例子同一部作品在不同馆藏、不同出版社、不同语言版本里可能被记录成完全不同的条目靠精确匹配很难把它们合并到一起而人工合并又非常耗时。标题里提到的 Building Bibliographic Superwork Clusters for Discovery with Local LLMs本质上是把“本地大语言模型”用于书目数据的语义理解、聚类判断和发现索引生成。本文不讨论概念堆砌而是直接拆开两件事一是“超级作品聚类”到底要解决什么问题二是怎么用本地 LLM 把它落地成一条可运行的工程链路。先说结论这个场景并不是让 LLM 把几百上千万条书目一次性读完那既不现实也不经济。更稳妥的路线是混合式管线——先用规则和文本嵌入做低成本预聚类缩小范围再把边界模糊的候选组交给本地 LLM 做语义审核。这样既保留了 LLM 对自然语言的理解能力又避免了全量推理带来的资源浪费。文章后面会给出从环境准备、数据预处理、嵌入聚类到 LLM 审核、批量处理、结果验证的完整代码示例。如果你手头有馆藏数据、开放书目数据、机构知识库或者只是想把内部知识资产按“同一作品的不同版本/相关改编”整理成更清晰的结构这篇文章可以直接收藏。整条链路用 Python 就能搭完本地 LLM 运行时不要求固定型号重点是把接口调用和工作流设计讲清楚。1. 本地 LLM 构建书目超级作品聚类的核心能力速览能力项说明项目目标将分散、异构的书目记录聚合为“超级作品”簇用于文献发现和知识导航技术路线规则归一化 文本嵌入预聚类 本地 LLM 边界审核 LLM 规范化命名LLM 主要角色判断多条书目是否属于同一超级作品、给作品簇生成权威题名和范围描述推荐硬件中低配置即可开始具体取决于所选 LLM 模型大小和量化精度显存需求不确定需以实际部署的模型和推理参数为准是否支持 CPU取决于本地运行时与量化模型CPU 可跑小模型但速度会低于 GPU启动方式本地 LLM 运行时 Python 脚本命令行启动是否支持 API支持通常通过本机 HTTP 服务调用是否支持批量任务支持文章提供批量提示词与重试机制示例输出格式CSV / JSONL / SQLite 等便于接入发现系统适合场景数字图书馆、机构知识库、图书元数据清洗、出版社内容组织需要强调的数据入口有三类MARC 记录、Excel/CSV 导出、来自 API 的 JSON 数据。凡是包含“题名 责任者 版本说明 出版年”的记录都可以进入这条管线。2. 超级作品聚类要解决的核心问题在图书馆信息学里FRBR 把书目实体拆成四个层级作品Work、内容表达Expression、载体表现Manifestation和单件Item。常规编目已经解决了“表现层”的记录但发现系统需要的往往是更高一层的聚合一个叫“超级作品”的逻辑单元把某部作品的各种版本、翻译、不同出版社的载体现甚至相关改编都归到同一个可发现的节点下。举一个常见例子。《白鲸》(Moby-Dick; or, The Whale) 在数据库里可能出现这些形式Herman Melville 写的 1851 年首版记录某个出版社的 Norton Critical Edition某个中文译本题名为《白鲸》手机上的一部有声书版本。如果检索系统只做关键词匹配这几个记录一般是散开的。用户搜“白鲸”时结果页可能挤满了同一个作品的不同版本却缺少一个能把它们聚合起来说明“这些都属于同一部作品”的入口。超级作品聚类要做的就是把这些记录合并成一个簇并给这个簇一个稳定、可读的标识。传统方法不是不能做但很困难。题名归一化能处理标点、大小写和“第 2 版 / revised edition”之类的后缀却很难处理《The Three-Body Problem》和《三体》之间的跨语言关系也很难判断某个续作、改编或同名书是否真的属于同一簇。规则要写的话会无穷无尽。本地 LLM 的价值在于它把“判断”变成了一个问题回答任务给定若干条书目让模型判断它们是否属于同一个超级作品并输出理由。模型能看到题名中“同名但不同作品”的细微差别也能捕捉翻译、副题名和版本说明里的语义线索。而且使用本地部署书目数据不需要发送到外部服务适合馆藏数据、未公开资源和机构内部资料。3. 适用场景、使用边界与合规提醒这个方案最适合以下场景机构知识库或图书馆盘点中需要把不同来源的重复书目合并。出版社或内容平台整理“作品族”给编辑和推荐系统提供聚合单元。数字人文研究者做文献计量或版本分析第一阶段先把语料按作品簇切分。内部知识系统做知识图谱时通过超级作品簇串联作者、翻译、改编和评论文献。不适合的场景也要说清楚。如果你的数据量只有几百条人工清点比搭管线更划算如果数据字段极不规范连题名和作者都混在一个字段里第一优先级不是 LLM而是数据治理如果要求 100% 的聚类准确率那必须引入人工复审LLM 只能做召回候选不能直接作为最终发布依据。合规边界需要特别注意。使用本地 LLM 并不代表可以随便处理版权材料。聚类结果如果对外发布需要确认原始书目数据是否有合法的再分发授权。涉及受版权保护的作品内容时本地 LLM 只处理元数据不要让模型去生成受版权保护的正文摘要。内部数据做聚类时建议先在隔离环境测试不要将包含个人信息的读者借阅记录与书目记录关联后随意输出。部署本地 LLM 服务后默认只监听 127.0.0.1 或内网地址不要直接暴露到公网。4. 混合式技术路线不是让 LLM 硬读全部数据把几百万条记录交给 LLM 判断单靠提示词轮询显然不行。合理的工程化设计是分阶段缩小数据规模让 LLM 只处理真正需要语义判断的边界样本。整体流程可以拆成四个阶段数据清洗与归一化统一字段格式提取题名、作者、出版年、语言、类型。低成本候选聚合使用文本嵌入向量 聚类算法把高度相似的记录预分组。本地 LLM 审核对候选组做“是否属于同一超级作品”的二分类判断并给出理由。发现层输出给通过审核的簇生成标准化题名、作品范围描述和检索标签。为什么不能跳过第二步因为 LLM 推理成本远高于向量相似度计算。嵌入模型在 CPU 上也能批量跑一条书目记录可以很快转成向量DBSCAN 这类聚类算法处理十万条记录也比较从容。预聚类完成后只剩边界组需要 LLM 参与调用量会小很多。举个例子。假设某库有 100 万条书目记录第一步清洗后剩 80 万条有效记录向量预聚类后可能会产生 40 万个候选簇但其中大量是单记录簇不需要审核。真正需要 LLM 判断的可能是那些带翻译、副题名、版本混杂的中等簇数量可能只有几千个。把几千次推理放在本地 LLM 上做完全是可接受的规模。5. 本地 LLM 环境准备与启动环境准备分成两部分本地 LLM 运行时和 Python 数据管线。操作系统方面Windows、Linux、macOS 都能运行但 Python 依赖安装会有细微差异。Python 建议使用 3.10 或更新的版本并创建一个单独的虚拟环境避免和系统 Python 包互相干扰。5.1 安装本地 LLM 运行时这里以 Ollama 为例因为它安装简单、默认提供本机 HTTP 接口并且支持 CPU / GPU 推理。实际使用中LM Studio、llama.cpp、vLLM 等也能达到同样目的接口地址需要按各自的文档调整。# 示例在 Linux / macOS 下安装 ollama实际命令以官网文档为准 curl -fsSL https://ollama.com/install.sh | sh # 启动服务默认监听 127.0.0.1:11434 ollama serve服务启动后拉取一个对话模型作为示例名称可以根据本机资源和任务需求替换# 拉取 7B 级对话模型尺寸和量化方式以实际输出为准 ollama pull qwen2.5:7b然后验证模型是否能正常调用curl http://127.0.0.1:11434/api/tags如果返回 JSON 中能看到模型列表说明服务已经可用。5.2 创建 Python 工作目录建议按下面的结构组织文件bibliographic-superwork/ ├── data/ │ ├── raw_records.csv │ ├── normalized_records.csv │ └── clusters.jsonl ├── scripts/ │ ├── 01_normalize.py │ ├── 02_embed_cluster.py │ ├── 03_llm_review.py │ └── 04_export_discovery.py ├── logs/ └── requirements.txtrequirements.txt 里的核心依赖包括 pandas、scikit-learn、requests以及用于生成嵌入向量的 sentence-transformerspandas2.0 scikit-learn1.3 requests2.31 sentence-transformers2.7安装命令pip install -r requirements.txt如果只需要做轻量嵌入也可以使用本地 LLM 运行时自带的 embedding 接口但不同运行时接口差异较大。为了减少不确定性本文示例采用 sentence-transformers在第一次运行时它会下载模型文件后续就可以离线使用。6. 数据预处理与候选聚合代码示例6.1 演示数据集先准备一份最小化的演示数据。为了避开版权和隐私问题这里使用经典公版书目做结构演示。实际数据请替换为自己的合法来源。record_id,title,author,publisher,publication_year,language rec001,Moby-Dick; or, The Whale,Melville Herman,Harper Brothers,1851,en rec002,Moby-Dick,Norton Critical Edition,Herman Melville,W. W. Norton,2017,en rec003,白鲸,赫尔曼·梅尔维尔,上海译文出版社,2007,zh rec004,Moby-Dick (Unabridged),Herman Melville,Audible,2019,en rec005,Moby Dick 漫画版,Herman Melville,某个漫画出版社,2015,zh rec006,The Whale,Herman Melville,Random House,2020,en从人眼角度rec001 到 rec006 基本都属于同一个超级作品族。但 rec006 的题名是 The Whale如果没有作者信息和出版背景算法层面很可能漏掉它。这正是需要嵌入相似度和 LLM 配合的地方。6.2 题名归一化题名归一化的目标是去除不影响语义判断的版本标记和符号代码不需要复杂import re import pandas as pd def normalize_title(title: str) - str: if not isinstance(title, str): return text title.lower() # 保留中英文和数字去掉多余标点 text re.sub(r[^\w\s\u4e00-\u9fff], , text) # 去掉常见的版本和载体说明词可按数据情况增删 text re.sub(r\b(edition|ed|vol|volume|unabridged|paperback|hardcover)\b, , text) return .join(text.split()) def build_candidate_key(row: pd.Series) - str: title_part normalize_title(row[title]) author_part if isinstance(row.get(author), str): author_part re.sub(r\s, , row[author].lower()) return f{author_part}:{title_part} df pd.read_csv(data/raw_records.csv) df[title_norm] df[title].apply(normalize_title) df[candidate_key] df.apply(build_candidate_key, axis1) df.to_csv(data/normalized_records.csv, indexFalse) print(df[[record_id, title_norm, candidate_key]].to_string())这个阶段并不是为了直接得到最终簇而是生成一个粗粒度的分桶键方便后续做精确比较。实际项目中一个易错点在于作者字段不同来源里“Melville, Herman”和“Herman Melville”顺序不一致所以通常需要对作者名做倒序归一或去空格处理。6.3 嵌入向量 DBSCAN 候选聚类对题名和作者做归一化之后还可以用嵌入向量对标准化题名做语义聚类。这里使用 sentence-transformers它会同时处理好中英文语料的统一向量表示。模型名称可以根据局域网可达情况替换成已下载的本地 embedding 模型。import numpy as np import pandas as pd from sklearn.cluster import DBSCAN from sentence_transformers import SentenceTransformer df pd.read_csv(data/normalized_records.csv) # 本地 embedding 模型第一次运行会下载后续离线可用 embedder SentenceTransformer(paraphrase-multilingual-MiniLM-L12-v2) titles df[title_norm].fillna().tolist() vectors embedder.encode(titles, normalize_embeddingsTrue, show_progress_barTrue) df[vector] list(vectors) # eps 参数需要在真实数据上调参min_samples1 表示即使只有一个记录也保留 clustering DBSCAN(eps0.25, min_samples1, metriccosine) df[cluster_id] clustering.fit_predict(np.array(vectors)) # 查看预聚类结果 for cid, group in df.groupby(cluster_id): print(fcluster {cid}: {group[title].tolist()})eps 控制聚类的疏密程度。如果 eps 太大不同作品会被错误合并如果太小同一个作品的不同版本又会被拆散。可以先用小批量样本来回测试找到一个能让《白鲸》的几条记录聚到一起、又不至于把无关书目混进来的取值。这一步做完之后得到的 cluster_id 只是候选簇。它的准确率通常足以把大量明显重复的数据去掉但处理跨语言、版本杂糅的边界时还不够。下一步交给本地 LLM。7. 本地 LLM 聚类审核与超级作品命名7.1 本地 LLM 调用封装先写一个通用的本地 LLM 调用函数。这里用 Ollama 的 HTTP API地址http://127.0.0.1:11434模型名qwen2.5:7b只是一个示例。实际项目里可以用你本机已经部署好的模型名替换。import json import time import requests LLM_BASE_URL http://127.0.0.1:11434 LLM_MODEL qwen2.5:7b def ask_llm(system_prompt: str, user_prompt: str, temperature: float 0.1) - str: payload { model: LLM_MODEL, messages: [ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ], stream: False, options: {temperature: temperature}, } resp requests.post(f{LLM_BASE_URL}/api/chat, jsonpayload, timeout180) resp.raise_for_status() return resp.json()[message][content]temperature 设低一点因为聚类审核是判断型任务不希望模型发挥太多创造性。7.2 判断同一超级作品的提示词给模型每组候选记录时提示词要明确“超级作品”的定义并让模型输出 JSON方便后续程序解析。一次请求里带上 5 到 10 条记录比较合适太多会超出小模型的上下文窗口。def build_review_user_prompt(records: list) - str: numbered \n.join( [f{i 1}. 题名: {r[title]} / 作者: {r[author]} / 出版年: {r[publication_year]} / 语言: {r[language]} for i, r in enumerate(records)] ) return f请判断下面 {len(records)} 条书目是否属于同一个超级作品\n{numbered}系统提示词可以这样设计SYSTEM_PROMPT_REVIEW 你是一名图书编目专家。所谓“超级作品”是指同一部文学或学术作品及其各种版本、翻译、载体现的聚合。 你的任务是判断给定的多条书目记录是否应该归入同一个超级作品簇。 判断规则 1. 同一原著的不同语言翻译通常属于同一个超级作品。 2. 同一原著的不同版本、不同出版社、精装/平装属于同一个超级作品。 3. 如果题名相同但作者不同、内容明显不同则不属于同一个超级作品。 4. 如果记录是对原著的改编例如漫画、影视剧本需要谨慎如果目标簇定义包含相关改编可以归入如果只做原作品聚合则不归入。 请只输出 JSON不要输出多余解释JSON 格式 {same_superwork: true, reason: 简要判断理由, risk_level: low/medium/high} 这里把 risk_level 作为额外输出是因为自动聚类不可能达到 100% 准确高风险簇需要后续人工复审。risk_level 是给工作人员做优先级队列用的不是模型凭空保证的事实。7.3 多候选组遍历遍历预聚类结果时不需要对所有簇都调用 LLM。一个簇只有一条记录直接跳过一个簇里的题名经过归一化后完全相同而且作者一致可以直接接受不调用模型。只有那些记录条数大于 1 且存在题名差异或语言差异的簇才提交给 LLM。def process_cluster_group(cluster_id, group_df, cache): cache_key fcluster_{cluster_id} if cache_key in cache: return cache[cache_key] records group_df[[title, author, publication_year, language]].to_dict(records) if len(records) 1: result {same_superwork: True, reason: 单条记录无需判断, risk_level: low} else: user_prompt build_review_user_prompt(records) raw ask_llm(SYSTEM_PROMPT_REVIEW, user_prompt) result parse_llm_json(raw) time.sleep(0.2) # 不要对本地服务造成过大压力 cache[cache_key] result return resultparse_llm_json 里要注意有些本地模型会在 JSON 外面加 markdown 代码块标记程序里要剥离import re def parse_llm_json(raw: str) - dict: if raw.startswith(): raw re.sub(r^(?:json)?|$, , raw, flagsre.MULTILINE).strip() try: return json.loads(raw) except json.JSONDecodeError: # 如果解析失败宁可标记为高风险让后续人工处理 return {same_superwork: False, reason: JSON 解析失败, risk_level: high}这里的处理原则是解析失败的簇不做自动合并留给人工。安全第一。7.4 超级作品命名与范围描述判断通过之后还可以让 LLM 给每个超级作品簇生成一个规范化名称和简短的描述方便后面做发现入口。SYSTEM_PROMPT_NAME 你是图书编目数据整理助手。请为一个已经确认的书目聚合簇生成一个稳定的超级作品名称和范围说明。 要求 1. 名称优先使用最权威的原始题名中文资料可附中文通用译名。 2. 范围说明不超过 60 个字说明该簇包含哪些类型的版本。 3. 只输出 JSON。 def generate_superwork_metadata(group_df): titles group_df[title].tolist() authors group_df[author].dropna().unique().tolist() user_prompt ( f该簇包含以下书目记录\n{chr(10).join(titles)}\n f责任者{, .join(authors)}\n 请生成超级作品名称和范围说明。 ) raw ask_llm(SYSTEM_PROMPT_NAME, user_prompt) return parse_llm_json(raw)生成的 JSON 建议保持成类似{name: 白鲸, scope: 赫尔曼·梅尔维尔原著及其主要中英文版本、全本有声书与无删节版, author: Herman Melville}的结构。8. 批量任务、缓存与接口调用注意事项当书目数据量大到几千组时建议增加三层控制第一层是缓存。已审核的簇 ID 和审核结果写入本地 JSON 文件避免脚本中断后重头再跑。第二层是分批和限速。本地 LLM 通常可以连续处理但并发过高还是会导致显存溢出或接口超时。第三层是失败重试。网络请求会因为模型加载、系统内存不足等原因失败需要捕获异常并进行指数退避。下面是一个批量脚本的骨架import json import time import requests from typing import Dict, List def batch_review_clusters(cluster_groups: Dict[str, object], output_path: str): cache {} try: with open(logs/review_cache.json, r, encodingutf-8) as f: cache json.load(f) except FileNotFoundError: cache {} results [] for cluster_id, group_df in cluster_groups.items(): try: result process_cluster_group(cluster_id, group_df, cache) results.append({cluster_id: cluster_id, **result}) with open(output_path, a, encodingutf-8) as f: f.write(json.dumps({cluster_id: cluster_id, **result}, ensure_asciiFalse) \n) except requests.exceptions.Timeout: print(f[timeout] cluster {cluster_id}, sleep 10s retry later) time.sleep(10) except Exception as exc: print(f[error] cluster {cluster_id}: {exc}) results.append({cluster_id: cluster_id, same_superwork: False, reason: fexception: {exc}, risk_level: high}) # 回写缓存 with open(logs/review_cache.json, w, encodingutf-8) as f: json.dump(cache, f, ensure_asciiFalse, indent2) print(fdone, total {len(results)} clusters)要注意打印输出不要包含原始书目全文日志里只保留 record_id 或 cluster_id降低敏感信息扩散风险。9. 输出发现数据与效果验证经过 LLM 审核和命名后可以生成一个便于检索的发现文件。每行 JSON 对应一个超级作品簇{ cluster_id: 12, superwork_name: 白鲸, superwork_scope: 赫尔曼·梅尔维尔原著及其主要中英文版本、全本有声书与无删节版, record_ids: [rec001, rec002, rec003, rec004, rec005, rec006], representative_title: Moby-Dick; or, The Whale, languages: [en, zh], risk_level: low }如果需要支持前端模糊搜索可以导入 SQLite 并用 FTS5 建全文索引。示例过程sqlite3 discovery.db CREATE VIRTUAL TABLE superwork_fts USING fts5(superwork_name, superwork_scope, representative_title); 验证聚类效果时不要只看准确率还要看召回。对一批手工标注的数据可以计算两个数字准确率生成的簇里真正属于同一超级作品的比例。召回率原本属于同一超级作品的记录有多少被成功归到同一个簇里。实际项目中不可能对全量数据做人工标注所以建议抽样随机抽 300 到 500 个簇人工判断簇边界是否正确。抽样策略要覆盖简单簇、双语簇、高风险簇不能只挑容易样本。判断成功的标准可以定义为同一作品的不同版本被合并为一个簇且簇内没有混入无关作品每个簇有可读的超级作品名称风险标记为 high 的簇不超过可接受比例。如果失败先看是预聚类阶段漏召回了还是 LLM 审核阶段误判了再针对性地调整 eps 或提示词。10. 资源占用与性能观察方法在真实书目数据集上跑需要关注两个计算环节的性能嵌入向量生成和 LLM 推理。嵌入阶段使用 sentence-transformers 时如果机器有 NVIDIA GPUsentence-transformers 会自动使用 CUDA如果只有 CPU也能跑但十万条记录可能需要几十分钟到几小时具体耗时取决于 CPU 核心数和向量化模型。执行时可以观察 htop、任务管理器或者用nvidia-smi查看 GPU 占用。LLM 推理阶段显存占用主要由模型大小和量化精度决定。7B 级模型全精度和 4bit 量化差别很大具体显存占用要以本地运行时的ollama ps或 nvidia-smi 输出为准。如果你的显卡显存不够可以考虑使用更小的模型、更低的量化精度、或者直接切到 CPU 推理。CPU 推理不是不能用但对响应时间和并发能力影响明显。降低显存占用和提升吞吐量的常用手段把温度参数调低默认 0.1 即可。一次 prompt 里多放几条候选记录减少请求总次数。模型加载后保持常驻避免重复重启。调整 DBSCAN 的 eps让预聚类更准减少 LLM 请求量。如果提示词太长做截断只保留每条记录的题名、作者、年份和语言。对 LLM 推理性能一个更稳妥的判断是实际调用一定以本机测试为准。不要照搬别的文章里的“占用 X GB”结论因为模型版本、上下文长度和并发策略都会影响结果。11. 常见问题与排查方法问题现象可能原因排查方式解决方案本地 LLM 接口请求返回 404运行时不同接口路径不一致查看本地运行时文档或服务日志换成对应运行时的接口路径例如 OpenAI 兼容接口模型未下载或名称错误pull 的模型名与调用名不一致先调用/api/tags查看已安装模型使用正确的模型名重新 pull脚本请求超时模型首次加载慢 / 系统负载高 / 上下文过长观察日志和 GPU 占用加大 timeout先预热模型缩短 prompt显存或内存不足模型过大或并发请求过多nvidia-smi / htop 查看占用切换到更小模型或量化版本降低并发聚类结果不理想版本被拆散eps 设定过小抽样查看候选簇增大 eps或调整 embedding 模型不同作品被误合并eps 设定过大抽样查看高风险簇调小 eps或让 LLM 提高 risk_level 阈值LLM 输出 JSON 解析失败小模型指令遵循能力偏弱打印原始返回剥离 markdown 标记增加 fallback 逻辑预处理后大量记录为空原始字段缺失严重检查 CSV/API 来源补全字段或将该部分数据排除后再处理一个特别需要注意的问题是Ollama 服务默认监听 127.0.0.1这个配置不要随意改成 0.0.0.0。如果确实需要在局域网内其他机器访问建议加鉴权并且只在可信网络环境里开启。12. 最佳实践与工程化建议先小规模跑通再扩大数据量。第一次实验不要直接处理百万级数据建议导出一万条左右的样本覆盖多语言、多版本、高重复几种类型。先验证向量聚类和 LLM 提示词是否稳定再决定是否要做全量批处理。数据目录最好分成三层原始数据、特征数据、结果数据。原始数据只读不允许程序直接改写特征数据包括归一化字段和向量结果数据每次生成都要带时间戳或版本号方便回溯。比如输出文件可以是results_20250101.jsonl不要把旧结果直接覆盖。对风险记录一定要人工复审通道。LLM 给出的 risk_level 为 high 的记录不要自动发布至少做二次确认。二次确认可以是人工也可以用一批更大的提示词重新审核两次取一致结果。自动合并必须可回滚不要让程序直接改动原始书目数据库。涉及版权的材料要坚持三不原则不在未授权情况下擅自对外聚合发布不把受版权保护的作品正文放入 LLM 提示词去生成摘要或重写不把内部聚类结果用作未授权模型的训练语料。书目元数据的再分发也需要查看数据供应商的许可条款。如果将来要接入第三方工具或做成服务建议把本地 LLM 接口封装成独立的微服务数据层只通过 JSON 交互。这样即使底层模型换掉上层聚类脚本也不需要重写。13. 总结与下一步可以先去本机部署一个本地 LLM 运行时准备几百条测试书目然后按文章里的三步走归一化、嵌入聚类、LLM 审核。验证完准确率和召回率以后再决定是否把规模扩大。最容易踩的坑是 eps 参数没有调好就贸然全量跑以及本地模型输出 JSON 不稳定没有做容错。建议把日志、缓存、风险标记一起做完整再进入批量环节。下一步可以考虑的方向是把聚类结果导出成知识图谱结构把作品、作者、翻译者和改编者关系合并成网络形成真正的发现系统。
返回列表