☰
RAG服装推荐实战:从知识库搭建到Agentic检索本地部署
2026/10/1 10:48:16 网站建设 项目流程

最初决定把 RAG 用在做服装推荐上,是因为我实在受够了传统电商搜索的物理学家式回答。你输入"冬天上班穿的,不要太正式但也有质感的外套",系统只会做关键词拆解,把"冬天""上班""外套"三个词拿去做字面匹配,然后在标题里同时含这三个词的商品中按销量排序给你。至于"不要太正式""有质感"这种真正的意图,传统推荐系统完全无感。而 RAG 的核心思路其实很适合这个场景:把商品知识拆成可检索的文本块,先召回再让大模型综合理解,最后生成一个既符合用户模糊描述又能解释为什么的推荐结果。这篇文章就是我基于这个想法从零搭起一个服装推荐 RAG 项目的完整记录,包含数据切分、本体设计、Agentic 化改造、本地部署和一堆实测踩坑,适合想入门 RAG 又不想只做"文档问答 Demo"的人参考。

1. 为什么服装推荐需要 RAG:传统推荐系统的三个死穴

1.1 关键词匹配处理不了组合语义

服装推荐里最难的不是数据少,而是用户表达的需求天然是组合式、模糊式的。同样是"通勤穿",在互联网公司上班和在外企坐前台,对衣服的要求完全不同;同样是"显瘦",小个子和大骨架人群理解得也不一样。传统搜索用倒排索引做字面匹配,能处理"白色 衬衫"这种词,但处理不了"适合面试的白色衬衫"和"适合约会的白色衬衫"之间的细微差别——这两个查询在字面上高度重合,但检索意图南辕北辙。

RAG 的逻辑不一样。它先把每个商品、每篇穿搭文章变成向量化表示,用户查询的时候也是把整句自然语言变成向量,在语义空间里找邻居。这样"适合面试的白色衬衫"会被优先召回那些提到"面试""正式场合""通勤""商务"的文本块,而不是简单撞词的商品。我在项目里测过,同样一组商品数据,传统 BM25 搜索和向量检索对同一条模糊查询的结果重合度不到 40%,向量检索对意图的把握明显更好。

1.2 推荐系统的核心其实是知识资产

很多团队做推荐只盯着点击率、转化率,却忽略了一个事实:服装推荐本质上是一个知识密集型任务。什么场合配什么风格、什么版型适合什么身材、什么面料适合什么季节,这些知识散落在商品详情页、穿搭文章、用户评价里。传统推荐系统要么把这些知识人工抽成标签,要么干脆不管,靠协同过滤让用户行为代替知识。

RAG 项目让我重新理解了这件事:推荐系统的竞争力,很大程度取决于你把这些知识组织成了什么形态。同样一批商品,如果只是结构化字段,那查询就只能走属性过滤,用户说"有点正式又不想太呆板"就完全没辙;如果把穿搭师写的内容、商品详情里的描述细节、甚至面料科普都拆成文本块放进向量库,大模型就有足够素材来理解模糊措辞。我搭这个项目时,特意把 200 篇穿搭文章和 1000 件商品描述混在一个知识库里,效果比只放商品结构化数据好得多。

1.3 服装 RAG 推荐助手的最小可运行形态

如果你也想复现这个项目,先建立正确的心智模型。整个系统的核心只有四个部分:

  • 知识库:承载服装知识的文本块集合,来源包括商品描述、穿搭文章、面料百科、用户常见问答;
  • 嵌入模型:把文本变成向量,中文场景推荐 bge-m3、m3e 这类模型;
  • 向量检索:根据用户查询召回最相关的 TopK 知识块,可以混入BM25关键词召回;
  • 大模型生成:把召回的知识块和用户原始问题拼进 Prompt,让模型生成推荐意见和解释理由。

我最初用 Python + LangChain 写,后来为了给团队 Java 栈复用,又用 LangChain4j 重写了一遍核心链路。LangChain4j 的 easy-rag 模块对初学者特别友好,几条配置就能跑通。别纠结框架选型,先把这条链路上的每一环跑明白,后面再谈优化。

2. 服装知识库搭建:切分、本体与向量化的完整链路

2.1 服装数据的特殊难点:属性密度高、语义粒度细

服装文本和通用文档最大的区别是属性密度极高。一件衬衫的描述里可能同时包含"面料成分""版型廓形""领口设计""适合场合""搭配建议""尺码说明"六类信息,而且这些信息是揉在一段话里的。如果用通用拆解工具按固定字数切,很容易把"版型修身"和"适合人群"切到两个 Chunk 里去,检索时永远拿不到完整上下文。

我的做法是分两层处理。第一层按商品粒度强制切分,每个商品一个独立单元,不跨商品合并文本;第二层在商品单元内部,按语义块做二次分段,比如把"设计亮点""面料与工艺""穿搭场景""尺码与版型"拆成字段级子块。这样做的好处是,用户问"这件衣服会不会显胖"时,检索能精确命中"版型与穿着效果"这个子块,而不是整篇详情页一股脑塞给大模型。

2.2 三个切分粒度:单品、场景搭配、知识问答

我建议任何服装 RAG 项目至少维护三个切分粒度的知识库,它们服务的查询类型完全不同。

第一个粒度是单品知识。每个商品一条记录,包含标题、属性、详情描述。服务的是"我要一件什么风格的外套"这类直接需求。

第二个粒度是场景搭配知识。一段文字描述一个完整的穿搭方案,比如"初秋通勤:西装外套 + 直筒西裤 + 乐福鞋"。服务的是"下周要去新公司报到,穿什么"这种需要组合推理的需求。

第三个粒度是知识问答。把"小个子怎么选大衣长度""羊毛衫怎么洗不变形"这类 FAQ 拆成问答对。用户问"羊毛大衣和羊绒大衣什么区别"时,千万不要去检索商品详情页,这类知识必须单独成库。

三个库共用同一个向量索引,但每个文本块开头都加了一个类型元数据标签(场景/单品/问答),检索之后先按标签过滤,再决定要不要混合排序。这是让服装 RAG 从"能跑"到"好用"的关键一步。

2.3 本体 RAG:把风格、场合、版型变成显式关系

纯向量检索的问题在于,它只懂"语义相似",不懂"逻辑关系"。用户说"想要通勤风",模型能查到"通勤"相关的文章,但如果遇到一条文本里通篇只写"适合都市白领"却从没出现"通勤"这个词,向量检索就可能漏检。这时就需要引入本体(Ontology)思路。

我在项目里手工定义了一个轻量级的服装本体模型,核心是四组关系:

  • 风格类目:通勤风 - 休闲风 - 街头风 - 甜美风 - 极简风,不同风格之间定义相关性;
  • 场合映射:办公、约会、运动、旅行、婚礼,每个场合关联可接受的风格列表;
  • 版型与身材适配:小个子、梨形、苹果型分别适配什么版型;
  • 单品互斥与互补:比如"厚底鞋"和"宽松拖地裤"是高风险搭配,而"修身西装"和"阔腿裤"是互补关系。

具体实现上,我在每个文本块的元数据里维护一组 RDF-style 三元组,比如(单品ID, 适合场合, 正式商务)。检索时先用向量召回一个候选集,再做一个本体关系扩展——把候选集里单品的关联单品、适配身材、互斥关系一并抓出来,重新排序后交给大模型。这个过程不需要额外部署图谱数据库,用内存图结构或者 NetworkX 就行。

2.4 Embedding 选型与向量库选择的实测记录

中文服装文本的 Embedding,我会直接给出结论:先用bge-m3或者m3e-base,不要一上来就追最新的英文模型。英文模型在中文服装俗语上表现很飘,尤其是"显瘦""遮胯""直角肩"这类词,很多模型完全抓不住语义。我用bge-m3在自建的 500 条服装查询集上测过,语义命中率明显比text-embedding-ada-002高。

向量库方面,本地项目直接上 Chroma 就够。Chroma 支持简单的元数据过滤,对上面说的标签预筛选完全够用。如果数据量到十万级以上,再考虑 Qdrant 或 Milvus。我是先用 Chroma 跑通,后面发现搭配类查询越来越多,才迁到 Qdrant 上加了 payload 索引。迁移成本不高,因为向量库的抽象层用 LangChain 的vector store接口隔离了,换了实现也不改上层代码。

3. 从普通 RAG 到 Agentic RAG:让服装推荐会追问

3.1 单轮 RAG 在服装推荐里的局限

第一版系统我做的很天真:用户输入一句话,检索 TopK 文本块,拼 Prompt,生成推荐。上线测了两天发现一个问题——用户根本不会把需求一次性说清楚。真实对话是这样的:"帮我推荐个外套""什么场合穿""上班穿""公司有 dress code 吗""不太严格但要体面""预算呢""一千五以内吧"。如果系统只在第一句话做完检索和生成,后面的追问信息就全浪费了。

这就是 RAG 项目里越来越被强调的 Agentic 化改造:检索不是一次性动作,而是可以多轮、可编程、可决策的。热词里那个agentscope 2.0的"RAG as Service"思路也是这个方向,让检索从固定步骤变成模型可调用的工具。

3.2 意图识别与属性补全:先把用户的话翻译成检索条件

我在第二版系统里引入了一个轻量级意图识别层,不是分类模型,而是用 LLM 做一次"查询改写"。用户输入进来,先让模型抽取出结构化的检索条件:

  • 场合:办公/约会/运动/日常
  • 风格:通勤/休闲/街头/极简
  • 版型偏好:宽松/修身/oversize
  • 价格区间:数值范围
  • 特殊修饰:显瘦、遮胯、高级感、不显老

这个阶段不给模型任何检索结果,只让他做"翻译"。翻译出来的结构化字段同时用于两件事:一是生成检索式(比如"通勤 外套 显瘦 1500以内"再做向量检索),二是调用规则过滤接口把不符合属性的商品直接排除。实测下来,多这个一步,推荐结果在"用户会不会点进去看"这个指标上提升了 30% 以上。原因很简单:向量检索容易把"风格接近但场合不符"的商品召回来,属性补全把这类噪声在源头卡掉了。

3.3 工具调用架构:把向量检索、规则过滤、搭配库串起来

Agentic RAG 落地时,我不是把所有检索逻辑塞进一个 ReAct Agent 里,而是拆成几个独立工具。核心工具只有三个。

search_knowledge_base(query, filters):向量检索 + 元数据过滤,返回知识块列表,带来源标签。这个工具负责"理解语义"。

filter_catalog(attributes):查询结构化商品库,按场合/价格/风格/版型做精确过滤。这个工具负责"保证准确"。

get_outfit_combo(occasion):调场景搭配知识库,返回预设的成套穿搭方案。这个工具负责"提供组合思路"。

Agent 的决策流程很自然:先用filter_catalog圈定候选池,再用search_knowledge_base补知识细节,如果用户明确说到场合,直接拉get_outfit_combo给出成套方案。这套设计比让 Agent 自由调用十几二十个工具稳定得多。工具越多,模型选错工具的概率越高,我宁愿用三个高内聚工具加一个查询改写层,也不愿堆功能面面俱到的十个小工具。

如果你在 Java 技术栈,LangChain4j 的ToolService支持注解定义工具和方法签名,调用链路非常清晰,同样的三工具架构很容易复刻,这也是热词里langchain4j easy rag能火的原因——它把 RAG 的复杂链路框架化,你只需要往框架里填数据源和工具逻辑。

4. 零基础本地 RAG 复现:Ollama 与简易知识库全流程

4.1 为什么要先跑本地而不是直接调云端 API

服装推荐这个场景里,商品信息、用户需求描述都算得上敏感业务数据,直接送第三方大模型 API,很多公司法务这关就过不去;另外本地跑还意味着你可以随便调 Prompt、随便改切分策略,不用心疼 token 费用。热词里ollama + 简易本地 rag 知识库【零基础可复制教程】搜索量那么高,说明大家都有同样的诉求。

我给自己的要求是:整套环境在 16G 内存的普通笔记本上能跑。实践下来完全没问题,就是响应速度会慢一点,做原型验证足够了。

4.2 本地部署的具体步骤与配置清单

我按自己的实际操作整理了一份可直接执行的清单,环境是 macOS,Windows 的差异我会单独标注。

第一步,安装 Ollama,并拉取两个模型:一个是生成模型,我用的qwen2.5:7b-instruct,中文理解力在 7B 级别里表现很稳;另一个是嵌入模型,用bge-m3。命令分别执行:

ollama pull qwen2.5:7b-instruct ollama pull bge-m3

bge-m3支持 8192 的上下文长度,用来做长商品描述的向量化很合适。如果你的机器显存吃紧,嵌入模型也可以换更小的nomic-embed-text,但中文效果会打折扣。

第二步,准备 Knowledge Base。我直接把 1000 件商品的详情文本和 200 篇穿搭文章丢了进去,切分用 LangChain 的RecursiveCharacterTextSplitter,参数是chunk_size=400、chunk_overlap=80。注意这个切分是粗略的,真正上线前一定要按我在第 2 小节说的语义块逻辑再过一遍。

第三步,初始化向量库并写入数据:

from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings embeddings = OllamaEmbeddings(model="bge-m3") vectorstore = Chroma.from_documents( documents=splitted_docs, embedding=embeddings, persist_directory="./clothing_rag_db", )

第四步,写检索加生成的代码。这一步我用 LangChain4j 的 Java 版本更顺手,但 Python 版本逻辑完全一样:

from langchain_community.chat_models import ChatOllama from langchain_core.prompts import ChatPromptTemplate llm = ChatOllama(model="qwen2.5:7b-instruct") retriever = vectorstore.as_retriever(search_kwargs={"k": 5, "filter": {"type": "item"}}) prompt = ChatPromptTemplate.from_template( "你是服装搭配顾问。基于以下知识库内容,回答用户问题。如果知识库不足,请直接说不知道。\n知识:{context}\n用户:{question}" )

第五步,把对话接口包成一个简单的 Python 服务,我用的 FastAPI,一个/recommend端点接收用户描述,内部走"查询改写-过滤-检索-生成"这条链。到这里,一个本地服装 RAG 的 MVP 就跑通了。

4.3 检索命中率(Hit Rate)怎么测、怎么调

很多初学者跑通了 Demo 就开始沾沾自喜,但一遇到真实查询就露馅。我强烈建议你在优化之前,先给你的检索写一个带标准答案的评测集。热词里rag hit rate冲上热搜,说明大家都在这里栽过跟头。

Hit Rate 的定义很简单:在 N 条测试查询里,标准答案对应的文档是否出现在检索结果的前 K 条中,统计命中比例。我准备了 100 条"用户需求描述 + 期望召回商品 ID"的测试集,每一条都对应一件确切的商品。比如"适合小个子的通勤大衣"对应商品 02417,跑完检索看这条商品在不在 top-5 结果里。

我的调优顺序是固定的:

  • 先调 K:从 3 递增到 10,观察 Hit Rate 曲线,K=5 和 K=8 的差距如果超过 10%,说明召回质量不够,先回去调切分;
  • 再调切分:把 chunk_size 从 400 调到 800 或 200,看哪组命中率更高,服装文本我最后定格在 500 左右;
  • 最后调混合检索:用 BM25 和向量检索各召回一批,做 RRF 融合排序,对于"型""款"这类属性词,BM25 命中率经常比向量检索高。

我在这个项目里,纯向量检索的 Hit Rate@5 大约 68%,改成 BM25 + 向量 + 本体扩展三路融合之后,Hit Rate@5 到了 84%。这个数据说明,服装 RAG 里单纯堆向量不如把几路召回混合起来。

5. 服装 RAG 的典型瓶颈:假命中、图片商品与知识割裂

5.1 TopK 召回里的"假命中":语义相似不等于推荐合适

服装领域最容易踩的坑是"语义相似但场景不搭"。用户说"想买件显瘦的裙子",向量检索召回了"显瘦的穿搭技巧"、"如何穿出显瘦效果"、"显瘦连衣裙推荐"三组文本,从语义上完全合理,但前两条根本不是商品知识。大模型拿到这种上下文,生成出来的答案自然是"看起来专业,实际上无法执行"。

解决假命中有两个有效手段。第一个是元数据隔离,我在第 2 小节提到的"单品/场景/问答"三分类,在检索后增加一道硬过滤,用户要商品推荐,就只允许type=item的文本块进入上下文。第二个手段是加 Reranker 重排,用bge-reranker-base对向量召回的 Top20 做精排,重排模型能看到查询和候选文本的完整语义,对"伪相关"的惩罚非常明显。我接入 Reranker 之后,最终推荐结果里"不可用推荐"的比例砍掉了近一半。

5.2 图片商品能不能进知识库:多模态与纯文本两条路线

很多做服装的都会问一句:RAG 知识库能存图片吗?答案是能,但要看怎么存。

存图片本身没有任何技术障碍,把图片文件路径塞进数据库就行。真正的问题是检索。你拿一段用户文本去匹配图片,传统向量模型做不了跨模态的语义对齐。要让图片可检索,至少要在多模态 Embedding 模型(比如 CLIP)把图片转成特征向量,然后和文本向量的语义空间对齐,才能实现"外貌"-"衣服"的跨模态召回。

我在项目里的实际方案是"先转文本再入库",因为 1000 件商品本来就有详情页描述文本,图片只做展示不做检索源。如果有的商品只有图片没有文案,就用一个本地视觉模型(我用的 Qwen2-VL 系列)先把图片生成一段描述文字,再走常规文本入库。这笔账算下来,比全局部署一套多模态向量库省很多显卡资源。

5.3 文本拆解工具的选择:自带分割器还是自定义

很多人搜过"有没有本地的 RAG 文本拆解工具",说明大家都对 LangChain 默认的字符切分效果不满。字符切分在服装文本上的问题很典型:一段 800 字的商品描述里,"前两段讲设计灵感,后两段讲面料参数",字符切分很可能在第三段中间断开,导致两个 Chunk 里都没有完整的"面料"信息。

我最终的拆解方案是半自动的:

  • 通用文本(穿搭文章、面料百科)用 LangChain 的RecursiveCharacterTextSplitter按标题层级切;
  • 商品详情页走了BeautifulSoup按 HTML 结构块解析,逐个h2/p标签切,保证信息完整性;
  • 高频问答整理成固定格式的 QA 对,不切分,每条记录一个单元。

如果你连正则都不想写,LangChain4j easy-rag里也封装了基于文档结构的拆解器,Java 生态可以直接拿来用。但我的体会是,拆解这种活儿没有银弹,最粗暴高效的永远是"读一遍自己的数据再决定怎么切"。每类数据的结构不同,强行统一切分参数,最后一定有一批 Chunk 是碎的。

5.4 知识割裂问题:GraphRAG 与本体关系的补充思路

"解决了知识割裂 rag"能成为热词,说明大家在实际项目中都撞过这堵墙。纯向量检索的每一个文本块都是独立的,它看不到"这件西装外套"和"去年那篇教你怎么搭西装的推文"之间其实是同一套装扮里的配套关系。数据规模小的时候靠 Reranker 能兜底,规模一大,割裂感会吞掉推荐的可信度。

我在项目里做的折中方案不复杂。用第 2 小节定义的本体关系搭了一个轻量级的图结构,节点是单品和知识文章,边是"搭配""互补""同风格""互斥"四类关系。检索流程变成:先向量召回候选集,再对候选集里的每个节点做一跳或两跳的图扩展,把关联节点一起塞进 Prompt。效果最明显的是搭配类查询——用户问"买了件卡其色风衣怎么搭",纯向量可能只召回风衣详情页,加图扩展之后,和它共现过三次的白色衬衫、直筒牛仔裤都会被带出来,推荐完整度高了不止一个档次。

这就是 GraphRAG 思路的极简落地版。不需要搞复杂的图索引算法,直接把关系网叠在向量检索之上,工程成本不高,但对"知识割裂"的缓解立竿见影。


最后再分享一个我自己的感受:RAG 项目的难度从来不在框架和技术选型,而在数据组织。你花一个晚上能跑通 Ollama + Chroma + LangChain 的 Demo,但要让推荐结果让真人觉得"懂我",前面 70% 的精力都要砸在切分、本体、评测集这些看着不起眼的活儿上。我踩过最深的坑就是过早优化 Prompt,后来发现 Prompt 调得再好,检索回来的上下文是垃圾,生成结果一样是垃圾。先把 Hit Rate 指标测明白,把知识库的组织结构打磨清楚,再去研究花哨的 Agent 编排,路会顺很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询