📊 中华诗词知识库 · 第六章:人工智能应用集成
Knowledge Base of Chinese Poetry (KBCP) — Chapter 6: AI in Action
🌟GitHub 开源地址:https://github.com/liang1057/Knowledge-Base-of-Chinese-Poetry
🌟如果资源对你有帮助,欢迎 Star 支持!
📥JSON数据下载:https://download.csdn.net/download/sdust_dx/92826598
🎯 前言
前五章我们完成了数据收集 → 数据清洗 → Schema 设计 → Web 系统 → AI 自动打标,知识库已经"能存、能看、能管、能标"。但"标好标签"只是起点——真正的价值在于:让使用者用自然语言与这 6.2 万首诗词对话。
本章是系列的收官之作,聚焦"人工智能的应用层":一个用户问题从输入到回答,背后究竟经过了哪些智能处理?
💡核心观点:真正的"智能"不是把数据一股脑丢给大模型,而是让大模型在确定的边界内"思考"。本章要讲的,正是一套"确定性 + 语义 + 推理"三者协作的混合智能架构。
一、整体架构:混合智能(Hybrid Intelligence)
一个常见的误区是:RAG 万能,把所有诗词塞进向量库、召回 top-k、丢给 LLM 生成,就完事了。这都是技术不成熟的人的表现,相当于硬吃LLM, 完全不是正确使用人工智能的方式。
这种方式都是半吊子人工智能,完全没有弄明白LLM的能力:
比如,在诗词知识库这个场景里,纯 RAG 有三个致命软肋:
| RAG 的软肋 | 具体表现 | 本项目的解法 |
|---|---|---|
| 算不了数 | "李白写了多少首诗"需要COUNT(*),向量相似度给不出精确数字 | 走确定性 SQL 计数快路径 |
| 比不了 | "李白和杜甫谁写月亮的诗更多"需要 JOIN + 聚合 | 走 Text-to-SQL 生成聚合查询 |
| 容易幻觉 | LLM 凭记忆补出"杜甫有 1500 首"之类错误 | 工具返回结构化事实,LLM 只负责"串讲" |
正确的方法应该是:
本项目的智能化RAG架构是在已有数据库(知识库)的基础上,使用LLM提升智能理解能力:
一句话总结:确定性事实交给 SQL,语义联想交给向量检索,综合表达与多步推理交给 LLM——各司其职,互不越界。
二、第一关:别名映射与查询理解(实体消歧)
2.1 别名映射 AliasMapper
用户不会规规矩矩地输入"苏轼",他可能说"子瞻"“东坡”“苏东坡”。系统启动时从author表一次性加载name / courtesy_name(字) / art_name(号) / other_names(别名),构建{别名小写 → 标准名}的哈希索引:
# KBCP_AliasMapper.py 核心逻辑(示意)forainauthors:self._add_alias(a['name'],'author',aid)# 标准名forfldin('courtesy_name','art_name','other_names'):foraliasinself._split(a[fld]):self._add_alias(alias,'author',aid,a['name'])# 子瞻→苏轼resolve("子瞻")直接返回{"matches": [("子瞻","苏轼","author","F0282")]},后续所有路径都基于标准名查询,从根本上消除"同名不同实"的歧义。
同时,通过数据库中的作者信息等,组成最好的答案。
2.2 查询分类 QueryClassifier
分类器完全基于规则、不调用 LLM,毫秒级、零成本、结果可复现:
| 优先级 | 类型 | 触发特征 | 路由去向 |
|---|---|---|---|
| 1 | STATS | 含"多少/几首/最多/总数" | 确定性计数 / SQLAssist |
| 2 | COMPARE | 含"与/和/对比/区别"且有两实体 | SQLAssist 聚合 |
| 3 | ENTITY_AUTHOR | 含"是谁/介绍/生平" | SQL 查作者 |
| 4 | FIND_POEM | 像诗句(含标点或 5/7 字句式) | SQL 诗句溯源 |
| 5 | ENTITY_POEM | 含《》书名号标题 | SQL 查作品 |
| 6 | TAG_BASED | 含"主题/风格/情感/意象" | SQLAssist 标签检索 |
| 7 | ANALYTICAL | 其余(赏析、联想等) | Agent + RAG |
💡为什么不用 LLM 做分类?分类是高频、低熵、强模式匹配任务,规则引擎在准确率、延迟、成本上全面优于 LLM,且不会因为模型"心情"而抖动。把 LLM 留给真正需要语义理解的地方。
三、第二关:Text-to-SQL —— 让大模型写查询,但不许乱来
统计、对比、主题检索这类结构化查询,本质是"自然语言 → SQL"。这是 LLM 的强项,但也是幻觉重灾区:它常会编造字段名、写SELECT *、甚至想DELETE。本项目的SQLAssist用三重约束把这个能力关进笼子里。
3.1 元数据注入:给 LLM 一份"数据库说明书"
系统从第三章设计的myschema表读出字段中文名 → 键名 → 类型对照,连同外键 JOIN 路径、检索优先级、禁止事项,一起注入 Prompt:
数据库表结构(字段中文名 -> 字段键名): poem: 标题(title: text) | 正文(content: text) | 作者ID(author_id: text) ... vocab: 类目(key: text) | 标签(label: text) ... 外键关联规则(固定 JOIN 路径): poem.author_id = author.author_id poem_tag.poem_id = poem.poem_id poem_tag.vocab_id = vocab.vocab_id 禁止事项: - 只允许 SELECT,禁止 UPDATE/DELETE/INSERT/DROP/ALTER - 禁止 SELECT *(明确列出需要的字段) - 涉及标签必须 JOIN poem_tag + vocab此外还会注入vocab表的全部标签取值(防止它写"月亮"而库里只有"月")和别名映射结果(“子瞻"→"苏轼”)。
3.2 生成后校验:把"野 SQL"挡在门外
LLM 生成的 SQL 不直接执行,而是先过校验器:
def_validate(self,sql:str)->tuple:# 1. 是否含危险操作danger=['DROP ','DELETE ','INSERT ','UPDATE ','ALTER ']ifany(dinsql.upper()fordindanger):returnFalse,f"禁止使用{d.strip()}操作"# 2. 禁止 SELECT *ifre.search(r'SELECT\s+\*',sql,re.I):returnFalse,"禁止 SELECT *,请明确列出字段"# 3. 引用的字段是否都在 myschema 中(防字段幻觉)valid_cols={row['column_name'].lower()forrowinself._schema}for_,colinre.findall(r'(\w+)\.(\w+)',sql):ifcol.lower()notinvalid_cols:returnFalse,f"字段 '{col}' 不在 myschema 中"returnTrue,''校验失败时,把错误信息回灌给 LLM 重试(最多 3 次),让它自我修正。最终 SQL 由execute_readonly_sql只读执行,彻底杜绝写操作。
💡这是全章最重要的工程经验:LLM 写 SQL 不可怕,可怕的是"直接执行"。用元数据约束 + 字段白名单 + 只读执行 + 错误重试四道防线,就能把幻觉控制在可接受范围。
四、第三关:RAG 向量检索与语义推荐
开放性问题(“写思乡的诗有哪些”“这首诗表达了什么情感”)不适合 SQL,需要语义检索。
4.1 向量是怎么来的
每首诗词用作者《标题》正文拼接成文本块,再用嵌入模型向量化,存进poem_embedding表:
| 方案 | 模型 | 特点 |
|---|---|---|
| 主方案 | paraphrase-multilingual-MiniLM-L12-v2 | 多语言、对古诗+现代提问都友好,需下载 |
| 兜底 | sklearnTF-IDF(字符级 n-gram 1~3) | 零下载、纯本地,网络受限也能跑 |
💡 选多语言模型而非纯中文模型,是因为用户提问常是白话(“写想家的诗”),而诗词是文言,多语言模型在跨风格语义对齐上更稳。TF-IDF 用字符级 n-gram而非词级,是因为古诗无空格、分词反而引入噪声。
4.2 多源检索与余弦相似度
RAGIndex同时从诗词(向量)、作者(LIKE)、标签(向量语义匹配)三个来源召回,合并去重后取 top-k。相似度用余弦:
[
\mathrm{sim}(q, d) = \frac{\vec q \cdot \vec d}{\lVert \vec q \rVert \lVert \vec d \rVert}
]
# KBCP_RAG_Index.py 余弦相似度(示意)dot=np.dot(query_vec,emb_vec)norm=np.linalg.norm(query_vec)*np.linalg.norm(emb_vec)sim=dot/normifnorm>0else0同一套向量还能做语义推荐:给定一首诗,按余弦相似度排序召回"意境相近"的作品,实现"读这首诗的人也喜欢"。
五、第四关:Agent 中枢与工具调用(Function Calling)
以上三关偏"确定性"。最体现 AI 深度的,是把 LLM 当大脑、把工具当手脚的 Agent 中枢(KBCP_Agent)。
5.1 工具集:给大模型装上"手脚"
LLM 本身不会查数据库,但能"决定调用哪个工具、传什么参数"。系统向 LLM 暴露 7 个结构化工具:
| 工具 | 作用 | 内部确定性保障 |
|---|---|---|
search_poem | 按诗句片段找出处 | SQLLIKE精确匹配 |
get_poem_full | 返回诗词全文/译文/赏析 | 主键查询 |
get_author | 查诗人生平字号 | 别名解析后查表 |
count_poems | 统计收录诗数 | COUNT(*)精确计数 |
count_poems_by_tag | 按主题精确计数 | JOINpoem_tag+vocab |
compare_by_tag | 多诗人主题对比 | 同一标签口径聚合 |
semantic_search | 语义向量检索 | RAGIndex 多源召回 |
5.2 工具返回"结构化",而非"自然语言"
关键设计:工具返回的是JSON 结构化字典,不是拼给人看的句子。
# KBCP_Tools.py:工具返回结构化数据供 LLM 综合return{"found":True,"author":"李白","total_count":472,# 数字由 SQL 保证精确"per_label":[{"label":"月","count":472}]}这样 LLM 拿到的是"事实原料",而不是"已完成的答案",它只能基于事实综合表达,从机制上杜绝了编造数字。
5.3 近义理解与多步推理
开启"主题词近义理解"后,用户问"写月亮的诗",系统先让 LLM 把"月亮"扩展为 vocab 中相关的近义标签集合(月/羁旅/思乡…),召回更智能;LLM 扩展不可用时,回退到向量语义匹配。
Agent 还支持多步推理(MAX_TURNS=5)与多轮对话指代消解:用户问完"李白",接着说"他写月亮的诗多吗","他"会被替换为"李白"再处理。
⚠️反幻觉铁律(写进 System Prompt):严禁凭记忆作答,所有事实必须来自工具返回;若工具无结果,如实告知"未查到",绝不可臆测。这是把通用大模型改造成"可信知识库助手"的灵魂条款。
5.4 多模型编排与降级
KBCP_LLM_Provider抽象了 Ollama(本地)/ DeepSeek / 智谱 GLM,统一 OpenAI 兼容接口。推理型本地模型(如 deepseek-r1)不支持 function calling,会被自动过滤;云模型按配置优先级串联,上一个不可用自动降级下一个,全部失败时还有本地确定性兜底(直接调 SQL 回答基础问题)。
而且我测试了一个不算是RAG的问题,是一个纯粹的语义理解问题,这个回答也很赞。我感觉这个是纯LLM的回答。有这样的兜底,那就非常棒了。
六、能力全景与落地权衡
把整套 AI 应用层的能力与适用场景汇总如下:
| 能力 | 技术路线 | 何时走这条路 | 优势 |
|---|---|---|---|
| 实体消歧 | 规则哈希索引 | 所有问题第一关 | 毫秒级、零成本 |
| 查询分类 | 规则引擎 | 所有问题第二关 | 可复现、不抖动 |
| 精确计数 | COUNT(*) | “多少首” | 100% 准确、秒回 |
| 结构化检索 | Text-to-SQL + 校验 | “对比/主题统计” | 可聚合、防幻觉 |
| 语义联想 | 向量检索(RAG) | “赏析/意境/推荐” | 跨字面语义匹配 |
| 综合推理 | Agent + 工具调用 | 复杂/多步问题 | LLM 推理 + 事实兜底 |
混合架构 vs 纯 RAG 的取舍:纯 RAG 实现简单,但面对"计数/对比/精确事实"会集体失灵;混合架构工程复杂度更高,却换来可引用、可解释、可追责的回答——这正是知识库区别于"聊天玩具"的本质。
📝 七、小结
| 设计目标 | 实现方式 |
|---|---|
| 确定性 | SQL 计数快路径 + Text-to-SQL 只读执行 + 字段白名单 |
| 语义性 | 多语言 Embedding + 多源 RAG 检索 + 余弦相似度推荐 |
| 智能性 | Agent 中枢 + 7 个结构化工具 + 多步/多轮推理 |
| 可控性 | 别名消歧 + 规则分类 + 反幻觉铁律 + 模型自动降级 |
| 可落地 | Ollama/DeepSeek/智谱多后端 + TF-IDF 纯本地兜底 |
至此,中华诗词知识库系列六章全部完成:
第1章 数据收集 → 4600+ 诗人 / 62000+ 诗词的原始积累 第2章 数据清洗 → 分句、去重、元数据补全、入库 SQLite 第3章 数据结构设计 → 五表联动 + 受控词表,标准化与可扩展 第4章 Web 与 AI 打标 → Flask 系统 + LLM 多维自动标注 第5章 Web 实现与实例 → 四栏浏览、后台管理、智能问答落地 第6章 人工智能的应用 → 混合智能:消歧 / 分类 / Text-to-SQL / RAG / Agent从"抓数据"到"会对话",我们走完了一条数据工程 → 知识工程 → 智能应用的完整路径。知识库的价值,不在于囤了多少文本,而在于当有人提问时,它能给出可引用、可解释、可信赖的答案——这就是人工智能在这套系统里真正的落点。
🌟如果本文对你有帮助,欢迎 Star 支持!
GitHub 开源地址:https://github.com/liang1057/Knowledge-Base-of-Chinese-Poetry
CSDN 专栏:中华诗词知识库(KBCP)系列文章
📧 交流邮箱:liang1057@163.com
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。