💻 源码
"""
COT思维链效果对比演示 - 主程序

功能:
1. 加载配置和测试用例
2. 使用三种提示词策略分别调用模型
3. 对比展示输出结果
4. 保存结果到JSON文件

使用方法:
    python main.py

环境要求:
    - Python 3.8+
    - dashscope
    - python-dotenv
    - tabulate
"""

import os
import sys
import json
import time
from datetime import datetime
from tabulate import tabulate

# 导入项目模块
from config import MODEL_CONFIG, OUTPUT_CONFIG, PATH_CONFIG
from model_client import DashscopeModel, create_model
from prompt_templates import (
    NO_COT_PROMPT, 
    ZERO_SHOT_COT_PROMPT, 
    FEW_SHOT_COT_PROMPT,
    PROMPT_TEMPLATES
)
from test_cases import TEST_CASES


def run_comparison(model, test_case):
    """
    运行单个测试用例的三种策略对比
    
    Args:
        model: 模型实例
        test_case: 测试用例字典
        
    Returns:
        dict: 包含三种策略结果的字典
    """
    question = test_case["question"]
    
    # 策略1:无思维链
    print("  📝 正在运行:无思维链...")
    prompt1 = NO_COT_PROMPT.format(question=question)
    result1 = model.call(prompt1)
    
    # 策略2:零样本思维链
    print("  📝 正在运行:零样本思维链...")
    prompt2 = ZERO_SHOT_COT_PROMPT.format(question=question)
    result2 = model.call(prompt2)
    
    # 策略3:少样本思维链
    print("  📝 正在运行:少样本思维链...")
    prompt3 = FEW_SHOT_COT_PROMPT.format(question=question)
    result3 = model.call(prompt3)
    
    return {
        "test_case": test_case,
        "no_cot": result1,
        "zero_shot_cot": result2,
        "few_shot_cot": result3
    }


def print_comparison(result):
    """
    打印单个测试用例的对比结果
    
    Args:
        result: 对比结果字典
    """
    test_case = result["test_case"]
    
    # 打印标题
    print(f"\n{'='*80}")
    print(f"测试用例 #{test_case['id']} - {test_case['category']} ({test_case['difficulty']})")
    print(f"问题:{test_case['question']}")
    print(f"{'='*80}")
    
    # 表格展示基本信息
    table_data = [
        ["策略", "是否成功", "消耗Token"],
        ["无思维链", "✅" if result["no_cot"]["success"] else "❌", 
         result["no_cot"].get("tokens", "N/A")],
        ["零样本COT", "✅" if result["zero_shot_cot"]["success"] else "❌", 
         result["zero_shot_cot"].get("tokens", "N/A")],
        ["少样本COT", "✅" if result["few_shot_cot"]["success"] else "❌", 
         result["few_shot_cot"].get("tokens", "N/A")]
    ]
    print(tabulate(table_data, headers="firstrow", tablefmt="grid"))
    print()
    
    # 详细输出对比
    print("--- 无思维链输出 ---")
    if result["no_cot"]["success"]:
        print(result["no_cot"]["content"])
    else:
        print(f"❌ 错误:{result['no_cot'].get('error', '未知错误')}")
    
    print("\n--- 零样本思维链输出 ---")
    if result["zero_shot_cot"]["success"]:
        print(result["zero_shot_cot"]["content"])
    else:
        print(f"❌ 错误:{result['zero_shot_cot'].get('error', '未知错误')}")
    
    print("\n--- 少样本思维链输出 ---")
    if result["few_shot_cot"]["success"]:
        print(result["few_shot_cot"]["content"])
    else:
        print(f"❌ 错误:{result['few_shot_cot'].get('error', '未知错误')}")
    
    # 显示预期答案
    print(f"\n💡 预期答案:{test_case.get('expected_answer', '无')}")
    print(f"💡 提示:{test_case.get('hint', '无')}")
    print()


def print_summary(all_results):
    """
    打印所有测试的汇总统计
    
    Args:
        all_results: 所有测试结果列表
    """
    print("\n" + "="*80)
    print("📊 测试汇总统计")
    print("="*80)
    
    # 统计成功次数
    success_counts = {"no_cot": 0, "zero_shot_cot": 0, "few_shot_cot": 0}
    token_counts = {"no_cot": 0, "zero_shot_cot": 0, "few_shot_cot": 0}
    
    for result in all_results:
        for strategy in ["no_cot", "zero_shot_cot", "few_shot_cot"]:
            if result[strategy]["success"]:
                success_counts[strategy] += 1
                token_counts[strategy] += result[strategy].get("tokens", 0)
    
    total = len(all_results)
    
    # 打印统计表格
    summary_data = [
        ["策略", "成功次数", "成功率", "平均Token消耗"],
        ["无思维链", 
         f"{success_counts['no_cot']}/{total}", 
         f"{success_counts['no_cot']/total*100:.1f}%",
         f"{token_counts['no_cot']//max(success_counts['no_cot'], 1)}"],
        ["零样本COT", 
         f"{success_counts['zero_shot_cot']}/{total}", 
         f"{success_counts['zero_shot_cot']/total*100:.1f}%",
         f"{token_counts['zero_shot_cot']//max(success_counts['zero_shot_cot'], 1)}"],
        ["少样本COT", 
         f"{success_counts['few_shot_cot']}/{total}", 
         f"{success_counts['few_shot_cot']/total*100:.1f}%",
         f"{token_counts['few_shot_cot']//max(success_counts['few_shot_cot'], 1)}"]
    ]
    
    print(tabulate(summary_data, headers="firstrow", tablefmt="grid"))
    print()


def save_results(all_results):
    """
    保存所有结果到JSON文件
    
    Args:
        all_results: 所有测试结果列表
        
    Returns:
        str: 保存的文件路径
    """
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"cot_comparison_{timestamp}.json"
    filepath = os.path.join(PATH_CONFIG["results_dir"], filename)
    
    # 确保目录存在
    os.makedirs(PATH_CONFIG["results_dir"], exist_ok=True)
    
    # 构建保存的数据结构
    save_data = {
        "metadata": {
            "timestamp": timestamp,
            "model": MODEL_CONFIG["model_name"],
            "temperature": MODEL_CONFIG["temperature"],
            "total_cases": len(all_results)
        },
        "results": all_results
    }
    
    # 写入文件
    with open(filepath, "w", encoding="utf-8") as f:
        json.dump(save_data, f, ensure_ascii=False, indent=2)
    
    return filepath


def run_single_case(model, case_id):
    """
    运行单个测试用例
    
    Args:
        model: 模型实例
        case_id: 测试用例ID
    """
    from test_cases import get_test_case
    
    test_case = get_test_case(case_id)
    if not test_case:
        print(f"❌ 找不到测试用例 #{case_id}")
        return None
    
    print(f"\n正在运行测试用例 #{case_id}...")
    result = run_comparison(model, test_case)
    print_comparison(result)
    
    return result


def main():
    """主程序入口"""
    print("="*80)
    print("🧠 COT思维链效果对比演示")
    print("="*80)
    print()
    
    # 检查命令行参数
    use_mock = "--mock" in sys.argv
    single_case = None
    
    for i, arg in enumerate(sys.argv):
        if arg == "--case" and i + 1 < len(sys.argv):
            try:
                single_case = int(sys.argv[i + 1])
            except ValueError:
                print("❌ --case 参数需要是整数")
                return
    
    # 初始化模型
    try:
        model = create_model(use_mock=use_mock)
        print(f"✅ 模型初始化成功")
        print(f"   模型名称:{model.model_name}")
        print(f"   温度参数:{model.temperature}")
        print()
    except Exception as e:
        print(f"❌ 模型初始化失败:{e}")
        return
    
    # 确定要运行的测试用例
    if single_case:
        from test_cases import get_test_case
        test_case = get_test_case(single_case)
        if not test_case:
            print(f"❌ 找不到测试用例 #{single_case}")
            return
        cases_to_run = [test_case]
        print(f"📋 运行模式:单个测试用例 (#{single_case})")
    else:
        cases_to_run = TEST_CASES
        print(f"📋 运行模式:全部测试用例 ({len(TEST_CASES)}个)")
    
    print()
    
    # 运行测试
    all_results = []
    start_time = time.time()
    
    for i, test_case in enumerate(cases_to_run, 1):
        print(f"\n[{i}/{len(cases_to_run)}] 正在运行测试用例 #{test_case['id']}...")
        
        try:
            result = run_comparison(model, test_case)
            all_results.append(result)
            print_comparison(result)
            
            # 在测试用例之间添加短暂延迟,避免API限流
            if i < len(cases_to_run):
                time.sleep(1)
                
        except Exception as e:
            print(f"❌ 运行测试用例 #{test_case['id']} 时发生错误:{e}")
            continue
    
    elapsed_time = time.time() - start_time
    
    # 打印汇总
    if len(all_results) > 1:
        print_summary(all_results)
    
    # 保存结果
    if OUTPUT_CONFIG["save_json"] and all_results:
        try:
            filepath = save_results(all_results)
            print(f"💾 结果已保存到:{filepath}")
        except Exception as e:
            print(f"❌ 保存结果时发生错误:{e}")
    
    # 结束信息
    print("\n" + "="*80)
    print("✅ 所有测试完成!")
    print(f"⏱️  总耗时:{elapsed_time:.1f}秒")
    print()
    print("📊 总结:")
    print("   • 思维链能够显著提升大模型在复杂推理任务上的表现")
    print("   • 少样本COT通常比零样本COT效果更好,因为提供了明确的格式示例")
    print("   • 无思维链策略在简单问题上可能更快,但在复杂推理上容易出错")
    print("="*80)


if __name__ == "__main__":
    main()