feat: add user interaction in data science scenario (#1251)

* feat: add interactor classes and user interaction handling for experiments

* update code

* use fragment retry mechanism instead of rerun()

* fix a bug

* integrate user instructions into proposal and coder

* fix CI

* fix CI

* feat: add approval option for user instructions submission

* feat: enhance user instructions handling in Task and DSExperiment classes

* fix CI

* add user instructions into hypothesis rewrite

* add interface to command line

---------

Co-authored-by: Bowen Xian <xianbowen@outlook.com>
This commit is contained in:
Xu Yang
2025-09-18 11:10:12 +08:00
committed by GitHub
parent 121b2dbe48
commit 07c228ef9f
13 changed files with 448 additions and 12 deletions
+33 -3
View File
@@ -13,7 +13,7 @@ from collections.abc import Sequence
from copy import deepcopy
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Any, Generic, TypeVar
from typing import TYPE_CHECKING, Any, Generic, List, TypeVar
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.evaluation import Feedback
@@ -48,13 +48,28 @@ class AbsTask(ABC):
"""
class UserInstructions(List[str]):
def __str__(self) -> str:
if self:
return ("\nUser Instructions (Top priority!):\n" + "\n".join(f"- {ui}" for ui in self)) if self else ""
else:
return ""
class Task(AbsTask):
def __init__(self, name: str, version: int = 1, description: str = "") -> None:
def __init__(
self,
name: str,
version: int = 1,
description: str = "",
user_instructions: UserInstructions | None = None,
) -> None:
super().__init__(name, version)
self.description = description
self.user_instructions = user_instructions
def get_task_information(self) -> str:
return f"Task Name: {self.name}\nDescription: {self.description}"
return f"Task Name: {self.name}\nDescription: {self.description}{str(self.user_instructions)}"
def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.name}>"
@@ -410,6 +425,21 @@ class Experiment(
self.plan: ExperimentPlan | None = (
None # To store the planning information for this experiment, should be generated inside exp_gen.gen
)
self.user_instructions: UserInstructions | None = None # To store the user instructions for this experiment
def set_user_instructions(self, user_instructions: UserInstructions | None) -> None:
if user_instructions is None:
return
if not isinstance(user_instructions, UserInstructions) and isinstance(user_instructions, list):
user_instructions = UserInstructions(user_instructions)
self.user_instructions = user_instructions
for ws in self.sub_workspace_list:
if ws is not None:
ws.target_task.user_instructions = user_instructions # type: ignore[union-attr]
for task in self.sub_tasks:
task.user_instructions = user_instructions
if self.experiment_workspace is not None and self.experiment_workspace.target_task is not None:
self.experiment_workspace.target_task.user_instructions = user_instructions
@property
def result(self) -> object:
+26
View File
@@ -0,0 +1,26 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from trace import Trace
from typing import TYPE_CHECKING, Generic
from rdagent.core.experiment import ASpecificExp
if TYPE_CHECKING:
from rdagent.core.scenario import Scenario
class Interactor(ABC, Generic[ASpecificExp]):
def __init__(self, scen: Scenario) -> None:
self.scen: Scenario = scen
@abstractmethod
def interact(self, exp: ASpecificExp, trace: Trace | None = None) -> ASpecificExp:
"""
Interact with the experiment to get feedback or confirmation.
Responsibilities:
- Present the current state of the experiment.
- Collect input to guide the next steps in the experiment.
- Rewrite the experiment based on feedback.
"""