☰
基于RAG的A股智能选股Agent开发复盘:从知识库到决策链路
2026/10/2 5:17:22 网站建设 项目流程

最近把基于RAG的A股智能选股分析智能体从零到一完整走了一遍,包括数据清洗、知识库构建、检索链路、Agent决策框架和最终的回测验证。这篇文章不是项目说明书,而是把整个开发过程复盘一遍,重点讲清楚每个环节为什么这么做、实际调试中会遇到什么问题。尤其是RAG部分,很多教程只讲“装个LangChain、调个向量库、跑通demo”,真到要做出一个能稳定输出的选股智能体时,细节全在检索质量、上下文组装、工具调用的可靠性上。

这个项目适合三类人看:一是想用RAG做垂直领域知识库的开发者,二是想给Agent接入真实数据和决策逻辑的朋友,三是对A股量化投研方向感兴趣、但不想碰复杂数学模型的入门者。我不提代码层面怎么调API,而是把设计思路和踩坑过程拆开讲。全程基于公开市场和公开数据,智能体的所有输出都只是分析辅助,不构成任何投资建议。

1. 项目背景与需求拆解

1.1 为什么选“A股智能选股”这个场景

A股市场的公开信息量非常大,财报、公告、研报、新闻、行业政策,每天都产生大量文本数据。一个人的精力完全无法持续跟踪几千只股票,更不要说在短时间内把某只股票的基本面、近期事件、行业位置和资金动向串起来形成判断。传统量化选股依赖结构化因子,比如PE、PB、ROE、动量指标,但结构化因子有个明显短板:它抓不住公告措辞变化、研报里的投资逻辑、新闻事件背后对产业链的影响。这些问题恰恰是文本里承载的。

RAG检索增强生成适合干这件事。它的核心思路是先把大量非结构化文本切分成片段、向量化、存入知识库,用户提问时先检索出相关片段,再把片段灌给大模型做回答。这样既能让大模型基于真实数据输出,也能避免它凭空编造。把RAG和智能体结合,等于在“读得懂文本”和“调用工具做计算”之间搭了一座桥。选股智能体不只是回答问题,它能主动调用财务数据接口、行业板块接口、行情接口,结合知识库里的研报和公告内容,生成一份带数据支撑的分析结论。

这个需求如果只用普通聊天机器人做,最后输出一定是一碗“正确但空泛”的鸡汤。必须有RAG层提供事实依据,必须有工具层提供可计算的指标,才能让“智能选股”四个字落地。

1.2 智能体能力拆解与边界确认

动手前我把智能体的能力边界画清楚了。它不是一个自动交易机器人,而是一个“分析型智能体”。主要提供四类能力:

  • 基本面快速画像:给定股票代码或名称,输出主营业务、财务核心指标、最新业绩变化。
  • 事件驱动解析:分析最新公告、研报中的关键信息,解释事件对公司的潜在影响。
  • 横向对比分析:把若干只股票从盈利能力、成长性、估值水平、行业地位等维度做对比。
  • 风险提示扫描:从知识库和公告文本中提取质押、诉讼、减持、审计意见异常等风险信号。

为什么要把边界定得这么具体?因为Agent项目最怕的就是“什么都会”的错觉。范围一旦模糊,用户问一句“帮我看看买哪个”,大模型可能会基于幻觉直接给出建议,这在金融场景里非常危险。我在系统Prompt里写死了“不构成投资建议”的兜底逻辑,同时规定所有涉及数字的表达必须来自检索结果或工具返回值,不允许模型自己推导具体数值。

1.3 技术选型背后的取舍逻辑

整体架构采用“数据层 + 知识库层 + Agent调度层 + 模型层”的四层设计。

  • 数据层:使用公开的A股数据源,包括日线行情、财务摘要、公告全文、新闻资讯。行情和财务数据走结构化接口,公告和新闻走文本采集通道。
  • 知识库层:负责把文本数据切分、清洗、向量化,并存储索引。我对比了FAISS、Milvus和Elasticsearch的向量能力,最终选了Milvus。原因很简单:项目后续不需要频繁更新全部索引,Milvus支持增量写入,而且它自带的HNSW索引在几十万条文本片段规模下查询延迟能控制在几十毫秒。如果数据量只有几万条,FAISS完全够用,没必要上重组件。
  • Agent调度层:我没有直接上LangChain完整框架,而是用LangGraph自己编排状态流转。理由是选股这个场景需要确定性的分支逻辑,比如先查工具、再查知识库、最后融合输出,每一步的依赖关系很清晰。LangGraph能让我显式控制节点状态,避免框架封装过深导致难以调试。
  • 模型层:拆成两个角色。检索增强生成的主模型负责理解和输出,一个轻量分类模型负责意图识别。这样主模型上下文更干净,输出质量更稳定。

这不是唯一方案,但适合“要快速上线、又不想被框架绑定”的情况。如果你团队对LangChain非常熟,用它也能做,只是要做好定制的心理准备。

2. 知识库构建:RAG系统的基础工程

2.1 数据采集与清洗:决定检索上限

RAG有一个被低估的关键点:知识库的质量直接决定检索质量,而检索质量直接决定模型输出质量。最开始我拿到的原始公告数据是混着PDF页眉、页脚、表格错位的文本,直接切分向量化之后,检索出来的片段经常是一堆半句话,模型根本读不出有效信息。

所以我在数据清洗上花的时间比预计多一倍。具体做了这几件事:

  • 把PDF和其他格式的文档统一转成纯文本,再用正则去除页眉页脚、重复行、表格错位符。
  • 针对财报和公告做结构化拆分。一份财报里有“财务摘要”“管理层讨论”“风险提示”多个段落,直接机械切分会把完整逻辑切碎。我按一级标题和二级标题边界切块,保证每个片段内有相对完整的语义。
  • 数据时效标记。每条文本入库时打上发布日期,检索时按时间衰减加分,避免三个月前的旧研报覆盖近期信息。
  • 去重。很多新闻源会转载同一篇内容,我用SimHash做了近似去重,把重复片段直接踢掉。这一步让知识库体积减少了约18%,但检索精度提升很明显。

清洗后的数据才是切分和向量化的输入。这个教训很朴素:RAG系统里“垃圾进垃圾出”效应比传统模型更致命,因为检索环节会把噪声放大。

2.2 切分策略:不是所有文本都用同一把尺子

文本切分直接决定检索单元的粒度。最开始我用固定长度切分,按256字符为一个chunk,效果很差。因为公告里一个财务句子可能超过200字,固定切分把“营业收入增长率”和“净利润增长率”这两组数据拆到两个片段,检索时只命中一半,模型的回答就变成单向维度,出现明显偏颇。

后来改成按结构边界切分,主体逻辑是:

  • 先按标题拆分出顶级段落。
  • 段落过长时再按句子边界二次切分,每段保持256到512个token之间。
  • 设置相邻chunk之间40到80个token的重叠,保证跨段信息的连续性。

为什么设置重叠?因为一句话的语义边界可能刚好落在切分点上,重叠部分相当于留了一条“安全缓冲带”,让检索能找回来一半的上下文。调参时可以从chunk_size=512、overlap=64起步,再根据检索命中情况微调。数据量越大,越建议保留结构化切分逻辑,而不是盲目增加重叠比例。

2.3 向量化、索引与混合检索

向量模型用的是开源中文Embedding模型,维度为1024。选型标准不是排行榜分数,而是它对金融中文文本的语义理解能力。我跑了一个小测试集,用“业绩预增”“股东减持”“资产重组”等典型短语做检索,对比了三个模型,效果差异比想象中大:有的模型对新闻口语化表达更好,有的模型对财报书面语更好,选股场景必须优先照顾财报和公告文本。

向量化之后,我建的是“向量检索 + 关键词检索”的混合检索。原因很简单:向量检索擅长语义相似,但对“ROE大于20%”这类精确条件不敏感;BM25关键词检索虽然不理解语义,但能准确匹配“ST”“退市”“商誉减值”这种硬信号。两类结果用RRF融合算法合并排序,重新排序后的Top结果才送入大模型。

检索方式擅长场景弱项在选股场景中的用法
向量检索语义相似、同义改写精确数字和代码匹配差检索“业绩下滑原因”这类泛化问法
BM25关键词专业术语、代码、数字无法理解同义表达检索“商誉减值”“st”这类硬标签
RRF融合综合两者优势需要调权重统一排序后送给重排模型

顺序上,先召回Top 50候选,再用reranker模型压缩到Top 5。这一步非常值得做,因为直接取向量检索的前几名,经常会把同一份公告的好几个片段都排进来,信息冗余严重。Reranker能把真正核心的片段提到最前面,上下文里就能多塞几个不同来源的片段,回答的信息密度立刻上来。

2.4 防止RAG幻觉的三道保险

RAG不是万能的,它不应该成为放弃调优的借口。我上了三道保险:

  • 检索内容必须带来源,最终输出需要以“光标引号”形式引用来源片段,不允许模型在没有检索结果的情况下直接陈述细节。
  • 检索不到相关内容时,模型必须明确说“知识库中暂无相关信息”,而不是编造一个答案。
  • 数值类问题强行引导到工具调用,比如用户问“PE多少”,Agent直接调结构化接口拿数据,不依赖文本片段里可能过期的数值。

这三道保险让系统的“胡说率”大幅下降。实际测试中,主模型在纯RAG模式下偶尔还会把两篇公告内容混在一起,加了“引用来源片段必须成句”的约束之后,混种出错明显减少。关键不是要模型写论文,而是让它形成一种“有据才说”的条件反射。

3. 选股智能体核心逻辑与实操实现

3.1 Agent状态机设计与工具注册

Agent调度层的状态流我画了五个节点:意图识别、参数抽取、工具调用、知识库检索、生成回答。节点之间是条件连线,比如意图识别判定为“需要最新行情”,就走行情工具节点;判定为“需要基本面”,就走财务接口和知识库双重通道。

工具注册遵循“窄接口、单一职责”原则。每个工具只做一件事:

  • get_quote:获取指定股票最新价格、涨跌幅、换手率。
  • get_financials:获取财务指标,比如营收、净利润、ROE、毛利率。
  • get_announcements:从知识库检索最近公告,返回结构化摘要。
  • get_industry_compare:对比股票所属行业主要竞争对手的估值和盈利能力。

为什么工具入口要窄?因为智能体的工具选择依赖大模型的意图判断,工具说明越复杂,模型越容易选错。每个工具的description我写得像操作手册:输入参数范围、输出格式、典型召回场景。实际测试下来,窄接口比一个“万能查询工具”的准确率高很多,后者看起来省事,实际是大模型最驾驭不了的东西。

3.2 用RAG增强选股决策的完整链路

完整的决策链路是这样的:用户输入“帮我看看宁德时代和比亚迪的财报表现,结合最近的行业消息做个对比”。

  • 意图识别节点:Token分类模型识别出“对比”“财报”“行业消息”三个意图标签,权重分数够了才往后续节点传。
  • 参数抽取节点:从问题中抽出两只股票名称,映射到股票代码。这里用了一个简单的映射表,因为大模型直接抽代码容易抽成公司名本身。
  • 工具调用节点:并行调用get_financials获取两家公司的核心财务指标,拿到的是结构化JSON。
  • 知识库检索节点:将“宁德时代 财报”“比亚迪 财报”“动力电池行业 政策 动态”拆成多个检索query,分别走向量库,召回相关公告和研报片段。
  • 生成回答节点:把工具返回的报表数据,加上检索到的文本片段,塞进Prompt模板。模板规定先摆数据、再写分析、最后单列“外部信息来源”区块。

这条链路的核心特征是“数据与文本分离”。数值走API,逻辑和背景走RAG,各司其职。对比纯大模型回答,带数据支撑的输出准确性高很多;对比纯SQL查询,又能多出“行业政策方向”“管理层对业绩的解释”这类非结构化信息。

3.3 Prompt工程:系统指令里的隐性条件

Prompt在这个项目里不是简单写个“你是专业人士”,而是用来控制Agent行为边界的配置文件。我在System Prompt里写清了以下几点:

  • 角色约束:你是研究助理,不是投资顾问,禁止对具体买入卖出行为表态。
  • 信息优先级:工具返回值优先于知识库检索内容,知识库检索内容优先于模型自身知识。
  • 数值处理规则:所有数字不四舍五入到“约”,要求精确列出来源。
  • 输出结构:必须使用“基本面概览”“业绩亮点”“风险信号”“综合分析”分段输出。
  • 反问规则:用户给的股票信息不完整,比如只说“新能源”没给代码,则必须列出可能对象并询问确认。

其中“信息优先级”这条最关键。大模型有个惯性,碰到自己“熟悉”的上市公司,容易直接把记忆里的数据写出来。如果工具返回的PE是30倍,而模型记忆是20倍,它很可能偷偷按记忆写。我用“必须给出原始数值来源编号”的方式强制它用工具结果,效果立竿见影。

3.4 从原型到回测:量化效果而非手感

智能体单次回答质量需要量化评估,不能只看几个案例觉得“还行”。我搭了一套简单的离线评估管道:准备70个问题,覆盖基本面、事件、对比、风险四类,请有金融背景的人给回答打分;机器人每次运行后自动记录检索到的片段来源、工具调用次数、最终回答结构完整性。最重要的指标是“回答中数值与知识库来源一致的比例”。这个指标能直接暴露模型幻觉。

评估维度通过标准实测结果
数值一致性抽查20个指标,回答与来源完全匹配占比≥90%第一版65%,第二版92%
工具调用正确率意图需要调用工具的问题中,工具选择正确占比≥95%第一版82%,第二版96%
回答完整度四段式结构不缺失,每段有实质内容占比≥85%第一版70%,第二版90%
风险信号覆盖率预设风险问题中,智能体能主动识别并输出的占比≥70%第一版58%,第二版76%

回测则用历史公告和行情数据构造了一个简化场景:让智能体在每个季度初基于当时可得的公告和财报生成一份“关注清单”,再对比该季度后实际涨幅。这里必须注意存活者偏差和未来函数的问题,我是严格按照公告发布日期截取数据,绝不让模型使用当季结束后才出现的信息。结果虽然不能代表什么超额收益,但能证明流程跑得通,数据链路没有严重的时序错位。

4. 开发过程中的典型问题与排查实录

4.1 乱象一:检索全命中,回答全是废话

第一版链接好之后,遇到的最让人崩溃的问题是:检索出来的片段确实都相关,但大模型把这些片段堆在一起,输出像在抄书,没有观点、没有结构。后来发现是Prompt里缺少了“结构化提炼”的指令,模型默认把检索内容当成要复述的文本。

解决方案是把检索结果从“原文”变成“素材”。我在组装上下文时,每个片段前面加了一个元信息标签,比如“片段来源:XX公司2024年中报,发布时间:2024-08-30,情绪标签:积极”。模型在生成时,可以总结提炼并引用来源,而不是整段照搬。这个改动让输出质量立刻提升一个档次,也避免了模型同时引用多份公告时原文过长的问题。

4.2 乱象二:工具返回值互相矛盾

智能体同时调用行情接口和财务接口后,经常出现“PE为负但净利润为正”这种逻辑矛盾。排查后发现是不同工具的更新时点不一样:行情有实时值,财务可能是截止上一报告期的值。如果不对齐时间口径,模型只会按照字面意思是两个数字都正确,却看不出内部口径冲突。

解决方法是给所有工具结果增加“数据日期”和“口径说明”字段,并在Prompt中强调“若各数据源时间口径不一致,应主动说明并优先采用最近数据日期”。这个逻辑开始只是防御性的,后来发现它其实是最有价值的产品功能:普通用户根本意识不到不同数据源之间有时间差,Agent主动指出这一点反而是专业性的体现。

4.3 乱象三:大模型在“不知道”时硬答

RAG系统里最难解决的问题不是检索不到,而是检索到了噪音内容,模型还误以为那是正确答案。我们遇到过一只基本面尚可的股票,模型检索到了一条几个月前的“减持计划公告”,直接把它写进风险提示,导致整段分析倾向性很强,好像公司要出事。

为了让Agent规避这类误判,我在知识库层加了“信息时效权重”,距离当前时间越近权重越高;在生成层加了一个“证据强度”标签,如果某条风险信号只来自单一来源且时间跨度超过三个月,模型必须标注“该信号时效性存疑”。这一条在实际使用中比任何Prompt话术都顶用,因为它直接从系统层面压制了噪声信息的影响。

4.4 性能优化与工程化经验

本地部署这套系统时,最吃性能的不是大模型,而是Embedding和Reranker。大模型调用走云端API,延迟可控;本地Embedding如果并发高,CPU直接被占满。后来我把Embedding模型加载成常驻服务,批量处理知识库更新,在线查询时走单独的轻量线程池,每秒并发控制在20以内。Reranker的推理时间约40ms/条,初期全量重排Top 50很慢,优化成“先粗排Top 30再重排”,整体延迟从2.3秒降到1.1秒。

向量库方面,Milvus的默认索引参数不一定适合中文金融文本。我调了nlist和nprobe:nlist代表聚类中心数,nprobe代表查询时探访的聚类数量。四五十万条数据规模下,nlist=1024、nprobe=16能兼顾召回和性能。参数调之前检索延迟有250ms,调之后降到60ms。如果你用的是FAISS,记得开启IDMap以避免删除重建索引的成本,增量更新体验会好很多。

5. 复盘心得:这几个坑提前知道能省两周时间

5.1 RAG部分最容易被低估的工作量

数据清洗和切分策略的实际工作量占了整个RAG链路开发的60%。很多项目一开始追求Embedding模型和向量库的选型,但真正影响效果的是你喂给它的数据长什么样。建议任何一个做垂直领域RAG的人,先花一周时间把原始数据格式、切分策略、清洗规则定清楚再碰代码。

5.2 Agent的可控性比聪明更重要

在金融场景下,智能体的“幻觉率”是安全指标。与其追求回答的全面和华丽,不如在一开始就给System Prompt加满约束条件,同时给每一个工具返回值加上完整的数据来源和时效信息。这会让模型看起来“笨”一些,但用户拿到的是有依据、能追溯的分析,而不是一堆漂亮但没有出处的话。

5.3 评估体系一定要提前建

没有评估体系的项目,后续每次换模型、换Embedding、改Prompt,都只能靠人工浏览案例来判断好坏,效率极低。我在第二版时就引入了离线评估集,并且把“数值一致率”作为核心指标。后续每次修改,跑一遍评估集,看指标变化,比翻聊天记录直观太多。

再分享一个我后来养成的习惯:把所有失败案例单独存成一个“badcase池”,每轮迭代后重跑badcase池,观察修正情况和新增回归问题。这种做法比看一堆正面示例更能反映系统真实水平,强烈推荐给所有在调RAG和Agent的朋友。

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

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

立即咨询