尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于RAG的A股智能选股:Ollama+Chroma+LangChain实战复盘

发布时间:2026/9/29 9:06:45

资讯中心
01
ARTICLE

基于RAG的A股智能选股:Ollama+Chroma+LangChain实战复盘

基于RAG的A股智能选股:Ollama+Chroma+LangChain实战复盘
1. 为什么我要用RAG给A股选股这件事加一层脑子先说结论单纯把行情数据丢给大模型做选股基本等于让一个从没看过财报的人去当基金经理。我最初也是这么干的把一堆K线数据、财务指标塞进prompt里让模型判断结果它给出的理由全是该股票近期表现良好建议关注这种正确的废话。问题出在哪大模型的知识是训练时冻结的它不知道昨天刚出的业绩预告也不清楚某家公司刚发的减持公告更没法把营收增速和行业景气度这两件本来该关联的事串起来。RAG检索增强生成解决的正是这个断层。它的核心逻辑不复杂在模型生成答案之前先从外部知识库里检索出相关的事实片段把这些片段作为上下文一起喂给模型让模型基于看得见的事实来推理而不是靠记忆瞎编。放到A股选股这个场景里知识库就是财报文本、公告、研报摘要、行业新闻这些非结构化数据检索器负责在用户提问时把最相关的片段捞出来生成器负责把这些片段和量化指标结合起来输出一份有依据的选股分析。这个项目我断断续续做了将近两个月中间推翻重来过两次架构。第一版想直接用在线API但A股数据敏感性和调用成本让我很快放弃第二版转向本地部署用Ollama跑量化后的模型用Chroma做向量存储用LangChain串起整个链路。最终跑通的这套方案单次完整分析从提问到输出报告在消费级显卡上大约40秒检索命中率经过调优后能稳定在80%以上。适合谁来参考这篇复盘如果你已经会用Python做数据处理对LLM有基本概念想做一个真正能跑起来的RAG项目而不是停留在demo阶段那这篇内容应该能帮你省掉不少弯路。如果你完全没接触过向量数据库和智能体编排建议先把LangChain的基础链路跑通再回来看不然中间很多设计取舍你会觉得莫名其妙。提示这个项目涉及的所有数据均来自公开渠道分析结果仅用于技术验证不构成任何投资建议。选股这件事本身风险极高技术方案再完善也不能替代独立判断。2. 技术选型为什么是Ollama Chroma LangChain这套组合2.1 本地部署模型这件事绕不开的取舍选Ollama最直接的原因是它把模型下载、量化、推理服务这三件事打包成了一个命令行工具。你不需要自己配CUDA环境、不需要手动转换模型格式、不需要写推理服务ollama run qwen2.5:7b一行命令就能跑起来。对于我这种主要精力放在业务逻辑上、不想在环境配置上耗三天的人来说这个便利性是决定性的。但本地部署有个硬约束显存。我手头是一张12G显存的卡7B参数的模型用Q4量化后大约占4.5G留给上下文和向量检索的空间还算充裕。如果你要跑14B甚至32B的模型要么上更大的卡要么接受推理速度降到难以忍受的程度。我实测过14B Q4单次生成要一分半以上交互体验直接崩掉所以最终锁定在7B级别。模型选择上我试过三个qwen2.5:7b、llama3.1:8b、deepseek-r1:7b。qwen2.5在中文金融文本的理解上明显更稳尤其是对归母净利润扣非ROE这类术语的把握llama3.1偶尔会把它们混为一谈。deepseek-r1的推理链更长适合做复杂分析但输出速度慢而且它的思考过程会占用大量token在RAG场景下反而拖累了整体响应。最终主力模型用qwen2.5:7b复杂分析任务再切到deepseek-r1。关于Ollama下载慢的问题这个几乎是每个国内开发者都会遇到的。我的做法是配置镜像源在环境变量里设置OLLAMA_HOST指向国内可访问的镜像地址下载速度能从几十KB/s提到几MB/s。具体配置方式因网络环境而异核心思路就是让模型拉取走一条更通畅的通道。离线安装包也是个选择如果你有多台机器要部署提前下好模型文件再分发会省很多事。2.2 Chroma为什么比FAISS更适合这个项目向量数据库这块我对比过FAISS、Chroma、Milvus三个。FAISS性能最强但它是纯索引库元数据管理、持久化、增删改查都要自己写一层封装对于需要频繁更新财报数据的场景来说太麻烦。Milvus功能全但部署重单机跑要起好几个容器对一个个人项目来说属于杀鸡用牛刀。Chroma的定位刚好卡在中间它自带持久化、支持元数据过滤、API设计极简而且和LangChain的集成是官方级别的。我用它存了三类数据财报文本片段、公告摘要、研报观点。每条记录除了向量本身还带了stock_code、report_date、doc_type这些元数据字段。这样在检索时可以加过滤条件比如只查2024年之后的、属于新能源行业的公告避免把三年前的旧闻捞出来干扰判断。Chroma的默认嵌入模型是all-MiniLM-L6-v2英文为主中文效果一般。我换成了BGE-M3这个模型对中文语义的捕捉明显更好尤其是金融领域的专业表述。代价是嵌入速度慢一些但离线预处理阶段可以接受。这里有个细节嵌入模型和生成模型是两回事嵌入模型负责把文本转成向量生成模型负责基于检索结果写分析两者可以独立替换。2.3 LangChain在链路里到底扮演什么角色很多人觉得LangChain是套壳但在这个项目里它确实省了不少事。最核心的价值是它把文档加载→切分→嵌入→存储→检索→生成这条链路标准化了。我只需要继承几个基类就能把自定义的数据源接进去不用自己写调度逻辑。具体用到的组件DocumentLoader负责从PDF财报和CSV公告里抽文本TextSplitter做分块VectorStore封装Chroma的读写RetrievalQA把检索和生成串起来。其中TextSplitter的参数调优花了我不少时间后面会专门讲。不过LangChain也有坑。它的版本迭代太快不同版本之间API差异很大网上搜到的教程经常对不上。我的建议是锁定一个稳定版本把依赖写死在requirements里不要盲目升级。另外它的默认prompt模板偏通用金融场景需要自己重写这个后面也会展开。3. 知识库构建从原始财报文本到可检索的向量片段3.1 数据源的清洗比想象中脏得多A股财报的PDF格式五花八门有的带复杂表格有的扫描件质量差有的页眉页脚混在正文里。我一开始用PyPDF2直接抽文本结果表格全乱套数字和单位对不上这种数据喂给模型只会产生幻觉。后来换成了pdfplumber它对表格的解析能力强很多能保留行列结构。但即便如此还是需要一套清洗规则去掉重复的页眉页脚、合并被换行切断的句子、把单位万元这类信息提取出来作为元数据、过滤掉纯页码行。这套规则我写了大概200行代码覆盖了80%的常见格式剩下的20%靠人工抽检修正。公告数据相对规整因为交易所的公告格式比较统一用正则就能提取出标题、日期、正文。研报摘要我主要从公开的行业报告里摘这部分量不大但质量高作为补充知识源很有价值。注意数据清洗阶段一定要保留原始文件的来源信息后面排查检索问题时需要回溯到具体是哪份文件、哪一页出的问题。我吃过这个亏有次检索结果明显不对但因为没记录来源花了半天才定位到是一份格式错乱的PDF污染了向量库。3.2 文本切分的粒度直接决定检索质量切分这件事看起来简单实际上是最影响RAG效果的环节之一。切太大一个片段里混了好几个主题检索时噪声大切太小上下文不完整模型拿到半句话没法推理。我试过三种策略固定长度切分、按段落切分、按语义切分。固定长度比如500字实现最简单但经常把一句话从中间切断。按段落切分保留了语义完整性但段落长度差异极大有的公告一段就20个字有的研报一段800字。最终我用的是递归切分优先按段落切如果段落超过800字就按句子切如果句子还超就按字符切同时设置200字的重叠区保证跨片段的语义连贯。针对财报这种结构化文本我还加了一条特殊规则把资产负债表利润表现金流量表作为硬分隔符确保同一张表的数据尽量落在同一个片段里。这个调整让财务指标相关的检索准确率提升了大概15个百分点。3.3 元数据设计让检索能按条件捞光有向量相似度还不够因为用户的问题往往带条件。比如帮我找一下最近三个月新能源行业营收增长超过30%的公司这里最近三个月新能源行业营收增长超过30%都是过滤条件纯向量检索没法处理。我的元数据字段设计如下字段名类型用途stock_codestring股票代码用于精确匹配stock_namestring股票名称industrystring所属行业支持行业过滤report_datedate报告日期支持时间范围过滤doc_typestring文档类型财报/公告/研报sectionstring所属章节如管理层讨论metric_tagslist涉及的财务指标标签有了这些字段检索时可以先做元数据过滤缩小范围再做向量相似度排序效率和准确率都上来了。Chroma的where参数支持这种组合查询写起来也不复杂。4. 检索链路调优命中率从50%提到80%的实操过程4.1 最初的检索为什么总捞不到对的东西项目刚跑通时我拿分析一下比亚迪2024年三季度的盈利能力这个问题测试检索出来的片段里居然有宁德时代的内容。排查后发现两个问题一是嵌入模型对比亚迪和宁德时代的向量区分度不够因为两者都是新能源车企文本语境相似二是没有做元数据过滤检索时把整个库都扫了一遍。第一个问题的解法是换嵌入模型。BGE-M3相比默认模型在中文实体区分上强不少换完之后比亚迪相关的片段基本不会再混入其他公司。第二个问题的解法是在检索前先做实体识别把问题里的股票名称或代码提取出来作为元数据过滤条件传给Chroma。4.2 混合检索向量关键词的双保险纯向量检索有个天然缺陷它对精确匹配不敏感。比如用户问ROE连续五年大于15%的公司向量检索可能返回一堆讲盈利能力的片段但未必精确命中ROE这个指标。这时候关键词检索BM25就派上用场了。我的方案是混合检索向量检索取Top 20BM25取Top 20然后用RRF倒数排名融合算法合并两个结果取融合后的Top 10作为最终上下文。RRF的核心思想是一个片段如果在两个检索器里都排得靠前那它大概率是真正相关的。实测下来混合检索比单用向量检索的命中率高了将近20个百分点。实现上LangChain有现成的EnsembleRetriever可以组合多个检索器我只需要把Chroma的向量检索器和BM25检索器传进去设置好权重就行。权重这块我调了几轮最终向量检索占0.6、BM25占0.4这个比例在金融文本场景下比较平衡。4.3 重排序最后一公里的精度提升检索出来的Top 10片段顺序未必是最优的。重排序Rerank的作用就是用一个小模型对这10个片段重新打分把最相关的排到最前面。我用的是BGE-Reranker它比嵌入模型更擅长判断问题和片段的相关性因为它能看到问题和片段的完整交互而不是各自独立的向量。这一步的代价是增加了一次模型推理单次大约多花200毫秒。但收益很明显重排序后最相关的片段基本能稳定排在前三位模型拿到的上下文质量高了一个档次。如果你的场景对延迟不敏感强烈建议加上这一步。4.4 检索失败时的兜底策略再好的检索也有失手的时候。我的兜底逻辑是如果重排序后的最高分低于某个阈值我设的是0.3就判定为检索失败这时候不硬答而是返回当前知识库中没有找到足够相关的信息请补充更多细节或换个问法。这个设计避免了模型在缺乏依据时胡编乱造虽然用户体验上会多一次交互但比给出错误分析要好得多。5. 智能体编排让选股分析从一问一答变成多步推理5.1 为什么单轮RAG不够用单轮RAG的流程是用户提问→检索→生成→返回。但选股分析往往需要多步先确定分析维度盈利能力、成长性、估值再针对每个维度检索数据然后综合各维度给出结论。如果把这些都塞进一次检索上下文会非常长而且不同维度的信息会互相干扰。所以我引入了智能体编排把整个分析拆成几个子任务每个子任务独立检索、独立生成最后由一个汇总节点整合。这样每个子任务的上下文更聚焦生成质量更高。5.2 用LangGraph搭一个简单的分析流水线LangGraph是LangChain生态里做智能体编排的库核心概念是节点和边。我定义了几个节点意图识别节点判断用户的问题属于哪类分析财务分析、行业对比、事件驱动等维度拆解节点把问题拆成具体的分析维度检索节点针对每个维度执行混合检索分析节点基于检索结果生成该维度的分析汇总节点整合各维度分析输出最终报告节点之间用条件边连接比如意图识别后根据类型走不同的分支。整个图跑下来一次完整的选股分析大约经过5到7个节点耗时40秒左右。这里有个设计取舍要不要让智能体自己决定检索什么我试过让模型自主规划检索策略但效果不稳定有时候它会漏掉关键维度。后来改成半自动维度拆解由模型做但检索策略是预设好的每个维度对应固定的检索模板。这样牺牲了一点灵活性换来了稳定性。5.3 提示词工程让模型说人话而不是套话金融场景的提示词有个特殊要求既要专业准确又要避免模棱两可的表述。我最初的提示词写得太宽松模型输出全是建议关注值得留意这种废话。后来加了几条硬约束每个结论必须引用具体的检索片段作为依据涉及数字的地方必须标注来源哪份财报、哪个日期禁止使用可能或许建议关注等模糊表述如果数据不足以支撑结论明确说数据不足这几条约束加上去之后输出质量明显提升。模型开始会说根据2024年三季报公司营收同比增长24.3%主要驱动因素是...而不是公司业绩表现良好。提示提示词里的约束要具体、可验证不要写请专业一点这种没法执行的要求。我一般会把约束写成检查清单的形式让模型逐条对照。6. 实测中暴露的问题和我的修复方案6.1 模型幻觉数字对不上是最危险的即便有RAG模型还是会在某些情况下编造数字。我遇到过最严重的一次检索片段里明明写的是净利润3.2亿模型输出成了净利润5.2亿。排查后发现模型在整合多个片段时把另一家公司的数字串了进来。修复方案有两层一是在提示词里强制要求每个数字必须紧跟着标注来源片段编号这样输出后可以用脚本校验数字是否与来源一致二是在汇总节点加一个数字核对步骤用规则引擎把输出里的数字和检索片段里的数字做比对不一致就标记出来让模型重新生成。6.2 上下文超长导致的截断7B模型的上下文窗口有限当检索片段太多时后面的内容会被截断。我最初的配置是Top 10片段全塞进去结果经常超限。后来改成动态控制根据每个片段的token数从高到低累加直到接近窗口上限就停止保证最相关的片段一定在上下文里。另外长片段我会做二次压缩用一个小模型把片段里的冗余信息去掉只保留和问题相关的部分。这个压缩步骤能减少30%到40%的token消耗让更多片段能塞进上下文。6.3 检索延迟的优化混合检索加重排序单次检索要跑三个模型嵌入、BM25、重排序延迟加起来有1秒多。对于交互式应用来说有点慢。我的优化手段是缓存把常见问题的检索结果缓存起来下次同样的问题直接命中缓存。缓存key用问题的嵌入向量做近似匹配相似度超过0.95就认为是同一个问题。这个优化让重复问题的响应时间降到了200毫秒以内。6.4 Ollama服务偶发的500错误跑长任务时遇到过几次500 internal server error日志显示是llama-server进程崩了。排查下来主要是两个原因一是并发请求太多Ollama默认的并发数有限二是显存不足长上下文把显存吃满了。解法是限制并发数在Ollama配置里设OLLAMA_NUM_PARALLEL1以及在代码里加显存监控接近上限时主动释放缓存。7. 这套方案还能往哪些方向继续打磨项目跑通之后我梳理了几个可以继续深化的点。第一个是引入GraphRAG把公司之间的股权关系、供应链关系建成图这样检索时能沿着关系链扩展回答某公司的上游供应商有哪些这类问题会更准。第二个是加入时序分析把财务数据按季度对齐让模型能识别趋势而不只是看单点。第三个是接入实时行情目前知识库是离线更新的如果能对接实时数据流分析的时效性会强很多。不过这些都是锦上添花核心链路已经能稳定跑通。我个人的体会是RAG项目最难的不是把链路搭起来而是把检索质量调上去。检索不准后面生成再花哨都是空中楼阁。所以如果你的项目效果不理想先别急着换模型回头看看切分策略、嵌入模型、元数据设计这几个环节大概率问题出在那里。最后分享一个我在调试时常用的小技巧把每次检索的Top 10片段和最终输出都存下来定期人工抽检。看多了你会发现一些规律比如某类问题总是检索不准某个数据源的片段总是排在后面。这些观察比任何自动化指标都更能指导优化方向。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。