Skip to content

Commit

Permalink
added test to react agent
Browse files Browse the repository at this point in the history
  • Loading branch information
liyin2015 committed Dec 19, 2024
1 parent 66390d6 commit ca95233
Show file tree
Hide file tree
Showing 13 changed files with 1,942 additions and 369 deletions.
61 changes: 39 additions & 22 deletions adalflow/adalflow/components/agent/react.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@


from adalflow.core.generator import Generator
from adalflow.core.component import Component
from adalflow.optim.grad_component import GradComponent
from adalflow.optim.parameter import Parameter, ParameterType
from adalflow.core.func_tool import FunctionTool, AsyncCallable
from adalflow.core.tool_manager import ToolManager
from adalflow.components.output_parsers import JsonOutputParser
Expand All @@ -27,40 +28,45 @@

# TODO: test react agent

DEFAULT_REACT_AGENT_SYSTEM_PROMPT = r"""<SYS>
{# role/task description #}
react_agent_task_desc = r"""{# role/task description #}
You are a helpful assistant.
Answer the user's query using the tools provided below with minimal steps and maximum accuracy.
{# REACT instructions #}
Each step you will read the previous Thought, Action, and Observation(execution result of the action) and then provide the next Thought and Action.
<START_OF_TASK_SPEC>
{# Task specification to teach the agent how to think using 'divide and conquer' strategy #}
- For simple queries: Directly call the ``finish`` action and provide the answer.
- For complex queries:
- Step 1: Read the user query and potentially divide it into subqueries. And get started with the first subquery.
- Call one available tool at a time to solve each subquery/subquestion. \
- At step 'finish', join all subqueries answers and finish the task.
Remember:
- Action must call one of the above tools with name. It can not be empty.
- You will always end with 'finish' action to finish the task. The answer can be the final answer or failure message.
<END_OF_TASK_SPEC>
"""

DEFAULT_REACT_AGENT_SYSTEM_PROMPT = r"""<START_OF_SYSTEM_PROMPT>
{{react_agent_task_desc}}
{# Tools #}
{% if tools %}
<TOOLS>
<START_OF_TOOLS>
You available tools are:
{% for tool in tools %}
{{ loop.index }}.
{{tool}}
------------------------
{% endfor %}
</TOOLS>
<END_OF_TOOLS>
{% endif %}
{# output format and examples for output format #}
<OUTPUT_FORMAT>
<START_OF_OUTPUT_FORMAT>
{{output_format_str}}
</OUTPUT_FORMAT>
<TASK_SPEC>
{# Task specification to teach the agent how to think using 'divide and conquer' strategy #}
- For simple queries: Directly call the ``finish`` action and provide the answer.
- For complex queries:
- Step 1: Read the user query and potentially divide it into subqueries. And get started with the first subquery.
- Call one available tool at a time to solve each subquery/subquestion. \
- At step 'finish', join all subqueries answers and finish the task.
Remember:
- Action must call one of the above tools with name. It can not be empty.
- You will always end with 'finish' action to finish the task. The answer can be the final answer or failure message.
</TASK_SPEC>
</SYS>
<END_OF_OUTPUT_FORMAT>
<END_OF_SYSTEM_PROMPT>
-----------------
<START_OF_USER_QUERY>
User query:
{{ input_str }}
{# Step History #}
Expand All @@ -76,10 +82,11 @@
{% endfor %}
</STEPS>
{% endif %}
You:"""
<END_OF_USER_QUERY>
"""


class ReActAgent(Component):
class ReActAgent(GradComponent):
__doc__ = r"""ReActAgent uses generator as a planner that runs multiple and sequential functional call steps to generate the final response.
Users need to set up:
Expand Down Expand Up @@ -135,11 +142,13 @@ def __init__(
max_steps: int = 10,
add_llm_as_fallback: bool = True,
# TODO: the examples are just for specifying the output format, not end to end input-output examples, need further optimization
examples: List[FunctionExpression] = [],
# examples: List[FunctionExpression] = [],
examples: Union[List[FunctionExpression], List[str]] = [],
*,
# the following arguments are mainly for the planner
model_client: ModelClient,
model_kwargs: Dict = {},
# template for the planner
template: Optional[str] = None, # allow users to customize the template
):
super().__init__()
Expand All @@ -165,6 +174,13 @@ def __init__(
prompt_kwargs = {
"tools": self.tool_manager.yaml_definitions,
"output_format_str": output_parser.format_instructions(),
"react_agent_task_desc": Parameter(
name="react_agent_task_desc",
data=react_agent_task_desc,
role_desc="Task description for the ReAct agent which functions as a planner using a Large Language Model.",
param_type=ParameterType.PROMPT,
requires_opt=True,
),
}
self.planner = Generator(
template=template,
Expand Down Expand Up @@ -249,6 +265,7 @@ def _run_one_step(self, step: int, prompt_kwargs: Dict, model_kwargs: Dict) -> s
response: GeneratorOutput = self.planner(
prompt_kwargs=prompt_kwargs, model_kwargs=model_kwargs
)

if response.error:
error_msg = f"Error planning step {step}: {response.error}"
step_output.observation = error_msg
Expand Down
3 changes: 3 additions & 0 deletions adalflow/adalflow/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,9 @@ def add(a, b):
The benefits are less failed function calls.
"""
question: Optional[str] = field(
default=None, metadata={"desc": "The question to ask the LLM"}
)
thought: Optional[str] = field(
default=None, metadata={"desc": "Why the function is called"}
)
Expand Down
7 changes: 4 additions & 3 deletions adalflow/adalflow/optim/text_grad/tgd_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ class HistoryPrompt(DataClass):
You must base on the following examples when modifying the {{variable_desc}}:
<EXAMPLES>{{in_context_examples}}</EXAMPLES>
{% endif %}
YOU MUST ENSURE the new variable shares the same intent as the original variable.
You can either rephrase the initial variable, or add more specific instructions based on the feedback.
You can not change the variable to only fit on one sample if the batch size is larger than 1.
<END_OF_USER_MESSAGE>
"""

# YOU MUST ENSURE the new variable shares the same intent as the original variable.
# You can either rephrase the initial variable, or add more specific instructions based on the feedback.
# You can not change the variable to only fit on one sample if the batch size is larger than 1.

# optimizer system prompt

# Tips:
Expand Down
2 changes: 0 additions & 2 deletions adalflow/adalflow/optim/trainer/adal.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,6 @@ def train_step(self, batch, batch_idx, num_workers: int = 2) -> List:
if isinstance(y_pred, Parameter):
raise ValueError(f"y_pred_{i} is a Parameter, {y_pred}")

print(f"y_pred: {y_pred})")

assert (
y_pred.id == sample.id
), f"ID mismatch: {y_pred.id} != {sample.id}, type: {type(y_pred)}"
Expand Down
Loading

0 comments on commit ca95233

Please sign in to comment.