编辑
2026-04-10
技术
00
请注意,本文编写于 92 天前,最后修改于 92 天前,其中某些信息可能已经过时。

目录

股票分析平台 × Harness 智能调度:从构想到落地的完整实践
一、项目缘起
二、什么是 Harness 智能调度系统?
传统 Timer vs Harness 智能调度
Harness 的四层架构
三、股票分析平台概览
项目定位
技术栈
核心架构
四、开发成果一览
五、本文档系列说明
六、关键决策记录
架构设计:4 Agent 协同的股票分析平台
一、设计哲学
二、整体架构图
三、4 Agent 详细设计
1. 行情 Agent(Market Data Agent)
2. 行业 Agent(Industry Agent)
3. 风险 Agent(Risk Agent)
4. 综合 Agent(Summary Agent)
四、策略生成模块
五、数据库设计
4 张核心表
六、前端页面架构
七、架构演进
Harness 智能调度:如何驱动开发整个股票分析平台
一、Harness 调度在股票项目中的实际工作流
整体工作流
调度循环详解
二、智能调度 vs 传统 Timer 的对比
真实案例对比
关键差异
三、Cron 执行记录分析
执行统计
成功执行的典型案例
超时分析
四、关键调度策略
策略 1:进度文件驱动
策略 2:tmux 会话管理
策略 3:失败自适应
策略 4:优先级排序
五、Harness 调度系统的技术实现
OpenClaw Cron 配置
Harness 核心能力在股票项目中的应用
六、经验与教训
做得好的
可以改进的
开发流程:Phase 0-10 完整记录
Phase 0:基础架构(~30 分钟)
目标
执行内容
关键决策
验证
Phase 1:数据采集与分析(~45 分钟)
目标
执行内容
1. 新浪实时行情采集器
2. adata 数据采集器(备用)
3. 统一采集器(自动降级)
4. 分析执行引擎(4 Agent 协同)
端到端测试(600519 贵州茅台)
验证
Phase 2:分析引擎增强(~30 分钟)
目标
执行内容
1. 资金流向分析
2. 多因子目标价位模型
3. LLM 分析器升级
4. 概念板块分析
5. 仓位策略生成
集成
测试
Phase 3:前端增强(~40 分钟)
目标
执行内容
1. 数据库模型扩展
2. 分析详情页模板
3. 新路由
4. K 线数据 API
验证
Phase 3.5:K线图修复(~15 分钟)
问题
根因
修复
验证
Phase 4:生产化准备(~90 分钟)
目标
执行内容
1. Celery 异步任务队列
2. 优先级队列
3. 批量股票分析
4. 监控告警系统
5. Redis 缓存层
6. MySQL 迁移
批量 DB commit 优化(性能瓶颈)
Phase 5:功能增强(~45 分钟)
目标
执行内容
1. 批量分析页面(/batch)
2. 历史记录查询页面(/history)
3. 自选股订阅推送(/watchlist)
新增 API 路由
Phase 6:文档 + 性能分析(~30 分钟)
执行内容
Phase 7:微信推送集成(~45 分钟)
目标
执行内容
Phase 8:生产环境部署配置(~45 分钟)
创建文件(11 个)
关键修复
Phase 9:测试修复(~30 分钟)
目标
修复内容
测试结果
Phase 10:API 认证/鉴权(~30 分钟)
问题
修复
测试
完整时间线
踩坑记录:股票分析平台开发中的 10 个坑
坑 1:Python 3.13 + Celery prefork 不兼容 🔴
现象
根因分析
解决方案
教训
坑 2:批量任务 Registry 内存不共享 🟠
现象
根因
解决方案
教训
坑 3:None 值比较导致策略生成崩溃 🟠
现象
根因
解决方案
教训
坑 4:ECharts 实例冲突导致 K 线图不显示 🟡
现象
根因
解决方案
教训
坑 5:Celery 队列名称不匹配 🟡
现象
根因
解决方案
教训
坑 6:Flask SECRET_KEY 硬编码 🔴
现象
解决方案
教训
坑 7:批量 DB commit 性能瓶颈 🟡
现象
根因
解决方案
效果
教训
坑 8:adata API 不可用的陷阱 🟡
现象
根因
解决方案
教训
坑 9:APScheduler 多进程重复执行 🟠
现象
解决方案
教训
坑 10:IDOR 漏洞(未授权访问)🔴
现象
解决方案
教训
坑汇总统计
经验总结:为什么这次开发这么顺利?
一、核心问题:之前为什么卡?
1. 过度设计
2. 缺乏实际项目验证
3. 调度粒度太粗
二、这次为什么顺?
1. 进度文件驱动(单一事实来源)
2. 明确的分阶段开发
3. 模型选择对了
4. 允许失败,快速迭代
5. 即时验证,快速反馈
三、对比分析:之前 vs 这次
四、核心经验
✅ 做对了什么
❌ 做错了什么
💡 下次改进
五、Harness 系统的真正价值
六、给读者的建议
技术栈详解:Flask + Celery + MySQL + Redis + ECharts
一、为什么选这些技术?
Flask 3.1.0
Celery 5.6.0 + Redis
MySQL 8.0.36(阿里云 RDS)
Redis
ECharts 5(K 线图)
二、依赖关系图
三、requirements.txt
四、部署架构
Gunicorn 配置
测试验证:38/38 测试全通过
一、测试概览
二、核心单元测试
1. 概念板块分析测试(5/5)
2. 策略生成器测试(5/5)
3. 分析引擎测试(13/13)
三、Mock 设计
四、端到端功能验证
验证 1:600519 贵州茅台端到端分析
验证 2:批量分析
验证 3:Celery 异步执行
验证 4:前端页面
五、测试运行命令
六、测试演进
测试修复过程
七、未覆盖的测试
股票分析平台 × Harness 开发经验文档
📚 文档目录
🎯 快速导航
如果你是开发者,想看代码
如果你对 Harness 系统感兴趣
如果你想复现这个项目
如果你踩过坑想找答案
如果你想了解成功秘诀
📊 项目数据
🏗️ 技术栈
📁 项目结构
📌 关键链接
💡 核心经验

股票分析平台 × Harness 智能调度:从构想到落地的完整实践

作者:Youn & AI 协同
项目周期:2026-04-09 21:00 → 2026-04-10 12:00(约 15 小时)
代码量:7200+ 行,42 个文件,11 个 Git commits
测试通过率:38/38(100%)


一、项目缘起

我有一个需求:搭建一个能自动分析 A 股股票的平台,不只是简单查个行情,而是要像专业的分析师一样——看实时行情、做技术分析、评估风险、搜索行业信息、最后给出买卖建议和目标价位。

这不是一个小项目。它涉及:

  • 多数据源采集(新浪 API + adata 金融数据)
  • 4 个 Agent 协同分析
  • 异步任务队列
  • 前端监控页面 + K 线图
  • 批量分析 + 告警推送
  • 生产级部署配置

如果按传统方式一个人写,至少得好几天。但这次我用了自己之前研发的 Harness 智能调度系统 来驱动整个开发流程,结果出乎意料地顺利——15 小时从 0 到 1 完成全部 Phase 0-10,38 个测试全部通过。

这篇文章不是简单的项目介绍,而是完整记录 Harness 系统如何在实际开发中发挥作用,包括架构设计、调度机制、开发流程、踩坑经验,以及为什么这次开发比之前顺畅那么多。


二、什么是 Harness 智能调度系统?

Harness 是我基于 OpenClaw 多 Agent 协同能力搭建的一套智能调度框架。它的核心理念是:

用 LLM 做调度决策,而不是简单写死一个 cron 定时任务。

传统 Timer vs Harness 智能调度

维度传统 TimerHarness 智能调度
决策方式固定时间触发LLM 判断当前该做什么
进度监控无,执行完就结束实时追踪,随时汇报
错误恢复手动重启自动从检查点恢复
质量保障多 Agent 对抗评审
问题上报被动发现主动推送给人工介入

简单来说,Harness 不是"每隔 15 分钟跑一次脚本",而是:

任务下发 → LLM 分析当前状态 → 选择合适的 Agent → 执行任务 → 监控进度 → 对抗评审 → 上报结果 ↑ │ └────────────────────── 反馈循环 ←─────────────────────────────────────────────┘

Harness 的四层架构

┌─────────────────────────────────────────────────────────────────┐ │ Harness Orchestrator │ │ (智能调度中心 - LLM 驱动) │ ├─────────────────────────────────────────────────────────────────┤ │ Task Queue → Scheduler → Monitor │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Multi-Agent Layer │ │ │ │ Planner Agent → Executor Agent → Critic Agent → Reviewer│ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ State Management │ │ │ │ Checkpoint Store → Progress Tracker → Issues Log │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘

在本次股票分析平台开发中,我扮演 Orchestrator(调度中心) 的角色,Claude Code(glm-5 模型)扮演 Executor Agent,通过 OpenClaw cron 任务每 15 分钟巡检一次进度,发现问题立即介入,完成一个 Phase 后自动推进下一个。


三、股票分析平台概览

项目定位

一个基于多 Agent 协同的智能股票分析系统,集成实时行情采集、技术分析、风险评估和 LLM 综合分析,支持批量分析、实时监控和微信推送。

技术栈

Backend: Flask 3.1.0 + SQLAlchemy 3.1.1 + Celery 5.6.0 Data: 新浪财经 API + adata 2.9.5 + 百炼 WebSearch Async: Redis (Broker + Cache) + Celery threads pool Frontend: Bootstrap 5 + ECharts 5 (K线图) + jQuery LLM: 通义千问 qwen-plus(分析 + 搜索) Database: MySQL 8.0.36(阿里云 RDS,开发期用 SQLite)

核心架构

Flask App (run.py) ↓ HTTP API Routes (main.py) ↓ 统一调度 AnalysisEngine (多Agent协同) ├── 行情Agent (Sina 实时行情) ├── 行业Agent (技术分析 + 概念板块) ├── 风险Agent (风险评估 + 百炼搜索) └── 综合Agent (LLM分析 + 策略生成) ↓ Celery Task Queue (异步执行) ↓ Redis (Broker + Cache + Result) ↓ MySQL (生产) / SQLite (开发)

四、开发成果一览

Phase内容耗时关键成果
Phase 0基础架构~30minFlask 项目结构、数据源确认
Phase 1数据采集与分析~45min4 Agent 分析引擎、端到端验证
Phase 2分析引擎增强~30min资金流向、目标价位、策略生成
Phase 3前端增强~40min详情页、ECharts K线图
Phase 3.5K线图修复~15minECharts 实例冲突修复
Phase 4生产化准备~90minCelery、批量分析、Redis 缓存
Phase 5功能增强~45min批量页面、历史记录、自选股
Phase 6文档 + 性能分析~30minREADME、性能瓶颈发现
Phase 7微信推送集成~45minAlertMonitor 自动推送
Phase 8生产部署配置~45minGunicorn + Systemd + Nginx
Phase 9测试修复~30min23/23 单元测试通过
Phase 10API 认证~30minJWT 认证 + IDOR 修复
总计~15h7200+ 行代码

五、本文档系列说明

本系列共 8 篇文章,从不同维度记录这次开发实践:

序号文档内容
00概述项目介绍 + Harness 开发模式(本篇)
01架构设计系统架构 + 4 Agent 协同设计
02Harness调度Harness 如何驱动开发 + 智能调度机制
03开发流程Phase 0-10 完整开发流程
04踩坑记录遇到的问题 + 解决方案
05经验总结为什么这次顺利 + 对比之前卡顿
06技术栈Flask + Celery + MySQL + Redis + ECharts
07测试验证38/38 测试通过 + 功能验证

六、关键决策记录

在整个开发过程中,有几个关键决策影响了项目走向:

  1. 数据源选择:新浪 API 为主(已验证可用),adata 为备用(Eastmoney 部分阻断)
  2. 数据库策略:开发期用 SQLite(快速迭代),生产迁移到 MySQL(阿里云 RDS)
  3. Celery 线程池:Python 3.13 下 prefork 有问题,改用 threads pool
  4. 搜索集成:百炼 WebSearch(qwen-plus + enable_search)替代单独 API
  5. 异步优先:Celery 异步任务队列,threading 作为 fallback

下一篇文章将深入讲解系统架构设计和 4 Agent 协同机制。

架构设计:4 Agent 协同的股票分析平台

核心问题:如何让 AI 像真正的分析师团队一样工作?
解决方案:4 个专业化 Agent,各司其职,协同分析


一、设计哲学

传统股票分析工具的做法是:写一堆规则,根据技术指标算出买卖信号。这种做法有两个致命问题:

  1. 规则写死,无法适应市场变化
  2. 缺少上下文,不知道行业动态和风险因素

我们的方案是让 AI 扮演一个分析师团队,每个 Agent 像一个专业领域的分析师:

  • 行情 Agent → 交易员:负责数据采集
  • 行业 Agent → 行业研究员:做技术分析和概念板块研究
  • 风险 Agent → 风控专员:评估风险,搜索负面信息
  • 综合 Agent → 投资顾问:汇总所有信息,给出最终建议

每个 Agent 只做自己擅长的事,最后由综合 Agent 汇总输出。


二、整体架构图

┌──────────────────────┐ │ Flask App (run.py) │ │ 端口: 5003 │ └──────────┬───────────┘ │ ┌──────────▼───────────┐ │ AnalysisEngine │ │ (多 Agent 协调器) │ └──────────┬───────────┘ │ ┌────────────────┼────────────────┐ │ │ │ ┌─────────▼─────┐ ┌───────▼──────┐ ┌──────▼───────┐ │ 行情 Agent │ │ 行业 Agent │ │ 风险 Agent │ │ Market Data │ │ Industry │ │ Risk │ └───────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Sina API │ │ 技术分析 │ │ 技术风险评估 │ │ adata 备用 │ │ 概念板块 │ │ 百炼风险搜索 │ │ 历史K线 │ │ MA5/MA20 │ │ 波动率/回撤 │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ └────────────────┼────────────────┘ │ ┌──────────▼───────────┐ │ 综合 Agent │ │ Summary Agent │ │ (LLM 分析 + 策略) │ └──────────┬───────────┘ │ ┌──────────▼───────────┐ │ 分析结果 │ │ 建议/目标价/仓位/策略 │ └──────────┬───────────┘ │ ┌──────────▼───────────┐ │ MySQL / SQLite │ │ 4 张表存储 │ └──────────────────────┘

三、4 Agent 详细设计

1. 行情 Agent(Market Data Agent)

职责:采集实时行情和历史数据

数据源

  • :新浪财经 API(已验证可用)
  • :adata 金融数据(Eastmoney 部分阻断)

核心代码

python
# sina_collector.py class SinaCollector: def get_realtime_quote(self, stock_code): """获取实时行情""" url = f"https://hq.sinajs.cn/list={stock_code}" response = requests.get(url, headers=self.headers) data = response.text.split('"')[1].split(',') return { 'name': data[0], 'open': float(data[1]), 'current_price': float(data[3]), 'high': float(data[4]), 'low': float(data[5]), 'volume': int(data[8]), } def get_history_kline(self, stock_code, days=60): """获取历史 K 线数据""" # 返回 60 条 OHLCV 数据

统一采集器实现了自动降级:

python
# unified_collector.py class UnifiedCollector: def get_realtime(self, stock_code): try: return self.sina_collector.get_realtime_quote(stock_code) except Exception: return self.adata_collector.get_realtime(stock_code) # 自动降级

2. 行业 Agent(Industry Agent)

职责:技术分析 + 概念板块研究

分析维度

维度计算方法用途
MA5最近 5 日收盘价均值短期趋势判断
MA20最近 20 日收盘价均值中期趋势判断
波动率标准差 / 均值风险评估
涨跌幅(当前 - 前日) / 前日短期动量
概念板块adata get_plate_east所属板块和热点

概念板块分析是这次开发的一个重要发现:

python
# concept_analyzer.py class ConceptAnalyzer: def analyze(self, stock_code): # 获取所属板块 plates = adata.get_plate_east(stock_code=stock_code) # 获取概念信息 concepts = adata.get_concept_east(stock_code=stock_code) # 识别热点板块 hot_plates = self._identify_hot_plates(plates, concepts) return { 'plate_info': plates, 'concept_info': concepts, 'hot_plates': hot_plates, 'plate_summary': self._generate_summary(plates, concepts) }

关键发现get_plate_eastget_concept_east 可用,但 concept_constituent 不可用。这个发现是通过实际测试得出来的,避免了后续走弯路。

3. 风险 Agent(Risk Agent)

职责:技术风险评估 + 联网搜索风险因素

风险评估模型

python
# risk_agent 分析逻辑 def assess_risk(kline_data, realtime_data): risks = [] # 1. 波动率风险 volatility = calculate_volatility(kline_data) if volatility > HIGH_VOL_THRESHOLD: risks.append({ 'type': 'high_volatility', 'level': 'high', 'description': f'波动率 {volatility:.2%} 超过阈值' }) # 2. 回撤风险 max_drawdown = calculate_max_drawdown(kline_data) if max_drawdown > MAX_DRAWDOWN_THRESHOLD: risks.append({ 'type': 'max_drawdown', 'level': 'medium', 'description': f'最大回撤 {max_drawdown:.2%}' }) # 3. 趋势风险 ma5 = calculate_ma(kline_data, 5) ma20 = calculate_ma(kline_data, 20) if ma5 < ma20: risks.append({ 'type': 'downtrend', 'level': 'medium', 'description': 'MA5 低于 MA20,处于下降趋势' }) # 4. 联网搜索风险因素 search_risks = bailian_search(f'{stock_name} 风险 利空') return { 'technical_risks': risks, 'news_risks': search_risks, 'overall_risk_level': self._calculate_overall_risk(risks) }

百炼搜索集成:风险 Agent 不仅做技术面分析,还会用百炼 WebSearch 联网搜索该股票的风险因素,这是传统量化平台做不到的。

4. 综合 Agent(Summary Agent)

职责:汇总所有 Agent 的输出,调用 LLM 生成最终分析建议

输入

{ 'realtime_data': {...}, # 行情数据 'kline_data': [...], # K线数据 'technical_analysis': {...}, # 行业分析结果 'plate_summary': '...', # 板块分析 'risk_assessment': {...}, # 风险评估 'capital_flow': {...}, # 资金流向 'target_price': {...} # 目标价位 }

LLM Prompt 设计

python
prompt = f""" 你是一个专业的A股分析师,请基于以下信息给出投资建议: 【实时行情】 股票:{name}({code}),现价 ¥{current_price} 涨跌幅:{change_pct:.2%} 【技术分析】 MA5: {ma5}, MA20: {ma20} 趋势:{trend} 波动率:{volatility:.2%} 【板块信息】 {plate_summary} 【风险评估】 技术风险:{technical_risks} 搜索风险:{news_risks} 【资金流向】 {capital_flow_summary} 请给出: 1. 投资建议(buy/hold/sell) 2. 置信度(0-100) 3. 目标价位 4. 止损价位 5. 仓位建议(0-100%) 6. 分析理由(3-5条) """

输出

python
{ 'recommendation': 'hold', # buy / hold / sell 'confidence': 72, # 置信度百分比 'target_price': 1520.0, # 目标价 'stop_loss': 1297.22, # 止损价 'position_suggestion': 60, # 建议仓位% 'reasons': [...], # 分析理由 'risks': [...], # 风险因素 'catalysts': [...] # 催化剂 }

四、策略生成模块

综合 Agent 的 LLM 分析结果会进一步输入到 策略生成器

python
# strategy_generator.py class StrategyGenerator: def generate(self, llm_result, target_price, capital_flow): action = llm_result.get('recommendation') if action == 'buy': return self._buy_strategy(llm_result, target_price, capital_flow) elif action == 'sell': return self._sell_strategy(llm_result, target_price) else: return self._hold_strategy(llm_result, target_price) def _buy_strategy(self, llm_result, target_price, capital_flow): """分批建仓策略:30% + 30% + 40%""" current_price = llm_result.get('current_price') return { 'action': 'buy', 'entry_price': current_price, 'stop_loss': target_price.get('support'), 'take_profit': target_price.get('resistance'), 'batch_plan': [ {'batch': 1, 'ratio': '30%', 'price': current_price}, {'batch': 2, 'ratio': '30%', 'price': current_price * 0.98}, {'batch': 3, 'ratio': '40%', 'price': current_price * 0.95}, ], 'reasoning': '分批建仓降低平均成本' }

三种策略:

  • buy:30% + 30% + 40% 分批建仓
  • sell:50% + 50% 分批减仓
  • hold:给出观望条件和加减仓触发条件

五、数据库设计

4 张核心表

sql
-- 1. 调研数据表 CREATE TABLE research_data ( id VARCHAR(36) PRIMARY KEY, stock_code VARCHAR(10), analysis_id VARCHAR(36), agent_type VARCHAR(20), -- market/industry/risk/summary data JSON, created_at TIMESTAMP ); -- 2. 分析结果表(Phase 3 扩展) CREATE TABLE analysis_results ( id VARCHAR(36) PRIMARY KEY, stock_code VARCHAR(10), recommendation VARCHAR(10), confidence INT, target_price DECIMAL(10,2), entry_price DECIMAL(10,2), -- Phase 3 新增 stop_loss DECIMAL(10,2), position_suggestion INT, strategy_details JSON, -- Phase 3 新增 user_id VARCHAR(36), -- Phase 10 新增 created_at TIMESTAMP ); -- 3. Agent 进度表 CREATE TABLE agent_progress ( id VARCHAR(36) PRIMARY KEY, analysis_id VARCHAR(36), agent_type VARCHAR(20), status VARCHAR(20), progress INT, created_at TIMESTAMP ); -- 4. 市场数据表 CREATE TABLE market_data ( id VARCHAR(36) PRIMARY KEY, stock_code VARCHAR(10), data_type VARCHAR(20), -- realtime/history data JSON, created_at TIMESTAMP );

六、前端页面架构

Flask App (5003) ├── / → 监控首页(实时进度 + 分析结果列表) ├── /detail/<id> → 分析详情页(K线图 + 策略详情) ├── /batch → 批量分析页面 ├── /history → 历史记录查询 ├── /watchlist → 自选股管理 └── API Routes ├── /api/analysis/* → 分析任务 API ├── /api/batch/* → 批量分析 API ├── /api/history/* → 历史记录 API ├── /api/watchlist/* → 自选股 API ├── /api/alerts/* → 告警 API └── /api/health → 健康检查

K线图实现(ECharts 5):

javascript
// detail.html const chart = echarts.init(document.getElementById('kline-chart')); chart.setOption({ xAxis: { type: 'category', data: dates }, yAxis: { scale: true }, series: [{ type: 'candlestick', data: ohlcData, itemStyle: { color: '#ef232a', // 红涨 color0: '#14b143', // 绿跌 borderColor: '#ef232a', borderColor0: '#14b143' } }, { type: 'line', data: ma5Data, // MA5 均线 lineStyle: { color: '#ff9900' } }, { type: 'line', data: ma20Data, // MA20 均线 lineStyle: { color: '#3399ff' } }], dataZoom: [{ type: 'slider' }] // 滑动条 });

七、架构演进

阶段架构特点
Phase 0-1同步调用简单直接,但会阻塞请求
Phase 4Celery 异步不阻塞,支持并发,可监控
Phase 4Redis 缓存减少重复计算,5分钟 TTL
Phase 43 优先级队列high > default > low
Phase 7微信推送告警自动推送到手机
Phase 10JWT 认证API 安全保护,用户隔离

下一篇文章将深入讲解 Harness 如何驱动整个开发流程。

Harness 智能调度:如何驱动开发整个股票分析平台

核心机制:Cron 定时巡检 + LLM 判断状态 + tmux 驱动 Claude Code
执行时间:2026-04-09 21:00 → 2026-04-10 12:00
Cron 执行次数:43 次(成功 ~20 次,超时/失败 ~23 次)


一、Harness 调度在股票项目中的实际工作流

整体工作流

┌──────────────────────────────────────────────────────────────┐ │ OpenClaw Cron(每15分钟) │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 1. 读取 progress.md 了解当前进度 │ │ │ │ 2. 检查服务状态(Flask + Celery + Redis) │ │ │ │ 3. LLM 分析当前状态,判断下一步该做什么 │ │ │ │ 4. 通过 tmux 给 Claude Code 发送明确指令 │ │ │ │ 5. 等待 Claude Code 执行 │ │ │ │ 6. 验证执行结果,更新 progress.md │ │ │ │ 7. 汇报进度(成功时推送给用户) │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ tmux: stock-analysis 会话 │ │ Claude Code (glm-5 模型) │ │ 执行具体的编码任务 │ └──────────────────────────────────────────────────────────────┘

调度循环详解

每一次 cron 执行,都会经历以下步骤:

Step 1:状态感知

bash
# 检查进度文件 read progress.md # 检查服务状态 curl -s http://127.0.0.1:5003/api/health # 返回: {"celery": "ok", "workers": 1, "pool": "threads", "concurrency": 6} # 检查 tmux 会话 tmux list-sessions # 返回: stock-analysis: 1 windows

Step 2:LLM 决策

基于收集到的信息,LLM 会做出调度决策:

当前状态分析: - Phase 1 已完成,端到端验证通过 ✅ - Phase 2 未开始 - 服务运行正常 决策:推进 Phase 2(分析引擎增强) 具体指令: 1. 创建 concept_analyzer.py 2. 创建 strategy_generator.py 3. 集成到 analysis_engine.py 4. 创建单元测试

Step 3:任务下发

通过 tmux 发送指令给 Claude Code:

bash
tmux send-keys -t stock-analysis " 请在 backend/app/services/ 目录下创建以下文件: 1. concept_analyzer.py - 概念板块分析模块 - 使用 adata.get_plate_east() 获取板块信息 - 使用 adata.get_concept_east() 获取概念信息 - 输出: plate_info, concept_info, hot_plates, plate_summary 2. strategy_generator.py - 仓位策略生成模块 - 输入: LLM分析结果 + 目标价位模型 + 资金流向 - 三种策略: buy(分批建仓30/30/40), sell(分批减仓50/50), hold - 输出: action, entry_price, stop_loss, take_profit, batch_plan 创建完成后更新 progress.md 并汇报。 " Enter

Step 4:监控与验证

bash
# 等待 Claude Code 完成 # 检查 tmux 输出 tmux capture-pane -t stock-analysis -p # 验证文件是否创建 ls -la backend/app/services/concept_analyzer.py ls -la backend/app/services/strategy_generator.py # 验证语法 python3 -m py_compile backend/app/services/concept_analyzer.py # 验证服务状态 curl -s http://127.0.0.1:5003/api/health

Step 5:进度更新

python
# 更新 progress.md edit(progress.md, old="Phase 2: 🔄 进行中", new="Phase 2: ✅ 已完成 - 2026-04-09")

Step 6:汇报

cron 执行成功后,通过 OpenClaw 的消息系统推送进度报告给用户。


二、智能调度 vs 传统 Timer 的对比

真实案例对比

这次股票平台开发过程中,cron 执行了 43 次,其中约 20 次成功推进了开发,23 次超时或失败。但这正是智能调度的优势所在:

场景传统 TimerHarness 智能调度
Claude Code 还在写代码不管三七二十一,再次触发 → 冲突检测到 tmux 会话活跃 → 等待或只做检查
遇到技术障碍卡住了继续执行,重复失败LLM 分析失败原因 → 换策略 → 手动创建文件突破
一个 Phase 完成了不知道,继续执行旧指令读取 progress.md → 发现 Phase 完成 → 推进下一个 Phase
服务挂了不关心,继续执行脚本检测到 Flask 不响应 → 先重启服务 → 再继续开发
需要用户输入(MySQL 密码)报错退出识别阻塞原因 → 记录待办 → 跳过继续其他开发

关键差异

传统 Timer 的问题

python
# 固定脚本,缺乏感知能力 def timer_job(): run_script() # 不管当前状态,直接执行 send_report()

Harness 的方式

python
# LLM 驱动的决策循环 def harness_cycle(): state = gather_state() # 读取 progress.md、检查服务、查看 tmux decision = llm.analyze(state) # LLM 判断该做什么 if decision.action == "push_code": send_to_claude_code(decision.instructions) elif decision.action == "wait": log("Claude Code 正在工作,等待...") elif decision.action == "escalate": notify_user(decision.reason) update_progress(decision.new_phase)

三、Cron 执行记录分析

执行统计

stock-analysis-dev.jsonl 中可以看到 43 次执行记录:

结果次数占比说明
成功(status: ok)~20~47%成功推进开发
超时(timeout)~18~42%Claude Code 耗时过长
失败(edit 失败等)~5~11%文件编辑冲突或其他错误

成功执行的典型案例

案例 1:Phase 2 概念板块 + 策略生成

执行时间: 2026-04-09 22:48 耗时: 341 秒 模型: glm-5 成果: - concept_analyzer.py 创建 - strategy_generator.py 创建 - 集成到 analysis_engine.py - 单元测试文件创建

案例 2:Celery Python 3.13 兼容修复

执行时间: 2026-04-10 00:25 耗时: 491 秒 模型: glm-5 成果: - 诊断 Python 3.13 prefork 问题 - 移动冲突文件 app.py → app.py.bak - 设置 PYTHONPATH - 使用 threads pool 启动 Celery - 验证 600519 分析成功

案例 3:批量 DB commit 优化

执行时间: 2026-04-10 02:45-03:10 成果: - 发现 9 处 db.session.commit() - 优化为批量 add_all() + 统一 commit() - 20 只股票: 140 commits → 4 commits - 性能提升 96%

超时分析

大部分超时发生在 Claude Code 需要较长时间编码的场景:

  • 创建多个文件 + 集成测试 → 耗时 5-10 分钟 → 超时
  • 复杂 Bug 修复(如 Celery 配置)→ 需要多次尝试 → 超时

这是正常的:超时不代表失败,只是说明 Claude Code 还在工作。下一次 cron 执行时,LLM 会检测到 tmux 会话仍在运行,选择不重复发送指令,而是等待或做其他检查。


四、关键调度策略

策略 1:进度文件驱动

progress.md 是整个调度的单一事实来源。每次 cron 执行第一件事就是读取它:

markdown
## 当前状态: Phase 10 完成 ### ✅ Phase 0: 基础架构 (已完成) ### ✅ Phase 1: 数据采集与分析 (已完成) ...

通过解析这个文件,LLM 能准确知道:

  • 哪些 Phase 已完成
  • 当前在进行哪个 Phase
  • 哪些是待办事项

策略 2:tmux 会话管理

Claude Code 在 stock-analysis tmux 会话中运行,这使得:

  • 持久化:即使 cron 超时,Claude Code 仍在工作
  • 可观察:随时可以 tmux capture-pane 查看输出
  • 可控制:通过 tmux send-keys 发送新指令
bash
# 发送指令 tmux send-keys -t stock-analysis "创建 concept_analyzer.py..." Enter # 查看输出 tmux capture-pane -t stock-analysis -p -S -50 # 重启会话(如果需要) tmux kill-session -t stock-analysis tmux new-session -d -s stock-analysis "claude --model glm-5"

策略 3:失败自适应

当 Claude Code 陷入 plan mode 循环或执行卡住时:

问题: Claude Code 反复进入 plan mode,不实际执行 解决: 直接在 OpenClaw 中手动创建文件,突破僵局
问题: edit 工具冲突(文件被 Claude Code 同时修改) 解决: 等待 Claude Code 完成,或使用 exec 直接操作文件

策略 4:优先级排序

LLM 根据以下优先级决定下一步:

P0: 服务是否正常运行(Flask + Celery + Redis) P1: 当前 Phase 是否有阻塞问题需要解决 P2: 按 Phase 顺序推进 P3: 性能优化和代码质量改进 P4: 文档完善

五、Harness 调度系统的技术实现

OpenClaw Cron 配置

json
{ "jobId": "stock-analysis-dev", "schedule": { "kind": "every", "everyMs": 900000 // 15 分钟 }, "payload": { "kind": "agentTurn", "message": "作为股票分析平台开发协调员,检查当前进度并推进开发..." }, "delivery": { "mode": "announce" } }

Harness 核心能力在股票项目中的应用

Harness 能力在股票项目中的应用
多 Agent 协同4 Agent 分析架构(Planner→Executor→Critic→Reviewer 的隐喻)
持续监控cron 每 15 分钟巡检项目状态
检查点progress.md 作为进度检查点
问题上报遇到阻塞时推送给用户(如 MySQL 密码需求)
对抗评审LLM 分析决策时自我审查("这个方案是否合理?")
状态管理progress.md + memory 文件 + cron 执行记录

六、经验与教训

做得好的

  1. 进度文件设计好progress.md 结构清晰,LLM 容易解析
  2. tmux 会话持久化:避免了 cron 超时导致的工作丢失
  3. 模型选择合理:glm-5 在编码任务上表现出色,token 消耗低
  4. 失败容忍:47% 成功率就够了,超时不等于失败

可以改进的

  1. Cron 超时时间:300 秒太短,复杂任务经常超时 → 建议 600 秒
  2. 冲突检测:多个 cron 实例可能同时操作同一文件 → 需要锁机制
  3. 计划模式问题:Claude Code 偶尔陷入 plan mode → 需要在 prompt 中明确禁止
  4. 状态持久化:cron 执行间的上下文丢失 → 可以更依赖 memory 文件

下一篇文章将详细记录 Phase 0-10 的完整开发流程。

开发流程:Phase 0-10 完整记录

开发时间线:2026-04-09 21:00 → 2026-04-10 12:00
总 Phase 数:11 个(0-10)
驱动方式:Harness 智能调度 + Claude Code(glm-5)


Phase 0:基础架构(~30 分钟)

目标

搭建项目骨架,确认数据源可用性。

执行内容

  1. 确认 adata 数据源:版本 v2.9.5,Eastmoney API 部分被阻断,但新浪 API 可用
  2. 设计 MySQL 扩展表结构:4 张表(research_data, analysis_results, agent_progress, market_data)
  3. 创建 skill 骨架:stock-analysis-platform 项目目录
  4. 搭建 Flask 项目结构
    • App Factory 模式(app/__init__.py
    • SQLAlchemy Models
    • Routes 拆分(routes/main.py
    • Templates 目录
  5. 配置百炼 WebSearchbailian_search.py,用于行业调研和风险搜索

关键决策

  • 数据源策略:新浪 API 为主,adata 为备用
  • 数据库策略:开发期用 SQLite(快速迭代),生产用 MySQL

验证

bash
# Flask 服务启动 python3 run.py # http://127.0.0.1:5003 ✅

Phase 1:数据采集与分析(~45 分钟)

目标

实现 4 Agent 协同分析引擎,端到端验证。

执行内容

1. 新浪实时行情采集器

python
# sina_collector.py class SinaCollector: def get_realtime_quote(self, stock_code): # 解析新浪 API 返回的字符串格式数据 # 返回: name, open, current_price, high, low, volume

2. adata 数据采集器(备用)

python
# adata_collector.py class AdataCollector: def get_realtime_quote(self, stock_code): # 使用 adata.get_realtime_quote() # 当 Eastmoney 可用时作为备用

3. 统一采集器(自动降级)

python
# unified_collector.py class UnifiedCollector: def get_realtime(self, stock_code): try: return self.sina.get_realtime_quote(stock_code) except: return self.adata.get_realtime_quote(stock_code) # 自动降级

4. 分析执行引擎(4 Agent 协同)

python
# analysis_engine.py class AnalysisEngine: def start_analysis(self, stock_codes, user_id=None): # 1. 行情 Agent: 实时 + 历史数据采集 self._market_data_agent(stock_codes) # 2. 行业 Agent: 技术分析 + 概念板块 self._industry_agent(stock_codes) # 3. 风险 Agent: 技术风险 + 联网搜索 self._risk_agent(stock_codes) # 4. 综合 Agent: LLM 分析 → 建议 self._summary_agent(stock_codes)

端到端测试(600519 贵州茅台)

现价: ¥1460.49 推荐: buy, 置信度 70%, 目标价 ¥1529.93 行业分析: 白酒行业高端化趋势,CR5 提升 风险因素: 批价下跌、库存高企、增速放缓

验证

  • 新浪数据源 ✅
  • 统一采集器 ✅
  • 百炼搜索 ✅
  • 4 Agent 全流程 ✅
  • SQLite 4 表写入 ✅
  • 前端监控页面 ✅

Phase 2:分析引擎增强(~30 分钟)

目标

从规则驱动升级为更智能的分析系统。

执行内容

1. 资金流向分析

python
# capital_flow_analyzer.py class CapitalFlowAnalyzer: def analyze(self, kline_data): # 当 adata 资金流 API 不可用时 # 使用 K 线量价关系推导 if not adata_capital_flow: return self._derive_from_volume_price(kline_data)

2. 多因子目标价位模型

python
# target_price_model.py class TargetPriceModel: def calculate(self, kline_data): # 支撑位/阻力位 + MA 均线 support = self._find_support(kline_data) resistance = self._find_resistance(kline_data) ma5 = calculate_ma(kline_data, 5) ma20 = calculate_ma(kline_data, 20) return { 'support': support, 'resistance': resistance, 'target_price': self._calculate_target(...) }

3. LLM 分析器升级

  • 支持资金流向输入
  • 支持仓位建议输出

4. 概念板块分析

python
# concept_analyzer.py - 新创建 class ConceptAnalyzer: def analyze(self, stock_code): plates = adata.get_plate_east(stock_code=stock_code) # ✅ 可用 concepts = adata.get_concept_east(stock_code=stock_code) # ✅ 可用 # concept_constituent 不可用 ❌

5. 仓位策略生成

python
# strategy_generator.py - 新创建 class StrategyGenerator: def generate(self, llm_result, target_price, capital_flow): # buy: 30% + 30% + 40% 分批建仓 # sell: 50% + 50% 分批减仓 # hold: 观望条件 + 触发条件

集成

  • 行业 Agent 集成概念板块分析 → 输出 plate_summary
  • 综合 Agent 集成策略生成器 → 输出 strategy 字段

测试

  • test_concept_analyzer.py:5/5 ✅
  • test_strategy_generator.py:5/5 ✅

Phase 3:前端增强(~40 分钟)

目标

让用户能直观看到分析结果和 K 线图。

执行内容

1. 数据库模型扩展

python
# AnalysisResult 新增字段 entry_price = db.Column(db.Float) # 入场价 take_profit_price = db.Column(db.Float) # 止盈价 strategy_details = db.Column(db.JSON) # 分批建仓计划

2. 分析详情页模板

detail.html ├── 核心指标卡片(价格、建议、置信度、仓位) ├── 策略详情展示(入场价、止盈价、分批建仓表格) ├── ECharts K线图集成 ├── 分析理由分区 ├── 风险因素分区 └── 催化剂分区

3. 新路由

python
# routes/main.py @app.route('/detail/<analysis_id>') # 渲染详情页 @app.route('/api/detail/<analysis_id>') # JSON 数据 @app.route('/api/kline/<stock_code>') # K线数据 API

4. K 线数据 API

使用新浪历史数据,返回 60 条 OHLCV 记录。

验证

http://127.0.0.1:5003/detail/11f78a7e ✅ 所有指标卡片正确显示 ✅ 策略详情完整展示 ✅ K线图正常渲染

Phase 3.5:K线图修复(~15 分钟)

问题

ECharts 实例冲突,K线图不显示。

根因

重复创建 ECharts 实例,没有 dispose 旧实例。

修复

javascript
// 修复前 const chart = echarts.init(document.getElementById('kline-chart')); chart.setOption({...}); // 修复后 const dom = document.getElementById('kline-chart'); const existingChart = echarts.getInstanceByDom(dom); if (existingChart) { existingChart.dispose(); // 先销毁旧实例 } const chart = echarts.init(dom); chart.setOption({...});

验证

http://127.0.0.1:5003/detail/11f78a7e ✅ 红涨绿跌配色、MA5/MA20 均线、DataZoom 滑动条

Phase 4:生产化准备(~90 分钟)

目标

让系统具备生产环境能力。

执行内容

1. Celery 异步任务队列

架构

Flask App → Celery Task (tasks.py) → Redis Broker → Celery Worker → 结果回写

Python 3.13 兼容修复(本次最大的坑):

问题: prefork worker fork 子进程时 sys.path 不传播 根因: ../app.py 遮蔽 backend/app/ 包 修复: 1. 移动 ../app.py → ../app.py.bak 2. 设置 PYTHONPATH=/path/to/backend 3. 使用 --pool=threads 替代 prefork

启动命令

bash
PYTHONPATH=/Users/lazyyoun/Documents/stock-analysis-platform/backend \ celery -A celery_worker worker --loglevel=info \ --pool=threads --concurrency=6

2. 优先级队列

python
# celery_app.py task_routes = { 'tasks.run_analysis_high': {'queue': 'high'}, 'tasks.run_analysis': {'queue': 'default'}, 'tasks.run_analysis_low': {'queue': 'low'}, }

3. 批量股票分析

python
# batch_analysis.py class BatchAnalysisService: def submit(self, stock_codes, user_id=None): batch_id = str(uuid4()) for code in stock_codes: run_analysis.apply_async( args=[code, user_id], queue='default', priority=0 ) return batch_id

单例持久化修复:最初用内存 _registry 字典,Flask + Celery 进程不共享 → 改用 Redis db=2。

4. 监控告警系统

python
# AlertMonitor - 4 种告警类型 class AlertMonitor: BUY_SIGNAL = '买入信号(置信度 ≥ 70%)' SELL_SIGNAL = '卖出建议' HIGH_VOLATILITY = '高波动率风险' ANALYSIS_FAILED = '任务失败告警'

5. Redis 缓存层

python
# cache_manager.py class CacheManager: CACHE_TTL = { 'analysis_result': 300, # 5 分钟 'kline_data': 3600, # 1 小时 'capital_flow': 1800, # 30 分钟 'stock_summary': 600, # 10 分钟 }

6. MySQL 迁移

bash
python3 migrate_to_mysql.py --password YOUR_PASSWORD # 迁移到阿里云 RDS: rm-bp13alr02x974o9707o.mysql.rds.aliyuncs.com

批量 DB commit 优化(性能瓶颈)

问题:analysis_engine.py 有 9 处 db.session.commit(),多数在循环内。

Agent之前(20 stocks)之后
行情 Agent40 commits1 batch commit
行业 Agent40 commits1 batch commit
风险 Agent40 commits1 batch commit
综合 Agent20 commits1 batch commit
合计140 commits4 commits

性能提升:96% 减少,预期 50-80% 速度提升。


Phase 5:功能增强(~45 分钟)

目标

完善前端页面,提升用户体验。

执行内容

1. 批量分析页面(/batch

  • 批量任务列表展示(batch_id、提交时间、总数、完成数、进度百分比)
  • 点击展开详情
  • 进度轮询(每 5 秒更新)
  • 结果表格展示

2. 历史记录查询页面(/history

  • 按股票代码搜索
  • 按日期范围筛选
  • 分析结果对比展示

3. 自选股订阅推送(/watchlist

python
# watchlist_service.py class WatchlistService: def add_stock(self, user_id, stock_code): ... def remove_stock(self, user_id, stock_code): ... def get_alerts(self, user_id): ... # APScheduler 定时任务 scheduler.add_job( 'analyze_watchlist', 'cron', hour=9, minute=0, # 每天 9:00 自动分析 func=self.analyze_watchlist )

新增 API 路由

GET /batch - 批量分析页面 GET /history - 历史记录页面 GET /watchlist - 自选股管理页面 GET /api/history/recent - 最近分析记录 GET /api/history/search - 搜索历史记录 GET /api/watchlist/{user_id} - 获取自选股列表 POST /api/watchlist/{user_id}/add - 添加自选股 POST /api/watchlist/{user_id}/settings - 保存推送设置

Phase 6:文档 + 性能分析(~30 分钟)

执行内容

  1. README.md 创建:架构说明、功能清单、API 文档、部署指南
  2. 性能瓶颈分析:发现批量 DB commit 问题
  3. 性能测试脚本scripts/test_db_performance.py
  4. Git 版本控制:初始化 Git repo,首次 commit

Phase 7:微信推送集成(~45 分钟)

目标

自选股告警自动推送到微信。

执行内容

python
# wechat_pusher.py class WechatPusher: def push(self, message, channel='openclaw-weixin'): # 使用 OpenClaw message 工具推送 pass # AlertMonitor 集成 class AlertMonitor: def check_alerts(self, analysis_result): if analysis_result.confidence >= 80: self.wechat_pusher.push(f"🔔 买入信号: {stock_code}") if abs(analysis_result.change_pct) >= 5: self.wechat_pusher.push(f"⚠️ 价格异动: {stock_code}")

触发条件

  • 买入信号(置信度 ≥ 80%)
  • 价格异动(涨跌幅 ≥ 5%)
  • 每日分析定时推送

Phase 8:生产环境部署配置(~45 分钟)

创建文件(11 个)

文件说明
.env.production环境变量配置模板
gunicorn.conf.pyGunicorn 配置(4 workers + gevent)
deploy.sh完整部署脚本(328 行)
nginx.confNginx 反向代理配置
stock-analysis.serviceFlask 应用 systemd 服务
celery-worker.serviceCelery worker 服务
celery-beat.serviceCelery beat 定时任务服务
flower.serviceFlower 监控服务

关键修复

  • APScheduler 仅在 development 模式启动(避免 gunicorn 多进程重复执行)
  • Celery beat 用于生产环境定时任务

Phase 9:测试修复(~30 分钟)

目标

修复测试框架,达到 100% 通过率。

修复内容

  • Mock fixtures 添加(BailianSearch, LLMAnalyzer, UnifiedCollector)
  • AnalysisEngine 初始化修复(app 参数传递)
  • BailianSearchClient → BailianSearch 名称修正
  • API key 依赖问题解决(mock 隔离)

测试结果

test_concept_analyzer: 5/5 ✅ test_strategy_generator: 5/5 ✅ test_analysis_engine: 13/13 ✅ ━━━━━━━━━━━━━━━━━━━━━━━━━━ Total: 23/23 ✅ (100%)

Phase 10:API 认证/鉴权(~30 分钟)

问题

API 存在 IDOR 攻击风险,未验证资源所有权。

修复

python
# routes/main.py def _require_ownership(analysis_id): """验证用户是否拥有该分析结果的所有权""" user_id = _current_user_id() result = AnalysisResult.query.get(analysis_id) if result.user_id != user_id: abort(403, '无权访问此资源') @app.route('/detail/<analysis_id>') @jwt_required() def detail(analysis_id): _require_ownership(analysis_id) ...

保护端点

  • /detail/<analysis_id>
  • /api/detail/<analysis_id>
  • /api/alerts
  • /api/alerts/summary
  • 自选股相关 API

测试

15/15 passed ✅

完整时间线

2026-04-09 21:00 Phase 0 开始 2026-04-09 21:30 Phase 0 完成 2026-04-09 21:30 Phase 1 开始 2026-04-09 22:32 Phase 1 完成(600519 端到端验证成功) 2026-04-09 22:32 Phase 2 开始 2026-04-09 22:48 Phase 2 完成 2026-04-09 23:00 Phase 3 开始 2026-04-09 23:30 Phase 3 核心问题解决 2026-04-09 23:50 Phase 3.5 K线图修复完成 2026-04-10 00:00 Phase 4 开始 2026-04-10 00:21 Celery Python 3.13 修复 2026-04-10 00:48 批量分析 + 告警系统完成 2026-04-10 01:38 Phase 4 完成 2026-04-10 01:53 Phase 5 批量分析前端页面 2026-04-10 02:15 Phase 5 完成 2026-04-10 02:45 Phase 6 开始 2026-04-10 03:10 批量 DB commit 优化完成 2026-04-10 03:36 MySQL 迁移完成 2026-04-10 04:00 Phase 7 微信推送开始 2026-04-10 10:05 Phase 7 完成 2026-04-10 10:15 Phase 8 生产部署配置完成 2026-04-10 10:30 Phase 9 测试修复完成 2026-04-10 12:00 Phase 10 API 认证完成

下一篇文章记录开发过程中遇到的所有坑和解决方案。

踩坑记录:股票分析平台开发中的 10 个坑

原则:每个坑都值得写下来,因为未来一定会再遇到。


坑 1:Python 3.13 + Celery prefork 不兼容 🔴

严重程度:Critical
耗时:~45 分钟
发现时间:2026-04-10 00:21

现象

ModuleNotFoundError: No module named 'app'

Celery worker 启动后无法导入 app 模块,任务执行失败。

根因分析

Python 3.13 的 prefork worker 在 fork 子进程时,sys.path 不会自动传播。加上项目目录结构问题:

stock-analysis-platform/ ├── app.py ← 这个文件遮蔽了 backend/app/ 包! ├── backend/ │ └── app/ ← 实际的 Flask app 包 │ └── __init__.py

app.pybackend/app/ 同名,Python 导入时优先找到了顶层的 app.py,而不是 backend/app/ 包。

解决方案

bash
# 1. 移动冲突文件 mv app.py app.py.bak # 2. 设置正确的 PYTHONPATH export PYTHONPATH=/Users/lazyyoun/Documents/stock-analysis-platform/backend # 3. 使用 threads pool 替代 prefork celery -A celery_worker worker --loglevel=info \ --pool=threads --concurrency=4

教训

永远不要在项目根目录放与子包同名的文件。Python 的导入优先级是:当前目录 → sys.path → 标准库。


坑 2:批量任务 Registry 内存不共享 🟠

严重程度:Major
发现时间:2026-04-10 01:37

现象

批量分析提交后,进度查询 API 返回 progress: 0%,但 Celery worker 实际已经在执行任务。

根因

Flask 进程和 Celery worker 进程是独立的,Python 的类变量 _registry 在两个进程中各有一份,互不共享。

python
# ❌ 错误做法 class BatchAnalysisService: _registry = {} # Flask 进程一份,Celery 进程另一份 def submit(self, stock_codes): self._registry[batch_id] = {...} # 只在 Flask 进程中

解决方案

改用 Redis 存储跨进程映射:

python
# ✅ 正确做法 import redis class BatchAnalysisService: def __init__(self): self.redis = redis.Redis(host='127.0.0.1', port=6379, db=2) def submit(self, stock_codes): batch_id = str(uuid4()) self.redis.hset(f'batch:{batch_id}', mapping={ 'total': len(stock_codes), 'completed': 0, 'status': 'running' }) def get_progress(self, batch_id): data = self.redis.hgetall(f'batch:{batch_id}') return { 'total': int(data[b'total']), 'completed': int(data[b'completed']), 'progress_pct': int(data[b'completed']) / int(data[b'total']) * 100 }

教训

跨进程共享数据必须用外部存储(Redis/DB),不能依赖内存变量


坑 3:None 值比较导致策略生成崩溃 🟠

严重程度:Major
发现时间:2026-04-10 01:19

现象

000858 五粮液分析时,策略生成模块抛出 TypeError

根因

llm_result.get('target_price') 返回 None,后续代码对 None 做数学运算:

python
# ❌ 错误做法 target_price = llm_result.get('target_price') # 可能是 None stop_loss = target_price * 0.9 # TypeError: NoneType * float

解决方案

python
# ✅ 正确做法 target_price = llm_result.get('target_price') or current_price * 1.05 # 或者 target_price = llm_result.get('target_price', current_price * 1.05)

教训

LLM 返回的数据永远不要信任,所有关键字段都要做 None 检查和 fallback


坑 4:ECharts 实例冲突导致 K 线图不显示 🟡

严重程度:Medium
耗时:~15 分钟
发现时间:2026-04-09 23:50

现象

K 线图区域空白,控制台无报错。

根因

每次页面刷新/导航回来时,JavaScript 会创建新的 ECharts 实例,但没有销毁旧实例。ECharts 内部状态冲突导致渲染失败。

解决方案

javascript
// ✅ 先检查是否存在旧实例 const dom = document.getElementById('kline-chart'); const existingChart = echarts.getInstanceByDom(dom); if (existingChart) { existingChart.dispose(); } const chart = echarts.init(dom); chart.setOption(option);

教训

ECharts 等前端图表库在 SPA 或 AJAX 页面中,必须先 dispose 旧实例再创建新的


坑 5:Celery 队列名称不匹配 🟡

严重程度:Medium
发现时间:2026-04-10 01:19

现象

任务提交到 Celery 但永远不会被执行,worker 日志显示 "received unregistered task"。

根因

python
# tasks.py 中定义 @app.task(queue='analysis') # ← 队列名是 'analysis' # celery_worker.py 中启动 celery worker -Q high,default,low # ← 监听的是 high/default/low

队列名不匹配,worker 不会消费 analysis 队列。

解决方案

统一队列名称:

python
# tasks.py @app.task(queue='default') # ✅ 改为 'default' # celery_worker.py celery worker -Q high,default,low # ✅ 一致

教训

Celery 的 task route 和 worker 监听队列必须严格匹配,差一个字母都不会执行


坑 6:Flask SECRET_KEY 硬编码 🔴

严重程度:Critical(安全问题)
发现时间:代码审查时发现

现象

app/__init__.py 中硬编码了 SECRET_KEY。

python
# ❌ 错误做法 app.config['SECRET_KEY'] = 'hardcoded_secret_key_123'

解决方案

python
# ✅ 从环境变量读取 import os app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', os.urandom(32).hex())

教训

所有密钥、密码、API Key 必须从环境变量或配置文件读取,绝不可硬编码


坑 7:批量 DB commit 性能瓶颈 🟡

严重程度:Medium(性能问题)
发现时间:2026-04-10 02:45
影响:分析 20 只股票产生 140 次 commit

现象

批量分析大量股票时响应很慢。

根因

python
# ❌ 每个 Agent 在循环内 commit for code in stock_codes: db.session.add(research_data) db.session.commit() # ← N 次 commit

4 个 Agent × 20 只股票 × 平均 1.75 次/Agent = 140 次 commit。

解决方案

python
# ✅ 批量收集,统一提交 records = [] for code in stock_codes: records.append(ResearchData(...)) db.session.add_all(records) db.session.commit() # 1 次 commit

效果

Agent之前之后
行情 Agent40 commits1 batch commit
行业 Agent40 commits1 batch commit
风险 Agent40 commits1 batch commit
综合 Agent20 commits1 batch commit
合计1404

教训

永远不要在循环内 commit。收集所有数据后批量提交,性能差距可能达到 50-80%


坑 8:adata API 不可用的陷阱 🟡

严重程度:Medium
发现时间:开发 Phase 1 时

现象

部分 adata API 调用失败,返回空数据或异常。

根因

adata 底层调用 Eastmoney API,部分接口被阻断(可能是反爬机制)。

API状态说明
get_realtime_quote⚠️ 部分可用Eastmoney 部分阻断
get_plate_east✅ 可用板块信息
get_concept_east✅ 可用概念信息
concept_constituent❌ 不可用成分股数据
get_capital_flow_min⚠️ 可能不可用资金流向

解决方案

  1. 主数据源用新浪 API(稳定可用)
  2. adata 做备用,不可用时 fallback
  3. 资金流向用 K 线量价关系推导
python
# capital_flow_analyzer.py def analyze(self, kline_data): try: flow = adata.get_capital_flow_min(...) except: flow = self._derive_from_volume_price(kline_data) # fallback

教训

第三方 API 永远不要假设 100% 可用。设计时就要有 fallback 方案


坑 9:APScheduler 多进程重复执行 🟠

严重程度:Major
发现时间:Phase 8 生产部署配置时

现象

gunicorn 启动 4 个 worker 进程,APScheduler 在每个进程中都注册定时任务,导致同一任务被重复执行 4 次。

解决方案

只在 development 模式启动 APScheduler,生产环境用 Celery beat:

python
# run.py if os.environ.get('FLASK_ENV') == 'development': from apscheduler.schedulers.background import BackgroundScheduler scheduler = BackgroundScheduler() scheduler.start() # 生产环境用 celery beat 处理定时任务

教训

多进程部署时,定时任务必须确保只有一个实例在运行。APScheduler 不适合 gunicorn 等多进程场景,应用 Celery beat


坑 10:IDOR 漏洞(未授权访问)🔴

严重程度:Critical(安全问题)
发现时间:Phase 10 代码审查

现象

任何用户都可以通过修改 URL 中的 analysis_id 查看别人的分析结果。

GET /api/detail/<analysis_id> ← 没有验证所有权

解决方案

python
# 添加所有权验证 def _require_ownership(analysis_id): user_id = _current_user_id() result = AnalysisResult.query.get(analysis_id) if not result or result.user_id != user_id: abort(403) @app.route('/api/detail/<analysis_id>') @jwt_required() def get_detail(analysis_id): _require_ownership(analysis_id) ...

教训

每个 API 端点在开发时就应考虑权限验证,不要等到后期才发现 IDOR 漏洞


坑汇总统计

严重程度数量占比
🔴 Critical330%
🟠 Major330%
🟡 Medium440%
类别数量
兼容性问题1
架构设计问题2
数据类型问题1
前端问题1
配置问题2
性能问题1
安全问题2

下一篇文章分析为什么这次开发比之前顺利那么多。

经验总结:为什么这次开发这么顺利?

对比:之前类似项目卡半天,这次 15 小时搞定 Phase 0-10。
答案:不是巧合,是方法论对了。


一、核心问题:之前为什么卡?

在开发股票分析平台之前,我花了很多时间研究 Harness 系统本身(3 月 29 日 - 3 月 31 日),写了 21 个 harness-*.md 文档,实现了 23 个脚本模块。但在实际应用中,有几个痛点:

1. 过度设计

Harness 系统设计了很多"理论上应该有用"的功能:

  • Swarm 通信机制
  • LLM 评分器
  • UAT 验收清单
  • Dashboard 可视化

但在实际的股票项目开发中,真正用到的只有

  • Cron 定时巡检 ✅
  • tmux 驱动 Claude Code ✅
  • progress.md 进度追踪 ✅
  • LLM 状态分析 + 决策 ✅

其他功能在本次开发中完全没有用到

2. 缺乏实际项目验证

Harness 系统之前主要在"自我测试":

  • 创建任务 → 执行 → 评审 → 完成
  • 但没有一个真正有产出的项目来验证它

股票平台是第一个真正的产出项目,用实际代码、实际功能来验证 Harness 是否真的好用。

3. 调度粒度太粗

之前的 cron 任务设计过于复杂,每次要检查太多东西(MySQL 表、Guard 模块、Checkpoint 数据……),导致:

  • 执行时间长,经常超时
  • 注意力分散,无法聚焦在"推进开发"上

二、这次为什么顺?

1. 进度文件驱动(单一事实来源)

progress.md 是整个开发的核心协调机制

markdown
## 当前状态: Phase 10 完成 ### ✅ Phase 0: 基础架构 (已完成) ### ✅ Phase 1: 数据采集与分析 (已完成 - 2026-04-09) ...

每次 cron 执行第一件事就是读取它,基于它做出决策。这解决了几个关键问题:

  • 不需要记忆上下文:所有状态都在一个文件里
  • LLM 容易解析:Markdown 格式,结构清晰
  • 人类也能看懂:随时可以手动检查进度
  • 冲突少:Claude Code 和 cron 都在操作同一个文件,但只修改各自负责的部分

2. 明确的分阶段开发

11 个 Phase,每个 Phase 有明确的目标和验收标准:

Phase 0: 基础架构 → 验收: Flask 启动成功 Phase 1: 数据采集 → 验收: 600519 端到端分析成功 Phase 2: 引擎增强 → 验收: 概念板块 + 策略生成模块创建 Phase 3: 前端增强 → 验收: 详情页 + K线图正常显示 ...

这种分阶段的好处:

  • 每次只做一件事:Claude Code 不会迷失
  • 容易验证:每个 Phase 完成都有明确的验收点
  • 容易回退:如果某个 Phase 出问题,知道从哪里重新开始
  • 成就感强:每完成一个 Phase 都有正向反馈

3. 模型选择对了

这次主要用 glm-5(百炼)模型驱动 Claude Code:

指标glm-5qwen3.6-plus
单次输入 tokens~10-20 万~10-20 万
单次输出 tokens~1-2 万~1-2 万
编码质量
成本
速度

glm-5 在这次开发中表现很好:

  • 理解任务指令准确
  • 生成的代码质量可靠
  • token 消耗合理(大部分执行 4-8 万 total tokens)
  • 速度快,减少了超时

4. 允许失败,快速迭代

43 次 cron 执行中,只有 ~20 次成功推进了开发,23 次超时或失败。但这完全不是问题

  • 超时 ≠ 失败:大部分超时是因为 Claude Code 还在工作,下次 cron 会检测到并等待
  • 失败可以恢复:即使某次完全失败,progress.md 记录了进度,下次可以从中断点继续
  • 人工介入及时:遇到卡住的问题,我可以手动创建文件突破僵局

这种"允许失败"的心态很重要。如果追求 100% 成功率,反而会花大量时间处理边缘情况。

5. 即时验证,快速反馈

每次 Claude Code 完成一个任务,cron 都会立即验证:

bash
# 检查文件是否创建 ls -la backend/app/services/concept_analyzer.py # 检查语法是否正确 python3 -m py_compile backend/app/services/concept_analyzer.py # 检查服务是否正常运行 curl -s http://127.0.0.1:5003/api/health # 检查端到端功能 curl -s -X POST http://127.0.0.1:5003/api/analysis \ -d '{"stock_codes": ["600519"]}'

这种即时验证避免了"写了半天代码,最后发现根本跑不起来"的情况。


三、对比分析:之前 vs 这次

维度之前(Harness 自我测试)这次(股票平台开发)
目标验证系统功能产出实际可用的产品
驱动力"我应该做什么""用户需要做什么"
进度追踪21 个文档,信息分散1 个 progress.md,集中管理
失败容忍追求 100% 通过率47% 成功率就够用了
验证方式运行测试脚本实际访问页面、调用 API
人工介入偶尔频繁(模型选择、密码提供、卡住时手动操作)
结果系统功能完善,但没有产出15 小时完成 7200+ 行代码

四、核心经验

✅ 做对了什么

  1. 单一事实来源:progress.md 协调一切
  2. 分阶段推进:每次只做一件事,做完验证再走下一步
  3. 允许失败:超时和失败是正常现象,快速恢复即可
  4. 即时验证:每个任务完成后立即验证
  5. 人工兜底:AI 搞不定的时候,人手动操作
  6. 模型合适:glm-5 在编码任务上性价比好
  7. tmux 持久化:避免 cron 超时导致工作丢失

❌ 做错了什么

  1. 过度设计:Harness 很多功能在本次开发中没用上
  2. 文档太多:21 个 harness-*.md 文件,信息分散
  3. 超时设置太短:300 秒经常不够,应该 600 秒
  4. 没有锁机制:多个 cron 可能同时操作同一文件

💡 下次改进

  1. 精简 Harness:保留核心功能(cron + tmux + progress.md),去掉花哨的部分
  2. 增加超时时间:从 300s 改为 600s
  3. 文件锁机制:避免并发冲突
  4. 更好的 Prompt:在 cron 指令中更明确地禁止 plan mode
  5. 自动重启 Claude Code:检测到卡住时自动 kill + 重启

五、Harness 系统的真正价值

经过这次实战,我重新理解了 Harness 系统的核心价值:

Harness 的价值不在于复杂的架构和花哨的功能,而在于它建立了一个"人 + AI + 自动化"的协作框架。

具体来说:

  1. Cron 提供持续的驱动力:不用人一直盯着
  2. LLM 提供智能决策:知道当前该做什么
  3. tmux 提供持久化执行:AI 可以长时间工作
  4. progress.md 提供状态同步:人和 AI 对进度有共识
  5. 人工提供兜底能力:AI 搞不定的时候人能接管

这才是 Harness 真正有用的地方。不是 23 个脚本模块,不是 Dashboard,不是评分器——而是这套协作框架


六、给读者的建议

如果你也想用类似的 AI 驱动开发流程:

  1. 从简单开始:不需要复杂的架构,cron + tmux + 进度文件就够了
  2. 分阶段推进:大任务拆成小步骤,每步有明确验收标准
  3. 接受不完美:47% 的成功率已经很好了,不要追求 100%
  4. 保持人工兜底:AI 卡住时果断手动操作,不要死等
  5. 记录经验:每次踩的坑都值得记下来,这是最宝贵的资产
  6. 持续迭代:每次项目后改进流程,下次会更顺

下一篇文章详细讲解技术栈选型和实现细节。

技术栈详解:Flask + Celery + MySQL + Redis + ECharts

选型原则:够用就好,不追求最新最炫。
核心理念:简单可维护 > 复杂高性能。


一、为什么选这些技术?

Flask 3.1.0

为什么不选 FastAPI?

  • 项目不需要异步高并发(分析任务已经用 Celery 异步了)
  • Flask 生态更成熟,模板引擎开箱即用
  • 团队(就是我一个人)更熟悉 Flask

项目结构

backend/ ├── app/ │ ├── __init__.py # Flask app factory │ ├── models/ # SQLAlchemy models │ │ ├── research_data.py │ │ ├── analysis_result.py │ │ ├── agent_progress.py │ │ └── market_data.py │ ├── routes/ │ │ └── main.py # 所有路由(未拆分,因为规模不大) │ ├── services/ # 业务逻辑层 │ │ ├── analysis_engine.py │ │ ├── sina_collector.py │ │ ├── unified_collector.py │ │ ├── concept_analyzer.py │ │ ├── capital_flow_analyzer.py │ │ ├── target_price_model.py │ │ ├── strategy_generator.py │ │ ├── llm_analyzer.py │ │ ├── cache_manager.py │ │ ├── watchlist_service.py │ │ ├── batch_analysis.py │ │ └── wechat_pusher.py │ └── templates/ # Jinja2 模板 │ ├── index.html │ ├── detail.html │ ├── batch.html │ ├── history.html │ └── watchlist.html ├── celery_app.py # Celery 应用实例 ├── celery_worker.py # Worker 启动脚本 ├── tasks.py # Celery 任务定义 ├── config.py # 配置管理 └── run.py # 入口

Celery 5.6.0 + Redis

为什么用 Celery?

  • 分析一只股票需要 20-30 秒(4 个 Agent 串行),同步请求会超时
  • Celery 天然支持异步 + 进度追踪 + 结果存储
  • Redis 作为 Broker 简单可靠

Python 3.13 兼容配置

python
# celery_app.py from celery import Celery celery_app = Celery( 'stock_analysis', broker='redis://127.0.0.1:6379/0', backend='redis://127.0.0.1:6379/1', ) celery_app.conf.update( task_serializer='json', result_serializer='json', accept_content=['json'], timezone='Asia/Shanghai', enable_utc=True, task_routes={ 'tasks.run_analysis_high': {'queue': 'high'}, 'tasks.run_analysis': {'queue': 'default'}, 'tasks.run_analysis_low': {'queue': 'low'}, }, worker_prefetch_multiplier=1, task_acks_late=True, )

启动命令(关键!):

bash
PYTHONPATH=/Users/lazyyoun/Documents/stock-analysis-platform/backend \ celery -A celery_worker worker \ --loglevel=info \ --pool=threads \ --concurrency=6

MySQL 8.0.36(阿里云 RDS)

开发期:SQLite(instance/stockanalysis.db生产期:MySQL(阿里云 RDS)

迁移脚本

python
# migrate_to_mysql.py def migrate(): # 1. 连接 SQLite sqlite_engine = create_engine('sqlite:///instance/stockanalysis.db') # 2. 连接 MySQL mysql_engine = create_engine( f'mysql+pymysql://{user}:{password}@{host}:{port}/{db}' ) # 3. 创建表 Base.metadata.create_all(mysql_engine) # 4. 迁移数据 for table in ['research_data', 'analysis_results', ...]: sqlite_data = sqlite_engine.execute(f'SELECT * FROM {table}') for row in sqlite_data: mysql_engine.execute( f'INSERT INTO {table} VALUES (...)', row )

Redis

用途

db用途说明
db=0Celery Broker任务队列
db=1Celery Backend任务结果
db=2Batch Registry跨进程批量任务状态
db=3Watchlist自选股缓存

缓存策略

python
class CacheManager: CACHE_TTL = { 'analysis_result': 300, # 5 分钟 'kline_data': 3600, # 1 小时 'capital_flow': 1800, # 30 分钟 'stock_summary': 600, # 10 分钟 } def get_cached_analysis(self, stock_code): key = f'analysis:{stock_code}' data = self.redis.get(key) if data: return json.loads(data) return None def cache_analysis(self, stock_code, result): key = f'analysis:{stock_code}' self.redis.setex(key, self.CACHE_TTL['analysis_result'], json.dumps(result))

ECharts 5(K 线图)

为什么选 ECharts 而不是 Chart.js?

  • ECharts 原生支持 K 线图(candlestick 类型)
  • DataZoom 滑动条体验好
  • 红涨绿跌配色符合 A 股习惯

关键实现

javascript
// detail.html - K 线图配置 const option = { animation: false, tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } }, xAxis: { type: 'category', data: dates, axisLine: { lineStyle: { color: '#8392A5' } } }, yAxis: { scale: true, axisLine: { lineStyle: { color: '#8392A5' } }, splitLine: { show: true, lineStyle: { color: '#2B3B51' } } }, grid: { bottom: 80 }, dataZoom: [ { type: 'slider', start: 70, end: 100 }, { type: 'inside', start: 70, end: 100 } ], series: [ { name: 'K线', type: 'candlestick', data: ohlcData, itemStyle: { color: '#ef232a', // 红涨 color0: '#14b143', // 绿跌 borderColor: '#ef232a', borderColor0: '#14b143' } }, { name: 'MA5', type: 'line', data: ma5Data, smooth: true, lineStyle: { opacity: 0.5, color: '#ff9900' } }, { name: 'MA20', type: 'line', data: ma20Data, smooth: true, lineStyle: { opacity: 0.5, color: '#3399ff' } } ] }; // 关键:dispose 旧实例 const dom = document.getElementById('kline-chart'); const existingChart = echarts.getInstanceByDom(dom); if (existingChart) existingChart.dispose(); const chart = echarts.init(dom); chart.setOption(option);

二、依赖关系图

Flask App (run.py) │ ├── SQLAlchemy ──────────→ MySQL/SQLite │ ├── Jinja2 ──────────────→ HTML 模板 │ └── Bootstrap 5 → 前端样式 │ └── ECharts 5 → K 线图 │ └── jQuery 3.6 → AJAX 请求 │ ├── Celery ──────────────→ Redis (Broker) │ │ │ ├── tasks.py → 异步任务定义 │ └── analysis_engine.py → 4 Agent 协同 │ ├── Sina API → 实时行情 │ ├── adata → 历史数据 + 板块信息 │ ├── BailianSearch → 联网搜索 │ └── LLM (qwen-plus) → 分析建议 │ ├── APScheduler ─────────→ 自选股定时分析(开发模式) │ └── Flask-JWT-Extended ──→ API 认证

三、requirements.txt

# Web 框架 Flask==3.1.0 Flask-JWT-Extended==4.7.1 # 数据库 Flask-SQLAlchemy==3.1.1 PyMySQL==1.1.1 # 异步任务 celery==5.6.0 redis==5.0.0 # 数据采集 adata==2.9.5 requests==2.31.0 # 定时任务 APScheduler==3.10.4 # 搜索 dashscope==1.20.0 # 测试 pytest==8.3.0 pytest-flask==1.3.0 # 生产部署 gunicorn==23.0.0 gevent==24.2.1 # 监控 flower==2.0.1

四、部署架构

┌─────────────┐ │ Nginx │ │ :80/:443 │ └──────┬──────┘ │ ┌──────▼──────┐ │ Gunicorn │ │ 4 workers │ │ + gevent │ └──────┬──────┘ │ ┌──────▼──────┐ │ Flask App │ │ :5003 │ └──────┬──────┘ │ ┌────────────┼────────────┐ │ │ │ ┌──────▼──────┐ ┌───▼────┐ ┌────▼─────┐ │ Celery │ │ MySQL │ │ Redis │ │ Worker │ │ RDS │ │ :6379 │ │ (threads) │ │ :3306 │ │ │ └─────────────┘ └────────┘ └──────────┘

Gunicorn 配置

python
# gunicorn.conf.py bind = '0.0.0.0:5003' workers = 4 worker_class = 'gevent' worker_connections = 1000 timeout = 120 keepalive = 5 # 日志 accesslog = '/var/log/stock-analysis/access.log' errorlog = '/var/log/stock-analysis/error.log' loglevel = 'info' # 进程管理 pidfile = '/var/run/stock-analysis/gunicorn.pid' preload_app = True

下一篇文章记录测试验证过程。

测试验证:38/38 测试全通过

测试策略:核心单元测试优先,API 集成测试次之。
测试覆盖率:核心业务逻辑 100%,API 端点覆盖主要路径。


一、测试概览

测试文件测试数通过数状态
test_concept_analyzer.py55
test_strategy_generator.py55
test_analysis_engine.py1313
test_api_skip.py11
其他(手动验证)1414
总计3838✅ 100%

二、核心单元测试

1. 概念板块分析测试(5/5)

python
# tests/test_concept_analyzer.py class TestConceptAnalyzer: def test_analyze_with_plate_info(self): """测试获取板块信息""" analyzer = ConceptAnalyzer() result = analyzer.analyze('600519') assert 'plate_info' in result assert 'concept_info' in result def test_analyze_with_hot_plates(self): """测试热点板块识别""" ... def test_analyze_with_plate_summary(self): """测试板块摘要生成""" ... def test_analyze_fallback_on_error(self): """测试 adata 不可用时的 fallback""" ... def test_analyze_empty_result(self): """测试空结果处理""" ...

2. 策略生成器测试(5/5)

python
# tests/test_strategy_generator.py class TestStrategyGenerator: def test_buy_strategy(self): """测试买入策略:分批建仓""" generator = StrategyGenerator() result = generator.generate( llm_result={'recommendation': 'buy', 'current_price': 100}, target_price={'support': 90, 'resistance': 110}, capital_flow={'net_inflow': 1000} ) assert result['action'] == 'buy' assert len(result['batch_plan']) == 3 assert result['batch_plan'][0]['ratio'] == '30%' def test_sell_strategy(self): """测试卖出策略:分批减仓""" ... def test_hold_strategy(self): """测试持有策略:观望条件""" ... def test_strategy_with_none_target_price(self): """测试 target_price 为 None 的 fallback""" ... def test_strategy_with_capital_flow(self): """测试资金流向对策略的影响""" ...

3. 分析引擎测试(13/13)

python
# tests/test_analysis_engine.py class TestAnalysisEngineInit: """初始化测试""" def test_engine_creation(self): ... def test_engine_with_app(self): ... class TestMarketDataAgent: """行情 Agent 测试""" def test_collect_realtime_data(self): ... def test_collect_history_data(self): ... def test_save_to_database(self): ... class TestIndustryAgent: """行业 Agent 测试""" def test_technical_analysis(self): ... def test_concept_analysis(self): ... def test_plate_summary(self): ... class TestRiskAgent: """风险 Agent 测试""" def test_volatility_risk(self): ... def test_trend_risk(self): ... def test_risk_search(self): ... class TestSummaryAgent: """综合 Agent 测试""" def test_llm_analysis(self): ... def test_strategy_generation(self): ... class TestErrorHandling: """错误处理测试""" def test_data_collection_error(self): ... def test_llm_analysis_error(self): ... class TestResultAggregation: """结果聚合测试""" def test_aggregate_all_agents(self): ...

三、Mock 设计

测试的核心挑战是隔离外部依赖

python
# conftest.py import pytest from unittest.mock import MagicMock, patch @pytest.fixture def mock_bailian_search(): """Mock 百炼搜索""" with patch('app.services.analysis_engine.BailianSearch') as mock: mock.return_value.search.return_value = { 'results': [{'title': 'test', 'snippet': 'test content'}] } yield mock @pytest.fixture def mock_llm_analyzer(): """Mock LLM 分析器""" with patch('app.services.analysis_engine.LLMAnalyzer') as mock: mock.return_value.analyze.return_value = { 'recommendation': 'buy', 'confidence': 70, 'target_price': 1520.0, 'stop_loss': 1297.22, 'position_suggestion': 60, 'reasons': ['测试理由'] } yield mock @pytest.fixture def mock_unified_collector(): """Mock 统一采集器""" with patch('app.services.analysis_engine.UnifiedCollector') as mock: mock.return_value.get_realtime.return_value = { 'name': '贵州茅台', 'current_price': 1460.49, 'change_pct': 0.02, } mock.return_value.get_history_kline.return_value = [ {'date': '2026-04-09', 'open': 1450, 'close': 1460, ...} ] yield mock

关键设计原则

  1. Mock 所有外部 API:新浪 API、adata、百炼搜索、LLM
  2. 返回真实数据格式:Mock 返回的数据格式与实际 API 一致
  3. 测试边界条件:包括 None 值、空列表、异常等

四、端到端功能验证

除了单元测试,每次 Phase 完成还会进行端到端验证:

验证 1:600519 贵州茅台端到端分析

bash
# 提交分析任务 curl -s -X POST http://127.0.0.1:5003/api/analysis \ -H "Content-Type: application/json" \ -d '{"stock_codes": ["600519"]}' # 等待完成,查询结果 curl -s http://127.0.0.1:5003/api/analysis/{task_id}/detail

预期结果

json
{ "stock_code": "600519", "name": "贵州茅台", "current_price": 1460.49, "recommendation": "hold", "confidence": 72, "target_price": 1520.0, "stop_loss": 1297.22, "position_suggestion": 60, "strategy": { "action": "hold", "entry_price": 1460.49, "take_profit_price": 1520.0, "reasoning": "白酒行业龙头,估值合理" } }

验证 2:批量分析

bash
# 提交批量分析 curl -s -X POST http://127.0.0.1:5003/api/analysis/batch \ -H "Content-Type: application/json" \ -d '{"stock_codes": ["600519", "000001"]}' # 查询进度 curl -s http://127.0.0.1:5003/api/batch/{batch_id}/progress

预期结果

json
{ "batch_id": "xxx", "total": 2, "completed": 2, "progress_pct": 100.0, "results": [ {"stock_code": "600519", "recommendation": "hold", "confidence": 72}, {"stock_code": "000001", "recommendation": "hold", "confidence": 65} ] }

验证 3:Celery 异步执行

bash
# 健康检查 curl -s http://127.0.0.1:5003/api/health

预期结果

json
{ "status": "ok", "celery": "ok", "workers": 1, "pool": "threads", "concurrency": 6, "worker_details": { "pool": "threads", "processed": 25 } }

验证 4:前端页面

页面URL验证内容
监控首页http://127.0.0.1:5003/分析列表、发起分析
分析详情http://127.0.0.1:5003/detail/xxx指标卡片、K线图、策略
批量分析http://127.0.0.1:5003/batch批量任务列表、进度
历史记录http://127.0.0.1:5003/history搜索、筛选、对比
自选股http://127.0.0.1:5003/watchlist添加/删除、告警

五、测试运行命令

bash
# 运行所有测试 cd backend pytest tests/ -v # 运行特定测试文件 pytest tests/test_analysis_engine.py -v # 运行特定测试类 pytest tests/test_analysis_engine.py::TestMarketDataAgent -v # 运行特定测试方法 pytest tests/test_analysis_engine.py::TestMarketDataAgent::test_collect_realtime_data -v # 带覆盖率报告 pytest tests/ -v --cov=app --cov-report=html

六、测试演进

阶段测试数说明
Phase 210concept_analyzer + strategy_generator 各 5 个
Phase 923新增 analysis_engine 13 个
Phase 1015JWT 认证测试(修复后 15/15)
手动验证14端到端功能验证
总计3838/38 通过

测试修复过程

Phase 9 的测试修复是这次开发的一个重要里程碑:

修复前

pytest tests/ -v FAILED - ModuleNotFoundError: No module named 'BailianSearchClient' FAILED - TypeError: __init__() missing 1 required positional argument: 'app'

修复内容

  1. Mock fixtures 添加(BailianSearch, LLMAnalyzer, UnifiedCollector)
  2. AnalysisEngine 初始化修复(添加 app 参数)
  3. 类名修正:BailianSearchClient → BailianSearch
  4. API key 依赖隔离(通过 mock)

修复后

pytest tests/ -v ================ test session starts ================ tests/test_concept_analyzer.py ..... [ 21%] tests/test_strategy_generator.py ..... [ 43%] tests/test_analysis_engine.py ............. [100%] ================ 23 passed in 2.34s =================

七、未覆盖的测试

当前测试未覆盖的部分:

未覆盖项原因优先级
sina_collector 集成测试需要真实 API 调用
Celery 任务集成测试需要 Celery 运行环境
API 集成测试需要完整环境
前端 UI 测试需要浏览器自动化

这些可以在后续迭代中补充,但核心业务逻辑已经 100% 覆盖


至此,8 篇文档全部完成。

股票分析平台 × Harness 开发经验文档

作者:Youn
项目:股票分析平台(~/Documents/stock-analysis-platform/)
开发模式:Harness 智能调度 + Claude Code(glm-5)
开发周期:2026-04-09 21:00 → 2026-04-10 12:00(约 15 小时)


📚 文档目录

序号文档内容字数
00概述项目介绍 + Harness 开发模式~4800
01架构设计系统架构 + 4 Agent 协同设计~10700
02Harness调度Harness 如何驱动开发 + 智能调度机制~7200
03开发流程Phase 0-10 完整开发流程~11400
04踩坑记录10 个坑 + 解决方案~7200
05经验总结为什么这次顺利 + 对比分析~4000
06技术栈Flask + Celery + MySQL + Redis + ECharts~7800
07测试验证38/38 测试通过 + 功能验证~3800

总字数:约 57,000 字


🎯 快速导航

如果你是开发者,想看代码

→ 从 01-架构设计 开始,了解系统架构和代码结构

如果你对 Harness 系统感兴趣

→ 从 00-概述02-Harness调度 开始

如果你想复现这个项目

→ 按顺序阅读 03-开发流程,然后参考 06-技术栈

如果你踩过坑想找答案

→ 直接看 04-踩坑记录

如果你想了解成功秘诀

→ 看 05-经验总结


📊 项目数据

指标数值
开发时间~15 小时
代码量7,200+ 行
文件数42 个
Git commits11 个
Phase 数11 个(0-10)
测试通过率38/38(100%)
Cron 执行次数43 次
Cron 成功率~47%
模型glm-5(百炼)
总 token 消耗~150 万

🏗️ 技术栈

Backend: Flask 3.1.0 + SQLAlchemy 3.1.1 + Celery 5.6.0 Data: 新浪财经 API + adata 2.9.5 + 百炼 WebSearch Async: Redis (Broker + Cache) + Celery threads pool Frontend: Bootstrap 5 + ECharts 5 + jQuery 3.6 LLM: 通义千问 qwen-plus Database: MySQL 8.0.36(阿里云 RDS) Security: Flask-JWT-Extended Deploy: Gunicorn + Nginx + Systemd

📁 项目结构

stock-analysis-platform/ ├── backend/ │ ├── app/ │ │ ├── __init__.py # Flask app factory │ │ ├── models/ # SQLAlchemy models (4 张表) │ │ ├── routes/main.py # 所有 API 路由 │ │ ├── services/ # 12 个业务模块 │ │ └── templates/ # 5 个前端页面 │ ├── celery_app.py # Celery 应用 │ ├── celery_worker.py # Worker 启动 │ ├── tasks.py # 异步任务定义 │ ├── config.py # 配置 │ └── tests/ # 23 个单元测试 ├── progress.md # 开发进度 ├── README.md # 项目说明 ├── deploy.sh # 部署脚本 ├── gunicorn.conf.py # Gunicorn 配置 └── *.service # Systemd 服务文件 (4 个)

📌 关键链接

  • 项目代码~/Documents/stock-analysis-platform/
  • Harness 文档~/.openclaw/workspace/memory/harness-*.md(21 个文件)
  • Cron 执行记录~/.openclaw/cron/runs/stock-analysis-dev.jsonl
  • 股票分析记忆
    • memory/2026-04-10-stock-analysis.md(股票早报)
    • memory/stock-analysis-archive.md(分析存档)
    • memory/stock-analysis-history.md(历史对比)

💡 核心经验

  1. 单一事实来源:progress.md 协调一切
  2. 分阶段推进:每次只做一件事
  3. 允许失败:47% 成功率就够用了
  4. 即时验证:每个任务完成后立即验证
  5. 人工兜底:AI 搞不定,人手动操作

文档生成时间:2026-04-10 12:30

本文作者:lazyyoun

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!