如果你也在做 AI 应用落地,大概见过不少“大模型跑个对话就上线”的玩法。这套东西刚起步的时候,即使是小白也能快速搭出聊天机器人,可一旦牵扯到垂直行业,尤其是投资分析这种对准确性、时效性、可追溯性要求极高的场景,事情就没那么简单了。今天这篇不是讲概念,而是完整复盘我一个人手搓的一套投资分析 agent,它背后的语料库累计超过了百亿 token,覆盖财报、研报、公告、宏观纪要、新闻信源。这个过程里踩过的坑、拆过的组件、调过的参数,我都会尽量原样摊开来讲。
这个 agent 能做什么呢?输入一个公司或行业问题,它能自动检索历史资料、拆解问题、多模块分析、核验证据,最后生成带引用编号的分析结论。它适合谁参考?如果你手里有大量非结构化的行业文本,想用大模型把资料盘活,或者想从一个纯调用 API 的“提示词工程师”变成会设计检索框架、编排任务、治理数据的 AI 应用开发者,这篇内容会很有用。
1. 为什么手搓一个百亿token级别的投资agent
1.1 百亿token到底是个什么量级
很多人看“百亿token”没什么概念。我先给个参照:一份正常财报大概 3000 到 8000 token,一篇深度研报大概是 10000 到 30000 token。如果把全市场主要的上市公司公告、财报、研报、宏观日报、行业纪要都收集起来,按十年维度累计,几百亿 token 是很正常的量级。
这里面真正的难点不是存储,而是整理。原始文件是 PDF、HTML、图片、扫描件、表格混在一起的,格式混乱,OCR 误差,年报里有大量重复的附注和模板套话,公告编号不一致。单纯把文本灌进模型毫无意义——模型读不懂版面结构,也没法理解时间顺序,更不知道什么是“对这个问题有用的段落”。
当时我的目标很简单:所有历史语料必须能支持秒级检索;检索结果必须能够回答用户问题;任何结论必须有原文出处。要达到这个目标,就不能把所有文本一股脑塞进上下文窗口,得先把它变成结构化的索引。
1.2 现成工具解决不了什么问题
现在市面上不是没有投资分析工具,也不是没有大模型辅助研究的产品。但实际用下来会发现一个问题:它们大多是黑盒,查不到细节,也没法按你的思路定制。
举个例子,市面上常见的“智能问答”工具,你问它“某公司去年的经营现金流为什么下滑”,它可能给你一段很流利的回答,但你追查关键数字的时候,发现它把去年三季报的某个数据和年报的数据混在一起了。这才是投资场景中最致命的问题。
另一个现实问题是数据权限和合规性。很多第三方工具自带数据源,但你不知道它背后的信源是什么,也无法保证数据更新的准确时间点。投资分析最怕的就是用不可追溯的信息做决策。所以我的选择是“手搓”——自己掌握数据管道、检索逻辑和各模块配置,把黑盒变成白盒。这才是我愿意投入这么大精力做全套系统的核心原因。
提示:如果你只是想快速验证想法,用现成工具完全没问题。但如果你要做长期、定期、自动化的分析任务,并且对输出有强溯源要求,那自建数据管道和检索层基本是绕不开的。
2. 整体架构:数据、知识、决策三层闭环
2.1 数据层:从原始资料到统一语料
整个系统先解决的是“把杂乱的文本变成可用的语料”。数据源包括历史财报、券商研报、上市公司公告、新闻资讯、宏观数据汇总、会议纪要等。这些数据来源的格式差异非常大。
我搭了一个数据管道,分下面几步:
- 格式归一化:代码把所有文件转换成统一文本格式。扫描件走 OCR,PDF 提取时保留标题层级和表格结构。这一步很关键,如果表格被拆散,后面所有财务计算都会出错。
- 去重与正文提取:一份公告在三个新闻源里重复出现,只保留原始来源和唯一编号。对公告和新闻还要做正文提取,去掉页眉页脚、导航栏、相关推荐等信息。
- 元数据标注:每个文本片段打上“公司代码、公告日期、公告类型、文件来源、页码”等标签。
- 更新机制:增量更新,定期跑同步任务。因为投资分析对时效性敏感,过期数据不能混在最新语料里。
这套数据管道跑起来之后,入库的文本量才勉强达到“百亿 token”这个级别。可以说,没有这一步,后面的知识层和决策层都无从谈起。
2.2 知识层:索引、向量化与检索增强
知识层是连接数据和模型的中间层。如果数据层是仓库,知识层就是仓库的目录系统。我用两种索引方式并存:
一种是传统的倒排索引,用于精确匹配关键词,比如公司简称、财报科目、人名、地名、产品名、代码。这种索引特别适合处理交易代码、股票简称这种不适合向量化的高频词。
另一种是向量索引,把文本切块后通过嵌入模型转成高维向量,用于语义召回。这样一来,即使查询词和原文没有完全相同的表达,也能找到语义上相关的内容。
最终检索采用混合模式:先用关键词索引做粗筛,再用向量索引做语义排序,最后用一个轻量的精排模型把结果重新排序。这套模式在投资领域的普适性很高,也是我在多次调优后确定下来的。
2.3 决策层:多模块协作与工具调用
知识和数据都有了,接下来是让模型“干实事”。决策层我做了四个角色模块:
- 规划器:负责拆解用户问题,判断需要哪些信息,需要检索几次,是否要多轮才能完成分析。
- 检索助理:调用知识层的接口,把粗检索结果收集起来,再根据任务需要补充额外数据。
- 分析模块:负责读检索结果,结合推理,形成观点和结论。
- 审查模块:重点核验引用编号是否真实存在于知识库,结论和原文是否一致,有没有明显的信息断层或过度推测。
这种多模块协作的好处是职责清晰。比如“检索助理”不负责写结论,“分析模块”不直接查库,出了问题排查逻辑链路时,很快就能定位到具体环节。如果没有这种分层,只是让一个大模型自由发挥,它很容易把检索到的内容和自己的记忆混在一起,产生幻觉。
3. 手搓过程中的关键实现细节
3.1 文档切分与向量化策略
把文档切成块是决定检索质量的基础。切得太短,上下文不足;切得太长,语义被稀释;粗暴按固定长度切,会把财报中“营业收入”和后面的数字拆散。
我采用的方案是“标题感知切分”:先解析文档结构,保留章节标题,按章节边界切分;段落特别长时再按语义段或句子组进一步切分。核心是保留上下文连续性。
当时写了一个分段函数,大致逻辑是这样:
def smart_split(text, max_chars=800, min_chars=200): sections = split_by_heading(text) # 按 Markdown 标题级别拆分 chunks = [] for section in sections: # 保护表格块,先拆成“标题 + 表格”结构 tables = extract_tables(section) if tables: for t in tables: chunks.append(header + table_text(t)) else: split_paragraphs(section, max_chars, min_chars, chunks) return chunks真实实现里还会判断句子边界、表格行数、数字完整性。切块后,我对每个块做了元数据校验,比如某一段引用的财务数据必须能对应到同一家公司,防止跨公司文本混切。这一步听起来琐碎,但直接决定了后面检索的精度。
3.2 混合检索与精排,怎么调才能准
投资领域的问题有一个特点:查询词往往是实体词为主,比如“某某公司”“2023年报”“ROE”“现金流”。这类词用纯向量检索容易出偏差,因为模型可能在语义上匹配了“资本回报率”这句话,但漏掉了精确的“ROE”缩写。用混合检索就互补。
我实际用的方案是:
第一轮召回:倒排索引做关键词匹配,向量索引做语义匹配,两者取并集。每类取前 50 条,合计约 100 条候选。
第二轮精排:用重排序模型对候选结果打分,这个模型要能理解“问题和文本段落的相关性”。对投资场景,我会额外加上时间衰减权重:同样内容,越新的资料得分越高;财报期刚出的快报优于一份几个月前的深度研报。
调优时的经验是:不要只依赖离线评测集。我每两周会人工检查 50 条检索结果,看看排名靠前的片段是不是真的能回答问题。有很多问题是评测集里发现不了的,比如“相关但缺乏关键数据”的片段,评分很高,但实际没法支撑结论。这种情况单纯调模型是解决不了的,需要在数据的净化和标注上下功夫。
3.3 提示词约束与引用溯源
投资agent不能像普通聊天助手那样随意联想。我在分析模块的提示词里明确写了三条硬规则:
- 输出必须包含引用编号,编号格式为“来源:[1][2][3]”
- 每个结论必须对应至少一个引用,如果检索材料不像结论,必须明确标注“数据不足”
- 禁止在没有材料支撑的情况下推测具体公司未来业绩的绝对值
唯一例外是对行业背景、宏观常识的描述,即便如此也会标注“通用背景,无特定来源”。为了让这条可执行,审查模块会逐条把引用编号映射回知识库,检查引用编号对应的文本是否真的存在,再比对内容和结论的关系。
如果一个结论引用了“某公司上半年营收同比增长 10%”,但检索引用的文本里根本没有“10%”这个数字,审查模块就会打回重写。这个环节不依赖大模型自觉,而是靠后处理代码校验。可以说,没有审查模块,这套系统不敢投入实际使用。
3.4 评估集与回归测试:不靠感觉调参
手搓系统的另一个麻烦是改动不可控。今天优化了切分逻辑,可能明天检索质量就下降了。所以必须建评估集。
我的做法是准备 200 个投资相关的问题,覆盖财务指标、行业竞争、公告解读、宏观政策等类型。每个问题配一个“预期答案”和“验证点”。什么时候跑评估呢?每次改完代码、更新完模型、调整完提示词,都跑一遍。
几个关键指标我固定统计:
- 引用通过率:检索结果中的引用是否真实存在,且能支撑答案
- 事实准确性:关键数字是否与原文完全一致
- 信息完整性:是否漏掉重要的财务科目或事件
- 时效性:使用了过期的文本片段会被扣分
有了这套回归机制,我才能放心迭代。不然就是在沙滩上盖房子,今天看着没问题,明天可能倒了重来。
4. 运行中遇到的典型问题与排查记录
4.1 数据时效性导致引用过期
第一次碰到这个坑是某公司发年报前,系统回答问题时仍然引用旧年报数据。原因很直接:数据管道增量更新没把最新年报的解析结果替换旧的。旧文档还在检索集里,新的资料虽然上传了,但权重没调。
解决办法是给每个检索块增加“发布时间”字段,并在检索时加一个“信息截止时间”参数。用户提问时默认使用当前时间,系统只检索“早于等于当前时间且距离最近”的资料。旧年报和新年报同时存在时,时间权重也会把新年报排到更前面。这个策略后来还被用在了公告解读上:公司出了公告,旧的深度研报不会消失,但它不再作为主要信源。
4.2 检索召回一堆噪音,怎么收敛
另一个常见问题是检索结果飘。比如问“某某公司的毛利率变化”,结果召回了关于行业整体毛利率的研报、竞争对手的报表分析、十年前的老新闻。召回很全,但排在前面的全是噪音。
我排查后发现,问题出在切分块过长。一份研报几万字,切分块如果包含“行业分析”“公司分析”“风险提示”等多个章节,向量化的时候语义互相干扰,整个块变得“什么都像,什么都不像”。
修复方式是细化切分粒度,尽量让每个块围绕一个具体主题。同时给检索接口加重排序分阈值,宁可结果少一点,也不要塞进一堆无关段落。少而精比大而全重要——因为分析模块一旦看到一堆不相关内容,很容易被带偏。
4.3 token用量和延迟失控
百亿token不可能全灌进大模型。就算能灌进来,时间和钱也不允许。我一开始的设计是有状态的对话模式,每轮把历史摘要和检索结果一起传给模型。跑了一段时间发现,复杂问题的累计上下文很容易超过几万 token,响应时间直奔几十秒,成本也飙高了。
后来我做了两件事:
第一,把多轮对话压缩成“简洁的已解决问题摘要”,不再保留原始消息历史。对投资分析而言,历史对话的价值不大,最终结论才重要。
第二,把一些常见问题做成预计算缓存。比如“某公司过去五年的营业收入、净利润、经营性现金流”这类统计型问题,直接用数据管道生成固定格式答案。只有复杂开放性问题才走大模型分析流程。这样高峰期延迟从几十秒降到了几秒内。
4.4 输出有幻觉,审查环节怎么兜底
幻觉是这类的系统的无解难题,尤其是投资场景,绝不能靠提示词洗脑。我遇到过的情况是:分析模块把 A 公司的财报数据当成 B 公司的,或者把三年前的数据说成最新的。这种情况即使有检索材料,模型还是会生成看似合理的错误结果。
真正有效的兜底是强校验。审查模块不只是检查引用编号是否存在,还会做类型层面的校验:
- 如果引用的文本是“某公司 2022 年年报”,就不能在结论里写“2023 年”
- 如果原文只提到“营收同比增长”,就不能扩展出“净利润增长率”
- 如果原文没有提到具体数字,结论就不能出现具体数字
这些规则看起来死板,但确实有效。允许模型发挥想象力的场景很多,但投资分析不在其中。对我来说,宁可让系统说“信息不足,无法判断”,也绝不让它编造。
5. 成本控制、性能优化与后续扩展
5.1 分批处理与缓存战略
百亿token级别的处理不可能一蹴而就。我的做法是把整体语料库按时间、公司、文档类型分批处理。每晚跑增量任务,错峰引入新数据。导入的时候,先做低成本的文本校验,再考虑是否更新向量索引;没有变化的文档直接跳过,不重复向量化。
缓存策略也是控制成本的关键。相同公司、相同格式、类似时间范围的问题,答案模式高度重复。我把这些统一用模板生成,不重复调用大模型。一次完整的财报分析,真正需要大模型做开放推理的部分不到总查询量的三成。省下来的费用都花在了最有价值的问题上。
5.2 模型分级路由与弹性调度
复杂问题用大模型,简单问题用轻量模型。听起来像废话,但真正做到精细化调度才是节省资源的关键。
我在调度层设置了一个路由规则:问题先经过分类器判断任务复杂度,再分配模型。比如“提取某公司最近一期研发费用”属于信息提取型,直接走轻量模型;而“评估某公司竞争壁垒”属于推理分析型,走更强的大模型。即便是大模型,我也会根据 token 量和上下文长度动态选择不同的配置。
这种弹性调度也会在高峰期做队列。如果任务量突增,把低优先级任务降级为异步处理,保证实时任务不排队。刚开始我以为投资分析都是实时问答,后来发现很多任务是固定的日报、周报生成,完全可以在深夜批跑。把在线和离线任务分开,系统的稳定性和成本都会明显改善。
5.3 后续可以往哪个方向扩展
目前这套系统已经能稳定覆盖公司分析、行业对比、公告解读、财报复盘几类任务。我自己计划中的下一步有三个方向:
第一个是知识图谱的接入。目前检索结果还是文档片段,如果能把实体和关系抽出来,形成结构化网络,分析模块在做公司间对比时会更有逻辑性。比如“供应商-客户关系”“股权结构变化”“行业上下游”这些内容,图谱比文本块更好用。
第二个是带业务逻辑的回测管道。让 agent 在产出分析结论后,自动拉到历史数据做验证,比如“如果按结论事件发生后的实际走势,判断当时的逻辑是否正确”。这能让系统自我迭代,而不只是文本层面的报告生成。
第三个是多语种支持。海外资料、英文公告、外资行研报全部纳入后,语料量可能从百亿token直接翻倍。届时数据管道和检索策略都需要重新设计,但这也意味着系统的覆盖面和适用场景会明显扩大。
最后说几点我的真实体会
这套系统从零开始,到能稳定输出带引用、可核验的投资分析内容,前后经历了很长的摸索期。最开始我也迷信大模型的能力,觉得“只要模型够强,什么都能搞定”。真正跑下来才发现,在垂直领域,数据治理、检索质量、校验机制和成本控制远比模型参数重要。模型只是一个聪明的同事,你得给它可靠的工具和清晰的工作流程。
如果让我给后来者一个建议,那就是:不要急于追求“大而全”。先把一小块数据治理干净,跑通一个垂直场景的闭环,再逐步扩大到“百亿token”的规模。每一步都要有评测指标,才能避免无效迭代。
另一个很有用的经验是:把系统设计成“可解释”的。我保留了每一次检索的原始片段、每一次分析的时间线和最终结果的所有记录。出现错误时能回溯到具体环节,效率提升很多。这种可解释性在投资这类容错率低的行业里,比模型精度更重要。
最后,不管系统做到什么程度,都要清楚一点:AI 分析工具是辅助研究的手段,任何结论都需要人工复核。这也是我在设计审查模块时最坚持的原则。工具可以帮助我们提升效率,但决策的责任始终在人身上。