#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Stage 7 独立运行脚本 从 Stage 6 结果开始,进行深度解构分析 支持指定 feature 和数量限制 """ import os import json import logging import argparse from stage7_analyzer import Stage7DeconstructionAnalyzer # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S', handlers=[ logging.FileHandler('stage7_standalone.log', encoding='utf-8'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) def main(): """主函数""" parser = argparse.ArgumentParser( description='Stage 7 深度解构分析(独立运行)', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=''' 示例用法: # 只处理"墨镜"特征的前10个高分帖子 python3 run_stage7.py --feature "墨镜" --max-notes 10 # 处理"墨镜"和"耳环"两个特征,每个最多5个 python3 run_stage7.py --feature "墨镜" "耳环" --max-notes 5 # 处理所有特征,按时间排序,前20个 python3 run_stage7.py --sort-by time --max-notes 20 # 只处理"墨镜",按互动量排序,跳过前3个 python3 run_stage7.py --feature "墨镜" --sort-by engagement --skip 3 # 降低分数阈值,处理更多帖子 python3 run_stage7.py --feature "墨镜" --min-score 6.0 --max-notes 30 # 使用配置文件 python3 run_stage7.py --config stage7_config.json ''' ) # 输入输出配置 parser.add_argument( '--input', default='output_v2/stage6_with_evaluations.json', help='Stage 6 结果文件路径(默认: output_v2/stage6_with_evaluations.json)' ) parser.add_argument( '--output', default='output_v2/stage7_with_deconstruction.json', help='Stage 7 输出文件路径(默认: output_v2/stage7_with_deconstruction.json)' ) # Feature 过滤(新增) parser.add_argument( '--feature', nargs='+', default=None, help='指定要处理的原始特征名称(可指定多个),如: --feature "墨镜" "耳环"。不指定则处理所有特征' ) # 过滤参数 parser.add_argument( '--min-score', type=float, default=8.0, help='最低分数阈值,只处理 >= 此分数的帖子(默认: 8.0)' ) parser.add_argument( '--skip', type=int, default=0, help='跳过前 N 个帖子(默认: 0)' ) parser.add_argument( '--max-notes', type=int, default=None, help='最多处理多少个帖子(默认: None 不限制)' ) parser.add_argument( '--sort-by', choices=['score', 'time', 'engagement'], default='score', help='排序方式: score(评分), time(时间), engagement(互动量)(默认: score)' ) # API 配置 parser.add_argument( '--api-url', default='http://192.168.245.150:7000/what/analysis/single', help='解构 API 地址(默认: http://192.168.245.150:7000/what/analysis/single)' ) parser.add_argument( '--timeout', type=int, default=30, help='API 超时时间(秒)(默认: 30)' ) parser.add_argument( '--max-retries', type=int, default=3, help='API 最大重试次数(默认: 3)' ) # 并发配置 parser.add_argument( '--max-workers', type=int, default=5, help='并发处理数(默认: 5)' ) # 从配置文件加载 parser.add_argument( '--config', default=None, help='从 JSON 配置文件加载参数' ) args = parser.parse_args() # 如果提供了配置文件,加载配置 if args.config: logger.info(f"从配置文件加载参数: {args.config}") with open(args.config, 'r', encoding='utf-8') as f: config = json.load(f) # 配置文件中的参数会覆盖命令行参数 for key, value in config.items(): setattr(args, key.replace('-', '_'), value) # 检查输入文件是否存在 if not os.path.exists(args.input): logger.error(f"输入文件不存在: {args.input}") return # 加载 Stage 6 结果 logger.info(f"加载 Stage 6 结果: {args.input}") with open(args.input, 'r', encoding='utf-8') as f: stage6_results = json.load(f) # 打印配置 logger.info("=" * 60) logger.info("运行配置:") logger.info(f" 输入文件: {args.input}") logger.info(f" 输出文件: {args.output}") if args.feature: logger.info(f" 指定特征: {', '.join(args.feature)}") else: logger.info(f" 指定特征: 全部") logger.info(f" API 地址: {args.api_url}") logger.info(f" 最低分数阈值: {args.min_score}") logger.info(f" 跳过前 N 个: {args.skip}") logger.info(f" 最多处理数: {args.max_notes if args.max_notes else '不限制'}") logger.info(f" 排序方式: {args.sort_by}") logger.info(f" 并发数: {args.max_workers}") logger.info(f" API 超时: {args.timeout}秒") logger.info(f" 最大重试: {args.max_retries}次") logger.info("=" * 60) # 创建分析器 analyzer = Stage7DeconstructionAnalyzer( api_url=args.api_url, max_workers=args.max_workers, max_notes=args.max_notes, min_score=args.min_score, skip_count=args.skip, sort_by=args.sort_by, timeout=args.timeout, max_retries=args.max_retries, output_dir=os.path.dirname(args.output) or 'output_v2', target_features=args.feature # 传递 feature 过滤参数 ) # 运行分析 try: stage7_results = analyzer.run( stage6_results=stage6_results, output_path=args.output ) # 打印结果摘要 logger.info("\n" + "=" * 60) logger.info("执行完成!") logger.info(f" 总匹配帖子数: {stage7_results['metadata']['total_matched_notes']}") logger.info(f" 实际处理数: {stage7_results['metadata']['processed_notes']}") logger.info(f" 成功: {stage7_results['metadata']['success_count']}") logger.info(f" 失败: {stage7_results['metadata']['failed_count']}") logger.info(f" 总耗时: {stage7_results['metadata']['processing_time_seconds']}秒") logger.info(f" 结果已保存: {args.output}") logger.info("=" * 60) except Exception as e: logger.error(f"执行失败: {e}", exc_info=True) raise if __name__ == '__main__': main()