Skip to content

feat: unify agent types #139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 63 commits into from
Jul 7, 2025
Merged

feat: unify agent types #139

merged 63 commits into from
Jul 7, 2025

Conversation

sicoyle
Copy link
Contributor

@sicoyle sicoyle commented Jun 23, 2025

Currently, there are a few different agent types:

  1. OpenAPIReAct
  2. ToolCalling
  3. ReAct
  4. Assistant
  5. Actor

This PR refactors things so there are two agents from an end user POV:

  1. Agent: which based on diff params passed in will become the OpenAPIReAct, ToolCalling , or ReAct agent. This is the simpler agent to use for more concrete tasks.
  2. DurableAgent: rename of AssistantAgent. The goal here is to make it more indicative that this is the prod use agent with durability and resiliency built in thanks to Dapr.
  3. Rm explicit and external classes for: ToolCallAgent, ReActAgent, OpenAPIReActAgent.

I've moved both agents to live side by side under dapr_agents/agents/agent and dapr_agents/agents/durableagent. This way they can leverage the same AgentBase class. There was a AgentWorkflowBase class that I totally removed in this refactor to give DurableAgent and Agent the same base class as it had basically the same fields plus a few things as the AgentBase class. So this is a lot cleaner now :)

I removed my initial thoughts on adding in config file support to keep this PR more focused on just cleaning up agent types for now. In a follow up PR I will tackle:

  1. Externalizing the configuration for creating agents to support a config file setup and/or params to instantiate agent alone
  2. Separating out the agent fields into different classes such as one for the workflow durability fields, one for pubsub related fields, one for state, etc.

I've added a common config class to bring in the basic config support; however, please note that the config will be one of the next things I tackle, so I am open to feedback here, but this is just a starting point aimed at making the DX better for agent instantiation by moving the params to a config file for users.

I've also fixed random things I came across such as supporting more graceful shutdowns if someone kills the terminal as the agent is working on different tasks, fixed a few things on the template formatting, etc.

Context: #126

Still todo for this PR:

  • make durableagent AgentWorkflowBase use same base agent class setup as regular agent class instead of custom one with duplicate logic
  • double check my config setup after all the refactors
  • add some tests
  • review it myself and clean up my logs I've added
  • fix all the mypy type errors from tox -e type
  • NOTE: docs I will add in a separate PR to accompany these changes

Additional context

Mental mapping / mostly UML-ish 😄 diagram for the agent factory class
These are diagrams I created as I went through things and did my refactoring, so sharing in case useful for others
image

Mental mapping / mostly UML-ish 😄 diagram for the now AssistantAgentClass
image

Note: I did not add in my config class to my diagrams

@sicoyle sicoyle changed the title Feat unify agent types feat: unify agent types Jun 23, 2025
@sicoyle
Copy link
Contributor Author

sicoyle commented Jun 23, 2025

this is ready for review, but i still would like to add tests to the PR before we send it. I looked and there is an e2e test github workflow that would be good to run, but looks like that still needs some setting up... so i guess that'll be another separate effort.

@cecilphillip
Copy link

Should this pr also consider #121 as well since it's refactoring the base agent types?

@sicoyle
Copy link
Contributor Author

sicoyle commented Jun 24, 2025

Should this pr also consider #121 as well since it's refactoring the base agent types?

Thanks for the feedback @cecilphillip ! Yes, good point. I am working on a different approach with just oneeeee agent type. So let me get some more progress there and can add the dapr chat client param for agent instantiation in the better option :)

Copy link
Collaborator

@Cyb3rWard0g Cyb3rWard0g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I need to run it locally now.

sicoyle added 2 commits June 25, 2025 12:16
Signed-off-by: Samantha Coyle <[email protected]>
@sicoyle
Copy link
Contributor Author

sicoyle commented Jun 25, 2025

LGTM. I need to run it locally now.

@Cyb3rWard0g Thanks for the feedback! I'm going to make a few more changes just to rm the OpenAPIReAct agent and ReAct agents under the hood so it's the case that we have:

  1. Durable Agent -> Assistant agent class logic leveraging dapr workflows
  2. Agent -> Just tool calling class

For visibility, the reasoning is that the DurableAgent workflow implementation uses ReAct pattern anyways, so we don't really need it and the underlying ReAct existing class. Also, OpenAPIReAct agent can just be transformed into a quickstart and we can keep the vector store capabilities, but not need to keep that as a concrete class.

I need to add tests too :)

Will mark the PR ready for review when I'm done with all of the above in this comment!

@sicoyle
Copy link
Contributor Author

sicoyle commented Jun 26, 2025

Should this pr also consider #121 as well since it's refactoring the base agent types?

@cecilphillip DaprChatClient still needs toolcall capabilities, so I'm not setting as the default yet, but will eventually when it's merged in dapr/dapr. Agent & DurableAgent does take an llm client, so you can technically already use the DaprChatClient as that parameter :)

@sicoyle
Copy link
Contributor Author

sicoyle commented Jun 30, 2025

ready for rechecks pls

content = response.get_content()
if content:
self.memory.add_message(AssistantMessage(content=content))
# # TODO(@Sicoyle): we should not clear the tool history here, but rather when the agent is done, or not at all.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should always clear self.tool_history when the assistant returns a final response (i.e., when there are no more tool calls in the message and the conversation is ready to return a user-facing answer).

This ensures:

  • Tool results are only kept for the current round of tool calls.
  • We do not accidentally send tool results for tool calls that are no longer relevant in the next round.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this maybe makes sense for the Agent, and we have a different implementation for DurableAgent. I guess my question here would be if folks using the Agent class would want audit-ability to debug agent behavior, or understand its decision making process, or just have their tool history handy. It might be nice to make this more configurable either with a TTL, or allowing users to manually purge, or yeah as we have here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it! Yeah, it would be good to give control to the user. With the open telemetry feature, all of that should be captured ;) , but for those not using the observability feature, it would be good to enable that capability.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I run the dapr run -f dapr-random.yaml following the README, I see error messages like these

== APP - WorkflowApp == ERROR:dapr_agents.workflow.base:Error on transaction attempt: 1: No etag found for key: agents_registry
== APP - WorkflowApp == INFO:dapr_agents.workflow.base:Sleeping for 1 second before retrying transaction...

and it seems that agents are not registering their metadata

== APP - WorkflowApp == INFO:dapr_agents.workflow.task:Executing task 'select_random_speaker'
== APP - WorkflowApp == INFO:dapr_agents.workflow.task:Invoking regular Python function
== APP - WorkflowApp == INFO:dapr_agents.workflow.agentic:Agents found in 'agentstatestore' for key 'agents_registry'.
== APP - WorkflowApp == INFO:dapr_agents.workflow.agentic:No other agents found after filtering.
== APP - WorkflowApp == WARNING:dapr_agents.workflow.orchestrators.random:No agents available for selection.
== APP - WorkflowApp == ERROR:dapr_agents.workflow.task:Error in task 'select_random_speaker'
== APP - WorkflowApp == Traceback (most recent call last):
== APP - WorkflowApp ==   File "/Users/wardog/Documents/GitHub/TEST/dapr-agents/dapr_agents/workflow/base.py", line 165, in run_sync
== APP - WorkflowApp ==     loop = asyncio.get_running_loop()
== APP - WorkflowApp == RuntimeError: no running event loop
== APP - WorkflowApp == 
== APP - WorkflowApp == During handling of the above exception, another exception occurred:
== APP - WorkflowApp == 
== APP - WorkflowApp == Traceback (most recent call last):
== APP - WorkflowApp ==   File "/Users/wardog/Documents/GitHub/TEST/dapr-agents/dapr_agents/workflow/task.py", line 114, in __call__
== APP - WorkflowApp ==     raw = await self._run_python(data)
== APP - WorkflowApp ==           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
== APP - WorkflowApp ==   File "/Users/wardog/Documents/GitHub/TEST/dapr-agents/dapr_agents/workflow/task.py", line 155, in _run_python
== APP - WorkflowApp ==     return self.func(**data)
== APP - WorkflowApp ==            ~~~~~~~~~^^^^^^^^
== APP - WorkflowApp ==   File "/Users/wardog/Documents/GitHub/TEST/dapr-agents/dapr_agents/workflow/decorators.py", line 88, in wrapper
== APP - WorkflowApp ==     return f(*args, **kwargs)
== APP - WorkflowApp ==   File "/Users/wardog/Documents/GitHub/TEST/dapr-agents/dapr_agents/workflow/orchestrators/random.py", line 176, in select_random_speaker
== APP - WorkflowApp ==     raise ValueError(
== APP - WorkflowApp ==         "Agents metadata is empty. Cannot select a random speaker."
== APP - WorkflowApp ==     )
== APP - WorkflowApp == ValueError: Agents metadata is empty. Cannot select a random speaker.
Exited App successfully

I can only see the orchestrator registering itself but not the agents

image

Copy link
Contributor Author

@sicoyle sicoyle Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example in particular had pinned the last released version of dapr agents. So if you did the pip install on the requirements.txt file as is, then I guess I'm not surprised you saw an err. I updated the file to use the local dapr agents package required dependencies and saw no issues. I also see agents_registry showing agents registering locally. I was sure to try a fresh venv in checking this btw :)

image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on our conversation, rhe following is needed:

# Prepare agent metadata
self._agent_metadata = {
    "name": self.name,
    "role": self.role,
    "goal": self.goal,
    "instructions": self.instructions,
    "topic_name": self.agent_topic_name,
    "pubsub_name": self.message_bus_name,
    "orchestrator": False,
}

# Register agent metadata
self.register_agentic_system()

right after:

https://github.com/sicoyle/dapr-agents/blob/feat-unify-agent-types/dapr_agents/agents/durableagent/agent.py#L83

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After that, I can see the agent registering its metadata :)
image

Signed-off-by: Samantha Coyle <[email protected]>
@sicoyle sicoyle requested a review from yaron2 as a code owner July 1, 2025 17:18
sicoyle added 7 commits July 1, 2025 13:45
Signed-off-by: Samantha Coyle <[email protected]>
Signed-off-by: Samantha Coyle <[email protected]>
Signed-off-by: Samantha Coyle <[email protected]>
Signed-off-by: Samantha Coyle <[email protected]>
Signed-off-by: Samantha Coyle <[email protected]>
Signed-off-by: Samantha Coyle <[email protected]>
@sicoyle
Copy link
Contributor Author

sicoyle commented Jul 1, 2025

readyyyyy 🙌

 python 01_ask_llm.py

Got response: Why don't skeletons fight each other?

They don't have the guts!
python 02_build_agent.py
 
No type hints provided for function 'my_weather_func'. Defaulting to 'str'.
user:
What's the weather?

--------------------------------------------------------------------------------

assistant:
Function name: MyWeatherFunc (Call Id: call_3ZTT6WZEunozOzkc3cOggwSD)
Arguments: {}

--------------------------------------------------------------------------------

MyWeatherFunc(tool) (Id: call_3ZTT6WZEunozOzkc3cOggwSD):
It's 72°F and sunny

--------------------------------------------------------------------------------

assistant:
The current weather is 72°F and sunny.

--------------------------------------------------------------------------------

The current weather is 72°F and sunny.
dapr run --app-id stateful-llm --app-port 8001 --dapr-http-port 3500 --resources-path components/ -- python 03_durable_agent.py

...
g workflow 'ToolCallingWorkflow'
== APP == INFO:dapr_agents.agents.durableagent.agent:Workflow 5e40d5919252476ba67d9bb780a48f38 has been finalized with verdict: model hit a natural stop point.
== APP == 2025-07-01 14:37:33.589 durabletask-worker INFO: 5e40d5919252476ba67d9bb780a48f38: Orchestration completed with status: COMPLETED
INFO[0030] 5e40d5919252476ba67d9bb780a48f38: 'ToolCallingWorkflow' completed with a COMPLETED status.  app_id=stateful-llm instance=macbookpro.lan scope=dapr.wfengine.durabletask.backend type=log ver=1.15.5
INFO[0030] Workflow Actor '5e40d5919252476ba67d9bb780a48f38': workflow completed with status 'ORCHESTRATION_STATUS_COMPLETED' workflowName 'ToolCallingWorkflow'  app_id=stateful-llm instance=macbookpro.lan scope=dapr.runtime.actors.targets.workflow type=log ver=1.15.5
== APP == 2025-07-01 14:37:33.591 durabletask-client INFO: Instance '5e40d5919252476ba67d9bb780a48f38' completed.
== APP == INFO:dapr_agents.workflow.base:Workflow 5e40d5919252476ba67d9bb780a48f38 completed with status: WorkflowStatus.COMPLETED.
== APP == INFO:dapr_agents.workflow.base:Workflow '5e40d5919252476ba67d9bb780a48f38' completed successfully. Status: COMPLETED.

...
 ✗ curl -i -X POST http://localhost:8001/start-workflow \
  -H "Content-Type: application/json" \
  -d '{"task": "I want to find flights to Paris"}'
HTTP/1.1 202 Accepted
date: Tue, 01 Jul 2025 19:37:28 GMT
server: uvicorn
content-length: 104
content-type: application/json

{"message":"Workflow initiated successfully.","workflow_instance_id":"5e40d5919252476ba67d9bb780a48f38"}%                                                                   
.venv➜  01-hello-world git:(feat-unify-agent-types) ✗ curl -i -X GET http://localhost:
8001/status
HTTP/1.1 200 OK
date: Tue, 01 Jul 2025 19:38:03 GMT
server: uvicorn
content-length: 11
content-type: application/json

{"ok":true}%                                                                          
.venv➜  01-hello-world git:(feat-unify-agent-types) ✗ curl -i -X GET http://localhost:3500/v1.0/workflows/dapr/5e40d5919252476ba67d9bb780a48f38
HTTP/1.1 200 OK
Content-Type: application/json
Traceparent: 00-97a65623a1c328ddc9da8c5ab2563df8-adc24e8286355cf5-01
Date: Tue, 01 Jul 2025 19:38:13 GMT
Content-Length: 575

{"instanceID":"5e40d5919252476ba67d9bb780a48f38","workflowName":"ToolCallingWorkflow","createdAt":"2025-07-01T19:37:32.574168Z","lastUpdatedAt":"2025-07-01T19:37:33.590063Z","runtimeStatus":"COMPLETED","properties":{"dapr.workflow.input":"{\"task\": null, \"iteration\": 1, \"workflow_instance_id\": null}","dapr.workflow.output":"{\"content\": \"Here are some flight options to Paris:\\n\\n1. Airline: SkyHighAir - Price: $450.00\\n2. Airline: GlobalWings - Price: $375.50\\n\\nWould you like more information or assistance with anything else?\", \"role\": \"assistant\"}"}}% 
dapr run --app-id dapr-agent-wf -- python 04_chain_tasks.py
...

== APP == 
== APP == **Conclusion**
== APP == 
== APP == From enhancing operational efficiency to redefining customer experiences, AI agents play a pivotal role in the technological evolution. As their influence expands, it is imperative to focus on developing and deploying these agents ethically, ensuring they contribute positively to the future landscape of technology. Embracing this path with care and foresight will unlock the vast potential AI agents hold in shaping tomorrow.
== APP == Result: "**Understanding AI Agents: Navigating the Future of Artificial Intelligence**\n\nArtificial Intelligence (AI) agents represent a cornerstone in the expansive field of AI, acting as autonomous entities that perceive their environments and make decisions to achieve specific goals. They are revolutionizing industries by streamlining operations, enhancing user experiences, and driving innovation. From healthcare to finance, AI agents are reshaping how tasks are executed across various domains.\n\n**Different Types of AI Agents**\n\nAI agents can be classified into four main types, each with distinct characteristics:\n\n- **Reactive Agents** focus on present perceptions and responses without retaining history or using internal models. Ideal for straightforward tasks, these agents power applications like real-time gaming AI opponents or simple autonomous robotic responses.\n\n- **Deliberative Agents** involve complex decision-making processes by simulating potential future states. They are pivotal in applications such as strategic planning in logistics where calculated steps ensure optimal outcomes.\n\n- **Interactive Agents** are designed to collaborate and communicate with humans, proving essential in customer service through chatbots and virtual assistants, facilitating smoother interactions and enhancing accessibility.\n\n- **Hybrid Agents** synergize the strengths of reactive, deliberative, and interactive elements, allowing them to operate efficiently in multifaceted environments like autonomous vehicles that require instant response and strategic planning.\n\n**The Architecture Underpinning AI Agents**\n\nAI agents rely on several architectural designs to function:\n\n- **Goal-based architectures** prioritize achieving defined objectives, making them suitable for navigation systems.\n  \n- **Utility-based architectures** assess the best action through cost-benefit analyses, enhancing decision-making in financial modeling.\n  \n- **Learning architectures** are adaptive, using past experiences to inform future decisions, critical in personalizing user experiences.\n\nEach architecture presents unique advantages, catering to different operational needs and computational frameworks.\n\n**Technologies Empowering AI Agents**\n\nAI agents harness cutting-edge technologies like Machine Learning, Deep Learning, Reinforcement Learning, Natural Language Processing, and Computer Vision. These technologies enable agents to perform tasks with accuracy, adaptability, and human-like understanding. Case studies illustrate impressive feats such as DeepMind's AlphaGo, which demonstrates the power of reinforcement learning.\n\n**Navigating Challenges**\n\nDespite their potential, AI agents face significant challenges. Ethical concerns, such as bias in decision-making, computational demands, and issues of scalability hinder widespread adoption. Real-world integration also poses obstacles, necessitating robust strategies to ensure AI agents can make meaningful impacts without adverse effects.\n\n**Looking Ahead: The Future of AI Agents**\n\nThe horizon for AI agents is bright with continuous advancements broadening their scope and capabilities. Emerging applications are likely to transform industries, enhance productivity, and lead to new societal norms. As these technologies evolve, weighing their societal impacts and ethical considerations remains crucial to harnessing their benefits responsibly.\n\n**Conclusion**\n\nFrom enhancing operational efficiency to redefining customer experiences, AI agents play a pivotal role in the technological evolution. As their influence expands, it is imperative to focus on developing and deploying these agents ethically, ensuring they contribute positively to the future landscape of technology. Embracing this path with care and foresight will unlock the vast potential AI agents hold in shaping tomorrow."
✅  Exited App successfully

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the agent registration update, all workflows in this quickstart worked :)

Copy link
Collaborator

@Cyb3rWard0g Cyb3rWard0g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested all quickstarts but 05-multi-agent-workflow-k8s. All seems to work as expected! There was some issues with mcp stdio, but it is not related to the unification of agents. I will open an issue for mcp stdio. Finally, I noticed several imports in a few classes that are not used.

sicoyle added 2 commits July 2, 2025 10:16
Signed-off-by: Samantha Coyle <[email protected]>
@sicoyle
Copy link
Contributor Author

sicoyle commented Jul 2, 2025

readyyyyyy 🚀

Signed-off-by: Samantha Coyle <[email protected]>
Copy link
Collaborator

@Cyb3rWard0g Cyb3rWard0g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much @sicoyle ! 🔥🎉

@sicoyle sicoyle mentioned this pull request Jul 3, 2025
@yaron2 yaron2 merged commit 6d55c38 into dapr:main Jul 7, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants