├── .env ├── .gitignore ├── LICENSE ├── README.md ├── gen_answers.py ├── requirements.txt ├── run_evol.py ├── src ├── __init__.py ├── analyzers │ ├── __init__.py │ ├── base_analyzer.py │ └── trajectory_analyzer.py ├── autoevol.py ├── evaluator │ ├── __init__.py │ ├── base_evaluator.py │ ├── failure_detector_evaluator.py │ └── reward_model_evaluator.py ├── evolvers │ ├── __init__.py │ ├── base_evolver.py │ └── recurrent_evolver.py ├── generators │ ├── __init__.py │ ├── base_generator.py │ ├── openai.py │ ├── openrouter.py │ └── vllm.py ├── optimizers │ ├── __init__.py │ ├── base_optimizer.py │ └── evol_optimizer.py └── utils.py ├── tests ├── analyzers │ └── test_trajectory.py ├── evolvers │ └── test_recurrent.py ├── generators │ ├── test_openai.py │ └── test_openrouter.py ├── optimizers │ └── test_wizard.py └── test_full_flow.py └── visualize_evol_instruct.ipynb /.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcee-ai/EvolKit/aa85caae55b4464c7bc1f8dca0c55e7dbdcbd6ee/.env -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *__pycache__* 2 | *.json 3 | .pytest_cache 4 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2024 Arcee.ai 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EvolKit 2 | 3 | EvolKit is an framework for automatically enhancing the complexity of instructions used in fine-tuning Large Language Models (LLMs). Our project aims to revolutionize the evolution process by leveraging open-source LLMs, moving away from closed-source alternatives. 4 | 5 | ## Key Features 6 | 7 | - Automatic instruction complexity enhancement 8 | - Integration with open-source LLMs 9 | - Streamlined fine-tuning process 10 | - Support for various datasets from Hugging Face 11 | - Flexible configuration options for optimization 12 | 13 | ## Installation 14 | 15 | To set up EvolKit, follow these steps: 16 | 17 | 1. Clone the repository: 18 | 19 | ``` 20 | git clone https://github.com/arcee-ai/EvolKit.git 21 | cd EvolKit 22 | ``` 23 | 24 | 2. Install the required dependencies: 25 | 26 | ``` 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | ## Usage 31 | 32 | To run the AutoEvol script, use the following command structure: 33 | 34 | ``` 35 | python run_evol.py --dataset [options] 36 | ``` 37 | 38 | ### Required Parameters: 39 | 40 | - `--dataset `: The name of the dataset on Hugging Face to use. 41 | - `--model `: Model to use for evolving instructions. 42 | - `--generator `: Type of generator to use ('openrouter' or 'vllm'). 43 | - `--batch_size `: Number of instructions to process in each batch. 44 | - `--num_methods `: Number of evolution methods to use. 45 | - `--max_concurrent_batches `: Maximum number of batches to process concurrently (in our experiment, a cluster of 8xH100 hosting Qwen2-72B-Instruct-GPTQ-Int8 can handle batch size of 50 concurrently). 46 | - `--evolve_epoch `: Maximum number of epochs for evolving each instruction. 47 | - `--output_file `: Name of the output file to save results. 48 | 49 | ### Optional Parameters: 50 | 51 | - `--dev_set_size `: Number of samples to use in the development set. Use -1 for no devset. Default is -1. (We do not recommend using a dev set since it will take much more time to finish each round) 52 | - `--use_reward_model`: Flag to use a reward model for evaluation. No value required. 53 | 54 | ### Models 55 | 56 | We found 2 models that work very well with this pipeline: 57 | - Qwen2-72B-Instruct and DeepSeek-V2.5 (GPTQ and AWQ versions are fine too). 58 | - Other models might work but it has to be very good at generating structured content (in order to parse using parsing operations) 59 | 60 | ### VLLM Support 61 | 62 | To use VLLM as the backend, set the `VLLM_BACKEND` environment variable: 63 | 64 | ``` 65 | export VLLM_BACKEND=http://your-vllm-backend-url:port/v1 66 | ``` 67 | 68 | If not set, it will default to 'http://localhost:8000/v1'. 69 | 70 | ### Example Usage: 71 | 72 | To run AutoEvol on the 'small_tomb' dataset with custom parameters: 73 | 74 | ``` 75 | python run_evol.py --dataset qnguyen3/small_tomb --model Qwen/Qwen2-72B-Instruct-GPTQ-Int8 --generator vllm --batch_size 100 --num_methods 3 --max_concurrent_batches 10 --evolve_epoch 3 --output_file the_tomb_evolved-3e-batch100.json --dev_set_size 5 --use_reward_model 76 | ``` 77 | 78 | This command will: 79 | 1. Load the 'qnguyen3/small_tomb' dataset from Hugging Face. 80 | 2. Use the Qwen2-72B-Instruct model with VLLM as the generator. 81 | 3. Process samples in batches of 100. 82 | 4. Apply 3 evolution methods for each instruction. 83 | 5. Process 10 batches concurrently. 84 | 6. Evolve each instruction for up to 3 epochs. 85 | 7. Use 5 samples for the development set. 86 | 8. Use the reward model for evaluation. 87 | 9. Output the final evolved instructions to the_tomb_evolved-3e-batch100.json. 88 | 89 | After evolving the instructions, you can generate answers using: 90 | 91 | ``` 92 | python gen_answers.py --model Qwen/Qwen2-72B-Instruct-GPTQ-Int8 --generator vllm --data_path the_tomb_evolved-3e-batch100.json --batch_size 50 --output completed_evol_data.json 93 | ``` 94 | 95 | The final dataset will be saved to completed_evol_data.json in ShareGPT format. 96 | 97 | ## Components 98 | 99 | EvolKit consists of several key components: 100 | 101 | - **Generator**: Uses an LLM for generating initial instructions (OpenRouter or VLLM). 102 | - **Evolver**: Employs a recurrent evolution strategy. 103 | - **Analyzer**: Utilizes trajectory analysis. 104 | - **Evaluator**: Offers two options: 105 | - Reward Model Evaluator 106 | - Failure Detector Evaluator (originally from WizardLM's paper) 107 | - **Optimizer**: Optimizes the evolution method for the next round. 108 | 109 | ## Output 110 | 111 | The script saves the results in JSON format to the specified output file. Each entry in the JSON file represents an evolved instruction along with relevant metadata. 112 | 113 | Find a 20k subset of a dataset generated using EvolKit [here](https://huggingface.co/datasets/arcee-ai/EvolKit-20k) 114 | 115 | ## Acknowledgement 116 | - Microsoft's WizardLM team for the inspiration from the [AutoEvol paper](https://arxiv.org/pdf/2406.00770). 117 | -------------------------------------------------------------------------------- /gen_answers.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import json 3 | import argparse 4 | from typing import List, Dict 5 | from src.generators import OpenRouterGenerator, VLLMGenerator, BaseGenerator 6 | from datasets import load_dataset 7 | from tqdm import tqdm 8 | import time 9 | from os import getenv 10 | 11 | async def process_batch(generator: BaseGenerator, batch: List[str], system_prompt: str) -> List[Dict]: 12 | tasks = [] 13 | for item in batch: 14 | try: 15 | task = generator.agenerate(item, system_prompt, temperature=0.5) 16 | tasks.append(task) 17 | except: 18 | task = 'error' 19 | tasks.append(task) 20 | results = await asyncio.gather(*tasks) 21 | 22 | processed_items = [] 23 | for item, result in zip(batch, results): 24 | processed_item = { 25 | 'conversations': [ 26 | {"from": "human", "value": item}, 27 | {"from": "gpt", "value": result} 28 | ] 29 | } 30 | processed_items.append(processed_item) 31 | 32 | return processed_items 33 | 34 | async def process_data(model:str, generator_str: str, file_path: str, batch_size: int, output_file: str): 35 | # Initialize OpenRouterGenerator 36 | generator = ( 37 | VLLMGenerator(model=model, base_url=getenv('VLLM_BACKEND') or 'http://localhost:8000/v1') 38 | if generator_str == 'vllm' 39 | else OpenRouterGenerator(model=model)) 40 | 41 | # Load data 42 | if '.json' not in file_path: 43 | data = load_dataset(file_path)['train'] # Assuming the main split is named 'train' 44 | else: 45 | with open(file_path, 'r') as file: 46 | data = json.load(file) 47 | 48 | start_idx = 0 49 | 50 | # Calculate total number of batches 51 | total_batches = (len(data) + batch_size - start_idx - 1) // batch_size 52 | 53 | instructions = [] 54 | for sample in data: 55 | convo = sample['conversations'] 56 | if convo[0]['from'] == 'human': 57 | user = convo[0]['value'] 58 | else: 59 | user = convo[1]['value'] 60 | 61 | instructions.append(user) 62 | 63 | 64 | # Process in batches 65 | all_results = [] 66 | with tqdm(total=total_batches, desc="Processing batches") as pbar: 67 | for i in range(0, len(data), batch_size): 68 | batch = instructions[i+start_idx:i+batch_size+start_idx] 69 | processed_batch = await process_batch(generator, batch, "You are a helpful assistant. Answer the question from the user. Give full solution and explaination.") 70 | 71 | # Extend results and save 72 | all_results.extend(processed_batch) 73 | 74 | # Save and overwrite for each batch 75 | with open(output_file, 'w') as f: 76 | json.dump(all_results, f, indent=2, ensure_ascii=False) 77 | 78 | pbar.update(1) 79 | time.sleep(5) 80 | 81 | def main(): 82 | parser = argparse.ArgumentParser(description="Process data using OpenRouterGenerator") 83 | parser.add_argument("--model", type=str, required=True, help="Model use to evol instructions.") 84 | parser.add_argument("--generator", type=str, required=True, choices=['openrouter', 'vllm'], help="Type of generator to use.") 85 | parser.add_argument("--data_path", required=True, help="Path to the JSON file or Hugging Face dataset repo") 86 | parser.add_argument("--batch_size", type=int, default=10, help="Batch size for processing") 87 | parser.add_argument("--output", default="final_evolved_data.json", help="Output file path") 88 | 89 | args = parser.parse_args() 90 | 91 | asyncio.run(process_data(args.model, args.generator, args.data_path, args.batch_size, args.output)) 92 | 93 | if __name__ == "__main__": 94 | main() -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asyncio 2 | accelerate 3 | transformers 4 | datasets 5 | torch 6 | protobuf 7 | pytest 8 | pytest-asyncio 9 | openai 10 | sentencepiece 11 | einops -------------------------------------------------------------------------------- /run_evol.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import asyncio 4 | import argparse 5 | from datasets import load_dataset 6 | from src.generators import OpenRouterGenerator, VLLMGenerator 7 | from src.evolvers import RecurrentEvolver 8 | from src.analyzers import TrajectoryAnalyzer 9 | from src.evaluator import FailureDetectorEvaluator, RewardModelEvaluator 10 | from src.optimizers.evol_optimizer import EvolOptimizer 11 | from src import AutoEvol 12 | from os import getenv 13 | 14 | def load_and_process_dataset(dataset_name, dev_set_size=5): 15 | # Load the dataset from Hugging Face 16 | dataset = load_dataset(dataset_name) 17 | 18 | # Assuming the dataset has a 'train' split. Adjust if needed. 19 | if 'train' not in dataset: 20 | raise ValueError(f"The dataset {dataset_name} does not have a 'train' split.") 21 | 22 | full_dataset = dataset['train'] 23 | 24 | # Shuffle the dataset 25 | full_dataset = full_dataset.shuffle(seed=42) 26 | 27 | # Ensure dev_set_size is not larger than the dataset 28 | if dev_set_size > len(full_dataset): 29 | raise ValueError(f"Specified dev set size ({dev_set_size}) is larger than the dataset size ({len(full_dataset)})") 30 | full_filtered = [] 31 | for sample in full_dataset['conversations']: 32 | for turn in sample: 33 | if turn['from'] == 'system': 34 | break 35 | if turn['from'] == 'human': 36 | full_filtered.append(turn['value']) 37 | break 38 | 39 | if dev_set_size != -1: 40 | # Split the dataset 41 | train_instructions = full_filtered[dev_set_size:] 42 | dev_instructions = full_filtered[:dev_set_size] 43 | 44 | return train_instructions, dev_instructions 45 | else: 46 | return full_filtered, [] 47 | 48 | async def save_results(results, output_file): 49 | with open(output_file, 'w') as f: 50 | json.dump(results, f, indent=2, ensure_ascii=False) 51 | 52 | async def main(): 53 | parser = argparse.ArgumentParser(description="Run AutoEvol with specified parameters") 54 | parser.add_argument("--dataset", required=True, help="Name of the dataset on Hugging Face") 55 | parser.add_argument("--model", type=str, required=True, help="Model use to evol instructions.") 56 | parser.add_argument("--generator", type=str, required=True, choices=['openrouter', 'vllm'], help="Type of generator to use.") 57 | parser.add_argument("--batch_size", type=int, required=True, help="Batch size for processing") 58 | parser.add_argument("--num_methods", type=int, required=True, help="Number of methods to use") 59 | parser.add_argument("--max_concurrent_batches", type=int, required=True, help="Maximum number of concurrent batches") 60 | parser.add_argument("--evolve_epoch", type=int, required=True, help="Maximum number of epoch for each instruction") 61 | parser.add_argument("--output_file", type=str, required=True, help="Name of output file") 62 | 63 | # Optional arguments 64 | parser.add_argument("--dev_set_size", type=int, default=-1, help="Maximum samples for dev set. Use -1 for no dev set.") 65 | parser.add_argument("--use_reward_model", action="store_true", help="Use reward model for evaluation") 66 | 67 | args = parser.parse_args() 68 | 69 | # Load and process the dataset 70 | train_set, dev_set = load_and_process_dataset(args.dataset, args.dev_set_size) 71 | 72 | generator = ( 73 | VLLMGenerator(model=args.model, base_url=getenv('VLLM_BACKEND') or 'http://localhost:8000/v1') 74 | if args.generator == 'vllm' 75 | else OpenRouterGenerator(model=args.model) 76 | ) 77 | 78 | 79 | components = { 80 | 'generator': generator, 81 | 'evolver': RecurrentEvolver(generator), 82 | 'analyzer': TrajectoryAnalyzer(generator), 83 | 'evaluator': RewardModelEvaluator() if args.use_reward_model else FailureDetectorEvaluator(), 84 | 'dev_set': dev_set 85 | } 86 | components['optimizer'] = EvolOptimizer(generator, components['evaluator']) 87 | 88 | auto_evol = AutoEvol(components) 89 | 90 | print(f"Dataset: {args.dataset}") 91 | if args.dev_set_size != -1: 92 | print(f"Train set size: {len(train_set)}, Dev set size: {len(dev_set)}") 93 | else: 94 | print(f"Train set size: {len(train_set)}") 95 | print(f"Batch size: {args.batch_size}") 96 | print(f"Number of methods: {args.num_methods}") 97 | print(f"Max concurrent batches: {args.max_concurrent_batches}") 98 | 99 | start_time = time.time() 100 | 101 | output_file = args.output_file 102 | all_results = [] 103 | 104 | total_batches = (len(train_set) + args.batch_size - 1) // args.batch_size # Calculate total number of batches 105 | 106 | for i in range(0, len(train_set), args.batch_size): 107 | batch = train_set[i:i+args.batch_size] 108 | batch_results = await auto_evol.run(batch, batch_size=args.batch_size, num_methods=args.num_methods, max_concurrent_batches=args.max_concurrent_batches, evolve_epoch=args.evolve_epoch) 109 | all_results.extend(batch_results) 110 | 111 | current_batch = i // args.batch_size + 1 112 | print(f"Done batch {current_batch}/{total_batches}") # New print statement for batch progress 113 | print(f"Batch {current_batch} completed. Saving results...") 114 | await save_results(all_results, output_file) 115 | 116 | end_time = time.time() 117 | total_time = end_time - start_time 118 | 119 | print(f"Total execution time: {total_time:.2f} seconds") 120 | print(f"Final results saved to {output_file}") 121 | 122 | if __name__ == "__main__": 123 | asyncio.run(main()) -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | from .generators import * 2 | from .autoevol import AutoEvol -------------------------------------------------------------------------------- /src/analyzers/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_analyzer import BaseAnalyzer 2 | from .trajectory_analyzer import TrajectoryAnalyzer -------------------------------------------------------------------------------- /src/analyzers/base_analyzer.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class BaseAnalyzer(ABC): 4 | @abstractmethod 5 | def analyze(self, current_method, feedback): 6 | pass -------------------------------------------------------------------------------- /src/analyzers/trajectory_analyzer.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import List 3 | 4 | from .base_analyzer import BaseAnalyzer 5 | from src.generators import BaseGenerator 6 | 7 | TRAJECTORY_ANALYZER_SYSTEM_PROMPT = """ 8 | You are an expert at analyzing the evolution of a given instruction. You will look at the trajectory of the evolution from an initial instruction and make feedbacks based on how the complexity is being increased in each stage. 9 | """ 10 | 11 | TRAJECTORY_ANALYZER_PROMPT = """ 12 | The following list shows cases where an Instruction evolves into a more complex version of an Instruction. 13 | For each case, stage 0 represents the Instruction in its initial state, and stage 1 requires an increase in complexity based on the previous stage. 14 | 15 | Please identify cases that failed to evolve, and provide the reason why it fails. 16 | 17 | Please strictly output using the following format, do not add anything else to the response: 18 | 19 | ***FORMAT INSTRUCTION*** 20 | Choose one of the two options: 21 | Option 1 - If all cases are evolving correctly, please strictly output: 22 | ### PASSED 23 | 24 | Option 2 - If you identify cases that did not evolve correctly, please strictly output: 25 | ### FAILED - Reason: [reason_of_fail] 26 | and so on... 27 | ***END OF FORMAT INSTRUCTION*** 28 | 29 | Evolution Trajectory: 30 | {{evol_trajectory}} 31 | """ 32 | 33 | class TrajectoryAnalyzer(BaseAnalyzer): 34 | def __init__(self, generator: BaseGenerator) -> None: 35 | self.generator = generator 36 | 37 | async def analyze_async(self, init_instruction: str, evolved_instructions: List[str]) -> List[str]: 38 | async def generate_single(evolved_instruction): 39 | trajectory_str = f""" 40 | Stage 0: {init_instruction} 41 | Stage 1: {evolved_instruction} 42 | """ 43 | trajectory_prompt = TRAJECTORY_ANALYZER_PROMPT.replace('{{evol_trajectory}}', trajectory_str) 44 | feedback = await self.generator.agenerate(prompt=trajectory_prompt, system_prompt=TRAJECTORY_ANALYZER_SYSTEM_PROMPT, temperature=0.2) 45 | 46 | return feedback 47 | 48 | tasks = [generate_single(evolved_instruction) for evolved_instruction in evolved_instructions] 49 | results = await asyncio.gather(*tasks) 50 | 51 | return results 52 | 53 | def analyze(self, init_instruction: str, evolved_instructions: List[str]) -> List[str]: 54 | return asyncio.run(self.analyze_async(init_instruction, evolved_instructions)) -------------------------------------------------------------------------------- /src/autoevol.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | from typing import List, Dict, Any 4 | from src.evolvers.recurrent_evolver import INITIAL_EVOLVE_METHOD 5 | from .utils import parse_steps 6 | from tqdm import tqdm 7 | 8 | class AutoEvol: 9 | def __init__(self, components: Dict[str, Any]): 10 | self.components = components 11 | 12 | async def process_instruction(self, instruction: str, num_methods: int, evolve_epoch: int = 2) -> Dict[str, Any]: 13 | start_time = time.time() 14 | instruction_stages = [instruction] 15 | methods = [INITIAL_EVOLVE_METHOD.replace("{{instruction}}", instruction_stages[0])] 16 | current_method = methods[0] 17 | 18 | result = { 19 | "original_instruction": instruction, 20 | "stages": [] 21 | } 22 | 23 | for i in range(evolve_epoch): 24 | stage_start_time = time.time() 25 | stage_result = { 26 | "stage": i + 1, 27 | "input_instruction": instruction_stages[-1], 28 | "method": current_method, 29 | "evolved_instructions": [], 30 | "feedbacks": [], 31 | "optimized_method": "", 32 | "final_evolved_instruction": "" 33 | } 34 | 35 | evolved_instructions = await self.components['evolver'].evolve_async(instruction_stages[-1], current_method, n=num_methods) 36 | 37 | feedbacks = await self.components['analyzer'].analyze_async(instruction_stages[-1], evolved_instructions) 38 | 39 | stage_result["evolved_instructions"] = evolved_instructions 40 | stage_result["feedbacks"] = feedbacks 41 | 42 | optimized_method, _ = await self.components['optimizer'].optimize( 43 | current_method, 44 | feedback=feedbacks, 45 | evolver=self.components['evolver'], 46 | development_set=self.components['dev_set'] if len(self.components['dev_set']) > 0 else [instruction_stages[-1]] 47 | ) 48 | 49 | optimized_method_steps = parse_steps(optimized_method) 50 | optimized_method = self.components['evolver'].build_new_method(optimized_method_steps, instruction_stages[-1]) 51 | 52 | stage_result["optimized_method"] = optimized_method 53 | 54 | evolved_instruction = await self.components['generator'].agenerate(prompt=optimized_method, temperature=0.5) 55 | evolved_instruction_steps = parse_steps(evolved_instruction) 56 | 57 | try: 58 | if evolved_instruction_steps[-1]['step_name'] == 'Finally Rewritten Instruction': 59 | evolved_instruction = evolved_instruction_steps[-1]['step_instruction'] 60 | except: 61 | print('Error: Unexpected step name in evolved instruction') 62 | evolved_instruction = instruction_stages[-1] # Append the same instruction as before 63 | 64 | instruction_stages.append(evolved_instruction) 65 | methods.append(optimized_method) 66 | current_method = self.components['evolver'].build_new_method(optimized_method_steps, evolved_instruction) 67 | 68 | stage_result["final_evolved_instruction"] = evolved_instruction 69 | stage_end_time = time.time() 70 | stage_result["stage_time"] = stage_end_time - stage_start_time 71 | result["stages"].append(stage_result) 72 | 73 | result["final_instruction"] = instruction_stages[-1] 74 | end_time = time.time() 75 | result["total_time"] = end_time - start_time 76 | return result 77 | 78 | async def process_batch(self, batch: List[str], num_methods: int, evolve_epoch: int, pbar: tqdm) -> List[Dict[str, Any]]: 79 | batch_results = await asyncio.gather(*[self.process_instruction(instruction, num_methods, evolve_epoch) for instruction in batch]) 80 | pbar.update(len(batch)) 81 | return batch_results 82 | 83 | async def run(self, dataset: List[str], batch_size: int = 10, num_methods: int = 5, max_concurrent_batches: int = 2, evolve_epoch: int = 2) -> List[Dict[str, Any]]: 84 | print(f"Starting dataset processing. Dataset size: {len(dataset)}, Max concurrent batches: {max_concurrent_batches}") 85 | start_time = time.time() 86 | 87 | batches = [dataset[i:i+batch_size] for i in range(0, len(dataset), batch_size)] 88 | pbar = tqdm(total=len(dataset), desc="Processing instructions") 89 | 90 | semaphore = asyncio.Semaphore(max_concurrent_batches) 91 | 92 | async def process_batch_with_semaphore(batch): 93 | async with semaphore: 94 | return await self.process_batch(batch, num_methods, evolve_epoch, pbar) 95 | 96 | results = await asyncio.gather(*[process_batch_with_semaphore(batch) for batch in batches]) 97 | 98 | pbar.close() 99 | 100 | end_time = time.time() 101 | total_time = end_time - start_time 102 | print(f"\nDataset processing complete. Total time: {total_time:.2f} seconds") 103 | return [item for sublist in results for item in sublist] # Flatten the list of batch results -------------------------------------------------------------------------------- /src/evaluator/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_evaluator import BaseEvaluator 2 | from .failure_detector_evaluator import FailureDetectorEvaluator 3 | from .reward_model_evaluator import RewardModelEvaluator -------------------------------------------------------------------------------- /src/evaluator/base_evaluator.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List, Optional 3 | 4 | class BaseEvaluator(ABC): 5 | @abstractmethod 6 | def evaluate(self, instructions: List[str], responses: List[str]) -> float: 7 | pass 8 | 9 | @abstractmethod 10 | def select_best_method(self, methods: List[str], instructions: List[str], responses: List[List[str]]) -> tuple: 11 | pass -------------------------------------------------------------------------------- /src/evaluator/failure_detector_evaluator.py: -------------------------------------------------------------------------------- 1 | from .base_evaluator import BaseEvaluator 2 | from typing import List, Tuple 3 | import re 4 | from concurrent.futures import ThreadPoolExecutor, as_completed 5 | 6 | class FailureDetectorEvaluator(BaseEvaluator): 7 | def __init__(self, max_workers: int = 4): 8 | self.stagnant_pattern = re.compile(r'\b(understood|thank you|noted|got it|okay|alright)\b.*\?$', re.IGNORECASE) 9 | self.insufficient_pattern = re.compile(r'\b(sure|certainly|of course|happy to help)\b.*\?$|what do you mean|could you explain', re.IGNORECASE) 10 | self.loss_pattern = re.compile(r'please provide|need more information|could you clarify|what exactly', re.IGNORECASE) 11 | self.executor = ThreadPoolExecutor(max_workers=max_workers) 12 | 13 | def is_failure(self, response: str) -> bool: 14 | return ( 15 | bool(self.stagnant_pattern.search(response)) or 16 | bool(self.insufficient_pattern.search(response)) or 17 | bool(self.loss_pattern.search(response)) 18 | ) 19 | 20 | def evaluate(self, instructions: List[str], responses: List[str]) -> float: 21 | with self.executor as executor: 22 | failure_futures = [executor.submit(self.is_failure, response) for response in responses] 23 | failures = sum(future.result() for future in as_completed(failure_futures)) 24 | return failures / len(responses) 25 | 26 | async def select_best_method(self, methods: List[str], instructions: List[str], responses: List[List[str]]) -> Tuple[str, float]: 27 | evaluation_results = [] 28 | 29 | with self.executor as executor: 30 | futures = [executor.submit(self.evaluate, instructions, method_responses) 31 | for method_responses in responses] 32 | 33 | for method, future in zip(methods, futures): 34 | failure_rate = future.result() 35 | evaluation_results.append((method, failure_rate)) 36 | 37 | best_method, lowest_failure_rate = min(evaluation_results, key=lambda x: x[1]) 38 | return best_method, lowest_failure_rate -------------------------------------------------------------------------------- /src/evaluator/reward_model_evaluator.py: -------------------------------------------------------------------------------- 1 | from .base_evaluator import BaseEvaluator 2 | from typing import List, Optional 3 | import torch 4 | from transformers import AutoModel, AutoTokenizer 5 | from concurrent.futures import ThreadPoolExecutor 6 | import asyncio 7 | 8 | 9 | class RewardModelEvaluator(BaseEvaluator): 10 | def __init__(self, model: str = "internlm/internlm2-1_8b-reward"): 11 | self.model = AutoModel.from_pretrained( 12 | model, 13 | device_map="cuda", 14 | torch_dtype=torch.float16, 15 | trust_remote_code=True, 16 | ) 17 | self.tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=True) 18 | self.executor = ThreadPoolExecutor(max_workers=4) # Adjust based on your GPU 19 | 20 | async def get_score(self, instruction: str, response: str) -> float: 21 | chat = [ 22 | {"role": "user", "content": instruction}, 23 | {"role": "assistant", "content": response} 24 | ] 25 | loop = asyncio.get_event_loop() 26 | score = await loop.run_in_executor(self.executor, self.model.get_score, self.tokenizer, chat) 27 | torch.cuda.empty_cache() 28 | return score 29 | 30 | async def evaluate(self, instructions: List[str], responses: List[str]) -> float: 31 | scores = await asyncio.gather(*[self.get_score(instruction, response) 32 | for instruction, response in zip(instructions, responses)]) 33 | torch.cuda.empty_cache() 34 | return sum(scores) / len(scores) 35 | 36 | async def select_best_method(self, methods: List[str], instructions: List[str], responses: List[List[str]]) -> tuple: 37 | evaluation_tasks = [self.evaluate(instructions, method_responses) 38 | for method_responses in responses] 39 | scores = await asyncio.gather(*evaluation_tasks) 40 | torch.cuda.empty_cache() 41 | 42 | best_index = max(range(len(scores)), key=scores.__getitem__) 43 | return methods[best_index], scores[best_index] -------------------------------------------------------------------------------- /src/evolvers/__init__.py: -------------------------------------------------------------------------------- 1 | from .recurrent_evolver import RecurrentEvolver 2 | from .base_evolver import BaseEvolver -------------------------------------------------------------------------------- /src/evolvers/base_evolver.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class BaseEvolver(ABC): 4 | @abstractmethod 5 | def evolve(self, instruction, evolving_method): 6 | pass -------------------------------------------------------------------------------- /src/evolvers/recurrent_evolver.py: -------------------------------------------------------------------------------- 1 | from .base_evolver import BaseEvolver 2 | from src.generators.base_generator import BaseGenerator 3 | 4 | import asyncio 5 | from typing import List 6 | 7 | INITIAL_EVOLVE_METHOD = """ 8 | You are an Instruction Rewriter that rewrites the given #Instruction# into a more complex version. 9 | Please follow the steps below to rewrite the given "#Instruction#" into a more complex version. 10 | 11 | Step 1: Please read the "#Instruction#" below carefully and list all the possible methods to make this instruction more complex (to make it a bit harder for well-known AI assistants such as ChatGPT and GPT4 to handle). Please do not provide methods to change the language of the instruction! 12 | 13 | Step 2: Please create a comprehensive plan based on the #Methods List# generated in Step 1 to make the #Instruction# more complex. The plan should include several methods from the #Methods List#. 14 | 15 | Step 3: Please execute the plan step by step and provide the #Rewritten Instruction#. #Rewritten Instruction# can only add 10 to 20 words into the "#Instruction#". 16 | 17 | Step 4: Please carefully review the #Rewritten Instruction# and identify any unreasonable parts. Ensure that the #Rewritten Instruction# is only a more complex version of the #Instruction#, make sure that it only adds 10 to 20 words into the "#Instruction#". Just provide the #Finally Rewritten Instruction# without any explanation. 18 | 19 | #Instruction#: {{instruction}} 20 | 21 | REMEMBER that you are generating a more complex version of the instruction (or question), NOT answering #Instruction#. The #Finally Rewritten Instruction# should only add 10 to 20 words the #Instruction# below. 22 | 23 | **Output Instructions** 24 | Please generate the optimized instruction strictly using ONLY the given below format, do not add anything else: 25 | 26 | ```Optimized Instruction 27 | Step 1: 28 | #Methods List# 29 | 30 | Step 2: 31 | #Plan# 32 | 33 | Step 3: 34 | #Rewritten Instruction# 35 | 36 | Step 4: 37 | #Finally Rewritten Instruction# 38 | ``` 39 | """ 40 | 41 | INTERATIVE_EVOLVE_METHOD = """ 42 | You are an Instruction Rewriter that rewrites the given #Instruction# into a more complex version. 43 | Please follow the steps below to rewrite the given "#Instruction#" into a more complex version. 44 | 45 | {steps} 46 | #Instruction#: {instruction} 47 | 48 | REMEMBER that you are generating a more complex version of the instruction (or question), NOT answering #Instruction#. The #Finally Rewritten Instruction# should only add 10 to 20 words the #Instruction# below. 49 | 50 | **Output Instructions** 51 | Please generate the optimized instruction strictly using ONLY the given below format, do not add anything else: 52 | 53 | ```Optimized Instruction 54 | {format_steps} 55 | ``` 56 | """ 57 | 58 | class RecurrentEvolver(BaseEvolver): 59 | def __init__(self, generator: BaseGenerator) -> None: 60 | self.generator = generator 61 | 62 | async def evolve_async(self, instruction: str, evolving_method: str = None, n: int = 1) -> List[str]: 63 | evol_method = evolving_method if evolving_method else INITIAL_EVOLVE_METHOD.format(instruction=instruction) 64 | 65 | async def generate_single(): 66 | return await self.generator.agenerate(evol_method) 67 | 68 | tasks = [generate_single() for _ in range(n)] 69 | results = await asyncio.gather(*tasks) 70 | 71 | return results 72 | 73 | def evolve(self, instruction: str, evolving_method: str = None, n: int = 1) -> List[str]: 74 | return asyncio.run(self.evolve_async(instruction, evolving_method, n)) 75 | 76 | def build_new_method(self, steps, instruction): 77 | step_details = "" 78 | format_steps = "" 79 | 80 | for i, step in enumerate(steps, start=1): 81 | step_name = step['step_name'] 82 | step_instruction = step['step_instruction'] 83 | 84 | step_details += f"Step {i}: {step_instruction}\n\n" 85 | format_steps += f"Step {i}:\n#{step_name}#\n\n" 86 | 87 | new_method = INTERATIVE_EVOLVE_METHOD.format(steps=step_details.strip(), instruction=instruction, format_steps=format_steps.strip()) 88 | return new_method 89 | -------------------------------------------------------------------------------- /src/generators/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_generator import BaseGenerator 2 | from .openai import OpenAIGenerator 3 | from .openrouter import OpenRouterGenerator 4 | from .vllm import VLLMGenerator -------------------------------------------------------------------------------- /src/generators/base_generator.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Optional 3 | 4 | class BaseGenerator(ABC): 5 | @abstractmethod 6 | def generate(self, prompt: str, system_prompt: Optional[str] = "You are a helpful AI assistant.", temperature: Optional[float] = 0.5) -> str: 7 | pass 8 | 9 | async def agenerate(self, prompt: str, system_prompt: Optional[str] = "You are a helpful AI assistant.", temperature: Optional[float] = 0.5): 10 | pass -------------------------------------------------------------------------------- /src/generators/openai.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from os import getenv 3 | 4 | from openai import OpenAI 5 | 6 | from .base_generator import BaseGenerator 7 | 8 | class OpenAIGenerator(BaseGenerator): 9 | def __init__(self, model: str = "gpt-4", api_key: Optional[str] = None) -> None: 10 | self.model = model 11 | self.api_key = api_key if api_key else getenv("OPENAI_API_KEY") 12 | self.client = OpenAI() 13 | 14 | def generate(self, prompt: str, system_prompt: str = "You are a helpful AI assistant.", temperature: float = 0.7): 15 | response = self.client.chat.completions.create( 16 | model=self.model, 17 | messages=[ 18 | {"role": "system", "content": system_prompt}, 19 | {"role": "user", "content": prompt} 20 | ], 21 | temperature=temperature,) 22 | # print(response.choices[0].message.content) # For Debuging 23 | return response.choices[0].message.content -------------------------------------------------------------------------------- /src/generators/openrouter.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from os import getenv 3 | 4 | from openai import OpenAI, AsyncOpenAI 5 | from .openai import OpenAIGenerator 6 | 7 | class OpenRouterGenerator(OpenAIGenerator): 8 | def __init__(self, model: str = "deepseek/deepseek-chat", api_key: Optional[str] = None) -> None: 9 | self.model = model 10 | self.api_key = api_key if api_key else getenv("OPENROUTER_API_KEY") 11 | self.client = OpenAI(base_url="https://openrouter.ai/api/v1", 12 | api_key=self.api_key,) 13 | 14 | self.aclient = AsyncOpenAI(base_url="https://openrouter.ai/api/v1", 15 | api_key=self.api_key,) 16 | 17 | def generate(self, prompt: str, system_prompt: str = "You are a helpful AI assistant.", temperature: float = 0.5): 18 | return super().generate(prompt, system_prompt, temperature) 19 | 20 | async def agenerate(self, prompt: str, system_prompt: str = "You are a helpful AI assistant.", temperature: float = 0.2): 21 | try: 22 | response = await self.aclient.chat.completions.create( 23 | model=self.model, 24 | messages=[ 25 | {"role": "system", "content": system_prompt}, 26 | {"role": "user", "content": prompt} 27 | ], 28 | temperature=temperature,) 29 | # print(response.choices[0].message.content) # For Debuging 30 | return response.choices[0].message.content 31 | except: 32 | return 'error' -------------------------------------------------------------------------------- /src/generators/vllm.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from os import getenv 3 | 4 | from openai import OpenAI, AsyncOpenAI 5 | from .openai import OpenAIGenerator 6 | 7 | class VLLMGenerator(OpenAIGenerator): 8 | def __init__(self, model: str = "deepseek/deepseek-chat", base_url: str = 'http://localhost:8000/v1') -> None: 9 | self.model = model 10 | # self.api_key = api_key if api_key else 'test-abc1' 11 | self.client = OpenAI(base_url=base_url, api_key='test-abc1') 12 | 13 | self.aclient = AsyncOpenAI(base_url=base_url, api_key='test-abc1') 14 | 15 | def generate(self, prompt: str, system_prompt: str = "You are a helpful AI assistant.", temperature: float = 0.5): 16 | return super().generate(prompt, system_prompt, temperature) 17 | 18 | async def agenerate(self, prompt: str, system_prompt: str = "You are a helpful AI assistant.", temperature: float = 0.5): 19 | response = await self.aclient.chat.completions.create( 20 | model=self.model, 21 | messages=[ 22 | {"role": "system", "content": system_prompt}, 23 | {"role": "user", "content": prompt} 24 | ], 25 | temperature=temperature,) 26 | # print(response.choices[0].message.content) # For Debuging 27 | return response.choices[0].message.content -------------------------------------------------------------------------------- /src/optimizers/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_optimizer import BaseOptimizer 2 | from .evol_optimizer import EvolOptimizer -------------------------------------------------------------------------------- /src/optimizers/base_optimizer.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class BaseOptimizer(ABC): 4 | @abstractmethod 5 | def optimize(self, current_method, feedback): 6 | pass -------------------------------------------------------------------------------- /src/optimizers/evol_optimizer.py: -------------------------------------------------------------------------------- 1 | from .base_optimizer import BaseOptimizer 2 | from src.evolvers import RecurrentEvolver 3 | from src.evaluator import BaseEvaluator 4 | from src.generators import BaseGenerator 5 | from src.utils import parse_steps 6 | 7 | from typing import List, Optional 8 | import asyncio 9 | 10 | METHOD_EVOL_PROMPT = """ 11 | Feedback: {feedback} 12 | You are an Instruction Method Optimizer. Based on the feedback from the evolution failure case, optimize the method below to create a more effective instruction rewriting process without negatively impacting performance on other cases. Ensure that the complexity of the optimized method is not lower than the previous method. 13 | If the feedback is "### PASSED", then come up with a better method than the current one to create a more complex and effective instruction rewriting process. Remember that the new method should not be very similar to the current method, be creative with new steps for the new method. 14 | 15 | Current Method: 16 | {current_method} 17 | 18 | **Output Instructions** 19 | Add more steps to achieve the most refined method if needed, however, REMEMBER that the final step in your output has to be "#Finally Rewritten Instruction#" no matter how many steps are added. 20 | Please generate the optimized method strictly using ONLY the given below format, do not add anything else: 21 | 22 | ```Optimized Method 23 | Step 1: 24 | #Methods List# 25 | Describe how to generate a list of methods to make instructions more complex, incorporating the feedback 26 | 27 | Step 2: 28 | #Plan# 29 | Explain how to create a comprehensive plan based on the Methods List 30 | 31 | [Note]Add more steps here as you want to achieve the best method. The steps should align with the instruction domain/topic, and should not involve any tools or visualization, it should be text-only methods. The last step should always be #Finally Rewritten Instruction#. 32 | 33 | Step N-1: 34 | #Rewritten Instruction# 35 | Do not generate new Instruction here, but please provide a detailed the process of executing the plan to rewrite the instruction. You are generating a guide to write a better instruction, NOT THE INSTRUCTION ITSELF. 36 | 37 | Step N: 38 | #Finally Rewritten Instruction# 39 | Do not generate new Instruction here, but please provide the process to write the final rewritten instruction. You are generating a guide to write a better instruction, NOT THE INSTRUCTION ITSELF. 40 | ``` 41 | """ 42 | 43 | class EvolOptimizer(BaseOptimizer): 44 | def __init__(self, generator: BaseGenerator, evaluator: BaseEvaluator) -> None: 45 | self.generator = generator 46 | self.evaluator = evaluator 47 | 48 | async def optimize(self, current_method: str, feedback: List[str], evolver: RecurrentEvolver, development_set: Optional[List] = None): 49 | async def generate_and_evaluate(feedback_item): 50 | optimized_prompt = METHOD_EVOL_PROMPT.format(current_method=current_method, feedback=feedback_item) 51 | evolved_method = await self.generator.agenerate(optimized_prompt, temperature=0.5) 52 | 53 | async def process_instruction(instruction): 54 | async def generate_with_timeout(prompt, temperature): 55 | try: 56 | return await asyncio.wait_for( 57 | self.generator.agenerate(prompt=prompt, temperature=temperature), 58 | timeout=60.0 # 60 seconds timeout 59 | ) 60 | except asyncio.TimeoutError: 61 | return None 62 | try: 63 | parsed_steps = parse_steps(evolved_method) 64 | new_method = evolver.build_new_method(parsed_steps, instruction) 65 | 66 | evolved_instruction = await generate_with_timeout(new_method, 0.2) 67 | if evolved_instruction is None: 68 | # print('bad') 69 | return instruction, "error response" 70 | 71 | try: 72 | parsed_evolved_instruction = parse_steps(evolved_instruction)[-1]['step_instruction'] 73 | except: 74 | fallback_response = await generate_with_timeout(instruction, 0.5) 75 | if fallback_response is None: 76 | # print('bad') 77 | return instruction, "error response" 78 | return instruction, fallback_response 79 | response = await generate_with_timeout(parsed_evolved_instruction, 0.5) 80 | if response is None: 81 | # print('bad') 82 | return instruction, "error response" 83 | 84 | # print('good') 85 | return parsed_evolved_instruction, response 86 | except: 87 | # print('bad') 88 | return instruction, "error response" 89 | 90 | 91 | results = await asyncio.gather(*[process_instruction(instruction) for instruction in development_set]) 92 | evolved_instructions, responses = zip(*results) 93 | 94 | return evolved_method, list(evolved_instructions), list(responses) 95 | 96 | results = await asyncio.gather(*[generate_and_evaluate(item) for item in feedback]) 97 | evolved_methods, all_evolved_instructions, all_responses = zip(*results) 98 | 99 | best_method, best_score = await self.evaluator.select_best_method( 100 | evolved_methods, 101 | [instr for method_instructions in all_evolved_instructions for instr in method_instructions], 102 | [resp for method_responses in all_responses for resp in method_responses] 103 | ) 104 | 105 | return best_method, list(evolved_methods) -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def parse_sections(string_example): 4 | # Use regular expressions to find sections 5 | pattern = re.compile(r"#.*?#:") 6 | matches = list(pattern.finditer(string_example)) 7 | sections = [] 8 | for i in range(len(matches)): 9 | start = matches[i].end() 10 | end = matches[i+1].start() if i+1 < len(matches) else len(string_example) 11 | section = string_example[start:end].strip() 12 | 13 | # Cut off text before the next "/nStep" if it exists 14 | step_cut = re.search(r'\nStep', section) 15 | if step_cut: 16 | section = section[:step_cut.start()] 17 | 18 | sections.append(section.strip()) 19 | 20 | return sections 21 | 22 | # def parse_steps(example_string): 23 | # # Regular expression to match step instructions 24 | # step_regex = re.compile(r"Step \d+: #([^#]+)#\n([^\n]+(?:\n-(?!Step)[^\n]+)*)", re.MULTILINE) 25 | 26 | # steps_list = [] 27 | # for match in step_regex.finditer(example_string): 28 | # step_dict = { 29 | # "step_name": match.group(1).strip(), 30 | # "step_instruction": match.group(2).strip() 31 | # } 32 | # steps_list.append(step_dict) 33 | 34 | # return steps_list 35 | 36 | def parse_steps(example_string): 37 | # Extract content inside the first pair of triple backticks 38 | content_match = re.search(r'```(.*)```', example_string, re.DOTALL) 39 | if content_match: 40 | example_string = content_match.group(1).strip() 41 | 42 | # Regular expression to match step instructions 43 | step_regex = re.compile(r"Step (\d+):\s*(?:#([^#]+)#)?\s*(.*?)(?=Step \d+:|$)", re.DOTALL) 44 | 45 | steps_list = [] 46 | for match in step_regex.finditer(example_string): 47 | step_number = int(match.group(1)) 48 | step_name = match.group(2).strip() if match.group(2) else "" 49 | step_instruction = match.group(3).strip() 50 | 51 | step_dict = { 52 | "step_number": step_number, 53 | "step_name": step_name, 54 | "step_instruction": step_instruction 55 | } 56 | steps_list.append(step_dict) 57 | 58 | return steps_list -------------------------------------------------------------------------------- /tests/analyzers/test_trajectory.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from src.generators.openai import OpenAIGenerator 3 | from src.generators.openrouter import OpenRouterGenerator 4 | from src.analyzers.trajectory_analyzer import TrajectoryAnalyzer 5 | 6 | # class TestTrajectoryAnalyzer(unittest.TestCase): 7 | def test_analyzer(): 8 | generator = OpenRouterGenerator(model='deepseek/deepseek-chat') 9 | analyzer = TrajectoryAnalyzer(generator=generator) 10 | result = analyzer.analyze("x + 2 = 12, what is x?", ["What is x in the case of 40x^2 - 5 = 40?"]) 11 | assert result is not None 12 | assert len(result) > 0 -------------------------------------------------------------------------------- /tests/evolvers/test_recurrent.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from src.generators import OpenAIGenerator, OpenRouterGenerator 3 | from src.evolvers.recurrent_evolver import RecurrentEvolver 4 | from src.utils import parse_steps 5 | 6 | def test_recurrent_evolver(): 7 | generator = OpenRouterGenerator(model='deepseek/deepseek-chat') 8 | evolver = RecurrentEvolver(generator=generator) 9 | results = evolver.evolve(instruction="What is the sum of 2 and 2, and please provide a detailed explanation of how you arrived at the answer in the form of a mathematical equation? Furthermore, imagine you have 2 apples and receive 2 more, how many apples do you have now?", n=2) 10 | parsed_results = [] 11 | for result in results: 12 | parsed_results.append(parse_steps(result)) 13 | 14 | # print(parsed_results) 15 | 16 | assert len(parsed_results) == 2 17 | assert parsed_results[0][-1]['step_name'] == "Finally Rewritten Instruction", f"Unexpected final step name for result 1: {parsed_results[0][-1]['step_name']}" 18 | assert parsed_results[1][-1]['step_name'] == "Finally Rewritten Instruction", f"Unexpected final step name for result 2: {parsed_results[1][-1]['step_name']}" 19 | -------------------------------------------------------------------------------- /tests/generators/test_openai.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from src.generators.openai import OpenAIGenerator 3 | 4 | def test_generate(): 5 | generator = OpenAIGenerator() 6 | result = generator.generate("Test prompt") 7 | assert result is not None 8 | assert len(result) > 0 -------------------------------------------------------------------------------- /tests/generators/test_openrouter.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import pytest 3 | from src.generators.openrouter import OpenRouterGenerator 4 | 5 | def test_generate(): 6 | generator = OpenRouterGenerator() 7 | result = generator.generate("Test prompt") 8 | assert result is not None 9 | assert len(result) > 0 -------------------------------------------------------------------------------- /tests/optimizers/test_wizard.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from src.generators import OpenRouterGenerator 3 | from src.optimizers.evol_optimizer import EvolOptimizer 4 | from src.analyzers.trajectory_analyzer import TrajectoryAnalyzer 5 | from src.evolvers.recurrent_evolver import RecurrentEvolver, INITIAL_EVOLVE_METHOD 6 | from src.evaluator.failure_detector_evaluator import FailureDetectorEvaluator 7 | from src.utils import parse_steps 8 | 9 | dev_set = ['Write a python function to perform bubble sort', 'Write a letter to my headmaster asking for a day off.'] 10 | 11 | @pytest.mark.asyncio 12 | async def test_wizard_optimizer(): 13 | generator = OpenRouterGenerator(model='openai/chatgpt-4o-latest') 14 | evolver = RecurrentEvolver(generator) 15 | analyzer = TrajectoryAnalyzer(generator) 16 | detector = FailureDetectorEvaluator() 17 | optimizer = EvolOptimizer(generator, detector) 18 | 19 | init_instruction = "What is the sum of 2 and 2, and please provide a detailed explanation of how you arrived at the answer in the form of a mathematical equation? Furthermore, imagine you have 2 apples and receive 2 more, how many apples do you have now?" 20 | 21 | # Assuming evolver.evolve and analyzer.analyze are also async 22 | evolved_instructions = evolver.evolve(init_instruction, INITIAL_EVOLVE_METHOD, n=5) 23 | 24 | 25 | feedbacks = analyzer.analyze(INITIAL_EVOLVE_METHOD, evolved_instructions) 26 | 27 | optimized_method, methods = await optimizer.optimize(INITIAL_EVOLVE_METHOD.format(instruction=init_instruction), feedback=feedbacks, evolver=evolver, development_set=dev_set) -------------------------------------------------------------------------------- /tests/test_full_flow.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import List 3 | from src.generators import OpenRouterGenerator 4 | from src.optimizers.evol_optimizer import EvolOptimizer 5 | from src.analyzers.trajectory_analyzer import TrajectoryAnalyzer 6 | from src.evolvers.recurrent_evolver import RecurrentEvolver, INITIAL_EVOLVE_METHOD 7 | from src.evaluator.failure_detector_evaluator import FailureDetectorEvaluator 8 | from src.utils import parse_steps 9 | 10 | async def process_instruction(instruction: str, components: dict) -> List[str]: 11 | instruction_stages = [instruction] 12 | current_method = INITIAL_EVOLVE_METHOD.format(instruction=instruction_stages[0]) 13 | 14 | for i in range(2): 15 | evolved_instructions = components['evolver'].evolve(instruction_stages[-1], current_method, n=5) 16 | feedbacks = components['analyzer'].analyze(instruction_stages[-1], evolved_instructions) 17 | 18 | optimized_method, _ = await components['optimizer'].optimize( 19 | current_method, 20 | feedback=feedbacks, 21 | evolver=components['evolver'], 22 | development_set=components['dev_set'] 23 | ) 24 | 25 | optimized_method_steps = parse_steps(optimized_method) 26 | optimized_method = components['evolver'].build_new_method(optimized_method_steps, instruction_stages[-1]) 27 | 28 | evolved_instruction = await components['generator'].agenerate(prompt=optimized_method, temperature=0.2) 29 | evolved_instruction_steps = parse_steps(evolved_instruction) 30 | 31 | if evolved_instruction_steps[-1]['step_name'] == 'Finally Rewritten Instruction': 32 | evolved_instruction = evolved_instruction_steps[-1]['step_instruction'] 33 | instruction_stages.append(evolved_instruction) 34 | current_method = components['evolver'].build_new_method(optimized_method_steps, evolved_instruction) 35 | else: 36 | print(evolved_instruction) 37 | print('Error: Unexpected step name in evolved instruction') 38 | 39 | return instruction_stages 40 | 41 | async def process_dataset(dataset: List[str], dev_set: List[str]) -> List[List[str]]: 42 | print(f"Starting dataset processing. Dataset size: {len(dataset)}") 43 | 44 | components = { 45 | 'generator': OpenRouterGenerator(model='openai/gpt-4o'), 46 | 'evolver': RecurrentEvolver(OpenRouterGenerator(model='openai/gpt-4o')), 47 | 'analyzer': TrajectoryAnalyzer(OpenRouterGenerator(model='openai/gpt-4o')), 48 | 'detector': FailureDetectorEvaluator(), 49 | 'dev_set': dev_set 50 | } 51 | components['optimizer'] = EvolOptimizer(components['generator'], components['detector']) 52 | 53 | print("Initialized all components") 54 | 55 | async def process_batch(batch: List[str]) -> List[List[str]]: 56 | return await asyncio.gather(*[process_instruction(instruction, components) for instruction in batch]) 57 | 58 | batch_size = 10 59 | results = [] 60 | for i in range(0, len(dataset), batch_size): 61 | batch = dataset[i:i+batch_size] 62 | print(f"Processing batch {i//batch_size + 1}") 63 | results.extend(await process_batch(batch)) 64 | 65 | print("Dataset processing complete") 66 | return results 67 | 68 | import pytest 69 | 70 | @pytest.mark.asyncio 71 | async def test_process_dataset(): 72 | print("Starting test_process_dataset") 73 | dataset = [ 74 | "Write a function to calculate the factorial of a number", 75 | "Explain the concept of recursion in programming", 76 | "Design a simple to-do list application", 77 | "Describe the process of photosynthesis", 78 | "Write a short story about a time traveler", 79 | "Explain the theory of relativity", 80 | "Create a recipe for a three-course meal", 81 | "Write a short story about a time traveler", 82 | "Explain the theory of relativity", 83 | "Create a recipe for a three-course meal" 84 | ] 85 | dev_set = [ 86 | 'Write a python function to perform bubble sort', 87 | 'Write a letter to my headmaster asking for a day off.', 88 | 'Write a python function to perform bubble sort', 89 | 'Write a letter to my headmaster asking for a day off.', 90 | 'Write a python function to perform bubble sort', 91 | ] 92 | 93 | print(f"Dataset size: {len(dataset)}, Dev set size: {len(dev_set)}") 94 | 95 | evolved_dataset = await process_dataset(dataset, dev_set) 96 | 97 | print("Dataset processing complete. Running assertions.") 98 | 99 | assert len(evolved_dataset) == len(dataset) 100 | for i, evolved_instructions in enumerate(evolved_dataset): 101 | print(f"Checking evolved instructions for dataset item {i+1}") 102 | assert len(evolved_instructions) == 3 # Original + 2 evolutions 103 | assert evolved_instructions[0] in dataset 104 | 105 | for j in range(1, len(evolved_instructions)): 106 | assert evolved_instructions[j] != evolved_instructions[j-1] 107 | print(f"All checks passed for dataset item {i+1}") 108 | 109 | for i in range(len(evolved_dataset)): 110 | for j in range(i+1, len(evolved_dataset)): 111 | assert evolved_dataset[i] != evolved_dataset[j] 112 | 113 | print("All assertions passed. Test complete.") 114 | 115 | if __name__ == "__main__": 116 | pytest.main([__file__]) -------------------------------------------------------------------------------- /visualize_evol_instruct.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import json\n", 10 | "import pandas as pd\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "from IPython.display import display, HTML\n", 13 | "\n", 14 | "def analyze_single_sample(sample):\n", 15 | " # Display instruction evolution\n", 16 | " def display_instruction_evolution(instruction_data):\n", 17 | " html = f\"

Original Instruction: {instruction_data['original_instruction']}

\"\n", 18 | " html += \"\"\n", 19 | " \n", 20 | " for stage in instruction_data['stages']:\n", 21 | " html += f\"\"\n", 22 | " \n", 23 | " html += f\"\"\n", 24 | " html += \"
StageInput InstructionFinal Evolved Instruction
{stage['stage']}{stage['input_instruction']}{stage['final_evolved_instruction']}
Final{instruction_data['final_instruction']}
\"\n", 25 | " \n", 26 | " display(HTML(html))\n", 27 | " \n", 28 | " display_instruction_evolution(sample)\n", 29 | " \n", 30 | " # Word count analysis\n", 31 | " def word_count(text):\n", 32 | " return len(text.split())\n", 33 | " \n", 34 | " stage_word_counts = {0: word_count(sample['original_instruction'])}\n", 35 | " for i, stage in enumerate(sample['stages']):\n", 36 | " stage_word_counts[i+1] = word_count(stage['final_evolved_instruction'])\n", 37 | " # stage_word_counts[len(sample['stages'])] = word_count(sample['final_instruction'])\n", 38 | " \n", 39 | " plt.figure(figsize=(10, 6))\n", 40 | " plt.plot(list(stage_word_counts.keys()), list(stage_word_counts.values()), marker='o')\n", 41 | " plt.title('Word Count of Instructions by Stage')\n", 42 | " plt.xlabel('Stage (0: Original, 0 to N-1: Intermediate, N: Final)')\n", 43 | " plt.ylabel('Word Count')\n", 44 | " plt.xticks(range(0, len(sample['stages']) + 1))\n", 45 | " plt.grid(True)\n", 46 | " plt.show()\n", 47 | " \n", 48 | " # Display methods used in each stage\n", 49 | " def display_methods(instruction_data):\n", 50 | " html = \"

Methods Used in Each Stage

\"\n", 51 | " html += \"\"\n", 52 | " \n", 53 | " for stage in instruction_data['stages']:\n", 54 | " html += f\"\"\n", 55 | " \n", 56 | " html += \"
StageMethod
{stage['stage']}{stage['optimized_method'][:500]}...
\"\n", 57 | " \n", 58 | " display(HTML(html))\n", 59 | " \n", 60 | " display_methods(sample)\n", 61 | " \n", 62 | " # Display statistics\n", 63 | " num_stages = len(sample['stages'])\n", 64 | " print(f\"Number of stages: {num_stages}\")\n", 65 | " \n", 66 | " # Display evolved instructions and feedbacks\n", 67 | " for i, stage in enumerate(sample['stages']):\n", 68 | " print(f\"\\nStage {i + 1}\")\n", 69 | " print(\"Evolved Instructions:\")\n", 70 | " for j, instruction in enumerate(stage['evolved_instructions']):\n", 71 | " print(f\" {j + 1}. {instruction}\")\n", 72 | " print(\"\\nFeedbacks:\")\n", 73 | " for j, feedback in enumerate(stage['feedbacks']):\n", 74 | " print(f\" {j + 1}. {feedback}\")\n", 75 | "\n", 76 | " # Display final instruction\n", 77 | " print(f\"\\nFinal Instruction: {sample['final_instruction']}\")" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 4, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "# change your path\n", 87 | "\n", 88 | "evolved_instruction_path = 'the_tomb_evolved-3e_batch1.json'\n", 89 | "\n", 90 | "with open(evolved_instruction_path, 'r') as f:\n", 91 | " data = json.load(f)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 7, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "data": { 101 | "text/html": [ 102 | "

Original Instruction: Please verify my approach to solving the following problem: In triangle ABC, there are 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side AC. How many different quadrilaterals can be formed by selecting four of these points? I considered two cases: (1) two collinear points on one side and two points on different sides, and (2) two points on one side and two points on another side. My calculation is as follows:\n", 103 | "\n", 104 | "$$\n", 105 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 106 | "$$\n", 107 | "\n", 108 | "Is this method and the resulting expression accurate?

StageInput InstructionFinal Evolved Instruction
1Please verify my approach to solving the following problem: In triangle ABC, there are 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side AC. How many different quadrilaterals can be formed by selecting four of these points? I considered two cases: (1) two collinear points on one side and two points on different sides, and (2) two points on one side and two points on another side. My calculation is as follows:\n", 109 | "\n", 110 | "$$\n", 111 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 112 | "$$\n", 113 | "\n", 114 | "Is this method and the resulting expression accurate?```Optimized Instruction\n", 115 | "Step 1:\n", 116 | "#Methods List# Incorporate hypothetical scenarios involving different triangle configurations; ask for evaluation of alternative calculation methods; require explanation of each step in the solution, including assumptions and reasoning; identify potential errors in the given approach.\n", 117 | "\n", 118 | "Step 2:\n", 119 | "#Plan# Rewrite the instruction to consider a hypothetical scenario where the triangle's sides have different numbers of points; ask for an evaluation of the given solution compared to alternative methods; require a detailed explanation of the reasoning behind each step, including any assumptions made; identify and justify any potential errors in the calculation.\n", 120 | "\n", 121 | "Step 3:\n", 122 | "#Rewritten Instruction Process# In a hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution against alternative methods, explain the rationale behind each step, and identify any potential errors.\n", 123 | "\n", 124 | "Step 4:\n", 125 | "#Review Process# Ensure the rewritten instruction includes the hypothetical scenario, evaluation of alternative methods, detailed explanation of reasoning, and identification of potential errors. Adjust as necessary to maintain clarity and answerability while increasing complexity.\n", 126 | "\n", 127 | "Step 5:\n", 128 | "#Finally Rewritten Instruction Process# In a hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution against alternative methods, explain the rationale behind each step, and identify any potential errors in the calculation. Is the given method and resulting expression accurate, considering the hypothetical scenario and alternative approaches?\n", 129 | "```
2```Optimized Instruction\n", 130 | "Step 1:\n", 131 | "#Methods List# Incorporate hypothetical scenarios involving different triangle configurations; ask for evaluation of alternative calculation methods; require explanation of each step in the solution, including assumptions and reasoning; identify potential errors in the given approach.\n", 132 | "\n", 133 | "Step 2:\n", 134 | "#Plan# Rewrite the instruction to consider a hypothetical scenario where the triangle's sides have different numbers of points; ask for an evaluation of the given solution compared to alternative methods; require a detailed explanation of the reasoning behind each step, including any assumptions made; identify and justify any potential errors in the calculation.\n", 135 | "\n", 136 | "Step 3:\n", 137 | "#Rewritten Instruction Process# In a hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution against alternative methods, explain the rationale behind each step, and identify any potential errors.\n", 138 | "\n", 139 | "Step 4:\n", 140 | "#Review Process# Ensure the rewritten instruction includes the hypothetical scenario, evaluation of alternative methods, detailed explanation of reasoning, and identification of potential errors. Adjust as necessary to maintain clarity and answerability while increasing complexity.\n", 141 | "\n", 142 | "Step 5:\n", 143 | "#Finally Rewritten Instruction Process# In a hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution against alternative methods, explain the rationale behind each step, and identify any potential errors in the calculation. Is the given method and resulting expression accurate, considering the hypothetical scenario and alternative approaches?\n", 144 | "```In a multi-level hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, with points that can move along the sides, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution and alternative methods, including their advantages and disadvantages, explain the rationale behind each step, and identify any potential errors in the calculation. Reflect on the decision-making process and predict possible challenges in the calculation, proposing solutions to overcome them. Consider the implications of moving points and the robustness of your solution against potential errors.
3In a multi-level hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, with points that can move along the sides, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution and alternative methods, including their advantages and disadvantages, explain the rationale behind each step, and identify any potential errors in the calculation. Reflect on the decision-making process and predict possible challenges in the calculation, proposing solutions to overcome them. Consider the implications of moving points and the robustness of your solution against potential errors.In a multi-level hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, with points that can move along the sides, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution and alternative methods, including their advantages and disadvantages, explain the rationale behind each step, and identify any potential errors in the calculation. Reflect on the decision-making process and predict possible challenges in the calculation, proposing solutions to overcome them. Consider the implications of moving points and the robustness of your solution against potential errors, while also contemplating the adaptability of your strategy based on previous errors in similar problems.
FinalIn a multi-level hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, with points that can move along the sides, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution and alternative methods, including their advantages and disadvantages, explain the rationale behind each step, and identify any potential errors in the calculation. Reflect on the decision-making process and predict possible challenges in the calculation, proposing solutions to overcome them. Consider the implications of moving points and the robustness of your solution against potential errors, while also contemplating the adaptability of your strategy based on previous errors in similar problems.
" 145 | ], 146 | "text/plain": [ 147 | "" 148 | ] 149 | }, 150 | "metadata": {}, 151 | "output_type": "display_data" 152 | }, 153 | { 154 | "data": { 155 | "image/png": "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/fr1WLFiBdzc3HDo0CHMnj27Sc94VbOyssLmzZuxY8cOuLq6IiYmBsuWLWt0PZlM1uCzb+rsw9pGjhyJgIAADBkyBFZWVvj2228hk8mwb98+tGnTBt7e3vDx8cEbb7yB7777TqP3aWFhgY8//hgVFRWNLrt//3706dMH77zzDgBgzJgx6NOnj9KX/4KCgmZ/hqxDhw5YuHAhoqKiYGNjIyWdixYtwvz587FkyRJ069YNAQEBOHDgADp16tSs2weA9u3b49SpU6isrISfnx969uyJWbNmoXXr1lKytHTpUrz11lsIDAyEj48PBg4cKFWKVXFzc8OKFSvw6aefokePHti6dWudaqyXlxemTJmC0aNHw8rKCp999hmAZyN8hoSEYO7cuejSpQuCgoKQmpoqVcOAxo9FVSIjI1XeHpiTk6NWRai5bNy4EXfv3oW7uzsmTJiAGTNmwNrausF19u3bB3t7+2at0hGR+mRCk6efiYhaoF9//RV2dnbSQ+y/R3BwMNzd3TFv3rxmiu7FmTRpEi5fvvxCh0LOzc2Fi4sLLl26hM6dO7+w7RLV9joei3/4wx8wY8YMfPDBB9oOhei1xGekiOilc+zYMSgUCvTs2RMFBQX46KOP4OjoCG9v79/d99KlS/HDDz80Q5TP37Jly+Dr6wsTExMcPHgQsbGxWLdu3QuNIS4uDpMnT35tvrhSy/W6HYvFxcX44x//iLFjx2o7FKLXFitSRPTSOXz4MObOnYvr169DLpdLP+Lp4OCg7dBeqFGjRiEpKQllZWV44403MH36dEyZMkXbYREREb0WmEgRERERERFpiINNEBERERERaYiJFBERERERkYaYSBEREREREWmIiRQREREREZGGmEgRERERERFpiIkUERERERGRhphIERERERERaYiJFBERERERkYb+D6ohpZicbYvTAAAAAElFTkSuQmCC", 156 | "text/plain": [ 157 | "
" 158 | ] 159 | }, 160 | "metadata": {}, 161 | "output_type": "display_data" 162 | }, 163 | { 164 | "data": { 165 | "text/html": [ 166 | "

Methods Used in Each Stage

StageMethod
1\n", 167 | "You are an Instruction Rewriter that rewrites the given #Instruction# into a more complex version.\n", 168 | "Please follow the steps below to rewrite the given \"#Instruction#\" into a more complex version.\n", 169 | "\n", 170 | "Step 1: To generate a list of methods to make instructions more complex, consider incorporating elements that challenge AI's understanding by introducing hypothetical scenarios, asking for the evaluation of multiple solutions, and requiring the explanation of steps taken. Additionally, include methods ...
2\n", 171 | "You are an Instruction Rewriter that rewrites the given #Instruction# into a more complex version.\n", 172 | "Please follow the steps below to rewrite the given \"#Instruction#\" into a more complex version.\n", 173 | "\n", 174 | "Step 1: To generate a list of methods to make instructions more complex, consider incorporating elements such as: \n", 175 | "1.1. Introducing multi-level hypothetical scenarios that require the AI to consider various possible outcomes and their implications.\n", 176 | "1.2. Asking for the evaluation of multiple solutions, ...
3\n", 177 | "You are an Instruction Rewriter that rewrites the given #Instruction# into a more complex version.\n", 178 | "Please follow the steps below to rewrite the given \"#Instruction#\" into a more complex version.\n", 179 | "\n", 180 | "Step 1: To create a list of methods for enhancing instruction complexity, consider integrating:\n", 181 | "1.1. Nested hypothetical scenarios that explore the implications of various outcomes on multiple levels.\n", 182 | "1.2. Comparative analysis of solutions, requiring the evaluation of their relative effectiveness in di...
" 183 | ], 184 | "text/plain": [ 185 | "" 186 | ] 187 | }, 188 | "metadata": {}, 189 | "output_type": "display_data" 190 | }, 191 | { 192 | "name": "stdout", 193 | "output_type": "stream", 194 | "text": [ 195 | "Number of stages: 3\n", 196 | "\n", 197 | "Stage 1\n", 198 | "Evolved Instructions:\n", 199 | " 1. ```Optimized Instruction\n", 200 | "Step 1:\n", 201 | "#Methods List# - Incorporate mathematical notations for clarity. - Request a detailed step-by-step explanation of the verification process. - Ask for confirmation on the correct usage of combinatorial formulas. - Inquire about potential alternative approaches to solving the problem.\n", 202 | "\n", 203 | "Step 2:\n", 204 | "#Plan# - Add mathematical notations to clarify the combinatorial expressions. - Request a detailed, sequential verification of the solution's logic and calculations. - Seek confirmation on the proper application of combinatorial formulas. - Encourage consideration of alternative problem-solving strategies.\n", 205 | "\n", 206 | "Step 3:\n", 207 | "#Rewritten Instruction# - Please meticulously verify my approach to solving the following problem, employing combinatorial formulas: In triangle \\(ABC\\), there are 3 distinct points on side \\(AB\\), 4 distinct points on side \\(BC\\), and 5 distinct points on side \\(AC\\). How many different quadrilaterals can be formed by selecting four of these points? I considered two cases: (1) two collinear points on one side and two points on different sides, and (2) two points on one side and two points on another side. My calculation is as follows:\n", 208 | "\\[\n", 209 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 210 | "\\]\n", 211 | "- Is this method and the resulting expression accurate? - Could you provide a detailed, step-by-step verification of the logic and calculations? - Could you confirm the correct usage of combinatorial formulas? - Are there alternative approaches to solving this problem worth considering?\n", 212 | "\n", 213 | "Step 4:\n", 214 | "#Finally Rewritten Instruction# Please meticulously verify my approach to solving the problem using combinatorial formulas: In triangle \\(ABC\\), how many quadrilaterals can be formed by selecting four points from 3 on side \\(AB\\), 4 on side \\(BC\\), and 5 on side \\(AC\\)? I considered cases (1) and (2), and calculated:\n", 215 | "\\[\n", 216 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 217 | "\\]\n", 218 | "Is this method and expression accurate? Could you confirm the correct usage of combinatorial formulas and consider alternative problem-solving strategies?\n", 219 | "```\n", 220 | " 2. ```Optimized Instruction\n", 221 | "Step 1:\n", 222 | "#Methods List#\n", 223 | "1. Incorporate technical terms related to combinatorics and geometry.\n", 224 | "2. Request for a detailed explanation of each step in the solution.\n", 225 | "3. Ask for a generalization of the problem to n points on each side.\n", 226 | "4. Suggest an alternative method to verify the solution, such as a graphical representation.\n", 227 | "5. Inquire about the conditions under which the solution would not hold.\n", 228 | "\n", 229 | "Step 2:\n", 230 | "#Plan#\n", 231 | "1. Add a request for the use of combinatorial identities in the explanation.\n", 232 | "2. Ask for a graphical representation of the quadrilaterals formed.\n", 233 | "3. Generalize the problem to n points on each side and ask for a formula.\n", 234 | "\n", 235 | "Step 3:\n", 236 | "#Rewritten Instruction#\n", 237 | "Please verify my approach to solving the following problem: In triangle ABC, there are 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side AC. How many different quadrilaterals can be formed by selecting four of these points? I considered two cases: (1) two collinear points on one side and two points on different sides, and (2) two points on one side and two points on another side. My calculation is as follows:\n", 238 | "$$\n", 239 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 240 | "$$\n", 241 | "Is this method and the resulting expression accurate? Please provide a detailed explanation using combinatorial identities, and also include a graphical representation of the quadrilaterals formed. Furthermore, could you generalize this problem to n points on each side and provide a formula for the number of quadrilaterals that can be formed?\n", 242 | "\n", 243 | "Step 4:\n", 244 | "#Finally Rewritten Instruction#\n", 245 | "Please rigorously verify my approach to solving the following problem: In triangle ABC, there are 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side AC. How many different quadrilaterals can be formed by selecting four of these points? I considered two cases: (1) two collinear points on one side and two points on different sides, and (2) two points on one side and two points on another side. My calculation is as follows:\n", 246 | "$$\n", 247 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 248 | "$$\n", 249 | "Is this method and the resulting expression accurate? Please provide a detailed explanation using combinatorial identities, and also include a graphical representation of the quadrilaterals formed. Additionally, could you generalize this problem to n points on each side?\n", 250 | "```\n", 251 | " 3. ```Optimized Instruction\n", 252 | "Step 1:\n", 253 | "#Methods List#\n", 254 | "1. Introduce additional constraints or conditions.\n", 255 | "2. Require the verification of the method's validity under certain conditions.\n", 256 | "3. Request a detailed explanation for each step of the method.\n", 257 | "4. Ask for alternative methods or approaches to solve the problem.\n", 258 | "5. Include a request for a proof or justification of the final answer.\n", 259 | "\n", 260 | "Step 2:\n", 261 | "#Plan#\n", 262 | "1. Add a condition that the quadrilaterals must not have any sides parallel to the triangle's sides.\n", 263 | "2. Request the verification of the method's validity when considering the additional condition.\n", 264 | "3. Ask for a detailed explanation of how the combination formula is applied in each term of the expression.\n", 265 | "4. Inquire about possible alternative methods to solve the problem, considering the new condition.\n", 266 | "5. Require a proof or justification that the final answer meets the given conditions and is mathematically sound.\n", 267 | "\n", 268 | "Step 3:\n", 269 | "#Rewritten Instruction#\n", 270 | "Please verify my approach to solving the following problem under the condition that no sides of the quadrilaterals are parallel to the triangle's sides: In triangle ABC, there are 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side AC. How many different quadrilaterals can be formed by selecting four of these points? I considered two cases: (1) two collinear points on one side and two points on different sides, and (2) two points on one side and two points on another side. My calculation is as follows:\n", 271 | "\n", 272 | "$$\n", 273 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 274 | "$$\n", 275 | "\n", 276 | "Is this method and the resulting expression accurate when considering the additional condition? Please provide a detailed explanation for each step of the method and suggest alternative methods if applicable. Additionally, please provide a proof or justification that the final answer meets the given conditions and is mathematically sound.\n", 277 | "\n", 278 | "Step 4:\n", 279 | "#Finally Rewritten Instruction#\n", 280 | "Please verify my approach to solving the following problem under the condition that no sides of the quadrilaterals are parallel to the triangle's sides: In triangle ABC, there are 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side AC. How many different quadrilaterals can be formed by selecting four of these points, ensuring no sides are parallel to the triangle's sides? I considered two cases: (1) two collinear points on one side and two points on different sides, and (2) two points on one side and two points on another side. My calculation is as follows:\n", 281 | "\n", 282 | "$$\n", 283 | "3 \\cdot 4 \\binom{5}{2} + 3 \\cdot 5 \\binom{4}{2} + 4 \\cdot 5 \\binom{3}{2} + \\binom{3}{2} \\binom{4}{2} + \\binom{3}{2} \\binom{5}{2} + \\binom{4}{2} \\binom{5}{2}\n", 284 | "$$\n", 285 | "\n", 286 | "Is this method and the resulting expression accurate when considering the additional condition? Please provide a detailed explanation for each step of the method and suggest alternative methods if applicable. Additionally, please provide a proof or justification that the final answer meets the given conditions and is mathematically sound.\n", 287 | "```\n", 288 | "\n", 289 | "Feedbacks:\n", 290 | " 1. ### PASSED\n", 291 | " 2. ### PASSED\n", 292 | " 3. ### PASSED\n", 293 | "\n", 294 | "Stage 2\n", 295 | "Evolved Instructions:\n", 296 | " 1. ```Optimized Instruction\n", 297 | "Step 1:\n", 298 | "#Methods List# Incorporate a scenario with a complex geometric figure; request evaluation of multiple calculation techniques; demand detailed reasoning for each step; seek identification of possible miscalculations.\n", 299 | "\n", 300 | "Step 2:\n", 301 | "#Plan# Design an instruction focusing on a scenario with a non-standard geometric shape; ask for comparison of the given solution with alternative calculation methods; require a thorough explanation of the logic behind each step; prompt for spotting and justifying potential calculation errors.\n", 302 | "\n", 303 | "Step 3:\n", 304 | "#Rewritten Instruction Process# Given a complex geometric shape with irregularly distributed points, how would you calculate the number of unique triangles that can be formed? Compare the provided solution to other calculation methods, explain the rationale for each step taken, and identify any potential miscalculations.\n", 305 | "\n", 306 | "Step 4:\n", 307 | "#Review Process# Ensure the instruction includes the complex scenario, comparison of methods, detailed rationale, and error identification. Adjust if necessary for clarity and answerability while increasing complexity.\n", 308 | "\n", 309 | "Step 5:\n", 310 | "#Finally Rewritten Instruction Process# Considering a complex, irregular geometric shape with unevenly distributed points, evaluate the method for calculating the number of unique triangles that can be formed. Compare this method to alternatives, thoroughly explain the logic for each step, and critically assess any potential sources of miscalculation. Is the chosen method the most efficient and accurate given the complexity of the shape and the distribution of points?\n", 311 | "```\n", 312 | " 2. ```Optimized Instruction\n", 313 | "Step 1:\n", 314 | "#Methods List# Incorporate a scenario with a complex geometric figure; ask for comparison of multiple calculation methods; require detailed explanation of each calculation step; identify possible errors in provided solutions.\n", 315 | "\n", 316 | "Step 2:\n", 317 | "#Plan# Rewrite the instruction to include a scenario with a complex geometric figure; ask for a comparison of the given solution with alternative calculation methods; require a detailed explanation of each calculation step; identify and justify possible errors in the provided solutions.\n", 318 | "\n", 319 | "Step 3:\n", 320 | "#Rewritten Instruction Process# Consider a scenario with a complex geometric figure composed of multiple shapes. How would you calculate the total area of this figure using the given solution? Compare this method with alternative calculation approaches, explain each step in detail, and identify any possible errors in the provided solution.\n", 321 | "\n", 322 | "Step 4:\n", 323 | "#Review Process# Ensure the rewritten instruction includes the scenario, comparison of methods, detailed explanation of steps, and identification of errors. Adjust if necessary to maintain clarity and answerability while increasing complexity.\n", 324 | "\n", 325 | "Step 5:\n", 326 | "#Finally Rewritten Instruction Process# In a scenario involving a complex geometric figure composed of multiple shapes, calculate the total area using the given solution. Compare this method with alternative approaches, explain each step in detail, and identify any possible errors in the provided solution. Is the given method efficient and accurate for the scenario, considering alternative calculation approaches?\n", 327 | "```\n", 328 | " 3. ```Optimized Instruction\n", 329 | "Step 1:\n", 330 | "#Methods List# Incorporate hypothetical scenarios involving different triangle configurations; ask for evaluation of alternative calculation methods; require explanation of each step in the solution, including assumptions and reasoning; identify potential errors in the given approach.\n", 331 | "\n", 332 | "Step 2:\n", 333 | "#Plan# Rewrite the instruction to consider a hypothetical scenario where the triangle's sides have varying numbers of points; ask for an evaluation of the given solution against alternative methods; require a detailed explanation of the reasoning behind each step, including any assumptions made; identify and justify any potential errors in the calculation.\n", 334 | "\n", 335 | "Step 3:\n", 336 | "#Rewritten Instruction Process# In a scenario where a triangle ABC has 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side CA, calculate the number of different triangles that can be formed by selecting three points. Evaluate the provided solution against alternative methods, explain the rationale behind each step, and identify any potential errors.\n", 337 | "\n", 338 | "Step 4:\n", 339 | "#Review Process# Ensure the rewritten instruction includes the scenario, evaluation of methods, detailed explanation, and error identification. Adjust as necessary to maintain clarity and answerability while increasing complexity.\n", 340 | "\n", 341 | "Step 5:\n", 342 | "#Finally Rewritten Instruction Process# In a scenario where a triangle ABC has 3 distinct points on side AB, 4 distinct points on side BC, and 5 distinct points on side CA, calculate the number of different triangles that can be formed by selecting three points. Evaluate the provided solution against alternative methods, explain the rationale behind each step, and identify any potential errors. Consider the accuracy of the method and the validity of the assumptions made in this complex scenario.\n", 343 | "```\n", 344 | "\n", 345 | "Feedbacks:\n", 346 | " 1. ### PASSED\n", 347 | " 2. ### PASSED\n", 348 | " 3. ### FAILED - Reason: The complexity did not increase from stage 0 to stage 1. In stage 0, the instruction involves calculating the number of different quadrilaterals that can be formed, while in stage 1, it is reduced to calculating the number of different triangles, which is less complex.\n", 349 | "\n", 350 | "Stage 3\n", 351 | "Evolved Instructions:\n", 352 | " 1. ```Optimized Instruction\n", 353 | "Step 1:\n", 354 | "#Methods List#\n", 355 | "1.1. Introduce multi-level hypothetical scenarios.\n", 356 | "1.2. Evaluate multiple solutions and their pros and cons.\n", 357 | "1.3. Explain steps and reasoning, identifying potential errors.\n", 358 | "1.4. Incorporate meta-cognitive questions.\n", 359 | "1.5. Predict challenges and propose solutions.\n", 360 | "\n", 361 | "Step 2:\n", 362 | "#Plan#\n", 363 | "2.1. Consider alternative calculation methods.\n", 364 | "2.2. Explain reasoning and identify errors.\n", 365 | "2.3. Reflect on decision-making process.\n", 366 | "2.4. Predict challenges in calculation.\n", 367 | "2.5. Ensure rewritten instruction is more complex.\n", 368 | "\n", 369 | "Step 3:\n", 370 | "#Elaborate on Methods#\n", 371 | "3.1. Design scenarios that challenge AI's outcome analysis.\n", 372 | "3.2. Provide detailed analysis of solution advantages and disadvantages.\n", 373 | "3.3. Explain reasoning and implications of potential errors.\n", 374 | "3.4. Encourage reflection on thought process and decision-making.\n", 375 | "3.5. Propose solutions to identified challenges.\n", 376 | "\n", 377 | "Step 4:\n", 378 | "#Execute the Plan#\n", 379 | "Rewrite instruction with multi-level scenarios, evaluate methods, explain steps, reflect, and predict challenges.\n", 380 | "\n", 381 | "Step 5:\n", 382 | "#Review the Rewritten Instruction#\n", 383 | "Ensure the rewritten instruction is significantly more complex, clear, and answerable.\n", 384 | "\n", 385 | "Step 6:\n", 386 | "#Finally Rewritten Instruction#\n", 387 | "In a multi-level hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, with points that can move along the sides, devise and evaluate multiple methods to calculate the number of different quadrilaterals that can be formed by selecting four points. Explain the rationale behind each step, identify potential errors, and reflect on the decision-making process. Predict possible challenges in the calculation due to moving points and propose robust solutions to overcome them, considering the implications on the solution's robustness.\n", 388 | "```\n", 389 | " 2. ```Optimized Instruction\n", 390 | "Step 1:\n", 391 | "#Methods List#\n", 392 | "1.1. Introducing multi-level hypothetical scenarios.\n", 393 | "1.2. Evaluating multiple solutions and their pros and cons.\n", 394 | "1.3. Explaining steps and identifying potential errors.\n", 395 | "1.4. Incorporating meta-cognitive reflection.\n", 396 | "1.5. Predicting challenges and proposing solutions.\n", 397 | "\n", 398 | "Step 2:\n", 399 | "#Plan#\n", 400 | "2.1. Challenge the AI to consider moving points and their implications.\n", 401 | "2.2. Ask for the evaluation of the provided solution and alternatives.\n", 402 | "2.3. Require detailed explanations and error identification.\n", 403 | "2.4. Encourage reflection on the decision-making process.\n", 404 | "2.5. Request prediction of challenges and solutions.\n", 405 | "\n", 406 | "Step 3:\n", 407 | "#Elaborate on Methods#\n", 408 | "3.1. Design scenarios that test understanding of moving points.\n", 409 | "3.2. Conduct a detailed analysis of solution effectiveness.\n", 410 | "3.3. Explain each decision and identify possible calculation errors.\n", 411 | "3.4. Reflect on the reasoning behind each choice.\n", 412 | "3.5. Propose solutions for potential calculation challenges.\n", 413 | "\n", 414 | "Step 4:\n", 415 | "#Execute the Plan#\n", 416 | "Rewrite the instruction to include multi-level scenarios, evaluate solutions, explain steps, reflect on decision-making, and predict challenges.\n", 417 | "\n", 418 | "Step 5:\n", 419 | "#Review the Rewritten Instruction#\n", 420 | "Ensure the rewritten instruction is complex, clear, and answerable, with all elements from the plan incorporated.\n", 421 | "\n", 422 | "Step 6:\n", 423 | "#Finally Rewritten Instruction#\n", 424 | "In a multi-level scenario with a moving-point triangle XYZ, calculate the number of possible quadrilaterals formed by selecting four points, considering robustness against errors. Evaluate the provided solution and alternatives, explaining each step and identifying potential errors. Reflect on your decision-making and predict challenges, proposing solutions. Consider the implications of point movement on the solution's robustness.\n", 425 | "```\n", 426 | " 3. ```Optimized Instruction\n", 427 | "Step 1:\n", 428 | "#Methods List#\n", 429 | "1.1. Introduce multi-level hypothetical scenarios involving moving points and side lengths.\n", 430 | "1.2. Evaluate multiple solutions for calculating quadrilaterals, considering their robustness and efficiency.\n", 431 | "1.3. Explain each step in detail, including the reasoning behind decisions and potential errors.\n", 432 | "1.4. Reflect on the decision-making process and thought process.\n", 433 | "1.5. Predict challenges such as point movement and propose error-checking mechanisms.\n", 434 | "\n", 435 | "Step 2:\n", 436 | "#Plan#\n", 437 | "2.1. Create a scenario where points can move dynamically on the triangle's sides.\n", 438 | "2.2. Evaluate three methods for calculating quadrilaterals, discussing their advantages and disadvantages.\n", 439 | "2.3. Explain each step, identifying potential errors and their implications.\n", 440 | "2.4. Incorporate meta-cognitive questions about the decision-making process.\n", 441 | "2.5. Predict possible challenges and propose solutions to ensure accuracy.\n", 442 | "\n", 443 | "Step 3:\n", 444 | "#Elaborate on Methods#\n", 445 | "3.1. Design a scenario that challenges the AI to consider the implications of point movement on quadrilateral formation.\n", 446 | "3.2. Evaluate methods in terms of computational efficiency and robustness against errors.\n", 447 | "3.3. Explain each decision, identifying potential errors and their impact on the final result.\n", 448 | "3.4. Ask reflective questions about the decision-making process and the thought process behind each step.\n", 449 | "3.5. Predict challenges related to point movement and propose mechanisms to check for errors and ensure accuracy.\n", 450 | "\n", 451 | "Step 4:\n", 452 | "#Execute the Plan#\n", 453 | "Rewrite the instruction to include a multi-level scenario with moving points. Evaluate three methods for calculating quadrilaterals, discussing their efficiency and robustness. Explain each step, including the reasoning and potential errors. Incorporate reflective questions about the decision process. Predict challenges and propose solutions to ensure accuracy.\n", 454 | "\n", 455 | "Step 5:\n", 456 | "#Review the Rewritten Instruction#\n", 457 | "Ensure the rewritten instruction is significantly more complex, includes all elements of the plan, and is clear and answerable. Adjust to maintain clarity and complexity.\n", 458 | "\n", 459 | "Step 6:\n", 460 | "#Finally Rewritten Instruction#\n", 461 | "In a multi-level scenario where points on the sides of triangle XYZ can dynamically move, calculate the number of different quadrilaterals that can be formed. Evaluate three methods for calculation, discussing their efficiency and robustness. Explain each step, identifying potential errors and their implications. Reflect on the decision-making process and thought process. Predict challenges related to point movement and propose error-checking mechanisms to ensure the accuracy of your solution.\n", 462 | "```\n", 463 | "\n", 464 | "Feedbacks:\n", 465 | " 1. ### PASSED\n", 466 | " 2. ### PASSED\n", 467 | " 3. ### PASSED\n", 468 | "\n", 469 | "Final Instruction: In a multi-level hypothetical scenario where a triangle XYZ has 4 distinct points on side XY, 5 distinct points on side YZ, and 6 distinct points on side ZX, with points that can move along the sides, how would you calculate the number of different quadrilaterals that can be formed by selecting four points? Evaluate the provided solution and alternative methods, including their advantages and disadvantages, explain the rationale behind each step, and identify any potential errors in the calculation. Reflect on the decision-making process and predict possible challenges in the calculation, proposing solutions to overcome them. Consider the implications of moving points and the robustness of your solution against potential errors, while also contemplating the adaptability of your strategy based on previous errors in similar problems.\n" 470 | ] 471 | } 472 | ], 473 | "source": [ 474 | "analyze_single_sample(data[600])" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": null, 480 | "metadata": {}, 481 | "outputs": [], 482 | "source": [] 483 | } 484 | ], 485 | "metadata": { 486 | "kernelspec": { 487 | "display_name": "base", 488 | "language": "python", 489 | "name": "python3" 490 | }, 491 | "language_info": { 492 | "codemirror_mode": { 493 | "name": "ipython", 494 | "version": 3 495 | }, 496 | "file_extension": ".py", 497 | "mimetype": "text/x-python", 498 | "name": "python", 499 | "nbconvert_exporter": "python", 500 | "pygments_lexer": "ipython3", 501 | "version": "3.11.9" 502 | } 503 | }, 504 | "nbformat": 4, 505 | "nbformat_minor": 2 506 | } 507 | --------------------------------------------------------------------------------