资讯动态

Sirchmunk:无需向量数据库的轻量级自进化搜索引擎架构解析

发布时间:2026/10/22 5:28:49 来源:尧图企业网站定制
1. 从零理解 Sirchmunk 的架构定位1.1 这个搜索引擎到底在解决什么问题第一次看到 Sirchmunk 这个项目的时候我脑子里冒出来的第一个疑问是现在向量数据库这么成熟为什么还要做一个“无需向量数据库”的搜索引擎这不是开倒车吗但仔细把它的设计思路捋了一遍之后我发现这个项目其实瞄准的是一个非常具体的痛点——中小规模知识库的检索场景下向量数据库带来的运维复杂度和资源开销往往超过了它带来的收益。你可以回忆一下自己搭建 RAG 应用的经历为了跑一个内部文档检索你得先部署 Milvus 或者 Qdrant然后搞 embedding 模型再处理 chunk 切分、维度对齐、索引重建这一整套流程。文档一更新还得重新 embedding 增量入库。这套东西对于动辄百万级文档的大厂来说没问题但对于一个只有几千到几万篇文档的团队知识库来说纯属杀鸡用牛刀。Sirchmunk 的核心主张就是用倒排索引加自进化排序机制在中小规模语料上做到接近语义检索的效果同时把部署成本压到几乎为零。它适合谁来参考我总结了三类人一是手头文档量在十万级以内、不想引入重型基础设施的独立开发者二是想理解搜索引擎底层排序逻辑、不想被向量数据库黑盒遮蔽视野的工程师三是在资源受限环境下比如边缘设备、单机部署需要做全文检索的技术人员。这个项目的价值不在于取代向量数据库而在于提供了一条更轻、更透明、可解释性更强的替代路径。1.2 为什么“无需向量数据库”是一个合理的设计选择要理解这个选择得先搞清楚向量数据库到底贵在哪里。向量检索的本质是把文本映射到高维空间然后做近似最近邻搜索ANN。它的优势是能捕捉语义相似性比如你搜“如何提升团队效率”它能召回“改善协作流程”这种字面不重叠但语义相近的文档。但代价是什么呢第一是存储成本。一篇 500 字的文档embedding 之后通常是一个 768 维或 1536 维的浮点向量按 float32 算就是 3KB 到 6KB。十万篇文档光向量就占 300MB 到 600MB再加上原始文本和索引存储直接翻倍。第二是计算成本每次查询都要把 query 也 embedding 一遍然后做向量相似度计算GPU 或高性能 CPU 基本跑不掉。第三是运维成本向量数据库的索引调优、分片策略、一致性保证每一项都是额外的学习曲线。Sirchmunk 的判断是在中小规模场景下用户查询和文档之间的语义鸿沟可以通过查询扩展和自进化排序来弥补而不必依赖稠密向量。具体来说它用稀疏检索BM25 类算法打底保证召回再用一套自进化的机制动态调整排序权重让系统随着使用不断优化结果。这个思路其实不新鲜经典的信息检索系统都是这么做的但 Sirchmunk 把“自进化”这个点做成了架构的核心特色这是它区别于传统倒排索引方案的关键。注意这里说的“无需向量数据库”不等于“完全不用向量”。Sirchmunk 在部分环节可能会用到轻量级的向量计算比如词嵌入做查询扩展但它不需要一个独立的向量数据库来存储和检索向量。这个区别很关键别理解偏了。1.3 自进化机制的核心价值在哪里“自进化”这个词听起来有点玄但拆开看其实很实在。传统搜索引擎的排序公式是固定的BM25 就是 BM25参数调好之后就不动了。但实际使用中用户的点击行为、停留时间、二次查询这些信号其实包含了大量关于“什么结果才是好结果”的信息。Sirchmunk 的自进化机制就是把这些隐式反馈收集起来动态调整排序权重和查询扩展策略。举个例子假设你搜“配置文件格式”系统一开始可能把讲 JSON 的文档排在前面。但如果用户每次都点开讲 YAML 的那篇系统就会逐渐学习到在这个查询下 YAML 相关文档应该获得更高权重。这种调整不是简单的点击率加权而是深入到词项权重层面——系统会记录哪些词项组合在实际使用中更有效然后更新倒排索引的查询扩展规则。这个机制的价值在于它让搜索引擎具备了“越用越懂你”的能力而不需要重新训练模型或重建索引。对于垂直领域的知识库来说这种自适应能力比通用的语义模型更实用因为垂直领域的语义往往有很强的特殊性通用 embedding 模型未必能捕捉到。2. 核心架构拆解与关键模块设计2.1 整体架构分层与数据流向Sirchmunk 的架构可以分成四层我从下往上捋一遍。最底层是存储层负责原始文档的持久化和倒排索引的维护。这一层没有用任何外部数据库就是基于文件系统的自定义存储格式索引文件按段segment组织支持增量写入和定期合并。这个设计参考了 Lucene 的段合并思路但做了大幅简化去掉了分布式相关的复杂性。往上一层是索引层包含分词器、倒排索引构建器和查询扩展模块。分词器支持中英文混合处理中文用的是基于词典的切分加统计补充英文走标准的分词加词干还原。倒排索引记录的是“词项 → 文档列表”的映射每个词项还附带位置信息和词频统计用于后续的短语查询和相关性打分。再往上是检索层这是整个系统的核心。它接收用户查询先做查询解析和扩展然后从倒排索引中召回候选文档再用排序模型打分。排序模型不是固定的而是由自进化模块动态调整的。自进化模块会收集用户反馈信号定期更新排序权重和查询扩展规则。最顶层是接口层提供 HTTP API 和命令行两种交互方式。API 设计得很简洁就几个端点搜索、反馈、索引管理。反馈端点接收用户的点击、停留等信号这些信号会异步写入反馈日志供自进化模块消费。整个数据流向是这样的用户查询 → 查询解析 → 查询扩展 → 倒排索引召回 → 排序打分 → 返回结果 → 用户反馈 → 反馈日志 → 自进化模块 → 更新排序权重和扩展规则 → 影响下一次查询。这个闭环就是“自进化”的物理实现。2.2 倒排索引的轻量化实现细节倒排索引是 Sirchmunk 的基石它的实现质量直接决定了检索性能。我仔细看了它的索引结构有几个设计点值得展开说。首先是词典的组织方式。Sirchmunk 没有用哈希表而是用了有限状态转换器FST来存储词项词典。FST 的好处是内存占用极小而且支持前缀查询和模糊匹配。十万级词项的词典用 FST 存储大概只占几 MB 内存而哈希表可能要几十 MB。这个选择对于单机部署场景非常关键因为内存是稀缺资源。其次是倒排列表的压缩。每个词项对应的文档 ID 列表Sirchmunk 用了差值编码加可变字节编码来压缩。简单说如果文档 ID 是递增的就只存差值差值小的时候用更少的字节。这个技巧在 Lucene 里也有用能把倒排列表的体积压到原始大小的三分之一左右。对于文档量不大的场景这个压缩率已经够用了。第三是段合并策略。新文档写入时先放在内存中的小段里等小段积累到一定数量再合并成一个大段。合并的时候会做去重和词项频率的重新统计。这个策略的取舍在于合并太频繁会浪费 CPU合并太少会导致查询时要扫描太多小段。Sirchmunk 默认的策略是内存中最多保留 8 个小段超过就触发合并。这个参数可以根据文档更新频率调整更新频繁就调小更新少就调大。实操心得如果你打算在自己的项目里复现这个索引结构我建议先用简单的哈希表加数组实现一版跑通了再换成 FST。FST 的构建和查询代码有一定复杂度直接上手容易卡住。另外段合并的触发条件不要设得太激进我试过设成 4 个小段就合并结果文档批量导入的时候 CPU 直接跑满查询延迟飙升。2.3 查询扩展与语义补偿机制“无需向量数据库”最大的挑战就是语义鸿沟。用户搜“怎么加快网页加载”文档里写的是“前端性能优化方案”字面完全不重叠纯倒排索引根本召不回来。Sirchmunk 的解法是查询扩展具体分三步走。第一步是同义词扩展。系统维护一个同义词词典查询时把原词替换成同义词组合。比如“加快”扩展成“加快 OR 加速 OR 提升”“网页”扩展成“网页 OR 页面 OR Web”。这个词典不是静态的自进化模块会根据用户反馈动态增删同义词条目。如果某个同义词扩展后用户点击率反而下降系统会降低它的权重甚至移除。第二步是基于词嵌入的近邻扩展。这里用到了一个轻量级的词向量模型但注意它不存储文档向量只用来做词级别的近邻查找。比如查询里的“加载”这个词通过词向量找到近邻词“载入”“读取”把这些词也加入查询。这个步骤的计算量很小因为只需要对查询词做近邻查找不涉及文档向量。第三步是基于反馈的查询重写。系统会记录历史上相似查询的点击文档如果当前查询和某个历史查询在词项上高度重叠就把历史查询中高点击文档的特征词加入当前查询。这个机制有点像“协同过滤”的思路但作用在查询词层面而不是文档层面。这三步组合起来能在不依赖稠密向量的情况下把语义召回率提升到一个可接受的水平。当然它肯定比不上专门的语义检索模型但在中小规模场景下这个 trade-off 是划算的。2.4 自进化排序模型的运作原理自进化排序是 Sirchmunk 最有意思的部分。它的核心是一个在线学习排序框架但做了大幅简化没有用复杂的神经网络而是基于线性加权模型加特征交叉。排序特征包括几类一是文本匹配特征比如 BM25 分数、词项覆盖度、短语匹配度二是文档质量特征比如文档长度、更新时间、历史点击率三是查询特征比如查询长度、查询词项数、查询扩展后的词项数。这些特征线性加权求和得到最终排序分数。自进化的关键在于权重的动态调整。系统维护一个权重向量初始值根据经验设定。每次用户查询后系统记录用户点击了哪些文档、在哪些文档上停留时间长、有没有翻页行为。这些信号被转换成训练样本用在线梯度下降更新权重向量。更新频率可以配置比如每 100 次查询更新一次或者每天定时更新。这个机制的巧妙之处在于特征交叉。单纯的线性加权表达能力有限但 Sirchmunk 引入了一些手工设计的交叉特征比如“BM25 分数 × 文档历史点击率”这样就能捕捉到“文本匹配度高且历史表现好的文档应该排更前”这种非线性关系。特征交叉的组合方式是固定的但权重是自进化的这样既保证了可解释性又保留了一定的表达能力。注意自进化机制有一个冷启动问题。系统刚上线时没有反馈数据权重只能靠默认值排序效果可能不理想。我的建议是先用一批人工标注的查询-文档对做初始化训练或者导入历史搜索日志做预热。另外反馈信号的噪声要控制比如停留时间太短小于 3 秒的点击可能是误点应该降权或过滤。3. 实操部署与核心环节实现3.1 环境准备与依赖安装Sirchmunk 的部署比我预想的要简单。它没有复杂的依赖链核心就是一个编译好的二进制文件加几个配置文件。我用的是 Linux 环境macOS 和 Windows 也支持但生产环境建议用 Linux。首先确认系统依赖。Sirchmunk 需要Rust 运行时如果从源码编译或者直接用预编译二进制。从源码编译的话需要安装 Rust 工具链和 Cargo。我实测下来编译大概需要 5 到 10 分钟取决于机器性能。预编译二进制就快多了下载解压就能用。# 从源码编译的步骤 git clone repo-url sirchmunk cd sirchmunk cargo build --release # 编译完成后二进制在 target/release/sirchmunk配置文件是一个 YAML 文件主要配置项包括索引路径、监听端口、分词器选项、自进化参数等。我建议第一次部署时把自进化相关的参数先注释掉用默认的静态排序跑通流程确认索引和检索没问题之后再开启自进化。# sirchmunk.yaml 核心配置示例 index: path: /data/sirchmunk/index segment_merge_threshold: 8 max_memory_segments: 4 server: host: 0.0.0.0 port: 8080 tokenizer: chinese_dict: /data/sirchmunk/dict/chinese.txt english_stemming: true evolution: enabled: true update_interval: 3600 # 每小时更新一次权重 learning_rate: 0.01 feedback_window: 86400 # 只使用最近24小时的反馈实操心得索引路径一定要放在 SSD 上机械硬盘的随机读写性能会严重拖慢检索速度。我试过把索引放在机械盘上查询延迟从 20ms 涨到了 200ms 以上。另外中文词典文件要提前准备好Sirchmunk 自带了一个基础词典但垂直领域需要自己补充专业术语否则分词效果会很差。3.2 文档导入与索引构建流程文档导入是第一个实操环节。Sirchmunk 支持多种格式纯文本、Markdown、HTML、PDF需要额外安装解析器。导入方式有两种命令行批量导入和 API 单篇导入。批量导入用命令行最方便sirchmunk index --path /data/documents --format auto --recursive这个命令会递归扫描目录下所有支持的文件自动识别格式然后构建索引。导入过程中会显示进度条和统计信息包括处理文档数、索引大小、耗时等。我实测下来一万篇平均 500 字的文档导入耗时大概 2 到 3 分钟索引大小约 50MB。这个体积控制得相当不错。单篇导入用 APIcurl -X POST http://localhost:8080/index \ -H Content-Type: application/json \ -d {id: doc001, title: 配置文件格式说明, content: 本文介绍...}API 导入适合增量更新场景比如新文档发布时实时入库。但要注意频繁的单篇导入会产生大量小段触发段合并的频率会变高。我的建议是批量导入用命令行增量更新用 API但增量更新积累到一定数量后手动触发一次段合并。段合并的命令是sirchmunk merge --force这个命令会强制合并所有小段释放内存并优化查询性能。我一般会在每天凌晨低峰期跑一次。3.3 查询接口调用与结果解析查询接口是使用频率最高的。Sirchmunk 的查询 API 设计得很简洁curl -X POST http://localhost:8080/search \ -H Content-Type: application/json \ -d {query: 配置文件格式, top_k: 10, explain: true}返回结果是一个 JSON 数组每个结果包含文档 ID、标题、摘要、排序分数和分数解释。explain参数打开后会返回每个排序特征的贡献值这对调试排序效果非常有用。{ results: [ { doc_id: doc001, title: 配置文件格式说明, snippet: 本文介绍常见的配置文件格式..., score: 12.5, explain: { bm25: 8.2, term_coverage: 2.1, doc_quality: 1.5, click_history: 0.7 } } ] }从 explain 里能清楚看到每个特征的贡献。如果发现某个特征的权重明显不合理比如 click_history 贡献过大导致新文档排不上去就可以调整自进化参数或者手动干预权重。注意top_k 不要设得太大一般 10 到 20 就够了。设太大不仅浪费计算资源还会让自进化模块的反馈信号变稀疏——用户只会点前几个结果后面的结果没有反馈权重更新会偏向头部文档。我试过 top_k 设 100结果自进化跑了一周排序效果反而下降了因为长尾文档的权重被过度压制。3.4 反馈信号采集与自进化触发反馈采集是自进化的燃料。Sirchmunk 提供了反馈 APIcurl -X POST http://localhost:8080/feedback \ -H Content-Type: application/json \ -d {query_id: q001, doc_id: doc001, action: click, dwell_time: 15.5}action 可以是 click、skip、like、dislike 等dwell_time 是停留时间秒。这些信号会写入反馈日志自进化模块定期消费。自进化的触发有两种方式定时触发和手动触发。定时触发在配置文件里设置 update_interval手动触发用命令sirchmunk evolve --force手动触发适合在批量导入新文档后立即更新权重。我一般会在导入重要文档集后手动跑一次让新文档的权重尽快得到调整。自进化更新完成后会输出一份报告显示权重变化情况和模型评估指标。我重点关注两个指标NDCG10和MRR。如果这两个指标在更新后下降说明反馈信号有噪声或者学习率设得太高需要回滚或调整参数。4. 常见问题排查与避坑指南4.1 检索结果不相关的排查思路这是最常见的问题。用户搜一个词返回的结果完全不沾边。排查要按顺序来别一上来就怀疑排序模型。第一步查分词。用sirchmunk analyze --query 你的查询词命令看分词结果。如果分词切错了比如把“配置文件”切成了“配置”和“文件”那召回结果肯定不对。中文分词对词典依赖很大垂直领域一定要补充专业词典。第二步查查询扩展。用--explain参数看查询被扩展成了哪些词。如果扩展词跑偏了比如“加载”扩展成了“下载”那召回结果就会混入不相关文档。同义词词典需要定期维护把错误的同义词删掉。第三步查排序权重。如果召回没问题但排序不对就看 explain 里的特征贡献。常见问题是某个特征权重过大比如 doc_quality 权重太高导致老文档永远排前面新文档沉底。这时候需要调整自进化参数降低该特征的初始权重或者提高学习率让它更快适应。第四步查反馈噪声。如果自进化跑了一段时间后效果变差很可能是反馈信号有噪声。检查反馈日志里有没有异常的 dwell_time比如全是 0 或者全是极大值过滤掉这些噪声再重新训练。4.2 索引性能下降的优化手段索引用久了会变慢这是正常现象。主要原因有三个小段太多、词典膨胀、反馈日志堆积。小段太多的问题用段合并解决前面说过了。词典膨胀是因为新词不断加入FST 虽然省内存但词项太多也会拖慢查询。解决办法是定期清理低频词项把出现次数少于阈值的词从词典里删掉。Sirchmunk 提供了sirchmunk prune --min-freq 3命令删除出现次数少于 3 的词项。这个操作要谨慎删掉的词项对应的文档就搜不到了所以阈值别设太高。反馈日志堆积会拖慢自进化模块的启动速度。解决办法是定期归档旧日志只保留最近一段时间的反馈用于训练。配置文件里的 feedback_window 参数就是干这个的默认 86400 秒一天可以根据需要调整。还有一个容易被忽略的点是文件系统碎片。索引文件频繁读写会产生碎片影响 IO 性能。Linux 下可以用e4defrag工具整理碎片或者干脆定期重建索引。重建索引的命令是sirchmunk reindex --path /data/documents耗时和首次导入差不多但能彻底解决碎片问题。4.3 自进化效果不稳定的应对策略自进化效果不稳定通常表现为排序结果忽好忽坏或者用了一段时间后效果反而下降。我踩过几次坑总结了几条经验。学习率别设太大。默认 0.01 是比较保守的值我试过设成 0.1结果权重震荡得厉害排序结果一天变好几次。如果你发现排序结果变化太频繁先把学习率降下来。反馈窗口别设太长。用太久之前的反馈数据训练会让模型“记不住”最近的用户偏好变化。我一般设 3 到 7 天既能保证样本量又能跟上偏好变化。定期做 A/B 测试。自进化更新后别直接全量上线先拿 10% 的流量做对比测试。如果新模型在测试流量上表现更好再全量。Sirchmunk 没有内置 A/B 测试功能但可以通过部署两个实例加流量分发来实现。保留回滚能力。每次自进化更新前备份当前的权重文件。如果更新后效果下降直接回滚。权重文件在索引目录下的evolution/weights.json备份很简单。4.4 常见问题速查表问题现象可能原因排查命令解决方法查询无结果分词错误或词典缺失sirchmunk analyze --query补充专业词典调整分词参数结果不相关查询扩展跑偏--explain查看扩展词维护同义词词典删除错误扩展新文档排不上排序权重偏向老文档查看 explain 特征贡献调整 doc_quality 权重提高学习率查询延迟高小段太多或磁盘碎片sirchmunk status段合并整理磁盘碎片自进化效果下降反馈噪声或学习率过高检查反馈日志过滤噪声降低学习率回滚权重内存占用高词典膨胀或段未合并sirchmunk status清理低频词项强制段合并导入速度慢磁盘 IO 瓶颈iostat查看磁盘负载换 SSD调整批量导入的批次大小实操心得这张表里的问题我基本都遇到过。最坑的是“新文档排不上”这个问题排查了半天才发现是 doc_quality 特征里包含了文档年龄老文档天然占优。后来我把文档年龄这个特征去掉换成“最近 7 天点击率”问题就解决了。所以特征设计的时候一定要想清楚每个特征背后的假设是什么会不会引入偏见。5. 架构扩展与二次开发建议5.1 从单机到分布式的演进路径Sirchmunk 目前是单机架构但它的模块化设计为分布式扩展留了口子。如果你手头的文档量增长到单机扛不住了可以考虑分片部署。最简单的分片方式是按文档 ID 哈希分片每个分片跑一个 Sirchmunk 实例查询时广播到所有分片然后合并结果。这个方案实现简单但查询延迟会随分片数线性增长。优化方案是按主题分片每个分片负责一个垂直领域查询时先路由到相关分片再检索。主题分片需要维护一个路由表可以用简单的关键词匹配或者轻量级分类器实现。再进一步是读写分离。索引构建和查询分开部署构建节点负责文档导入和段合并查询节点只读索引。这样查询节点可以水平扩展构建节点专心处理写入。Sirchmunk 的索引文件格式是只读友好的查询节点可以直接加载构建节点生成的索引文件。注意分布式化会引入一致性问题。索引更新后查询节点需要重新加载索引才能看到新文档。Sirchmunk 没有内置的索引同步机制需要自己实现。我的建议是用共享存储比如 NFS存放索引文件查询节点定期检查文件版本号有更新就重新加载。这个方案简单可靠但重新加载索引期间查询会短暂不可用需要做好流量切换。5.2 自定义分词器与领域适配Sirchmunk 的分词器是可插拔的你可以实现自己的分词逻辑。接口很简单输入是原始文本输出是词项列表。自定义分词器用 Rust 写编译成动态库然后在配置文件里指定路径。// 自定义分词器接口示例 pub trait Tokenizer { fn tokenize(self, text: str) - VecString; }垂直领域适配的关键是领域词典。比如医疗领域需要把“心肌梗死”作为一个词而不是切成“心肌”和“梗死”。法律领域需要识别“不可抗力”“善意取得”这类法律术语。领域词典的构建可以半自动化先用通用分词器跑一遍人工检查错误切分把需要合并的词加入词典。除了词典还可以调整词项权重。领域内的核心术语应该获得更高权重比如医疗领域里“诊断”“治疗”这些词的权重可以调高。Sirchmunk 支持在词典文件里指定词项权重格式是词项 权重默认权重是 1.0。5.3 与现有系统的集成方案Sirchmunk 作为一个独立的检索服务集成到现有系统主要有两种方式API 集成和嵌入式集成。API 集成最简单现有系统通过 HTTP 调用 Sirchmunk 的搜索接口拿到结果后自己渲染。这种方式解耦彻底Sirchmunk 可以独立部署和升级。缺点是网络开销每次查询都要走 HTTP延迟比嵌入式高几毫秒。嵌入式集成是把 Sirchmunk 作为库链接到现有系统里直接调用函数接口没有网络开销。这种方式适合对延迟敏感的场景比如实时搜索建议。但嵌入式集成会增加现有系统的编译复杂度而且 Sirchmunk 的升级需要重新编译整个系统。我的建议是后台管理类系统用 API 集成前端实时搜索用嵌入式集成。两者可以共存共用同一份索引文件。还有一个集成点是反馈采集。现有系统需要把用户的点击、停留等信号回传给 Sirchmunk才能驱动自进化。如果现有系统没有这些信号自进化就是无源之水。所以集成的时候一定要把反馈采集通道打通哪怕先只采集点击信号也比没有强。5.4 后续可以探索的优化方向Sirchmunk 的架构还有不少可以打磨的地方。我列几个我觉得值得尝试的方向。一是查询缓存。热门查询的结果可以缓存起来避免重复计算。缓存粒度可以是查询词级别的也可以是查询词加过滤条件级别的。缓存失效策略要设计好索引更新后相关缓存要清除。二是多字段检索。目前 Sirchmunk 主要检索文档正文标题和摘要的权重没有单独处理。可以增加字段级别的索引标题匹配给更高权重这样能提升排序准确性。三是个性化排序。自进化目前是全局的所有用户共享一套权重。如果用户群体差异大可以考虑做用户分群每个群一套权重。再进一步可以做用户级别的个性化但要注意隐私和冷启动问题。四是索引压缩的进一步优化。目前的差值编码加可变字节编码已经不错了但还可以尝试更激进的压缩算法比如 PForDelta 或者 SIMD-BP128。这些算法在学术界已经很成熟工程实现也不复杂能把索引体积再压 20% 到 30%。五是与轻量级语义模型的结合。虽然 Sirchmunk 主打无需向量数据库但可以引入一个轻量级的交叉编码器做重排序。只对 top 20 的结果做重排序计算量可控但能显著提升排序质量。这个方向我觉得最有潜力值得深入探索。我个人在实际操作中的体会是Sirchmunk 这套架构最大的价值不在于它现在有多完善而在于它提供了一个透明、可干预、可扩展的检索框架。你可以清楚地知道每个结果为什么被召回、为什么被排在这个位置也可以方便地调整每个环节的参数和逻辑。这种透明性在调试和优化的时候太重要了用向量数据库的时候你只能看到最终结果中间过程完全是黑盒。所以如果你正在做中小规模的知识库检索又不想被重型基础设施绑架Sirchmunk 的思路值得认真参考。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑