Skip to content
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

feature: add AgentTemplate & WorkPattern modules in the agentUniverse. #186

Merged
merged 6 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ If your system has external access, we strongly recommend installing version v0.
- Added LLM integration methods for Qwen, WenXin, Kimi, Baichuan, etc.

### Note
- Added a multimodal example agent, see the invocation details in `sample_standard_app.app.test.test_multimodal_agent.MultimodalAgentTest`.
- Added a multimodal example agent, see the invocation details in `sample_standard_app.intelligence.test.test_multimodal_agent.MultimodalAgentTest`.
- Some code optimizations and documentation updates.

## [0.0.6] - 2024-05-15
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Note - 对于版本的额外说明。
- 新增通义千问、文心一言、Kimi、百川等常用LLM接入方式

### Note
- 添加多模态样例agent调用详情见`sample_standard_app.app.test.test_multimodal_agent.MultimodalAgentTest`
- 添加多模态样例agent调用详情见`sample_standard_app.intelligence.test.test_multimodal_agent.MultimodalAgentTest`
- 部分代码优化与文档更新

## [0.0.6] - 2024-05-15
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pip install magent-ui ruamel.yaml

**One-click Run**

Run the [product_application.py](sample_standard_app/app/bootstrap/product_application.py) file located in sample_standard_app/app/bootstrap for a one-click start.
Run the [product_application.py](sample_standard_app/boostrap/platform/product_application.py) file located in sample_standard_app/bootstrap for a one-click start.

For more details, refer to [Quick Start for Product Platform](./docs/guidebook/en/10_1_1_Product%20Platform%20Quick%20Start.md) and the [Advanced Guide](./docs/guidebook/en/10_1_2_Product_Platform_Advancement_Guide.md).

Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pip install magent-ui ruamel.yaml

**一键运行**

运行sample_standard_app/app/boostrap下的[product_application.py](sample_standard_app/app/bootstrap/product_application.py)文件,一键启动。
运行sample_standard_app/boostrap下的[product_application.py](sample_standard_app/boostrap/platform/product_application.py)文件,一键启动。

更多详情参考 [产品化平台快速开始](./docs/guidebook/zh/10_1_1_产品化平台快速开始.md)
与 [产品化平台进阶指南](./10_1_2_产品化平台进阶指南.md) 。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ def _rag_route(self, query: Query, store_list: List[str]) \
agent = AgentManager().get_instance_obj(self.agent_name)
if self.llm:
agent.agent_model.profile['llm_model'] = self.llm
else:
agent.agent_model.profile['llm_model'] = {"name": "__default_instance__"}
store_info = {}
for _store in store_list:
store_info[_store] = StoreManager().get_instance_obj(_store).description
Expand Down
7 changes: 4 additions & 3 deletions agentuniverse/agent/action/tool/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ def run(self, **kwargs):

def input_check(self, kwargs: dict) -> None:
"""Check whether the input parameters of the tool contain input keys of the tool"""
for key in self.input_keys:
if key not in kwargs.keys():
raise Exception(f'{self.get_instance_code()} - The input must include key: {key}.')
if self.input_keys:
for key in self.input_keys:
if key not in kwargs.keys():
raise Exception(f'{self.get_instance_code()} - The input must include key: {key}.')

@trace_tool
def langchain_run(self, *args, callbacks=None, **kwargs):
Expand Down
33 changes: 24 additions & 9 deletions agentuniverse/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# @FileName: agent.py
"""The definition of agent paradigm."""
import json
from abc import abstractmethod
from abc import abstractmethod, ABC
from datetime import datetime
from typing import Optional

Expand All @@ -26,10 +26,9 @@
from agentuniverse.base.config.component_configer.configers.agent_configer \
import AgentConfiger
from agentuniverse.base.util.logging.logging_util import LOGGER
from agentuniverse.llm.llm import LLM


class Agent(ComponentBase):
class Agent(ComponentBase, ABC):
"""The parent class of all agent models, containing only attributes."""

agent_model: Optional[AgentModel] = None
Expand Down Expand Up @@ -61,13 +60,13 @@ def parse_input(self, input_object: InputObject, agent_input: dict) -> dict:
pass

@abstractmethod
def parse_result(self, planner_result: dict) -> dict:
"""Planner result parser.
def parse_result(self, agent_result: dict) -> dict:
"""Agent result parser.

Args:
planner_result(dict): Planner result
agent_result(dict): The raw result of the agent.
Returns:
dict: Agent result object.
dict: The parsed result of the agent
"""
pass

Expand All @@ -91,6 +90,21 @@ def run(self, **kwargs) -> OutputObject:
output_object = OutputObject(agent_result)
return output_object

@trace_agent
async def async_run(self, **kwargs) -> OutputObject:
self.input_check(kwargs)
input_object = InputObject(kwargs)

agent_input = self.pre_parse_input(input_object)

agent_result = await self.async_execute(input_object, agent_input)

agent_result = self.parse_result(agent_result)

self.output_check(agent_result)
output_object = OutputObject(agent_result)
return output_object

def execute(self, input_object: InputObject, agent_input: dict) -> dict:
"""Execute agent instance.

Expand All @@ -101,11 +115,13 @@ def execute(self, input_object: InputObject, agent_input: dict) -> dict:
Returns:
dict: planner result generated by the planner execution.
"""

planner_base: Planner = PlannerManager().get_instance_obj(self.agent_model.plan.get('planner').get('name'))
planner_result = planner_base.invoke(self.agent_model, agent_input, input_object)
return planner_result

async def async_execute(self, input_object: InputObject, agent_input: dict) -> dict:
pass

def pre_parse_input(self, input_object) -> dict:
"""Agent execution parameter pre-parsing.

Expand All @@ -121,7 +137,6 @@ def pre_parse_input(self, input_object) -> dict:
agent_input['date'] = datetime.now().strftime('%Y-%m-%d')
agent_input['session_id'] = input_object.get_data('session_id') or ''
agent_input['agent_id'] = self.agent_model.info.get('name', '')

self.parse_input(input_object, agent_input)
return agent_input

Expand Down
1 change: 1 addition & 0 deletions agentuniverse/agent/agent_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class AgentModel(BaseModel):
plan: Optional[dict] = dict()
memory: Optional[dict] = dict()
action: Optional[dict] = dict()
work_pattern: Optional[dict] = dict()

def llm_params(self) -> dict:
"""
Expand Down
147 changes: 6 additions & 141 deletions agentuniverse/agent/default/executing_agent/executing_agent.py
Original file line number Diff line number Diff line change
@@ -1,147 +1,12 @@
# !/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time : 2024/3/19 18:18
# @Author : heji
# @Email : lc299034@antgroup.com
# @FileName: executing_agent.py
"""Executing Agent module."""
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED
from typing import Optional, Any

from agentuniverse.agent.action.tool.tool_manager import ToolManager
from agentuniverse.agent.agent import Agent
from agentuniverse.agent.agent_model import AgentModel
from agentuniverse.agent.input_object import InputObject
from agentuniverse.agent.plan.planner.planner import Planner
from agentuniverse.agent.plan.planner.planner_manager import PlannerManager
from agentuniverse.base.context.framework_context_manager import FrameworkContextManager
# @Time : 2024/10/17 20:41
# @Author : wangchongshi
# @Email : wangchongshi.wcs@antgroup.com
# @FileName: executing_agent.py
from agentuniverse.agent.template.executing_agent_template import ExecutingAgentTemplate


class ExecutingAgent(Agent):
class ExecutingAgent(ExecutingAgentTemplate):
"""Executing Agent class."""

executor: Optional[Any] = ThreadPoolExecutor(max_workers=10, thread_name_prefix="executing_agent")
_context_values: Optional[dict] = {}

def input_keys(self) -> list[str]:
"""Return the input keys of the Agent."""
return ['planning_result']

def output_keys(self) -> list[str]:
"""Return the output keys of the Agent."""
return ['executing_result']

def parse_input(self, input_object: InputObject, agent_input: dict) -> dict:
"""Agent parameter parsing.

Args:
input_object (InputObject): input parameters passed by the user.
agent_input (dict): agent input preparsed by the agent.
Returns:
dict: agent input parsed from `input_object` by the user.
"""
agent_input['input'] = input_object.get_data('input')
agent_input['framework'] = input_object.get_data('planning_result').get_data('framework')
self.agent_model.profile.setdefault('prompt_version', 'default_executing_agent.cn')
return agent_input

def parse_result(self, planner_result: dict) -> dict:
"""Planner result parser.

Args:
planner_result(dict): Planner result
Returns:
dict: Agent result object.
"""
llm_result = []
executing_result = []
futures = planner_result.get('futures')
index = 1
# assemble results of the execution process.
for future in futures:
task_result = future.result()
llm_result.append(task_result)
executing_result.append({
'input': f"Question {index}: " + task_result.get('input', ''),
'output': f"Answer {index}: " + task_result.get('output', '')
})
index += 1

return {'executing_result': executing_result, 'llm_result': llm_result}

def execute(self, input_object: InputObject, agent_input: dict) -> dict:
"""Execute agent instance.

Args:
input_object (InputObject): input parameters passed by the user.
agent_input (dict): agent input parsed from `input_object` by the user.

Returns:
dict: Agent result object.
"""
self._context_values: dict = FrameworkContextManager().get_all_contexts()
framework = agent_input.get('framework', [])
futures = []
for task in framework:
# note: agent input shallow copy.
agent_input_copy: dict = dict(agent_input)
agent_input_copy['input'] = task
planner: Planner = PlannerManager().get_instance_obj(self.agent_model.plan.get('planner').get('name'))
futures.append(
self.executor.submit(self.run_in_executor, planner, self.agent_model, agent_input_copy,
self.process_intput_object(input_object, task, planner.input_key)))
wait(futures, return_when=ALL_COMPLETED)
return {'futures': futures}

def run_in_executor(self, planner: Planner, agent_model: AgentModel, planner_input: dict,
input_object: InputObject) -> dict:
"""The execution function of the thread pool.

Args:
planner(Planner): The planner object.
agent_model (AgentModel): The agent model object.
planner_input (dict): The planner input dict.
input_object (InputObject): The input parameters passed by the user.
Returns:
dict: The planner execution result.
"""
context_tokens = {}
try:
# pass the framework context into the thread.
for var_name, var_value in self._context_values.items():
token = FrameworkContextManager().set_context(var_name, var_value)
context_tokens[var_name] = token
# invoke planner
res = planner.invoke(agent_model, planner_input, input_object)
return res
finally:
# clear the framework context.
for var_name, token in context_tokens.items():
FrameworkContextManager().reset_context(var_name, token)

def process_intput_object(self, input_object: InputObject, subtask: str, planner_input_key: str) -> InputObject:
"""Process input object for the executing agent.

Args:
input_object (InputObject): input parameters passed by the user.
subtask (str): subtask to be executed.
planner_input_key (str): planner input key.

Returns:
input_object (InputObject): processed input object.
"""
# get agent toolsets.
action: dict = self.agent_model.action or dict()
tools: list = action.get('tool') or list()
# note: input object shallow copy.
input_object_copy: InputObject = InputObject(input_object.to_dict())
# wrap input_object for agent knowledge.
input_object_copy.add_data(planner_input_key, subtask)
# wrap input_object for agent toolsets.
for tool_name in tools:
tool = ToolManager().get_instance_obj(tool_name)
if tool is None:
continue
# note: only insert the first key of tool input.
input_object_copy.add_data(tool.input_keys[0], subtask)
return input_object_copy
13 changes: 5 additions & 8 deletions agentuniverse/agent/default/executing_agent/executing_agent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ info:
description: 'executing agent'
profile:
llm_model:
name: 'default_openai_llm'
model_name: 'gpt-3.5-turbo'
temperature: 0.7
plan:
planner:
name: 'executing_planner'
name: 'default_qwen_llm'
model_name: 'qwen2.5-72b-instruct'
temperature: 0.5
memory:
name: ''
action:
metadata:
type: 'AGENT'
module: 'agentuniverse.agent.default.executing_agent.executing_agent'
class: 'ExecutingAgent'
module: 'agentuniverse.agent.template.executing_agent_template'
class: 'ExecutingAgentTemplate'
61 changes: 6 additions & 55 deletions agentuniverse/agent/default/expressing_agent/expressing_agent.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,12 @@
# !/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time : 2024/3/19 19:37
# @Author : heji
# @Email : lc299034@antgroup.com

# @Time : 2024/10/17 21:08
# @Author : wangchongshi
# @Email : wangchongshi.wcs@antgroup.com
# @FileName: expressing_agent.py
"""Expressing Agent module."""
from agentuniverse.agent.agent import Agent
from agentuniverse.agent.input_object import InputObject
from agentuniverse.agent.template.expressing_agent_template import ExpressingAgentTemplate


class ExpressingAgent(Agent):
class ExpressingAgent(ExpressingAgentTemplate):
"""Expressing Agent class."""

def input_keys(self) -> list[str]:
"""Return the input keys of the Agent."""
return ['input']

def output_keys(self) -> list[str]:
"""Return the output keys of the Agent."""
return ['output']

def parse_input(self, input_object: InputObject, agent_input: dict) -> dict:
"""Agent parameter parsing.

Args:
input_object (InputObject): input parameters passed by the user.
agent_input (dict): agent input preparsed by the agent.
Returns:
dict: agent input parsed from `input_object` by the user.
"""
agent_input['input'] = input_object.get_data('input')
agent_input['background'] = self.build_background(input_object)
self.agent_model.profile.setdefault('prompt_version', 'default_expressing_agent.cn')
return agent_input

def parse_result(self, planner_result: dict) -> dict:
"""Planner result parser.

Args:
planner_result(dict): Planner result
Returns:
dict: Agent result object.
"""
return planner_result

def build_background(self, input_object: InputObject) -> str:
"""Build the background knowledge.

Args:
input_object(InputObject): agent parameter object
Returns:
str: Background knowledge.
"""
executing_result = input_object.get_data('executing_result').get_data('executing_result', [])
knowledge_list = []
for execution in executing_result:
knowledge_list.append("question:" + execution.get('input'))
knowledge_list.append("answer:" + execution.get('output'))

return '\n\n'.join(knowledge_list)
Loading