Compare commits

...

197 Commits

Author SHA1 Message Date
you-n-g 11be3a480b chore: release 0.7.0
Release-As: 0.7.0
2025-07-08 11:19:08 +08:00
Linlang a7cc6fa5f1 add dependence (#1029) 2025-07-08 11:12:44 +08:00
Tim ed086dcec4 feat: merge code summary and support more traces (#1025)
* merge with code_change_summary

* fix: prevent max_sota_retrieved_num_per_trace from becoming zero

* select sota from more than 2 traces
2025-07-08 10:00:52 +08:00
Yuante Li b05c2cba09 fix: fix some bugs in quant scen (#1026)
* fix some  bugs

* fix ci

* fix

* refine

* fix ci
2025-07-07 19:21:05 +08:00
you-n-g f71a005d55 refactor: privatize remain_time_duration and use remain_time() (#1027) 2025-07-07 18:52:17 +08:00
Tim a0015c1511 chore: sort trace by loop_id (#977)
* last as sota to submit

* chore: sort trace by loop_id

* Update rdagent/log/ui/utils.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* reformat

* update _log_path_hash_func

* compare sota_exp_to_submit

* fix ui storage

* move sota_exp_to_submit setting to record(from direct_exp_gen)

* save sota_exp_to_submit

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Bowen Xian <xianbowen@outlook.com>
2025-07-07 17:08:55 +08:00
amstrongzyf ea86b537eb fix: support experimental support for Deepseek models and update docs about configuration (#1024)
* fix qlib running bug for deepseek and add configuration docs about deepseek
2025-07-07 14:18:56 +08:00
Tim 4bf16d03cf chore: restore parameter timeout in loop (#1022)
* restore parameter timeout in loop
2025-07-05 14:55:26 +08:00
you-n-g 6dbef13837 chore: document APIBackend parameters and remove sparse dep (#1019) 2025-07-04 20:31:06 +08:00
XianBW eff5dcc879 add trace DAG in trace, make exp show compatible with old versions (#1018) 2025-07-04 15:47:45 +08:00
Xu Yang 4608a0c63e feat: try coder on whole data (#1017)
* commit all code

* fix feedback bug

* add debug mode in ds

* update prompt

* prioritize performance than time lit

* use run instead

* store running time

* move all data running into running phase

* fix a bug

* use sample data as default

---------

Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-07-04 14:30:40 +08:00
XianBW 6e57d0adc4 chore: ui server updates (#951)
* ui server update

* update

* fix bugs

* updates

* use randomname

* fix

* fix bugs

* change time interval in debug server

* some change

* fix CI

* time interval change

* updates

* some changes

* fix

* fix curves

* one IP, one pointers to show msgs return progress

* enable /control

* fix bugs

* fix CI

* fix isort
2025-07-04 12:32:14 +08:00
XianBW 76660b1ace feat: show first evo round codes diff (#1009)
* log settings

* add first diff in evolving

* save dict settings

* load settings in data science ui
2025-07-03 19:21:14 +08:00
you-n-g 05bb19ad4b docs: add README for exp_gen folder structure proposal (#1010)
* docs: add README for exp_gen folder structure proposal

* docs: update README with tool strategy and adjust folder structure

* docs: rename 'Tool strategy' section to 'Tools' in exp_gen README
2025-07-03 11:31:51 +08:00
you-n-g 7a7f865733 refactor: replace run_ret_code with run and adjust helper methods (#1013) 2025-07-03 11:24:05 +08:00
Yuante Li 92bb49fc8f fix: fix a minor bug in DS eval (#1012) 2025-07-02 23:31:29 +08:00
you-n-g a67ec5ba01 chore: add devcontainer Dockerfile, devcontainer.json, and env (#997)
* chore: add devcontainer Dockerfile, devcontainer.json, and env

* style: remove unnecessary comment on ENABLE_CACHE setting

* docs: add internal devcontainer README with setup instructions

* Update README in .devcontainer based on PR #997

---------

Co-authored-by: jingyuanlm <842442862@qq.com>
2025-07-02 18:54:49 +08:00
Tim c4007b4655 chore: sample data map (#1005)
* chore: map data sampler
2025-07-02 15:11:57 +08:00
Yuante Li 3d227787e8 feat: added running time statistics for the DS scenario experiment (#1007)
* added running time statistics for the DS scenario experiment

* update execute_ret_code to return running_time

* fix

* fix

* update describe

* add EnvResult

* update corresponding calls

* add RunningInfo class

* fix

* fix

* fix

* fix ci

* rename function name

* fix ci

* fix

* refine running_time logic

* fix ci
2025-07-02 15:11:18 +08:00
XianBW 92ce8a898b add code diff in evolving window (#1008) 2025-06-30 20:10:34 +08:00
you-n-g 55f7ccc93d feat: add hide_base_name option and update data folder prompts (#1004)
* feat: add hide_base_name option and update data folder prompts

* rename folder
2025-06-29 21:22:16 +08:00
you-n-g 49aec3135e feat: add code change summary (#1000)
* feat: add code change summary and dict_get_with_warning util

* feat: support code_change_summary in feedback classes

* lint

* feat: validate response_format using BaseModel and warn unknown formats
2025-06-29 13:40:15 +08:00
amstrongzyf 69500fc52d refactor: convert data describe functions to class-based implementation and add output example (#999)
* feat: Enhance data folder description for clarity and robustness

* fix bug

* fix present bugs

* delete useless files

* add output example and refactor the hole util.py

* fix bug for file tree

* add corner case example

* delete useless file
2025-06-28 21:23:40 +08:00
you-n-g d89fb2a82e chore: release 0.6.1
Release-As: 0.6.1
2025-06-28 20:06:57 +08:00
Linlang 976b86664f fix: fix mount (#1001)
* docs: document extra_volumes dict format in DockerConf

* feat: accept dict values in extra_volumes to specify bind and mode

* fix: skip invalid PDF reports to prevent infinite loop

* from break to raise self.LoopTerminationError

* format with black

---------

Co-authored-by: Young <afe.young@gmail.com>
2025-06-28 20:01:14 +08:00
xuangu-fang 05b584b184 fix: handle the bug of wrong dag_parant index (#996)
* fix the dag_parant_index bug caused by the wrong calling order of

* auto-lint
2025-06-27 17:01:29 +08:00
Yuante Li c701a8105b fix: improve log folder sorting and selection UX (#993)
* fix ui

* update
2025-06-27 11:38:05 +08:00
you-n-g f075824a16 chore: release 0.6.0
Release-As: 0.6.0
2025-06-26 22:32:15 +08:00
you-n-g 5c5508c8cc fix: add async to direct_exp_gen avoid infinite loop (#992)
* refactor: convert direct_exp_gen to async and enforce parallel limit

* fix bug

* change coroutine function position

* fix fin_quant's direct_exp_gen

* format with isort

---------

Co-authored-by: Bowen Xian <xianbowen@outlook.com>
Co-authored-by: SunsetWolf <Lv.Linlang@hotmail.com>
2025-06-26 22:10:52 +08:00
xuangu-fang a5c5c7172b feat: async mechanism for multi-trace (#981)
* start to work on multi-trace + async

* init ver of async-multi-tarce, to test

* add eng-ver log

* complete version of async+ mul-trace

* debug

* fix bug on         DS_RD_SETTING.get()

* update

* fix bug + simplif the usage of async in multi-trace

* fix mini bug of arg_name

* Move local_selection into class Experiment & clean the code
2025-06-26 15:49:47 +08:00
you-n-g 58b5cc1458 fix: refine prompt (#987)
* refactor: rename failed_exp_and_feedback_list to include _after_sota suffix

* refactor: merge prompts_v3 into prompts_v2 and update references
2025-06-26 12:08:31 +08:00
Xu Yang 80d81c8ea9 support tar in dataset & fix a small bug in loop (#986)
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-06-25 15:32:37 +08:00
XianBW 1b762ceddd fix LLM log bug (#985) 2025-06-25 10:38:20 +08:00
Tim ed84b6b0a3 chore: merge with more trace (#982)
* merge support more traces
* use feedback from all traces
2025-06-24 10:47:45 +08:00
you-n-g 46367e15f5 fix: refine details (#979)
* feat: add parquet preview and extract common DataFrame preview logic

* refactor: improve error messages, prompts, regex, and session loading

* lint
2025-06-23 10:38:27 +08:00
Yuante Li 0859b9f3a2 fix: fix a bug and update the docs (#978) 2025-06-20 10:58:14 +08:00
Copilot e03fca3206 fix: docker container cleanup to prevent accumulation and system slowdown (#975)
* Initial plan for issue

* Fix Docker container cleanup issue by using try-finally block

Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>

* Fix additional Docker container leaks in health_check and GPU test functions

Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>

* Remove temporary test files and finalize Docker container cleanup fix

Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>

* Refactor container cleanup code to reduce duplication as requested in review feedback

Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>

* Refactor container cleanup to use shared function and always stop before remove

Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>

* fix CI

* Fix mypy type checking errors for Docker container cleanup

Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>

* fix CI

* Remove unnecessary _cleanup_container wrapper method in DockerEnv class

Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: peteryang1 <25981102+peteryang1@users.noreply.github.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-06-19 18:32:50 +08:00
Xu Yang 977136f1f3 fix: merge datascience v3 and v2 (#974)
* add coder version

* merge cooder and feedback prompts

* align v2 and v3 proposal prompts

* fix a small bug

* fix a bug

* fix another bug

* support both function calling and json mode in v2 proposal

* fix minor bug

* reformat

* remove proposal v3

* fix a small bug in json mode

* fix CI

* remove tmp file

* remove v3 check

---------

Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-06-19 13:25:10 +08:00
you-n-g bf9efe127d chore: release 0.5.0
Release-As: 0.5.0
2025-06-18 15:08:11 +08:00
Yuante Li 243d5259f7 docs: update readme for v0.5.0 release (#973) 2025-06-18 14:49:24 +08:00
Linlang dd10ddf9fd fix: main bug (#938)
* feat: parameterize cache paths with USER to avoid conflicts

* guide for missing training_hyperparameters

* guidance for  KeyError: 'concise_reason'

* fixed three bugs in the test

* fix general_model task bug

* fixed some bugs in the med_model scenario

* delete comments

* format with black

* fix mypy error

* fix ruff error

* fix isort error

* sync code

* revert cache_path code

* revert cache_path code

* delete data mining scenario

* fix factor report loop

* fix LiteLLMAPIBackend log_llm_chat_content setting

* refine fin factor report scenario

* remove unused LogColors

* fix UI

* remove medical scenario docs

* change **kaggle** to **data_science**

* remove default dataset_path in create_debug_data

* remove KAGGLE_SETTINGS in kaggle_crawler

* limit litellm versions

* reformat with black

* change README

* fix_data_science_docs

* make hypothesis observations string

* Hiding old versions of kaggle docs

* hidding kaggle agent docs

---------

Co-authored-by: Young <afe.young@gmail.com>
Co-authored-by: Bowen Xian <xianbowen@outlook.com>
Co-authored-by: yuanteli <1957922024@qq.com>
2025-06-18 14:35:45 +08:00
Yuante Li a619ff999b refactor: refactor RD-Agent(Q) configuration files (#972)
* refactor rdagent(q) conf files

* fix

* fix ci
2025-06-18 12:28:29 +08:00
Tim c215e735c2 fix: use simple stdout and stderr (#966)
* use simple stdout and stderr
* add live_output config in LocalConf
2025-06-18 11:54:27 +08:00
Yuante Li 0c219b81e0 docs: update the documentation for custom dataset (#969) 2025-06-18 10:58:28 +08:00
Tim 9f793d58b3 fix: get_metric_direction for aerial-cactus-identification (#970) 2025-06-17 18:06:38 +08:00
Yuante Li f0deace7a7 docs: add readme for custom data in R&D Agent DS scenario (#968) 2025-06-17 16:29:07 +08:00
Tim 682f657637 chore: break when loop_n runs out (#964)
* raise loop termination in execute_loop

* add SENTINEL
2025-06-17 15:46:38 +08:00
Yuante Li af5898211b refactor: add custom data setting for data science scene (#967)
* add custom data setting for the data science scene

* fix ci?

* fix ci

* add custom data as an example

* fix ci

* add package

* fix test_import ci error
2025-06-17 14:55:11 +08:00
XianBW 1a1e88c47c fix: log info (#965)
* fix log caller_info

* make env info beauty
2025-06-16 19:52:44 +08:00
amstrongzyf 9890bb4b00 docs: update explanation for separate config use in litellm (#958)
* docs: update explanation for separate config use in litellm

* docs: update default backend to `rdagent.oai.backend.LiteLLMAPIBackend`

* docs: update .rst format

* Update installation_and_configuration.rst
2025-06-13 18:32:39 +08:00
XianBW 5a3a2bf2be chore: remove redundant tag in old scenarios (#917)
* remove state.times in old ui

* remove "r" tag

* remove "d" tag

* remove "ef" tag

* remove "init" tag

* fix CI

* remove old tag in app UI

* fix bugs

* fix CI

* some updates

* filter tags
2025-06-13 17:49:58 +08:00
you-n-g 77e9f186d5 fix: add missing semicolon after chmod in env shell command (#955) 2025-06-12 21:02:07 +08:00
you-n-g 2187081905 feat: replace hard-coded cache paths with dynamic cache_path config (#952)
* feat: replace hard-coded cache paths with dynamic cache_path config

* style: reorder wait_retry import and format chmod list

* refactor: pass workspace_path to chmod command and use DockerConf check
2025-06-12 17:44:31 +08:00
Xu Yang 698f89e07c print out the qlib data generation error message to help user find the problem (#954)
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-06-12 17:34:51 +08:00
you-n-g 304d3fad2e feat: parallel loop running based on asyncio (#932)
* refactor: split workflow into pkg, add WorkflowTracker & wait_retry

* feat: add async LoopBase with parallel workers and step semaphores

* fix: replace pickle with dill and run blocking tasks via joblib wrapper

* feat: add log format settings, dynamic parallelism & pickle-based snapshot

* fix: default step semaphore to 1 and avoid subprocess when single worker

* merge bowen's changes

* merge tim's changes

* refactor: extract component task mapping, add conditional logger setup

* lint

* refactor: add type hints and safer remain_time metric logging in workflow

* lint

* fix: allow BadRequestError to be pickled via custom copyreg reducer

* fix: stop loop when LoopTerminationError is raised in LoopBase

* lint

* refactor: make log tag context-local using ContextVar for thread safety

* feat: add subproc_step flag and helper to decide subprocess execution

* fix: use ./cache path and normalize relative volume bind paths

* fix: reset loop_idx to 0 on loop restart/resume to ensure correct flow

* fix: avoid chmod on cache and input dirs in Env timeout wrapper

* fix: skip chmod on 'cache' and 'input' dirs using find -prune

* fix: restrict chmod to immediate mount dirs excluding cache/input

* fix: chmod cache and input dirs alongside their contents after entry run

* fix: guard chmod with directory checks for cache and input

* fix: prefix mount_path in chmod command for cache/input dirs

* fix: drop quotes from find exclude patterns to ensure chmod executes

* fix: skip chmod on cache/input directories to avoid warning spam

* feat: support string volume mappings and poll subprocess stdout/stderr

* support remove symbolic link

* test: use dynamic home path and code volume in LocalEnv local_simple

* fix: skip trace and progress update when loop step is withdrawn

* refactor: add clean_workspace util and non-destructive workspace backup

* fix: preserve symlinks when backing up workspace with copytree

* fix: prevent AttributeError when _pbar not yet initialized in LoopBase

* perf: replace shutil.copytree with rsync for faster workspace backup

* fix: cast log directory Path to str in tar command of data science loop

* fix: use portable 'cp -r -P' instead of rsync for workspace backup

* fix: add retry and logging to workspace backup for robustness

* refactor: extract backup_folder helper and reuse in DataScienceRDLoop

* fix: propagate backup errors & default _pbar getattr to avoid error

* fix the division by zero bug

* refactor: execute RD loops via asyncio.run and add necessary imports

* lint

* lint

* lint

---------

Co-authored-by: Xu <v-xuminrui@microsoft.com>
2025-06-12 11:44:14 +08:00
xuangu-fang ba76063069 feat: enable to set different version of idea-proposal for multi traces (#895)
* fix the logic of kb-inject, allow different verion

* set more flexiable proposal-version change for multi-tarce

* auto-lint

* fix the divede-zero-bug in a trival way

* keep the dump imp. first, update in next version

* use get_sub_trace_count() to get trace_num_count

* fix the conern case bug of divide-zero

* update corner case

* fix the bug

* auto-lint

* fis the bug

* fix the logic bug in max_sota_filter

* fix bug of old version of self.exp_gen.gen

* update the reset_exp_gen_version

* use get_parent_exps to replace all  collect_all_ancestors

* auto lint

* fix the bug of reset_exp_gen_version

* fix bug: update V3's old hypothesis_rank

* trival patch on gap of V3 & V2

* make dump patch to unify proposal_V3's dentify_problems

* auto-lint

* fix the bug of sub_trace_count
2025-06-11 23:13:30 +08:00
Haoran Pan d18271275e docs: update llm setting guidance and "REASONING_THINK_RM" description (#943)
* update llm setting guidance and "REASONING_THINK_RM" description

* remove deprecated backend in readme
2025-06-11 15:45:22 +08:00
Tim a4986c415c fix: 'DSProposalV2ExpGen' object has no attribute 'COMPONENT_TASK_MAP… (#950)
* fix: 'DSProposalV2ExpGen' object has no attribute 'COMPONENT_TASK_MAPPING'
* chore: add get_component function
2025-06-09 21:49:46 +08:00
Jensen Lee 155675d4bf fix: filter system metadata dirs and init missing DSTrace attribute (#946)
* fix: filter system metadata dirs and init missing DSTrace attribute

* style:pre-commit
2025-06-07 19:44:23 +08:00
you-n-g 15e3ba9004 docs: Update README.md (#948) 2025-06-07 15:24:25 +08:00
Yuante Li 671771a626 docs: update document for RD-Agent(Q) (#940) 2025-06-07 14:41:02 +08:00
Tim 63ba3296dd chore: continue to read output (#945) 2025-06-07 13:22:56 +08:00
Linlang 805f337fc4 chore: add a rdagent server with UI & logger storage refinement(#553)
* change_log_object

* lint code

* delete comments

* change_log_object

* change_log_object

* fix import test error

* update code

* update code

* fix bugs

* skip mypy error

* skip mypy error

* skip mypy error

* Start the flask server before running the demo.

* achieve front and back interaction

* fix github-advanced-security comments

* fix github-advanced-security comments

* tmp ignore

* fix CI

* move some logic

* change format

* adjust logic

* log2json changes

* tmp

* fix

* fix bug

* refine log2json between 5 scenarios

* fix

* refine codes

* fix logic

* use localhost

* add loop & all_duration param for old scenario startup

* merge control logic

* add README for server ui api

* update README

* reuse code in logger

* add loop_n and all_duration param

* fix upload

* ui server now use port in setting

* fix port setting

* fix port setting

* fix mypy check

* refine logger and log storage

* fix ruff error

* fix CI

* refine logger, loop, storage

* bind one FileStorage with one logger

* not truncate log storage

* refine LoopBase.load(), use `checkout` instead of `output_path` and `do_truncate`

* clear session folder when loading loop to run

* move component info init step to ExpGen Class

* Update rdagent/utils/workflow.py

* move truncate_session function to LoopBase class

* add checkout param for other scenarios

* fix bug

* move WebStorage to UI

* change web_storage name

* add randomname to requirements

* add typer

* fix requirements

---------

Co-authored-by: WinstonLiyte <1957922024@qq.com>
Co-authored-by: Bowen Xian <xianbowen@outlook.com>
Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>
2025-06-06 18:52:19 +08:00
you-n-g bdaa376569 chore: update load add & (#944) 2025-06-06 16:58:09 +08:00
Haoran Pan de6b7f7648 feat: update prompt to improve json respond format of some LLM models (#928)
* update prompt to improve json respond format of some LLM models

* fix

* fix

---------

Co-authored-by: WinstonLiyte <1957922024@qq.com>
2025-06-06 11:49:40 +08:00
Tim 5bb5722921 fix: conda error information (#941)
* test with conda error

* reformat
2025-06-06 11:45:48 +08:00
XianBW 1a61d51fa8 change qlib install method (#937) 2025-06-05 11:40:46 +08:00
XianBW 2b7e0c4bd0 refine debug window logic (#936) 2025-06-05 11:29:37 +08:00
XianBW 311c2bf977 fix detect scenario logic (#935) 2025-06-05 11:19:09 +08:00
XianBW 50702e254e some update (#934) 2025-06-05 10:56:54 +08:00
Tim 543fb2c1ef fix: ds trace (#929)
* fix: remove_ansi_codes
* chore: rename function
2025-06-04 18:30:29 +08:00
Tim 51a5247285 feat: merge selectively (#888)
* chore: avoid incorporate changes
best as sota
merge hypothesis
fix: max_retrieve_num after decision
chore: select last experiments and feedbacks
* add the set_current_selection before the exp_gen when merging
add trace.NEW_ROOT
fix: no scen_prob_multiplier
fix: use regex with timeout
chore: hypothesis_rank with selected_idx
chore: define is_parent in proposal
chore: rename collect_all_ancestors to get_parent_exps
---------

Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>
Co-authored-by: Young <afe.young@gmail.com>
Co-authored-by: Xu <v-xuminrui@microsoft.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-06-04 14:46:47 +08:00
you-n-g a1843a9c4e feat: log reaching max time limit before breaking CoSTEER evolution (#921) 2025-06-03 12:51:56 +08:00
Yuante Li b1029cbd5a docs: update README for RD-Agent(Q) (#913) 2025-05-30 12:28:52 +08:00
you-n-g 0611a0846a fix: default cost to NaN when calculation fails in LiteLLM backend (#912)
* fix: default cost to NaN when calculation fails in LiteLLM backend

* lint

* lint
2025-05-29 22:58:29 +08:00
you-n-g 4974277f1c feat: add last_exp_fb to DSTrace and update feedback retrieval usage (#910)
* feat: add last_exp_fb to DSTrace and update feedback retrieval usage

* fix: use trace.last_exp_fb for previous trial feedback description
2025-05-29 20:32:06 +08:00
Tim a7c6fca8b4 chore: withdraw for policy error (#907)
* chore: withdraw for policy error

Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>

* Apply suggestions from code review
2025-05-29 17:23:06 +08:00
Tim 1091d5ea29 fix: use trace count as index (#909) 2025-05-29 16:52:37 +08:00
XianBW 63ebdb5ada fix loop_num calc logic (#911) 2025-05-29 16:40:57 +08:00
Yuante Li d6ce70b551 feat: add RD-Agent-Quant scenario (#838)
* fix model input shape bug and costeer_model bug

* fix a bug

* fix a bug in docker result extraction

* a system-level optimization

* add a filter of stdout

* update

* add stdout to model

* model training_hyperparameters update

* quant scenario

* update some quant settings

* llm choose action

* Thompson Sampling Bandit for action choosing

* refine both scens

* add trace messages for quant scen

* fix some bugs

* fix some bugs

* update

* update

* update

* fix

* fix

* fix

* update for merge

* fix ci

* fix some bugs

* fix ci

* fix ci

* fix ci

* fix ci

* refactor

* default qlib4rdagent local env downloading

* fix ci

* fix ci

* fix a bug

* fix ci

* fix: align all prompts on template (#908)

* use template to render all prompts

* fix CI

---------

Co-authored-by: Xu Yang <xuyang1@microsoft.com>

* add fin_quant in cli

* fix a bug

* fix ci

* fix some bugs

* refactor

* remove the columns in hypothesis if no value generated in this column

* fix a bug

* fix ci

* fix conda env

* add qlib gitignore

* remove existed qlib folder & install torch in qlib conda

* fix workspace ui in feedback

* align model config in coder and runner in docker or conda

* fix CI

* fix CI

---------

Co-authored-by: Xu Yang <peteryang@vip.qq.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-05-29 16:16:51 +08:00
Haoran Pan 628deade72 feat: enhance compatibility with more LLM models (#905)
* add try-except to avoid retry when using completion_cost

* feat: add JSON prompt injection and think tag removal handling

* refactor: simplify cost handling in LiteLLM backend and clean up style

* docs: clarify purpose of reasoning_think_rm in LLMSettings

* refactor: remove unused *args and redundant cost assignment

---------

Co-authored-by: Young <afe.young@gmail.com>
2025-05-29 15:21:34 +08:00
Roland Minrui 29c245c6e3 fix: fix the problems weights bug (#898)
* fix the problems weights bug

* refactor: remove DSExpGen

* update problems weights calculation

* update problems weights calculation

* remove the selection parameter from exp_gen

* v2 support draft

* v3 also support decomposition

* make the identify_problems an independent function

* fix minor bug

* reformat

* rename exp_num to weighted_exp_num

* add the set_current_selection before the exp_gen when merging

* reformat

* fix wrong selection

* refactor: drop selection arg from ExpGen.gen and DS merge generators

---------

Co-authored-by: Xu <v-xuminrui@microsoft.com>
Co-authored-by: Young <afe.young@gmail.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-05-28 11:14:37 +08:00
amstrongzyf 2e39700eeb docs: add docstring for confusing columns in logs dir (#902)
* Docs updated

docs: add docstring for confusing columns in ds-summary

* update

* update black format
2025-05-27 20:54:32 +08:00
Roland Minrui 370adbfa1e fix: refine feedback prompt (#901)
* feedback observation must base on evidence

* avoid too strong constrain

---------

Co-authored-by: Xu <v-xuminrui@microsoft.com>
2025-05-27 19:41:23 +08:00
Tim dbaf70ce57 chore: add kill after for timeout (#899) 2025-05-27 12:02:22 +08:00
Haoran Pan 3c6f9ef76d docs: update guidance for running mle-bench (#896)
* update guidance for running mle-bench

* ci issue

* Update guidance for setting kaggle api
2025-05-26 17:43:43 +08:00
XianBW 0109b044a0 add select_best param for log.ui.utils.compare tool function (#897) 2025-05-23 18:32:31 +08:00
Yuante Li 5a62b47e95 fix: fix the bug in the regular expression matching for stdout (#890) 2025-05-23 14:13:31 +08:00
Tim 5b47c0ab5c feat: raise policy violation (#894) 2025-05-22 16:54:34 +08:00
xuangu-fang 3e60749f70 fix: fix the bug of Exceed-LLM-Context in online merge of multi-tarce (#892)
* set constrains on max_sota_retrieved, fix logis on identical problem

* fix: only Auto SOTA selector use max_sota_retrieved_num

* set max_sota_retrieved_num=10 by default

* minor update

* auto lint
2025-05-22 16:10:00 +08:00
you-n-g cfa5f382aa docs: update paper report (#893)
* doc: update paper report

* Update README.md

* Update README.md

* Update README.md
2025-05-22 15:37:34 +08:00
Yuge Zhang 9ea44be5f8 feat: new proposal (structured outputs) prompts (#887)
* some initial modifications

* first prompt

* refine prompts

* isolate to v3 exp gen

* Revert prompts

* Add hypothesis

* task gen prompt

* minor updates

* minor updates

* update proposal

* Move the pydantic schema upfront to avoid loading error

* Update first prompts

* New prompt

* Update prompt

* Update data folder

* .

* Revert changes

* support v3 in pipeline and feedback

* sort imports

* black format

* Update rdagent/scenarios/data_science/dev/prompts.yaml

Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>

---------

Co-authored-by: Xu Yang <peteryang@vip.qq.com>
Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>
2025-05-22 15:08:33 +08:00
XianBW 37e9912ac0 chore: organize the tool functions related to trace and summary (#889)
* provide get metric direction in kaggle_crawler.py

* move utils function

* move statistics logic

* fix CI

* fix CI

* fix CI

* fix CI

* move some tool functions

* fix CI

* move curves win

* add compare tool

* change function name

* fix CI
2025-05-21 17:20:31 +08:00
xuangu-fang bc9cee49ba feat: multi-trace online merge (#886)
* prompt: highlight overfitting rist in AutoSOTAexpSelector

* set online merge time in conf

* online multi-trace merge with time-limit policy

* fix typo

* feat: allow soft-knowledge-base + multi_trace

* fix: improve file tree and _walk symlink handling (#877)

* refactor: improve file tree and _walk symlink handling

* remove unused code

* lint

* prompt: highlight overfitting rist in AutoSOTAexpSelector

* set online merge time in conf

* online multi-trace merge with time-limit policy

* fix typo

* feat: allow soft-knowledge-base + multi_trace

* auto-lint

* put the multi-trace related config together

---------

Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>
2025-05-19 17:59:42 +08:00
Linlang 763dff1596 fix mypy check dependence error (#885) 2025-05-18 15:15:32 +08:00
XianBW fe11ee92e6 chore: ui bug fix (#884)
* bug fix

* bug fix

* test ci

* bug fix

* ci fix

* ci fix
2025-05-18 11:08:41 +08:00
XianBW 2c11752a54 chore: log storage refine (#883)
* log storage change

* fix bug

* fix ci
2025-05-16 18:29:59 +08:00
you-n-g 7aea9a27dc fix: use fallback messages for missing submission and scores files (#882) 2025-05-16 14:39:47 +08:00
Linlang 0c97fe4ae7 update_QR_code_url (#881) 2025-05-16 14:13:40 +08:00
you-n-g 586349af36 fix: update DS env setup with competition volume and timeout (#878)
* refactor: update DS env setup with competition volume and timeout

* refactor: update volume mapping and timeout based on run type
2025-05-16 02:05:15 +08:00
you-n-g 5b3f5f66cf fix: improve file tree and _walk symlink handling (#877)
* refactor: improve file tree and _walk symlink handling

* remove unused code

* lint
2025-05-16 00:11:25 +08:00
Xu Yang 69e5c3a7de doc: small typo in README (#876) 2025-05-15 21:40:39 +08:00
cslwqxx b76abe839b Update README.md (#875) 2025-05-15 21:32:20 +08:00
Xu Yang c8c8e26812 add multi trace results (#873)
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-05-15 20:29:52 +08:00
XianBW 42ea6f106b fix debug_tpl and debug_llm saving method(#874) 2025-05-15 18:37:51 +08:00
you-n-g 7742a9e759 docs: update README.md (#871) 2025-05-14 11:32:23 +08:00
you-n-g 8acd24a016 chore: news MLE-bench release (#870)
* docs: add MLE-bench details to README

* docs: update README with revised MLE-bench description and leaderboard

* docs: update RD-Agent text and add trial info in README

* Update README.md

* Update README.md

* update by M

* update format

* Add documents

* docs: update RD-Agent references to R&D-Agent

* docs: update README with MLE-Bench complexity level details
2025-05-14 11:21:13 +08:00
you-n-g 54465d6e82 feat: add competition level filter and extract constants to utils (#869)
* feat: add competition level filter and extract constants to utils

* lint
2025-05-12 21:56:57 +08:00
Roland Minrui 90f6c94d3e fix coder bug (#868)
Co-authored-by: Xu <v-xuminrui@microsoft.com>
2025-05-12 17:20:26 +08:00
Tim 7f4e5096e8 chore: custom data refine (#864)
* chore: print up to 100 columns in simple mode

* fix: check content for model dump

* chore: add show_nan_columns config
2025-05-10 17:02:12 +08:00
you-n-g a294bc74e0 feat: truncate by time (#863)
* refactor: move session file lookup logic to folder utils module

* print more info

* lint
2025-05-10 00:42:59 +08:00
xuangu-fang e71d8f6c3c feat: advanced checkpoint selectors (#790)
* rebase selection code

* bug-free run: checkpoint selection and dynamic EDA loading

* add prototypes of various selectors, to imp. and test later

* fix EDA write bug

* imp SOTA-Jump policy

* fix small bug

* allow to set different selector by .env

* add always-win selector

* add init length for AlwaysWinCKPSelector

* add back_jump selector

* auto lint

* add sota_exp_to_submit attribute; change the name of ckp_selector and sota-selector

* fix bug

* auto lint

* working on auto sota selector

* add subtrace counter

* fix bug, remove unuse selector

* add auto sota selector

* auto lint

* fix bug

* fix small logic bug

* add logging

* add inject_diverse feat

* auto lint

* capable to None-select

* feat: add hypothesis_gen config and ExpGen2TraceAndMerge functionality

* refactor: use dynamic import for experiment generator instantiation

* feat: add BestValidSelector for improved SOTA experiment selection

* runnable twin-trace version

* fix logic error of trace-merge

* auto lint

* use import_class to set selector,

* auto-lint

---------

Co-authored-by: Young <afe.young@gmail.com>
2025-05-09 15:38:25 +08:00
Xu Yang 5c7cdf298c fix: typo in workflow (#861) 2025-05-09 13:29:36 +08:00
Xu Yang 8a0a0fa3c4 feat: log api status to mlflow (#860)
* log api status to mlflow to frontend tracking

* fix CI

---------

Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-05-09 12:20:49 +08:00
Xu Yang 69c630e983 make timeout fail a hyper parameter (#859)
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-05-08 21:30:44 +08:00
XianBW ad12fccb61 change method of cut log trace (#858) 2025-05-08 21:07:33 +08:00
Roland Minrui 9986b5f9ce fix: refine the time/memory constraints prompt in hypothesis proposal (#856)
* refine prompt

* refine the wording

* add ratelimit retry to align with the suggested wait seconds

* add max retry to 0

* don't delete hist

---------

Co-authored-by: Xu <v-xuminrui@microsoft.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-05-08 21:05:07 +08:00
Linlang eb1cefb202 fix: fixed CI execution failures caused by document builds (#857)
* fixed CI execution failures caused by document builds

* add comments
2025-05-08 20:35:13 +08:00
XianBW 4f5cce0607 add cache for summary, remove workspace dependency when generate summary (#854) 2025-05-08 12:24:48 +08:00
you-n-g 495063c385 fix: adjust ds_trace lookup and add stderr redirect to mlebench command (#853)
* fix: adjust ds_trace lookup and add stderr redirect to mlebench command

* style: reformat SOTA experiment lookup in ds_trace.py

* feat: add DS_RD_SETTING pipeline to MergeExpGen success message
2025-05-08 02:40:49 +08:00
Xu Yang 318acb4922 fix: trace list but (#852) 2025-05-07 21:28:24 +08:00
XianBW 8340ab5d09 add aide.py (#851) 2025-05-07 19:59:40 +08:00
Xu Yang 22a0c7fe56 feat: revert draft stage into a soft decay in hypothesis selection (#849)
* revert drafting

* update hypothesis rank logic

* prioritize time constraint in task design

* refine trace_desc and feedback problem prompt

* refine experiment_and_feedback_list_after_init

* fix DSHypothesis default parameter and print logic

* refine the selection weight

* merge simple_trace and trace

* refine weight and prompt

* refine sample logic

* fix CI

* robust code

---------

Co-authored-by: WinstonLiyte <1957922024@qq.com>
Co-authored-by: Xu <v-xuminrui@microsoft.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-05-07 19:05:50 +08:00
Tim 02534cfd88 chore: dump test data (#850)
* debug path for model dump
* update prompt
2025-05-07 17:24:50 +08:00
you-n-g 9a8426f373 fix: non-exist variable test_eval.py (#847) 2025-05-07 04:36:00 +08:00
you-n-g 6eef7517ad fix: wrong variable test_eval.py (#846) 2025-05-07 04:17:57 +08:00
XianBW 29a14e1e22 fix bug (#845) 2025-05-06 18:15:20 +08:00
Tim 6f9843a7df feat: custom data (#810)
* custom data

* fix: simplify competition check and log local description file

* no sample data

* feat: add test evaluation module with error handling support

* fix: update eval path to use eval_sub_dir and add valid_check TODO

* refactor: add MLETestEval check to conditionally run grading steps

* avoid blank stdout

* valid in testeval

* rename test.csv to avoid conflict

* Support Disabling sample submission

* refactoring

* fix: remove DS_KAGGLE_DATA and update prompt instructions

* add try for grade

* ignore submission

* fix: remove tee from eval command and warn about pipeline exit code detection

* optional to use raw description

* support old data

* add execution result to stdout

* add metric to raw description

* custom data explain

* add debug_path

* rst update

---------

Co-authored-by: Young <afe.young@gmail.com>
2025-05-06 16:00:13 +08:00
XianBW aa97d5a09d chore: ui change (#844)
* ui change

* ui change
2025-05-06 11:34:21 +08:00
you-n-g 6b7988fba1 feat: refine merge (#842)
* feat: add search_type param and customize success trial desc

* docs: simplify trial descriptions in share.yaml

* lint
2025-05-04 23:17:58 +08:00
you-n-g 93303873d0 fix: adapting UI to mock trace (#841)
* fix: return first index if 'SOTA Exp Score (valid)' is empty

* feat: add get_state_data_range helper for loops and slider bounds

* fix: exclude batch embedding tag from log filtering

* feat: add feedback support in sota_experiment and adjust merge flow

* fix: use fb function for merging experiments

* style: remove extra whitespace and reformat code
2025-05-04 15:51:32 +08:00
Xu Yang d0e3fc1573 fix new draft bugs (#840) 2025-04-30 18:53:27 +08:00
Haoran Pan cbedbc7d54 feat: reanalyze competition info & pipeline coding evaluator prompt (#837)
* update coding evaluator prompt similar to feedback

* reanaylyzing competition description when three sonsecutive coding failures

* update reanalyzing competition implementation

* fix bug

* update prompts and reanalyze

* fix bugs

* ci issue

* improve some code

* fix CI

---------

Co-authored-by: Xu Yang <peteryang@vip.qq.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-04-30 17:36:35 +08:00
Roland Minrui 03040de35f feat: add drafting pipeline (#832)
* init commit

* add drafting prompt

* complete the drafting

* remove scenario problems from proposal

* rename prompts_drafting.yaml

* fix bug

* fix DSHypothesis print bug

* add failed drafting exp to prompt

* fix small bug

* use get_task_information() for task design

* resolve all comments

* add problem_desc to pesudo hypothesis

---------

Co-authored-by: Xu <v-xuminrui@microsoft.com>
2025-04-30 16:25:32 +08:00
you-n-g a73f2dbc12 feat: trace merging (#836)
* feat: runnalbe -- add exp_gen_cls param, get_leaves and merge exp gen functionalities

* fix: remove unused scenario_desc and update YAML task labels

* feat: override selection and update merge task description

* lint

* lint

* lint

* lint

* lint

* fix: log competition setting to enable mle_summary

* fix name error
2025-04-29 09:30:45 +08:00
Xu Yang 45931b1012 when restart with kb and different pkl path, we need to initialize the kb from json file (#835) 2025-04-28 15:51:51 +08:00
Tim 6c179442bf chore: log cost object (#829) 2025-04-25 19:26:08 +08:00
Xu Yang 8affbf89a9 add pipeline to hypothesis spec (#828) 2025-04-25 18:29:14 +08:00
Roland Minrui 8968ab8c9a feat: propose hypothesis across multiple parts in pipeline (#827)
* refine hypothesis for pipeline

* refine component selection prompt

* update feedback prompt

* remove feat_eng in pipeline coding

---------

Co-authored-by: Xu <v-xuminrui@microsoft.com>
Co-authored-by: Xu Yang <peteryang@vip.qq.com>
2025-04-25 17:17:53 +08:00
Xu Yang c89321f5a2 fix: add time to timer when api timeout bug (#826)
* If not use the session stored timer, need to replace it with the default timer

* when api timeout, add the waiting time to timer
2025-04-25 10:50:27 +08:00
Xu Yang d6990a12b8 If not use the session stored timer, need to replace it with the default timer (#825) 2025-04-25 00:39:31 +08:00
Yuante Li 86e05e6d35 fix: fix a bug in docker result extraction (#824)
* fix a bug in docker result extraction
2025-04-24 19:02:38 +08:00
XianBW fda5c281a8 can set reasoning_effor=None in chat_model_map (#823) 2025-04-24 18:47:24 +08:00
XianBW 4f2a915da5 feat: using different chat model in different part (#822)
* using model in the chat_model_map in one tag

* add replace timer to DS loop

* fix CI

* fix CI

* add more custom config in chat_model_map

* fix CI

* fix CI

* fix CI

---------

Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-04-24 18:22:51 +08:00
Yuante Li ff99e4dc2c fix: fix model input shape bug and costeer_model bug (#821)
* fix model input shape bug and costeer_model bug

* fix a bug
2025-04-24 12:48:25 +08:00
Xu Yang c5beaeeaf8 fix: update runner max loop to 1 in DS scenario (#820) 2025-04-23 19:15:29 +08:00
Yuante Li 82b037cb63 fix: fix some minor bugs in qlib scenario (#817)
* fix some bugs

* fix a bug

* fix a bug in qlib frontend

* fix ci

* fic ci

* fix qlib Dockerfile
2025-04-23 18:48:02 +08:00
Haoran Pan 456ed100a0 update qrcode (#818) 2025-04-23 16:12:48 +08:00
Yuante Li 0798c05cb7 fix: improve eval alignment check (e.g. small-scale finetuning) (#802)
* fix

* fix
2025-04-22 18:47:24 +08:00
Xu Yang 7dbffc516d feat: add mlflow logger in RD loop to log (#815)
* add mlflow logger in DS loop

* fix CI

* fix CI

---------

Co-authored-by: Xu Yang <xuyang1@microsoft.com>
2025-04-22 01:23:40 +08:00
Xu Yang 4509d92ea7 feat: archive python and csv files in workspace to maintain results (#814)
* archive workspace also

* remove non python csv and md files in workspace to avoid big workspace dump

* FIX ci
2025-04-21 16:34:29 +08:00
XianBW 19bb2e740e fix: align competion_full_desc and scenario_all_desc, remove redundant info in problems proposal (#808)
* align competition desc & scenario desc string

* remove competition_desc when having used scenario_desc in problem gen

* fix bug

* remove redundant competition desc in naive expgen

* improve proposal prompt

* modify phrase

---------

Co-authored-by: Xu Yang <peteryang@vip.qq.com>
2025-04-18 16:48:26 +08:00
XianBW 2fea82bee7 add select lite and select best button (#809) 2025-04-18 16:28:31 +08:00
Xu Yang c68b597069 fix: bug fix in timer start (#807) 2025-04-18 16:16:28 +08:00
Xu Yang bbb9d36f77 fix: bug in problem identification (#806) 2025-04-18 15:46:11 +08:00
Roland Minrui e44bb2dcc1 feat: idea pool integrated to exp_gen & add timer to RD-Agent & pause-resume to RD-loops (#795)
* update all code

* update all code

* dump knowledge base

* rename the tag

* add timer to RD-Agent

* fix CI

* fix CI

* use batch embedding

* fix a small bug

* fix prompt bug

* feat: add pause resume to handle K8S cluster pause (#804)

* add resume to cluster running

* fix non-pickle problem

* fix a small bug

* fix a small bug

* avoid shutil move error

* refine the logic

* move knowledge base out of session

* avoid mistake information to pipeline coding

* avoid load and dump in steps

* archive the right folder

* small improvement

* avoid restart when timer is already started

* fix CI

---------

Co-authored-by: Xu Yang <xuyang1@microsoft.com>

---------

Co-authored-by: Xu Yang <peteryang@vip.qq.com>
Co-authored-by: Xu Yang <xuyang1@microsoft.com>
Co-authored-by: Xu <v-xuminrui@microsoft.com>
2025-04-18 14:01:03 +08:00
Linlang 08fc2dcef3 chore: modify kaggle docs & Adding ds_loop at program entry (#786)
* modify kaggle docs

* optimise code based on comments

* Update docs/scens/kaggle_agent.rst

* fix docs build error

---------

Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>
2025-04-17 22:02:49 +08:00
you-n-g c2759a2a0a refactor: use remove_eda_part for EDA cleanup, fix diff eval (#800) 2025-04-17 16:19:26 +08:00
Linlang 05092c5c5b update wechat qrcode (#798) 2025-04-16 21:39:56 +08:00
XianBW d9cee55063 fix retry when hypothesis gen (#796) 2025-04-16 19:01:46 +08:00
you-n-g fe3337d6ff refactor: use dynamic input path and update template loader (#792)
* refactor: use dynamic input path and update template loader

* fix: update include syntax for data source in prompts.yaml

* add customization path

* docs: update prompts for ensemble scoring and metric direction

* chore: remove obsolete data_science/share.yaml file
2025-04-16 18:11:46 +08:00
Xu Yang 90f6fde3d4 feat: raise error when timeout in api call (#793)
* small change to try catch in backend

* fix CI

* add timeout tolerance to 3
2025-04-16 13:50:20 +08:00
you-n-g 0e3dcc2d8d feat: refine prompt (#760)
* style: Simplify language and improve clarity in prompts and share.yaml

* style: Update prompt wording for clarity in raw_data_loader

* style: Simplify conditional logic in task_gen system prompt

* refactor: Update prompts and proposal for component output format handling

* fix: Correct grammar and add clarification in prompts.yaml

* feat: Include coding guidelines in data science component prompts

* lint
2025-04-16 09:35:30 +08:00
you-n-g aedd0a0f72 fix: update metric direction to return bool (#791) 2025-04-15 17:10:31 +08:00
Roland Minrui 214da4826f fix: add wait_retry to exp_gen v2 (#783)
* add wait retry to v2

* format

* fix a bug

---------

Co-authored-by: Xu <v-xuminrui@microsoft.com>
Co-authored-by: yuanteli <1957922024@qq.com>
2025-04-14 11:02:45 +08:00
you-n-g f25d729a02 fix: import path of T (#787)
* feat: add DocDev for auto-generating workspace documentation

* fix: update markdown instructions in tpl.yaml

* feat: add enable_doc_dev flag and conditionally call DocDev

* refactor: update T import and prompt keys for DocDev

* fix: update include path for MarkdownOut template

* fix: update file search, README injection, and brief prompt text

* docs: update prompt to only introduce models

* lint
2025-04-13 10:21:24 +08:00
Yuante Li 773b7ac439 fix: fix competition metric direction (#784) 2025-04-11 16:25:30 +08:00
Yuante Li 19dd259165 refactor: refactor feedback logic and fix bug in task_gen (#782)
* task gen refine & feedback refine

* fix

* refine feedback logic

* fix a bug

* fix

* fix

* fix ci
2025-04-11 15:52:52 +08:00
you-n-g ab52e1b65c feat: add DocDev for auto-generating workspace documentation (#781)
* feat: add DocDev for auto-generating workspace documentation

* fix: update markdown instructions in tpl.yaml

* feat: add enable_doc_dev flag and conditionally call DocDev
2025-04-10 20:12:21 +08:00
Xu Yang 959f2fa076 still assign five component to pipeline (#780) 2025-04-10 18:30:32 +08:00
Tim f06e6986c8 chore: fit more competition (#723)
1. Remove potential <code> tags from the generated code.
2. Use return codes in data_loader, feature, and model.
3. Configure the debug timeout.
2025-04-10 17:56:57 +08:00
Tim b5631b9f78 feat: pull image with progress (#777)
pull image with progress
2025-04-10 12:00:27 +08:00
you-n-g b662f45021 feat: dump model (#776)
* feat: add model dump flag and multi-evaluator support

* tmp code

* refactor: update evaluator feedback and FBWorkspace types

* feat: add get_clear_ws_cmd and CPU count in Docker environment

* feat: Add model dump check level and enhance evaluator functionality

fix data type bug

* fix: Ensure required files exist before model dump evaluation

* refactor: streamline prompt and file checks in model dump evaluation

* fix: add assertions and reorder file reads in model dump evaluator

* feat: remove EDA part from evaluation output

* docs: update dump_model guidelines and eval prompt to include template

* style: reformat multiline dicts and lists in conf and eval files

* fix: add DOTALL flag to EDA removal regex
2025-04-09 23:24:12 +08:00
XianBW 92d007e179 ui updates (#778) 2025-04-09 20:11:00 +08:00
you-n-g c40f22361c refactor: update MLEBench submission validation logic in runner and eval (#769)
* refactor: Update MLEBench submission validation logic in runner and eval

* lint

* Delete docs/scens/data_science.rst

* fix: update variable name in feedback prompt

* define a variable: submission_check_out

* fix: update submission check handling for MLE data

* refactor: reformat if condition for clarity

---------

Co-authored-by: Linlang <Lv.Linlang@hotmail.com>
2025-04-09 13:34:48 +08:00
Linlang e22fb84fb3 update wechat qrcode (#775) 2025-04-09 12:23:38 +08:00
XianBW 80c5eb209e catch llm_data pkl read exception (#774) 2025-04-09 12:21:54 +08:00
xuangu-fang 67114540b1 feat: checkpoint selection (#744)
* rebase selection code

* bug-free run: checkpoint selection and dynamic EDA loading

* add prototypes of various selectors, to imp. and test later

* fix EDA write bug

* move selector to from proposal.py tp seletc.py

* auto lint

* fix line-too-long typos

* aligh the design of "selection", rm extra instance check

* make auto-lint

* add non-trival selector: SOTAjump
2025-04-09 09:42:30 +08:00
Xu Yang 8517eb4a18 fix: update feedback.py (#772) 2025-04-08 23:35:11 +08:00
Xu Yang a16c79e45b small bug fix in feedback (#771) 2025-04-08 22:17:47 +08:00
XianBW ce23e1c30b chore: data science scenario UI updates (#770)
* fix some pandas warning

* fix load time logic

* optimize load_time logic

* fix CI

* fix bug
2025-04-08 19:33:20 +08:00
Xu Yang 92e7932126 add rule-based eval to speed up the whole process (#768) 2025-04-08 17:27:21 +08:00
Yuante Li 31752bb8f0 feat: merge failed and successful traces together (#766)
* merge failed and successful traces together

* delete the task description from the trace display

* prune unnecessary info for the proposal stage
2025-04-08 17:04:55 +08:00
Yuante Li 7372d1011e feat: add reviewer in feedback (#765) 2025-04-08 16:20:13 +08:00
XianBW a918f941dd add hypothesis table (#764) 2025-04-07 20:16:37 +08:00
XianBW dee11fbd9c fix: duplicate model names test in pipeline coder & runner (#763)
* add model name duplicate test in pipeline costeer

* fix ci
2025-04-07 19:06:42 +08:00
you-n-g 0dcc990bd4 fix: Set PYTHONPATH in env.run_ret_code call in FBWorkspace class (#755) 2025-04-07 18:15:33 +08:00
XianBW 3f41e868b3 chore: data science scenario UI updates (#757)
* ui changes

* add ours vs medal threshold

* add success loop statistic of components

* show times info

* UI updates

* summary selected

* change colors

* fix CI

* add stat hours param for `mle_summary.py --summary` command

* add 24h summary button

* fix CI

* add logger info for dockerEnv/condaEnv running time
2025-04-07 16:24:27 +08:00
Roland Minrui fac39e5588 remove buggy info in hypothesis rank (#762)
Co-authored-by: Xu <v-xuminrui@microsoft.com>
2025-04-07 14:09:36 +08:00
Xu Yang fce5342ad4 pretrained & validation alignment (#761) 2025-04-07 13:29:41 +08:00
you-n-g 44ada185f4 feat: add naive experiment generator and update proposal configurations (#759)
* feat: Add naive experiment generator and update proposal configurations

* lint

* lint
2025-04-07 13:01:29 +08:00
Yuante Li 6818ada2cf fix: fix some minor bugs (#758) 2025-04-04 23:54:54 +08:00
Yuante Li 11e8acc88b feat: add a check for whether values in score_df are NaN (#756)
* add a check for whether values in score_df are NaN

* fix ci

* change raise to assert
2025-04-04 22:44:55 +08:00
you-n-g 9415113e01 feat: add reasoning_effort parameter to LiteLLMAPIBackend and LLMSett… (#754)
* feat: Add reasoning_effort parameter to LiteLLMAPIBackend and LLMSettings

* style: Use consistent quotation marks for reasoning_effort literals
2025-04-04 19:37:40 +08:00
Xu Yang d3414ea07d fix: task_gen for better understanding (#752) 2025-04-04 17:09:26 +08:00
Tim 8bd2714814 feat: joblib cache (#749)
* cache function

* fix test

* bin cache

* fix test

* fix test

* fix test

* cache for different source

* cache for localenv

* remove unnecessary log

* reformat

* remove unrelated modify
2025-04-04 12:08:18 +08:00
Tim 200764d38b workflow entrypoint check (#748) 2025-04-04 12:07:46 +08:00
226 changed files with 14275 additions and 5141 deletions
+10
View File
@@ -0,0 +1,10 @@
# 1. Pull down your Azure Container Registry image
FROM rdagentappregistry.azurecr.io/rd-agent-mle:20250623
# 2. (Optional) install any additional tools you need
# e.g. git, bash-completion, etc.
# RUN apt update && \
# apt install -y git bash-completion && \
# rm -rf /var/lib/apt/lists/*
RUN apt update && \
apt install -y git bash-completion
+30
View File
@@ -0,0 +1,30 @@
# Introduction
!!!!!This dev container is not for public development!!!!!!
!!!!!Please don't use it if you are just a public open-source user.!!!!!!
# Steps to run the dev container (for internal use only)
Prerequisites(this is the reason why this dev container is not for public use):
- Make sure you have the `rdagentappregistry.azurecr.io/rd-agent-mle:20250623` image locally & DevContainer is installed in your IDE
- The kaggle dataset is located at `/home/shared/RD-Agent/kaggle`
1. Open the project and select "Open In DevContainer"
2. Set up your Kaggle Key (do not share this; other internal URLs are hardcoded in the config files)
```bash
export KAGGLE_USERNAME=
export KAGGLE_KEY=
```
3. Run: python rdagent/app/data_science/loop.py --competition nomad2018-predict-transparent-conductors
# Additional Notes
- Please install and use this Dev Container in VS Code.
- You **must open VS Code remotely and enter the `RD-Agent` directory before running the DevContainer configuration (`.devcontainer/devcontainer.json`)**. Otherwise, the workspace and path mappings will not work as expected.
- To open the DevContainer correctly in VS Code:
1. Remotely connect to the machine and open the `RD-Agent` folder in VS Code.
2. Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac), type and select **"Dev Containers: Reopen in Container"**.
+29
View File
@@ -0,0 +1,29 @@
{
"name": "rd-agent-mle DevContainer",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"workspaceFolder": "/workspace/RD-Agent",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace/RD-Agent,type=bind,consistency=cached",
"remoteUser": "root",
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"mounts": [
"source=/home/shared/RD-Agent/kaggle,target=/home/shared/RD-Agent/kaggle,type=bind,consistency=cached,readonly"
],
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-toolsai.jupyter"
],
"runArgs": [
"--init",
"--shm-size=1g",
"--env-file", "${localWorkspaceFolder}/.devcontainer/env",
"--network=host",
"--gpus=all"
],
"postCreateCommand": "make dev"
}
+47
View File
@@ -0,0 +1,47 @@
# Global configs:
MAX_RETRY=12000
RETRY_WAIT_SECONDS=5
TIMEOUT_FAIL_LIMIT=100
# litellm
# CHAT_MODEL=gpt-4o
# CHAT_TEMPERATURE=0.7
CHAT_STREAM=False
CHAT_TEMPERATURE=1
CHAT_MODEL=o1-preview
SYSTEM_PROMPT_ROLE=user
BACKEND=rdagent.oai.backend.LiteLLMAPIBackend
OPENAI_API_KEY=sk-1234
OPENAI_API_BASE=http://ep14.213428.xyz:38881
# amc chat model configs:
EMBEDDING_MODEL=text-embedding-ada-002
# Cache Setting (Optional):
DUMP_CHAT_CACHE=True
USE_CHAT_CACHE=False
DUMP_EMBEDDING_CACHE=True
USE_EMBEDDING_CACHE=False
LOG_LLM_CHAT_CONTENT=True
DS_LOCAL_DATA_PATH=/home/shared/RD-Agent/kaggle
DS_IF_USING_MLE_DATA=True
PICKLE_CACHE_FOLDER_PATH_STR=./log/pickle_cache
CACHE_WITH_PICKLE=False
ENABLE_CACHE=False
PROMPT_CACHE_PATH=./log/prompt_cache.db
DS_CODER_COSTEER_ENV_TYPE=conda
DS_PROPOSAL_VERSION=v2
DS_CODER_ON_WHOLE_PIPELINE=True
COSTEER_V2_QUERY_FORMER_TRACE_LIMIT=3
# export PYTHONPATH=. # this is for running researcher branch;
+39 -11
View File
@@ -7,25 +7,53 @@ For more information about configuration options, please refer to the documentat
"""
# ==========================================
# Global configs:
USE_AZURE=False
CHAT_USE_AZURE_TOKEN_PROVIDER=False
EMBEDDING_USE_AZURE_TOKEN_PROVIDER=False
MAX_RETRY=10
RETRY_WAIT_SECONDS=20
# ==========================================
# LLM API Setting:
OPENAI_API_KEY=<your_api_key>
CHAT_MODEL=gpt-4-turbo
CHAT_MAX_TOKENS=3000
CHAT_TEMPERATURE=0.7
# ==========================================
# Backend Configuration
# ==========================================
# BACKEND=rdagent.oai.backend.LiteLLMAPIBackend
# ==========================================
# ==========================================
# Backend Configuration (choose one)
# ==========================================
# 1. Set universal API key
# CHAT_MODEL="gpt-4o"
# EMBEDDING_MODEL="text-embedding-3-small"
# OPENAI_API_BASE="https://your-endpoint.com/v1"
# OPENAI_API_KEY="sk-your-api-key-here"
# 2. Set separate API KEY
# Chat configuration
OPENAI_API_KEY="sk-chat-key"
OPENAI_API_BASE="https://xxx-litellm.com/v1"
CHAT_MODEL='gpt-4o'
# Embedding configuration (using other service)
# Use siliconflow as example, pay attention to the litellm_proxy prefix
LITELLM_PROXY_API_KEY="sk-embedding-service-key"
LITELLM_PROXY_API_BASE="https://api.siliconflow.cn/v1"
EMBEDDING_MODEL="litellm_proxy/BAAI/bge-large-en-v1.5"
# ==========================================
# ==========================================
# Other Configuration
# ==========================================
# CHAT_AZURE_API_BASE=<for_Azure_user>
# CHAT_AZURE_API_VERSION=<for_Azure_user>
EMBEDDING_MODEL=text-embedding-3-small
# EMBEDDING_AZURE_API_BASE=<for_Azure_user>
# EMBEDDING_AZURE_API_VERSION=<for_Azure_user>
# Cache Setting (Optional):
# Senario Configs:
# USE_CHAT_CACHE=True
# USE_EMBEDDING_CACHE=True
# Senario Configs:
# ==========================================
+5
View File
@@ -112,6 +112,7 @@ celerybeat.pid
# Environments
.env*
*.env
.venv
^env/
venv/
@@ -173,3 +174,7 @@ mlruns/
/*.sh
.aider*
rdagent/app/benchmark/factor/example.json
# UI Server resources
videos/
static/
+138
View File
@@ -1,5 +1,143 @@
# Changelog
## [0.7.0](https://github.com/microsoft/RD-Agent/compare/v0.6.1...v0.7.0) (2025-07-08)
### Features
* add code change summary ([#1000](https://github.com/microsoft/RD-Agent/issues/1000)) ([937ec26](https://github.com/microsoft/RD-Agent/commit/937ec263b215928633822c4d76ad4e47442c8198))
* add hide_base_name option and update data folder prompts ([#1004](https://github.com/microsoft/RD-Agent/issues/1004)) ([2f61fa8](https://github.com/microsoft/RD-Agent/commit/2f61fa8cd90c91ad29f320ce9ea6c49f49ac9111))
* added running time statistics for the DS scenario experiment ([#1007](https://github.com/microsoft/RD-Agent/issues/1007)) ([030abd8](https://github.com/microsoft/RD-Agent/commit/030abd87191377641a678c80852f5ecad84e7a6e))
* merge code summary and support more traces ([#1025](https://github.com/microsoft/RD-Agent/issues/1025)) ([48201e7](https://github.com/microsoft/RD-Agent/commit/48201e79b55ff5a98dad51702a7d0ac6b1ddc9eb))
* show first evo round codes diff ([#1009](https://github.com/microsoft/RD-Agent/issues/1009)) ([4844622](https://github.com/microsoft/RD-Agent/commit/4844622e5fd28d7cbaabd9d7888f8204c60b76b3))
* try coder on whole data ([#1017](https://github.com/microsoft/RD-Agent/issues/1017)) ([4973e05](https://github.com/microsoft/RD-Agent/commit/4973e0532248c6172eec3bb70dffda052af2d14f))
### Bug Fixes
* fix a minor bug in DS eval ([#1012](https://github.com/microsoft/RD-Agent/issues/1012)) ([5a520e9](https://github.com/microsoft/RD-Agent/commit/5a520e9d44899d44fddc0f2e5571596223161b71))
* fix some bugs in quant scen ([#1026](https://github.com/microsoft/RD-Agent/issues/1026)) ([7b34d41](https://github.com/microsoft/RD-Agent/commit/7b34d418642d1c0c2986db9ecf6a5d9bc22cc3da))
* support experimental support for Deepseek models and update docs about configuration ([#1024](https://github.com/microsoft/RD-Agent/issues/1024)) ([35cfc19](https://github.com/microsoft/RD-Agent/commit/35cfc193f9b35d786aeb7585334427ad358c982f))
## [0.6.1](https://github.com/microsoft/RD-Agent/compare/v0.6.0...v0.6.1) (2025-06-28)
### Bug Fixes
* fix mount ([#1001](https://github.com/microsoft/RD-Agent/issues/1001)) ([4ae2f13](https://github.com/microsoft/RD-Agent/commit/4ae2f1303dfcbaea53d459be7c8e85bf85ce5f4f))
* handle the bug of wrong dag_parant index ([#996](https://github.com/microsoft/RD-Agent/issues/996)) ([bda12ff](https://github.com/microsoft/RD-Agent/commit/bda12ffecf9ae116e0d04eece0c6a1b61413d916))
* improve log folder sorting and selection UX ([#993](https://github.com/microsoft/RD-Agent/issues/993)) ([b116807](https://github.com/microsoft/RD-Agent/commit/b11680777f116b6c40f9e535e0da10c186c95050))
## [0.6.0](https://github.com/microsoft/RD-Agent/compare/v0.5.0...v0.6.0) (2025-06-26)
### Features
* async mechanism for multi-trace ([#981](https://github.com/microsoft/RD-Agent/issues/981)) ([9e60c32](https://github.com/microsoft/RD-Agent/commit/9e60c32cf348481eb55617809c059c359d7603b8))
### Bug Fixes
* add async to direct_exp_gen avoid infinite loop ([#992](https://github.com/microsoft/RD-Agent/issues/992)) ([78c203d](https://github.com/microsoft/RD-Agent/commit/78c203d8eefbba67fc120b35cb25e85b2200ac49))
* docker container cleanup to prevent accumulation and system slowdown ([#975](https://github.com/microsoft/RD-Agent/issues/975)) ([05cf094](https://github.com/microsoft/RD-Agent/commit/05cf094913e48c903c8a4476d6c609d8bfa10681))
* fix a bug and update the docs ([#978](https://github.com/microsoft/RD-Agent/issues/978)) ([d1ae9e1](https://github.com/microsoft/RD-Agent/commit/d1ae9e1dcc2ccd1ffe05cb1c6db3e905fa70425c))
* merge datascience v3 and v2 ([#974](https://github.com/microsoft/RD-Agent/issues/974)) ([1ba7548](https://github.com/microsoft/RD-Agent/commit/1ba754853ce2010ce1cb0bbd217b67689fa1ebdf))
* refine details ([#979](https://github.com/microsoft/RD-Agent/issues/979)) ([25caa3d](https://github.com/microsoft/RD-Agent/commit/25caa3d00c255286dce27915b9355987b87ed2e8))
* refine prompt ([#987](https://github.com/microsoft/RD-Agent/issues/987)) ([76df96e](https://github.com/microsoft/RD-Agent/commit/76df96ee88212a8aee7f518b9cacf80591dc2939))
## [0.5.0](https://github.com/microsoft/RD-Agent/compare/v0.4.0...v0.5.0) (2025-06-18)
### Features
* add a check for whether values in score_df are NaN ([#756](https://github.com/microsoft/RD-Agent/issues/756)) ([d9cc780](https://github.com/microsoft/RD-Agent/commit/d9cc78098beb27f3a1bf2f2d461302db177b7d41))
* add competition level filter and extract constants to utils ([#869](https://github.com/microsoft/RD-Agent/issues/869)) ([b40b605](https://github.com/microsoft/RD-Agent/commit/b40b6055368e6c72d8435352104b1c281b06da7f))
* add DocDev for auto-generating workspace documentation ([#781](https://github.com/microsoft/RD-Agent/issues/781)) ([bcba6ea](https://github.com/microsoft/RD-Agent/commit/bcba6eac32684ebb267c93b4e85dbfa9561d15d1))
* add drafting pipeline ([#832](https://github.com/microsoft/RD-Agent/issues/832)) ([efedddf](https://github.com/microsoft/RD-Agent/commit/efedddf39bc19221fdffc2e39ee0a09097fc82b0))
* add last_exp_fb to DSTrace and update feedback retrieval usage ([#910](https://github.com/microsoft/RD-Agent/issues/910)) ([10531fd](https://github.com/microsoft/RD-Agent/commit/10531fda9438c6915b26d5013bd2413e1333ceb9))
* add mlflow logger in RD loop to log ([#815](https://github.com/microsoft/RD-Agent/issues/815)) ([b91b54f](https://github.com/microsoft/RD-Agent/commit/b91b54f355c26b751087d0c14774f466e82866de))
* add naive experiment generator and update proposal configurations ([#759](https://github.com/microsoft/RD-Agent/issues/759)) ([75494f4](https://github.com/microsoft/RD-Agent/commit/75494f4fed5bc845acfd7f7bacef385f0f96c514))
* add RD-Agent-Quant scenario ([#838](https://github.com/microsoft/RD-Agent/issues/838)) ([6e42d52](https://github.com/microsoft/RD-Agent/commit/6e42d523a85df67aa13927abbf0894564c71880e))
* add reasoning_effort parameter to LiteLLMAPIBackend and LLMSett… ([#754](https://github.com/microsoft/RD-Agent/issues/754)) ([113889f](https://github.com/microsoft/RD-Agent/commit/113889fefe9b09aaea1b564704c81664b8f77ec5))
* add reviewer in feedback ([#765](https://github.com/microsoft/RD-Agent/issues/765)) ([1a95bee](https://github.com/microsoft/RD-Agent/commit/1a95bee6aa6bc6f45fdeb484f3a6f81caa273038))
* advanced checkpoint selectors ([#790](https://github.com/microsoft/RD-Agent/issues/790)) ([50ea033](https://github.com/microsoft/RD-Agent/commit/50ea0336e93d8cb39fb871e81a3f61abdf293bc7))
* archive python and csv files in workspace to maintain results ([#814](https://github.com/microsoft/RD-Agent/issues/814)) ([67d0e01](https://github.com/microsoft/RD-Agent/commit/67d0e01e7c9237da1371d93cbf9d86f5f46faac4))
* checkpoint selection ([#744](https://github.com/microsoft/RD-Agent/issues/744)) ([a15a06a](https://github.com/microsoft/RD-Agent/commit/a15a06ad643977db59d7cac9da52e637cf80395a))
* custom data ([#810](https://github.com/microsoft/RD-Agent/issues/810)) ([6322916](https://github.com/microsoft/RD-Agent/commit/632291608cf605bd8bcfcab0017824823bdecdb8))
* dump model ([#776](https://github.com/microsoft/RD-Agent/issues/776)) ([b49481e](https://github.com/microsoft/RD-Agent/commit/b49481e073e6f536d2b1b3bd2d01229ed05abdea))
* enable to set different version of idea-proposal for multi traces ([#895](https://github.com/microsoft/RD-Agent/issues/895)) ([236c28f](https://github.com/microsoft/RD-Agent/commit/236c28f29c6bc5da62129632e464bbc32056ebdb))
* enhance compatibility with more LLM models ([#905](https://github.com/microsoft/RD-Agent/issues/905)) ([8800624](https://github.com/microsoft/RD-Agent/commit/8800624ad4749d6e798785a082c9f94c306792ef))
* idea pool integrated to exp_gen & add timer to RD-Agent & pause-resume to RD-loops ([#795](https://github.com/microsoft/RD-Agent/issues/795)) ([e62aefa](https://github.com/microsoft/RD-Agent/commit/e62aefa56e34ff45a8ed033f7bf28b95c8e63656))
* joblib cache ([#749](https://github.com/microsoft/RD-Agent/issues/749)) ([83a0411](https://github.com/microsoft/RD-Agent/commit/83a041148ff908871b1906f9e6889d80ab513412))
* log api status to mlflow ([#860](https://github.com/microsoft/RD-Agent/issues/860)) ([049921b](https://github.com/microsoft/RD-Agent/commit/049921beb0b4ed0ba1ab7508d9857d2c1e729349))
* log reaching max time limit before breaking CoSTEER evolution ([#921](https://github.com/microsoft/RD-Agent/issues/921)) ([837fff2](https://github.com/microsoft/RD-Agent/commit/837fff29096fefe1369d386ef8a860395b737173))
* merge failed and successful traces together ([#766](https://github.com/microsoft/RD-Agent/issues/766)) ([3a2aa8c](https://github.com/microsoft/RD-Agent/commit/3a2aa8cf0102647950b2dfc0007c118b0c799cd4))
* merge selectively ([#888](https://github.com/microsoft/RD-Agent/issues/888)) ([06ba314](https://github.com/microsoft/RD-Agent/commit/06ba314ff0f91e7e78e8d456c719ac3194a8c774))
* multi-trace online merge ([#886](https://github.com/microsoft/RD-Agent/issues/886)) ([2112d67](https://github.com/microsoft/RD-Agent/commit/2112d676d0938de6fea163b2e5eb9c36771e7041))
* new proposal (structured outputs) prompts ([#887](https://github.com/microsoft/RD-Agent/issues/887)) ([150796a](https://github.com/microsoft/RD-Agent/commit/150796aaa72eaa5037fd7db8e785058fbc4d4967))
* parallel loop running based on asyncio ([#932](https://github.com/microsoft/RD-Agent/issues/932)) ([c63e207](https://github.com/microsoft/RD-Agent/commit/c63e2071f3179feef69f88061c0172cb5c3157f2))
* propose hypothesis across multiple parts in pipeline ([#827](https://github.com/microsoft/RD-Agent/issues/827)) ([acb0e21](https://github.com/microsoft/RD-Agent/commit/acb0e21a331410d044849e12e2887f41e5ff1c3a))
* pull image with progress ([#777](https://github.com/microsoft/RD-Agent/issues/777)) ([5cad086](https://github.com/microsoft/RD-Agent/commit/5cad0860204ede974533dc7bdc9808cfd135fa24))
* raise error when timeout in api call ([#793](https://github.com/microsoft/RD-Agent/issues/793)) ([eafd4df](https://github.com/microsoft/RD-Agent/commit/eafd4dfc6263f19a8cdaf27498a1d07b43815306))
* raise policy violation ([#894](https://github.com/microsoft/RD-Agent/issues/894)) ([5b9d007](https://github.com/microsoft/RD-Agent/commit/5b9d0072aebe15369e9a0010af83e71684baeae7))
* reanalyze competition info & pipeline coding evaluator prompt ([#837](https://github.com/microsoft/RD-Agent/issues/837)) ([f7b5258](https://github.com/microsoft/RD-Agent/commit/f7b52580080c75d311355bcc6193b49495801809))
* refine merge ([#842](https://github.com/microsoft/RD-Agent/issues/842)) ([99463b4](https://github.com/microsoft/RD-Agent/commit/99463b46819b3a0dcb2bb12a823a9cdf7ec560b4))
* refine prompt ([#760](https://github.com/microsoft/RD-Agent/issues/760)) ([a91b182](https://github.com/microsoft/RD-Agent/commit/a91b182c4c9510eb34e4aab956588e909fa5d70b))
* replace hard-coded cache paths with dynamic cache_path config ([#952](https://github.com/microsoft/RD-Agent/issues/952)) ([db56894](https://github.com/microsoft/RD-Agent/commit/db568947f1084a80d603718f5a13fdbd72b90a47))
* revert draft stage into a soft decay in hypothesis selection ([#849](https://github.com/microsoft/RD-Agent/issues/849)) ([d41db0c](https://github.com/microsoft/RD-Agent/commit/d41db0ca357b07091825ebd9d18c303b6db3cc6a))
* trace merging ([#836](https://github.com/microsoft/RD-Agent/issues/836)) ([a3d5473](https://github.com/microsoft/RD-Agent/commit/a3d547369e408a05cff570c1239b6320be40418d))
* truncate by time ([#863](https://github.com/microsoft/RD-Agent/issues/863)) ([2b9427a](https://github.com/microsoft/RD-Agent/commit/2b9427ae036ffe1e28a717502f45500fe91fe5ac))
* update prompt to improve json respond format of some LLM models ([#928](https://github.com/microsoft/RD-Agent/issues/928)) ([0b84709](https://github.com/microsoft/RD-Agent/commit/0b84709e59c7abb9754961cd17cc9673fcf508aa))
* using different chat model in different part ([#822](https://github.com/microsoft/RD-Agent/issues/822)) ([c052ea6](https://github.com/microsoft/RD-Agent/commit/c052ea6d1f8948183a4a6ebc873ec01b57373cce))
### Bug Fixes
* 'DSProposalV2ExpGen' object has no attribute 'COMPONENT_TASK_MAP… ([#950](https://github.com/microsoft/RD-Agent/issues/950)) ([e353895](https://github.com/microsoft/RD-Agent/commit/e353895251f231fee85abdcb1b22b022a577af77))
* adapting UI to mock trace ([#841](https://github.com/microsoft/RD-Agent/issues/841)) ([8a5754c](https://github.com/microsoft/RD-Agent/commit/8a5754c9b9c9410d0943aeed777a93c13422e54a))
* add missing semicolon after chmod in env shell command ([#955](https://github.com/microsoft/RD-Agent/issues/955)) ([1128eaa](https://github.com/microsoft/RD-Agent/commit/1128eaa89ec1dcab4a05ef50d64c7f7e6aae88a8))
* add time to timer when api timeout bug ([#826](https://github.com/microsoft/RD-Agent/issues/826)) ([f45d6ae](https://github.com/microsoft/RD-Agent/commit/f45d6ae6595c1c39b389485b637a0ae53ffc8782))
* add wait_retry to exp_gen v2 ([#783](https://github.com/microsoft/RD-Agent/issues/783)) ([b9fb7cf](https://github.com/microsoft/RD-Agent/commit/b9fb7cf4e3070062d91b5b67d0f10d6266b45142))
* adjust ds_trace lookup and add stderr redirect to mlebench command ([#853](https://github.com/microsoft/RD-Agent/issues/853)) ([4e53108](https://github.com/microsoft/RD-Agent/commit/4e53108e020db719b39cba3a67e0c6dae3de19cf))
* align competion_full_desc and scenario_all_desc, remove redundant info in problems proposal ([#808](https://github.com/microsoft/RD-Agent/issues/808)) ([76d8536](https://github.com/microsoft/RD-Agent/commit/76d8536d9ec53952383019306781d49cb3e9f75c))
* bug fix in timer start ([#807](https://github.com/microsoft/RD-Agent/issues/807)) ([9af7161](https://github.com/microsoft/RD-Agent/commit/9af7161eb57bdd2e24b072335e9d185951c32472))
* bug in problem identification ([#806](https://github.com/microsoft/RD-Agent/issues/806)) ([e1d5a29](https://github.com/microsoft/RD-Agent/commit/e1d5a2914046476f2f10d5884ed3c3ff956d65ff))
* conda error information ([#941](https://github.com/microsoft/RD-Agent/issues/941)) ([fd39a94](https://github.com/microsoft/RD-Agent/commit/fd39a947763fb4a9be87b907c399bebe384df505))
* default cost to NaN when calculation fails in LiteLLM backend ([#912](https://github.com/microsoft/RD-Agent/issues/912)) ([51a4048](https://github.com/microsoft/RD-Agent/commit/51a4048129cbfbc3b84bcf50fd8866fafb3e2da3))
* ds trace ([#929](https://github.com/microsoft/RD-Agent/issues/929)) ([127e441](https://github.com/microsoft/RD-Agent/commit/127e441602e21a46d6313ff39133ab8ca841937e))
* duplicate model names test in pipeline coder & runner ([#763](https://github.com/microsoft/RD-Agent/issues/763)) ([be3ee9d](https://github.com/microsoft/RD-Agent/commit/be3ee9da9882edda3c06ff7d1099d1bbda2203c3))
* filter system metadata dirs and init missing DSTrace attribute ([#946](https://github.com/microsoft/RD-Agent/issues/946)) ([10050ef](https://github.com/microsoft/RD-Agent/commit/10050ef368ae7ec07cbf20ac4e52e21c2875eaab))
* fix a bug in docker result extraction ([#824](https://github.com/microsoft/RD-Agent/issues/824)) ([e1c0f98](https://github.com/microsoft/RD-Agent/commit/e1c0f9826abcbc11dda215a600a2637c9ac6e984))
* fix competition metric direction ([#784](https://github.com/microsoft/RD-Agent/issues/784)) ([3be0057](https://github.com/microsoft/RD-Agent/commit/3be0057556f46c899065ee1c7f9bafe33e79249c))
* fix model input shape bug and costeer_model bug ([#821](https://github.com/microsoft/RD-Agent/issues/821)) ([b34bd89](https://github.com/microsoft/RD-Agent/commit/b34bd895d6d9c326aab85856a15be0cb72b2c4c8))
* fix some minor bugs ([#758](https://github.com/microsoft/RD-Agent/issues/758)) ([963f96e](https://github.com/microsoft/RD-Agent/commit/963f96e5596bee04074135c2a0e31a8adc39ad8c))
* fix some minor bugs in qlib scenario ([#817](https://github.com/microsoft/RD-Agent/issues/817)) ([79962a7](https://github.com/microsoft/RD-Agent/commit/79962a7ca40c77a3997a68da9ad1b5ab16728483))
* fix the bug in the regular expression matching for stdout ([#890](https://github.com/microsoft/RD-Agent/issues/890)) ([ee57e37](https://github.com/microsoft/RD-Agent/commit/ee57e37a22af874b262c033d1606dbe7799706db))
* fix the bug of Exceed-LLM-Context in online merge of multi-tarce ([#892](https://github.com/microsoft/RD-Agent/issues/892)) ([f760a3e](https://github.com/microsoft/RD-Agent/commit/f760a3eff7bd927a31e4958ed2f706312e83e3e3))
* fix the problems weights bug ([#898](https://github.com/microsoft/RD-Agent/issues/898)) ([013d79f](https://github.com/microsoft/RD-Agent/commit/013d79f12060e908aeb57c3eb1bb56eea86df086))
* fixed CI execution failures caused by document builds ([#857](https://github.com/microsoft/RD-Agent/issues/857)) ([5c116b2](https://github.com/microsoft/RD-Agent/commit/5c116b24ce727f6ed9ef39d5aa5b60442038c344))
* get_metric_direction for aerial-cactus-identification ([#970](https://github.com/microsoft/RD-Agent/issues/970)) ([70dc62d](https://github.com/microsoft/RD-Agent/commit/70dc62de5fbd4272ecda1b6fcbcf898b3624a991))
* import path of T ([#787](https://github.com/microsoft/RD-Agent/issues/787)) ([ac008a6](https://github.com/microsoft/RD-Agent/commit/ac008a61d03b4737ab3d994024e922839d8f3fe1))
* improve eval alignment check (e.g. small-scale finetuning) ([#802](https://github.com/microsoft/RD-Agent/issues/802)) ([d391578](https://github.com/microsoft/RD-Agent/commit/d3915788082de640a4ce1eea6d2e607319b89c3e))
* improve file tree and _walk symlink handling ([#877](https://github.com/microsoft/RD-Agent/issues/877)) ([516cb69](https://github.com/microsoft/RD-Agent/commit/516cb69357483ddd99f84b221a056d8491c34f9b))
* log info ([#965](https://github.com/microsoft/RD-Agent/issues/965)) ([f1dbc21](https://github.com/microsoft/RD-Agent/commit/f1dbc2100498e22c8e5edbb2e4563c99c3d54775))
* main bug ([#938](https://github.com/microsoft/RD-Agent/issues/938)) ([c6d34d6](https://github.com/microsoft/RD-Agent/commit/c6d34d67b8aedf5496bf6a875915ce657fc58448))
* non-exist variable test_eval.py ([#847](https://github.com/microsoft/RD-Agent/issues/847)) ([4948c38](https://github.com/microsoft/RD-Agent/commit/4948c38560f4cf021d9354b201b22dfa5ccb9441))
* refine feedback prompt ([#901](https://github.com/microsoft/RD-Agent/issues/901)) ([12bb2c4](https://github.com/microsoft/RD-Agent/commit/12bb2c4a1494b9aa29962905abb5e433a60eb716))
* refine the time/memory constraints prompt in hypothesis proposal ([#856](https://github.com/microsoft/RD-Agent/issues/856)) ([51ce8ef](https://github.com/microsoft/RD-Agent/commit/51ce8ef84b4fe6590ce20599a56eee596f2f04e6))
* Set PYTHONPATH in env.run_ret_code call in FBWorkspace class ([#755](https://github.com/microsoft/RD-Agent/issues/755)) ([68b5018](https://github.com/microsoft/RD-Agent/commit/68b501889caca754f27b57d9ab6f72184e93b15c))
* task_gen for better understanding ([#752](https://github.com/microsoft/RD-Agent/issues/752)) ([6bfc1e5](https://github.com/microsoft/RD-Agent/commit/6bfc1e570449ee69ac110a4ced9a7cecbc0e6a73))
* trace list but ([#852](https://github.com/microsoft/RD-Agent/issues/852)) ([32cdc57](https://github.com/microsoft/RD-Agent/commit/32cdc575bde103d71a358d4d99bd413076328ebd))
* typo in workflow ([#861](https://github.com/microsoft/RD-Agent/issues/861)) ([0e54c9f](https://github.com/microsoft/RD-Agent/commit/0e54c9fe41d25a4cc45ab9e61bb2c2c01b854751))
* update DS env setup with competition volume and timeout ([#878](https://github.com/microsoft/RD-Agent/issues/878)) ([816ada0](https://github.com/microsoft/RD-Agent/commit/816ada096afabe90578672b0e61b656802a30b62))
* update feedback.py ([#772](https://github.com/microsoft/RD-Agent/issues/772)) ([133778c](https://github.com/microsoft/RD-Agent/commit/133778c67ee3349f1c2fe029bcf6a9ee14568efe))
* update metric direction to return bool ([#791](https://github.com/microsoft/RD-Agent/issues/791)) ([0bf365e](https://github.com/microsoft/RD-Agent/commit/0bf365e7830aa86d2350b9d1c47410af46b3a7e8))
* update runner max loop to 1 in DS scenario ([#820](https://github.com/microsoft/RD-Agent/issues/820)) ([3da378e](https://github.com/microsoft/RD-Agent/commit/3da378e986e8b776a17dbc694d29ef211192ed3e))
* use fallback messages for missing submission and scores files ([#882](https://github.com/microsoft/RD-Agent/issues/882)) ([898fdea](https://github.com/microsoft/RD-Agent/commit/898fdeae80801d537ebc5c4a3b7df9de74c3403a))
* use simple stdout and stderr ([#966](https://github.com/microsoft/RD-Agent/issues/966)) ([0b1c445](https://github.com/microsoft/RD-Agent/commit/0b1c445f1f0c212887ffff9f8fac44236df3607c))
* use trace count as index ([#909](https://github.com/microsoft/RD-Agent/issues/909)) ([b87de56](https://github.com/microsoft/RD-Agent/commit/b87de56e54b206b3aada53850804474eff80b96d))
* wrong variable test_eval.py ([#846](https://github.com/microsoft/RD-Agent/issues/846)) ([808ea6c](https://github.com/microsoft/RD-Agent/commit/808ea6cba541e60c35dd283cee9098ce46f2a59e))
## [0.4.0](https://github.com/microsoft/RD-Agent/compare/v0.3.0...v0.4.0) (2025-04-04)
+1 -1
View File
@@ -92,7 +92,7 @@ isort:
# First deal with the core folder, and then gradually increase the scope of detection,
# and eventually realize the detection of the complete project.
mypy:
$(PIPRUN) python -m mypy rdagent/core # --exclude rdagent/scripts,git_ignore_folder
$(PIPRUN) python -m mypy rdagent/core
# Check lint with ruff.
# First deal with the core folder, and then gradually increase the scope of detection,
+159 -73
View File
@@ -1,7 +1,11 @@
<h4 align="center">
<img src="docs/_static/logo.png" alt="RA-Agent logo" style="width:70%; ">
<a href="https://rdagent.azurewebsites.net" target="_blank">🖥️ Live Demo</a> | <a href="https://rdagent.azurewebsites.net/factor_loop" target="_blank">🎥 Demo Video</a> <a href="https://www.youtube.com/watch?v=JJ4JYO3HscM&list=PLALmKB0_N3_i52fhUmPQiL4jsO354uopR" target="_blank">▶️YouTube</a> | <a href="https://rdagent.readthedocs.io/en/latest/index.html" target="_blank">📖 Documentation</a> | <a href="#-paperwork-list"> 📃 Papers </a>
<a href="https://rdagent.azurewebsites.net" target="_blank">🖥️ Live Demo</a> |
<a href="https://rdagent.azurewebsites.net/factor_loop" target="_blank">🎥 Demo Video</a> <a href="https://www.youtube.com/watch?v=JJ4JYO3HscM&list=PLALmKB0_N3_i52fhUmPQiL4jsO354uopR" target="_blank">▶️YouTube</a> |
<a href="https://rdagent.readthedocs.io/en/latest/index.html" target="_blank">📖 Documentation</a> |
<a href="https://aka.ms/RD-Agent-Tech-Report" target="_blank">📄 Tech Report</a> |
<a href="#-paperwork-list"> 📃 Papers </a>
</h3>
@@ -21,35 +25,76 @@
[![Chat](https://img.shields.io/badge/chat-discord-blue)](https://discord.gg/ybQ97B6Jjy)
[![Documentation Status](https://readthedocs.org/projects/rdagent/badge/?version=latest)](https://rdagent.readthedocs.io/en/latest/?badge=latest)
[![Readthedocs Preview](https://github.com/microsoft/RD-Agent/actions/workflows/readthedocs-preview.yml/badge.svg)](https://github.com/microsoft/RD-Agent/actions/workflows/readthedocs-preview.yml) <!-- this badge is too long, please place it in the last one to make it pretty -->
[![arXiv](https://img.shields.io/badge/arXiv-2505.14738-00ff00.svg)](https://arxiv.org/abs/2505.14738)
# 🏆 The Best Machine Learning Engineering Agent!
[MLE-bench](https://github.com/openai/mle-bench) is a comprehensive benchmark evaluating the performance of AI agents on machine learning engineering tasks. Utilizing datasets from 75 Kaggle competitions, MLE-bench provides robust assessments of AI systems' capabilities in real-world ML engineering scenarios.
R&D-Agent currently leads as the top-performing machine learning engineering agent on MLE-bench:
| Agent | Low == Lite (%) | Medium (%) | High (%) | All (%) |
|---------|--------|-----------|---------|----------|
| R&D-Agent o1-preview | 48.18 ± 2.49 | 8.95 ± 2.36 | 18.67 ± 2.98 | 22.4 ± 1.1 |
| R&D-Agent o3(R)+GPT-4.1(D) | 51.52 ± 6.21 | 7.89 ± 3.33 | 16.67 ± 3.65 | 22.45 ± 2.45 |
| AIDE o1-preview | 34.3 ± 2.4 | 8.8 ± 1.1 | 10.0 ± 1.9 | 16.9 ± 1.1 |
**Notes:**
- **O3(R)+GPT-4.1(D)**: This version is designed to both reduce average time per loop and leverage a cost-effective combination of backend LLMs by seamlessly integrating Research Agent (o3) with Development Agent (GPT-4.1).
- **AIDE o1-preview**: Represents the previously best public result on MLE-bench as reported in the original MLE-bench paper.
- Average and standard deviation results for R&D-Agent o1-preview is based on a independent of 5 seeds and for R&D-Agent o3(R)+GPT-4.1(D) is based on 6 seeds.
- According to MLE-Bench, the 75 competitions are categorized into three levels of complexity: **Low==Lite** if we estimate that an experienced ML engineer can produce a sensible solution in under 2 hours, excluding the time taken to train any models; **Medium** if it takes between 2 and 10 hours; and **High** if it takes more than 10 hours.
You can inspect the detailed runs of the above results online.
- [R&D-Agent o1-preview detailed runs](https://aka.ms/RD-Agent_MLE-Bench_O1-preview)
- [R&D-Agent o3(R)+GPT-4.1(D) detailed runs](https://aka.ms/RD-Agent_MLE-Bench_O3_GPT41)
For running R&D-Agent on MLE-bench, refer to **[MLE-bench Guide: Running ML Engineering via MLE-bench](https://rdagent.readthedocs.io/en/latest/scens/data_science.html)**
# 🥇 The First Data-Centric Quant Multi-Agent Framework!
R&D-Agent for Quantitative Finance, in short **RD-Agent(Q)**, is the first data-centric, multi-agent framework designed to automate the full-stack research and development of quantitative strategies via coordinated factor-model co-optimization.
![image](https://github.com/user-attachments/assets/3198bc10-47ba-4ee0-8a8e-46d5ce44f45d)
Extensive experiments in real stock markets show that, at a cost under $10, RD-Agent(Q) achieves approximately 2× higher ARR than benchmark factor libraries while using over 70% fewer factors. It also surpasses state-of-the-art deep time-series models under smaller resource budgets. Its alternating factormodel optimization further delivers excellent trade-off between predictive accuracy and strategy robustness.
You can learn more details about **RD-Agent(Q)** through the [paper](https://arxiv.org/abs/2505.15155) and reproduce it through the [documentation](https://rdagent.readthedocs.io/en/latest/scens/quant_agent_fin.html).
# 📰 News
| 🗞️ News | 📝 Description |
| -- | ------ |
| [Technical Report Release](#overall-technical-report) | Overall framework description and results on MLE-bench |
| [R&D-Agent-Quant Release](#deep-application-in-diverse-scenarios) | Apply R&D-Agent to quant trading |
| MLE-Bench Results Released | R&D-Agent currently leads as the [top-performing machine learning engineering agent](#-the-best-machine-learning-engineering-agent) on MLE-bench |
| Support LiteLLM Backend | We now fully support **[LiteLLM](https://github.com/BerriAI/litellm)** as a backend for integration with multiple LLM providers. |
| General Data Science Agent | [Data Science Agent](https://rdagent.readthedocs.io/en/latest/scens/data_science.html) |
| Kaggle Scenario release | We release **[Kaggle Agent](https://rdagent.readthedocs.io/en/latest/scens/data_science.html)**, try the new features! |
| Official WeChat group release | We created a WeChat group, welcome to join! (🗪[QR Code](https://github.com/microsoft/RD-Agent/issues/880)) |
| Official Discord release | We launch our first chatting channel in Discord (🗪[![Chat](https://img.shields.io/badge/chat-discord-blue)](https://discord.gg/ybQ97B6Jjy)) |
| First release | **R&D-Agent** is released on GitHub |
# Data Science Agent Preview
Check out our demo video showcasing the current progress of our Data Science Agent under development:
https://github.com/user-attachments/assets/3eccbecb-34a4-4c81-bce4-d3f8862f7305
# 📰 News
| 🗞️ News | 📝 Description |
| -- | ------ |
| Support LiteLLM Backend | We now fully support **[LiteLLM](https://github.com/BerriAI/litellm)** as a backend for integration with multiple LLM providers. |
| More General Data Science Agent | 🚀Coming soon! |
| Kaggle Scenario release | We release **[Kaggle Agent](https://rdagent.readthedocs.io/en/latest/scens/kaggle_agent.html)**, try the new features! |
| Official WeChat group release | We created a WeChat group, welcome to join! (🗪[QR Code](docs/WeChat_QR_code.jpg)) |
| Official Discord release | We launch our first chatting channel in Discord (🗪[![Chat](https://img.shields.io/badge/chat-discord-blue)](https://discord.gg/ybQ97B6Jjy)) |
| First release | **RDAgent** is released on GitHub |
# 🌟 Introduction
<div align="center">
<img src="docs/_static/scen.png" alt="Our focused scenario" style="width:80%; ">
</div>
RDAgent aims to automate the most critical and valuable aspects of the industrial R&D process, and we begin with focusing on the data-driven scenarios to streamline the development of models and data.
R&D-Agent aims to automate the most critical and valuable aspects of the industrial R&D process, and we begin with focusing on the data-driven scenarios to streamline the development of models and data.
Methodologically, we have identified a framework with two key components: 'R' for proposing new ideas and 'D' for implementing them.
We believe that the automatic evolution of R&D will lead to solutions of significant industrial value.
<!-- Tag Cloud -->
R&D is a very general scenario. The advent of RDAgent can be your
R&D is a very general scenario. The advent of R&D-Agent can be your
- 💰 **Automatic Quant Factory** ([🎥Demo Video](https://rdagent.azurewebsites.net/factor_loop)|[▶️YouTube](https://www.youtube.com/watch?v=X4DK2QZKaKY&t=6s))
- 🤖 **Data Mining Agent:** Iteratively proposing data & models ([🎥Demo Video 1](https://rdagent.azurewebsites.net/model_loop)|[▶️YouTube](https://www.youtube.com/watch?v=dm0dWL49Bc0&t=104s)) ([🎥Demo Video 2](https://rdagent.azurewebsites.net/dmm)|[▶️YouTube](https://www.youtube.com/watch?v=VIaSTZuoZg4)) and implementing them by gaining knowledge from data.
- 🦾 **Research Copilot:** Auto read research papers ([🎥Demo Video](https://rdagent.azurewebsites.net/report_model)|[▶️YouTube](https://www.youtube.com/watch?v=BiA2SfdKQ7o)) / financial reports ([🎥Demo Video](https://rdagent.azurewebsites.net/report_factor)|[▶️YouTube](https://www.youtube.com/watch?v=ECLTXVcSx-c)) and implement model structures or building datasets.
@@ -85,8 +130,8 @@ Ensure the current user can run Docker commands **without using sudo**. You can
conda activate rdagent
```
### 🛠️ Install the RDAgent
- You can directly install the RDAgent package from PyPI:
### 🛠️ Install the R&D-Agent
- You can directly install the R&D-Agent package from PyPI:
```sh
pip install rdagent
```
@@ -106,46 +151,74 @@ Ensure the current user can run Docker commands **without using sudo**. You can
- json_mode
- embedding query
- For example: If you are using the `OpenAI API`, you have to configure your GPT model in the `.env` file like this.
You can set your Chat Model and Embedding Model in the following ways:
> **🔥 Attention**: We now provide experimental support for **DeepSeek** models! You can use DeepSeek's official API for cost-effective and high-performance inference. See the configuration example below for DeepSeek setup.
- **Using LiteLLM (Default)**: We now support LiteLLM as a backend for integration with multiple LLM providers. You can configure in multiple ways:
**Option 1: Unified API base for both models**
```bash
cat << EOF > .env
OPENAI_API_KEY=<replace_with_your_openai_api_key>
# EMBEDDING_MODEL=text-embedding-3-small
CHAT_MODEL=gpt-4-turbo
EOF
```
- However, not every API services support these features by default. For example: `AZURE OpenAI`, you have to configure your GPT model in the `.env` file like this.
```bash
cat << EOF > .env
USE_AZURE=True
EMBEDDING_OPENAI_API_KEY=<replace_with_your_azure_openai_api_key>
EMBEDDING_AZURE_API_BASE=<replace_with_your_azure_endpoint>
EMBEDDING_AZURE_API_VERSION=<replace_with_the_version_of_your_azure_openai_api>
# Set to any model supported by LiteLLM.
CHAT_MODEL=gpt-4o
EMBEDDING_MODEL=text-embedding-3-small
CHAT_OPENAI_API_KEY=<replace_with_your_azure_openai_api_key>
CHAT_AZURE_API_BASE=<replace_with_your_azure_endpoint>
CHAT_AZURE_API_VERSION=<replace_with_the_version_of_your_azure_openai_api>
CHAT_MODEL=<replace_it_with_the_name_of_your_azure_chat_model>
EOF
# Configure unified API base
OPENAI_API_BASE=<your_unified_api_base>
OPENAI_API_KEY=<replace_with_your_openai_api_key>
```
- We now support LiteLLM as a backend for integration with multiple LLM providers. If you use LiteLLM Backend to use models, you can configure as follows:
**Option 2: Separate API bases for Chat and Embedding models**
```bash
cat << EOF > .env
BACKEND=rdagent.oai.backend.LiteLLMAPIBackend
# It can be modified to any model supported by LiteLLM.
CHAT_MODEL=gpt-4o
EMBEDDING_MODEL=text-embedding-3-small
# The backend api_key fully follow the convention of litellm.
OPENAI_API_KEY=<replace_with_your_openai_api_key>
```
# Set to any model supported by LiteLLM.
# Configure separate API bases for chat and embedding
- For more configuration information, please refer to the [documentation](https://rdagent.readthedocs.io/en/latest/installation_and_configuration.html).
# CHAT MODEL:
CHAT_MODEL=gpt-4o
OPENAI_API_BASE=<your_chat_api_base>
OPENAI_API_KEY=<replace_with_your_openai_api_key>
# EMBEDDING MODEL:
# TAKE siliconflow as an example, you can use other providers.
# Note: embedding requires litellm_proxy prefix
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-large-en-v1.5
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
```
**Configuration Example: DeepSeek Setup**:
>Since many users encounter configuration errors when setting up DeepSeek. Here's a complete working example for DeepSeek Setup:
```bash
cat << EOF > .env
# CHAT MODEL: Using DeepSeek Official API
CHAT_MODEL=deepseek/deepseek-chat
DEEPSEEK_API_KEY=<replace_with_your_deepseek_api_key>
# EMBEDDING MODEL: Using SiliconFlow for embedding since deepseek has no embedding model.
# Note: embedding requires litellm_proxy prefix
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-m3
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
```
Notice: If you are using reasoning models that include thought processes in their responses (such as \<think> tags), you need to set the following environment variable:
```bash
REASONING_THINK_RM=True
```
- You can also use a deprecated backend if you only use `OpenAI API` or `Azure OpenAI` directly. For this deprecated setting and more configuration information, please refer to the [documentation](https://rdagent.readthedocs.io/en/latest/installation_and_configuration.html).
### 🚀 Run the Application
The **[🖥️ Live Demo](https://rdagent.azurewebsites.net/)** is implemented by the following commands(each item represents one demo, you can select the one you prefer):
- Run the **Automated Quantitative Trading & Iterative Factors Model Joint Evolution**: [Qlib](http://github.com/microsoft/qlib) self-loop factor & model proposal and implementation application
```sh
rdagent fin_quant
```
- Run the **Automated Quantitative Trading & Iterative Factors Evolution**: [Qlib](http://github.com/microsoft/qlib) self-loop factor proposal and implementation application
```sh
rdagent fin_factor
@@ -156,19 +229,6 @@ The **[🖥️ Live Demo](https://rdagent.azurewebsites.net/)** is implemented b
rdagent fin_model
```
- Run the **Automated Medical Prediction Model Evolution**: Medical self-loop model proposal and implementation application
>(1) Apply for an account at [PhysioNet](https://physionet.org/). <br /> (2) Request access to FIDDLE preprocessed data: [FIDDLE Dataset](https://physionet.org/content/mimic-eicu-fiddle-feature/1.0.0/). <br />
(3) Place your username and password in `.env`.
```bash
cat << EOF >> .env
DM_USERNAME=<your_username>
DM_PASSWORD=<your_password>
EOF
```
```sh
rdagent med_model
```
- Run the **Automated Quantitative Trading & Factors Extraction from Financial Reports**: Run the [Qlib](http://github.com/microsoft/qlib) factor extraction and implementation application based on financial reports
```sh
# 1. Generally, you can run this scenario using the following command:
@@ -199,24 +259,20 @@ The **[🖥️ Live Demo](https://rdagent.azurewebsites.net/)** is implemented b
> 3. Join the competition: Click `Join the competition` -> `I Understand and Accept` at the bottom of the [competition details page](https://www.kaggle.com/competitions/sf-crime/data).
```bash
# Generally, you can run the Kaggle competition program with the following command:
rdagent kaggle --competition <your competition name>
rdagent data_science --competition <your competition name>
# Specifically, you need to create a folder for storing competition files (e.g., competition description file, competition datasets, etc.), and configure the path to the folder in your environment. In addition, you need to use chromedriver when you download the competition descriptors, which you can follow for this specific example:
# Specifically, you will need to first prepare some competition description files and configure the competition description file path, which you can follow for this specific example:
# 1. Prepare the competition description files
wget https://github.com/SunsetWolf/rdagent_resource/releases/download/kaggle_data/kaggle_data.zip
unzip kaggle_data.zip -d git_ignore_folder/kaggle_data
# 1. Install chromedriver.
# 2. Add the competition description file path to the `.env` file.
dotenv set KG_LOCAL_DATA_PATH "$(pwd)/git_ignore_folder/kaggle_data"
mkdir -p ./git_ignore_folder/kaggle_data
dotenv set DS_LOCAL_DATA_PATH "$(pwd)/git_ignore_folder/kaggle_data"
dotenv set DS_IF_USING_MLE_DATA True
# 3. run the application
rdagent kaggle --competition sf-crime
rdagent data_science --competition sf-crime
```
> **Description of the above example:** <br />
> - Kaggle competition data, contains two parts: competition description file (json file) and competition dataset (zip file). We prepare the competition description file for you, the competition dataset will be downloaded automatically when you run the program, as in the example. <br />
> - If you want to download the competition description file automatically, you need to install chromedriver, The instructions for installing chromedriver can be found in the [documentation](https://rdagent.readthedocs.io/en/latest/scens/kaggle_agent.html#example-guide). <br />
> - The **Competition List Available** can be found [here](https://rdagent.readthedocs.io/en/latest/scens/kaggle_agent.html#competition-list-available). <br />
### 🖥️ Monitor the Application Results
- You can run the following command for our demo program to see the run logs.
@@ -235,7 +291,7 @@ The **[🖥️ Live Demo](https://rdagent.azurewebsites.net/)** is implemented b
# 🏭 Scenarios
We have applied RD-Agent to multiple valuable data-driven industrial scenarios.
We have applied R&D-Agent to multiple valuable data-driven industrial scenarios.
## 🎯 Goal: Agent for Data-driven R&D
@@ -262,7 +318,7 @@ The supported scenarios are listed below:
| **🩺 Medical** | 🤖 [Iteratively Proposing Ideas & Evolving](https://rdagent.azurewebsites.net/dmm)[▶️YouTube](https://www.youtube.com/watch?v=VIaSTZuoZg4) | - |
| **🏭 General** | 🦾 [Auto paper reading & implementation](https://rdagent.azurewebsites.net/report_model)[▶️YouTube](https://www.youtube.com/watch?v=BiA2SfdKQ7o) <br/> 🤖 Auto Kaggle Model Tuning | 🤖Auto Kaggle feature Engineering |
- **[RoadMap](https://rdagent.readthedocs.io/en/latest/scens/kaggle_agent.html#roadmap)**: Currently, we are working hard to add new features to the Kaggle scenario.
- **[RoadMap](https://rdagent.readthedocs.io/en/latest/scens/data_science.html#roadmap)**: Currently, we are working hard to add new features to the Kaggle scenario.
Different scenarios vary in entrance and configuration. Please check the detailed setup tutorial in the scenarios documents.
@@ -292,6 +348,21 @@ More documents can be found in the **[📖 readthedocs](https://rdagent.readthed
# 📃 Paper/Work list
## Overall Technical Report
- [R&D-Agent: Automating Data-Driven AI Solution Building Through LLM-Powered Automated Research, Development, and Evolution](https://arxiv.org/abs/2505.14738)
```BibTeX
@misc{yang2024rdagent,
title={R\&D-Agent: Automating Data-Driven AI Solution Building Through LLM-Powered Automated Research, Development, and Evolution},
author={Xu Yang and Xiao Yang and Shikai Fang and Bowen Xian and Yuante Li and Jian Wang and Minrui Xu and Haoran Pan and Xinpeng Hong and Weiqing Liu and Yelong Shen and Weizhu Chen and Jiang Bian},
year={2025},
eprint={2505.14738},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2505.14738}
}
```
![image](https://github.com/user-attachments/assets/28b0488d-a546-4fef-8dc5-563ed64a9b4d)
## 📊 Benchmark
- [Towards Data-Centric Automatic R&D](https://arxiv.org/abs/2404.11276)
```BibTeX
@@ -329,16 +400,31 @@ For more detail, please refer to our **[🖥️ Live Demo page](https://rdagent.
```
![image](https://github.com/user-attachments/assets/75d9769b-0edd-4caf-9d45-57d1e577054b)
## Deep Application in Diverse Scenarios
- [R&D-Agent-Quant: A Multi-Agent Framework for Data-Centric Factors and Model Joint Optimization](https://arxiv.org/abs/2505.15155)
```BibTeX
@misc{li2025rdagentquant,
title={R\&D-Agent-Quant: A Multi-Agent Framework for Data-Centric Factors and Model Joint Optimization},
author={Yuante Li and Xu Yang and Xiao Yang and Minrui Xu and Xisen Wang and Weiqing Liu and Jiang Bian},
year={2025},
eprint={2505.15155},
archivePrefix={arXiv},
primaryClass={cs.AI}
}
```
![image](https://github.com/user-attachments/assets/3186f67a-c2f8-4b6b-8bb9-a9b959c13866)
# 🤝 Contributing
We welcome contributions and suggestions to improve RD-Agent. Please refer to the [Contributing Guide](CONTRIBUTING.md) for more details on how to contribute.
We welcome contributions and suggestions to improve R&D-Agent. Please refer to the [Contributing Guide](CONTRIBUTING.md) for more details on how to contribute.
Before submitting a pull request, ensure that your code passes the automatic CI checks.
## 📝 Guidelines
This project welcomes contributions and suggestions.
Contributing to this project is straightforward and rewarding. Whether it's solving an issue, addressing a bug, enhancing documentation, or even correcting a typo, every contribution is valuable and helps improve RDAgent.
Contributing to this project is straightforward and rewarding. Whether it's solving an issue, addressing a bug, enhancing documentation, or even correcting a typo, every contribution is valuable and helps improve R&D-Agent.
To get started, you can explore the issues list, or search for `TODO:` comments in the codebase by running the command `grep -r "TODO:"`.
@@ -348,7 +434,7 @@ To get started, you can explore the issues list, or search for `TODO:` comments
<img src="https://contrib.rocks/image?repo=microsoft/RD-Agent&max=100&columns=15" />
</a>
Before we released RD-Agent as an open-source project on GitHub, it was an internal project within our group. Unfortunately, the internal commit history was not preserved when we removed some confidential code. As a result, some contributions from our group members, including Haotian Chen, Wenjun Feng, Haoxue Wang, Zeqi Ye, Xinjie Shen, and Jinhui Li, were not included in the public commits.
Before we released R&D-Agent as an open-source project on GitHub, it was an internal project within our group. Unfortunately, the internal commit history was not preserved when we removed some confidential code. As a result, some contributions from our group members, including Haotian Chen, Wenjun Feng, Haoxue Wang, Zeqi Ye, Xinjie Shen, and Jinhui Li, were not included in the public commits.
# ⚖️ Legal disclaimer
<p style="line-height: 1; font-style: italic;">The RD-agent is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. The RD-agent is aimed to facilitate research and development process in the financial industry and not ready-to-use for any financial investment or advice. Users shall independently assess and test the risks of the RD-agent in a specific use scenario, ensure the responsible use of AI technology, including but not limited to developing and integrating risk mitigation measures, and comply with all applicable laws and regulations in all applicable jurisdictions. The RD-agent does not provide financial opinions or reflect the opinions of Microsoft, nor is it designed to replace the role of qualified financial professionals in formulating, assessing, and approving finance products. The inputs and outputs of the RD-agent belong to the users and users shall assume all liability under any theory of liability, whether in contract, torts, regulatory, negligence, products liability, or otherwise, associated with use of the RD-agent and any inputs and outputs thereof.</p>
+1
View File
@@ -5,3 +5,4 @@ psutil==6.1.0
rich==13.9.2
scipy==1.14.1
tqdm==4.66.5
litellm==1.72.4
+1
View File
@@ -5,3 +5,4 @@ psutil==6.1.0
rich==13.9.2
scipy==1.14.1
tqdm==4.66.5
litellm==1.72.4
Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 88 KiB

+65 -11
View File
@@ -13,30 +13,78 @@ Installation
**Install Docker**: RDAgent is designed for research and development, acting like a human researcher and developer. It can write and run code in various environments, primarily using Docker for code execution. This keeps the remaining dependencies simple. Users must ensure Docker is installed before attempting most scenarios. Please refer to the `official 🐳Docker page <https://docs.docker.com/engine/install/>`_ for installation instructions.
Ensure the current user can run Docker commands **without using sudo**. You can verify this by executing `docker run hello-world`.
LiteLLM Backend Configuration
=============================
LiteLLM Backend Configuration (Default)
=======================================
Please create a `.env` file in the root directory of the project and add environment variables.
.. note::
🔥 **Attention**: We now provide experimental support for **DeepSeek** models! You can use DeepSeek's official API for cost-effective and high-performance inference. See the configuration example below for DeepSeek setup.
Here is a sample configuration for using OpenAI's gpt-4o via LiteLLM.
Option 1: Unified API base for both models
------------------------------------------
.. code-block:: Properties
BACKEND=rdagent.oai.backend.LiteLLMAPIBackend
# It can be modified to any model supported by LiteLLM.
CHAT_MODEL=gpt-4o
# Set to any model supported by LiteLLM.
CHAT_MODEL=gpt-4o
EMBEDDING_MODEL=text-embedding-3-small
# Configure unified API base
# The backend api_key fully follows the convention of litellm.
OPENAI_API_BASE=<your_unified_api_base>
OPENAI_API_KEY=<replace_with_your_openai_api_key>
Option 2: Separate API bases for Chat and Embedding models
----------------------------------------------------------
.. code-block:: Properties
# Set to any model supported by LiteLLM.
# CHAT MODEL:
CHAT_MODEL=gpt-4o
OPENAI_API_BASE=<your_chat_api_base>
OPENAI_API_KEY=<replace_with_your_openai_api_key>
# EMBEDDING MODEL:
# TAKE siliconflow as an example, you can use other providers.
# Note: embedding requires litellm_proxy prefix
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-large-en-v1.5
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
Configuration Example: DeepSeek Setup
-------------------------------------
Many users encounter configuration errors when setting up DeepSeek. Here's a complete working example:
.. code-block:: Properties
# CHAT MODEL: Using DeepSeek Official API
CHAT_MODEL=deepseek/deepseek-chat
DEEPSEEK_API_KEY=<replace_with_your_deepseek_api_key>
# EMBEDDING MODEL: Using SiliconFlow for embedding since DeepSeek has no embedding model.
# Note: embedding requires litellm_proxy prefix
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-m3
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
Necessary parameters include:
- `BACKEND`: The backend to use. The default is `rdagent.oai.backend.DeprecBackend`. To use the LiteLLM backend, set it to `rdagent.oai.backend.LiteLLMAPIBackend`.
- `CHAT_MODEL`: The model name of the chat model.
- `CHAT_MODEL`: The model name of the chat model.
- `EMBEDDING_MODEL`: The model name of the embedding model.
- `OPENAI_API_BASE`: The base URL of the API. If `EMBEDDING_MODEL` does not start with `litellm_proxy/`, this is used for both chat and embedding models; otherwise, it is used for `CHAT_MODEL` only.
Optional parameters (required if your embedding model is provided by a different provider than `CHAT_MODEL`):
- `LITELLM_PROXY_API_KEY`: The API key for the embedding model, required if `EMBEDDING_MODEL` starts with `litellm_proxy/`.
- `LITELLM_PROXY_API_BASE`: The base URL for the embedding model, required if `EMBEDDING_MODEL` starts with `litellm_proxy/`.
**Note:** If you are using an embedding model from a provider different from the chat model, remember to add the `litellm_proxy/` prefix to the `EMBEDDING_MODEL` name.
The `CHAT_MODEL` and `EMBEDDING_MODEL` parameters will be passed into LiteLLM's completion function.
Therefore, when utilizing models provided by different providers, first review the interface configuration of LiteLLM. The model names must match those allowed by LiteLLM.
@@ -51,6 +99,12 @@ For example, if you are using a DeepSeek model, you need to set as follows:
CHAT_MODEL=deepseek/deepseek-chat
DEEPSEEK_API_KEY=<replace_with_your_deepseek_api_key>
Besides, when you are using reasoning models, the response might include the thought process. For this case, you need to set the following environment variable:
.. code-block:: Properties
REASONING_THINK_RM=True
For more details on LiteLLM requirements, refer to the `official LiteLLM documentation <https://docs.litellm.ai/docs>`_.
@@ -59,7 +113,7 @@ Configuration(deprecated)
To run the application, please create a `.env` file in the root directory of the project and add environment variables according to your requirements.
The standard configuration options for the user using the OpenAI API are provided in the `.env.example` file.
If you are using this deprecated version, you should set `BACKEND` to `rdagent.oai.backend.DeprecBackend`.
Here are some other configuration options that you can use:
+11 -15
View File
@@ -13,26 +13,23 @@ In the two key areas of data-driven scenarios, model implementation and data bui
The supported scenarios are listed below:
.. list-table::
.. list-table::
:header-rows: 1
* - Scenario/Target
- Model Implementation
- Data Building
* - 💹 Finance
- :ref:`🤖Iteratively Proposing Ideas & Evolving <model_agent_fin>`
- :ref:`🦾Auto reports reading & implementation <data_copilot_fin>`
- :ref:`🥇The First Data-Centric Quant Multi-Agent Framework <quant_agent_fin>`
- :ref:`🤖Iteratively Proposing Ideas & Evolving <model_agent_fin>`
:ref:`🦾Auto reports reading & implementation <data_copilot_fin>`
:ref:`🤖Iteratively Proposing Ideas & Evolving <data_agent_fin>`
* - 🩺 Medical
- :ref:`🤖Iteratively Proposing Ideas & Evolving <model_agent_med>`
-
* - 🏭 General
- :ref:`🦾Auto paper reading & implementation <model_copilot_general>`
:ref:`🤖Auto Kaggle Model Tuning <kaggle_agent>`
- :ref:`🤖Auto Kaggle feature Engineering <kaggle_agent>`
- :ref:`🦾Auto paper reading & implementation <model_copilot_general>`
- :ref:`🤖 Data Science <data_science_agent>`
.. toctree::
@@ -40,10 +37,9 @@ The supported scenarios are listed below:
:caption: Doctree:
:hidden:
quant_agent_fin
data_agent_fin
data_copilot_fin
model_agent_fin
model_agent_med
model_copilot_general
kaggle_agent
data_science
+184
View File
@@ -0,0 +1,184 @@
.. _data_science_agent:
=======================
Data Science Agent
=======================
**🤖 Automated Feature Engineering & Model Tuning Evolution**
------------------------------------------------------------------------------------------
The Data Science Agent is an agent that can automatically perform feature engineering and model tuning. It can be used to solve various data science problems, such as image classification, time series forecasting, and text classification.
🧭 Example Guide
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 🔧 **Set up RD-Agent Environment**
- Before you start, please make sure you have installed RD-Agent and configured the environment for RD-Agent correctly. If you want to know how to install and configure the RD-Agent, please refer to the `documentation <../installation_and_configuration.html>`_.
- 🔩 **Setting the Environment variables at .env file**
- Determine the path where the data will be stored and add it to the ``.env`` file.
.. code-block:: sh
dotenv set DS_LOCAL_DATA_PATH <your local directory>/ds_data
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.DataScienceScen
- 📥 **Prepare Competition Data**
- Data Science competition data typically consists of three components: a competition description file (in Markdown format), the competition dataset, and evaluation scripts. For reference, an example of a custom user-defined dataset is provided in ``rdagent/scenarios/data_science/example``.
- **Correct directory structure (Here is an example of competition data with id custom_data)**
.. code-block:: text
ds_data
└── eval
| └── custom_data
| └── grade.py
| └── valid.py
| └── test.csv
└── custom_data
└── train.csv
└── test.csv
└── sample_submission.csv
└── description.md
└── sample.py
- ``ds_data/custom_data/train.csv:`` Necessary training data in csv or parquet format, or training images.
- ``ds_data/custom_data/description.md:`` (Optional) Competition description file.
- ``ds_data/custom_data/sample_submission.csv:`` (Optional) Competition sample submission file.
- ``ds_data/custom_data/sample.py:`` (Optional) Sample code for generating debug data from the competition dataset. If not provided, R&D-Agent will use its default sampling logic. For details, see the ``create_debug_data`` function in ``rdagent/scenarios/data_science/debug/data.py``.
- ``ds_data/eval/custom_data/grade.py:`` (Optional) Competition grade script, in order to calculate the score for the submission.
- ``ds_data/eval/custom_data/valid.py:`` (Optional) Competition validation script, in order to check if the submission format is correct.
- ``ds_data/eval/custom_data/submission_test.csv:`` (Optional) Competition test label file.
- 🔧 **Set up Environment for Custom User-defined Dataset**
.. code-block:: sh
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.DataScienceScen
dotenv set DS_LOCAL_DATA_PATH rdagent/scenarios/data_science/example
dotenv set DS_IF_USING_MLE_DATA False
dotenv set DS_CODER_ON_WHOLE_PIPELINE True
dotenv set DS_CODER_COSTEER_ENV_TYPE docker
- 🚀 **Run the Application**
- You can directly run the application by using the following command:
.. code-block:: sh
rdagent data_science --competition <Competition ID>
- Then, you can run the test set score corresponding to each round of the loop.
.. code-block:: sh
dotenv run -- python rdagent/log/mle_summary.py grade <url_to_log>
Here, <url_to_log> refers to the parent directory of the log folder generated during the run.
- 📥 **Visualize the R&D Process**
- We provide a web UI to visualize the log. You just need to run:
.. code-block:: sh
streamlit run rdagent/log/ui/dsapp.py
- Then you can input the log path and visualize the R&D process.
🔍 MLE-bench Guide: Running ML Engineering via MLE-bench
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 📝 **MLE-bench Overview**
- MLE-bench is a comprehensive benchmark designed to evaluate the ML engineering capabilities of AI systems using real-world scenarios. The dataset comprises 75 Kaggle competitions. Since Kaggle does not provide held-out test sets for these competitions, the benchmark includes preparation scripts that split the publicly available training data into new training and test sets, and grading scripts are provided for each competition to accurately evaluate submission scores.
- 🔧 **Set up Environment for MLE-bench**
- Running R&D-Agent on MLE-bench is designed for full automation. There is no need for manual downloads and data preparation. Simply set the environment variable ``DS_IF_USING_MLE_DATA`` to True.
- At runtime, R&D-Agent will automatically build the Docker image specified at ``rdagent/scenarios/kaggle/docker/mle_bench_docker/Dockerfile``. This image is responsible for downloading the required datasets and grading files for MLE-bench.
- Note: The first run may take longer than subsequent runs as the Docker image and data are being downloaded and set up for the first time.
.. code-block:: sh
dotenv set DS_LOCAL_DATA_PATH <your local directory>/ds_data
dotenv set DS_IF_USING_MLE_DATA True
- 🔨 **Configuring the Kaggle API**
- Downloading Kaggle competition data requires the Kaggle API. You can set up the Kaggle API by following these steps:
- Register and login on the `Kaggle <https://www.kaggle.com/>`_ website.
- Click on the avatar (usually in the top right corner of the page) -> ``Settings`` -> ``Create New Token``, A file called ``kaggle.json`` will be downloaded.
- Move ``kaggle.json`` to ``~/.config/kaggle/``
- Modify the permissions of the ``kaggle.json`` file.
.. code-block:: sh
chmod 600 ~/.config/kaggle/kaggle.json
- For more information about Kaggle API Settings, refer to the `Kaggle API <https://github.com/Kaggle/kaggle-api>`_.
- 🔩 **Setting the Environment Variables for MLE-bench**
- In addition to auto-downloading the benchmark data, you must also configure the runtime environment for executing the competition code.
- Use the environment variable ``DS_CODER_COSTEER_ENV_TYPE`` to select the execution mode:
• When set to docker (the default), RD-Agent utilizes the official Kaggle Docker image (``gcr.io/kaggle-gpu-images/python:latest``) to ensure that all required packages are available.
• If you prefer to use a custom Docker setup, you can modify the configuration using ``DS_DOCKER_IMAGE`` or ``DS_DOCKERFILE_FOLDER_PATH``.
• Alternatively, if your competition work only demands basic libraries, you may set ``DS_CODER_COSTEER_ENV_TYPE`` to conda. In this mode, you must create a local conda environment named “kaggle” and pre-install the necessary packages. RD-Agent will execute the competition code within this “kaggle” conda environment.
.. code-block:: sh
# Configure the runtime environment: choice between 'docker' (default) or 'conda'
dotenv set DS_CODER_COSTEER_ENV_TYPE docker
- 🚀 **Run the Application**
- You can directly run the application by using the following command:
.. code-block:: sh
rdagent data_science --competition <Competition ID>
- 📥 **Visualize the R&D Process**
- We provide a web UI to visualize the log. You just need to run:
.. code-block:: sh
streamlit run rdagent/log/ui/dsapp.py
- Then you can input the log path and visualize the R&D process.
- **Additional Guidance**
- **Combine different LLM Models at R&D Stage**
- You can combine different LLM models at the R&D stage.
- By default, when you set environment variable ``CHAT_MODEL``, it covers both R&D stages. When customizing the model for the development stage, you can set:
.. code-block:: sh
# This example sets the model to "o3-mini". For some models, the reasoning effort shoule be set to "None".
dotenv set LITELLM_CHAT_MODEL_MAP '{"coding":{"model":"o3-mini","reasoning_effort":"high"},"running":{"model":"o3-mini","reasoning_effort":"high"}}'
-272
View File
@@ -1,272 +0,0 @@
.. _kaggle_agent:
=======================
Kaggle Agent
=======================
**🤖 Automated Feature Engineering & Model Tuning Evolution**
------------------------------------------------------------------------------------------
🎨 Design
~~~~~~~~~~~
.. image:: kaggle_design.png
:alt: Design of Kaggle Agent
:align: center
📖 Background
~~~~~~~~~~~~~~
In the landscape of data science competitions, Kaggle serves as the ultimate arena where data enthusiasts harness the power of algorithms to tackle real-world challenges.
The Kaggle Agent stands as a pivotal tool, empowering participants to seamlessly integrate cutting-edge models and datasets, transforming raw data into actionable insights.
By utilizing the **Kaggle Agent**, data scientists can craft innovative solutions that not only uncover hidden patterns but also drive significant advancements in predictive accuracy and model robustness.
🌟 Introduction
~~~~~~~~~~~~~~~~
In this scenario, our automated system proposes hypothesis, choose action, implements code, conducts validation, and utilizes feedback in a continuous, iterative process.
The goal is to automatically optimize performance metrics within the validation set or Kaggle Leaderboard, ultimately discovering the most efficient features and models through autonomous research and development.
Here's an enhanced outline of the steps:
**Step 1 : Hypothesis Generation 🔍**
- Generate and propose initial hypotheses based on previous experiment analysis and domain expertise, with thorough reasoning and financial justification.
**Step 2 : Experiment Creation ✨**
- Transform the hypothesis into a task.
- Choose a specific action within feature engineering or model tuning.
- Develop, define, and implement a new feature or model, including its name, description, and formulation.
**Step 3 : Model/Feature Implementation 👨‍💻**
- Implement the model code based on the detailed description.
- Evolve the model iteratively as a developer would, ensuring accuracy and efficiency.
**Step 4 : Validation on Test Set or Kaggle 📉**
- Validate the newly developed model using the test set or Kaggle dataset.
- Assess the model's effectiveness and performance based on the validation results.
**Step 5: Feedback Analysis 🔍**
- Analyze validation results to assess performance.
- Use insights to refine hypotheses and enhance the model.
**Step 6: Hypothesis Refinement ♻️**
- Adjust hypotheses based on validation feedback.
- Iterate the process to continuously improve the model.
🧭 Example Guide
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 🔧 **Set up RD-Agent Environment**
- Before you start, please make sure you have installed RD-Agent and configured the environment for RD-Agent correctly. If you want to know how to install and configure the RD-Agent, please refer to the `documentation <../installation_and_configuration.html>`_.
- 🔨 **Configuring the Kaggle API**
- Register and login on the `Kaggle <https://www.kaggle.com/>`_ website.
- Click on the avatar (usually in the top right corner of the page) -> ``Settings`` -> ``Create New Token``, A file called ``kaggle.json`` will be downloaded.
- Move ``kaggle.json`` to ``~/.config/kaggle/``
- Modify the permissions of the ``kaggle.json`` file.
.. code-block:: sh
chmod 600 ~/.config/kaggle/kaggle.json
- For more information about Kaggle API Settings, refer to the `Kaggle API <https://github.com/Kaggle/kaggle-api>`_.
- 🔩 **Setting the Environment variables at .env file**
- Determine the path where the data will be stored and add it to the ``.env`` file.
.. code-block:: sh
dotenv set KG_LOCAL_DATA_PATH <your local directory>/kaggle_data
- 📥 **Download Competition Data**
- Kaggle competition data, contains two parts: competition description file (json file) and competition dataset (zip file).
- **How to get the competition description file**
- *Manual Download (General User Suggestions):*
- Download the competition description file prepared in advance, and extract it to the specified directory.
.. code-block:: sh
wget https://github.com/SunsetWolf/rdagent_resource/releases/download/kaggle_data/kaggle_data.zip
unzip kaggle_data.zip -d <your local directory>/kaggle_data
- *Automatic Download (Developer Suggestions):*
- Alternatively, you can choose to download the competition description file automatically when you run the program, but it requires ``chromedriver`` to be installed, as follows:
.. code-block:: sh
# install chrome
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
google-chrome --version
# install chromedriver
wget "https://storage.googleapis.com/chrome-for-testing-public/$(google-chrome --version | grep -oP '\d+\.\d+\.\d+\.\d+')/linux64/chromedriver-linux64.zip"
unzip chromedriver-linux64.zip
cd chromedriver-linux64
sudo mv chromedriver /usr/local/bin
sudo chmod +x /usr/local/bin/chromedriver
chromedriver --version
- **How to get the competition dataset**
- The competition dataset is downloaded and extracted automatically when the program is run. If the zip file exists, the download will be skipped, if the unzip folder exists, the unzip will be skipped.
- **Correct directory structure (Here is an example of competition data with id sf-crime)**
.. code-block:: text
kaggle_data
└── zip_files
| └── sf-crime.zip
├── sf-crime.json
└── sf-crime
└── ...
- ``kaggle_data/zip_files/sf-crime.zip:`` Competition dataset zip files downloaded from the Kaggle website.
- ``kaggle_data/sf-crime.json:`` Competition description file.
- ``kaggle_data/sf-crime:`` The target folder for unzipping the competition dataset.
- 🗳️ **Join the competition**
- If your Kaggle API account has not joined a competition, you will need to join the competition before running the program.
- At the bottom of the competition details page, you can find the ``Join the competition`` button, click on it and select ``I Understand and Accept`` to join the competition.
- In the **Competition List Available** below, you can jump to the competition details page.
- 🚀 **Run the Application**
- You can directly run the application by using the following command:
.. code-block:: sh
rdagent kaggle --competition <Competition ID>
- 📤 **Submit the Result Automatically or Manually**
- If Auto: You need to set ``KG_AUTO_SUBMIT`` to ``true`` in the ``.env`` file.
.. code-block:: sh
dotenv set KG_AUTO_SUBMIT true
- Else: You can download the prediction results from the UI interface and submit them manually. For more details, refer to the :doc:`UI guide <../ui>`.
📋 Competition List Available
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| **index** | **Competition Name** | **Task** | **Modal** | **ID** |
+===========+===================================+==================+===========+=========================================================================================================+
| 01 | Media Campaign Cost Dataset | Regression | Tabular | `playground-series-s3e11 <https://www.kaggle.com/competitions/playground-series-s3e11/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 02 | Wild Blueberry Yield Dataset | Regression | Tabular | `playground-series-s3e14 <https://www.kaggle.com/competitions/playground-series-s3e14/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 03 | Crab Age Dataset | Regression | Tabular | `playground-series-s3e16 <https://www.kaggle.com/competitions/playground-series-s3e16/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 04 | Flood Prediction Dataset | Regression | Tabular | `playground-series-s4e5 <https://www.kaggle.com/competitions/playground-series-s4e5/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 05 | Used Car Prices Dataset | Regression | Tabular | `playground-series-s4e9 <https://www.kaggle.com/competitions/playground-series-s4e9/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 06 | Cirrhosis Outcomes Dataset | Multi-Class | Tabular | `playground-series-s3e26 <https://www.kaggle.com/competitions/playground-series-s3e26/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 07 | San Francisco Crime Classification| Multi-Class | Tabular | `sf-crime <https://www.kaggle.com/competitions/sf-crime/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 08 | Poisonous Mushrooms Dataset | Classification | Tabular | `playground-series-s4e8 <https://www.kaggle.com/competitions/playground-series-s4e8/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 09 | Spaceship Titanic | Classification | Tabular | `spaceship-titanic <https://www.kaggle.com/competitions/spaceship-titanic/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 10 | Forest Cover Type Prediction | Classification | Tabular | `forest-cover-type-prediction <https://www.kaggle.com/competitions/forest-cover-type-prediction/data>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| 11 | Digit Recognizer | Classification | Image | `digit-recognizer <https://www.kaggle.com/competitions/digit-recognizer>`_ |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
| To be continued ... |
+-----------+-----------------------------------+------------------+-----------+---------------------------------------------------------------------------------------------------------+
🎨 Customize one template for a new competition
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to facilitate RD-Agent to generate competition codes, we have specified a competition code structure:
.. image:: kaggle_template.png
:alt: Design of Kaggle Code Template
:align: center
- **feature directory** contains the feature engineering code. Generally no modification is required.
- **model directory** contains the model codes.
select_xx.py is used to select different features according to different models.
model_xx.py is the basic code of different models. Generally, only some initial parameters need to be adjusted.
- **fea_share_preprocess.py** is some basic preprocessing code shared by different models. The degree of customization here is high, but the preprocess_script() function needs to be retained, which will be called by train.py
- **train.py** is the main code, which connects all the codes and is also the code called during the final execution.
**We will soon provide a tool for automatic/semi-automatic template generation.**
If you want to try a different competition now, you can refer to our current template structure and content to write a new template.
🎯 Roadmap
~~~~~~~~~~~
**Completed:**
- **Kaggle Project Schema Design**
- **RD-Agent Integration with kaggle schema**
**Ongoing:**
- **Template auto generation**
- **Bench Optimization**
- **Online Bench**
- **RealMLBench**
- Ongoing integration
- Auto online submission
- Batch Evaluation
- **Offline Bench**
- MLE-Bench
🛠️ Usage of modules
~~~~~~~~~~~~~~~~~~~~~
.. _Env Config:
- **Env Config**
The following environment variables can be set in the `.env` file to customize the application's behavior:
.. autopydantic_settings:: rdagent.app.kaggle.conf.KaggleBasePropSetting
:settings-show-field-summary: False
:exclude-members: Config
.. autopydantic_settings:: rdagent.components.coder.factor_coder.config.FactorCoSTEERSettings
:settings-show-field-summary: False
:members: coder_use_cache, file_based_execution_timeout, select_method, max_loop
:exclude-members: Config, fail_task_trial_limit, v1_query_former_trace_limit, v1_query_similar_success_limit, v2_query_component_limit, v2_query_error_limit, v2_query_former_trace_limit, v2_error_summary, v2_knowledge_sampler, v2_add_fail_attempt_to_latest_successful_execution, new_knowledge_base_path, knowledge_base_path, data_folder, data_folder_debug
:no-index:
-128
View File
@@ -1,128 +0,0 @@
.. _model_agent_med:
=======================
Medical Model Agent
=======================
**🤖 Automated Medical Predtion Model Evolution**
------------------------------------------------------------------------------------------
📖 Background
~~~~~~~~~~~~~~
In this scenario, we consider the problem of risk prediction from patients' ICU monitoring data. We use the a public EHR dataset - MIMIC-III and extract a binary classification task for evaluating the framework.
In this task, we aim at predicting the whether the patients will suffer from Acute Respiratory Failure (ARF) based their first 12 hours ICU monitoring data.
🎥 `Demo <https://rdagent.azurewebsites.net/dmm>`_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. raw:: html
<div style="display: flex; justify-content: center; align-items: center;">
<video width="600" controls>
<source src="https://rdagent.azurewebsites.net/media/1653542fc1b9fa14a306c35c1b1fc48288f980793f38abe82b023af9.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</div>
🌟 Introduction
~~~~~~~~~~~~~~~~
In this scenario, our automated system proposes hypothesis, constructs model, implements code, receives back-testing, and uses feedbacks.
Hypothesis is iterated in this continuous process.
The system aims to automatically optimise performance metrics of medical prediction thereby finding the optimised code through autonomous research and development.
Here's an enhanced outline of the steps:
**Step 1 : Hypothesis Generation 🔍**
- Generate and propose initial hypotheses based on previous experiment analysis and domain expertise, with thorough reasoning and justification.
**Step 2 : Model Creation ✨**
- Transform the hypothesis into a model.
- Develop, define, and implement a machine learning model, including its name, description, and formulation.
**Step 3 : Model Implementation 👨‍💻**
- Implement the model code based on the detailed description.
- Evolve the model iteratively as a developer would, ensuring accuracy and efficiency.
**Step 4 : Backtesting with MIMIC-III 📉**
- Conduct backtesting using the newly developed model on the extracted task from MIMIC-III.
- Evaluate the model's effectiveness and performance in terms of AUROC score.
**Step 5 : Feedback Analysis 🔍**
- Analyze backtest results to assess performance.
- Incorporate feedback to refine hypotheses and improve the model.
**Step 6 :Hypothesis Refinement ♻️**
- Refine hypotheses based on feedback from backtesting.
- Repeat the process to continuously improve the model.
⚡ Quick Start
~~~~~~~~~~~~~~~~~
Please refer to the installation part in :doc:`../installation_and_configuration` to prepare your system dependency.
You can try our demo by running the following command:
- 🐍 Create a Conda Environment
- Create a new conda environment with Python (3.10 and 3.11 are well tested in our CI):
.. code-block:: sh
conda create -n rdagent python=3.10
- Activate the environment:
.. code-block:: sh
conda activate rdagent
- 📦 Install the RDAgent
- You can install the RDAgent package from PyPI:
.. code-block:: sh
pip install rdagent
- 📦 Request PhysioNet Account
- Apply for an account at `PhysioNet <https://physionet.org/>`_.
- Request access to FIDDLE preprocessed data: `FIDDLE Dataset <https://physionet.org/content/mimic-eicu-fiddle-feature/1.0.0/>`_.
- Place your username and password in `.env`.
.. code-block:: bash
cat << EOF >> .env
DM_USERNAME=<your_username>
DM_PASSWORD=<your_password>
EOF
- 🚀 Run the Application
- You can directly run the application by using the following command:
.. code-block:: sh
rdagent med_model
🛠️ Usage of modules
~~~~~~~~~~~~~~~~~~~~~
.. _Env Config:
- **Env Config**
The following environment variables can be set in the `.env` file to customize the application's behavior:
.. autopydantic_settings:: rdagent.app.data_mining.conf.MedBasePropSetting
:settings-show-field-summary: False
:exclude-members: Config
+113
View File
@@ -0,0 +1,113 @@
.. _quant_agent_fin:
=====================
Finance Quant Agent
=====================
**🥇The First Data-Centric Quant Multi-Agent Framework RD-Agent(Q)**
---------------------------------------------------------------------
R&D-Agent for Quantitative Finance, in short **RD-Agent(Q)**, is the first data-centric, multi-agent framework designed to automate the full-stack research and development of quantitative strategies via coordinated factor-model co-optimization.
You can learn more details about **RD-Agent(Q)** through the `paper <https://arxiv.org/abs/2505.15155>`_.
⚡ Quick Start
~~~~~~~~~~~~~~~~~
Before you start, please make sure you have installed RD-Agent and configured the environment for RD-Agent correctly. If you want to know how to install and configure the RD-Agent, please refer to the `documentation <../installation_and_configuration.html>`_.
Then, you can run the framework by running the following command:
- 🐍 Create a Conda Environment
- Create a new conda environment with Python (3.10 and 3.11 are well tested in our CI):
.. code-block:: sh
conda create -n rdagent python=3.10
- Activate the environment:
.. code-block:: sh
conda activate rdagent
- 📦 Install the RDAgent
- You can install the RDAgent package from PyPI:
.. code-block:: sh
pip install rdagent
- 🚀 Run the Application
- You can directly run the application by using the following command:
.. code-block:: sh
rdagent fin_quant
🛠️ Usage of modules
~~~~~~~~~~~~~~~~~~~~~
.. _Env Config:
- **Env Config**
The following environment variables can be set in the `.env` file to customize the application's behavior:
.. autopydantic_settings:: rdagent.app.qlib_rd_loop.conf.QuantBasePropSetting
:settings-show-field-summary: False
:exclude-members: Config
.. autopydantic_settings:: rdagent.components.coder.factor_coder.config.FactorCoSTEERSettings
:settings-show-field-summary: False
:members: coder_use_cache, data_folder, data_folder_debug, file_based_execution_timeout, select_method, max_loop, knowledge_base_path, new_knowledge_base_path
:exclude-members: Config, fail_task_trial_limit, v1_query_former_trace_limit, v1_query_similar_success_limit, v2_query_component_limit, v2_query_error_limit, v2_query_former_trace_limit, v2_error_summary, v2_knowledge_sampler
:no-index:
- **Qlib Configuration**
- The `.yaml` files in both the `model_template` and `factor_template` directories contain some configurations for running the corresponding models or factors within the Qlib framework. Below is an overview of their contents and roles:
- **General Settings**:
- **provider_uri**: Specifies the local Qlib data path, set to `~/.qlib/qlib_data/cn_data`.
- **market**: Configured to `csi300`, representing the CSI 300 index constituents.
- **benchmark**: Set to `SH000300`, used for backtesting evaluation.
- **Data Handling**:
- **start_time** and **end_time**: Define the full data range, from `2008-01-01` to `2022-08-01`.
- **fit_start_time**: The start date for fitting the model, set to `2008-01-01`.
- **fit_end_time**: The end date for fitting the model, set to `2014-12-31`.
- **features and labels**: Generated via a nested data loader combining `Alpha158DL` (for engineered features such as `RESI5`, `WVMA5`, `RSQR5`, `KLEN`, etc.) and a `StaticDataLoader` that loads precomputed factor files (`combined_factors_df.parquet`).
- **normalization**: The pipeline includes `RobustZScoreNorm` (with clipping) and `Fillna` for inference, and `DropnaLabel` with `CSZScoreNorm` for training.
- **Training Configuration**:
- **Model**: Uses `GeneralPTNN`, a PyTorch-based neural network model.
- **Dataset Splits**:
- **train**: `2008-01-01` to `2014-12-31`
- **valid**: `2015-01-01` to `2016-12-31`
- **test**: `2017-01-01` to `2020-08-01`
- **Default Hyperparameters** (can be overridden by command-line arguments):
- **n_epochs**: `100`
- **lr**: `2e-4`
- **early_stop**: `10`
- **batch_size**: `256`
- **weight_decay**: `0.0`
- **metric**: `loss`
- **loss**: `mse`
- **n_jobs**: `20`
- **GPU**: `0` (uses GPU 0 if available)
- **Backtesting and Evaluation**:
- **strategy**: `TopkDropoutStrategy`, which selects the top 50 stocks and randomly drops 5 to introduce exploration.
- **backtest period**: `2017-01-01` to `2020-08-01`
- **initial capital**: `100,000,000`
- **cost configuration**: Includes open/close costs, minimum transaction costs, and slippage control.
- **Recording and Analysis**:
- **SignalRecord**: Logs predicted signals.
- **SigAnaRecord**: Performs signal analysis without long-short separation.
- **PortAnaRecord**: Conducts portfolio analysis using the configured strategy and backtest settings.
+12 -4
View File
@@ -17,14 +17,14 @@ from importlib.resources import path as rpath
import fire
from rdagent.app.data_mining.model import main as med_model
from rdagent.app.data_science.loop import main as data_science
from rdagent.app.general_model.general_model import (
extract_models_and_implement as general_model,
)
from rdagent.app.kaggle.loop import main as kaggle_main
from rdagent.app.qlib_rd_loop.factor import main as fin_factor
from rdagent.app.qlib_rd_loop.factor_from_report import main as fin_factor_report
from rdagent.app.qlib_rd_loop.model import main as fin_model
from rdagent.app.qlib_rd_loop.quant import main as fin_quant
from rdagent.app.utils.health_check import health_check
from rdagent.app.utils.info import collect_info
@@ -44,17 +44,25 @@ def ui(port=19899, log_dir="", debug=False):
subprocess.run(cmds)
def server_ui(port=19899):
"""
start web app to show the log traces in real time
"""
subprocess.run(["python", "rdagent/log/server/app.py", f"--port={port}"])
def app():
fire.Fire(
{
"fin_factor": fin_factor,
"fin_factor_report": fin_factor_report,
"fin_model": fin_model,
"med_model": med_model,
"fin_quant": fin_quant,
"general_model": general_model,
"ui": ui,
"health_check": health_check,
"collect_info": collect_info,
"kaggle": kaggle_main,
"data_science": data_science,
"server_ui": server_ui,
}
)
-45
View File
@@ -1,45 +0,0 @@
from pathlib import Path
from pydantic_settings import SettingsConfigDict
from rdagent.components.workflow.conf import BasePropSetting
class MedBasePropSetting(BasePropSetting):
model_config = SettingsConfigDict(env_prefix="DM_", protected_namespaces=())
# 1) overriding the default
scen: str = "rdagent.scenarios.data_mining.experiment.model_experiment.DMModelScenario"
"""Scenario class for data mining model"""
hypothesis_gen: str = "rdagent.scenarios.data_mining.proposal.model_proposal.DMModelHypothesisGen"
"""Hypothesis generation class"""
hypothesis2experiment: str = "rdagent.scenarios.data_mining.proposal.model_proposal.DMModelHypothesis2Experiment"
"""Hypothesis to experiment class"""
coder: str = "rdagent.scenarios.data_mining.developer.model_coder.DMModelCoSTEER"
"""Coder class"""
runner: str = "rdagent.scenarios.data_mining.developer.model_runner.DMModelRunner"
"""Runner class"""
summarizer: str = "rdagent.scenarios.data_mining.developer.feedback.DMModelExperiment2Feedback"
"""Summarizer class"""
evolving_n: int = 10
"""Number of evolutions"""
evolving_n: int = 10
# 2) Extra config for the scenario
# physionet account
# NOTE: You should apply the account in https://physionet.org/
username: str = ""
"""Physionet account username"""
password: str = ""
"""Physionet account password"""
MED_PROP_SETTING = MedBasePropSetting()
-31
View File
@@ -1,31 +0,0 @@
import fire
from rdagent.app.data_mining.conf import MED_PROP_SETTING
from rdagent.components.workflow.rd_loop import RDLoop
from rdagent.core.exception import ModelEmptyError
class ModelRDLoop(RDLoop):
skip_loop_error = (ModelEmptyError,)
def main(path=None, step_n=None):
"""
Auto R&D Evolving loop for models in a medical scenario.
You can continue running session by
.. code-block:: python
dotenv run -- python rdagent/app/data_mining/model.py $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is a optional paramter
"""
if path is None:
model_loop = ModelRDLoop(MED_PROP_SETTING)
else:
model_loop = ModelRDLoop.load(path)
model_loop.run(step_n=step_n)
if __name__ == "__main__":
fire.Fire(main)
+83 -2
View File
@@ -1,19 +1,32 @@
from typing import Literal
from pydantic_settings import SettingsConfigDict
from rdagent.app.kaggle.conf import KaggleBasePropSetting
class DataScienceBasePropSetting(KaggleBasePropSetting):
# TODO: Kaggle Setting should be the subclass of DataScience
model_config = SettingsConfigDict(env_prefix="DS_", protected_namespaces=())
# Main components
## Scen
scen: str = "rdagent.scenarios.data_science.scen.KaggleScen"
"""Scenario class for data mining model"""
"""
Scenario class for data science tasks.
- For Kaggle competitions, use: "rdagent.scenarios.data_science.scen.KaggleScen"
- For custom data science scenarios, use: "rdagent.scenarios.data_science.scen.DataScienceScen"
"""
hypothesis_gen: str = "rdagent.scenarios.data_science.proposal.exp_gen.proposal.DSProposalV2ExpGen"
"""Hypothesis generation class"""
## Workflow Related
consecutive_errors: int = 5
## Coding Related
coding_fail_reanalyze_threshold: int = 3
debug_timeout: int = 600
"""The timeout limit for running on debugging data"""
full_timeout: int = 3600
@@ -24,11 +37,79 @@ class DataScienceBasePropSetting(KaggleBasePropSetting):
#### enable specification
spec_enabled: bool = True
#### proposal related
proposal_version: str = "v1"
coder_on_whole_pipeline: bool = False
max_trace_hist: int = 3
coder_max_loop: int = 10
runner_max_loop: int = 3
runner_max_loop: int = 1
rule_base_eval: bool = False
sample_data_by_LLM: bool = False
use_raw_description: bool = False
show_nan_columns: bool = False
#### model dump
enable_model_dump: bool = False
enable_doc_dev: bool = False
model_dump_check_level: Literal["medium", "high"] = "medium"
### knowledge base
enable_knowledge_base: bool = False
knowledge_base_version: str = "v1"
knowledge_base_path: str | None = None
idea_pool_json_path: str | None = None
### archive log folder after each loop
enable_log_archive: bool = True
log_archive_path: str | None = None
log_archive_temp_path: str | None = (
None # This is to store the mid tar file since writing the tar file is preferred in local storage then copy to target storage
)
#### Evaluation on Test related
eval_sub_dir: str = "eval" # TODO: fixme, this is not a good name
"""We'll use f"{DS_RD_SETTING.local_data_path}/{DS_RD_SETTING.eval_sub_dir}/{competition}"
to find the scriipt to evaluate the submission on test"""
"""---below are the settings for multi-trace---"""
### multi-trace related
max_trace_num: int = 3
"""The maximum number of traces to grow before merging"""
#### multi-trace:checkpoint selector
selector_name: str = "rdagent.scenarios.data_science.proposal.exp_gen.ckp_select.LatestCKPSelector"
"""The name of the selector to use"""
sota_count_window: int = 5
"""The number of trials to consider for SOTA count"""
sota_count_threshold: int = 1
"""The threshold for SOTA count"""
#### multi-trace: SOTA experiment selector
sota_exp_selector_name: str = "rdagent.scenarios.data_science.proposal.exp_gen.sota_exp_select.GlobalSOTASelector"
"""The name of the SOTA experiment selector to use"""
### multi-trace:inject optimals for multi-trace
# inject diverse when start a new sub-trace
enable_inject_diverse: bool = False
# inject knowledge at the root of the trace
enable_inject_knowledge_at_root: bool = False
# enable different version of DSExpGen for multi-trace
enable_multi_version_exp_gen: bool = False
exp_gen_version_list: str = "v3,v2"
#### multi-trace: time for final multi-trace merge
merge_hours: int = 2
"""The time for merge"""
#### multi-trace: max SOTA-retrieved number, used in AutoSOTAexpSelector
# constrains the number of SOTA experiments to retrieve, otherwise too many SOTA experiments to retrieve will cause the exceed of the context window of LLM
max_sota_retrieved_num: int = 10
"""The maximum number of SOTA experiments to retrieve in a LLM call"""
DS_RD_SETTING = DataScienceBasePropSetting()
+39 -169
View File
@@ -1,203 +1,73 @@
import asyncio
from pathlib import Path
from typing import Any
import fire
from rdagent.app.data_science.conf import DS_RD_SETTING
from rdagent.components.coder.data_science.ensemble import EnsembleCoSTEER
from rdagent.components.coder.data_science.ensemble.exp import EnsembleTask
from rdagent.components.coder.data_science.feature import FeatureCoSTEER
from rdagent.components.coder.data_science.feature.exp import FeatureTask
from rdagent.components.coder.data_science.model import ModelCoSTEER
from rdagent.components.coder.data_science.model.exp import ModelTask
from rdagent.components.coder.data_science.pipeline import PipelineCoSTEER
from rdagent.components.coder.data_science.pipeline.exp import PipelineTask
from rdagent.components.coder.data_science.raw_data_loader import DataLoaderCoSTEER
from rdagent.components.coder.data_science.raw_data_loader.exp import DataLoaderTask
from rdagent.components.coder.data_science.workflow import WorkflowCoSTEER
from rdagent.components.coder.data_science.workflow.exp import WorkflowTask
from rdagent.components.workflow.conf import BasePropSetting
from rdagent.components.workflow.rd_loop import RDLoop
from rdagent.core.exception import CoderError, RunnerError
from rdagent.core.proposal import ExperimentFeedback
from rdagent.core.scenario import Scenario
from rdagent.core.utils import import_class
from rdagent.log import rdagent_logger as logger
from rdagent.scenarios.data_science.dev.feedback import DSExperiment2Feedback
from rdagent.scenarios.data_science.dev.runner import DSCoSTEERRunner
from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
from rdagent.scenarios.data_science.proposal.exp_gen import DSExpGen, DSTrace
from rdagent.scenarios.kaggle.kaggle_crawler import download_data
class DataScienceRDLoop(RDLoop):
skip_loop_error = (CoderError, RunnerError)
def __init__(self, PROP_SETTING: BasePropSetting):
logger.log_object(PROP_SETTING.competition, tag="competition")
scen: Scenario = import_class(PROP_SETTING.scen)(PROP_SETTING.competition)
### shared components in the workflow # TODO: check if
knowledge_base = (
import_class(PROP_SETTING.knowledge_base)(PROP_SETTING.knowledge_base_path, scen)
if PROP_SETTING.knowledge_base != ""
else None
)
# 1) task generation from scratch
# self.scratch_gen: tuple[HypothesisGen, Hypothesis2Experiment] = DummyHypothesisGen(scen),
# 2) task generation from a complete solution
# self.exp_gen: ExpGen = import_class(PROP_SETTING.exp_gen)(scen)
self.exp_gen = DSExpGen(scen)
self.data_loader_coder = DataLoaderCoSTEER(scen)
self.feature_coder = FeatureCoSTEER(scen)
self.model_coder = ModelCoSTEER(scen)
self.ensemble_coder = EnsembleCoSTEER(scen)
self.workflow_coder = WorkflowCoSTEER(scen)
self.pipeline_coder = PipelineCoSTEER(scen)
self.runner = DSCoSTEERRunner(scen)
# self.summarizer: Experiment2Feedback = import_class(PROP_SETTING.summarizer)(scen)
# logger.log_object(self.summarizer, tag="summarizer")
# self.trace = KGTrace(scen=scen, knowledge_base=knowledge_base)
self.trace = DSTrace(scen=scen)
self.summarizer = DSExperiment2Feedback(scen)
super(RDLoop, self).__init__()
def direct_exp_gen(self, prev_out: dict[str, Any]):
exp = self.exp_gen.gen(self.trace)
logger.log_object(exp)
# FIXME: this is for LLM debug webapp, remove this when the debugging is done.
logger.log_object(exp, tag="debug_exp_gen")
return exp
def coding(self, prev_out: dict[str, Any]):
exp = prev_out["direct_exp_gen"]
for tasks in exp.pending_tasks_list:
exp.sub_tasks = tasks
with logger.tag(f"{exp.sub_tasks[0].__class__.__name__}"):
if isinstance(exp.sub_tasks[0], DataLoaderTask):
exp = self.data_loader_coder.develop(exp)
elif isinstance(exp.sub_tasks[0], FeatureTask):
exp = self.feature_coder.develop(exp)
elif isinstance(exp.sub_tasks[0], ModelTask):
exp = self.model_coder.develop(exp)
elif isinstance(exp.sub_tasks[0], EnsembleTask):
exp = self.ensemble_coder.develop(exp)
elif isinstance(exp.sub_tasks[0], WorkflowTask):
exp = self.workflow_coder.develop(exp)
elif isinstance(exp.sub_tasks[0], PipelineTask):
exp = self.pipeline_coder.develop(exp)
else:
raise NotImplementedError(f"Unsupported component in DataScienceRDLoop: {exp.hypothesis.component}")
exp.sub_tasks = []
logger.log_object(exp)
return exp
def running(self, prev_out: dict[str, Any]):
exp: DSExperiment = prev_out["coding"]
if exp.is_ready_to_run():
new_exp = self.runner.develop(exp)
logger.log_object(new_exp)
return new_exp
return exp
def feedback(self, prev_out: dict[str, Any]) -> ExperimentFeedback:
"""
Assumption:
- If we come to feedback phase, the previous development steps are successful.
"""
exp: DSExperiment = prev_out["running"]
if self.trace.next_incomplete_component() is None or DS_RD_SETTING.coder_on_whole_pipeline:
# we have alreadly completed components in previous trace. So current loop is focusing on a new proposed idea.
# So we need feedback for the proposal.
feedback = self.summarizer.generate_feedback(exp, self.trace)
else:
# Otherwise, it is on drafting stage, don't need complicated feedbacks.
feedback = ExperimentFeedback(
reason=f"{exp.hypothesis.component} is completed.",
decision=True,
)
logger.log_object(feedback)
return feedback
def record(self, prev_out: dict[str, Any]):
e = prev_out.get(self.EXCEPTION_KEY, None)
if e is None:
self.trace.hist.append((prev_out["running"], prev_out["feedback"]))
else:
self.trace.hist.append(
(
prev_out["direct_exp_gen"] if isinstance(e, CoderError) else prev_out["coding"],
ExperimentFeedback.from_exception(e),
)
)
if (
self.trace.sota_experiment() is None
and len(self.trace.hist) >= DS_RD_SETTING.consecutive_errors
and not DS_RD_SETTING.coder_on_whole_pipeline
):
# if {in inital/drafting stage} and {tried enough times}
for _, fb in self.trace.hist[-DS_RD_SETTING.consecutive_errors :]:
if fb:
break # any success will stop restarting.
else: # otherwise restart it
logger.error("Consecutive errors reached the limit. Dumping trace.")
logger.log_object(self.trace, tag="trace before restart")
self.trace = DSTrace(scen=self.trace.scen, knowledge_base=self.trace.knowledge_base)
logger.log_object(self.trace, tag="trace")
logger.log_object(self.trace.sota_experiment(), tag="SOTA experiment")
from rdagent.scenarios.data_science.loop import DataScienceRDLoop
def main(
path=None, output_path=None, step_n=None, loop_n=None, competition="bms-molecular-translation", do_truncate=True
path: str | None = None,
checkout: bool | str | Path = True,
step_n: int | None = None,
loop_n: int | None = None,
timeout: str | None = None,
competition="bms-molecular-translation",
replace_timer=True,
exp_gen_cls: str | None = None,
):
"""
Parameters
----------
path :
path like `$LOG_PATH/__session__/1/0_propose`. It indicates that we restore the state that after finish the step 0 in loop 1
output_path :
path like `$LOG_PATH`. It indicates that where we want to save our session and log information.
A path like `$LOG_PATH/__session__/1/0_propose`. This indicates that we restore the state after finishing step 0 in loop 1.
checkout :
Used only when a path is provided.
Can be True, False, or a path.
Default is True.
- If True, the new loop will use the existing folder and clear logs for sessions after the one corresponding to the given path.
- If False, the new loop will use the existing folder but keep the logs for sessions after the one corresponding to the given path.
- If a path (or a str like Path) is provided, the new loop will be saved to that path, leaving the original path unchanged.
step_n :
How many steps to run; if None, it will run forever until error or KeyboardInterrupt
Number of steps to run; if None, the process will run indefinitely until an error or KeyboardInterrupt occurs.
loop_n :
How many loops to run; if None, it will run forever until error or KeyboardInterrupt
- if current loop is incomplete, it will be counted as the first loop for completion.
- if both step_n and loop_n are provided, the process will stop as soon as either condition is met.
Number of loops to run; if None, the process will run indefinitely until an error or KeyboardInterrupt occurs.
- If the current loop is incomplete, it will be counted as the first loop for completion.
- If both step_n and loop_n are provided, the process will stop as soon as either condition is met.
competition :
do_truncate :
If set to True, the logger will truncate the future log messages by calling `logger.storage.truncate`.
Competition name.
replace_timer :
If a session is loaded, determines whether to replace the timer with session.timer.
exp_gen_cls :
When there are different stages, the exp_gen can be replaced with the new proposal.
Auto R&D Evolving loop for models in a Kaggle scenario.
You can continue running session by
You can continue running a session by using the command:
.. code-block:: bash
dotenv run -- python rdagent/app/data_science/loop.py [--competition titanic] $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is a optional parameter
rdagent kaggle --competition playground-series-s4e8 # You are encouraged to use this one.
dotenv run -- python rdagent/app/data_science/loop.py [--competition titanic] $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is an optional parameter
rdagent kaggle --competition playground-series-s4e8 # This command is recommended.
"""
if competition is not None:
DS_RD_SETTING.competition = competition
if DS_RD_SETTING.competition:
if DS_RD_SETTING.scen.endswith("KaggleScen"):
download_data(competition=DS_RD_SETTING.competition, settings=DS_RD_SETTING)
else:
if not Path(f"{DS_RD_SETTING.local_data_path}/{competition}").exists():
logger.error(f"Please prepare data for competition {competition} first.")
return
else:
if not DS_RD_SETTING.competition:
logger.error("Please specify competition name.")
if path is None:
kaggle_loop = DataScienceRDLoop(DS_RD_SETTING)
else:
kaggle_loop = DataScienceRDLoop.load(path, output_path, do_truncate)
kaggle_loop.run(step_n=step_n, loop_n=loop_n)
kaggle_loop: DataScienceRDLoop = DataScienceRDLoop.load(path, checkout=checkout, replace_timer=replace_timer)
# replace exp_gen if we have new class
if exp_gen_cls is not None:
kaggle_loop.exp_gen = import_class(exp_gen_cls)(kaggle_loop.exp_gen.scen)
asyncio.run(kaggle_loop.run(step_n=step_n, loop_n=loop_n, all_duration=timeout))
if __name__ == "__main__":
+9 -12
View File
@@ -30,18 +30,15 @@ def extract_models_and_implement(report_file_path: str) -> None:
Returns:
None
"""
with logger.tag("init"):
scenario = GeneralModelScenario()
logger.log_object(scenario, tag="scenario")
with logger.tag("r"):
# Save Relevant Images
img = extract_first_page_screenshot_from_pdf(report_file_path)
logger.log_object(img, tag="pdf_image")
exp = ModelExperimentLoaderFromPDFfiles().load(report_file_path)
logger.log_object(exp, tag="load_experiment")
with logger.tag("d"):
exp = QlibModelCoSTEER(scenario).develop(exp)
logger.log_object(exp, tag="developed_experiment")
scenario = GeneralModelScenario()
logger.log_object(scenario, tag="scenario")
# Save Relevant Images
img = extract_first_page_screenshot_from_pdf(report_file_path)
logger.log_object(img, tag="pdf_image")
exp = ModelExperimentLoaderFromPDFfiles().load(report_file_path)
logger.log_object(exp, tag="load_experiment")
exp = QlibModelCoSTEER(scenario).develop(exp)
logger.log_object(exp, tag="developed_experiment")
if __name__ == "__main__":
+2
View File
@@ -46,9 +46,11 @@ class KaggleBasePropSetting(ExtendedBaseSettings):
local_data_path: str = ""
"""Folder storing Kaggle competition data"""
# Evaluation on Test related
if_using_mle_data: bool = False
auto_submit: bool = False
"""Automatically upload and submit each experiment result to Kaggle platform"""
# Conditionally set the knowledge_base based on the use of graph RAG
knowledge_base: str = ""
"""Knowledge base class, uses 'KGKnowledgeGraph' when advanced graph-based RAG is enabled, otherwise empty."""
+72 -79
View File
@@ -28,90 +28,83 @@ from rdagent.scenarios.kaggle.proposal.proposal import KGTrace
class KaggleRDLoop(RDLoop):
def __init__(self, PROP_SETTING: BasePropSetting):
with logger.tag("init"):
scen: Scenario = import_class(PROP_SETTING.scen)(PROP_SETTING.competition)
logger.log_object(scen, tag="scenario")
knowledge_base = (
import_class(PROP_SETTING.knowledge_base)(PROP_SETTING.knowledge_base_path, scen)
if PROP_SETTING.knowledge_base != ""
else None
)
logger.log_object(knowledge_base, tag="knowledge_base")
self.hypothesis_gen: HypothesisGen = import_class(PROP_SETTING.hypothesis_gen)(scen)
logger.log_object(self.hypothesis_gen, tag="hypothesis generator")
self.hypothesis2experiment: Hypothesis2Experiment = import_class(PROP_SETTING.hypothesis2experiment)()
logger.log_object(self.hypothesis2experiment, tag="hypothesis2experiment")
self.feature_coder: Developer = import_class(PROP_SETTING.feature_coder)(scen)
logger.log_object(self.feature_coder, tag="feature coder")
self.model_feature_selection_coder: Developer = import_class(PROP_SETTING.model_feature_selection_coder)(
scen
)
logger.log_object(self.model_feature_selection_coder, tag="model feature selection coder")
self.model_coder: Developer = import_class(PROP_SETTING.model_coder)(scen)
logger.log_object(self.model_coder, tag="model coder")
self.feature_runner: Developer = import_class(PROP_SETTING.feature_runner)(scen)
logger.log_object(self.feature_runner, tag="feature runner")
self.model_runner: Developer = import_class(PROP_SETTING.model_runner)(scen)
logger.log_object(self.model_runner, tag="model runner")
self.summarizer: Experiment2Feedback = import_class(PROP_SETTING.summarizer)(scen)
logger.log_object(self.summarizer, tag="summarizer")
self.trace = KGTrace(scen=scen, knowledge_base=knowledge_base)
super(RDLoop, self).__init__()
scen: Scenario = import_class(PROP_SETTING.scen)(PROP_SETTING.competition)
logger.log_object(scen, tag="scenario")
knowledge_base = (
import_class(PROP_SETTING.knowledge_base)(PROP_SETTING.knowledge_base_path, scen)
if PROP_SETTING.knowledge_base != ""
else None
)
logger.log_object(knowledge_base, tag="knowledge_base")
self.hypothesis_gen: HypothesisGen = import_class(PROP_SETTING.hypothesis_gen)(scen)
logger.log_object(self.hypothesis_gen, tag="hypothesis generator")
self.hypothesis2experiment: Hypothesis2Experiment = import_class(PROP_SETTING.hypothesis2experiment)()
logger.log_object(self.hypothesis2experiment, tag="hypothesis2experiment")
self.feature_coder: Developer = import_class(PROP_SETTING.feature_coder)(scen)
logger.log_object(self.feature_coder, tag="feature coder")
self.model_feature_selection_coder: Developer = import_class(PROP_SETTING.model_feature_selection_coder)(scen)
logger.log_object(self.model_feature_selection_coder, tag="model feature selection coder")
self.model_coder: Developer = import_class(PROP_SETTING.model_coder)(scen)
logger.log_object(self.model_coder, tag="model coder")
self.feature_runner: Developer = import_class(PROP_SETTING.feature_runner)(scen)
logger.log_object(self.feature_runner, tag="feature runner")
self.model_runner: Developer = import_class(PROP_SETTING.model_runner)(scen)
logger.log_object(self.model_runner, tag="model runner")
self.summarizer: Experiment2Feedback = import_class(PROP_SETTING.summarizer)(scen)
logger.log_object(self.summarizer, tag="summarizer")
self.trace = KGTrace(scen=scen, knowledge_base=knowledge_base)
super(RDLoop, self).__init__()
def coding(self, prev_out: dict[str, Any]):
with logger.tag("d"): # develop
if prev_out["direct_exp_gen"]["propose"].action in [
KG_ACTION_FEATURE_ENGINEERING,
KG_ACTION_FEATURE_PROCESSING,
]:
exp = self.feature_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
elif prev_out["direct_exp_gen"]["propose"].action == KG_ACTION_MODEL_FEATURE_SELECTION:
exp = self.model_feature_selection_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
else:
exp = self.model_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
logger.log_object(exp.sub_workspace_list, tag="coder result")
if prev_out["direct_exp_gen"]["propose"].action in [
KG_ACTION_FEATURE_ENGINEERING,
KG_ACTION_FEATURE_PROCESSING,
]:
exp = self.feature_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
elif prev_out["direct_exp_gen"]["propose"].action == KG_ACTION_MODEL_FEATURE_SELECTION:
exp = self.model_feature_selection_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
else:
exp = self.model_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
logger.log_object(exp.sub_workspace_list, tag="coder result")
return exp
def running(self, prev_out: dict[str, Any]):
with logger.tag("ef"): # evaluate and feedback
if prev_out["direct_exp_gen"]["propose"].action in [
KG_ACTION_FEATURE_ENGINEERING,
KG_ACTION_FEATURE_PROCESSING,
]:
exp = self.feature_runner.develop(prev_out["coding"])
else:
exp = self.model_runner.develop(prev_out["coding"])
logger.log_object(exp, tag="runner result")
if KAGGLE_IMPLEMENT_SETTING.competition in [
"optiver-realized-volatility-prediction",
"covid19-global-forecasting-week-1",
]:
try:
python_files_to_notebook(
KAGGLE_IMPLEMENT_SETTING.competition, exp.experiment_workspace.workspace_path
)
except Exception as e:
logger.error(f"Merge python files to one file failed: {e}")
if KAGGLE_IMPLEMENT_SETTING.auto_submit:
csv_path = exp.experiment_workspace.workspace_path / "submission.csv"
try:
subprocess.run(
[
"kaggle",
"competitions",
"submit",
"-f",
str(csv_path.absolute()),
"-m",
str(csv_path.parent.absolute()),
KAGGLE_IMPLEMENT_SETTING.competition,
],
check=True,
)
except subprocess.CalledProcessError as e:
logger.error(f"Auto submission failed: \n{e}")
except Exception as e:
logger.error(f"Other exception when use kaggle api:\n{e}")
if prev_out["direct_exp_gen"]["propose"].action in [
KG_ACTION_FEATURE_ENGINEERING,
KG_ACTION_FEATURE_PROCESSING,
]:
exp = self.feature_runner.develop(prev_out["coding"])
else:
exp = self.model_runner.develop(prev_out["coding"])
logger.log_object(exp, tag="runner result")
if KAGGLE_IMPLEMENT_SETTING.competition in [
"optiver-realized-volatility-prediction",
"covid19-global-forecasting-week-1",
]:
try:
python_files_to_notebook(KAGGLE_IMPLEMENT_SETTING.competition, exp.experiment_workspace.workspace_path)
except Exception as e:
logger.error(f"Merge python files to one file failed: {e}")
if KAGGLE_IMPLEMENT_SETTING.auto_submit:
csv_path = exp.experiment_workspace.workspace_path / "submission.csv"
try:
subprocess.run(
[
"kaggle",
"competitions",
"submit",
"-f",
str(csv_path.absolute()),
"-m",
str(csv_path.parent.absolute()),
KAGGLE_IMPLEMENT_SETTING.competition,
],
check=True,
)
except subprocess.CalledProcessError as e:
logger.error(f"Auto submission failed: \n{e}")
except Exception as e:
logger.error(f"Other exception when use kaggle api:\n{e}")
return exp
+46 -2
View File
@@ -67,10 +67,54 @@ class FactorFromReportPropSetting(FactorBasePropSetting):
max_factors_per_exp: int = 10000
"""Maximum number of factors implemented per experiment"""
is_report_limit_enabled: bool = False
"""Limits report processing count if True; processes all if False"""
report_limit: int = 10000
"""Maximum number of reports to process"""
class QuantBasePropSetting(BasePropSetting):
model_config = SettingsConfigDict(env_prefix="QLIB_QUANT_", protected_namespaces=())
# 1) override base settings
scen: str = "rdagent.scenarios.qlib.experiment.quant_experiment.QlibQuantScenario"
"""Scenario class for Qlib Model"""
quant_hypothesis_gen: str = "rdagent.scenarios.qlib.proposal.quant_proposal.QlibQuantHypothesisGen"
"""Hypothesis generation class"""
model_hypothesis2experiment: str = "rdagent.scenarios.qlib.proposal.model_proposal.QlibModelHypothesis2Experiment"
"""Hypothesis to experiment class"""
model_coder: str = "rdagent.scenarios.qlib.developer.model_coder.QlibModelCoSTEER"
"""Coder class"""
model_runner: str = "rdagent.scenarios.qlib.developer.model_runner.QlibModelRunner"
"""Runner class"""
model_summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibModelExperiment2Feedback"
"""Summarizer class"""
factor_hypothesis2experiment: str = (
"rdagent.scenarios.qlib.proposal.factor_proposal.QlibFactorHypothesis2Experiment"
)
"""Hypothesis to experiment class"""
factor_coder: str = "rdagent.scenarios.qlib.developer.factor_coder.QlibFactorCoSTEER"
"""Coder class"""
factor_runner: str = "rdagent.scenarios.qlib.developer.factor_runner.QlibFactorRunner"
"""Runner class"""
factor_summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibFactorExperiment2Feedback"
"""Summarizer class"""
evolving_n: int = 10
"""Number of evolutions"""
action_selection: str = "bandit"
"""Action selection strategy: 'bandit' for bandit-based selection, 'llm' for LLM-based selection, 'random' for random selection"""
FACTOR_PROP_SETTING = FactorBasePropSetting()
FACTOR_FROM_REPORT_PROP_SETTING = FactorFromReportPropSetting()
MODEL_PROP_SETTING = ModelBasePropSetting()
QUANT_PROP_SETTING = QuantBasePropSetting()
+16 -9
View File
@@ -2,6 +2,8 @@
Factor workflow with session control
"""
import asyncio
from pathlib import Path
from typing import Any
import fire
@@ -16,16 +18,21 @@ class FactorRDLoop(RDLoop):
skip_loop_error = (FactorEmptyError,)
def running(self, prev_out: dict[str, Any]):
with logger.tag("ef"): # evaluate and feedback
exp = self.runner.develop(prev_out["coding"])
if exp is None:
logger.error(f"Factor extraction failed.")
raise FactorEmptyError("Factor extraction failed.")
logger.log_object(exp, tag="runner result")
exp = self.runner.develop(prev_out["coding"])
if exp is None:
logger.error(f"Factor extraction failed.")
raise FactorEmptyError("Factor extraction failed.")
logger.log_object(exp, tag="runner result")
return exp
def main(path=None, step_n=None):
def main(
path: str | None = None,
step_n: int | None = None,
loop_n: int | None = None,
all_duration: str | None = None,
checkout: bool | str | Path = True,
):
"""
Auto R&D Evolving loop for fintech factors.
@@ -39,8 +46,8 @@ def main(path=None, step_n=None):
if path is None:
model_loop = FactorRDLoop(FACTOR_PROP_SETTING)
else:
model_loop = FactorRDLoop.load(path)
model_loop.run(step_n=step_n)
model_loop = FactorRDLoop.load(path, checkout=checkout)
asyncio.run(model_loop.run(step_n=step_n, loop_n=loop_n, all_duration=all_duration))
if __name__ == "__main__":
+38 -55
View File
@@ -1,9 +1,9 @@
import asyncio
import json
from pathlib import Path
from typing import Any, Dict, Tuple
import fire
from jinja2 import Environment, StrictUndefined
from rdagent.app.qlib_rd_loop.conf import FACTOR_FROM_REPORT_PROP_SETTING
from rdagent.app.qlib_rd_loop.factor import FactorRDLoop
@@ -11,7 +11,7 @@ from rdagent.components.document_reader.document_reader import (
extract_first_page_screenshot_from_pdf,
load_and_process_pdfs_by_langchain,
)
from rdagent.core.prompts import Prompts
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.proposal import Hypothesis
from rdagent.log import rdagent_logger as logger
from rdagent.oai.llm_utils import APIBackend
@@ -19,11 +19,9 @@ from rdagent.scenarios.qlib.experiment.factor_experiment import QlibFactorExperi
from rdagent.scenarios.qlib.factor_experiment_loader.pdf_loader import (
FactorExperimentLoaderFromPDFfiles,
)
from rdagent.utils.agent.tpl import T
from rdagent.utils.workflow import LoopMeta
prompts_path = Path(__file__).parent / "prompts.yaml"
prompts = Prompts(file_path=prompts_path)
def generate_hypothesis(factor_result: dict, report_content: str) -> str:
"""
@@ -36,13 +34,9 @@ def generate_hypothesis(factor_result: dict, report_content: str) -> str:
Returns:
str: The generated hypothesis.
"""
system_prompt = (
Environment(undefined=StrictUndefined).from_string(prompts["hypothesis_generation"]["system"]).render()
)
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(prompts["hypothesis_generation"]["user"])
.render(factor_descriptions=json.dumps(factor_result), report_content=report_content)
system_prompt = T(".prompts:hypothesis_generation.system").r()
user_prompt = T(".prompts:hypothesis_generation.user").r(
factor_descriptions=json.dumps(factor_result), report_content=report_content
)
response = APIBackend().build_messages_and_create_chat_completion(
@@ -64,7 +58,7 @@ def generate_hypothesis(factor_result: dict, report_content: str) -> str:
)
def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> Tuple[QlibFactorExperiment, Hypothesis]:
def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> QlibFactorExperiment | None:
"""
Extract hypothesis and experiment details from report files.
@@ -72,17 +66,15 @@ def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> Tuple[Qlib
report_file_path (str): Path to the report file.
Returns:
Tuple[QlibFactorExperiment, Hypothesis]: The extracted experiment and generated hypothesis.
QlibFactorExperiment: An instance of QlibFactorExperiment containing the extracted details.
None: If no valid experiment is found in the report.
"""
with logger.tag("extract_factors_and_implement"):
with logger.tag("load_factor_tasks"):
exp = FactorExperimentLoaderFromPDFfiles().load(report_file_path)
if exp is None or exp.sub_tasks == []:
return None, None
exp = FactorExperimentLoaderFromPDFfiles().load(report_file_path)
if exp is None or exp.sub_tasks == []:
return None
with logger.tag("load_pdf_screenshot"):
pdf_screenshot = extract_first_page_screenshot_from_pdf(report_file_path)
logger.log_object(pdf_screenshot)
pdf_screenshot = extract_first_page_screenshot_from_pdf(report_file_path)
logger.log_object(pdf_screenshot, tag="load_pdf_screenshot")
docs_dict = load_and_process_pdfs_by_langchain(report_file_path)
@@ -99,7 +91,7 @@ def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> Tuple[Qlib
report_content = "\n".join(docs_dict.values())
hypothesis = generate_hypothesis(factor_result, report_content)
exp.hypothesis = hypothesis
return exp, hypothesis
return exp
class FactorReportLoop(FactorRDLoop, metaclass=LoopMeta):
@@ -112,49 +104,40 @@ class FactorReportLoop(FactorRDLoop, metaclass=LoopMeta):
else:
self.judge_pdf_data_items = [i for i in Path(report_folder).rglob("*.pdf")]
self.pdf_file_index = 0
self.valid_pdf_file_count = 0
self.current_loop_hypothesis = None
self.current_loop_exp = None
self.steps = ["propose_hypo_exp", "propose", "direct_exp_gen", "coding", "running", "feedback"]
self.loop_n = min(len(self.judge_pdf_data_items), FACTOR_FROM_REPORT_PROP_SETTING.report_limit)
self.shift_report = (
0 # some reports does not contain viable factor, so we ship some of them to avoid infinite loop
)
def propose_hypo_exp(self, prev_out: dict[str, Any]):
with logger.tag("r"):
while True:
if FACTOR_FROM_REPORT_PROP_SETTING.is_report_limit_enabled and self.valid_pdf_file_count > 15:
break
report_file_path = self.judge_pdf_data_items[self.pdf_file_index]
logger.info(f"Processing number {self.pdf_file_index} report: {report_file_path}")
self.pdf_file_index += 1
exp, hypothesis = extract_hypothesis_and_exp_from_reports(str(report_file_path))
async def direct_exp_gen(self, prev_out: dict[str, Any]):
while True:
if self.get_unfinished_loop_cnt(self.loop_idx) < RD_AGENT_SETTINGS.get_max_parallel():
report_file_path = self.judge_pdf_data_items[self.loop_idx + self.shift_report]
logger.info(f"Processing number {self.loop_idx} report: {report_file_path}")
exp = extract_hypothesis_and_exp_from_reports(str(report_file_path))
if exp is None:
self.shift_report += 1
self.loop_n -= 1
if self.loop_n < 0: # NOTE: on every step, we self.loop_n -= 1 at first.
raise self.LoopTerminationError("Reach stop criterion and stop loop")
continue
self.valid_pdf_file_count += 1
exp.based_experiments = [QlibFactorExperiment(sub_tasks=[], hypothesis=hypothesis)] + [
exp.based_experiments = [QlibFactorExperiment(sub_tasks=[], hypothesis=exp.hypothesis)] + [
t[0] for t in self.trace.hist if t[1]
]
exp.sub_workspace_list = exp.sub_workspace_list[: FACTOR_FROM_REPORT_PROP_SETTING.max_factors_per_exp]
exp.sub_tasks = exp.sub_tasks[: FACTOR_FROM_REPORT_PROP_SETTING.max_factors_per_exp]
logger.log_object(hypothesis, tag="hypothesis generation")
logger.log_object(exp.hypothesis, tag="hypothesis generation")
logger.log_object(exp.sub_tasks, tag="experiment generation")
self.current_loop_hypothesis = hypothesis
self.current_loop_exp = exp
return None
def propose(self, prev_out: dict[str, Any]):
return self.current_loop_hypothesis
def direct_exp_gen(self, prev_out: dict[str, Any]):
return {"propose": self.current_loop_hypothesis, "exp_gen": self.current_loop_exp}
return exp
await asyncio.sleep(1)
def coding(self, prev_out: dict[str, Any]):
with logger.tag("d"): # develop
exp = self.coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
logger.log_object(exp.sub_workspace_list, tag="coder result")
exp = self.coder.develop(prev_out["direct_exp_gen"])
logger.log_object(exp.sub_workspace_list, tag="coder result")
return exp
def main(report_folder=None, path=None, step_n=None):
def main(report_folder=None, path=None, all_duration=None, checkout=True):
"""
Auto R&D Evolving loop for fintech factors (the factors are extracted from finance reports).
@@ -166,11 +149,11 @@ def main(report_folder=None, path=None, step_n=None):
if path is None and report_folder is None:
model_loop = FactorReportLoop()
elif path is not None:
model_loop = FactorReportLoop.load(path)
model_loop = FactorReportLoop.load(path, checkout=checkout)
else:
model_loop = FactorReportLoop(report_folder=report_folder)
model_loop.run(step_n=step_n)
asyncio.run(model_loop.run(all_duration=all_duration))
if __name__ == "__main__":
+5 -3
View File
@@ -2,6 +2,8 @@
Model workflow with session control
"""
import asyncio
import fire
from rdagent.app.qlib_rd_loop.conf import MODEL_PROP_SETTING
@@ -13,7 +15,7 @@ class ModelRDLoop(RDLoop):
skip_loop_error = (ModelEmptyError,)
def main(path=None, step_n=None):
def main(path=None, step_n=None, loop_n=None, all_duration=None, checkout=True):
"""
Auto R&D Evolving loop for fintech models
@@ -27,8 +29,8 @@ def main(path=None, step_n=None):
if path is None:
model_loop = ModelRDLoop(MODEL_PROP_SETTING)
else:
model_loop = ModelRDLoop.load(path)
model_loop.run(step_n=step_n)
model_loop = ModelRDLoop.load(path, checkout=checkout)
asyncio.run(model_loop.run(step_n=step_n, loop_n=loop_n, all_duration=all_duration))
if __name__ == "__main__":
-4
View File
@@ -5,10 +5,6 @@ hypothesis_generation:
{
"hypothesis": "A clear and concise hypothesis based on the provided information.",
"reason": "A detailed explanation supporting the generated hypothesis.",
"concise_reason": "One line summary that focuses on the justification for the change that leads to the hypothesis (like a part of a knowledge that we are building)",
"concise_observation": "One line summary. It focuses on the observation of the given scenario, data characteristics, or previous experiences (failures & succeses).",
"concise_justification": "One line summary. It focuses on the justification for the change in new hypothesis and the route of exploration supporting the growth of the hypothesis, based on the observation. ",
"concise_knowledge": "One line summary. It focuses on a transferable knowledege that comes with the new hypothesis. Use conditional grammar. eg. "If...., ..; When..., .; and etc"
}
user: |-
+137
View File
@@ -0,0 +1,137 @@
"""
Quant (Factor & Model) workflow with session control
"""
import asyncio
from typing import Any
import fire
from rdagent.app.qlib_rd_loop.conf import QUANT_PROP_SETTING
from rdagent.components.workflow.conf import BasePropSetting
from rdagent.components.workflow.rd_loop import RDLoop
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.developer import Developer
from rdagent.core.exception import FactorEmptyError, ModelEmptyError
from rdagent.core.proposal import (
Experiment2Feedback,
Hypothesis2Experiment,
HypothesisFeedback,
HypothesisGen,
)
from rdagent.core.scenario import Scenario
from rdagent.core.utils import import_class
from rdagent.log import rdagent_logger as logger
from rdagent.scenarios.qlib.proposal.quant_proposal import QuantTrace
class QuantRDLoop(RDLoop):
skip_loop_error = (
FactorEmptyError,
ModelEmptyError,
)
def __init__(self, PROP_SETTING: BasePropSetting):
scen: Scenario = import_class(PROP_SETTING.scen)()
logger.log_object(scen, tag="scenario")
self.hypothesis_gen: HypothesisGen = import_class(PROP_SETTING.quant_hypothesis_gen)(scen)
logger.log_object(self.hypothesis_gen, tag="quant hypothesis generator")
self.factor_hypothesis2experiment: Hypothesis2Experiment = import_class(
PROP_SETTING.factor_hypothesis2experiment
)()
logger.log_object(self.factor_hypothesis2experiment, tag="factor hypothesis2experiment")
self.model_hypothesis2experiment: Hypothesis2Experiment = import_class(
PROP_SETTING.model_hypothesis2experiment
)()
logger.log_object(self.model_hypothesis2experiment, tag="model hypothesis2experiment")
self.factor_coder: Developer = import_class(PROP_SETTING.factor_coder)(scen)
logger.log_object(self.factor_coder, tag="factor coder")
self.model_coder: Developer = import_class(PROP_SETTING.model_coder)(scen)
logger.log_object(self.model_coder, tag="model coder")
self.factor_runner: Developer = import_class(PROP_SETTING.factor_runner)(scen)
logger.log_object(self.factor_runner, tag="factor runner")
self.model_runner: Developer = import_class(PROP_SETTING.model_runner)(scen)
logger.log_object(self.model_runner, tag="model runner")
self.factor_summarizer: Experiment2Feedback = import_class(PROP_SETTING.factor_summarizer)(scen)
logger.log_object(self.factor_summarizer, tag="factor summarizer")
self.model_summarizer: Experiment2Feedback = import_class(PROP_SETTING.model_summarizer)(scen)
logger.log_object(self.model_summarizer, tag="model summarizer")
self.trace = QuantTrace(scen=scen)
super(RDLoop, self).__init__()
async def direct_exp_gen(self, prev_out: dict[str, Any]):
while True:
if self.get_unfinished_loop_cnt(self.loop_idx) < RD_AGENT_SETTINGS.get_max_parallel():
hypo = self._propose()
assert hypo.action in ["factor", "model"]
if hypo.action == "factor":
exp = self.factor_hypothesis2experiment.convert(hypo, self.trace)
else:
exp = self.model_hypothesis2experiment.convert(hypo, self.trace)
logger.log_object(exp.sub_tasks, tag="experiment generation")
return {"propose": hypo, "exp_gen": exp}
await asyncio.sleep(1)
def coding(self, prev_out: dict[str, Any]):
if prev_out["direct_exp_gen"]["propose"].action == "factor":
exp = self.factor_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
elif prev_out["direct_exp_gen"]["propose"].action == "model":
exp = self.model_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
logger.log_object(exp, tag="coder result")
return exp
def running(self, prev_out: dict[str, Any]):
if prev_out["direct_exp_gen"]["propose"].action == "factor":
exp = self.factor_runner.develop(prev_out["coding"])
if exp is None:
logger.error(f"Factor extraction failed.")
raise FactorEmptyError("Factor extraction failed.")
elif prev_out["direct_exp_gen"]["propose"].action == "model":
exp = self.model_runner.develop(prev_out["coding"])
logger.log_object(exp, tag="runner result")
return exp
def feedback(self, prev_out: dict[str, Any]):
e = prev_out.get(self.EXCEPTION_KEY, None)
if e is not None:
feedback = HypothesisFeedback(
observations=str(e),
hypothesis_evaluation="",
new_hypothesis="",
reason="",
decision=False,
)
logger.log_object(feedback, tag="feedback")
self.trace.hist.append((prev_out["direct_exp_gen"]["exp_gen"], feedback))
else:
if prev_out["direct_exp_gen"]["propose"].action == "factor":
feedback = self.factor_summarizer.generate_feedback(prev_out["running"], self.trace)
elif prev_out["direct_exp_gen"]["propose"].action == "model":
feedback = self.model_summarizer.generate_feedback(prev_out["running"], self.trace)
logger.log_object(feedback, tag="feedback")
self.trace.hist.append((prev_out["running"], feedback))
def main(path=None, step_n=None, loop_n=None, all_duration=None, checkout=True):
"""
Auto R&D Evolving loop for fintech factors.
You can continue running session by
.. code-block:: python
dotenv run -- python rdagent/app/qlib_rd_loop/quant.py $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is a optional paramter
"""
if path is None:
quant_loop = QuantRDLoop(QUANT_PROP_SETTING)
else:
quant_loop = QuantRDLoop.load(path, checkout=checkout)
asyncio.run(quant_loop.run(step_n=step_n, loop_n=loop_n, all_duration=all_duration))
if __name__ == "__main__":
fire.Fire(main)
+2 -2
View File
@@ -5,7 +5,7 @@ This is the preliminary version of the APE (Automated Prompt Engineering)
import pickle
from pathlib import Path
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.log.conf import LOG_SETTINGS
def get_llm_qa(file_path):
@@ -21,7 +21,7 @@ def get_llm_qa(file_path):
# Example usage
# use
file_path = Path(RD_AGENT_SETTINGS.log_trace_path) / "debug_llm.pkl"
file_path = Path(LOG_SETTINGS.trace_path) / "debug_llm.pkl"
llm_qa = get_llm_qa(file_path)
print(len(llm_qa))
+4 -1
View File
@@ -3,22 +3,25 @@ import socket
import docker
from rdagent.log import rdagent_logger as logger
from rdagent.utils.env import cleanup_container
def check_docker() -> None:
container = None
try:
client = docker.from_env()
client.images.pull("hello-world")
container = client.containers.run("hello-world", detach=True)
logs = container.logs().decode("utf-8")
print(logs)
container.remove()
logger.info(f"The docker status is normal")
except docker.errors.DockerException as e:
logger.error(f"An error occurred: {e}")
logger.warning(
f"Docker status is exception, please check the docker configuration or reinstall it. Refs: https://docs.docker.com/engine/install/ubuntu/."
)
finally:
cleanup_container(container, "health check")
def is_port_in_use(port):
+2 -1
View File
@@ -103,8 +103,9 @@ class CoSTEER(Developer[Experiment]):
assert isinstance(evo_exp, Experiment) # multiple inheritance
logger.log_object(evo_exp.sub_workspace_list, tag="evolving code")
for sw in evo_exp.sub_workspace_list:
logger.info(f"evolving code workspace: {sw}")
logger.info(f"evolving workspace: {sw}")
if (datetime.now() - start_datetime).seconds > self.max_seconds:
logger.info(f"Reached max time limit {self.max_seconds} seconds, stop evolving")
break
if self.with_feedback and self.filter_final_evo:
+57 -16
View File
@@ -1,4 +1,5 @@
from abc import abstractmethod
from copy import deepcopy
from dataclasses import dataclass
from typing import TYPE_CHECKING, List
@@ -113,21 +114,35 @@ class CoSTEERSingleFeedbackDeprecated(CoSTEERSingleFeedback):
# Instead, we should create subclass for it.
self.shape_feedback = shape_feedback # Not general enough. So
# TODO: @property
@property
def execution(self):
return self.execution_feedback
@execution.setter
def execution(self, value):
self.execution_feedback = value
@property
def return_checking(self):
if self.value_generated_flag:
return f"value feedback: {self.value_feedback}\n\nshape feedback: {self.shape_feedback}"
return None
@return_checking.setter
def return_checking(self, value):
# Since return_checking is derived from value_feedback and shape_feedback,
# we don't need to do anything here
self.value_feedback = value
self.shape_feedback = value
@property
def code(self):
return self.code_feedback
@code.setter
def code(self, value):
self.code_feedback = value
def __str__(self) -> str:
return f"""------------------Execution Feedback------------------
{self.execution_feedback if self.execution_feedback is not None else 'No execution feedback'}
@@ -197,7 +212,7 @@ class CoSTEEREvaluator(Evaluator):
class CoSTEERMultiEvaluator(CoSTEEREvaluator):
"""This is for evaluation of experiment. Due to we have multiple tasks, so we will return a list of evaluation feebacks"""
def __init__(self, single_evaluator: CoSTEEREvaluator, *args, **kwargs) -> None:
def __init__(self, single_evaluator: CoSTEEREvaluator | list[CoSTEEREvaluator], *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.single_evaluator = single_evaluator
@@ -207,30 +222,56 @@ class CoSTEERMultiEvaluator(CoSTEEREvaluator):
queried_knowledge: QueriedKnowledge = None,
**kwargs,
) -> CoSTEERMultiFeedback:
multi_implementation_feedback = multiprocessing_wrapper(
[
(
self.single_evaluator.evaluate,
eval_l = self.single_evaluator if isinstance(self.single_evaluator, list) else [self.single_evaluator]
task_li_feedback_li = []
for ev in eval_l:
multi_implementation_feedback = multiprocessing_wrapper(
[
(
evo.sub_tasks[index],
evo.sub_workspace_list[index],
evo.sub_gt_implementations[index] if evo.sub_gt_implementations is not None else None,
queried_knowledge,
ev.evaluate,
(
evo.sub_tasks[index],
evo.sub_workspace_list[index],
evo.sub_gt_implementations[index] if evo.sub_gt_implementations is not None else None,
queried_knowledge,
),
)
for index in range(len(evo.sub_tasks))
],
n=RD_AGENT_SETTINGS.multi_proc_n,
)
task_li_feedback_li.append(multi_implementation_feedback)
# merge the feedbacks
merged_task_feedback = []
for task_id, fb in enumerate(task_li_feedback_li[0]):
fb = deepcopy(fb) # deep copy to make it more robust
fb.final_decision = all(
task_li_feedback[task_id].final_decision for task_li_feedback in task_li_feedback_li
)
for attr in "execution", "return_checking", "code":
setattr(
fb,
attr,
"\n\n".join(
[
getattr(task_li_feedback[task_id], attr)
for task_li_feedback in task_li_feedback_li
if getattr(task_li_feedback[task_id], attr) is not None
]
),
)
for index in range(len(evo.sub_tasks))
],
n=RD_AGENT_SETTINGS.multi_proc_n,
)
merged_task_feedback.append(fb)
final_decision = [
None if single_feedback is None else single_feedback.final_decision
for single_feedback in multi_implementation_feedback
for single_feedback in merged_task_feedback
]
logger.info(f"Final decisions: {final_decision} True count: {final_decision.count(True)}")
# TODO: this is to be compatible with factor_implementation;
for index in range(len(evo.sub_tasks)):
if final_decision[index]:
evo.sub_tasks[index].factor_implementation = True
return CoSTEERMultiFeedback(multi_implementation_feedback)
return CoSTEERMultiFeedback(merged_task_feedback)
@@ -1,7 +1,6 @@
from __future__ import annotations
from abc import abstractmethod
from pathlib import Path
from rdagent.components.coder.CoSTEER.config import CoSTEERSettings
from rdagent.components.coder.CoSTEER.evaluators import (
@@ -15,12 +14,9 @@ from rdagent.components.coder.CoSTEER.knowledge_management import (
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.evolving_framework import EvolvingStrategy, EvoStep, QueriedKnowledge
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.core.prompts import Prompts
from rdagent.core.scenario import Scenario
from rdagent.core.utils import multiprocessing_wrapper
implement_prompts = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
class MultiProcessEvolvingStrategy(EvolvingStrategy):
def __init__(self, scen: Scenario, settings: CoSTEERSettings):
@@ -8,8 +8,6 @@ from itertools import combinations
from pathlib import Path
from typing import List, Union
from jinja2 import Environment, StrictUndefined
from rdagent.components.coder.CoSTEER.config import CoSTEERSettings
from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback
from rdagent.components.knowledge_management.graph import (
@@ -26,12 +24,12 @@ from rdagent.core.evolving_framework import (
RAGStrategy,
)
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.core.prompts import Prompts
from rdagent.log import rdagent_logger as logger
from rdagent.oai.llm_utils import (
APIBackend,
calculate_embedding_distance_between_str_list,
)
from rdagent.utils.agent.tpl import T
class CoSTEERKnowledge(Knowledge):
@@ -216,8 +214,6 @@ class CoSTEERQueriedKnowledgeV2(CoSTEERQueriedKnowledgeV1):
class CoSTEERRAGStrategyV2(RAGStrategy):
prompt = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
def __init__(self, knowledgebase: CoSTEERKnowledgeBaseV2, settings: CoSTEERSettings) -> None:
super().__init__(knowledgebase)
self.current_generated_trace_count = 0
@@ -324,12 +320,8 @@ class CoSTEERRAGStrategyV2(RAGStrategy):
all_component_content = ""
for _, component_node in enumerate(all_component_nodes):
all_component_content += f"{component_node.content}, \n"
analyze_component_system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(self.prompt["analyze_component_prompt_v1_system"])
.render(
all_component_content=all_component_content,
)
analyze_component_system_prompt = T(".prompts:analyze_component_prompt_v1_system").r(
all_component_content=all_component_content,
)
analyze_component_user_prompt = target_task_information
+22 -1
View File
@@ -1,5 +1,6 @@
from typing import Literal
from rdagent.app.data_science.conf import DS_RD_SETTING
from rdagent.components.coder.CoSTEER.config import CoSTEERSettings
from rdagent.utils.env import (
CondaConf,
@@ -23,7 +24,13 @@ class DSCoderCoSTEERSettings(CoSTEERSettings):
# TODO: extract a function for env and conf.
def get_ds_env(conf_type: Literal["kaggle", "mlebench"] = "kaggle") -> Env:
def get_ds_env(
conf_type: Literal["kaggle", "mlebench"] = "kaggle",
extra_volumes: dict = {},
running_timeout_period: int = (
DS_RD_SETTING.debug_timeout if not DS_RD_SETTING.sample_data_by_LLM else DS_RD_SETTING.full_timeout
),
) -> Env:
"""
Retrieve the appropriate environment configuration based on the env_type setting.
@@ -47,4 +54,18 @@ def get_ds_env(conf_type: Literal["kaggle", "mlebench"] = "kaggle") -> Env:
)
else:
raise ValueError(f"Unknown env type: {conf.env_type}")
env.conf.extra_volumes = extra_volumes
env.conf.running_timeout_period = running_timeout_period
return env
def get_clear_ws_cmd(stage: Literal["before_training", "before_inference"] = "before_training") -> str:
"""
Clean the files in workspace to a specific stage
"""
assert stage in ["before_training", "before_inference"], f"Unknown stage: {stage}"
if DS_RD_SETTING.enable_model_dump and stage == "before_training":
cmd = "rm -r submission.csv scores.csv models"
else:
cmd = "rm submission.csv scores.csv"
return cmd
@@ -11,9 +11,7 @@ File structure
- Each coder could be tested.
"""
import json
from pathlib import Path
from typing import Dict
from jinja2 import Environment, StrictUndefined
@@ -74,7 +72,7 @@ class EnsembleMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
)
# Generate code with knowledge integration
competition_info = self.scen.get_scenario_all_desc()
competition_info = self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None))
system_prompt = T(".prompts:ensemble_coder.system").r(
task_desc=ensemble_information_str,
competition_info=competition_info,
@@ -10,6 +10,7 @@ from rdagent.components.coder.CoSTEER.evaluators import (
CoSTEERSingleFeedback,
)
from rdagent.components.coder.data_science.conf import get_ds_env
from rdagent.components.coder.data_science.utils import remove_eda_part
from rdagent.core.evolving_framework import QueriedKnowledge
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.utils.agent.tpl import T
@@ -46,8 +47,7 @@ class EnsembleCoSTEEREvaluator(CoSTEEREvaluator):
final_decision=False,
)
env = get_ds_env()
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
env = get_ds_env(extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()})
fname = "test/ensemble_test.txt"
test_code = (DIRNAME / "eval_tests" / "ensemble_test.txt").read_text()
@@ -63,13 +63,15 @@ class EnsembleCoSTEEREvaluator(CoSTEEREvaluator):
)
implementation.inject_files(**{fname: test_code})
stdout, ret_code = implementation.execute_ret_code(env=env, entry=f"python {fname}")
result = implementation.run(env=env, entry=f"python {fname}")
stdout = result.stdout
ret_code = result.exit_code
stdout += f"\nNOTE: the above scripts run with return code {ret_code}"
if "main.py" in implementation.file_dict and ret_code == 0:
workflow_stdout = implementation.execute(env=env, entry="python main.py")
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
workflow_stdout = remove_eda_part(workflow_stdout)
else:
workflow_stdout = None
@@ -128,5 +128,10 @@ assert model_set_in_scores == set({{model_names}}).union({"ensemble"}), (
assert score_df.index.is_unique, "The scores dataframe has duplicate model names."
assert score_df.columns.tolist() == ["{{metric_name}}"], f"The column names of the scores dataframe should be ['{{metric_name}}'], but is '{score_df.columns.tolist()}'"
# Check for NaN values in score_df
assert not score_df.isnull().values.any(), (
f"The scores dataframe contains NaN values at the following locations:\n{score_df[score_df.isnull().any(axis=1)]}"
)
print("Ensemble test end.")
@@ -39,6 +39,7 @@ ensemble_coder:
1. The function's code is associated with several other functions including a data loader, feature engineering, and model training. all codes are as follows:
{{ all_code }}
2. You should avoid using logging module to output information in your generated code, and instead use the print() function.
{% include "scenarios.data_science.share:guidelines.coding" %}
## Output Format
{% if out_spec %}
@@ -61,7 +61,7 @@ class FeatureMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
# 2. code
system_prompt = T(".prompts:feature_coder.system").r(
competition_info=self.scen.get_scenario_all_desc(),
competition_info=self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None)),
task_desc=feature_information_str,
data_loader_code=workspace.file_dict.get("load_data.py"),
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
@@ -8,6 +8,7 @@ from rdagent.components.coder.CoSTEER.evaluators import (
CoSTEERSingleFeedback,
)
from rdagent.components.coder.data_science.conf import get_ds_env
from rdagent.components.coder.data_science.utils import remove_eda_part
from rdagent.core.evolving_framework import QueriedKnowledge
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.utils.agent.tpl import T
@@ -20,7 +21,6 @@ FeatureEvalFeedback = CoSTEERSingleFeedback
class FeatureCoSTEEREvaluator(CoSTEEREvaluator):
def evaluate(
self,
target_task: Task,
@@ -29,7 +29,6 @@ class FeatureCoSTEEREvaluator(CoSTEEREvaluator):
queried_knowledge: QueriedKnowledge = None,
**kwargs,
) -> FeatureEvalFeedback:
target_task_information = target_task.get_task_information()
if (
queried_knowledge is not None
@@ -44,19 +43,18 @@ class FeatureCoSTEEREvaluator(CoSTEEREvaluator):
final_decision=False,
)
env = get_ds_env()
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
env = get_ds_env(extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()})
# TODO: do we need to clean the generated temporary content?
fname = "test/feature_test.py"
test_code = (DIRNAME / "eval_tests" / "feature_test.txt").read_text()
implementation.inject_files(**{fname: test_code})
stdout, ret_code = implementation.execute_ret_code(env=env, entry=f"python {fname}")
result = implementation.run(env=env, entry=f"python {fname}")
if "main.py" in implementation.file_dict and ret_code == 0:
if "main.py" in implementation.file_dict and result.exit_code == 0:
workflow_stdout = implementation.execute(env=env, entry="python main.py")
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
workflow_stdout = remove_eda_part(workflow_stdout)
else:
workflow_stdout = None
@@ -68,13 +66,16 @@ class FeatureCoSTEEREvaluator(CoSTEEREvaluator):
workflow_code=implementation.all_codes,
)
user_prompt = T(".prompts:feature_eval.user").r(
stdout=shrink_text(stdout),
stdout=shrink_text(result.stdout),
workflow_stdout=workflow_stdout,
)
return build_cls_from_json_with_retry(
fb = build_cls_from_json_with_retry(
FeatureEvalFeedback,
system_prompt=system_prompt,
user_prompt=user_prompt,
init_kwargs_update_func=FeatureEvalFeedback.val_and_update_init_dict,
)
fb.final_decision = fb.final_decision and result.exit_code == 0
return fb
@@ -28,10 +28,22 @@ X_loaded = deepcopy(X)
y_loaded = deepcopy(y)
X_test_loaded = deepcopy(X_test)
import sys
import reprlib
from joblib.memory import MemorizedFunc
def get_original_code(func):
if isinstance(func, MemorizedFunc):
return func.func.__code__
return func.__code__
def debug_info_print(func):
def wrapper(*args, **kwargs):
original_code = get_original_code(func)
def local_trace(frame, event, arg):
if event == "return" and frame.f_code == func.__code__:
if event == "return" and frame.f_code == original_code:
print("\n" + "="*20 + "Running feat_eng code, local variable values:" + "="*20)
for k, v in frame.f_locals.items():
printed = aRepr.repr(v)
@@ -39,10 +39,19 @@ feature_coder:
```python
{{ data_loader_code }}
```
3. **Additional Guidance:**
4. **Additional Guidance:**
- If a previous attempt exists, improve upon it without repeating mistakes.
- If errors indicate a missing file, find a way to download it or implement an alternative solution.
- You should avoid using logging module to output information in your generated code, and instead use the print() function.
5. You should use the following cache decorator to cache the results of the function:
```python
from joblib import Memory
memory = Memory(location='{% include "scenarios.data_science.share:scen.cache_path" %}', verbose=0)
@memory.cache```
6. Coding tricks:
- If the input consists of a batch of file paths and you need to modify the file contents to complete your feature engineering task, you can accomplish your feature engineering task by modifying these files and creating new files in a subfolder within "{% include "scenarios.data_science.share:scen.cache_path" %}" (this path is persistent, otherwise you may lose your created file). Then the new file paths are returned.
{% include "scenarios.data_science.share:guidelines.coding" %}
## Output Format
{% if out_spec %}
@@ -62,7 +62,7 @@ class ModelMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
# 2. code
system_prompt = T(".prompts:model_coder.system").r(
task_desc=model_information_str,
competition_info=self.scen.get_scenario_all_desc(),
competition_info=self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None)),
data_loader_code=workspace.file_dict.get("load_data.py"),
feature_code=workspace.file_dict["feature.py"],
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
@@ -13,6 +13,7 @@ from rdagent.components.coder.CoSTEER.evaluators import (
CoSTEERSingleFeedback,
)
from rdagent.components.coder.data_science.conf import get_ds_env
from rdagent.components.coder.data_science.utils import remove_eda_part
from rdagent.core.evolving_framework import QueriedKnowledge
from rdagent.core.exception import CoderError
from rdagent.core.experiment import FBWorkspace, Task
@@ -56,8 +57,7 @@ class ModelGeneralCaseSpecEvaluator(CoSTEEREvaluator):
final_decision=False,
)
env = get_ds_env()
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
env = get_ds_env(extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()})
if_model_removed = False
@@ -67,7 +67,9 @@ class ModelGeneralCaseSpecEvaluator(CoSTEEREvaluator):
(DIRNAME / "eval_tests" / "model_test.txt").read_text().replace("model01", target_task.name)
) # only check the model changed this time
implementation.inject_files(**{fname: test_code})
stdout, ret_code = implementation.execute_ret_code(env=env, entry=f"python {fname}")
result = implementation.run(env=env, entry=f"python {fname}")
stdout = result.stdout
ret_code = result.exit_code
if stdout is None:
raise CoderError(
@@ -80,7 +82,7 @@ class ModelGeneralCaseSpecEvaluator(CoSTEEREvaluator):
if "main.py" in implementation.file_dict and ret_code == 0:
workflow_stdout = implementation.execute(env=env, entry="python main.py")
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
workflow_stdout = remove_eda_part(workflow_stdout)
else:
workflow_stdout = None
@@ -107,9 +109,12 @@ class ModelGeneralCaseSpecEvaluator(CoSTEEREvaluator):
workflow_stdout=workflow_stdout,
)
return build_cls_from_json_with_retry(
fb = build_cls_from_json_with_retry(
ModelSingleFeedback,
system_prompt=system_prompt,
user_prompt=user_prompt,
init_kwargs_update_func=ModelSingleFeedback.val_and_update_init_dict,
)
fb.final_decision = fb.final_decision and result.exit_code == 0
return fb
@@ -35,6 +35,17 @@ print(f"test_ids length: {len(test_ids)}")
train_X, val_X, train_y, val_y = train_test_split(X, y, test_size=0.8, random_state=42)
import sys
import reprlib
from joblib.memory import MemorizedFunc
def get_original_code(func):
if isinstance(func, MemorizedFunc):
return func.func.__code__
return func.__code__
print("train_X:", aRepr.repr(train_X))
print("train_y:", aRepr.repr(train_y))
print("val_X:", aRepr.repr(val_X))
@@ -46,10 +57,12 @@ print(f"val_X.shape: {val_X.shape}" if hasattr(val_X, 'shape') else f"val_X leng
print(f"val_y.shape: {val_y.shape}" if hasattr(val_y, 'shape') else f"val_y length: {len(val_y)}")
def debug_info_print(func):
def wrapper(*args, **kwargs):
original_code = get_original_code(func)
def local_trace(frame, event, arg):
if event == "return" and frame.f_code == func.__code__:
if event == "return" and frame.f_code == original_code:
print("\n" + "="*20 + "Running model training code, local variable values:" + "="*20)
for k, v in frame.f_locals.items():
printed = aRepr.repr(v)
@@ -40,6 +40,12 @@ model_coder:
{{ feature_code }}
2. You should avoid using logging module to output information in your generated code, and instead use the print() function.
3. If the model can both be implemented by PyTorch and Tensorflow, please use pytorch for broader compatibility.
4. You should use the following cache decorator to cache the results of the function:
```python
from joblib import Memory
memory = Memory(location='{% include "scenarios.data_science.share:scen.cache_path" %}', verbose=0)
@memory.cache``
{% include "scenarios.data_science.share:guidelines.coding" %}
## Output Format
{% if out_spec %}
@@ -48,6 +48,7 @@ from rdagent.components.coder.data_science.raw_data_loader.eval import (
DataLoaderCoSTEEREvaluator,
)
from rdagent.components.coder.data_science.raw_data_loader.exp import DataLoaderTask
from rdagent.components.coder.data_science.share.eval import ModelDumpEvaluator
from rdagent.core.exception import CoderError
from rdagent.core.experiment import FBWorkspace
from rdagent.core.scenario import Scenario
@@ -66,7 +67,7 @@ class PipelineMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
workspace: FBWorkspace | None = None,
prev_task_feedback: CoSTEERSingleFeedback | None = None,
) -> dict[str, str]:
competition_info = self.scen.get_scenario_all_desc()
competition_info = self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None))
runtime_environment = self.scen.get_runtime_environment()
data_folder_info = self.scen.processed_data_folder_description
pipeline_task_info = target_task.get_task_information()
@@ -95,6 +96,8 @@ class PipelineMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
out_spec=PythonAgentOut.get_spec(),
runtime_environment=runtime_environment,
spec=T("scenarios.data_science.share:component_spec.Pipeline").r(),
enable_model_dump=DS_RD_SETTING.enable_model_dump,
enable_debug_mode=DS_RD_SETTING.sample_data_by_LLM,
)
user_prompt = T(".prompts:pipeline_coder.user").r(
competition_info=competition_info,
@@ -146,8 +149,12 @@ class PipelineCoSTEER(CoSTEER):
**kwargs,
) -> None:
settings = DSCoderCoSTEERSettings()
eval_l = [PipelineCoSTEEREvaluator(scen=scen)]
if DS_RD_SETTING.enable_model_dump:
eval_l.append(ModelDumpEvaluator(scen=scen, data_type="sample"))
eva = CoSTEERMultiEvaluator(
PipelineCoSTEEREvaluator(scen=scen), scen=scen
single_evaluator=eval_l, scen=scen
) # Please specify whether you agree running your eva in parallel or not
es = PipelineMultiProcessEvolvingStrategy(scen=scen, settings=settings)
@@ -15,8 +15,10 @@ from rdagent.components.coder.CoSTEER.evaluators import (
from rdagent.components.coder.CoSTEER.knowledge_management import (
CoSTEERQueriedKnowledgeV2,
)
from rdagent.components.coder.data_science.conf import get_ds_env
from rdagent.components.coder.data_science.conf import get_clear_ws_cmd, get_ds_env
from rdagent.components.coder.data_science.utils import remove_eda_part
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.scenarios.data_science.test_eval import get_test_eval
from rdagent.utils.agent.tpl import T
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
@@ -51,13 +53,35 @@ class PipelineCoSTEEREvaluator(CoSTEEREvaluator):
final_decision=False,
)
env = get_ds_env()
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
env = get_ds_env(extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()})
# Clean the scores.csv & submission.csv.
implementation.execute(env=env, entry=f"rm submission.csv scores.csv")
stdout = implementation.execute(env=env, entry=f"python main.py")
stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", stdout)
stdout = ""
implementation.execute(env=env, entry=get_clear_ws_cmd())
if DS_RD_SETTING.sample_data_by_LLM:
# Because coder runs on full data, we need to run debug mode in advance to save time
result = implementation.run(env=env, entry=f"python -m coverage run main.py --debug")
else:
result = implementation.run(env=env, entry=f"python -m coverage run main.py")
execute_ret_code = result.exit_code
result.stdout = remove_eda_part(result.stdout)
if result.exit_code != 0:
stdout += f"Code failed to run. Please check the stdout:\n Following the stdout of the debug mode run:\n{result.stdout.strip()}\n"
else:
stdout += f"Code ran successfully.\n Following the stdout of the debug mode run:\n{result.stdout.strip()}\n"
if DS_RD_SETTING.sample_data_by_LLM:
debug_time, full_estimated_time = None, None
if match := re.search(r"debug_time:\s*(\d+(?:.\d+)?)", result.stdout, re.DOTALL):
debug_time = float(match.group(1))
if match := re.search(r"estimated_time:\s*(\d+(?:.\d+)?)", result.stdout, re.DOTALL):
full_estimated_time = float(match.group(1))
if debug_time is not None and full_estimated_time is not None:
stdout += f"Debug mode ran in {debug_time:.2f} seconds, estimated full run time is {full_estimated_time:.2f} seconds.\n"
if full_estimated_time < env.conf.running_timeout_period * 3:
stdout += "The estimated full run time is less than three times the timeout period.\n"
else:
stdout += f"The estimated full run time is more than three times the timeout period.\n"
else:
stdout += "Debug mode did not provide debug_time or estimated_time, it's a buggy implementation.\n"
score_fp = implementation.workspace_path / "scores.csv"
score_ret_code = 0
@@ -71,34 +95,69 @@ class PipelineCoSTEEREvaluator(CoSTEEREvaluator):
model_set_in_scores = set(score_df.index)
# Check model names (index)
if "ensemble" not in model_set_in_scores:
score_check_text += (
f"\n[Error] The score dataframe doesn't contain the ensemble model.\nscore_df is:\n{score_df}"
)
if not score_df.index.is_unique:
score_check_text += "\n[Error] The file 'scores.csv' contains duplicate model names."
score_ret_code = 1
if "ensemble" not in model_set_in_scores:
score_check_text += "\n[Error] The file 'scores.csv' doesn't contain the ensemble model."
score_ret_code = 1
if score_ret_code != 0:
score_check_text += f"The dataframe in file 'scores.csv' is:\n{score_df}"
# Check metric name (columns)
if score_df.columns.tolist() != [self.scen.metric_name]:
score_check_text += f"\n[Error] The scores dataframe does not contain the correct column names.\nCorrect columns is: ['{self.scen.metric_name}']\nBut got: {score_df.columns.tolist()}"
score_ret_code = 1
# Check if scores contain NaN (values)
if score_df.isnull().values.any():
nan_locations = score_df[score_df.isnull().any(axis=1)]
score_check_text += f"\n[Error] The scores dataframe contains NaN values at the following locations:\n{nan_locations}"
score_ret_code = 1
except Exception as e:
score_check_text += f"\n[Error] in checking the scores.csv file: {e}\nscores.csv's content:\n-----\n{score_fp.read_text()}\n-----"
score_ret_code = 1
# Check submission file
base_check_code = (DIRNAME / "eval_tests" / "submission_format_test.txt").read_text()
implementation.inject_files(**{"test/submission_format_test.py": base_check_code})
# stdout += "----Submission Check 1-----\n"
submission_check_out, submission_ret_code = implementation.execute_ret_code(
env=env, entry="python test/submission_format_test.py"
)
stdout += "\n" + submission_check_out
test_eval = get_test_eval()
if not test_eval.is_sub_enabled(self.scen.competition):
submission_ret_code = 0
else:
# Check submission file
base_check_code = T(".eval_tests.submission_format_test", ftype="txt").r()
implementation.inject_files(**{"test/submission_format_test.py": base_check_code})
# stdout += "----Submission Check 1-----\n"
submission_result = implementation.run(env=env, entry="python test/submission_format_test.py")
submission_check_out = submission_result.stdout
submission_ret_code = submission_result.exit_code
if DS_RD_SETTING.rule_base_eval:
if execute_ret_code == 0 and score_ret_code == 0 and submission_ret_code == 0:
return PipelineSingleFeedback(
execution=stdout,
return_checking=score_check_text + "\n" + submission_check_out,
code="Code evaluation is not available.",
final_decision=True,
)
else:
return PipelineSingleFeedback(
execution=stdout,
return_checking=score_check_text + "\n" + submission_check_out,
code="Code evaluation is not available.",
final_decision=False,
)
stdout += "\n" + submission_check_out
if not isinstance(implementation, FBWorkspace):
eda_output = None
else:
eda_output = implementation.file_dict.get("EDA.md", None)
system_prompt = T(".prompts:pipeline_eval.system").r(
scenario=self.scen.get_scenario_all_desc(),
scenario=self.scen.get_scenario_all_desc(eda_output=eda_output),
task_desc=target_task.get_task_information(),
is_sub_enabled=test_eval.is_sub_enabled(self.scen.competition),
spec=T("scenarios.data_science.share:component_spec.Pipeline").r(),
debug_mode=DS_RD_SETTING.sample_data_by_LLM,
)
user_prompt = T(".prompts:pipeline_eval.user").r(
stdout=stdout.strip(),
@@ -1,14 +1,20 @@
import hashlib
from pathlib import Path
import pandas as pd
import hashlib
def calculate_md5(file_path):
with open(file_path, "rb") as f:
file_hash = hashlib.md5(f.read()).hexdigest()
return file_hash
file_md5 = calculate_md5("scores.csv")
if Path("scores.csv").exists():
file_md5 = calculate_md5("scores.csv")
else:
print("Warning: scores.csv does not exist. MD5 check will be skipped.")
file_md5 = None
"""
find . | grep -i sample | grep -i submission | grep -v sample_submission.csv | grep -v zip_files | grep -v 'sample/'
./denoising-dirty-documents/sampleSubmission.csv
@@ -22,56 +28,67 @@ find . | grep -i sample | grep -i submission | grep -v sample_submission.csv | g
"""
# Find sample submission file dynamically
input_dir = Path("/kaggle/input")
# Look for common variations of sample submission filenames
sample_submission_files = list(input_dir.glob("*sample_submission*.csv")) + \
list(input_dir.glob("*sampleSubmission*.csv"))
input_dir = Path('{% include "scenarios.data_science.share:scen.input_path" %}')
sample_submission_files = list(input_dir.glob("*sample_submission*.csv")) + list(
input_dir.glob("*sampleSubmission*.csv")
)
assert sample_submission_files, "Error: No sample submission file found in /kaggle/input/"
if not sample_submission_files:
print(f'Error: No sample submission file found in {% include "scenarios.data_science.share:scen.input_path" %}')
sample_submission_name = None
SAMPLE_SUBMISSION_PATH = None
else:
sample_submission_name = sample_submission_files[0].name
SAMPLE_SUBMISSION_PATH = str(sample_submission_files[0])
print(f"Using sample submission file: {sample_submission_name}")
# Use first matching file
sample_submission_name = sample_submission_files[0].name
SAMPLE_SUBMISSION_PATH = str(sample_submission_files[0])
print(f"Using sample submission file: {sample_submission_name}")
if SAMPLE_SUBMISSION_PATH is not None and not Path(SAMPLE_SUBMISSION_PATH).exists():
print(f"Error: {sample_submission_name} not found at {SAMPLE_SUBMISSION_PATH}")
# Check if the sample submission file exists
assert Path(SAMPLE_SUBMISSION_PATH).exists(), f"Error: {sample_submission_name} not found at {SAMPLE_SUBMISSION_PATH}"
if not Path("submission.csv").exists():
print("Error: submission.csv not found")
# Check if our submission file exists
assert Path('submission.csv').exists(), "Error: submission.csv not found"
if SAMPLE_SUBMISSION_PATH is not None and Path(SAMPLE_SUBMISSION_PATH).exists() and Path("submission.csv").exists():
sample_submission = pd.read_csv(SAMPLE_SUBMISSION_PATH)
our_submission = pd.read_csv("submission.csv")
sample_submission = pd.read_csv(SAMPLE_SUBMISSION_PATH)
our_submission = pd.read_csv('submission.csv')
success = True
print(f"Columns in {sample_submission_name}:", sample_submission.columns)
print("Columns in our_submission.csv:", our_submission.columns)
success = True
# Print the columns of the sample submission file
print(f"Columns in {sample_submission_name}:", sample_submission.columns)
print("Columns in our_submission.csv:", our_submission.columns)
for col in sample_submission.columns:
if col not in our_submission.columns:
success = False
print(f"Column {col} not found in submission.csv")
for col in sample_submission.columns:
if col not in our_submission.columns:
success = False
print(f'Column {col} not found in submission.csv')
if success:
print(f'submission.csv\'s columns aligns with {sample_submission_name} .')
if success:
print(f"submission.csv's columns aligns with {sample_submission_name} .")
else:
print(f"submission.csv's columns does not align with {sample_submission_name} .")
# Print the first 5 rows of the two submission files, with columns separated by commas.
def print_first_rows(file_path, file_name, num_rows=5):
print(f"\nFirst {num_rows} rows of {file_name}:")
try:
with open(file_path, 'r') as file:
for i, line in enumerate(file):
if i < num_rows:
print(line.strip())
else:
break
except FileNotFoundError:
print(f"Error: {file_name} not found.")
def print_first_rows(file_path, file_name, num_rows=5):
print(f"\nFirst {num_rows} rows of {file_name}:")
try:
with open(file_path, "r") as file:
for i, line in enumerate(file):
if i < num_rows:
print(line.strip())
else:
break
except FileNotFoundError:
print(f"Error: {file_name} not found.")
print_first_rows(SAMPLE_SUBMISSION_PATH, sample_submission_name)
print_first_rows('submission.csv', 'submission.csv')
assert calculate_md5("scores.csv") == file_md5, "scores.csv should not be rewritten"
print(f"\nPlease Checked the content of the submission file(submission.csv should has the same format with {sample_submission_name} but might not the same index with {sample_submission_name}). ")
print_first_rows(SAMPLE_SUBMISSION_PATH, sample_submission_name)
print_first_rows("submission.csv", "submission.csv")
if file_md5 is not None:
if calculate_md5("scores.csv") != file_md5:
print("Warning: scores.csv has been rewritten in the test script!")
else:
print("Skipping comparison and preview due to missing files.")
print(
f"\nPlease Checked the content of the submission file(submission.csv should has the same format with {sample_submission_name} but might not the same index with {sample_submission_name}). "
)
@@ -3,4 +3,5 @@ from rdagent.components.coder.CoSTEER.task import CoSTEERTask
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
class PipelineTask(CoSTEERTask):
pass
def __init__(self, name: str = "Pipeline", *args, **kwargs) -> None:
super().__init__(name=name, *args, **kwargs)
@@ -37,8 +37,9 @@ pipeline_coder:
## Guidelines
1. Ensure that the dataset is loaded strictly from `/kaggle/input/`, following the exact folder structure described in the **Data Folder Description**, and do not attempt to load data from the current directory (`./`).
1. Ensure that the dataset is loaded strictly from `{% include "scenarios.data_science.share:scen.input_path" %}`, following the exact folder structure described in the **Data Folder Description**, and do not attempt to load data from the current directory (`./`).
2. You should avoid using logging module to output information in your generated code, and instead use the print() function.
3. You should be very careful about the try catch block in your code. You may use it to handle missing files in data reading, but you should not use it to handle the errors in your code. Especially use it to bypass the errors in your code. Directly solve the errors in your code instead of using try catch block to bypass them.
## Exploratory Data Analysis (EDA) part(Required):
- Before returning the data, you should always add an EDA part describing the data to help the following steps understand the data better.
@@ -57,6 +58,37 @@ pipeline_coder:
User will use the following code to match: re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===", stdout, re.DOTALL).groups()[1]
- An evaluation agent will help to check whether the EDA part is added correctly.
- During the EDA part, you should try to avoid any irrelevant information sending to the standard output.
{% include "scenarios.data_science.share:guidelines.coding" %}
{% if enable_model_dump %}
## Model Dumping
{% include "components.coder.data_science.share.prompts:dump_model_coder.guideline" %}
{% endif %}
{% if enable_debug_mode %}
Your code will be executed in a debug mode with following command:
```bash
python main.py --debug
```
In debug mode, you should only sample ten percent of the data and run the minimum epochs to quickly test the correctness of the code.
In debug mode, you should implement a timer to measure the time taken for your debug configuration and estimate the time required for the full run.
For example, you can sample ten percent of the data and run for one epoch, then the full run with ten epochs will take one hundred times the time taken for the debug run. The scale is calculated by yourself depending on the data sampling and epoch number you choose. If your full run enables early stopping, the scale should be smaller considering the early stopping will stop the training earlier than the full epochs.
Your debug code should run exactly the same as the full run, except for the data sampling and epoch number, to ensure the correctness of the code.
You should print total time and estimated time in standard output using print function in the following schema:
=== Start of Debug Information ===
debug_time: time_taken_for_debug_run_in_seconds (e.g., 'debug_time: 10.0')
estimated_time: estimated_time_for_full_run_in_seconds (e.g., 'estimated_time: 100.0')
=== End of Debug Information ===
User will use the following code to match: re.search(r"(.*?)=== Start of Debug Information ===(.*)=== End of Debug Information ===", stdout, re.DOTALL).groups()[1]
Notice, data sampling should only be applied in debug mode. Always use the full data in the full run!
Example code:
```python
if args.debug:
sample_size = int(0.1 * len(train_dataset)) # 10% for debug
else:
sample_size = len(train_dataset)
```
{% endif %}
## Output Format
{% if out_spec %}
@@ -72,7 +104,7 @@ pipeline_coder:
--------- Competition Information ---------
{{ competition_info }}
--------- Data Folder Description (All path are relative to the data folder) ---------
--------- Data Folder Description (All path are relative to the data folder, i.e. "{% include "scenarios.data_science.share:scen.input_path" %}") ---------
{{ folder_spec }}
{% if latest_code %}
@@ -82,6 +114,7 @@ pipeline_coder:
--------- Feedback to former code ---------
{{ latest_code_feedback }}
The former code contains errors. You should correct the code based on the provided information, ensuring you do not repeat the same mistakes.
Keep the part that already seem correct intact. Avoid modifying them to refrain from introducing new errors.
{% else %}
The former code is correct. You should try to improve the code based on the provided task while not changing the irrelevant parts.
{% endif %}
@@ -89,7 +122,6 @@ pipeline_coder:
You should strictly follow the code specifications provided by the specification to implement the function.
pipeline_eval:
system: |-
You are a data scientist responsible for evaluating code generation.
@@ -103,11 +135,32 @@ pipeline_eval:
The details on how to structure the code are given in the specification:
{{ spec }}
{% if is_sub_enabled %}
## Evaluation Scope
Your focus is to check whether the workflow code:
1. Executes successfully, correctly generating a final submission.
2. Generates predictions in the correct format, ensuring they align with the submission structure!
Step 1: Executes successfully without any errors. Please distinguish between the errors and warnings.
Step 2: Correctly generates a final submission in the correct format, ensuring: they align with the submission structure, the index names and column names should match the sample, and the items should not be empty or apparently incorrect. The final submission and validation metrics should not be generated with mock data or dummy values. They should only the results of the actual code execution. Once you found the coding containing any data mock or dummy values, you should emphasize it in your feedback and reject this implementation.
Step 3: No data sampling is applied in the full run, and the code runs on the full dataset. The code should not contain any data sampling or data reduction logic in the full run. Once you found any data sampling or reduction logic in the full run, you should emphasize it in your feedback and reject this implementation.
Step 4: Aligns with the competition requirements. This includes:
- CAREFULLY ANALYZE WHETHER THE EXPERIMENTAL SETUP AND CODE MAY CAUSE MISALIGNMENT BETWEEN VALIDATION AND TEST PERFORMANCE.
- Confirm strict adherence to the competition's evaluation rules listed in `scenario`:
- Exact match between the implementation code of metric and the requirements of the scenario. The metric number is not the focus.
- Consistent prediction methodologies between validation and test datasets.
- No shortcuts or fold-specific strategies applied inconsistently.
- Rigorous checks for corner-case consistency.
- If such discrepancies or risks are found:
- Clearly document these issues in `code`.
- Begin your `code` with `[Evaluation error]`, explicitly stating the evaluation alignment issues causing experiment failure.
- If no issues are found, begin your `code` with `[Code analysis]`, providing a detailed analysis of the code quality, readability, and adherence to specifications.
{% if debug_mode %}
Step 5: The code is executed in a debug mode with the command `python main.py --debug`. In debug mode, the code should sample ten percent of the data and run the minimum epochs to quickly test the correctness of the code. You should check whether the code follows this requirements. If not, you should emphasize it in your feedback and reject this implementation.
You will also be given the execution time and estimated time for the full run. If not time information is provided or the estimated full run time is more than three times the timeout period, you should reject the implementation because the full run will not finish in time.
{% endif %}
## Evaluation Criteria
You will be given the execution output (`stdout`) to determine correctness.
@@ -121,14 +174,34 @@ pipeline_eval:
{
"execution": "Describe whether the code executed successfully, correctly integrating all components and generating the final submission. Include any errors or issues encountered, and append all error messages and full traceback details without summarizing or omitting any information.",
"return_checking": "Verify the generated files, particularly the submission file. Ensure that its format matches the sample submission, checking the index, column names, and CSV content.",
"code": "Begin explicitly with [Code analysis] or [Evaluation error]. Provide feedback on code quality, readability, adherence to the given specifications, and alignment with competition requirements.",
"final_decision": <true/false>
}
```
{% else %}
## Evaluation Scope
Your focus is to check whether the workflow code executes successfully.
You will be given the execution output (`stdout`) to determine correctness.
[Note]
1. Model performance is NOT a concern in this evaluation—only correct execution and formatting matter.
Please respond with your feedback in the following JSON format and order
```json
{
"execution": "Describe whether the code executed successfully. Include any errors or issues encountered, and append all error messages and full traceback details without summarizing or omitting any information.",
"return_checking": "Describe the expected file to be generated.",
"code": "Provide feedback on code quality, readability, and adherence to the given specifications.",
"final_decision": <true/false>
}
```
{% endif %}
# NOTE: when is_sub_enabled == False, we don't have any checking about the return. So it is just placeholder currently
user: |-
--------- code generated by user ---------
{{ code }}
--------- code running stdout ---------
{{ stdout }}
{{ stdout }}
@@ -67,7 +67,7 @@ class DataLoaderMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
) -> dict[str, str]:
# return a workspace with "load_data.py", "spec/load_data.md" inside
# assign the implemented code to the new workspace.
competition_info = self.scen.get_scenario_all_desc()
competition_info = self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None))
runtime_environment = self.scen.get_runtime_environment()
data_folder_info = self.scen.processed_data_folder_description
data_loader_task_info = target_task.get_task_information()
@@ -225,11 +225,21 @@ class DataLoaderCoSTEER(CoSTEER):
def develop(self, exp):
new_exp = super().develop(exp)
env = get_ds_env()
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}": "/kaggle/input"}
env = get_ds_env(
extra_volumes={
f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}": T(
"scenarios.data_science.share:scen.input_path"
).r()
},
running_timeout_period=DS_RD_SETTING.full_timeout,
)
stdout = new_exp.experiment_workspace.execute(env=env, entry=f"python test/data_loader_test.py")
match = re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===", stdout, re.DOTALL)
eda_output = match.groups()[1] if match else None
self.scen.eda_output = eda_output
if eda_output is not None:
new_exp.experiment_workspace.inject_files(**{"EDA.md": eda_output})
else:
eda_output = "No EDA output."
new_exp.experiment_workspace.inject_files(**{"EDA.md": eda_output})
return new_exp
@@ -13,6 +13,7 @@ from rdagent.components.coder.CoSTEER.knowledge_management import (
CoSTEERQueriedKnowledgeV2,
)
from rdagent.components.coder.data_science.conf import get_ds_env
from rdagent.components.coder.data_science.utils import remove_eda_part
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.utils.agent.tpl import T
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
@@ -23,7 +24,6 @@ DataLoaderEvalFeedback = CoSTEERSingleFeedback
class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
def evaluate(
self,
target_task: Task,
@@ -32,7 +32,6 @@ class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
queried_knowledge: CoSTEERQueriedKnowledgeV2 = None,
**kwargs,
) -> DataLoaderEvalFeedback:
target_task_information = target_task.get_task_information()
if (
queried_knowledge is not None
@@ -47,14 +46,15 @@ class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
final_decision=False,
)
env = get_ds_env()
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
env = get_ds_env(extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()})
# TODO: do we need to clean the generated temporary content?
fname = "test/data_loader_test.py"
test_code = (DIRNAME / "eval_tests" / "data_loader_test.txt").read_text()
implementation.inject_files(**{fname: test_code})
stdout, ret_code = implementation.execute_ret_code(env=env, entry=f"python {fname}")
result = implementation.run(env=env, entry=f"python {fname}")
stdout = result.stdout
ret_code = result.exit_code
match = re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===(.*)", stdout, re.DOTALL)
stdout_part_1, eda_output, stdout_part_2 = match.groups() if match else (stdout, None, "")
stdout = stdout_part_1 + stdout_part_2
@@ -63,7 +63,7 @@ class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
if "main.py" in implementation.file_dict and ret_code == 0:
workflow_stdout = implementation.execute(env=env, entry="python main.py")
workflow_stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", workflow_stdout)
workflow_stdout = remove_eda_part(workflow_stdout)
else:
workflow_stdout = None
@@ -80,9 +80,12 @@ class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
workflow_stdout=workflow_stdout,
)
return build_cls_from_json_with_retry(
fb = build_cls_from_json_with_retry(
DataLoaderEvalFeedback,
system_prompt=system_prompt,
user_prompt=user_prompt,
init_kwargs_update_func=DataLoaderEvalFeedback.val_and_update_init_dict,
)
fb.final_decision = fb.final_decision and ret_code == 0
return fb
@@ -9,12 +9,22 @@ from load_data import load_data
import sys
import reprlib
from joblib.memory import MemorizedFunc
def get_original_code(func):
if isinstance(func, MemorizedFunc):
return func.func.__code__
return func.__code__
def debug_info_print(func):
aRepr = reprlib.Repr()
aRepr.maxother=300
def wrapper(*args, **kwargs):
original_code = get_original_code(func)
def local_trace(frame, event, arg):
if event == "return" and frame.f_code == func.__code__:
if event == "return" and frame.f_code == original_code:
print("\n" + "="*20 + "Running data_load code, local variable values:" + "="*20)
for k, v in frame.f_locals.items():
printed = aRepr.repr(v)
@@ -48,7 +48,7 @@ spec:
- `test_ids` (DT): Identifiers for the test data.
- Docstring Requirements:
- Describe the purpose of the function.
- Specify the data source location (`/kaggle/input/`).
- Specify the data source location (`{% include "scenarios.data_science.share:scen.input_path" %}`).
- Clearly define the structure and type of the output.
- Inferred data shape to each input and output data variables. To uncertain dimension, use -1.
2. Notes:
@@ -224,71 +224,7 @@ spec:
You should return the specification in markdown format directly, while the **function definition** within it should be in code format, tailored to the Competition Information, with detailed explanations provided in the docstring.
workflow: |-
Your task is to implement the main workflow script (`main.py`) for a Kaggle-style machine learning competition project.
Follow the provided project structure and specifications to ensure consistency and maintainability:
1. Workflow Integration:
- Integrate the following components into the workflow:
- Data loading (`load_data.py`).
- Feature engineering (`feature.py`).
- Model workflow for training and testing (`model_*.py`).
- Ensemble workflow that combines results from the model workflow to obtain the final prediction (`ensemble.py`).
- Treat each component as a modular and callable Python function.
- The workflow script should be flexible enough to handle either a single model or multiple models, with filenames (model_*.py) that are not determined at the outset.
For multiple model selection, utilize Python code to identify eligible models based on filenames, for example:
```python
available_models = [f for f in os.listdir('.') if f.startswith('model_') and 'test' not in f]
```
2. Feature Engineering
- The feature engineering should be called only once. For example:
`X_transformed, y_transformed, X_test_transformed = feat_eng(X, y, X_test)`
- It should be called before dataset splitting.
3. Dataset Splitting
- The dataset returned by `load_data` is not pre-split. After calling `feat_eng`, split the data into training and test sets.
- [Notice] If feasible, apply cross-validation on the training set (`X_transformed`, `y_transformed`) to ensure a reliable assessment of model performance.
- Keep the test set (`X_test_transformed`) unchanged, as it is only used for generating the final predictions.
- Pseudocode logic for reference:
```
Set number of splits and initialize KFold cross-validator.
Create dictionaries for validation and test predictions.
For each model file:
Import the model dynamically.
Initialize arrays for out-of-fold (OOF) and test predictions.
For each fold in KFold:
Split data into training and validation sets.
Run model workflow to get validation and test predictions.
Validate shapes.
Store validation and test predictions.
Compute average test predictions across folds.
Save OOF and averaged test predictions.
Ensemble predictions from all models and print the final shape.
```
4. Submission File:
- Save the final predictions as `submission.csv`, ensuring the format matches the competition requirements (refer to `sample_submission` in the Folder Description for the correct structure).
- Present the required submission format explicitly and ensure the output adheres to it.
5. Code Standards:
- Do not use progress bars (e.g., tqdm) in the code.
6. Ensemble Strategy:
Consolidate all model outputs into a dictionary, where each key is the model's filename (excluding the .py extension) and its corresponding value is the model's output.
Sample code:
{% raw %}
{% for model_name in model_names %}
model_module = __import__(model_name.replace('.py', ''))
val_pred, test_pred, _ = model_module.model_workflow(
X=train_X,
y=train_y,
val_X=val_X,
val_y=val_y,
test_X=X_test_transformed
)
val_preds_dict[model_module.__name__] = val_pred
test_preds_dict[model_module.__name__] = test_pred
{% endfor %}
final_pred = ensemble_workflow(test_preds_dict, val_preds_dict, val_y)
{% endraw %}
{% include "scenarios.data_science.share:component_spec.Workflow" %}
{% if latest_spec %}
7. Former Specification:
@@ -313,8 +249,8 @@ data_loader_coder:
{% endif %}
{% if queried_similar_successful_knowledge|length != 0 %}
--------- Successful Implementations for Similar Models ---------
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Model {{ loop.index }}:=====
--------- Successful Implementation Examples for Similar Task ---------
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Example {{ loop.index }}:=====
{{ similar_successful_knowledge.target_task.get_task_information() }}
=====Code:=====
{{ similar_successful_knowledge.implementation.all_codes }}
@@ -332,8 +268,14 @@ data_loader_coder:
{% endif %}
## Guidelines
1. Ensure that the dataset is loaded strictly from `/kaggle/input/`, following the exact folder structure described in the **Data Folder Description**, and do not attempt to load data from the current directory (`./`).
1. Ensure that the dataset is loaded strictly from `{% include "scenarios.data_science.share:scen.input_path" %}`, following the exact folder structure described in the **Data Folder Description**, and do not attempt to load data from the current directory (`./`).
2. You should avoid using logging module to output information in your generated code, and instead use the print() function.
3. You should use the following cache decorator to cache the results of the function:
```python
from joblib import Memory
memory = Memory(location='{% include "scenarios.data_science.share:scen.cache_path" %}', verbose=0)
@memory.cache```
{% include "scenarios.data_science.share:guidelines.coding" %}
## Exploratory Data Analysis (EDA) part(Required):
- Before returning the data, you should always add an EDA part describing the data to help the following steps understand the data better.
@@ -370,7 +312,7 @@ data_loader_coder:
--------- Code Specification ---------
{{ code_spec }}
--------- Data Folder Description (All path are relative to the data folder) ---------
--------- Data Folder Description (All path are relative to the data folder, i.e. "{% include "scenarios.data_science.share:scen.input_path" %}") ---------
{{ folder_spec }}
{% if latest_code %}
@@ -0,0 +1,37 @@
"""
Developers concentrating on writing documents for a workspace
"""
from rdagent.core.developer import Developer
from rdagent.core.experiment import Experiment, FBWorkspace
from rdagent.oai.llm_utils import APIBackend
from rdagent.utils.agent.ret import MarkdownAgentOut
from rdagent.utils.agent.tpl import T
class DocDev(Developer[Experiment]):
"""
The developer is responsible for writing documents for a workspace.
"""
def develop(self, exp: Experiment) -> None:
"""
Write documents for the workspace.
"""
ws: FBWorkspace = exp.experiment_workspace
file_li = [str(file.relative_to(ws.workspace_path)) for file in ws.workspace_path.rglob("*") if file.is_file()]
key_file_list = ["main.py", "scores.csv"]
system_prompt = T(".prompts:docdev.system").r()
user_prompt = T(".prompts:docdev.user").r(
file_li=file_li,
key_files={f: (ws.workspace_path / f).read_text() for f in key_file_list},
)
resp = APIBackend().build_messages_and_create_chat_completion(
user_prompt=user_prompt, system_prompt=system_prompt
)
markdown = MarkdownAgentOut.extract_output(resp)
ws.inject_files(**{"README.md": markdown})
@@ -0,0 +1,147 @@
from pathlib import Path
from typing import Literal
import pandas as pd
from rdagent.app.data_science.conf import DS_RD_SETTING
from rdagent.components.coder.CoSTEER import CoSTEERMultiFeedback
from rdagent.components.coder.CoSTEER.evaluators import (
CoSTEEREvaluator,
CoSTEERSingleFeedback,
)
from rdagent.components.coder.data_science.conf import get_clear_ws_cmd, get_ds_env
from rdagent.components.coder.data_science.utils import remove_eda_part
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.core.scenario import Scenario
from rdagent.utils.agent.tpl import T
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
DIRNAME = Path(__file__).absolute().resolve().parent
PipelineSingleFeedback = CoSTEERSingleFeedback
PipelineMultiFeedback = CoSTEERMultiFeedback
NO_SUB = "<No submission.csv file found.>"
NO_SCORE = "<No scores.csv file found.>"
class ModelDumpEvaluator(CoSTEEREvaluator):
"""This evaluator assumes that it runs after the model"""
def __init__(self, scen: Scenario, data_type: Literal["sample", "full"]):
super().__init__(scen)
self.data_type = data_type
def evaluate(
self, target_task: Task, implementation: FBWorkspace, gt_implementation: FBWorkspace, *kargs, **kwargs
) -> CoSTEERSingleFeedback:
model_folder = implementation.workspace_path / "models"
# 1) Check if the model_folder is not empty
if not model_folder.exists() or not any(model_folder.iterdir()):
err_msg = "Model folder (`models` sub folder) is empty or does not exist. The model is not dumped."
return CoSTEERSingleFeedback(
execution=err_msg,
return_checking=err_msg,
code=err_msg,
final_decision=False,
)
data_source_path = (
f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}"
if self.data_type == "full"
else self.scen.debug_path
)
env = get_ds_env(
extra_volumes={data_source_path: T("scenarios.data_science.share:scen.input_path").r()},
running_timeout_period=(
DS_RD_SETTING.full_timeout if self.data_type == "full" else DS_RD_SETTING.debug_timeout
),
)
# 2) check the result and stdout after reruning the model.
# Read the content of files submission.csv and scores.csv before execution
submission_content_before = (
(implementation.workspace_path / "submission.csv").read_text()
if (implementation.workspace_path / "submission.csv").exists()
else NO_SUB
)
scores_content_before = (
(implementation.workspace_path / "scores.csv").read_text()
if (implementation.workspace_path / "scores.csv").exists()
else NO_SCORE
)
# Remove the files submission.csv and scores.csv
implementation.execute(env=env, entry=get_clear_ws_cmd(stage="before_inference"))
# Execute the main script
stdout = remove_eda_part(implementation.execute(env=env, entry="python main.py"))
# walk model_folder and list the files
model_folder_files = [
str(file.relative_to(implementation.workspace_path)) for file in model_folder.iterdir() if file.is_file()
]
# this will assert the generation of necessary files
for f in ["submission.csv", "scores.csv"]:
if not (implementation.workspace_path / f).exists():
err_msg = f"{f} does not exist. The model is not dumped. Make sure that the required files, like submission.csv and scores.csv, are created even if you bypass the model training step by loading the saved model file directly."
return CoSTEERSingleFeedback(
execution=err_msg,
return_checking=err_msg,
code=err_msg,
final_decision=False,
)
# Check if scores contain NaN (values)
score_df = pd.read_csv((implementation.workspace_path / "scores.csv"), index_col=0)
if score_df.isnull().values.any():
nan_locations = score_df[score_df.isnull().any(axis=1)]
err_msg = f"\n[Error] The scores dataframe contains NaN values at the following locations:\n{nan_locations}"
return CoSTEERSingleFeedback(
execution=err_msg,
return_checking=err_msg,
code=err_msg,
final_decision=False,
)
submission_content_after = (
(implementation.workspace_path / "submission.csv").read_text()
if (implementation.workspace_path / "submission.csv").exists()
else NO_SUB
)
scores_content_after = (
(implementation.workspace_path / "scores.csv").read_text()
if (implementation.workspace_path / "scores.csv").exists()
else NO_SCORE
)
system_prompt = T(".prompts:dump_model_eval.system").r()
user_prompt = T(".prompts:dump_model_eval.user").r(
stdout=stdout.strip(),
code=implementation.all_codes,
model_folder_files=model_folder_files,
scores_content_before=scores_content_before,
scores_content_after=scores_content_after,
)
csfb = build_cls_from_json_with_retry(
CoSTEERSingleFeedback,
system_prompt=system_prompt,
user_prompt=user_prompt,
)
if DS_RD_SETTING.model_dump_check_level == "high":
# Read the content of files submission.csv and scores.csv after execution
# Check if the content has changed
# excactly same checking. But it will take more user's time
if scores_content_before != scores_content_after:
return_msg = "\n[Error] The content of scores.csv has changed. Please check the code to ensure that the model is dumped correctly, and rerun the code to use the model directly without retraining it."
return_msg += f"\nBefore:\n{scores_content_before}\nAfter:\n{scores_content_after}"
if submission_content_before != submission_content_after:
# If the scores file changes, display the two contents and append it into the return_checking
return_msg = "[Error] The content of submission.csv has changed. Please check the code to ensure that the model is dumped correctly, and rerun the code to use the model directly without retraining it."
csfb.return_checking = (csfb.return_checking or "") + return_msg
return csfb
@@ -0,0 +1,91 @@
dump_model_coder:
guideline: |-
Please dump the model in a "models/" subfolder in the first running, and the script rerun performs inference without needing to retrain the model when running the code again.
If there are parameters generated from the training data that might be needed for inference on test data, please save them in the "models/" subfolder as well.
If no test set is provided, reserve a portion of the data as your test set and save the generated test files in the models/ subfolder for use in submission and inference.
Make sure that the required files, like submission.csv and scores.csv, are created without model training step through loading the saved model and test data file directly.
dump_model_eval:
system: |-
You are a data scientist tasked with evaluating code generation. You've developed a Kaggle competition code that can produce a submission file.
The code should follow the guideline below:
{% include "components.coder.data_science.share.prompts:dump_model_coder.guideline" %}
You will receive the following information:
- The implemented code
- The stdout from running the code
- The file list in "models/" subfolder
- The scores.csv file generated during both training and inference (if it exists)
Focus on these aspects:
- Check if the code saves the model in the "models/" subfolder.
- Check if the code saves the test data in the "models/" subfolder when there is no test data specified.
- Ensure that when the code is rerun, it skips the training process and loads the model from the "models/" subfolder for direct inference.
- Verify that there is no training activity in the output.
- Ensure that even if you skip the model training by loading saved models, the files like scores.csv and submission.csv are still correctly created.
- The model's performance should remain consistent and not vary unreasonably between training and inference.
Please respond with your feedback in the following JSON format and order
```json
{
"execution": "Describe whether the code executed successfully. Include any errors or issues encountered, and append all error messages and full traceback details without summarizing or omitting any information. Carefully check the stdout to ensure that when the code is rerun, it skips the training process and loads the model from the 'models/' subfolder for direct inference. Append the information that makes you think that the model is still being retrained when rerunning the code."
"return_checking": "Verify the generated files include necessary files. Make sure scores.csv file does not change unreasonably between training and inference",
"code": "The code has explicity dump the model into 'models/' subfolder; When the modes files are already in 'models/' subfolder, the code will explicity skip the training process.",
"final_decision": <true or false in boolean type; only return true when ensuring that the code saves the model in a 'models/' subfolder, and the script rerun performs inference without needing to retrain the model.>
}
```
user: |-
------------ The implemented code ------------
{{code}}
------------ The stdout from running the code ------------
{{stdout}}
------------ The file list in "models/" subfolder ------------
{% for f in model_folder_files %}
- {{ f }}
{% endfor %}
------------ The scores.csv file generated ------------
# Training:
{{scores_content_before}}
# Inference:
{{scores_content_after}}
docdev:
system: |-
{% include "scenarios.data_science.share:scen.role" %} Your task is to create documentation for a data science solution.
You will be given:
- a list of files in the folder.
- content from some important files.
Please explain the trained models in the "models/" folder. The training and inference processes are detailed in the `main.py` file. The models' evaluation results are in `scores.csv`. Please respond with a markdown file that includes the following information:
- Explain the purpose of each model. If some models are part of a group (like those from cross-validation), describe them together.
- Provide key details for each model group:
- Important training parameters
- Model details
- Performance of each model
Be brief. Mention the file path when you introduce files.
Don't introduce anything other than models.
{% include "utils.agent.tpl:MarkdownOut" %}
user: |-
--------------- The file list in the workspace ---------------
{% for f in file_li %}
- {{ f }}
{% endfor %}
--------------- File content of each file ---------------
{% for fname, content in key_files.items() %}
File Path: {{fname}}
```
{{content}}
```
{% endfor %}
@@ -0,0 +1,6 @@
import re
def remove_eda_part(stdout: str) -> str:
"""Data Science scenario have a LLM-based EDA feature. We can remove it when current task does not involve EDA"""
return re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", stdout, flags=re.DOTALL)
@@ -59,7 +59,7 @@ class WorkflowMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
# 2. code
system_prompt = T(".prompts:workflow_coder.system").r(
task_desc=workflow_information_str,
competition_info=self.scen.get_scenario_all_desc(),
competition_info=self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None)),
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
queried_former_failed_knowledge=queried_former_failed_knowledge[0],
out_spec=PythonAgentOut.get_spec(),
@@ -10,9 +10,11 @@ from rdagent.components.coder.CoSTEER.evaluators import (
CoSTEERMultiFeedback,
CoSTEERSingleFeedback,
)
from rdagent.components.coder.data_science.conf import get_ds_env
from rdagent.components.coder.data_science.conf import get_clear_ws_cmd, get_ds_env
from rdagent.components.coder.data_science.utils import remove_eda_part
from rdagent.core.evolving_framework import QueriedKnowledge
from rdagent.core.experiment import FBWorkspace, Task
from rdagent.log import rdagent_logger as logger
from rdagent.utils.agent.tpl import T
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
@@ -53,8 +55,7 @@ class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
final_decision=False,
)
env = get_ds_env()
env.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/sample/{self.scen.competition}": "/kaggle/input"}
env = get_ds_env(extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()})
# # DockerEnv for MLEBench submission validation
# mle_de_conf = MLEBDockerConf()
@@ -65,12 +66,12 @@ class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
# mde.prepare()
# Clean the scores.csv & submission.csv.
implementation.execute(env=env, entry=f"rm submission.csv scores.csv")
implementation.execute(env=env, entry=get_clear_ws_cmd())
stdout = implementation.execute(env=env, entry=f"python main.py")
stdout = implementation.execute(env=env, entry=f"python -m coverage run main.py")
# remove EDA part
stdout = re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", stdout)
stdout = remove_eda_part(stdout)
# Check score file
score_fp = implementation.workspace_path / "scores.csv"
@@ -79,6 +80,14 @@ class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
if not score_fp.exists():
score_check_text = "[Error] Metrics file (scores.csv) is not generated!"
score_ret_code = 1
implementation.execute(env=env, entry="python -m coverage json -o coverage.json")
coverage_report_path = implementation.workspace_path / "coverage.json"
if coverage_report_path.exists():
used_files = set(json.loads(coverage_report_path.read_text())["files"].keys())
coverage_report_path.unlink()
logger.info(f"All used scripts: {used_files}")
if len(used_files) == 1:
score_check_text += f"\n[Error] The only used script is {used_files}.\nPlease check if you have implemented entry point in 'main.py'."
else:
try:
score_df = pd.read_csv(score_fp, index_col=0)
@@ -98,21 +107,28 @@ class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
score_check_text += f"\n[Error] The scores dataframe does not contain the correct column names.\nCorrect columns is: ['{self.scen.metric_name}']\nBut got: {score_df.columns.tolist()}"
score_ret_code = 1
# Check if scores contain NaN (values)
if score_df.isnull().values.any():
nan_locations = score_df[score_df.isnull().any(axis=1)]
score_check_text += f"\n[Error] The scores dataframe contains NaN values at the following locations:\n{nan_locations}"
score_ret_code = 1
except Exception as e:
score_check_text += f"\n[Error] in checking the scores.csv file: {e}\nscores.csv's content:\n-----\n{score_fp.read_text()}\n-----"
score_ret_code = 1
# Check submission file
base_check_code = (DIRNAME / "eval_tests" / "submission_format_test.txt").read_text()
base_check_code = T(".eval_tests.submission_format_test", ftype="txt").r()
implementation.inject_files(**{"test/submission_format_test.py": base_check_code})
# stdout += "----Submission Check 1-----\n"
submission_check_out, submission_ret_code = implementation.execute_ret_code(
env=env, entry="python test/submission_format_test.py"
)
submission_result = implementation.run(env=env, entry="python test/submission_format_test.py")
submission_check_out = submission_result.stdout
submission_ret_code = submission_result.exit_code
stdout += "\n" + submission_check_out
system_prompt = T(".prompts:workflow_eval.system").r(
scenario=self.scen.get_scenario_all_desc(),
# here we pass `None` to `eda_output` because we do not have nor need EDA output for workflow.
scenario=self.scen.get_scenario_all_desc(eda_output=None),
task_desc=target_task.get_task_information(),
spec=(
implementation.file_dict["spec/workflow.md"]
@@ -22,12 +22,12 @@ find . | grep -i sample | grep -i submission | grep -v sample_submission.csv | g
"""
# Find sample submission file dynamically
input_dir = Path("/kaggle/input")
input_dir = Path("{% include "scenarios.data_science.share:scen.input_path" %}")
# Look for common variations of sample submission filenames
sample_submission_files = list(input_dir.glob("*sample_submission*.csv")) + \
list(input_dir.glob("*sampleSubmission*.csv"))
assert sample_submission_files, "Error: No sample submission file found in /kaggle/input/"
assert sample_submission_files, "Error: No sample submission file found in {% include "scenarios.data_science.share:scen.input_path" %}"
# Use first matching file
sample_submission_name = sample_submission_files[0].name
@@ -10,4 +10,5 @@ from rdagent.core.utils import cache_with_pickle
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
class WorkflowTask(CoSTEERTask):
pass
def __init__(self, name: str = "Workflow", *args, **kwargs) -> None:
super().__init__(name=name, *args, **kwargs)
@@ -40,6 +40,7 @@ workflow_coder:
3. The user may provide specific code organization rules and instructions. Ensure that the integration follows the given framework and structure.
4. After predicting the output, print the shape and other information of the output to stdout to help the evaluator assess the code.
5. You should avoid using logging module to output information in your generated code, and instead use the print() function.
{% include "scenarios.data_science.share:guidelines.coding" %}
## Output Format
{% if out_spec %}
@@ -133,4 +134,4 @@ workflow_eval:
--------- Workflow test stdout ---------
{{ stdout }}
--------- Workflow code generated by user ---------
{{ code }}
{{ code }}
@@ -26,6 +26,7 @@ class FactorCoSTEER(CoSTEER):
try:
exp = super().develop(exp)
finally:
es = self.evolve_agent.evolving_trace[-1]
exp.prop_dev_feedback = es.feedback
if hasattr(self, "evolve_agent") and self.evolve_agent.evolving_trace:
es = self.evolve_agent.evolving_trace[-1]
exp.prop_dev_feedback = es.feedback
return exp
@@ -15,7 +15,7 @@ class FactorCoSTEERSettings(CoSTEERSettings):
simple_background: bool = False
"""Whether to use simple background information for code feedback"""
file_based_execution_timeout: int = 120
file_based_execution_timeout: int = 3600
"""Timeout in seconds for each factor implementation execution"""
select_method: str = "random"
@@ -1,20 +1,16 @@
import io
import json
from abc import abstractmethod
from pathlib import Path
from typing import Dict, Tuple
import pandas as pd
from jinja2 import Environment, StrictUndefined
from rdagent.components.coder.factor_coder.config import FACTOR_COSTEER_SETTINGS
from rdagent.components.coder.factor_coder.factor import FactorTask
from rdagent.core.experiment import Task, Workspace
from rdagent.core.prompts import Prompts
from rdagent.oai.llm_conf import LLM_SETTINGS
from rdagent.oai.llm_utils import APIBackend
evaluate_prompts = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
from rdagent.utils.agent.tpl import T
class FactorEvaluator:
@@ -81,36 +77,26 @@ class FactorCodeEvaluator(FactorEvaluator):
factor_information = target_task.get_task_information()
code = implementation.all_codes
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(evaluate_prompts["evaluator_code_feedback_v1_system"])
.render(
scenario=(
self.scen.get_scenario_all_desc(
target_task,
filtered_tag="feature",
simple_background=FACTOR_COSTEER_SETTINGS.simple_background,
)
if self.scen is not None
else "No scenario description."
system_prompt = T(".prompts:evaluator_code_feedback_v1_system").r(
scenario=(
self.scen.get_scenario_all_desc(
target_task,
filtered_tag="feature",
simple_background=FACTOR_COSTEER_SETTINGS.simple_background,
)
if self.scen is not None
else "No scenario description."
)
)
execution_feedback_to_render = execution_feedback
for _ in range(10): # 10 times to split the content is enough
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
evaluate_prompts["evaluator_code_feedback_v1_user"],
)
.render(
factor_information=factor_information,
code=code,
execution_feedback=execution_feedback_to_render,
value_feedback=value_feedback,
gt_code=gt_implementation.code if gt_implementation else None,
)
user_prompt = T(".prompts:evaluator_code_feedback_v1_user").r(
factor_information=factor_information,
code=code,
execution_feedback=execution_feedback_to_render,
value_feedback=value_feedback,
gt_code=gt_implementation.code if gt_implementation else None,
)
if (
APIBackend().build_messages_and_calculate_token(
@@ -189,17 +175,11 @@ class FactorOutputFormatEvaluator(FactorEvaluator):
buffer = io.StringIO()
gen_df.info(buf=buffer)
gen_df_info_str = f"The user is currently working on a feature related task.\nThe output dataframe info is:\n{buffer.getvalue()}"
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
evaluate_prompts["evaluator_output_format_system"],
)
.render(
scenario=(
self.scen.get_scenario_all_desc(implementation.target_task, filtered_tag="feature")
if self.scen is not None
else "No scenario description."
)
system_prompt = T(".prompts:evaluator_output_format_system").r(
scenario=(
self.scen.get_scenario_all_desc(implementation.target_task, filtered_tag="feature")
if self.scen is not None
else "No scenario description."
)
)
@@ -504,35 +484,25 @@ class FactorFinalDecisionEvaluator(FactorEvaluator):
code_feedback: str,
**kwargs,
) -> Tuple:
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(evaluate_prompts["evaluator_final_decision_v1_system"])
.render(
scenario=(
self.scen.get_scenario_all_desc(target_task, filtered_tag="feature")
if self.scen is not None
else "No scenario description."
)
system_prompt = T(".prompts:evaluator_final_decision_v1_system").r(
scenario=(
self.scen.get_scenario_all_desc(target_task, filtered_tag="feature")
if self.scen is not None
else "No scenario description."
)
)
execution_feedback_to_render = execution_feedback
for _ in range(10): # 10 times to split the content is enough
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
evaluate_prompts["evaluator_final_decision_v1_user"],
)
.render(
factor_information=target_task.get_task_information(),
execution_feedback=execution_feedback_to_render,
code_feedback=code_feedback,
value_feedback=(
value_feedback
if value_feedback is not None
else "No Ground Truth Value provided, so no evaluation on value is performed."
),
)
user_prompt = T(".prompts:evaluator_final_decision_v1_user").r(
factor_information=target_task.get_task_information(),
execution_feedback=execution_feedback_to_render,
code_feedback=code_feedback,
value_feedback=(
value_feedback
if value_feedback is not None
else "No Ground Truth Value provided, so no evaluation on value is performed."
),
)
if (
APIBackend().build_messages_and_calculate_token(
@@ -1,11 +1,9 @@
from __future__ import annotations
import json
from pathlib import Path
import re
from typing import Dict
from jinja2 import Environment, StrictUndefined
from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback
from rdagent.components.coder.CoSTEER.evolving_strategy import (
MultiProcessEvolvingStrategy,
@@ -17,11 +15,9 @@ from rdagent.components.coder.CoSTEER.knowledge_management import (
from rdagent.components.coder.factor_coder.config import FACTOR_COSTEER_SETTINGS
from rdagent.components.coder.factor_coder.factor import FactorFBWorkspace, FactorTask
from rdagent.core.experiment import FBWorkspace
from rdagent.core.prompts import Prompts
from rdagent.oai.llm_conf import LLM_SETTINGS
from rdagent.oai.llm_utils import APIBackend
implement_prompts = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
from rdagent.utils.agent.tpl import T
class FactorMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
@@ -36,24 +32,14 @@ class FactorMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
queried_former_failed_knowledge_to_render: list,
queried_similar_error_knowledge_to_render: list,
) -> str:
error_summary_system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(implement_prompts["evolving_strategy_error_summary_v2_system"])
.render(
scenario=self.scen.get_scenario_all_desc(target_task),
factor_information_str=target_task.get_task_information(),
code_and_feedback=queried_former_failed_knowledge_to_render[-1].get_implementation_and_feedback_str(),
)
.strip("\n")
error_summary_system_prompt = T(".prompts:evolving_strategy_error_summary_v2_system").r(
scenario=self.scen.get_scenario_all_desc(target_task),
factor_information_str=target_task.get_task_information(),
code_and_feedback=queried_former_failed_knowledge_to_render[-1].get_implementation_and_feedback_str(),
)
for _ in range(10): # max attempt to reduce the length of error_summary_user_prompt
error_summary_user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(implement_prompts["evolving_strategy_error_summary_v2_user"])
.render(
queried_similar_error_knowledge=queried_similar_error_knowledge_to_render,
)
.strip("\n")
error_summary_user_prompt = T(".prompts:evolving_strategy_error_summary_v2_user").r(
queried_similar_error_knowledge=queried_similar_error_knowledge_to_render,
)
if (
APIBackend().build_messages_and_calculate_token(
@@ -106,16 +92,9 @@ class FactorMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
latest_attempt_to_latest_successful_execution = queried_knowledge.task_to_former_failed_traces[
target_factor_task_information
][1]
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
implement_prompts["evolving_strategy_factor_implementation_v1_system"],
)
.render(
scenario=self.scen.get_scenario_all_desc(target_task, filtered_tag="feature"),
queried_former_failed_knowledge=queried_former_failed_knowledge_to_render,
)
system_prompt = T(".prompts:evolving_strategy_factor_implementation_v1_system").r(
scenario=self.scen.get_scenario_all_desc(target_task, filtered_tag="feature"),
queried_former_failed_knowledge=queried_former_failed_knowledge_to_render,
)
queried_similar_successful_knowledge_to_render = queried_similar_successful_knowledge
queried_similar_error_knowledge_to_render = queried_similar_error_knowledge
@@ -136,19 +115,12 @@ class FactorMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
else:
error_summary_critics = None
# 构建user_prompt。开始写代码
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
implement_prompts["evolving_strategy_factor_implementation_v2_user"],
)
.render(
factor_information_str=target_factor_task_information,
queried_similar_successful_knowledge=queried_similar_successful_knowledge_to_render,
queried_similar_error_knowledge=queried_similar_error_knowledge_to_render,
error_summary_critics=error_summary_critics,
latest_attempt_to_latest_successful_execution=latest_attempt_to_latest_successful_execution,
)
.strip("\n")
user_prompt = T(".prompts:evolving_strategy_factor_implementation_v2_user").r(
factor_information_str=target_factor_task_information,
queried_similar_successful_knowledge=queried_similar_successful_knowledge_to_render,
queried_similar_error_knowledge=queried_similar_error_knowledge_to_render,
error_summary_critics=error_summary_critics,
latest_attempt_to_latest_successful_execution=latest_attempt_to_latest_successful_execution,
)
if (
APIBackend().build_messages_and_calculate_token(user_prompt=user_prompt, system_prompt=system_prompt)
@@ -165,18 +137,28 @@ class FactorMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
queried_similar_error_knowledge_to_render = queried_similar_error_knowledge_to_render[:-1]
for _ in range(10):
try:
code = json.loads(
APIBackend(
use_chat_cache=FACTOR_COSTEER_SETTINGS.coder_use_cache
).build_messages_and_create_chat_completion(
user_prompt=user_prompt,
system_prompt=system_prompt,
json_mode=True,
json_target_type=Dict[str, str],
)
)["code"]
response = APIBackend(
use_chat_cache=FACTOR_COSTEER_SETTINGS.coder_use_cache
).build_messages_and_create_chat_completion(
user_prompt=user_prompt,
system_prompt=system_prompt,
json_mode=True,
json_target_type=Dict[str, str],
)
try:
code = json.loads(response)["code"]
except json.decoder.JSONDecodeError:
# extract python code block
match = re.search(r"```python(.*?)```", response, re.DOTALL)
if match:
code = match.group(1).strip()
else:
raise # continue to retry
return code
except json.decoder.JSONDecodeError:
except (json.decoder.JSONDecodeError, KeyError):
pass
else:
return "" # return empty code if failed to get code after 10 attempts
@@ -49,6 +49,12 @@ class FactorTask(CoSTEERTask):
return f"""factor_name: {self.factor_name}
factor_description: {self.factor_description}
factor_formulation: {self.factor_formulation}
variables: {str(self.variables)}"""
def get_task_brief_information(self):
return f"""factor_name: {self.factor_name}
factor_description: {self.factor_description}
factor_formulation: {self.factor_formulation}
variables: {str(self.variables)}"""
def get_task_information_and_implementation_result(self):
@@ -49,6 +49,8 @@ evolving_strategy_factor_implementation_v1_system: |-
Your must write your code based on your former latest attempt below which consists of your former code and code feedback, you should read the former attempt carefully and must not modify the right part of your former code.
Notice that you should not add any other text before or after the json format.
{% if queried_former_failed_knowledge|length != 0 %}
--------------Your former latest attempt:---------------
=====Code to the former implementation=====
@@ -116,6 +118,9 @@ evolving_strategy_error_summary_v2_system: |-
You suggestion should not include any code, just some clear and short suggestions. Please point out very critical issues in your response, ignore non-important issues to avoid confusion. If no big issue found in the code, you can response "No critics found".
[NOTE]
1. When processing data, avoid time leakage.
Please response the critic in the following format. Here is an example structure for the output:
critic 1: The critic message to critic 1
critic 2: The critic message to critic 2
@@ -185,7 +190,7 @@ evaluator_final_decision_v1_system: |-
The implementation final decision is considered in the following logic:
1. If the value and the ground truth value are exactly the same under a small tolerance, the implementation is considered correct.
2. If the value and the ground truth value have a high correlation on ic or rank ic, the implementation is considered correct.
3. If no ground truth value is provided, the implementation is considered correct if the code executes successfully (assuming the data provided is correct). Any exceptions, including those actively raised, are considered faults of the code. Additionally, the code feedback must align with the scenario and factor description.
3. If no ground truth value is provided, the implementation is considered correct if the code executes successfully (assuming the data provided is correct). Any exceptions, including those actively raised, are considered faults of the code. Additionally, the code feedback must align with the scenario and factor description. The implementation cannot be considered correct if the code execution failed, no matter what the reason is.
Please response the critic in the json format. Here is an example structure for the JSON output, please strictly follow the format:
{
@@ -0,0 +1,13 @@
from pydantic_settings import SettingsConfigDict
from rdagent.components.coder.CoSTEER.config import CoSTEERSettings
class ModelCoSTEERSettings(CoSTEERSettings):
model_config = SettingsConfigDict(env_prefix="MODEL_CoSTEER_")
env_type: str = "conda" # or "docker"
"""Environment to run model code in coder and runner: 'conda' for local conda env, 'docker' for Docker container"""
MODEL_COSTEER_SETTINGS = ModelCoSTEERSettings()
@@ -1,18 +1,14 @@
import json
from pathlib import Path
from typing import Dict, Tuple
import numpy as np
from jinja2 import Environment, StrictUndefined
from rdagent.components.coder.CoSTEER.evaluators import CoSTEEREvaluator
from rdagent.components.coder.model_coder.model import ModelFBWorkspace, ModelTask
from rdagent.core.experiment import Task, Workspace
from rdagent.core.prompts import Prompts
from rdagent.oai.llm_conf import LLM_SETTINGS
from rdagent.oai.llm_utils import APIBackend
evaluate_prompts = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
from rdagent.utils.agent.tpl import T
# This shape evaluator is also used in data_science
@@ -70,32 +66,21 @@ class ModelCodeEvaluator(CoSTEEREvaluator):
model_task_information = target_task.get_task_information()
code = implementation.all_codes
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(evaluate_prompts["evaluator_code_feedback"]["system"])
.render(
scenario=(
self.scen.get_scenario_all_desc(target_task, filtered_tag=target_task.model_type)
if self.scen is not None
else "No scenario description."
)
system_prompt = T(".prompts:evaluator_code_feedback.system").r(
scenario=(
self.scen.get_scenario_all_desc(target_task, filtered_tag=target_task.model_type)
if self.scen is not None
else "No scenario description."
)
)
execution_feedback_to_render = model_execution_feedback
for _ in range(10): # 10 times to split the content is enough
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
evaluate_prompts["evaluator_code_feedback"]["user"],
)
.render(
model_information=model_task_information,
code=code,
model_execution_feedback=execution_feedback_to_render,
model_value_feedback=model_value_feedback,
gt_code=gt_implementation.all_codes if gt_implementation else None,
)
user_prompt = T(".prompts:evaluator_code_feedback.user").r(
model_information=model_task_information,
code=code,
model_execution_feedback=execution_feedback_to_render,
model_value_feedback=model_value_feedback,
gt_code=gt_implementation.all_codes if gt_implementation else None,
)
if (
APIBackend().build_messages_and_calculate_token(
@@ -133,34 +118,25 @@ class ModelFinalEvaluator(CoSTEEREvaluator):
if gt_implementation is not None:
assert isinstance(gt_implementation, ModelFBWorkspace)
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(evaluate_prompts["evaluator_final_feedback"]["system"])
.render(
scenario=(
self.scen.get_scenario_all_desc(target_task, filtered_tag=target_task.model_type)
if self.scen is not None
else "No scenario description."
)
system_prompt = T(".prompts:evaluator_final_feedback.system").r(
scenario=(
self.scen.get_scenario_all_desc(target_task, filtered_tag=target_task.model_type)
if self.scen is not None
else "No scenario description."
)
)
execution_feedback_to_render = model_execution_feedback
for _ in range(10): # 10 times to split the content is enough
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
evaluate_prompts["evaluator_final_feedback"]["user"],
)
.render(
model_information=target_task.get_task_information(),
model_execution_feedback=execution_feedback_to_render,
model_shape_feedback=model_shape_feedback,
model_code_feedback=model_code_feedback,
model_value_feedback=model_value_feedback,
)
user_prompt = T(".prompts:evaluator_final_feedback.user").r(
model_information=target_task.get_task_information(),
model_execution_feedback=execution_feedback_to_render,
model_shape_feedback=model_shape_feedback,
model_code_feedback=model_code_feedback,
model_value_feedback=model_value_feedback,
)
if (
APIBackend().build_messages_and_calculate_token(
user_prompt=user_prompt,
@@ -1,9 +1,6 @@
import json
from pathlib import Path
from typing import Dict
from jinja2 import Environment, StrictUndefined
from rdagent.components.coder.CoSTEER.config import CoSTEER_SETTINGS
from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback
from rdagent.components.coder.CoSTEER.evolving_strategy import (
@@ -13,17 +10,11 @@ from rdagent.components.coder.CoSTEER.knowledge_management import (
CoSTEERQueriedKnowledge,
CoSTEERQueriedKnowledgeV2,
)
from rdagent.components.coder.model_coder.model import (
ModelExperiment,
ModelFBWorkspace,
ModelTask,
)
from rdagent.components.coder.model_coder.model import ModelFBWorkspace, ModelTask
from rdagent.core.experiment import FBWorkspace
from rdagent.core.prompts import Prompts
from rdagent.oai.llm_conf import LLM_SETTINGS
from rdagent.oai.llm_utils import APIBackend
coder_prompts = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
from rdagent.utils.agent.tpl import T
class ModelMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
@@ -52,32 +43,18 @@ class ModelMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
if isinstance(queried_knowledge, CoSTEERQueriedKnowledgeV2)
else queried_former_failed_knowledge
)
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
coder_prompts["evolving_strategy_model_coder"]["system"],
)
.render(
scenario=self.scen.get_scenario_all_desc(filtered_tag=target_task.model_type),
queried_former_failed_knowledge=queried_former_failed_knowledge_to_render,
current_code=target_task.base_code,
)
system_prompt = T(".prompts:evolving_strategy_model_coder.system").r(
scenario=self.scen.get_scenario_all_desc(filtered_tag="model"),
queried_former_failed_knowledge=queried_former_failed_knowledge_to_render,
current_code=workspace.file_dict.get("model.py"),
)
queried_similar_successful_knowledge_to_render = queried_similar_successful_knowledge
for _ in range(10): # max attempt to reduce the length of user_prompt
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(
coder_prompts["evolving_strategy_model_coder"]["user"],
)
.render(
model_information_str=model_information_str,
queried_similar_successful_knowledge=queried_similar_successful_knowledge_to_render,
queried_former_failed_knowledge=queried_former_failed_knowledge_to_render,
)
.strip("\n")
user_prompt = T(".prompts:evolving_strategy_model_coder.user").r(
model_information_str=model_information_str,
queried_similar_successful_knowledge=queried_similar_successful_knowledge_to_render,
queried_former_failed_knowledge=queried_former_failed_knowledge_to_render,
)
if (
APIBackend().build_messages_and_calculate_token(
+24 -2
View File
@@ -5,10 +5,11 @@ from pathlib import Path
from typing import Dict, Optional
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
from rdagent.components.coder.model_coder.conf import MODEL_COSTEER_SETTINGS
from rdagent.core.experiment import Experiment, FBWorkspace
from rdagent.core.utils import cache_with_pickle
from rdagent.oai.llm_utils import md5_hash
from rdagent.utils.env import KGDockerEnv, QTDockerEnv
from rdagent.utils.env import KGDockerEnv, QlibCondaConf, QlibCondaEnv, QTDockerEnv
class ModelTask(CoSTEERTask):
@@ -19,6 +20,7 @@ class ModelTask(CoSTEERTask):
architecture: str,
*args,
hyperparameters: Dict[str, str],
training_hyperparameters: Dict[str, str],
formulation: str = None,
variables: Dict[str, str] = None,
model_type: Optional[str] = None,
@@ -28,6 +30,7 @@ class ModelTask(CoSTEERTask):
self.architecture: str = architecture
self.variables: str = variables
self.hyperparameters: str = hyperparameters
self.training_hyperparameters: str = training_hyperparameters
self.model_type: str = (
model_type # Tabular for tabular model, TimesSeries for time series model, Graph for graph model, XGBoost for XGBoost model
)
@@ -41,6 +44,17 @@ description: {self.description}
task_desc += f"architecture: {self.architecture}\n"
task_desc += f"variables: {self.variables}\n" if self.variables else ""
task_desc += f"hyperparameters: {self.hyperparameters}\n"
task_desc += f"training_hyperparameters: {self.training_hyperparameters}\n"
task_desc += f"model_type: {self.model_type}\n"
return task_desc
def get_task_brief_information(self):
task_desc = f"""name: {self.name}
description: {self.description}
"""
task_desc += f"architecture: {self.architecture}\n"
task_desc += f"hyperparameters: {self.hyperparameters}\n"
task_desc += f"training_hyperparameters: {self.training_hyperparameters}\n"
task_desc += f"model_type: {self.model_type}\n"
return task_desc
@@ -99,7 +113,15 @@ class ModelFBWorkspace(FBWorkspace):
):
self.before_execute()
try:
qtde = QTDockerEnv() if self.target_task.version == 1 else KGDockerEnv()
if self.target_task.version == 1:
if MODEL_COSTEER_SETTINGS.env_type == "docker":
qtde = QTDockerEnv()
elif MODEL_COSTEER_SETTINGS.env_type == "conda":
qtde = QlibCondaEnv(conf=QlibCondaConf())
else:
raise ValueError(f"Unknown env_type: {MODEL_COSTEER_SETTINGS.env_type}")
else:
qtde = KGDockerEnv()
qtde.prepare()
if self.target_task.version == 1:
@@ -16,8 +16,8 @@ if MODEL_TYPE == "Tabular":
m = model_cls(num_features=input_shape[1])
data = torch.full(input_shape, INPUT_VALUE)
elif MODEL_TYPE == "TimeSeries":
input_shape = (BATCH_SIZE, NUM_FEATURES, NUM_TIMESTEPS)
m = model_cls(num_features=input_shape[1], num_timesteps=input_shape[2])
input_shape = (BATCH_SIZE, NUM_TIMESTEPS, NUM_FEATURES)
m = model_cls(num_features=input_shape[2], num_timesteps=input_shape[1])
data = torch.full(input_shape, INPUT_VALUE)
elif MODEL_TYPE == "Graph":
node_feature = torch.randn(BATCH_SIZE, NUM_FEATURES)
@@ -1,12 +1,10 @@
import re
from pathlib import Path
from jinja2 import Environment, StrictUndefined
from rdagent.components.coder.model_coder.model import ModelExperiment, ModelFBWorkspace
from rdagent.core.developer import Developer
from rdagent.core.prompts import Prompts
from rdagent.oai.llm_utils import APIBackend
from rdagent.utils.agent.tpl import T
DIRNAME = Path(__file__).absolute().resolve().parent
@@ -17,18 +15,14 @@ class ModelCodeWriter(Developer[ModelExperiment]):
for t in exp.sub_tasks:
mti = ModelFBWorkspace(t)
mti.prepare()
pr = Prompts(file_path=DIRNAME / "prompt.yaml")
user_prompt_tpl = Environment(undefined=StrictUndefined).from_string(pr["code_implement_user"])
sys_prompt_tpl = Environment(undefined=StrictUndefined).from_string(pr["code_implement_sys"])
user_prompt = user_prompt_tpl.render(
user_prompt = T(".prompts:code_implement_user").r(
name=t.name,
description=t.description,
formulation=t.formulation,
variables=t.variables,
)
system_prompt = sys_prompt_tpl.render()
system_prompt = T(".prompts:code_implement_sys").r()
resp = APIBackend().build_messages_and_create_chat_completion(user_prompt, system_prompt)
@@ -1,41 +1,29 @@
extract_model_formulation_system: |-
offer description of the proposed model in this paper, write a latex formula with variable as well as the architecture of the model. the format should be like
{
"model_name (The name of the model)": {
"description": "A detailed description of the model",
"formulation": "A LaTeX formula representing the model's formulation",
"architecture": "A detailed description of the model's architecture, e.g., neural network layers or tree structures",
"variables": {
"\\hat{y}_u": "The predicted output for node u",
"variable_name_2": "Description of variable 2",
"variable_name_3": "Description of variable 3"
},
"hyperparameters": {
"hyperparameter_name_1": "value of hyperparameter 1",
"hyperparameter_name_2": "value of hyperparameter 2",
"hyperparameter_name_3": "value of hyperparameter 3"
},
"model_type": "Tabular or TimeSeries or Graph or XGBoost" # Should be one of "Tabular", "TimeSeries", "Graph", or "XGBoost"
}
}
Eg.
{
"ABC Model": {
"description": "A detailed description of the model",
"formulation": "A LaTeX formula representing the model's formulation",
"architecture": "A detailed description of the model's architecture, e.g., neural network layers or tree structures",
"variables": {
"\\hat{y}_u": "The predicted output for node u",
"variable_name_2": "Description of variable 2",
"variable_name_3": "Description of variable 3"
},
"hyperparameters": {
"hyperparameter_name_1": "value of hyperparameter 1",
"hyperparameter_name_2": "value of hyperparameter 2",
"hyperparameter_name_3": "value of hyperparameter 3"
},
"model_type": "Tabular or TimeSeries or Graph or RandomForest or XGBoost" # If torch & Neural network models are required, the choice should be one of "Tabular", "TimeSeries", or "Graph"
}
"model_name (The name of the model)": {
"description": "A detailed description of the model",
"formulation": "A LaTeX formula representing the model's formulation",
"architecture": "A detailed description of the model's architecture, e.g., neural network layers or tree structures",
"variables": {
"\\hat{y}_u": "The predicted output for node u",
"variable_name_2": "Description of variable 2",
"variable_name_3": "Description of variable 3"
},
"hyperparameters": {
"hyperparameter_name_1": "value of hyperparameter 1",
"hyperparameter_name_2": "value of hyperparameter 2",
"hyperparameter_name_3": "value of hyperparameter 3"
},
"training_hyperparameters" { # All values are for reference; you can set them yourself
"n_epochs": "100",
"lr": "1e-3",
"early_stop": 10,
"batch_size": 256,
"weight_decay": 1e-4,
}
"model_type": "Tabular or TimeSeries or Graph or XGBoost" # Should be one of "Tabular", "TimeSeries", "Graph", or "XGBoost"
}
}
such format content should be begin with ```json and end with ``` and the content should be in json format.
@@ -2,19 +2,19 @@ from __future__ import annotations
import json
import re
from pathlib import Path
from rdagent.components.coder.model_coder.model import ModelExperiment, ModelTask
from pydantic import BaseModel, Field
from rdagent.components.coder.model_coder.model import ModelTask
from rdagent.components.document_reader.document_reader import (
load_and_process_pdfs_by_langchain,
)
from rdagent.components.loader.task_loader import ModelTaskLoader
from rdagent.core.prompts import Prompts
from rdagent.log import rdagent_logger as logger
from rdagent.oai.llm_utils import APIBackend
from rdagent.scenarios.qlib.experiment.model_experiment import QlibModelExperiment
document_process_prompts = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
from rdagent.utils.agent.tpl import T
from rdagent.utils.workflow import wait_retry
def extract_model_from_doc(doc_content: str) -> dict:
@@ -32,7 +32,7 @@ def extract_model_from_doc(doc_content: str) -> dict:
{model_name: dict{description, formulation, variables}}
"""
session = APIBackend().build_chat_session(
session_system_prompt=document_process_prompts["extract_model_formulation_system"],
session_system_prompt=T(".prompts:extract_model_formulation_system").r(),
)
current_user_prompt = doc_content
@@ -97,7 +97,7 @@ def extract_model_from_docs(docs_dict):
class ModelExperimentLoaderFromDict(ModelTaskLoader):
def load(self, model_dict: dict) -> list:
def load(self, model_dict: dict) -> QlibModelExperiment:
"""Load data from a dict."""
task_l = []
for model_name, model_data in model_dict.items():
@@ -108,6 +108,7 @@ class ModelExperimentLoaderFromDict(ModelTaskLoader):
architecture=model_data["architecture"],
variables=model_data["variables"],
hyperparameters=model_data["hyperparameters"],
training_hyperparameters=model_data["training_hyperparameters"],
model_type=model_data["model_type"],
)
task_l.append(task)
@@ -115,7 +116,8 @@ class ModelExperimentLoaderFromDict(ModelTaskLoader):
class ModelExperimentLoaderFromPDFfiles(ModelTaskLoader):
def load(self, file_or_folder_path: str) -> dict:
@wait_retry(retry_n=5)
def load(self, file_or_folder_path: str) -> QlibModelExperiment:
docs_dict = load_and_process_pdfs_by_langchain(file_or_folder_path) # dict{file_path:content}
model_dict = extract_model_from_docs(
docs_dict
@@ -13,15 +13,17 @@ from rdagent.components.knowledge_management.vector_base import (
cosine,
)
from rdagent.core.knowledge_base import KnowledgeBase
from rdagent.log import rdagent_logger as logger
from rdagent.oai.llm_utils import APIBackend
Node = KnowledgeMetaData
class UndirectedNode(Node):
def __init__(self, content: str = "", label: str = "", embedding: Any = None) -> None:
def __init__(self, content: str = "", label: str = "", embedding: Any = None, appendix: Any = None) -> None:
super().__init__(content, label, embedding)
self.neighbors: set[UndirectedNode] = set()
self.appendix = appendix # appendix stores any additional information
assert isinstance(content, str), "content must be a string"
def add_neighbor(self, node: UndirectedNode) -> None:
@@ -86,6 +88,10 @@ class Graph(KnowledgeBase):
size = 16
embeddings = []
for i in range(0, len(contents), size):
logger.info(
f"Creating embedding for index {i} to {i + size} with {len(contents)} contents",
tag="batch embedding",
)
embeddings.extend(
APIBackend().create_embedding(input_content=contents[i : i + size]),
)
@@ -270,7 +276,7 @@ class UndirectedGraph(Graph):
self,
node: UndirectedNode | str,
similarity_threshold: float = 0.0,
topk_k: int = 5,
topk_k: int = None,
constraint_labels: list[str] | None = None,
) -> list[UndirectedNode]:
"""
@@ -87,7 +87,7 @@ class VectorBase(KnowledgeBase):
"""
pass
def search(self, content: str, topk_k: int = 5, similarity_threshold: float = 0) -> List[Document]:
def search(self, content: str, topk_k: int | None = None, similarity_threshold: float = 0) -> List[Document]:
"""
search vector_df by node
Parameters
@@ -156,7 +156,11 @@ class PDVectorBase(VectorBase):
self.add(document=doc)
def search(
self, content: str, topk_k: int = 5, similarity_threshold: float = 0, constraint_labels: list[str] | None = None
self,
content: str,
topk_k: int | None = None,
similarity_threshold: float = 0,
constraint_labels: list[str] | None = None,
) -> Tuple[List[Document], List]:
"""
Search vector by node's embedding.
@@ -192,7 +196,9 @@ class PDVectorBase(VectorBase):
lambda x: 1 - cosine(x, document.embedding)
) # cosine is cosine distance, 1-similarity
searched_similarities = similarities[similarities > similarity_threshold].nlargest(topk_k)
searched_similarities = similarities[similarities > similarity_threshold]
if topk_k is not None:
searched_similarities = searched_similarities.nlargest(topk_k)
most_similar_docs = filtered_df.loc[searched_similarities.index]
docs = []
+46 -43
View File
@@ -1,11 +1,7 @@
from abc import abstractmethod
from pathlib import Path
from typing import Tuple
from jinja2 import Environment, StrictUndefined
from rdagent.core.experiment import Experiment
from rdagent.core.prompts import Prompts
from rdagent.core.proposal import (
Hypothesis,
Hypothesis2Experiment,
@@ -14,8 +10,8 @@ from rdagent.core.proposal import (
Trace,
)
from rdagent.oai.llm_utils import APIBackend
prompt_dict = Prompts(file_path=Path(__file__).parent / "prompts.yaml")
from rdagent.utils.agent.tpl import T
from rdagent.utils.workflow import wait_retry
class LLMHypothesisGen(HypothesisGen):
@@ -32,27 +28,31 @@ class LLMHypothesisGen(HypothesisGen):
def gen(self, trace: Trace) -> Hypothesis:
context_dict, json_flag = self.prepare_context(trace)
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(prompt_dict["hypothesis_gen"]["system_prompt"])
.render(
targets=self.targets,
scenario=self.scen.get_scenario_all_desc(filtered_tag="hypothesis_and_experiment"),
hypothesis_output_format=context_dict["hypothesis_output_format"],
hypothesis_specification=context_dict["hypothesis_specification"],
)
system_prompt = T(".prompts:hypothesis_gen.system_prompt").r(
targets=self.targets,
scenario=(
self.scen.get_scenario_all_desc(filtered_tag=self.targets)
if self.targets in ["factor", "model"]
else self.scen.get_scenario_all_desc(filtered_tag="hypothesis_and_experiment")
),
hypothesis_output_format=context_dict["hypothesis_output_format"],
hypothesis_specification=context_dict["hypothesis_specification"],
)
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(prompt_dict["hypothesis_gen"]["user_prompt"])
.render(
targets=self.targets,
hypothesis_and_feedback=context_dict["hypothesis_and_feedback"],
RAG=context_dict["RAG"],
)
user_prompt = T(".prompts:hypothesis_gen.user_prompt").r(
targets=self.targets,
hypothesis_and_feedback=context_dict["hypothesis_and_feedback"],
last_hypothesis_and_feedback=(
context_dict["last_hypothesis_and_feedback"] if "last_hypothesis_and_feedback" in context_dict else ""
),
sota_hypothesis_and_feedback=(
context_dict["sota_hypothesis_and_feedback"] if "sota_hypothesis_and_feedback" in context_dict else ""
),
RAG=context_dict["RAG"],
)
resp = APIBackend().build_messages_and_create_chat_completion(user_prompt, system_prompt, json_mode=json_flag)
resp = APIBackend().build_messages_and_create_chat_completion(
user_prompt, system_prompt, json_mode=json_flag, json_target_type=dict[str, str]
)
hypothesis = self.convert_response(resp)
@@ -84,30 +84,33 @@ class LLMHypothesis2Experiment(Hypothesis2Experiment[Experiment]):
@abstractmethod
def convert_response(self, response: str, hypothesis: Hypothesis, trace: Trace) -> Experiment: ...
@wait_retry(retry_n=5)
def convert(self, hypothesis: Hypothesis, trace: Trace) -> Experiment:
context, json_flag = self.prepare_context(hypothesis, trace)
system_prompt = (
Environment(undefined=StrictUndefined)
.from_string(prompt_dict["hypothesis2experiment"]["system_prompt"])
.render(
targets=self.targets,
scenario=trace.scen.get_scenario_all_desc(filtered_tag="hypothesis_and_experiment"),
experiment_output_format=context["experiment_output_format"],
)
system_prompt = T(".prompts:hypothesis2experiment.system_prompt").r(
targets=self.targets,
scenario=trace.scen.get_scenario_all_desc(filtered_tag=self.targets),
experiment_output_format=context["experiment_output_format"],
)
user_prompt = (
Environment(undefined=StrictUndefined)
.from_string(prompt_dict["hypothesis2experiment"]["user_prompt"])
.render(
targets=self.targets,
target_hypothesis=context["target_hypothesis"],
hypothesis_and_feedback=context["hypothesis_and_feedback"],
target_list=context["target_list"],
RAG=context["RAG"],
)
user_prompt = T(".prompts:hypothesis2experiment.user_prompt").r(
targets=self.targets,
target_hypothesis=context["target_hypothesis"],
hypothesis_and_feedback=(
context["hypothesis_and_feedback"] if "hypothesis_and_feedback" in context else ""
),
last_hypothesis_and_feedback=(
context["last_hypothesis_and_feedback"] if "last_hypothesis_and_feedback" in context else ""
),
sota_hypothesis_and_feedback=(
context["sota_hypothesis_and_feedback"] if "sota_hypothesis_and_feedback" in context else ""
),
target_list=context["target_list"],
RAG=context["RAG"],
)
resp = APIBackend().build_messages_and_create_chat_completion(user_prompt, system_prompt, json_mode=json_flag)
resp = APIBackend().build_messages_and_create_chat_completion(
user_prompt, system_prompt, json_mode=json_flag, json_target_type=dict[str, dict[str, str | dict]]
)
return self.convert_response(resp, hypothesis, trace)
+38 -22
View File
@@ -1,48 +1,64 @@
hypothesis_gen:
system_prompt: |-
The user is working on generating new hypotheses for the {{targets}} in a data-driven research and development process.
The {{targets}} are used in the following scenario:
{{scenario}}
The user has already proposed several hypotheses and conducted evaluations on them. This information will be provided to you. Your task is to check whether a similar hypothesis has already been generated.
The user is working on generating new hypotheses for the {{ targets }} in a data-driven research and development process.
The {{ targets }} are used in the following scenario:
{{ scenario }}
The user has already proposed several hypotheses and conducted evaluations on them. This information will be provided to you. Your task is to analyze previous experiments, reflect on the decision made in each experiment, and consider why experiments with a decision of true were successful while those with a decision of false failed. Then, think about how to improve further — either by refining the existing approach or by exploring an entirely new direction.
If one exists and you agree with it, feel free to use it. If you disagree, please generate an improved version.
{% if hypothesis_specification %}
To assist you in formulating new hypotheses, the user has provided some additional information: {{hypothesis_specification}}.
To assist you in formulating new hypotheses, the user has provided some additional information: {{ hypothesis_specification }}.
**Important:** If the hypothesis_specification outlines the next steps you need to follow, ensure you adhere to those instructions.
{% endif %}
Please generate the output using the following format and specifications:
{{ hypothesis_output_format }}
user_prompt: |-
{% if hypothesis_and_feedback|length == 0 %}It is the first round of hypothesis generation. The user has no hypothesis on this scenario yet.
{% else %}It is not the first round, the user has made several hypothesis on this scenario and did several evaluation on them.
The former hypothesis and the corresponding feedbacks are as follows (focus on the last one & the new hypothesis that it provides and reasoning to see if you agree):
{% if hypothesis_and_feedback|length == 0 %}
It is the first round of hypothesis generation. The user has no hypothesis on this scenario yet.
{% else %}
The former hypothesis and the corresponding feedbacks are as follows:
{{ hypothesis_and_feedback }}
{% endif %}
{% if RAG %}
To assist you in generating new {{targets}}, we have provided the following information: {{RAG}}.
**Note:** The provided RAG is for reference only.
You must carefully assess whether the RAG aligns with the {{targets}}.
If it does not, it should not be used. Exercise caution and make your own judgment.
{% if last_hypothesis_and_feedback %}
Here is the last trial's hypothesis and the corresponding feedback (The main feedback contains a new hypothesis for your reference only. You need to evaluate the complete trace chain to decide whether to adopt it or propose a more appropriate hypothesis):
{{ last_hypothesis_and_feedback }}
{% endif %}
{% if sota_hypothesis_and_feedback != "" %}
Here is the SOTA trail's hypothesis and the corresponding feedback:
{{ sota_hypothesis_and_feedback }}
{% endif %}
{% if RAG %}
To assist you in generating new {{ targets }}, we have provided the following information: {{ RAG }}.
{% endif %}
Also generate the relevant keys for the reasoning and the distilled knowledge that follows. For those keys, in particular for knowledge, explain in the context of the specific scenario to build up domain knowledge in the specific field rather than general knowledge.
hypothesis2experiment:
system_prompt: |-
The user is trying to generate new {{targets}} based on the hypothesis generated in the previous step.
The {{targets}} are used in certain scenario, the scenario is as follows:
The user is trying to generate new {{ targets }} based on the hypothesis generated in the previous step.
The {{ targets }} are used in certain scenario, the scenario is as follows:
{{ scenario }}
The user will use the {{targets}} generated to do some experiments. The user will provide this information to you:
1. The target hypothesis you are targeting to generate {{targets}} for.
The user will use the {{ targets }} generated to do some experiments. The user will provide this information to you:
1. The target hypothesis you are targeting to generate {{ targets }} for.
2. The hypothesis generated in the previous steps and their corresponding feedbacks.
3. Former proposed {{targets}} on similar hypothesis.
4. Some additional information to help you generate new {{targets}}.
3. Former proposed {{ targets }} on similar hypothesis.
4. Some additional information to help you generate new {{ targets }}.
Please generate the output following the format below:
{{ experiment_output_format }}
user_prompt: |-
The user has made several hypothesis on this scenario and did several evaluation on them.
The target hypothesis you are targeting to generate {{targets}} for is as follows:
The target hypothesis you are targeting to generate {{ targets }} for is as follows:
{{ target_hypothesis }}
{% if hypothesis_and_feedback %}
The former hypothesis and the corresponding feedbacks are as follows:
{{ hypothesis_and_feedback }}
Please generate the new {{targets}} based on the information above.
{% endif %}
{% if last_hypothesis_and_feedback %}
The latest hypothesis and the corresponding feedback are as follows:
{{ last_hypothesis_and_feedback }}
{% endif %}
{% if sota_hypothesis_and_feedback %}
The SOTA hypothesis and the corresponding feedback are as follows:
{{ sota_hypothesis_and_feedback }}
{% endif %}
Please generate the new {{ targets }} based on the information above.
+27 -30
View File
@@ -3,9 +3,11 @@ Model workflow with session control
It is from `rdagent/app/qlib_rd_loop/model.py` and try to replace `rdagent/app/qlib_rd_loop/RDAgent.py`
"""
import asyncio
from typing import Any
from rdagent.components.workflow.conf import BasePropSetting
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.developer import Developer
from rdagent.core.proposal import (
Experiment2Feedback,
@@ -24,25 +26,20 @@ from rdagent.utils.workflow import LoopBase, LoopMeta
class RDLoop(LoopBase, metaclass=LoopMeta):
def __init__(self, PROP_SETTING: BasePropSetting):
with logger.tag("init"):
scen: Scenario = import_class(PROP_SETTING.scen)()
logger.log_object(scen, tag="scenario")
scen: Scenario = import_class(PROP_SETTING.scen)()
logger.log_object(scen, tag="scenario")
logger.log_object(PROP_SETTING.model_dump(), tag="RDLOOP_SETTINGS")
logger.log_object(RD_AGENT_SETTINGS.model_dump(), tag="RD_AGENT_SETTINGS")
self.hypothesis_gen: HypothesisGen = import_class(PROP_SETTING.hypothesis_gen)(scen)
self.hypothesis_gen: HypothesisGen = import_class(PROP_SETTING.hypothesis_gen)(scen)
logger.log_object(self.hypothesis_gen, tag="hypothesis generator")
self.hypothesis2experiment: Hypothesis2Experiment = import_class(PROP_SETTING.hypothesis2experiment)()
self.hypothesis2experiment: Hypothesis2Experiment = import_class(PROP_SETTING.hypothesis2experiment)()
logger.log_object(self.hypothesis2experiment, tag="hypothesis2experiment")
self.coder: Developer = import_class(PROP_SETTING.coder)(scen)
self.runner: Developer = import_class(PROP_SETTING.runner)(scen)
self.coder: Developer = import_class(PROP_SETTING.coder)(scen)
logger.log_object(self.coder, tag="coder")
self.runner: Developer = import_class(PROP_SETTING.runner)(scen)
logger.log_object(self.runner, tag="runner")
self.summarizer: Experiment2Feedback = import_class(PROP_SETTING.summarizer)(scen)
logger.log_object(self.summarizer, tag="summarizer")
self.trace = Trace(scen=scen)
super().__init__()
self.summarizer: Experiment2Feedback = import_class(PROP_SETTING.summarizer)(scen)
self.trace = Trace(scen=scen)
super().__init__()
# excluded steps
def _propose(self):
@@ -56,37 +53,37 @@ class RDLoop(LoopBase, metaclass=LoopMeta):
return exp
# included steps
def direct_exp_gen(self, prev_out: dict[str, Any]):
with logger.tag("r"): # research
hypo = self._propose()
exp = self._exp_gen(hypo)
return {"propose": hypo, "exp_gen": exp}
async def direct_exp_gen(self, prev_out: dict[str, Any]):
while True:
if self.get_unfinished_loop_cnt(self.loop_idx) < RD_AGENT_SETTINGS.get_max_parallel():
hypo = self._propose()
exp = self._exp_gen(hypo)
return {"propose": hypo, "exp_gen": exp}
await asyncio.sleep(1)
def coding(self, prev_out: dict[str, Any]):
with logger.tag("d"): # develop
exp = self.coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
logger.log_object(exp.sub_workspace_list, tag="coder result")
exp = self.coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
logger.log_object(exp.sub_workspace_list, tag="coder result")
return exp
def running(self, prev_out: dict[str, Any]):
with logger.tag("ef"): # evaluate and feedback
exp = self.runner.develop(prev_out["coding"])
logger.log_object(exp, tag="runner result")
exp = self.runner.develop(prev_out["coding"])
logger.log_object(exp, tag="runner result")
return exp
def feedback(self, prev_out: dict[str, Any]):
e = prev_out.get(self.EXCEPTION_KEY, None)
if e is not None:
feedback = HypothesisFeedback(
observations="Error occurred in loop, skip this loop",
observations=str(e),
hypothesis_evaluation="",
new_hypothesis="",
reason="",
decision=False,
)
logger.log_object(feedback, tag="feedback")
self.trace.hist.append((prev_out["direct_exp_gen"]["exp_gen"], feedback))
else:
feedback = self.summarizer.generate_feedback(prev_out["running"], self.trace)
with logger.tag("ef"): # evaluate and feedback
logger.log_object(feedback, tag="feedback")
logger.log_object(feedback, tag="feedback")
self.trace.hist.append((prev_out["running"], feedback))
+23 -7
View File
@@ -1,6 +1,5 @@
from __future__ import annotations
# TODO: use pydantic for other modules in Qlib
from pathlib import Path
from typing import cast
@@ -39,17 +38,12 @@ class ExtendedBaseSettings(BaseSettings):
env_prefix=base_cls.model_config.get("env_prefix"),
env_nested_delimiter=base_cls.model_config.get("env_nested_delimiter"),
)
for base_cls in base_iter(cast(type[ExtendedBaseSettings], settings_cls))
for base_cls in base_iter(cast("type[ExtendedBaseSettings]", settings_cls))
]
return init_settings, env_settings, *parent_env_settings, dotenv_settings, file_secret_settings
class RDAgentSettings(ExtendedBaseSettings):
# TODO: (xiao) I think LLMSetting may be a better name.
# TODO: (xiao) I think most of the config should be in oai.config
# Log configs
# TODO: (xiao) think it can be a separate config.
log_trace_path: str | None = None
# azure document intelligence configs
azure_document_intelligence_key: str = ""
@@ -80,5 +74,27 @@ class RDAgentSettings(ExtendedBaseSettings):
stdout_context_len: int = 400
stdout_line_len: int = 10000
enable_mlflow: bool = False
initial_fator_library_size: int = 20
# parallel loop
step_semaphore: int | dict[str, int] = 1
"""the semaphore for each step; you can specify a overall semaphore
or a step-wise semaphore like {"coding": 3, "running": 2}"""
def get_max_parallel(self) -> int:
"""Based on the setting of semaphore, return the maximum number of parallel loops"""
if isinstance(self.step_semaphore, int):
return self.step_semaphore
return max(self.step_semaphore.values())
# NOTE: for debug
# the following function only serves as debugging and is necessary in main logic.
subproc_step: bool = False
def is_force_subproc(self) -> bool:
return self.subproc_step or self.get_max_parallel() > 1
RD_AGENT_SETTINGS = RDAgentSettings()
+6
View File
@@ -57,3 +57,9 @@ class KaggleError(Exception):
"""
Exceptions raised when calling Kaggle API
"""
class PolicyError(Exception):
"""
Exceptions raised due to content management policy
"""
+36 -14
View File
@@ -9,12 +9,17 @@ import uuid
from abc import ABC, abstractmethod
from collections.abc import Sequence
from copy import deepcopy
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Generic, Literal, TypeVar
from typing import TYPE_CHECKING, Any, Generic, TypeVar
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.evaluation import Feedback
from rdagent.utils import filter_redundant_text
if TYPE_CHECKING:
from rdagent.utils.env import EnvResult
from rdagent.utils.fmt import shrink_text
if typing.TYPE_CHECKING:
@@ -59,6 +64,12 @@ ASpecificTask = TypeVar("ASpecificTask", bound=Task)
ASpecificFeedback = TypeVar("ASpecificFeedback", bound=Feedback)
@dataclass
class RunningInfo:
result: object = None # The result of the experiment, can be different types in different scenarios.
running_time: float | None = None
class Workspace(ABC, Generic[ASpecificTask, ASpecificFeedback]):
"""
A workspace is a place to store the task implementation. It evolves as the developer implements the task.
@@ -68,6 +79,7 @@ class Workspace(ABC, Generic[ASpecificTask, ASpecificFeedback]):
def __init__(self, target_task: ASpecificTask | None = None) -> None:
self.target_task: ASpecificTask | None = target_task
self.feedback: ASpecificFeedback | None = None
self.running_info: RunningInfo = RunningInfo()
@abstractmethod
def execute(self, *args: Any, **kwargs: Any) -> object | None:
@@ -250,26 +262,25 @@ class FBWorkspace(Workspace):
"""
Before each execution, make sure to prepare and inject code.
"""
stdout, _ = self.execute_ret_code(env, entry)
return stdout
result = self.run(env, entry)
return result.stdout
def execute_ret_code(self, env: Env, entry: str) -> tuple[str, int]:
def run(self, env: Env, entry: str) -> EnvResult:
"""
Execute the code in the environment and return both the stdout and the exit code.
Execute the code in the environment and return an EnvResult object (stdout, exit_code, running_time).
Before each execution, make sure to prepare and inject code.
"""
self.prepare()
self.inject_files(**self.file_dict)
stdout, return_code = env.run_ret_code(entry, str(self.workspace_path))
return (
shrink_text(
filter_redundant_text(stdout),
context_lines=RD_AGENT_SETTINGS.stdout_context_len,
line_len=RD_AGENT_SETTINGS.stdout_line_len,
),
return_code,
result = env.run(entry, str(self.workspace_path), env={"PYTHONPATH": "./"})
# result is EnvResult
result.stdout = shrink_text(
filter_redundant_text(result.stdout),
context_lines=RD_AGENT_SETTINGS.stdout_context_len,
line_len=RD_AGENT_SETTINGS.stdout_line_len,
)
return result
def __str__(self) -> str:
return f"Workspace[{self.workspace_path=}" + (
@@ -319,11 +330,22 @@ class Experiment(
# NOTE: Assumption
# - only runner will assign this variable
# - We will always create a new Experiment without copying previous results when we goto the next new loop.
self.result: object = None # The result of the experiment, can be different types in different scenarios.
self.running_info = RunningInfo()
self.sub_results: dict[str, float] = (
{}
) # TODO: in Kaggle, now sub results are all saved in self.result, remove this in the future.
# For parallel multi-trace support
self.local_selection: tuple[int, ...] | None = None
@property
def result(self) -> object:
return self.running_info.result
@result.setter
def result(self, value: object) -> None:
self.running_info.result = value
ASpecificExp = TypeVar("ASpecificExp", bound=Experiment)
+134 -18
View File
@@ -1,19 +1,19 @@
""" """
# TODO: remove `self.scen` if traces will be passed into the instance.
from __future__ import annotations
import asyncio
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Generic, TypeVar
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.evaluation import Feedback
from rdagent.core.experiment import ASpecificExp, Experiment
from rdagent.core.knowledge_base import KnowledgeBase
from rdagent.core.scenario import Scenario
if TYPE_CHECKING:
from rdagent.core.prompts import Prompts
# class data_ana: XXX
from rdagent.utils.workflow.loop import LoopBase
class Hypothesis:
@@ -42,12 +42,7 @@ class Hypothesis:
def __str__(self) -> str:
return f"""Hypothesis: {self.hypothesis}
Reason: {self.reason}
Concise Reason & Knowledge: {self.concise_reason}
Concise Observation: {self.concise_observation}
Concise Justification: {self.concise_justification}
Concise Knowledge: {self.concise_knowledge}
"""
Reason: {self.reason}"""
# source: data_ana | model_nan = None
@@ -60,6 +55,7 @@ class ExperimentFeedback(Feedback):
self,
reason: str,
*,
code_change_summary: str | None = None,
decision: bool,
exception: Exception | None = None,
) -> None:
@@ -70,12 +66,17 @@ class ExperimentFeedback(Feedback):
self.exception: Exception | None = (
exception # if the experiment raises exception, it will be integrated into part of the feedback.
)
self.code_change_summary = code_change_summary
def __bool__(self) -> bool:
return self.decision
def __str__(self) -> str:
return f"Decision: {self.decision}\nReason: {self.reason}"
res = f"Decision: {self.decision}\nReason: {self.reason}"
code_change_summary = getattr(self, "code_change_summary", None)
if code_change_summary is not None:
res += "\nCode Change Summary: " + code_change_summary
return res
@classmethod
def from_exception(cls, e: Exception) -> ExperimentFeedback:
@@ -93,9 +94,10 @@ class HypothesisFeedback(ExperimentFeedback):
new_hypothesis: str,
reason: str,
*,
code_change_summary: str | None = None,
decision: bool,
) -> None:
super().__init__(reason, decision=decision)
super().__init__(reason, decision=decision, code_change_summary=code_change_summary)
self.observations = observations
self.hypothesis_evaluation = hypothesis_evaluation
self.new_hypothesis = new_hypothesis
@@ -112,11 +114,20 @@ ASpecificKB = TypeVar("ASpecificKB", bound=KnowledgeBase)
class Trace(Generic[ASpecificScen, ASpecificKB]):
NodeType = tuple[Experiment, ExperimentFeedback] # Define NodeType as a new type representing the tuple
NEW_ROOT: tuple = ()
def __init__(self, scen: ASpecificScen, knowledge_base: ASpecificKB | None = None) -> None:
self.scen: ASpecificScen = scen
self.hist: list[tuple[Experiment, ExperimentFeedback]] = []
self.hist: list[Trace.NodeType] = (
[]
) # List of tuples containing experiments and their feedback, organized over time.
self.dag_parent: list[tuple[int, ...]] = [] # List of tuples representing parent indices in the DAG structure.
# (,) represents no parent; (1,) presents one parent; (1, 2) represents two parents.
# TODO: self.hist is 2-tuple now, remove hypothesis from it, change old code for this later.
self.knowledge_base: ASpecificKB | None = knowledge_base
self.current_selection: tuple[int, ...] = (-1,)
def get_sota_hypothesis_and_experiment(self) -> tuple[Hypothesis | None, Experiment | None]:
"""Access the last experiment result, sub-task, and the corresponding hypothesis."""
@@ -127,6 +138,105 @@ class Trace(Generic[ASpecificScen, ASpecificKB]):
return None, None
def is_selection_new_tree(self, selection: tuple[int, ...] | None = None) -> bool:
"""
Check if the current trace is a new tree.
- selection maybe (-1,) when the dag_parent is empty.
"""
if selection is None:
selection = self.get_current_selection()
return selection == self.NEW_ROOT or len(self.dag_parent) == 0
def get_current_selection(self) -> tuple[int, ...]:
return self.current_selection
def set_current_selection(self, selection: tuple[int, ...]) -> None:
self.current_selection = selection
def get_parent_exps(
self,
selection: tuple[int, ...] | None = None,
) -> list[Trace.NodeType]:
"""
Collect all ancestors of the given selection.
The return list follows the order of [root->...->parent->current_node].
"""
if selection is None:
selection = self.get_current_selection()
if self.is_selection_new_tree(selection):
return []
return [self.hist[i] for i in self.get_parents(selection[0])]
def exp2idx(self, exp: Experiment | list[Experiment]) -> int | list[int] | None:
if isinstance(exp, list):
exps: list[Experiment] = exp
# keep the order
exp_to_index: dict[Experiment, int] = {_exp: i for i, (_exp, _) in enumerate(self.hist)}
return [exp_to_index[_exp] for _exp in exps]
for i, (_exp, _) in enumerate(self.hist):
if _exp == exp:
return i
return None
def idx2exp(self, idx: int | list[int]) -> Experiment | list[Experiment]:
if isinstance(idx, list):
idxs: list[int] = idx
return [self.hist[_idx][0] for _idx in idxs]
return self.hist[idx][0]
def is_parent(self, parent_idx: int, child_idx: int) -> bool:
ancestors = self.get_parents(child_idx)
return parent_idx in ancestors
def get_parents(self, child_idx: int) -> list[int]:
if self.is_selection_new_tree((child_idx,)):
return []
ancestors: list[int] = []
curr = child_idx
while True:
ancestors.insert(0, curr)
parent_tuple = self.dag_parent[curr]
if not parent_tuple or parent_tuple[0] == curr:
break
curr = parent_tuple[0]
return ancestors
class CheckpointSelector:
"""
In the trace, we may start from any check point (we'll represent it as a variable `from_checkpoint_idx`)
"""
@abstractmethod
def get_selection(self, trace: Trace) -> tuple[int, ...] | None:
"""
checkpoint_idx represents the place where we want to create a new node.
the return value should be the idx of target node (the parent of the new generating node).
- `(-1, )` represents starting from the latest trial in the trace - default value
- `(idx, )` represents starting from the `idx`-th trial in the trace.
- `None` represents starting from scratch (start a new trace)
- More advanced selection strategies in `select.py`
"""
class SOTAexpSelector:
"""
Select the SOTA experiment from the trace to submit
"""
@abstractmethod
def get_sota_exp_to_submit(self, trace: Trace) -> Experiment | None:
"""
Select the SOTA experiment from the trace to submit
"""
class ExpGen(ABC):
@@ -148,13 +258,19 @@ class ExpGen(ABC):
)
"""
async def async_gen(self, trace: Trace, loop: LoopBase) -> Experiment:
"""
generate the experiment and decide whether to stop yield generation and give up control to other routines.
"""
# we give a default implementation here.
# The proposal is set to try best to generate the experiment in max-parallel level.
while True:
if loop.get_unfinished_loop_cnt(loop.loop_idx) < RD_AGENT_SETTINGS.get_max_parallel():
return self.gen(trace)
await asyncio.sleep(1)
class HypothesisGen(ABC):
# NOTE: the design is a little wierd
# - Sometimes we want accurate access the prompts in a specific level
# - It renders the prompt to a specific abstract level
# - Sometimes we want to access the most recent level prompts
prompts: Prompts # this is a class level prompt.
def __init__(self, scen: Scenario) -> None:
self.scen = scen
+1 -1
View File
@@ -69,7 +69,7 @@ def similarity(text1: str, text2: str) -> int:
text2 = text2 if isinstance(text2, str) else ""
# Maybe we can use other similarity algorithm such as tfidf
return cast(int, fuzz.ratio(text1, text2)) # mypy does not regard it as int
return cast("int", fuzz.ratio(text1, text2)) # mypy does not regard it as int
def import_class(class_path: str) -> Any:
+14 -9
View File
@@ -5,7 +5,7 @@ from collections.abc import Generator
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Literal, Optional, Union
from typing import Literal, Optional
@dataclass
@@ -43,10 +43,8 @@ class Storage:
def log(
self,
obj: object,
name: str = "",
save_type: Literal["json", "text", "pkl"] = "text",
tag: str = "",
timestamp: datetime | None = None,
**kwargs: dict,
) -> str | Path:
"""
@@ -66,15 +64,22 @@ class Storage:
...
@abstractmethod
def iter_msg(self, watch: bool = False) -> Generator[Message, None, None]:
def iter_msg(self) -> Generator[Message, None, None]:
"""
Parameters
----------
watch : bool
should we watch the new content and display them
Iterate the message in the storage.
"""
...
@abstractmethod
def truncate(self, time: datetime) -> None:
"""
Remove all log entries after the specified time.
"""
...
def __str__(self) -> str:
return self.__class__.__name__
class View:
"""
+27
View File
@@ -0,0 +1,27 @@
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from pydantic_settings import SettingsConfigDict
from rdagent.core.conf import ExtendedBaseSettings
class LogSettings(ExtendedBaseSettings):
model_config = SettingsConfigDict(env_prefix="LOG_", protected_namespaces=())
trace_path: str = str(Path.cwd() / "log" / datetime.now(timezone.utc).strftime("%Y-%m-%d_%H-%M-%S-%f"))
format_console: str | None = None
""""If it is None, leave it as the default"""
ui_server_port: int | None = None
storages: dict[str, list[int | str]] = {}
def model_post_init(self, _context: Any, /) -> None:
if self.ui_server_port is not None:
self.storages["rdagent.log.ui.storage.WebStorage"] = [self.ui_server_port, self.trace_path]
LOG_SETTINGS = LogSettings()
+54 -101
View File
@@ -1,28 +1,26 @@
import json
import os
import pickle
import sys
from contextlib import contextmanager
from datetime import datetime, timezone
from functools import partial
from logging import LogRecord
from multiprocessing import Pipe
from multiprocessing.connection import Connection
from contextvars import ContextVar
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, Generator, Union
from typing import Generator
from loguru import logger
if TYPE_CHECKING:
from loguru import Record
from .conf import LOG_SETTINGS
if LOG_SETTINGS.format_console is not None:
logger.remove()
logger.add(sys.stdout, format=LOG_SETTINGS.format_console)
from psutil import Process
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.utils import SingletonBaseClass
from rdagent.core.utils import SingletonBaseClass, import_class
from .base import Storage
from .storage import FileStorage
from .utils import LogColors, get_caller_info
from .utils import get_caller_info
class RDAgentLog(SingletonBaseClass):
@@ -48,45 +46,49 @@ class RDAgentLog(SingletonBaseClass):
"""
# TODO: Simplify it to introduce less concepts ( We may merge RDAgentLog, Storage &)
# Solution: Storage => PipeLog, View => PipeLogView, RDAgentLog is an instance of PipeLogger
# PipeLogger.info(...) , PipeLogger.get_resp() to get feedback from frontend.
# def f():
# logger = PipeLog()
# logger.info("<code>")
# feedback = logger.get_reps()
_tag: str = ""
# Thread-/coroutine-local tag; In Linux forked subprocess, it will be copied to the subprocess.
_tag_ctx: ContextVar[str] = ContextVar("_tag_ctx", default="")
def __init__(self, log_trace_path: Union[str, None] = RD_AGENT_SETTINGS.log_trace_path) -> None:
if log_trace_path is None:
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d_%H-%M-%S-%f")
self.log_trace_path = Path.cwd() / "log" / timestamp
else:
self.log_trace_path = Path(log_trace_path)
@property
def _tag(self) -> str: # Get current tag
return self._tag_ctx.get()
self.log_trace_path.mkdir(parents=True, exist_ok=True)
@_tag.setter # Set current tag
def _tag(self, value: str) -> None:
self._tag_ctx.set(value)
self.storage = FileStorage(self.log_trace_path)
def __init__(self) -> None:
self.storage = FileStorage(LOG_SETTINGS.trace_path)
self.other_storages: list[Storage] = []
for storage, args in LOG_SETTINGS.storages.items():
storage_cls = import_class(storage)
self.other_storages.append(storage_cls(*args))
self.main_pid = os.getpid()
def set_trace_path(self, log_trace_path: str | Path) -> None:
self.log_trace_path = Path(log_trace_path)
self.storage = FileStorage(log_trace_path)
@contextmanager
def tag(self, tag: str) -> Generator[None, None, None]:
if tag.strip() == "":
raise ValueError("Tag cannot be empty.")
if self._tag != "":
tag = "." + tag
# TODO: It may result in error in mutithreading or co-routine
self._tag = self._tag + tag
# Generate a new complete tag
current_tag = self._tag_ctx.get()
new_tag = tag if current_tag == "" else f"{current_tag}.{tag}"
# Set and save token for later restore
token = self._tag_ctx.set(new_tag)
try:
yield
finally:
self._tag = self._tag[: -len(tag)]
# Restore previous tag (thread/coroutine safe)
self._tag_ctx.reset(token)
def set_storages_path(self, path: str | Path) -> None:
for storage in [self.storage] + self.other_storages:
if hasattr(storage, "path"):
storage.path = path
def truncate_storages(self, time: datetime) -> None:
for storage in [self.storage] + self.other_storages:
storage.truncate(time=time)
def get_pids(self) -> str:
"""
@@ -103,81 +105,32 @@ class RDAgentLog(SingletonBaseClass):
process = parent_process
return pid_chain
def file_format(self, record: "Record", raw: bool = False) -> str:
# FIXME: the formmat is tightly coupled with the message reading in storage.
record["message"] = LogColors.remove_ansi_codes(record["message"])
if raw:
return "{message}"
return "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}\n"
def log_object(self, obj: object, *, tag: str = "") -> None:
# TODO: I think we can merge the log_object function with other normal log methods to make the interface simpler.
caller_info = get_caller_info()
tag = f"{self._tag}.{tag}.{self.get_pids()}".strip(".")
# FIXME: it looks like a hacking... We should redesign it...
if "debug_" in tag:
debug_log_path = self.log_trace_path / "debug_llm.pkl"
debug_data = {"tag": tag, "obj": obj}
if debug_log_path.exists():
with debug_log_path.open("rb") as f:
existing_data = pickle.load(f)
existing_data.append(debug_data)
with debug_log_path.open("wb") as f:
pickle.dump(existing_data, f)
else:
with debug_log_path.open("wb") as f:
pickle.dump([debug_data], f)
return
for storage in [self.storage] + self.other_storages:
storage.log(obj, tag=tag)
logp = self.storage.log(obj, name=tag, save_type="pkl")
def _log(self, level: str, msg: str, *, tag: str = "", raw: bool = False) -> None:
caller_info = get_caller_info(level=3)
tag = f"{self._tag}.{tag}.{self.get_pids()}".strip(".")
file_handler_id = logger.add(
self.log_trace_path / tag.replace(".", "/") / "common_logs.log", format=self.file_format
)
logger.patch(lambda r: r.update(caller_info)).info(f"Logging object in {Path(logp).absolute()}")
logger.remove(file_handler_id)
def info(self, msg: str, *, tag: str = "", raw: bool = False) -> None:
# TODO: too much duplicated. due to we have no logger with stream context;
caller_info = get_caller_info()
if raw:
logger.remove()
logger.add(sys.stderr, format=lambda r: "{message}")
tag = f"{self._tag}.{tag}.{self.get_pids()}".strip(".")
log_file_path = self.log_trace_path / tag.replace(".", "/") / "common_logs.log"
if raw:
file_handler_id = logger.add(log_file_path, format=partial(self.file_format, raw=True))
else:
file_handler_id = logger.add(log_file_path, format=self.file_format)
logger.patch(lambda r: r.update(caller_info)).info(msg)
logger.remove(file_handler_id)
log_func = getattr(logger.patch(lambda r: r.update(caller_info)), level)
log_func(msg)
if raw:
logger.remove()
logger.add(sys.stderr)
def warning(self, msg: str, *, tag: str = "") -> None:
# TODO: reuse code
# _log(self, msg: str, *, tag: str = "", level=Literal["warning", "error", ..]) -> None:
# getattr(logger.patch(lambda r: r.update(caller_info)), level)(msg)
caller_info = get_caller_info()
def info(self, msg: str, *, tag: str = "", raw: bool = False) -> None:
self._log("info", msg, tag=tag, raw=raw)
tag = f"{self._tag}.{tag}.{self.get_pids()}".strip(".")
file_handler_id = logger.add(
self.log_trace_path / tag.replace(".", "/") / "common_logs.log", format=self.file_format
)
logger.patch(lambda r: r.update(caller_info)).warning(msg)
logger.remove(file_handler_id)
def warning(self, msg: str, *, tag: str = "", raw: bool = False) -> None:
self._log("warning", msg, tag=tag, raw=raw)
def error(self, msg: str, *, tag: str = "") -> None:
caller_info = get_caller_info()
tag = f"{self._tag}.{tag}.{self.get_pids()}".strip(".")
file_handler_id = logger.add(
self.log_trace_path / tag.replace(".", "/") / "common_logs.log", format=self.file_format
)
logger.patch(lambda r: r.update(caller_info)).error(msg)
logger.remove(file_handler_id)
def error(self, msg: str, *, tag: str = "", raw: bool = False) -> None:
self._log("error", msg, tag=tag, raw=raw)
+97 -68
View File
@@ -1,36 +1,28 @@
import json
import re
import pickle
import traceback
from collections import defaultdict
from pathlib import Path
import fire
import pandas as pd
from rdagent.app.data_science.conf import DS_RD_SETTING
from rdagent.components.coder.data_science.conf import get_ds_env
from rdagent.core.experiment import FBWorkspace
from rdagent.core.proposal import ExperimentFeedback
from rdagent.log.storage import FileStorage
from rdagent.log.utils import extract_json, extract_loopid_func_name, is_valid_session
from rdagent.log.utils.folder import get_first_session_file_after_duration
from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
from rdagent.scenarios.data_science.test_eval import (
MLETestEval,
NoTestEvalError,
get_test_eval,
)
from rdagent.scenarios.kaggle.kaggle_crawler import score_rank
from rdagent.utils.env import DockerEnv, MLEBDockerConf
from rdagent.utils.workflow import LoopBase
de = get_ds_env("mlebench")
de.conf.extra_volumes = {f"{DS_RD_SETTING.local_data_path}/zip_files": "/mle/data"}
de.prepare()
test_eval = get_test_eval()
def extract_mle_json(log_content: str) -> dict | None:
match = re.search(r"\{.*\}", log_content, re.DOTALL)
if match:
return json.loads(match.group(0))
return None
def extract_loopid_func_name(tag):
"""提取 Loop ID 和函数名称"""
match = re.search(r"Loop_(\d+)\.([^.]+)", tag)
return match.groups() if match else (None, None)
is_mle = isinstance(test_eval, MLETestEval)
def save_grade_info(log_trace_path: Path):
@@ -41,27 +33,53 @@ def save_grade_info(log_trace_path: Path):
if "running" in msg.tag:
if isinstance(msg.content, DSExperiment):
mle_score_str = msg.content.experiment_workspace.execute(
env=de,
entry=f"mlebench grade-sample submission.csv {competition} --data-dir /mle/data | tee mle_score.txt",
)
msg.content.experiment_workspace.execute(env=de, entry="chmod 777 mle_score.txt")
trace_storage.log(
mle_score_str, name=f"{msg.tag}.mle_score.pid", save_type="pkl", timestamp=msg.timestamp
)
def is_valid_session(p: Path) -> bool:
return p.is_dir() and p.joinpath("__session__").exists()
# TODO: mle_score.txt is not a general name now.
# Please use a more general name like test_score.txt
try:
mle_score_str = test_eval.eval(competition, msg.content.experiment_workspace)
trace_storage.log(
mle_score_str, tag=f"{msg.tag}.mle_score.pid", save_type="pkl", timestamp=msg.timestamp
)
except Exception as e:
print(f"Error in {log_trace_path}: {e}", traceback.format_exc())
def save_all_grade_info(log_folder):
for log_trace_path in log_folder.iterdir():
for log_trace_path in Path(log_folder).iterdir():
if is_valid_session(log_trace_path):
save_grade_info(log_trace_path)
try:
save_grade_info(log_trace_path)
except NoTestEvalError as e:
print(f"Error in {log_trace_path}: {e}", traceback.format_exc())
def summarize_folder(log_folder: Path):
def _get_loop_and_fn_after_hours(log_folder: Path, hours: int):
stop_session_fp = get_first_session_file_after_duration(log_folder, f"{hours}h")
with stop_session_fp.open("rb") as f:
session_obj: LoopBase = pickle.load(f)
loop_trace = session_obj.loop_trace
stop_li = max(loop_trace.keys())
last_loop = loop_trace[stop_li]
last_step = last_loop[-1]
stop_fn = session_obj.steps[last_step.step_idx]
print(f"Stop Loop: {stop_li=}, {stop_fn=}")
files = sorted(
(log_folder / "__session__").glob("*/*_*"), key=lambda f: (int(f.parent.name), int(f.name.split("_")[0]))
)
print(f"Max Session: {files[-1:]=}")
return stop_li, stop_fn
def summarize_folder(log_folder: Path, hours: int | None = None):
"""
Summarize the log folder and save the summary as a pickle file.
Args:
log_folder (Path): The path to the log folder (contains many log traces).
hours (int | None): The number of hours to stat. If None, stat all.
"""
log_folder = Path(log_folder)
stat = defaultdict(dict)
for log_trace_path in log_folder.iterdir(): # One log trace
@@ -88,44 +106,50 @@ def summarize_folder(log_folder: Path):
sota_exp_score = None
sota_exp_rank = None
grade_output = None
for msg in FileStorage(log_trace_path).iter_msg(): # messages in log trace
if hours:
stop_li, stop_fn = _get_loop_and_fn_after_hours(log_trace_path, hours)
msgs = [(msg, extract_loopid_func_name(msg.tag)) for msg in FileStorage(log_trace_path).iter_msg()]
msgs = [(msg, int(loop_id) if loop_id else loop_id, fn) for msg, (loop_id, fn) in msgs]
msgs.sort(key=lambda m: m[1] if m[1] else -1) # sort by loop id
for msg, loop_id, fn in msgs: # messages in log trace
if loop_id:
loop_num = max(loop_id + 1, loop_num)
if hours and loop_id == stop_li and fn == stop_fn:
break
if msg.tag and "llm" not in msg.tag and "session" not in msg.tag:
if "competition" in msg.tag:
stat[log_trace_path.name]["competition"] = msg.content
# get threshold scores
workflowexp = FBWorkspace()
stdout = workflowexp.execute(
env=de,
entry=f"mlebench grade-sample None {stat[log_trace_path.name]['competition']} --data-dir /mle/data",
)
grade_output = extract_mle_json(stdout)
if grade_output:
bronze_threshold = grade_output["bronze_threshold"]
silver_threshold = grade_output["silver_threshold"]
gold_threshold = grade_output["gold_threshold"]
median_threshold = grade_output["median_threshold"]
if "direct_exp_gen" in msg.tag and isinstance(msg.content, DSExperiment):
loop_num += 1
if is_mle:
stdout = workflowexp.execute(
env=test_eval.env,
entry=f"mlebench grade-sample None {stat[log_trace_path.name]['competition']} --data-dir /mle/data",
)
grade_output = extract_json(stdout)
if grade_output:
bronze_threshold = grade_output["bronze_threshold"]
silver_threshold = grade_output["silver_threshold"]
gold_threshold = grade_output["gold_threshold"]
median_threshold = grade_output["median_threshold"]
if "running" in msg.tag:
if isinstance(msg.content, DSExperiment):
submission_path = msg.content.experiment_workspace.workspace_path / "submission.csv"
if submission_path.exists():
made_submission_num += 1
scores_path = msg.content.experiment_workspace.workspace_path / "scores.csv"
valid_scores[loop_num - 1] = pd.read_csv(scores_path, index_col=0)
if msg.content.result is not None:
valid_scores[loop_id] = msg.content.result
elif "mle_score" in msg.tag:
loop_id, _ = extract_loopid_func_name(msg.tag)
loop_id = int(loop_id)
grade_output = extract_mle_json(msg.content)
grade_output = extract_json(msg.content)
if grade_output:
if grade_output["submission_exists"]:
made_submission_num += 1
if grade_output["score"] is not None:
test_scores[loop_id + 1] = grade_output["score"]
_, test_ranks[loop_id + 1] = score_rank(
stat[log_trace_path.name]["competition"], grade_output["score"]
)
test_scores[loop_id] = grade_output["score"]
if is_mle:
_, test_ranks[loop_id] = score_rank(
stat[log_trace_path.name]["competition"], grade_output["score"]
)
if grade_output["valid_submission"]:
valid_submission_num += 1
if grade_output["above_median"]:
@@ -158,9 +182,10 @@ def summarize_folder(log_folder: Path):
sota_exp_stat = "made_submission"
if grade_output["score"] is not None:
sota_exp_score = grade_output["score"]
_, sota_exp_rank = score_rank(
stat[log_trace_path.name]["competition"], grade_output["score"]
)
if is_mle:
_, sota_exp_rank = score_rank(
stat[log_trace_path.name]["competition"], grade_output["score"]
)
stat[log_trace_path.name].update(
{
@@ -185,10 +210,14 @@ def summarize_folder(log_folder: Path):
"median_threshold": median_threshold,
}
)
if (log_folder / "summary.pkl").exists():
(log_folder / "summary.pkl").unlink()
print("Old summary file removed.")
pd.to_pickle(stat, log_folder / "summary.pkl")
# Save the summary
save_name = f"summary_{hours}h.pkl" if hours else "summary.pkl"
save_p = log_folder / save_name
if save_p.exists():
save_p.unlink()
print(f"Old {save_name} removed.")
pd.to_pickle(stat, save_p)
# {
+253
View File
@@ -0,0 +1,253 @@
# API
## A. Controls
### 1. /upload [POST]
#### Request
- "scenario": one of six values
1. "Finance Data Building"
2. "Finance Data Building (Reports)"
3. "Finance Model Implementation"
4. "General Model Implementation"
5. "Medical Model Implementation"
6. "Data Science"
- "files": **2** scenarios need this
1. in "Finance Data Building (Reports)" Scenario, one or more pdf files.
2. in "General Model Implementation" Scenario, one pdf file or one pdf link like `https://arxiv.org/pdf/2210.09789`
- "competition": **Data Science** Scenario need this, one of 75 competitions.
- "loops": Number of loops after which RD-Agent will automatically stop (optional; if not set, it will not stop automatically and must be stopped manually).
- "all_duration": Total duration (in hours) for which the RD-Agent should run before stopping automatically. If not set, the agent will continue running until stopped manually or by the "loops" parameter.
#### Response
- "id": a unique identifier string, such as `/home/rdagent_log/data_science/competition_A/trace_1` or `/home/rdagent_log/finance/trace_1`, used to mark the series of logs generated by this RD-Agent run.
### 2. /control [POST]
#### Request
- "id": identifier
- "action": one of three values
1. "pause"
2. "resume"
3. "stop"
#### Response
- "status": "success" / "error: ..."
### 3. /trace [POST]
Returns the sequence of Messages generated for the current id on the backend that **have not yet been returned to the frontend**.
#### Request
- "id": identifier
- "all": True / False. True means all Messages not yet provided to the frontend will be returned; False returns a random 1 to 10 Messages. In most cases, this should be True.
- "reset": True / False. Reset means the pointer for "not yet returned to the frontend" will be set back to the first Message generated for this id, i.e., return from the beginning. In most cases, this should be False.
#### Response
- a list of [Messages](#b-messages)
## B. Messages
### Research
Only **2** Message in one loop
1. hypothesis
```json
{
"tag": "research.hypothesis",
"timestamp": "<isoformat>",
"loop_id": "1",
"content": {
"hypothesis": "...",
"reason": "...",
"component": "...", // only exists in Data Science Scenario
"concise_reason": "...",
"concise_justification": "...",
"concise_observation": "...",
"concise_knowledge": "...",
}
}
```
2. tasks
```json
{
"tag": "research.tasks",
"timestamp": "<isoformat>",
"loop_id": "1",
"content": [ // list of tasks
{
"name": "...",
"description": "...",
"model_type": "...", // only exists in "Finance Model Implementation", "General Model Implementation", "Medical Model Implementation", or some tasks of "Data Science"
"architecture": "...", // same as above
"hyperparameters": "...", // same as above
},
{
}
//... same as above
]
}
```
### evolving
- 1 to 10 pairs of Messages (codes & feedbacks), each identified by an "evo_id" indicating the evolving round.
- In the **Data Science** scenario, each evolving round contains only **one task**, but the "codes" for that task may include **multiple code files**.
- In other scenarios, each evolving round may contain **multiple tasks**, but each task's "codes" will include only **one code file**.
1. codes
```json
{
"tag": "evolving.codes",
"timestamp": "<isoformat>",
"loop_id": "1",
"evo_id": "0",
"content": [ // list of task_name & codes
{
"evo_id": "0",
"target_task_name": "task_1",
"workspace": { // one or more codes
"a.py": "...<python codes>",
"b.py": "...<python codes>",
//...
}
},
{
"evo_id": "0",
"target_task_name": "task_2",
"workspace": {
"a.py": "...<python codes>",
//...
}
}
//... same as above
]
}
{
"tag": "evolving.codes",
"timestamp": "<isoformat>",
"loop_id": "1",
"evo_id": "1",
"content": [
//... same as above
]
}
```
2. feedbacks
```json
{
"tag": "evolving.feedbacks",
"timestamp": "<isoformat>",
"loop_id": "1",
"evo_id": "0",
"content": [ // list of feedbacks
{
"evo_id": "0",
"final_decision": "True", // True or False
"execution": "...",
"code": "...",
"return_checking": "..."
},
//... same as above
]
}
{
"tag": "evolving.codes",
"timestamp": "<isoformat>",
"loop_id": "1",
"evo_id": "1",
"content": [
//... same as above
]
}
```
### feedback
Each tag below appears only once per loop.
1. config (only exists in "Finance Data Building"/"Finance Data Building (Reports)"/"Finance Model Implementation")
```json
{
"tag": "feedback.config",
"timestamp": "<isoformat>",
"loop_id": "1",
"content": {
"config": "a markdown string",
}
}
```
2. return_chart (only exists in "Finance Data Building"/"Finance Data Building (Reports)"/"Finance Model Implementation")
```json
{
"tag": "feedback.return_chart",
"timestamp": "<isoformat>",
"loop_id": "1",
"content": {
"chart_html": "chart html codes string",
}
}
```
3. metric
```json
{
"tag": "feedback.metric",
"timestamp": "<isoformat>",
"loop_id": "1",
"content": {
"result": "{ \"<metric_name>\": <value>, ... }" // A JSON string containing metric names and their corresponding values.
}
}
```
4. hypothesis_feedback
```json
{
"tag": "feedback.hypothesis_feedback",
"timestamp": "<isoformat>",
"loop_id": "1",
"content": {
"decision": "True",
"reason": "...",
"exception": "...",
"observations": "...", // may not exists
"hypothesis_evaluation": "...", // may not existsc
"new_hypothesis": "...", // may not exists
}
}
```
# TODO
## Session
- How to continue.
- show & copy trace_id(name)?
-
## Page
1. remove Medical, add Finance Whole Pipeline
2.
+260
View File
@@ -0,0 +1,260 @@
import os
import random
import signal
import subprocess
from collections import defaultdict
from datetime import datetime, timezone
from pathlib import Path
import randomname
import typer
from flask import Flask, jsonify, request, send_from_directory
from flask_cors import CORS
from rdagent.log.storage import FileStorage
from rdagent.log.ui.conf import UI_SETTING
from rdagent.log.ui.storage import WebStorage
from rdagent.log.utils import is_valid_session
app = Flask(__name__, static_folder=UI_SETTING.static_path)
CORS(app)
rdagent_processes = defaultdict()
server_port = 19899
log_folder_path = Path(UI_SETTING.trace_folder).absolute()
@app.route("/favicon.ico")
def favicon():
return send_from_directory(app.static_folder, "favicon.ico", mimetype="image/vnd.microsoft.icon")
msgs_for_frontend = defaultdict(list)
pointers = defaultdict(lambda: defaultdict(int)) # pointers[trace_id][user_ip]
def read_trace(log_path: Path, id: str = "") -> None:
fs = FileStorage(log_path)
ws = WebStorage(port=1, path=log_path)
msgs_for_frontend[id] = []
last_timestamp = None
for msg in fs.iter_msg():
data = ws._obj_to_json(obj=msg.content, tag=msg.tag, id=id, timestamp=msg.timestamp.isoformat())
if data:
if isinstance(data, list):
for d in data:
msgs_for_frontend[id].append(d["msg"])
last_timestamp = msg.timestamp
else:
msgs_for_frontend[id].append(data["msg"])
last_timestamp = msg.timestamp
now = datetime.now(timezone.utc)
if last_timestamp and (now - last_timestamp).total_seconds() > 1800:
msgs_for_frontend[id].append({"tag": "END", "timestamp": now.isoformat(), "content": {}})
# load all traces from the log folder
for p in log_folder_path.glob("*/*/"):
if is_valid_session(p):
read_trace(p, id=str(p))
@app.route("/trace", methods=["POST"])
def update_trace():
global pointers, msgs_for_frontend
data = request.get_json()
trace_id = data.get("id")
return_all = data.get("all")
reset = data.get("reset")
msg_num = random.randint(1, 10)
app.logger.info(data)
log_folder_path = Path(UI_SETTING.trace_folder).absolute()
if not trace_id:
return jsonify({"error": "Trace ID is required"}), 400
trace_id = str(log_folder_path / trace_id)
user_ip = request.remote_addr
if reset:
pointers[trace_id][user_ip] = 0
start_pointer = pointers[trace_id][user_ip]
end_pointer = start_pointer + msg_num
if end_pointer > len(msgs_for_frontend[trace_id]) or return_all:
end_pointer = len(msgs_for_frontend[trace_id])
returned_msgs = msgs_for_frontend[trace_id][start_pointer:end_pointer]
pointers[trace_id][user_ip] = end_pointer
if returned_msgs:
app.logger.info([msg["tag"] for msg in returned_msgs])
return jsonify(returned_msgs), 200
@app.route("/upload", methods=["POST"])
def upload_file():
# 获取请求体中的字段
global rdagent_processes, server_port
scenario = request.form.get("scenario")
files = request.files.getlist("files")
competition = request.form.get("competition")
loop_n = request.form.get("loops")
all_duration = request.form.get("all_duration")
# scenario = "Data Science Loop"
if scenario == "Data Science":
competition = competition[10:] # Eg. MLE-Bench:aerial-cactus-competition
trace_name = f"{competition}-{randomname.get_name()}"
else:
trace_name = randomname.get_name()
trace_files_path = log_folder_path / scenario / "uploads" / trace_name
log_trace_path = (log_folder_path / scenario / trace_name).absolute()
stdout_path = log_folder_path / scenario / f"{trace_name}.stdout"
if not stdout_path.exists():
stdout_path.parent.mkdir(parents=True, exist_ok=True)
# save files
for file in files:
if file:
p = log_folder_path / scenario / "uploads" / trace_name
if not p.exists():
p.mkdir(parents=True, exist_ok=True)
file.save(p / file.filename)
if scenario == "Finance Data Building":
cmds = ["rdagent", "fin_factor"]
if scenario == "Finance Data Building (Reports)":
cmds = ["rdagent", "fin_factor_report", "--report_folder", str(trace_files_path)]
if scenario == "Finance Model Implementation":
cmds = ["rdagent", "fin_model"]
if scenario == "General Model Implementation":
if len(files) == 0: # files is one link
rfp = request.form.get("files")[0]
else: # one file is uploaded
rfp = str(trace_files_path / files[0].filename)
cmds = ["rdagent", "general_model", "--report_file_path", rfp]
if scenario == "Finance Whole Pipeline":
cmds = ["rdagent", "fin_quant"]
if scenario == "Data Science":
cmds = ["rdagent", "data_science", "--competition", competition]
# time control parameters
if scenario != "Finance Data Building (Reports)":
if loop_n:
cmds += ["--loop_n", loop_n]
if all_duration:
cmds += ["--timeout", f"{all_duration}h"]
app.logger.info(f"Started process for {log_trace_path} with parameters: {cmds}")
with stdout_path.open("w") as log_file:
rdagent_processes[str(log_trace_path)] = subprocess.Popen(
cmds,
stdout=log_file,
stderr=log_file,
env={
**os.environ,
"LOG_TRACE_PATH": str(log_trace_path),
"LOG_UI_SERVER_PORT": str(server_port),
},
)
return (
jsonify(
{
"id": f"{scenario}/{trace_name}",
}
),
200,
)
@app.route("/receive", methods=["POST"])
def receive_msgs():
try:
data = request.get_json()
# app.logger.info(data["msg"]["tag"])
if not data:
return jsonify({"error": "No JSON data received"}), 400
except Exception as e:
return jsonify({"error": "Internal Server Error"}), 500
if isinstance(data, list):
for d in data:
msgs_for_frontend[d["id"]].append(d["msg"])
else:
msgs_for_frontend[data["id"]].append(data["msg"])
return jsonify({"status": "success"}), 200
@app.route("/control", methods=["POST"])
def control_process():
global rdagent_processes, msgs_for_frontend
data = request.get_json()
app.logger.info(data)
if not data or "id" not in data or "action" not in data:
return jsonify({"error": "Missing 'id' or 'action' in request"}), 400
id = str(log_folder_path / data["id"])
action = data["action"]
if id not in rdagent_processes or rdagent_processes[id] is None:
return jsonify({"error": "No running process for given id"}), 400
process = rdagent_processes[id]
if process.poll() is not None:
msgs_for_frontend[id].append({"tag": "END", "timestamp": datetime.now(timezone.utc).isoformat(), "content": {}})
return jsonify({"error": "Process has already terminated"}), 400
try:
if action == "pause":
os.kill(process.pid, signal.SIGSTOP)
return jsonify({"status": "paused"}), 200
elif action == "resume":
os.kill(process.pid, signal.SIGCONT)
return jsonify({"status": "resumed"}), 200
elif action == "stop":
process.terminate()
process.wait()
del rdagent_processes[id]
msgs_for_frontend[id].append(
{"tag": "END", "timestamp": datetime.now(timezone.utc).isoformat(), "content": {}}
)
return jsonify({"status": "stopped"}), 200
else:
return jsonify({"error": "Unknown action"}), 400
except Exception as e:
return jsonify({"error": f"Failed to {action} process, {e}"}), 500
@app.route("/test", methods=["GET"])
def test():
# return 'Hello, World!'
global msgs_for_frontend, pointers
msgs = {k: [i["tag"] for i in v] for k, v in msgs_for_frontend.items()}
pointers = pointers
return jsonify({"msgs": msgs, "pointers": pointers}), 200
@app.route("/", methods=["GET"])
def index():
# return 'Hello, World!'
# return {k: [i["tag"] for i in v] for k, v in msgs_for_frontend.items()}
return send_from_directory(app.static_folder, "index.html")
@app.route("/<path:fn>", methods=["GET"])
def server_static_files(fn):
return send_from_directory(app.static_folder, fn)
def main(port: int = 19899):
global server_port
server_port = port
app.run(debug=False, host="0.0.0.0", port=port)
if __name__ == "__main__":
typer.run(main)

Some files were not shown because too many files have changed in this diff Show More