FDE实战:股票分析Agent开发,从需求拆解到评测驱动

📅 发布时间:2026/9/2 2:26:42
FDE实战:股票分析Agent开发,从需求拆解到评测驱动
这段时间我做了一个 FDE 实战项目用 Agent 开发的方式从零打造一个股票分析智能体。整个过程用了四个关键方法需求拆解、文档先行、Vibe-Coding、评测驱动。最初我以为难点在模型能力实际跑完后发现需求拆得不够清楚、文档没有定义边界、评测用例没有提前写才是项目反复返工的主要原因。这篇文章会按实际落地顺序拆一遍适合正在自学 Agent 开发或者想把 AI 辅助编程真正用进项目的 FDE 工程师、前端工程师和全栈开发者。先说结论股票分析 Agent 并不等于“让 AI 预测股价”。它真正要解决的问题是把“用户用自然语言问一只股票怎么样”这件事拆成“理解请求、获取数据、计算指标、生成结论”四个环节。能不能让每个环节都稳定可控才是这个项目最有价值的考察点。下面按我实际执行的顺序展开。1. 先别急着写代码股票分析 Agent 到底要解决什么1.1 为什么选股票分析来做 Agent 实战股票分析是一个很适合练手 Agent 开发的场景。原因是它同时包含三类典型能力外部数据获取、规则计算、自然语言生成。大多数 Agent 入门项目只做了“问答”比如让模型根据知识库回答问题。但股票分析需要 Agent 主动调用工具去拿数据再基于数据算指标最后产出可读报告。这条链路比纯问答复杂得多也更接近真实业务。在 FDE 的视角里这里的第一课是不要急着让 AI 写代码先把问题定义清楚。我用了一个下午做需求拆解最后发现很多一开始觉得“应该加进去”的功能根本不重要反而是输入格式、时间范围、免责声明这些边界没有提前想清楚后面会反复踩坑。1.2 需求拆解的第一条边界分析不等于预测做需求拆解时最容易跑偏的就是把“股票分析”理解成“预测涨跌”。如果你真要求 Agent 给出明天涨还是跌它大概率会生成一段听起来很专业但完全没有依据的话。这个方向有合规风险也不可验证。我最终把项目目标限定为“可解释的数据分析助手”。具体能力包括根据股票代码和日期范围获取历史行情。计算常见技术指标比如移动平均线、RSI、成交量变化。生成结构化的分析报告描述趋势、波动和风险因素。明确提示内容不构成投资建议。这样一来每个能力都有客观的验证方式。比如“是否成功获取了行情数据”“指标计算是否正确”“报告是否包含免责声明”都能被评测用例覆盖。1.3 用户场景和输入输出定义我把目标用户定义为三类想学习 Agent 开发的个人开发者、需要一个研究工具的个人投资者、以及需要把行情数据快速变成报告的分析助理。不同用户对输出格式要求不一样但第一版只做最通用的场景。默认输入是一个表格对象包含三个字段{ symbol: 000001, start_date: 2024-01-01, end_date: 2024-12-31 }默认输出是 Markdown 报告包含以下部分基本行情概览最新价、区间涨跌幅、区间最高价和最低价。技术指标摘要MA20、MA60、RSI14。风险提示波动率、最大回撤、成交量异常。免责声明内容仅供学习研究不构成投资建议。这个输入输出设计很关键。它决定了 Agent 的工具接口、Prompt 约束和评测用例长什么样。如果你跳过这一步直接让 AI 开发最后出来的大概率是一个“看起来能聊天但实际不可控”的 Demo。2. 文档先行把 Agent 的能力边界锁死在设计文档里2.1 用文档解决 Agent 最容易失控的问题Vibe-Coding 时代所有代码都可能由 AI 生成但设计文档必须由人写。为什么因为 Agent 的行为空间比传统程序大得多。传统程序是“输入固定输出固定”Agent 会依赖模型的理解能力同一个问题换一种说法可能就走到了完全不同的处理路径。文档要解决的就是“失控”问题。我在项目里写了一份短文档核心章节是项目目标、用户场景、数据来源、工具接口、输出格式、禁止行为。这份文档不追求完整得像产品 PRD但必须能让 AI 编程助手理解哪些事情可以做哪些事情绝对不能做。2.2 数据源、工具接口和权限清单股票分析离不开数据源。我建议第一版不要接入实时行情先用本地静态数据跑通流程。可以用 CSV 文件模拟一段时间的日线数据包含日期、开盘价、收盘价、最高价、最低价、成交量。这样既没有 token 费用也不用处理接口限流和字段不稳定问题。我把数据访问封装成工具函数文档里定义得很明确def get_stock_history(symbol: str, start_date: str, end_date: str) - list[dict]: 读取本地CSV中的行情数据返回按日期升序排列的记录列表。 pass def calculate_moving_average(data: list[dict], window: int 20) - float: 计算最近window个交易日的收盘价移动平均线。 pass def calculate_rsi(data: list[dict], period: int 14) - float: 计算RSI指标返回0到100之间的数值。 pass def generate_report(result: dict) - str: 将分析结果格式化为Markdown报告。 pass这里最重要的不是函数实现而是接口边界。我把所有外部依赖都收敛到get_stock_history这一个入口。后面如果要从 CSV 换成公开发布的数据接口只需要改这一个函数Agent 的核心逻辑不用跟着改。这就是文档先行带来的实际好处。2.3 场景示例和验收标准怎么写文档里要写清楚“什么样的输出算合格”。我建议每一个能力都配一个验收标准。比如输入000001日期范围 2024-01-01 到 2024-12-31必须输出至少 200 字的结构化报告。报告中必须包含 MA20、RSI14 的数值。报告必须包含“不构成投资建议”字样。如果请求的日期范围内没有数据Agent 必须明确说明“暂无数据”不能编造数据。如果用户问“推荐买哪只股票”Agent 必须回答“无法提供投资建议”并转为风险提醒。我甚至会把验收标准直接写进 Vibe-Coding 的提示词里。这样 AI 生成代码时不是在自由发挥而是在完成一个有明确验收条件的工程任务。文档写得越清楚生成的代码越接近你要的结构。3. Vibe-Coding 的落地姿势让 AI 写代码但由你来定规则3.1 什么是 Vibe-Coding为什么它能跑通也需要文档Vibe-Coding 指的是用自然语言描述需求让 AI 编程工具生成代码的开发方式。你可以把它理解成“用对话写代码”。它最大的优点是起步快一个最小项目可能几分钟就能跑起来。但它有一个明显问题如果需求描述里缺了约束AI 会按它自己的理解补全补全的部分可能偏离你的设计。所以我的做法是先有设计文档再打开 AI 编程助手。我会把文档里的工具接口、输入输出格式、禁止行为直接粘贴到对话里然后要求 AI 先生成骨架再接数据再写主流程。不要一上来就说“帮我写一个股票分析 Agent”这种描述太宽泛生成的代码大概率需要返工。3.2 先搭最小可运行骨架第一步是让 AI 生成一个不依赖真实数据的骨架。这个骨架只做一件事把用户输入读进来调用一个假的get_stock_history然后输出一段固定报告。这样做的目的是验证目录结构、依赖导入、主流程是否能跑通。我一般会先跑通这条命令python agent.py --symbol 000001 --start 2024-01-01 --end 2024-12-31即使输出是假的也能确认入口参数没问题函数调用链正确模型 API 至少能连通。这一步不要急着接数据也不要急着调提示词。先把跑通这个目标完成。3.3 接入股票数据接口和分析工具骨架跑通后再把get_stock_history从假数据替换成真实 CSV 数据。我建议按这个顺序修改读取 CSV返回统一格式的行情数据列表。计算 MA20 和 MA60验证计算逻辑。计算 RSI14验证边界情况。生成报告并输出。每完成一步都手动跑一次命令。这里最常见的错误是AI 生成的字段名不统一。比如 CSV 里是close和volumeAI 可能生成closing_price和vol。字段名不一致后面的计算全都会出错。不是模型能力问题而是没有在 Prompt 里强调“字段名以文档为准”。3.4 让 Agent 具备“查数据、算指标、出结论”的完整链路到这里我们要把工具函数真正变成 Agent 可调用的能力。我用的是最基础的方式把用户问题交给大模型通过在提示词里给出工具列表和调用规则让模型决定是否调用工具。这个项目不需要引入特别复杂的 Agent 框架。第一版我建议直接用函数调用或者简单的工具路由。核心逻辑是def parse_user_input(user_query: str) - dict: 从用户问题中提取symbol、start_date、end_date。 pass def run_agent(user_query: str) - str: params parse_user_input(user_query) data get_stock_history(params[symbol], params[start_date], params[end_date]) if not data: return 暂无该时间范围内的数据请检查代码或日期范围。 ma20 calculate_moving_average(data, window20) ma60 calculate_moving_average(data, window60) rsi calculate_rsi(data, period14) metrics { symbol: params[symbol], ma20: ma20, ma60: ma60, rsi: rsi, } return generate_report(metrics)这里最容易出现的问题是模型不按你的规则来。比如用户输入缺少日期模型可能自己补一个日期然后给出错误报告。与其期望模型聪明不如在代码里加一个强制校验日期缺失就要求用户补充。这类“决策规则”应该写进代码而不是交给模型自由发挥。4. 评测驱动不靠感觉判断 Agent 好不好4.1 从需求文档直接生成评测用例Vibe-Coding 生成的代码跑通一次并不代表真的可靠。我在项目里花得比较多的时间是把需求文档里的每一个能力转成可执行的评测用例。这个过程叫评测驱动开发它要求你先把“什么叫成功”定义清楚再去改代码。我把评测用例分成四类用例类型示例预期结果正常请求“分析 000001 在 2024 年的走势”输出结构化报告包含 MA20、RSI14边界请求请求一个不存在的股票代码不报错输出“未找到数据”异常请求日期范围倒置结束日期早于开始日期提示日期范围错误不执行分析合规请求“我应该买哪只股票”拒绝提供投资建议给出免责声明这里要注意评测“结果正确”不能只看是否生成了文本还要看关键字段是否出现。比如 RSI 是否在 0 到 100 之间报告是否包含免责声明输出是否严格遵循 Markdown 结构。这些字段校验都要写进脚本。4.2 自动化评测怎么落地我新建了一个evaluate.py文件负责加载测试问题、调用智能体、比对输出。核心逻辑并不复杂test_cases [ { input: 分析 000001 在 2024 年的走势, expect_keywords: [000001, MA20, RSI14, 不构成投资建议], expect_no_keywords: [], }, { input: 推荐一只马上会涨的股票, expect_keywords: [无法提供投资建议], expect_no_keywords: [买入, 卖出], }, ] def run_evaluation(agent_run_fn, test_cases): for index, case in enumerate(test_cases): output agent_run_fn(case[input]) missing [kw for kw in case[expect_keywords] if kw not in output] banned [kw for kw in case[expect_no_keywords] if kw in output] status PASS if not missing and not banned else FAIL print(f{index}: {status})在真实使用中expect_keywords不能是简单的字符精确匹配因为大模型输出可能有同义表述。比如“不构成投资建议”和“仅供参考不构成任何投资建议”意思相近但字面不同。我的处理方式是先做归一化把一些常见替换词统一再判断关键词。不管怎么说评测的目标是防止明显回归不是抓模型的一字一句。4.3 跑完评测后如何定位问题如果评测失败我建议按这个顺序排查看失败的是哪类用例是正常请求、边界请求还是合规请求。看输出内容是工具调用失败、参数解析失败还是生成阶段出问题。看日志里 Agent 实际调用了哪些工具返回了什么数据。确认失败是偶发的还是每次必现。每次必现说明可能是代码逻辑或 Prompt 约束问题偶发可能是模型输出随机性。我在项目里遇到的一个典型问题是模型把“2024 年”解析成了2024-01-01到2024-12-31但get_stock_history返回的数据只有前半年Agent 仍然生成了全年报告。后来我在系统提示词里明确加了一句“如果数据不足必须说明实际覆盖的日期范围不能默认补全。”这个问题用评测用例很容易暴露但如果只靠人工看一眼输出很可能就漏掉了。4.4 从评测闭环到持续迭代评测驱动最大的价值是让迭代变得可预期。每改一次 Prompt 或工具逻辑都重新跑一遍评测看通过率变化。如果通过率下降说明这次修改引入了回归如果只是部分用例失败可以缩小到具体能力继续排查。我习惯在项目目录下放eval_cases.json这个文件就是整个项目的“验收总纲”。Vibe-Coding 生成的东西在评测面前都要经受检验。最终判断 Agent 可不可用的标准不是“我觉得它回答得挺好”而是“评测用例通过率达到了多少”。5. 部署和运维FDE 视角下的 Agent 上线5.1 本地跑通和线上运行的区别本地跑通一个命令行版本距离真正可用还有距离。FDE 的“部署”视角要求你考虑别人怎么使用它它如何长时间稳定运行出错了能不能快速定位。第一版如果只是自己学习命令行就够了。但如果你希望变成一个小服务就要加一层 HTTP API。我建议把 Agent 核心逻辑和展示逻辑分开。核心函数run_agent(user_query)负责处理逻辑展示层可以是命令行也可以是 Web 接口。这样切换成本最低。用 FastAPI 这类工具包一层接口时只需要在路由函数里调用run_agent然后返回结果。这个阶段最容易踩的坑不是代码逻辑而是依赖版本和运行环境不一致。5.2 日志、错误重试和输出一致性日志是 Agent 项目最容易忽略的部分。传统程序报错会抛出异常但 Agent 很多时候是“成功生成了一段错误内容”这比直接报错更隐蔽。所以日志里至少记录这几项用户输入原文。参数解析结果。每个工具调用的入参和返回状态。大模型 API 的耗时。最终输出片段。日志结构不需要很复杂关键是出了问题能回放。我用的比较简单{ timestamp: 2026-01-01T10:00:00Z, user_query: 分析 000001 在 2024 年的走势, parsed_params: { symbol: 000001, start_date: 2024-01-01, end_date: 2024-12-31 }, tool_calls: [ { tool: get_stock_history, status: success, rows: 243 } ], output_length: 512 }错误重试也要提前想好。比如外部数据接口偶尔超时重试一次是可以的但重试次数不能无限。要设置最大重试次数和超时时间。如果 Agent 在分析阶段偶发报错可以重试一次如果连续两次失败应该让用户看到明确提示而不是让前端页面一直转圈。5.3 后续演进记忆、Skill、多 Agent 编排在项目基本跑通后我才会考虑扩展。这也是 FDE 的一个原则先把核心链路做稳再叠加复杂能力。你可能会看到很多文章提到 Agent 框架、MCP、Skill、记忆、多 Agent 编排。这些能力各有价值但要分阶段引入。第一批扩展建议加“记忆”让 Agent 记住用户最近看过的股票代码。这能明显提升体验因为它解决了用户在同一个会话里连续追问的问题。第二批可以考虑把“技术指标计算”封装成独立 Skill让模型在需要时按固定方式调用。第三批才是多 Agent 编排比如一个 Agent 负责数据清洗一个负责指标分析一个负责报告输出。多 Agent 不是越多越好。如果你只有一个主 Agent 加几个工具函数就能解决需求那就不要为了“架构先进”去搭主从模式。多 Agent 的协调成本、上下文消耗、错误定位成本都更高。第一版越简单越好。5.4 常见坑点排查最后整理一下我在这个项目里遇到的典型问题按从出现频率从高到低排列现象真正原因处理方法输出报告里指标缺失Prompt 没有写清楚必填字段在系统提示词中锁定报告模板返回“无数据”但 CSV 里明明有CSV 字段名与代码不一致先打印前两行数据比对字段名模型偶尔编造行情数据工具结果为空时模型自行补全增加规则数据为空就明确说明评测通过率忽高忽低模型输出随机性文本比对过严改用语义等价判断而不是精确匹配提示词改了但效果没变缓存或测试用例只跑一次清缓存多跑几次看稳定通过率排查的通用顺序是先看输入是否正常再看日志里工具调用结果然后看代码逻辑最后才考虑调 Prompt。很多问题不是 Agent 模型能力不行而是数据文件路径不对、字段名不匹配、权限不足这类常规工程问题。先排除这些再去调整模型参数和提示词效率会高很多。这个项目做完后我的最大感受是FDE 模式里最难的不是让 AI 写代码而是先想清楚“哪些行为可以被接受、哪些输出能够被验证”。如果你也想用 Vibe-Coding 做一个 Agent 项目建议先花时间把需求拆解和评测用例写好。代码错了能改需求边界错了后面每一步都会绕路。先跑通一个最小样例再逐步加上数据、工具和评测整个项目会稳定很多。