Refine all the implementation code to higher quality for release (#29)

* refine CI script

* refine all the code to higher quality

* refine the script to factor extraction and implementation

* add task loader interface

* add a task loader interface && move pdf analysis to pdf task loader

* change the name to global variables

---------

Co-authored-by: xuyang1 <xuyang1@microsoft.com>
This commit is contained in:
Xu Yang
2024-06-18 11:50:03 +08:00
committed by GitHub
parent ebb659a018
commit a126c84c92
30 changed files with 567 additions and 564 deletions
@@ -8,21 +8,23 @@ import pandas as pd
from jinja2 import Template
from rdagent.oai.llm_utils import APIBackend
from rdagent.core.log import FinCoLog
from rdagent.core.log import RDAgentLog
from rdagent.factor_implementation.evolving.evolving_strategy import FactorImplementTask, FactorEvovlingItem
from rdagent.core.task import (
TaskImplementation,
)
from typing import List, Tuple
from rdagent.core.evolving_framework import QueriedKnowledge,Feedback
from rdagent.core.evolving_framework import QueriedKnowledge, Feedback
from rdagent.core.evaluation import Evaluator
from rdagent.core.prompts import Prompts
from rdagent.factor_implementation.share_modules.factor_implementation_config import FactorImplementSettings
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.factor_implementation.share_modules.factor_implementation_config import FACTOR_IMPLEMENT_SETTINGS
from rdagent.core.utils import multiprocessing_wrapper
from pathlib import Path
evaluate_prompts = Prompts(file_path=Path(__file__).parent.parent / "prompts.yaml")
class FactorImplementationEvaluator(Evaluator):
# TODO:
# I think we should have unified interface for all evaluates, for examples.
@@ -58,6 +60,7 @@ class FactorImplementationEvaluator(Evaluator):
gt_df = gt_df.to_frame("gt_factor")
return gt_df, gen_df
class FactorImplementationCodeEvaluator(Evaluator):
def evaluate(
self,
@@ -89,7 +92,7 @@ class FactorImplementationCodeEvaluator(Evaluator):
system_prompt=system_prompt,
former_messages=[],
)
> FactorImplementSettings().chat_token_limit
> RD_AGENT_SETTINGS.chat_token_limit
):
execution_feedback_to_render = execution_feedback_to_render[len(execution_feedback_to_render) // 2 :]
user_prompt = Template(
@@ -109,6 +112,7 @@ class FactorImplementationCodeEvaluator(Evaluator):
return critic_response
class FactorImplementationSingleColumnEvaluator(FactorImplementationEvaluator):
def evaluate(
self,
@@ -442,7 +446,7 @@ class FactorImplementationValueEvaluator(Evaluator):
)
except Exception as e:
FinCoLog().warning(f"Error occurred when calculating the correlation: {str(e)}")
RDAgentLog().warning(f"Error occurred when calculating the correlation: {str(e)}")
conclusions.append(
f"Some error occurred when calculating the correlation. Investigate the factors that might be causing the discrepancies and ensure that the logic of the factor calculation is consistent. Error: {e}",
)
@@ -474,7 +478,9 @@ class FactorImplementationFinalDecisionEvaluator(Evaluator):
code_feedback: str,
**kwargs,
) -> Tuple:
system_prompt = Prompts(file_path=Path(__file__).parent.parent / "prompts.yaml")["evaluator_final_decision_v1_system"]
system_prompt = Prompts(file_path=Path(__file__).parent.parent / "prompts.yaml")[
"evaluator_final_decision_v1_system"
]
execution_feedback_to_render = execution_feedback
user_prompt = Template(
evaluate_prompts["evaluator_final_decision_v1_user"],
@@ -494,7 +500,7 @@ class FactorImplementationFinalDecisionEvaluator(Evaluator):
system_prompt=system_prompt,
former_messages=[],
)
> FactorImplementSettings().chat_token_limit
> RD_AGENT_SETTINGS.chat_token_limit
):
execution_feedback_to_render = execution_feedback_to_render[len(execution_feedback_to_render) // 2 :]
user_prompt = Template(
@@ -522,6 +528,7 @@ class FactorImplementationFinalDecisionEvaluator(Evaluator):
final_evaluation_dict["final_feedback"],
)
class FactorImplementationSingleFeedback:
"""This class is a feedback to single implementation which is generated from an evaluator."""
@@ -556,12 +563,14 @@ class FactorImplementationSingleFeedback:
This implementation is {'SUCCESS' if self.final_decision else 'FAIL'}.
"""
class FactorImplementationsMultiFeedback(
Feedback,
List[FactorImplementationSingleFeedback],
):
"""Feedback contains a list, each element is the corresponding feedback for each factor implementation."""
class FactorImplementationEvaluatorV1(FactorImplementationEvaluator):
"""This class is the v1 version of evaluator for a single factor implementation.
It calls several evaluators in share modules to evaluate the factor implementation.
@@ -633,7 +642,7 @@ class FactorImplementationEvaluatorV1(FactorImplementationEvaluator):
value_decision,
) = self.value_evaluator.evaluate(source_df=source_df, gt_df=gt_df)
except Exception as e:
FinCoLog().warning("Value evaluation failed with exception: %s", e)
RDAgentLog().warning("Value evaluation failed with exception: %s", e)
factor_feedback.factor_value_feedback = "Value evaluation failed."
value_decision = False
@@ -713,13 +722,12 @@ class FactorImplementationsMultiEvaluator(Evaluator):
),
),
)
multi_implementation_feedback = multiprocessing_wrapper(calls, n=FactorImplementSettings().evo_multi_proc_n)
multi_implementation_feedback = multiprocessing_wrapper(calls, n=FACTOR_IMPLEMENT_SETTINGS.evo_multi_proc_n)
final_decision = [
None if single_feedback is None else single_feedback.final_decision
for single_feedback in multi_implementation_feedback
]
print(f"Final decisions: {final_decision} True count: {final_decision.count(True)}")
RDAgentLog().info(f"Final decisions: {final_decision} True count: {final_decision.count(True)}")
return multi_implementation_feedback
@@ -1,50 +0,0 @@
from rdagent.core.evolving_framework import Feedback, EvolvableSubjects, Evaluator, EvoStep
from rdagent.core.evolving_framework import EvoAgent
from tqdm import tqdm
class RAGEvoAgent(EvoAgent):
def __init__(self, max_loop, evolving_strategy, rag) -> None:
super().__init__(max_loop, evolving_strategy)
self.rag = rag
self.evolving_trace = []
def multistep_evolve(
self,
evo: EvolvableSubjects,
eva: Evaluator | Feedback,
*,
with_knowledge: bool = False,
with_feedback: bool = True,
knowledge_self_gen: bool = False) -> EvolvableSubjects:
for _ in tqdm(range(self.max_loop), "Implementing factors"):
# 1. knowledge self-evolving
if knowledge_self_gen and self.rag is not None:
self.rag.generate_knowledge(self.evolving_trace)
# 2. 检索需要的Knowledge
queried_knowledge = None
if with_knowledge and self.rag is not None:
# TODO: 这里放了evolving_trace实际上没有作用
queried_knowledge = self.rag.query(evo, self.evolving_trace)
# 3. evolve
evo = self.evolving_strategy.evolve(
evo=evo,
evolving_trace=self.evolving_trace,
queried_knowledge=queried_knowledge,
)
# 4. 封装Evolve结果
es = EvoStep(evo, queried_knowledge)
# 5. 环境评测反馈
if with_feedback:
es.feedback = eva if isinstance(eva, Feedback) else eva.evaluate(evo, queried_knowledge=queried_knowledge)
# 6. 更新trace
self.evolving_trace.append(es)
for index, feedback in enumerate(es.feedback):
if feedback is not None:
evo.evolve_trace[evo.target_factor_tasks[index].factor_name][-1].feedback = feedback
return evo
@@ -8,10 +8,11 @@ from typing import TYPE_CHECKING
from jinja2 import Template
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.evolving_framework import EvolvingStrategy, QueriedKnowledge
from rdagent.oai.llm_utils import APIBackend
from rdagent.factor_implementation.share_modules.factor_implementation_config import (
FactorImplementSettings,
FACTOR_IMPLEMENT_SETTINGS,
)
from rdagent.core.task import (
@@ -81,18 +82,18 @@ class MultiProcessEvolvingStrategy(EvolvingStrategy):
# 2. 选择selection方法
# if the number of factors to be implemented is larger than the limit, we need to select some of them
if FactorImplementSettings().select_ratio < 1:
if FACTOR_IMPLEMENT_SETTINGS.select_ratio < 1:
# if the number of loops is equal to the select_loop, we need to select some of them
implementation_factors_per_round = int(
FactorImplementSettings().select_ratio * len(to_be_finished_task_index)
FACTOR_IMPLEMENT_SETTINGS.select_ratio * len(to_be_finished_task_index)
)
if FactorImplementSettings().select_method == "random":
if FACTOR_IMPLEMENT_SETTINGS.select_method == "random":
to_be_finished_task_index = RandomSelect(
to_be_finished_task_index,
implementation_factors_per_round,
)
if FactorImplementSettings().select_method == "scheduler":
if FACTOR_IMPLEMENT_SETTINGS.select_method == "scheduler":
to_be_finished_task_index = LLMSelect(
to_be_finished_task_index,
implementation_factors_per_round,
@@ -105,17 +106,18 @@ class MultiProcessEvolvingStrategy(EvolvingStrategy):
(self.implement_one_factor, (new_evo.target_factor_tasks[target_index], queried_knowledge))
for target_index in to_be_finished_task_index
],
n=FactorImplementSettings().evo_multi_proc_n,
n=FACTOR_IMPLEMENT_SETTINGS.evo_multi_proc_n,
)
for index, target_index in enumerate(to_be_finished_task_index):
new_evo.corresponding_implementations[target_index] = result[index]
if result[index].target_task.factor_name in new_evo.evolve_trace:
new_evo.evolve_trace[result[index].target_task.factor_name].append(result[index])
else:
new_evo.evolve_trace[result[index].target_task.factor_name] = [result[index]]
new_evo.corresponding_selection.append(to_be_finished_task_index)
# for target_index in to_be_finished_task_index:
# new_evo.corresponding_implementations[target_index] = self.implement_one_factor(
# new_evo.target_factor_tasks[target_index], queried_knowledge
# )
new_evo.corresponding_selection = to_be_finished_task_index
return new_evo
@@ -172,7 +174,7 @@ class FactorEvolvingStrategy(MultiProcessEvolvingStrategy):
session.build_chat_completion_message_and_calculate_token(
user_prompt,
)
< FactorImplementSettings().chat_token_limit
< RD_AGENT_SETTINGS.chat_token_limit
):
break
elif len(queried_former_failed_knowledge_to_render) > 1:
@@ -205,7 +207,7 @@ class FactorEvolvingStrategyWithGraph(MultiProcessEvolvingStrategy):
target_task: FactorImplementTask,
queried_knowledge,
) -> TaskImplementation:
error_summary = FactorImplementSettings().v2_error_summary
error_summary = FACTOR_IMPLEMENT_SETTINGS.v2_error_summary
# 1. 提取因子的背景信息
target_factor_task_information = target_task.get_factor_information()
@@ -284,7 +286,7 @@ class FactorEvolvingStrategyWithGraph(MultiProcessEvolvingStrategy):
)
if (
session_summary.build_chat_completion_message_and_calculate_token(error_summary_user_prompt)
< FactorImplementSettings().chat_token_limit
< RD_AGENT_SETTINGS.chat_token_limit
):
break
elif len(queried_similar_error_knowledge_to_render) > 0:
@@ -311,7 +313,7 @@ class FactorEvolvingStrategyWithGraph(MultiProcessEvolvingStrategy):
session.build_chat_completion_message_and_calculate_token(
user_prompt,
)
< FactorImplementSettings().chat_token_limit
< RD_AGENT_SETTINGS.chat_token_limit
):
break
elif len(queried_former_failed_knowledge_to_render) > 1:
@@ -1,6 +1,6 @@
from __future__ import annotations
from rdagent.factor_implementation.share_modules.factor_implementation_config import (
FactorImplementSettings,
FACTOR_IMPLEMENT_SETTINGS,
)
from rdagent.core.task import (
@@ -9,7 +9,7 @@ from rdagent.core.task import (
TestCase,
)
from rdagent.core.evolving_framework import EvolvableSubjects
from rdagent.core.log import FinCoLog
from rdagent.core.log import RDAgentLog
from pathlib import Path
@@ -35,7 +35,7 @@ class FactorImplementTask(BaseTask):
factor_name,
factor_description,
factor_formulation,
factor_formulation_description: str = '',
factor_formulation_description: str = "",
variables: dict = {},
resource: str = None,
) -> None:
@@ -68,20 +68,17 @@ class FactorEvovlingItem(EvolvableSubjects):
def __init__(
self,
target_factor_tasks: list[FactorImplementTask],
corresponding_gt: list[TestCase] = None,
corresponding_gt_implementations: list[TaskImplementation] = None,
):
super().__init__()
self.target_factor_tasks = target_factor_tasks
self.corresponding_implementations: list[TaskImplementation] = [None for _ in target_factor_tasks]
self.corresponding_selection: list[list] = []
self.evolve_trace = {}
self.corresponding_gt = corresponding_gt
self.corresponding_selection: list = None
if corresponding_gt_implementations is not None and len(
corresponding_gt_implementations,
) != len(target_factor_tasks):
self.corresponding_gt_implementations = None
FinCoLog.warning(
RDAgentLog().warning(
"The length of corresponding_gt_implementations is not equal to the length of target_factor_tasks, set corresponding_gt_implementations to None",
)
else:
@@ -112,10 +109,10 @@ class FileBasedFactorImplementation(TaskImplementation):
super().__init__(target_task)
self.code = code
self.executed_factor_value_dataframe = executed_factor_value_dataframe
self.logger = FinCoLog()
self.logger = RDAgentLog()
self.raise_exception = raise_exception
self.workspace_path = Path(
FactorImplementSettings().file_based_execution_workspace,
FACTOR_IMPLEMENT_SETTINGS.file_based_execution_workspace,
) / str(uuid.uuid4())
@staticmethod
@@ -151,17 +148,14 @@ class FileBasedFactorImplementation(TaskImplementation):
# TODO: to make the interface compatible with previous code. I kept the original behavior.
raise ValueError(self.FB_CODE_NOT_SET)
with FileLock(self.workspace_path / "execution.lock"):
(Path.cwd() / "git_ignore_folder" / "factor_implementation_execution_cache").mkdir(
exist_ok=True, parents=True
)
if FactorImplementSettings().enable_execution_cache:
if FACTOR_IMPLEMENT_SETTINGS.enable_execution_cache:
# NOTE: cache the result for the same code
target_file_name = md5_hash(self.code)
cache_file_path = (
Path.cwd()
/ "git_ignore_folder"
/ "factor_implementation_execution_cache"
/ f"{target_file_name}.pkl"
Path(FACTOR_IMPLEMENT_SETTINGS.implementation_execution_cache_location) / f"{target_file_name}.pkl"
)
Path(FACTOR_IMPLEMENT_SETTINGS.implementation_execution_cache_location).mkdir(
exist_ok=True, parents=True
)
if cache_file_path.exists() and not self.raise_exception:
cached_res = pickle.load(open(cache_file_path, "rb"))
@@ -173,7 +167,7 @@ class FileBasedFactorImplementation(TaskImplementation):
return self.FB_FROM_CACHE, self.executed_factor_value_dataframe
source_data_path = Path(
FactorImplementSettings().file_based_execution_data_folder,
FACTOR_IMPLEMENT_SETTINGS.file_based_execution_data_folder,
)
self.workspace_path.mkdir(exist_ok=True, parents=True)
@@ -189,7 +183,7 @@ class FileBasedFactorImplementation(TaskImplementation):
shell=True,
cwd=self.workspace_path,
stderr=subprocess.STDOUT,
timeout=FactorImplementSettings().file_based_execution_timeout,
timeout=FACTOR_IMPLEMENT_SETTINGS.file_based_execution_timeout,
)
except subprocess.CalledProcessError as e:
import site
@@ -206,7 +200,7 @@ class FileBasedFactorImplementation(TaskImplementation):
if self.raise_exception:
raise RuntimeErrorException(execution_feedback)
except subprocess.TimeoutExpired:
execution_feedback += f"Execution timeout error and the timeout is set to {FactorImplementSettings().file_based_execution_timeout} seconds."
execution_feedback += f"Execution timeout error and the timeout is set to {FACTOR_IMPLEMENT_SETTINGS.file_based_execution_timeout} seconds."
if self.raise_exception:
raise RuntimeErrorException(execution_feedback)
@@ -227,7 +221,7 @@ class FileBasedFactorImplementation(TaskImplementation):
if store_result and executed_factor_value_dataframe is not None:
self.executed_factor_value_dataframe = executed_factor_value_dataframe
if FactorImplementSettings().enable_execution_cache:
if FACTOR_IMPLEMENT_SETTINGS.enable_execution_cache:
pickle.dump(
(execution_feedback, executed_factor_value_dataframe),
open(cache_file_path, "wb"),
@@ -249,4 +243,3 @@ class FileBasedFactorImplementation(TaskImplementation):
with factor_path.open("r") as f:
code = f.read()
return FileBasedFactorImplementation(task, code=code, **kwargs)
@@ -18,7 +18,7 @@ from rdagent.core.evolving_framework import (
QueriedKnowledge,
RAGStrategy,
)
from rdagent.core.log import FinCoLog
from rdagent.core.log import RDAgentLog
from rdagent.core.prompts import Prompts
from rdagent.factor_implementation.evolving.evaluators import FactorImplementationSingleFeedback
from rdagent.core.task import (
@@ -30,9 +30,10 @@ from rdagent.knowledge_management.graph import UndirectedGraph, UndirectedNode
from rdagent.oai.llm_utils import APIBackend, calculate_embedding_distance_between_str_list
from rdagent.factor_implementation.share_modules.factor_implementation_config import (
FactorImplementSettings,
FACTOR_IMPLEMENT_SETTINGS,
)
class FactorImplementationKnowledge(Knowledge):
def __init__(
self,
@@ -138,9 +139,9 @@ class FactorImplementationRAGStrategyV1(RAGStrategy):
evo: EvolvableSubjects,
evolving_trace: list[EvoStep],
) -> QueriedKnowledge | None:
v1_query_former_trace_limit = FactorImplementSettings().v1_query_former_trace_limit
v1_query_similar_success_limit = FactorImplementSettings().v1_query_similar_success_limit
fail_task_trial_limit = FactorImplementSettings().fail_task_trial_limit
v1_query_former_trace_limit = FACTOR_IMPLEMENT_SETTINGS.v1_query_former_trace_limit
v1_query_similar_success_limit = FACTOR_IMPLEMENT_SETTINGS.v1_query_similar_success_limit
fail_task_trial_limit = FACTOR_IMPLEMENT_SETTINGS.fail_task_trial_limit
queried_knowledge = FactorImplementationQueriedKnowledgeV1()
for target_factor_task in evo.target_factor_tasks:
@@ -280,7 +281,7 @@ class FactorImplementationGraphRAGStrategy(RAGStrategy):
return None
def query(self, evo: EvolvableSubjects, evolving_trace: list[EvoStep]) -> QueriedKnowledge | None:
conf_knowledge_sampler = FactorImplementSettings().v2_knowledge_sampler
conf_knowledge_sampler = FACTOR_IMPLEMENT_SETTINGS.v2_knowledge_sampler
factor_implementation_queried_graph_knowledge = FactorImplementationQueriedGraphKnowledge(
success_task_to_knowledge_dict=self.knowledgebase.success_task_to_knowledge_dict,
)
@@ -288,18 +289,18 @@ class FactorImplementationGraphRAGStrategy(RAGStrategy):
factor_implementation_queried_graph_knowledge = self.former_trace_query(
evo,
factor_implementation_queried_graph_knowledge,
FactorImplementSettings().v2_query_former_trace_limit,
FACTOR_IMPLEMENT_SETTINGS.v2_query_former_trace_limit,
)
factor_implementation_queried_graph_knowledge = self.component_query(
evo,
factor_implementation_queried_graph_knowledge,
FactorImplementSettings().v2_query_component_limit,
FACTOR_IMPLEMENT_SETTINGS.v2_query_component_limit,
knowledge_sampler=conf_knowledge_sampler,
)
factor_implementation_queried_graph_knowledge = self.error_query(
evo,
factor_implementation_queried_graph_knowledge,
FactorImplementSettings().v2_query_error_limit,
FACTOR_IMPLEMENT_SETTINGS.v2_query_error_limit,
knowledge_sampler=conf_knowledge_sampler,
)
return factor_implementation_queried_graph_knowledge
@@ -327,7 +328,7 @@ class FactorImplementationGraphRAGStrategy(RAGStrategy):
)["component_no_list"]
return [all_component_nodes[index - 1] for index in sorted(list(set(component_no_list)))]
except:
FinCoLog.warning("Error when analyzing components.")
RDAgentLog().warning("Error when analyzing components.")
analyze_component_user_prompt = "Your response is not a valid component index list."
return []
@@ -383,7 +384,7 @@ class FactorImplementationGraphRAGStrategy(RAGStrategy):
"""
Query the former trace knowledge of the working trace, and find all the failed task information which tried more than fail_task_trial_limit times
"""
fail_task_trial_limit = FactorImplementSettings().fail_task_trial_limit
fail_task_trial_limit = FACTOR_IMPLEMENT_SETTINGS.fail_task_trial_limit
for target_factor_task in evo.target_factor_tasks:
target_factor_task_information = target_factor_task.get_factor_information()
@@ -705,7 +706,7 @@ class FactorImplementationGraphKnowledgeBase(KnowledgeBase):
Load knowledge, offer brief information of knowledge and common handle interfaces
"""
self.graph: UndirectedGraph = UndirectedGraph.load(Path.cwd() / "graph.pkl")
FinCoLog().info(f"Knowledge Graph loaded, size={self.graph.size()}")
RDAgentLog().info(f"Knowledge Graph loaded, size={self.graph.size()}")
if init_component_list:
for component in init_component_list:
@@ -901,4 +902,3 @@ class FactorImplementationGraphKnowledgeBase(KnowledgeBase):
intersection_node_list_sort_by_freq.append(node)
return intersection_node_list_sort_by_freq
@@ -1,27 +1,32 @@
from rdagent.oai.llm_utils import APIBackend
from jinja2 import Template
from rdagent.factor_implementation.share_modules.factor_implementation_config import FactorImplementSettings
import json
from rdagent.factor_implementation.share_modules.factor_implementation_utils import get_data_folder_intro
from rdagent.factor_implementation.evolving.factor import FactorEvovlingItem
from rdagent.core.prompts import Prompts
from rdagent.core.log import RDAgentLog
from rdagent.core.conf import RD_AGENT_SETTINGS
from pathlib import Path
scheduler_prompts = Prompts(file_path=Path(__file__).parent.parent / "prompts.yaml")
def RandomSelect(to_be_finished_task_index, implementation_factors_per_round):
import random
to_be_finished_task_index = random.sample(
to_be_finished_task_index,
implementation_factors_per_round,
)
print("The random selection is:",to_be_finished_task_index)
RDAgentLog().info(f"The random selection is: {to_be_finished_task_index}")
return to_be_finished_task_index
def LLMSelect(to_be_finished_task_index, implementation_factors_per_round, evo:FactorEvovlingItem, former_trace):
def LLMSelect(to_be_finished_task_index, implementation_factors_per_round, evo: FactorEvovlingItem, former_trace):
tasks = []
for i in to_be_finished_task_index:
# find corresponding former trace for each task
# find corresponding former trace for each task
target_factor_task_information = evo.target_factor_tasks[i].get_factor_information()
if target_factor_task_information in former_trace:
tasks.append((i, evo.target_factor_tasks[i], former_trace[target_factor_task_information]))
@@ -37,20 +42,17 @@ def LLMSelect(to_be_finished_task_index, implementation_factors_per_round, evo:F
)
while True:
user_prompt = (
Template(
scheduler_prompts["select_implementable_factor_user"],
)
.render(
factor_num = implementation_factors_per_round,
target_factor_tasks=tasks,
)
user_prompt = Template(
scheduler_prompts["select_implementable_factor_user"],
).render(
factor_num=implementation_factors_per_round,
target_factor_tasks=tasks,
)
if (
session.build_chat_completion_message_and_calculate_token(
user_prompt,
)
< FactorImplementSettings().chat_token_limit
< RD_AGENT_SETTINGS.chat_token_limit
):
break
@@ -65,5 +67,5 @@ def LLMSelect(to_be_finished_task_index, implementation_factors_per_round, evo:F
selection_index = [x for x in selection if isinstance(x, int)]
except:
return to_be_finished_task_index
return selection_index