当古诗词遇上 AI:从 38 万句诗词中取一个好名字
用 FAISS 向量检索 + DeepSeek 大模型,把八字五行、音律五格和诗词意境串成一条自动化取名流水线。
缘起
给新生儿取名这件事,对很多新手父母来说堪比一场「信息战」------要查八字五行、翻诗词典故、算五格吉凶、排除谐音梗,最后还得在家族群里投票。每一项都不难,但叠在一起就变成了一项耗时数周的系统工程。
这个项目试图用 AI 把整个流程自动化:输入姓氏、性别和出生时辰,系统会从 38 万句古诗词中检索灵感,结合八字五行约束,调用大语言模型生成名字,再经过音律评分、五格校验和谐音过滤,最终返回 5 个兼顾文化底蕴和数理吉凶的候选名。
整个系统可以拆成三层来理解:数据层 (诗词预处理 + 向量索引 + 康熙字典)、服务层 (八字计算 → 五行分析 → 向量检索 → LLM 生成 → 多维过滤 → 综合排序)、接入层(Web H5、微信/支付宝小程序)。

架构全景
先看整体架构。系统采用经典的分层设计,从上到下依次是客户端、网关、API 路由、中间件链、核心服务、数据存储和外部 AI 服务:

接入层支持 Vue 3 H5 页面和微信/支付宝小程序,所有请求统一经过 Nginx 反代进入 FastAPI 后端。
中间件链是第一个有意思的设计。每个请求在到达业务逻辑之前,要依次通过四道关卡:反爬虫校验(UA 黑名单 + HMAC 签名)、邀请码验证、IP 速率限制(滑动窗口 + 每日配额)、免费额度检查。这个链式结构让安全策略和业务逻辑完全解耦,任何一层的规则变更都不需要改动其他部分。
核心服务层是取名流水线的主战场,下面重点展开。
取名流水线:从出生时辰到好名字

上图展示了一个名字从请求到返回的完整旅程。整个流程可以分为四个阶段:命理分析 、诗词检索 、LLM 生成 、质量过滤。
阶段一:命理分析 ------ 搞清楚五行缺什么
用户提交出生年月日和时辰后,系统首先用寿星天文历(sxtwl)排出八字四柱,然后分析五行强弱,确定「喜用神」。
这一步的关键在于五行强弱的量化判定。系统不是简单地数个数,而是综合月令、长生十二宫和天地干得分来计算一个综合分数:
python
# Mock: 五行强弱判定核心逻辑
def analyze_wuxing_strength(bazi_chart: BaziChart) -> WuxingResult:
day_master = bazi_chart.day_master_stem # 日主天干
month_order_score = get_month_shishen_score(day_master, bazi_chart)
changsheng_score = get_changsheng_score(day_master, bazi_chart)
ground_score = sum(score_stem(stem) for stem in bazi_chart.dizhi_stems)
stem_score = sum(score_stem(stem) for stem in bazi_chart.tiangan_stems)
total = month_order_score + changsheng_score + ground_score + stem_score
if total >= 3.0:
verdict = "偏旺" # 日主强,喜泄耗
elif total > -2.5:
verdict = "中和" # 平衡,喜本气
else:
verdict = "偏弱" # 日主弱,喜生扶
xi_shen = derive_favorable_element(verdict, day_master.wuxing)
return WuxingResult(verdict=verdict, xi=xi_shen, ji=opposite(xi_shen))
这里有个容易踩的坑:晚子时 (23:00-24:00)的处理。传统八字学认为晚子时属于第二天,需要将日柱和时柱推进到下一天。很多八字排盘程序在这里出错,系统用 sxtwl 的 isLateZiHour 标志做了专门处理。
阶段二:诗词检索 ------ 从 38 万句中找灵感
这是整个系统最有技术含量的一环。
数据准备阶段 ,原始诗词数据来自 chinese-poetry 开源项目,涵盖诗经、楚辞、全唐诗、宋词和四书五经。预处理管线做了三件事:繁体转简体(OpenCC)、按标点断句、过滤负面关键词(悼、亡、死、悲、愁等 30 多个字)。最终得到约 38 万条干净诗句,存入 processed_poems.json。
向量化阶段 ,每条诗句调用智谱 AI 的 Embedding-3 模型生成 2048 维向量,写入 FAISS 索引。为了在有限内存下跑起来,索引使用了 IndexScalarQuantizer + QT_8bit 量化,配合 IO_FLAG_MMAP 做内存映射,比 float32 方案节省约 75% 内存,且不需要把整个索引加载到 RAM。
python
# Mock: 向量检索 + 多样性采样
def search_poetry(surname, gender, wuxing_keywords, zodiac_keywords):
# 1. 构建复合查询:姓氏 + 性别 + 五行意象 + 生肖宜忌
query = f"{surname} {gender} {' '.join(wuxing_keywords)} {' '.join(zodiac_keywords)}"
embedding = zhipu_embedding(query) # 2048-dim vector
# 2. FAISS 检索 top_k * 2 条候选
candidates = faiss_index.search(embedding, top_k=100)
# 3. 分层采样,避免唐诗因语料量大而过度代表
quotas = {"诗经": 12, "楚辞": 8, "唐诗": 18, "宋词": 12, "其他": 10}
sampled = stratified_sample(candidates, quotas)
return sampled # ~50 条,来源均衡
多样性采样是这里的设计亮点。如果直接取 FAISS 的 Top-K 结果,由于唐诗数量远超其他朝代,检索到的诗句会严重偏向唐诗。系统采用分层配额采样,确保诗经、楚辞、唐诗、宋词各有代表,让 LLM 看到风格多元的灵感素材。
阶段三:LLM 生成 ------ 大模型登场
拿到诗词素材后,naming_engine 模块开始组装 Prompt。这个 Prompt 相当「重」------它需要把八字分析结论、五行宜忌(附带示例字)、生肖取名规则、音律约束(姓氏的声母和声调信息)、以及采样到的诗句全部注入进去。
python
# Mock: Prompt 核心结构
prompt = f"""
你是一位精通古诗词和姓名学的取名专家。
## 基本信息
姓氏:{surname}(声母:{shengmu},声调:{tone})
性别:{gender}
## 五行分析
{bazi_analysis}
喜用神:{xi_shen},宜用五行属性为「{xi_shen}」的字。
## 音律约束
姓氏声母为「{shengmu}」,名字中避免使用相同或相近声母。
姓氏声调为第{tone}声,名字应有声调变化。
## 诗词素材
{sampled_poems}
请根据以上约束,生成 6 个名字,每个名字需注明诗词出处。
"""
系统通过 OpenAI 兼容接口调用 DeepSeek-V4 Flash(主力)或 Mimo V2.5 Pro。支持 SSE 流式输出,使用 <<NAME>> 分隔符协议而非增量 JSON 解析,让流式场景下的名字逐个呈现更加稳健。

阶段四:质量过滤 ------ 五道关卡
LLM 生成的名字不能直接用,还要经过五道过滤关卡:
第一关:五格吉凶。 根据康熙字典笔画数计算天格、人格、地格、外格、总格,对照 81 数理吉凶表。30 个「大凶」数理直接淘汰。
第二关:禁用字和谐音。 检查名字中是否包含禁用字,以及是否存在不雅的谐音联想。
第三关:生僻字。 排除用户可能不认识、输入法打不出的字。
第四关:音律评分。 基于 pypinyin 检查五个维度------声母重复、声调单调、声母相近、韵母雷同等,每项违规扣分,低于 60 分淘汰。
python
# Mock: 音律评分核心规则
def check_phonetics(surname, char1, char2):
score = 100
s_sm, c1_sm, c2_sm = get_shengmu(surname), get_shengmu(char1), get_shengmu(char2)
s_tone, c1_tone, c2_tone = get_tone(surname), get_tone(char1), get_tone(char2)
if s_sm == c1_sm: score -= 20 # 姓与名同声母
if c1_sm == c2_sm: score -= 15 # 两字同声母
if s_tone == c1_tone == c2_tone:
score -= 25 # 三字同声调
if are_similar_groups(s_sm, c1_sm):
score -= 10 # 相邻声母组
if get_yunmu(char1) == get_yunmu(char2):
score -= 10 # 同韵母
return score # < 60 则淘汰
第五关:五行加分。 对通过前面所有关卡的名字,根据字的五行属性给予加分。这里用了三级回退策略:硬编码字典 → 康熙字典查询 → 偏旁部首推断,最大化覆盖率。
最终的综合排序公式是:
总分 = 音律 × 0.4 + 五格 × 0.3 + 诗词来源 × 0.2 + 五行匹配 × 0.1
音律权重最高(40%),体现了系统的设计理念:一个好名字首先得好听。诗词来源的时代权重也有讲究------先秦诗词(诗经、楚辞)得分 100,唐代 80,宋代 70,鼓励系统优先引用更古老的经典。

几个值得聊的技术选型
为什么用 FAISS 而不是向量数据库? 这个项目的数据规模(38 万条 × 2048 维)其实不大,FAISS 配合 8-bit 量化后单机就能跑,不需要额外部署 Milvus 或 Pinecone 这样的分布式向量数据库。IO_FLAG_MMAP 让索引文件常驻磁盘按需加载,对内存友好。对于中小规模的 RAG 场景,FAISS 仍然是性价比最高的选择。
为什么用 SSE 而不是 WebSocket? 取名场景是典型的「服务端单向推送」------用户发一次请求,服务端陆续返回多个名字。SSE 天然适合这种单向流,实现简单且对 HTTP 代理友好(只需设置 X-Accel-Buffering: no)。WebSocket 的双工能力在这里是多余的。
为什么用分隔符协议而不是流式 JSON? LLM 的输出是逐 token 生成的,增量解析 JSON 既脆弱又复杂。系统让 LLM 用 <<NAME>> 分隔每个名字块,服务端按分隔符 split 后逐块解析,比尝试从流中拼凑合法 JSON 稳健得多。
会话缓存的设计。 系统一次让 LLM 生成 6 个名字,但只返回前 5 个。第 6 个(以及可能的更多溢出名字)缓存在内存中的 session_store 里,TTL 60 分钟。这支持了「换一批」的 UX 模式------用户不满意时可以快速获得备选,而不需要重新调用 LLM。
数据管线:从原始诗词到向量索引
整个数据准备过程由三个脚本串联完成:
scss
chinese-poetry/ (Git submodule, 原始数据)
│
▼ preprocess.py
processed_poems.json (38万句, ~64MB)
│
▼ vectorize.py
poetry.faiss + metadata.db (向量索引 + 元数据)
preprocess.py 的负面关键词过滤值得一提。系统维护了一个 30 多个字的黑名单(悼、亡、死、悲、愁、苦、泪......),任何包含这些字的诗句都会被丢弃。这确保了取名素材的情感基调是积极向上的------毕竟没人想用一个充满悲伤意象的名字。
vectorize.py 使用异步并发 + 信号量控制(asyncio.Semaphore(20))来加速 Embedding 生成,支持多 API Key 轮询和断点续传。每处理 5 个 batch 就 flush 一次到 FAISS 索引,避免内存中积累过多数据。
安全与运营
一个面向公众的 AI 服务不能没有安全防护。系统的中间件链实现了多层防御:
反爬虫层使用双重认证策略------Web 端走 Session Token(2 小时 TTL,绑定 IP),小程序端走 HMAC 签名。签名算法用了双哈希方案:hash(secret:timestamp:nonce) + hash(reverse(secret:timestamp:nonce)),增加了逆向难度。
速率限制层采用 IP 滑动窗口 + 每日配额 + 会话冷却的三重机制,默认配置是每 60 秒最多 5 次请求、每天最多 200 次 API 调用、同一会话两次请求间隔至少 30 秒。
免费额度层支持按天或按终身两种模式,可以灵活配置免费体验次数。
小结
这个项目的核心价值在于把「取名」这个看似感性的过程,拆解成了一条可量化、可追溯的工程流水线。八字五行不再是玄学黑箱,而是可以计算和验证的数值;诗词检索不再是随机翻阅,而是基于语义相似度的定向搜索;质量过滤不再是人工逐条检查,而是五道自动化关卡加综合评分。
从技术栈的角度看,FAISS + DeepSeek + pypinyin + sxtwl 的组合覆盖了向量检索、大模型生成、音律分析和历法计算四个维度,每个组件都各司其职。分层架构和中间件链让系统具备良好的可维护性,而多样性采样、分隔符协议、会话缓存这些细节设计则体现了工程实践中的务实思考。
如果你正在考虑构建一个类似的「传统文化 + AI」应用,希望这个项目的架构思路和技术选型能给你一些启发。
关于本项目
这套系统已落地为「文间拾字」,微信搜索即可体验。文章中的架构图和流程图均基于该项目的真实实现绘制。如果对技术方案有更多交流,欢迎在评论区讨论。