[Vibe Coding][DeepSeek]用大模型自动为博客生成分类与标签V2

前几周我实现了第一版基于大语言模型(LLM)的博客文章分类与标签自动生成系统,初衷是借助 AI 的语义理解能力,解放双手、提升元数据的一致性与可维护性。然而,在实际运行一段时间并观察生成结果后,我发现这套方案虽然自动化了流程,但是没有真正实现智能化 — 最终得到的类别列表和标签体系既不能精准反映单篇文章的核心内容,也无法体现整个博客的技术定位与知识结构

我试图用一个端到端的黑箱流程解决所有问题。而现实是,好的元数据体系需要清晰的顶层设计、细粒度的内容理解,以及人机协同的持续演进机制。我对整个流程进行了彻底重构,将原先的两个脚本拆解为四个职责明确、可独立验证的阶段,形成一套更稳健、更可控、也更贴合实际需求的四阶段自动化工作流。

引言:为什么第一版不够好?

最初的两阶段方案(generate_ontology.py + assign_categories_tags.py)在概念上简洁优雅,但在实践中暴露了多个关键缺陷,导致生成的元数据看起来合理,用起来别扭

文章生成的标签列表不足够体现文章内容

  • 混入无关噪声:LLM 在分析文章时,会把文章中的相关文章参考链接当作正文内容,从而生成与本文核心主题无关的标签。
  • 出现“幻觉标签”:模型有时会强行匹配一个语义相近但原文根本未提及的技术名词。比方说,一篇讲GitHub Actions的文章,生成的标签列表竟然出现了GitLab CIJenkins
  • 输入内容被过度截断:我发现之前实现的脚本对于输入LLM的文章内容长度限制太多,对于长文来说关键信息也会出现在中后段,这样导致标签覆盖不全、重点偏移。

每篇文章的类别列表不足够体现博客网站的定位

  • 分类过度集中:几乎所有技术文章都被归入几个类别当中,缺乏对于文章目的(如工具评测工程反思领域综述)的区分,导致分类失去导航价值。
  • 类别命名模糊且不一致:同一类文章在不同批次处理中可能被命名为 ["开发笔记"]["实践记录"]["经验分享"],语义高度重叠但形式各异,破坏了体系的整洁性。
  • 缺乏顶层约束:分类完全由模型从下而上归纳得出,没有体现我对博客聚焦 AI 工程化、强调可复现性、重视方法论提炼的整体定位。

新的设计原则

在新版本的实现中,我彻底转变思路:先由人主导、LLM 辅助,构建一个稳定、清晰、有边界的知识本体;再让 LLM 在这个本体内做精准、受限的分配。为此,我将整个流程细化为四个独立脚本:

  1. 针对标签列表:允许每篇文章保留其独特的原始标签候选,仅在全局层面合并语义相同的标签,不再为了简洁而牺牲表达力;
  2. 针对类别列表:不再基于文章内容反推分类,而是从博客整体定位出发,通过人工与 LLM 协作,自上而下设计分类骨架;
    • 一旦确定,主类结构即固化,不随新文章动态变化;
    • 明确哪些主类可细分(比如算法与模型),哪些不可(比如人生感悟);
  3. 生成缓存体系:将分类骨架与标准化标签词表持久化为 .ontology/ 下的配置文件,作为后续分配的唯一权威来源;
  4. 新文章处理:仅使用缓存体系进行分配,绝不自动修改或扩展缓存,确保体系稳定性。只有当积累足够多的新主题文章后,才人工介入评估是否需要更新本体。

这一重构不仅提升了元数据的质量,更重要的是,它建立了一套可持续演进、人机协同、边界清晰的知识管理机制——这才是 LLM 在个人知识工程中应有的角色。

第一阶段:深度解析每篇文章,生成结构化元数据(stage1_extract_meta.py

目标:为每篇文章生成高质量、去噪后的原始元数据,作为后续标签聚类与分类分配的唯一依据。

在新流程中,第一阶段不再尝试构建全局体系,而是专注于“理解单篇文章”。我们把 LLM 当作一位资深技术编辑,让它逐篇阅读、提炼主旨、提取标签,但不做任何跨文章的归纳或决策

设计原则

  1. 全文参与,拒绝截断
    旧方案因成本顾虑默认截取前 3000 字符,导致长文信息丢失。新脚本采用分块处理 + 摘要融合策略:

    • 将正文按 8000 字符分块(重叠 500 字防断句);
    • 对每块生成技术摘要;
    • 合并摘要后,再由 LLM 输出最终 main_ideatags
    • 确保即使万字长文也能被完整理解。
  2. 内容清洗,去除噪声
    通过 clean_markdown() 函数,自动剥离以下干扰内容:

    • 所有代码块(... 和行内 ...);
    • 图片与链接(保留链接文本,如 [GitHub]GitHub);
    • 引用块(> 开头的段落);
    • 参考文献/相关阅读区块(通过正则识别参考延伸阅读等标题,截断其后所有内容)。
  3. 输出结构化 JSON,杜绝 YAML 风险
    早期使用 YAML 作为中间格式时,常因 LLM 输出包含冒号(如 NLP: 命名实体识别)导致解析失败。现在统一输出标准 JSON,并通过三重保障确保可解析:

    • 格式自校验:调用 LLM 专门修复 JSON 语法(如补双引号、闭合括号),但严禁修改语义;
    • 智能回退:若校验后结果无效,自动回退到原始输出;
    • 截断修复:对常见截断(如缺少 ]})进行程序化补全。
  4. 支持缓存与增量处理
    每篇文章的输出保存为 .tmp/{abbrlink}.json。若文件已存在且格式有效,则直接跳过,避免重复调用 API。这使得:

    • 脚本可安全中断后重启;
    • 新增文章只需重新运行,不影响已有结果。
  5. 精细化标签清洗
    对 LLM 返回的原始标签进行后处理:

    • 过滤纯数字(如年份 2024);
    • 规范双语格式(中文/English),避免 Docker/Docker 这类冗余;
    • 去重并限制最多 20 个,保留信息量同时控制噪声。

输出示例

以本文为例,stage1_extract_meta.py 会生成如下 .tmp/new_meta_system.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"main_idea": "本文介绍了重构博客元数据体系的四阶段自动化流程,强调通过人机协同构建稳定、可控的分类与标签体系。",
"tags": [
"大语言模型/LLM",
"DeepSeek",
"元数据/Metadata",
"自动化/Automation",
"博客/Blog",
"知识管理/Knowledge Management",
"工程化/Engineering",
"Prompt Engineering",
"分类/Category",
"标签/Tag",
"工作流/Workflow"
],
"source_file": "new_meta_system.md"
}

💡 注意:此时的 tags该文章独有的原始候选集,尚未进行全局标准化。这正是新方案的关键——先保留个性,再做共性抽象

💡 提示:此阶段耗时最长(需调用 LLM 处理全文),但只需运行一次。后续新增文章可单独处理,无需全量重跑。

第二阶段:构建统一 Ontology —— 标签标准化与分类体系对齐

目标:构建一个结构清晰、语义一致、可扩展的全局知识本体(Ontology)。

具体目标包括:

  • 标签标准化(Tag Normalization)
    合并同义、近义、格式变体等冗余标签,建立标准标签 + 别名映射体系,确保不同文章中表达相同概念的标签被统一处理。

  • 分类体系固化(Category Schema Enforcement)
    基于人工预定义的分类架构(category_schema.json),构建严格的 [主类, 子类] 二元分类路径,明确区分写作目的技术主题,避免分类泛化或歧义。

  • 文章-本体对齐(Assignment)
    为每篇文章分配最多 3 个符合规范的分类路径,并将其原始标签映射到标准化标签空间,形成结构化的 assignment.json 输出。

设计原则

✅ 写作目的优先于技术细节

分类体系聚焦于作者的写作意图(如论文解读踩坑记录职业成长),而非单纯的技术关键词(如TransformerDocker)。这使得内容组织更具语义一致性,便于读者按需浏览。

✅ 严格层级约束

  • 对于 allow_subcategory: false 的主类(如人生感悟),仅允许单层路径 ["人生感悟"]
  • 对于 allow_subcategory: true 的主类(如算法与模型),必须使用双层路径 ["算法与模型", "计算机视觉技术"]
  • 禁止跨层级混用或自定义子类,确保分类树的稳定性与可维护性。

✅ 标签语义去重,宁缺毋滥

在标签聚类过程中,LLM 被要求仅在 100% 确定等价时才合并。例如:

  • ✅ 合法合并:["LLM", "Large Language Model"]["ResNet50", "ResNet-50"]
  • ❌ 禁止合并:["Adam", "AdaGrad"]["CIFAR-10", "ImageNet"]

此策略牺牲部分召回率,换取高精度的标签一致性,避免后期因错误归一化导致语义污染。

关键实现流程

步骤 1:标签标准化(三阶段流水线)

  1. 语义聚类(Batched LLM Clustering)
    将所有原始标签分批(≤30/批)送入 LLM,要求其按严格等价关系聚类。输出为若干语义簇。

  2. 簇内标准化(Standard Tag Selection)
    对每个簇,选择最规范、完整、含中英文的形式作为 standard_tag,其余作为 aliases

  3. 全局冲突消解(Union-Find 合并)
    处理跨批次可能出现的间接等价(如 A→B 且 B→C ⇒ A,B,C 合并),最终输出无冲突的标准标签集。

输出文件:.ontology/tags.json
格式示例:

1
2
3
4
5
6
7
8
9
10
11
12
{
"tags": [
{
"standard_tag": "大模型/Large Language Model",
"aliases": ["LLM", "Large Language Models"]
},
{
"standard_tag": "ResNet50",
"aliases": ["ResNet-50"]
}
]
}

步骤 2:加载预定义分类体系

直接读取人工确认的 .ontology/category_schema.json,生成合法的分类路径列表。不再依赖 LLM 动态生成子类,确保分类体系的可控性与稳定性

输出文件:.ontology/categories.json
示例路径:

  • ["踩坑记录"] (不可细分)
  • ["项目复现", "前沿论文代码验证"] (可细分)

步骤 3:文章分配(LLM 辅助对齐)

对每篇文章:

  • 将其原始标签通过别名映射转换为标准标签;
  • 结合 content_typemain_idea,调用 LLM 从预定义路径池中选择最多 3 个最匹配的分类;
  • 强制校验路径合法性(层级、主类存在性等),过滤非法输出。

输出文件:.ontology/assignment.json
格式示例:

1
2
3
4
5
6
7
8
9
10
[
{
"file": "resnet_from_scratch.md",
"categories": [
["项目复现", "从零实现核心算法"],
["算法与模型", "深度神经网络架构"]
],
"tags": ["ResNet50", "大模型/Large Language Model"]
}
]

缓存与容错机制

  • 标签缓存:若 .ontology/tags.json 已存在且非空,则跳过耗时的 LLM 聚类,加速迭代。
  • 失败回退:LLM 调用失败时,自动降级为最长字符串作为标准标签,保证流程不中断。
  • 路径强校验:即使 LLM 返回非法路径(如 ["不存在的主类", "xxx"]),也会被过滤,确保输出 100% 符合 schema。

输出成果概览

文件 内容 用途
tags.json 标准标签 + 别名映射 支持标签搜索、去重、国际化显示
categories.json 全局合法分类路径列表 前端导航菜单、筛选器数据源
assignment.json 每篇文章的结构化元数据 内容索引、推荐系统输入、静态站点生成

第三阶段:注入结构化元数据 —— 自动更新博文 Front-matter

目标:将结构化的分类与标签信息回写到原始 Markdown 博文中。

具体表现为:

  • 自动更新每篇博文的 front-matter(YAML 元数据头)
  • 确保静态博客生成器Hexo能正确识别 categoriestags
  • 保留原始正文内容不变,仅精准修改元数据区域

输入与输出

类型 路径 说明
输入 .ontology/assignment.json Stage 2 产出的结构化分配结果,包含每篇文章的 filecategoriestags
输入 blog/source/_posts/**/*.md 原始博文目录(支持递归子目录),脚本自动匹配文件名
输出 修改后的 .md 文件 在 front-matter 中注入 categoriestags 字段
副产品 .backup_stage3/ 自动备份所有被修改的文件,防止误操作

💡 注意:assignment.json 中的 "file" 字段仅为纯文件名(如 "LLM-Inference-Optimization.md"),不包含路径。脚本需在 POSTS_ROOT 下递归查找真实位置。

输出效果示例

假设某文章原内容为:

1
2
3
4
5
6
---
title: 从零实现 Softmax 函数
date: 2024-05-20
---

本文手写 Softmax...

阶段3处理后变为:

1
2
3
4
5
6
7
8
9
10
11
12
---
title: 从零实现 Softmax 函数
date: 2024-05-20
categories:
- ["项目复现", "从零实现核心算法"]
- ["算法与模型", "经典机器学习方法"]
tags:
- "Softmax"
- "数值稳定性/Numerical Stability"
---

本文手写 Softmax...

第四阶段:单篇分析与交互式调试 —— 基于全局本体的即时分类推理

目标:提供一个轻量级、高精度的单篇文章分析工具。

具体用于:

  • 验证新文章在已有本体下的分类与标签表现
  • 支持人工审核、调试或增量处理场景
  • 确保新增内容与全局知识体系(Ontology)保持语义一致

该工具不修改任何文件,仅基于 .ontology/ 中已构建的标准化标签集、合法分类路径和分类 Schema,对单篇 .json 元数据进行“模拟分配”,输出符合规范的 categoriestags

设计定位

特性 说明
依赖缓存 必须存在 .ontology/tags.jsoncategories.jsoncategory_schema.json(由 Stage 2 生成)
输入格式 单个 JSON 文件(如 .tmp/xxx.json),结构需包含 main_ideacontent_typetags 字段(由 Stage 1 产出)
输出方式 控制台打印标准化标签与推荐分类路径,不写入磁盘
使用场景 新文章试分类、错误案例复现、本体迭代测试、CI/CD 预检等

💡 本质是 Stage 2 分配逻辑的“只读重放”,但针对单篇优化,适合开发与运维介入。

核心流程

步骤 1:加载全局本体缓存

  • tags.json 构建 alias → standard_tag 映射表,用于标签标准化
  • categories.json 提取所有合法分类路径(如 ["算法与模型", "大模型训练"]
  • category_schema.json 判断哪些主类不可细分(如“人生感悟”),用于路径合法性校验

步骤 2:标准化输入标签

对输入 JSON 中的原始标签:

  • 去重、去空、去空白
  • 通过别名映射转换为标准形式
  • 未匹配的标签保留原样(保守策略)

步骤 3:构造 LLM 分类提示(Prompt)

精心设计上下文约束:

  • 明确列出所有可用分类路径(按主类+子类排序)
  • 强调“必须严格使用列表中的路径”
  • 指定输出为纯 JSON,含 categories 字段
  • 写作类型 + 主旨 + 标签三元组作为判断依据

示例片段:

1
2
3
4
5
6
7
8
9
10
可用分类路径(共 28 条):
- ["人生感悟"]
- ["踩坑记录", "CUDA配置"]
- ["项目复现", "前沿论文代码验证"]
...

文章信息:
- 写作类型: 论文解读
- 主旨: 解读 Llama 3 的架构改进与训练策略
- 相关标签: Llama3, 大模型/Large Language Model

步骤 4:调用 LLM 并后处理

  • 使用 temperature=0.0 确保确定性输出
  • 自动剥离 Markdown 代码块包裹(如 json ...
  • 严格校验返回路径是否在 valid_paths
  • 最多保留 3 个有效分类,非法项自动过滤

步骤 5:结构化输出结果

以清晰格式打印:

  • 文件名(来自 JSON stem)
  • 标准化后的标签列表
  • 推荐的分类路径(最多 3 个)
  • 若无匹配,明确提示“(无匹配分类)”

全流程小结:从原始博文到结构化知识库的自动化演进

上述4个阶段脚本保存在仓库:zjykzj/zjykzj.github.io

四阶段协同架构

本系统通过四个精心设计、职责分明的阶段,构建了一条端到端的博客内容结构化流水线

阶段 核心任务 关键产出 技术特点
Stage 1
(元信息提取)
单篇 Markdown → 结构化 JSON .tmp/*.json
(含 main_idea, content_type, tags
LLM 理解写作意图,聚焦语义而非关键词
Stage 2
(本体构建)
全局标签标准化 + 分类体系对齐 .ontology/tags.json
.ontology/categories.json
.ontology/assignment.json
人工 Schema + LLM 聚类,确保高精度一致性
Stage 3
(元数据注入)
将结构化结果写回 front-matter 修改后的 _posts/*.md
(带标准 categories/tags
安全更新、自动备份、路径智能匹配
Stage 4
(单篇分析)
基于本体的即时分类推理 控制台输出(只读) 支持调试、预检、增量处理,不修改任何文件

四者形成闭环:理解 → 统一 → 注入 → 验证,既支持全量重建,也支持单篇增量。

设计哲学与核心优势

✅ 以“写作目的”为中心的分类逻辑

区别于传统基于技术关键词的打标方式,本系统优先识别作者意图(如“踩坑记录”、“职业成长”),使内容组织更符合读者心智模型。

✅ 人工可控 + 机器高效 的混合范式

  • 分类主干由人工定义(category_schema.json),确保业务语义稳定
  • 标签聚类与文章分配由 LLM 辅助,提升处理效率
  • 所有 LLM 输出均受规则强约束,避免“幻觉污染”

✅ 安全、可逆、可观测

  • Stage 3 自动备份,Stage 4 只读分析,杜绝误操作
  • 每阶段均有明确输入/输出契约,便于监控与重试
  • 错误信息清晰(如“未找到文件”、“缺少缓存”),降低运维成本

后记

毫无疑问,上面的内容也是用LLM进行整理和总结的,写得很好,就是缺少一些人味,可能还需要再调教下LLM吧。

这段时间使用LLM处理博客文章分类和标签的过程中,很深刻的体会就是大模型在加速完成你任务的同时,也让你在这个过程中少了一些学习和理解,它屏蔽掉了过往那种工作跟学习的方式,取而代之是一直围绕着LLM进行分析和调试。大模型很棒,所以这种方式会持续下去,也就是Vibe Coding

可以依靠LLM进行加速工作和学习,但是完全依赖LLM进行黑盒操作,往往没法达到预期目标,所以还是要对事物有足够基本的认知,就像上面说的:需要清晰的顶层设计、细粒度的内容理解,以及人机协同的持续演进机制。说人话,就是不要想着用几个提示词调用LLM就能够完成任务了,还是得不断调试调优,对每个环节进行分析归纳,这样搭配着LLM一起工作,能够达到事半功倍的效果。