AI Agent自主驱动投研工作流:从采集到风险标注的工程实践 📅 发布时间:2026/9/19 10:57:56 👁 浏览次数: 金融研究这个领域最耗人的其实不是“判断”而是“准备判断的过程”。每天早上一睁眼几十份研报、公告、新闻、数据源堆在面前光是把它们读完、理清逻辑线、提炼出值得深挖的点就可能耗掉一上午。这两年AI大模型火起来之后我一直想做一个系统能替代掉这部分重复劳动让我把精力集中在真正需要经验的地方。所以就有了这套AI自主驱动 投研的工作流试验。简单说就是用AI Agent把“采集信息 → 清洗加工 → 观点生成 → 风险标注”这条链路串起来让它每天定时跑一遍主动盯住我关注的行业和公司再把成果直接归档成投研纪要。这篇文章把我从最初的想法、方案设计、实测过程到踩坑经验全部整理出来适合有一定工程基础、又不想在信息预处理上浪费太多时间的投研人员和开发者参考。1. 整体设计思路与任务拆解1.1 为什么投研场景特别适合Agent化我先说结论投研是天然适合AI Agent落地的场景因为它同时满足三个条件——数据密集、流程半结构化、判断有规律可循。数据密集不用多说一个行业每天新增的公开信息量凭人眼根本看不完。流程半结构化指的是从数据到结论之间有一条相对固定的加工路径先收集再清洗再打标签再比对历史最后形成观点。这条路径虽然长但每一步要做什么是明确的正好适合用程序来约束和驱动。判断有规律可循这一点最容易被忽略很多人觉得投资判断是玄学但实际上一线研究员在做公司分析时用的框架基本一致行业空间、竞争格局、财务质量、管理层能力、估值水平、潜在风险。既然框架稳定就可以把框架固化成提示词和校验规则让模型按部就班地输出而不是天马行空地发挥。用个生活化的类比这就像你雇了一个很聪明的实习生他不一定有十年经验但你给他一张足够细的清单他能把八成基础工作做到及格线以上。AI Agent在投研里扮演的就是这个角色而你要做的是给他画清楚路线图并在几个关键路口设卡检查。1.2 Agent驱动投研的三层Web网结构我实际落地时把整个系统拆成了三层每一层只干一件事这样出问题时定位最快也方便单独升级某一个环节。第一层是信息采集层负责盯信息源。我给它划定了三类来源交易所公告、行业垂直媒体、头部券商研报摘要。这一层不直接和大模型聊天而是用定时任务去抓取新内容做出去重后进入待加工队列。第二层是加工分析层这是AI Agent真正发挥价值的地方。系统会按照我预设的行业框架把每条信息拆成“时间、主体、事件、影响对象、影响方向、置信度”几个字段然后再结合公司历史数据做横向比对生成逻辑链。第三层是表达产出层负责把分析结果组装成可读性强的日报或个股跟踪纪要同时标注信息来源和风险未覆盖项。这套结构里有一条核心原则尽量让大模型做“选择题”和“填空题”而不是“自由论述题”。比如你问模型“这家公司的核心优势是什么”它可能写一篇小作文但你给它预设框架问“根据以下五个维度逐项评分并给出理由”它反而输出得更稳定、更容易校验。这不算限制模型能力而是投研场景本身需要一致性前后口径一致才能做归档和历史比对。2. 核心细节解析与实操要点2.1 信息源决策不能什么都往模型里扔我在早期犯过一个错误——把能抓到的数据全部塞给大模型以为信息越多结论越准。实测结果是模型确实会很认真地分析每一段话但信息间互相干扰结论反而模糊而且费用和耗时直线上升。后来我改用“预筛 精选”的策略。预筛规则用传统代码写死比如关键词白名单、信源域名权重、公告类型过滤这些规则便宜、稳定、速度快。只有通过了预筛的内容才会进入模型加工环节。这相当于给Agent装了一道闸门让它不要对所有信息都一视同仁而是优先处理高价值目标。具体到信息源名单我维护了三张白名单分别是政策与监管动态类只看国家级主管部门和行业协会官方发布这类信息一旦出现往往意味着行业拐点必须第一时间进入分析队列。产业链上下游快讯类重点覆盖行业头部企业的产能、价格、订单、开工率数据这类信息适合做趋势判断但要注意时效性和口径一致性。竞争对手与替代技术跟踪类这是大多数人容易忽略的但Agent最擅长做这种横向扫描它可以在海量信息里发现“某项专利公开可能影响现有格局”这类线索。每张名单都设了刷新周期短的一天查两次长的一周查一次避免无效请求浪费token。这套信息源体系的建设比模型选型更耗时但回报也最明显——输入质量直接决定输出质量喂给模型的东西越干净它的分析就越靠谱。2.2 文本加工链路从公告到结构化数据的必经步骤拿到一条原始信息后不能直接扔给Agent让它分析中间必须经过一道加工工序否则模型很容易被公告里的法务措辞带偏。我的加工链路是这样的先做格式标准化把PDF公告、网页正文、图片型研报统一转成纯文本注意PDF里扫描件要先做OCR否则后面的步骤全是白费。然后是段落拆分按公告的小标题或自然段落切块这一步很重要因为大模型对长文本的注意力会衰减切成块之后可以针对性地做局部判断。再下一步是要素提取提取公告里的公司名称、发生日期、事件类型、涉及金额、法律关系等关键字段这些字段是后续所有分析的物料基础。最后才进入观点分析阶段让模型基于这些字段做逻辑推理而不是让它对着整篇原文做开放式阅读理解。整套链路里最容易被忽视的是“要素提取”这一步。很多人觉得直接用模型读原文更省事但我实测下来先提取要素能显著降低幻觉概率。原因很简单模型在做填空题时几乎没有自由发挥空间但在做阅读题时它会用已有的知识去脑补原文没写的内容。要素提取的本质是把自由发挥的空间提前锁死。2.3 结构化输出的关键JSON Schema不只是给程序看的投研工作流里模型输出的结构稳定性和内容质量同等重要。如果你让模型“返回一份公司分析”它可能每次的Markdown标题层级都不一样程序解析时就会踩坑。我的做法是给Agent定义一个严格的JSON Schema并且这个Schema不是只给程序解析用它同时约束了模型的分析思路。举个例子我定义的分析结果结构是{ ticker: 公司代码, event_date: 事件日期, event_type: policy|industry|company|macro, impact_direction: positive|negative|neutral, confidence_score: 0.0, affected_business: 受影响的具体业务线, logic_chain: [原因1, 传导路径2, 影响结果3], risk_flag: true, sources: [来源URL1, 来源URL2] }我把这个Schema直接写进Prompt里并要求模型只返回一个合法的JSON对象不要额外解释。这听起来像技术细节但对投研工作的意义很大因为结构固定了分析师看报告时可以直接跳转到自己关心的字段程序也可以自动做数据聚合、跨期比对和异常值触发。比如confidence_score低于0.5的信息系统会自动降级为“参考消息”不会进入正式分析结论这就多了一道机器防错闸门。另外说一句JSON Schema定义好后最好先用固定的几条历史公告做回归测试确认模型每次都按设定结构返回再放开让它跑真实数据。不要指望第一次定义就完美字段命名不合理、枚举值覆盖不全这类问题都会在回归测试里暴露出来。3. 实操过程与核心环节实现3.1 基于Spring AI编排Agent的轻量工程实现选型阶段我调研过很多Agent框架最后选了Spring AI作为接入层。原因很现实我的数据处理管线里有大量Java基础组件文件解析、消息队列、定时调度都已经是现成的Spring AI可以让AI能力无缝嵌进现有架构不用为了引入Python技术栈把整个数据链路推翻重来。核心实现思路是把“采集—加工—产出”做成一个Pipeline每个环节模板化。我定义了三个核心抽象类public interface InfoCollector { ListRawDocument collect(); } public interface InfoAnalyzer { AnalysisResult analyze(RawDocument doc); } public interface ReportGenerator { Report generate(ListAnalysisResult results); }每个接口只做一件事然后用一个调度器把它们串起来。调度器用Spring的Scheduled注解定时触发我设定的频率是每天早上8点跑一次全量扫描盘前出日报下午3点收盘后再跑一次增量更新仅处理当天新增的信息源。InfoCollector的实现就是一个传统爬虫加规则过滤器这一层我刻意不用大模型纯靠关键字、域名权重、文本哈希去重等硬规则保证速度和成本都可控。InfoAnalyzer是核心它会调用大模型的接口内部先做要素提取再做逻辑链构建最终输出上面定义的JSON对象。ReportGenerator是最后一公里把多个AnalysisResult按行业和公司分组渲染成Markdown格式的早报文件存储到本地目录晚上定时推送到我的投研笔记系统里。这套架构的好处是每个模块可以单独替换。比如我今天觉得某家大模型的行业分析能力更强只需要改InfoAnalyzer里的模型调用参数对采集和生成模块完全无感。3.2 让大模型“主动干活”的机制有限自主循环“AI自主驱动”这个词听起来很玄学做起来其实是一套有限自主循环即在预设边界内让Agent自己决定下一步做什么但所有决策都要经过条件检查。我实现了一个轻量的Action Loop机制核心代码如下public class AgentLoop { private static final int MAX_STEPS 5; private int currentStep 0; private boolean shouldContinue true; public void run() { while (shouldContinue currentStep MAX_STEPS) { ActionResult action policyAgent.decideNextAction(); if (!action.isValid()) { shouldContinue false; break; } executeAction(action); currentStep; } } }在这个循环里policyAgent是根据当前状态决定下一步动作的模型。比如它读取了一份公司公告提取出“公司拟收购某上游矿产企业”这个事件它可能会决定下一步去检索该矿产企业的基本信息再决定下一步去比对同行业历史收购案例的估值水平最后才生成分析结论。每个动作执行前都要经过isValid检查校验动作类型是否在白名单内、目标参数是否完整、执行频率是否超限。这就像给Agent套了一根“安全绳”它可以在圈内自由走动但跑不出预设的边界。这个设计在投研场景里特别重要因为分析任务不像聊天不能无限发散必须在有限的几步内收敛出结论否则成本和时间都会失控。3.3 用“策略提示词”把投研框架固化进AgentPrompt工程是这个项目里投入产出比最高的一环。我试过直接把投研方法论写进Prompt也试过让模型自由发挥效果都不够好。最后用的是“策略树”套“结构化输出”的组合。策略树的意思是在Prompt里先定义分析主框架再用分支条件给模型指路。比如分析主线是“事件冲击评估”那么第一个分支是判断事件类型如果是政策类就走政策传导路径如果是公司类就走基本面重估路径。每条路径都附带具体的分析要点清单。下面是简化版Prompt的核心片段你是一位资深行业研究员请基于以下公告内容完成事件影响分析。 分析步骤 1. 先识别事件类型单选policy/industry/company/macro 2. 根据事件类型回答对应问题 - policy该政策影响哪些细分领域落地节奏如何对行业格局有何影响 - company该事项是扩张型/收缩型/中性型对短期业绩和长期价值分别有何影响 - industry该事件是否标志供需格局拐点传导链条是什么 - macro该事件对行业流动性和风险偏好的影响方向如何 3. 实际判断每步分析所用的依据来自哪些段落并在logic_chain中逐条引用。 特别注意 - 只输出合法JSON不要输出任何其他内容。 - 不确定的字段请填null不要编造数据。 - 如果公告内容不完整无法支撑结论risk_flag设置为true并在风险描述中说明缺失信息。把这套提示词固化进Agent之后输出的质量稳定性提升非常明显。之前模型经常输出“该事件对公司有积极影响但也存在不确定性”这种正确的废话现在因为框架限制了它必须给具体方向、给传导链条、给置信度废话的比例大幅下降。3.4 运行实测一次真实的情报分析全流程我拿一个涉及光伏产业链的案例跑了一遍完整流程这里把关键节点和输出明细分享出来方便你对照理解。当天采集层抓到了三条相关信息某头部硅料企业发布的产能扩建公告、某行业协会发布的月度装机数据、一条海外市场对华光伏组件需求变化的行业新闻。传统代码做的预筛全部通过进入加工层。InfoAnalyzer先对硅料企业公告做要素提取提取出“新增产能10万吨”“总投资约50亿元”“建设周期18个月”几个关键字段。事件类型被识别为companyimpact_direction被判定为neutral偏negative模型的逻辑链是短期看该产能不会立刻释放对供需格局影响有限但市场可能提前反应悲观预期压制板块估值。confidence_score给了0.62说明模型自己也觉得确定性不算高。装机数据那条被识别为industry事件impact_direction为positive逻辑链是“月度装机同比增长30% → 下游需求超预期 → 对硅料价格形成支撑”置信度0.81明显高于企业公告那条。海外新闻那条最终被判定为neutral因为信息来源单一、关键数据缺失risk_flag自动置为true系统没有把它纳入正式结论只在风险标注里提示“海外需求存在分歧建议持续跟踪”。最终生成的日报里三条信息按置信度从高到低排列每一段都附了来源引用和风险提示。整个过程从触发到完成大约用了6分钟其中大头是模型推理时间采集和文本处理只占了不到40秒。这个结果基本达到了我预期的“机器先把粗活干完人只做深度验证”的效果。4. 常见问题与排查技巧实录4.1 幻觉问题投研场景是重灾区但可以被压到很低大模型幻觉在投资场景里不是“小事”因为它可能会把一个不存在的订单、不存在的合作方写得有鼻子有眼。我在调试阶段就遇到过模型把“公司拟投资建设”误解为“公司已完成投资”导致分析结论完全偏离实际。压制幻觉我用了三招。第一招是上文提到的要素提取先行让模型先填事实字段不要在事实未定时就急着做分析。第二招是引用回指要求模型在logic_chain里明确标注“这段判断来自公告的哪一句”生成报告时每个结论后面都挂一个可点击的来源锚点这样不管模型对不对人回来复核时都能迅速定位到原始材料。第三招是置信度门槛confidence_score低于0.6的内容不进入正式摘要只在待跟踪清单里出现。这三招叠加之后我实测严重幻觉从早期的大约每十条信息出现一次降到了二十条以上才偶发一次。这里要特别强调完全消除幻觉不现实目标应该是“把幻觉影响控制在可复核范围内”。就像你不能让实习生永远不犯错但你可以要求他每一个结论都标注出处方便你事后检查。4.2 模型输出结构不稳定一个解析层面的“老坑”即使Prompt里写了“只返回JSON”模型偶尔还是会在JSON前后加一段解释性文字或者在里面漏一个逗号。这个问题在投研批量处理时会变成致命伤一个字段解析失败可能导致整条信息丢失。我的处理方法是在解析层做了双层保险。第一层是清理层解析前先把回复内容里的Markdown围栏和首尾非JSON字符全部剥离。第二层是重试层如果清理后JSON.parse仍然失败就调用一次修复接口把错误字符串和期望Schema一起发给模型让它输出修正后的合法JSON。这个方法实测能把结构解析成功率从92%左右提升到99%以上。另外还有一个细节JSON里如果包含“换行符”或“引号”等特殊字符很多时候不是模型的错而是原始公告文本里就有这些符号模型只是照抄。所以我在要素提取前会先做一次文本转义把所有控制字符统一替换成空格从源头规避这类问题。4.3 任务空转与重复执行给Agent加“心跳”和去重闸门自主循环最大的风险是Agent在一个无解的问题上反复横跳。比如它试图判断某政策的量化影响但数据明显不足它会不停地检索信息源每检索一步都要花费时间和token却始终无法收敛。我在AgentLoop里加了“最大步数”限制单次分析任务最多执行5个动作超过就强制收敛把已收集到的信息按低置信度输出同时置risk_flag。这套机制解决的是“过度发散”问题。另一个高发问题是重复执行。因为定时任务每6小时扫一遍信息源如果某条公告头条上没有做有效的去重就会被重复加工三次既浪费资源又会污染历史数据。我的去重逻辑是“源URL 公告日期 标题哈希”三元组联合判断只要三元组完全一致就判定为已处理直接跳过。这个方案简单、开销低目前还没有出现过误杀情况。4.4 限流、超时与模型选型工程侧最容易低估的麻烦如果你把Agent跑在生产环境模型API的限流和超时问题一定会找上门。投研日报是早晨集中跑的高峰时段一次性要分析几十条信息如果全部串行调用模型耗时可能超过半小时用户的耐心肯定不够。我的对策是给调度加上并发控制。先把所有待分析信息按优先级排序再开一个固定大小为5的并发池池内每条任务独立调用模型接口整体吞吐量大约是串行的4倍多。这里注意并发数不能开得太大否则触发限流反而更慢5是实测下来比较稳的平衡点。模型选型方面我的经验是不要迷信参数规模。分析公告、提取要素这类任务中等规模的模型完全够用而且响应速度更快、成本更低。只有涉及长逻辑链推理和跨领域知识融合时才有必要切到更大的模型。所以在我的架构里Action Loop里的policyAgent用高端模型但每个具体动作的执行器用低档模型这样既保证了决策质量又把预算控制住了。5. 后续扩展方向与个人体会5.1 从个股分析到行业图谱Agent下一步能做什么现在这套系统完成的是“点状分析”任务处理一条信息生成一份分析结果。但如果把时间维度拉长让Agent持续跟踪一个行业的全部关键主体它会慢慢积累出一张“行业关系图谱”。举例来说系统在连续三周里抓取到“硅料涨价”“组件排产下调”“电站招标量减少”三条信息单看任意一条都无法确认趋势但放在一起就能发现产业链价格传导正在受阻。这种跨时间的模式识别靠人眼盯数据很难发现但对Agent来说不过是把历史结果做了个关联比对。我计划下一步给Agent加一个“周度复盘”模式每周日自动把过去7天的全部分析结果重新读一遍生成行业景气度变化说明并输出下周需要重点监控的信号清单。这样AI就不只是“被动回答问题的工具”而是真正意义上的“主动盯盘助手”。5.2 我对AI自主驱动投研的个人体会项目跑了一段时间我有一个很深的感受AI自主驱动不等于“撒手不管”。它更像是把一个七成靠谱的实习生用起来你得定期检查作业、修正框架、喂高质量物料才能放心让他分担越来越多的工作。从效果上看这套系统目前帮我节省了每天大约一个半小时的信息预处理时间让我能把精力放到真正需要经验判断的地方。更重要的收获是因为所有分析都有结构化归档我发现自己的复盘效率比以前高了很多——历史判断、当时的逻辑链、后续实际情况一比对就知道哪里想对了、哪里想错了。现在每次看到有新的行业公告第一反应不再是去翻资料而是先看Agent输出的日报里有没有捕捉到这个信号。工具当然不可能替代人做投资决策但它确实能把“读材料、理逻辑、盯风险”这些基础工作做得又快又稳。对我个人来说这就已经值回票了。