Skip to content

Commit

Permalink
Merge pull request #74 from alipay/dev_chongshi_react
Browse files Browse the repository at this point in the history
add react planner
  • Loading branch information
LandJerry authored Jun 13, 2024
2 parents e2ef121 + 05c0323 commit 430378f
Show file tree
Hide file tree
Showing 56 changed files with 1,656 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# @Author : wangchongshi
# @Email : wangchongshi.wcs@antgroup.com
# @FileName: openai_embedding.py

from typing import List, Optional, Any

from langchain_community.embeddings.openai import OpenAIEmbeddings
Expand Down
82 changes: 82 additions & 0 deletions agentuniverse/agent/action/knowledge/embedding/qwen_embedding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# !/usr/bin/env python3
# -*- coding:utf-8 -*-

# @Time : 2024/6/11 16:30
# @Author : weizjajj
# @Email : weizhongjie.wzj@antgroup.com
# @FileName: qwen_embedding.py


from typing import List, Optional

import dashscope
from langchain_community.embeddings import DashScopeEmbeddings
from pydantic import Field

from agentuniverse.agent.action.knowledge.embedding.embedding import Embedding
from agentuniverse.base.util.env_util import get_from_env


class QwenEmbedding(Embedding):
"""The openai embedding class."""

dashscope_api_key: Optional[str] = Field(default_factory=lambda: get_from_env("DASHSCOPE_API_KEY"))
"""The DashScope client."""
model: Optional[str] = "text-embedding-v1"

def get_embeddings(self, texts: List[str]) -> List[List[float]]:
"""Get the OpenAI embeddings.
Note:
The `embedding_model_name` parameter of the openai embedding class must be provided.
The `dimensions` parameter of the openai embedding class is optional.
Args:
texts (List[str]): A list of texts that need to be embedded.
Returns:
List[List[float]]: Each text gets a float list, and the result is a list of the results for each text.
Raises:
ValueError: If texts exceed the embedding model token limit or missing some required parameters.
"""
result = []
resp = dashscope.TextEmbedding.call(
model=self.model,
input=texts,
api_key=self.dashscope_api_key
)
if resp.status_code == 200:
result = [item['embedding'] for item in resp.output.get('embeddings')]
elif resp.status_code in [400, 401]:
raise ValueError(
f"status_code: {resp.status_code} \n "
f"code: {resp.code} \n message: {resp.message}"
)
else:
raise Exception(f"status_code: {resp.status_code} \n code: {resp.code} \n message: {resp.message}")
return result

async def async_get_embeddings(self, texts: List[str]) -> List[List[float]]:
"""Asynchronously get the OpenAI embeddings.
Note:
The `embedding_model_name` parameter of the openai embedding class must be provided.
The `dimensions` parameter of the openai embedding class is optional.
Args:
texts (List[str]): A list of texts that need to be embedded.
Returns:
List[List[float]]: Each text gets a float list, and the result is a list of the results for each text.
Raises:
ValueError: If texts exceed the embedding model token limit or missing some required parameters.
"""
raise NotImplementedError

def as_langchain(self) -> DashScopeEmbeddings:
"""Convert the agentUniverse(aU) openai embedding class to the langchain openai embedding class."""
return DashScopeEmbeddings(
model=self.model,
dashscope_api_key=self.dashscope_api_key,
)
20 changes: 19 additions & 1 deletion agentuniverse/agent/action/tool/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,32 @@ def input_check(self, kwargs: dict) -> None:
if key not in kwargs.keys():
raise Exception(f'{self.get_instance_code()} - The input must include key: {key}.')

def langchain_run(self, *args, callbacks=None, **kwargs):
"""The callable method that runs the tool."""
kwargs["callbacks"] = callbacks
tool_input = ToolInput(kwargs)
parse_result = self.parse_react_input(args[0])
for key in self.input_keys:
tool_input.add_data(key, parse_result[key])
return self.execute(tool_input)

def parse_react_input(self, input_str: str):
"""
parse react string to you input
you can define your own logic here by override this function
"""
return {
self.input_keys[0]: input_str
}

@abstractmethod
def execute(self, tool_input: ToolInput):
raise NotImplementedError

def as_langchain(self) -> LangchainTool:
"""Convert the agentUniverse(aU) tool class to the langchain tool class."""
return LangchainTool(name=self.name,
func=self.run,
func=self.langchain_run,
description=self.description)

def get_instance_code(self) -> str:
Expand Down
Empty file.
27 changes: 27 additions & 0 deletions agentuniverse/agent/default/nl2api_agent/default_cn_prompt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
introduction: 你是一位精通工具选择ai助手。
target: 你的目标是根据用户的问题选择出合适的工具。
instruction: |
你需要根据问题和用户提供的工具,选择其中的一个或几个工具用来回答用户提出的问题。
你必须从多个角度、维度分析用户的问题,需要根据背景和问题,决定使用哪些工具可以回答用户问题。
您可以使用以下工具:
{tools}
之前的对话:
{chat_history}
背景信息是:
{background}
回答必须是按照以下格式化的Json代码片段。
1. tools字段代表选择的几个工具的完整名称,列表格式。例如:[add, sub, mul, div]
2. thought字段代表选择工具的思考过程和原因。
```{{
"tools": list,
"thought": string
}}```
当前的问题:{input}
metadata:
type: 'PROMPT'
version: 'default_nl2api_agent.cn'
26 changes: 26 additions & 0 deletions agentuniverse/agent/default/nl2api_agent/default_en_prompt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
introduction: You are an AI assistant proficient in tool selection.
target: Your goal is to select the appropriate tools based on the user's questions.
instruction: |
Your task is to select one or several tools from those provided by the user, based on their question and the context, in order to answer the user's query.
You must analyze the user's problem from multiple angles and dimensions, taking into account the background and context of the question, and decide which tools can be used to answer the user's question.
You may use the following tools:
{tools}
Previous conversation:
{chat_history}
The background information is:
{background}
The response must follow the format below as a formatted JSON code snippet.
1. The tools field represents the full names of the selected tools in a list format, such as:[add, sub, mul, div]
2. The thought field represents the thinking process and reasons behind the selection of tools.
```{{
"tools": list,
"thought": string
}}```
Question: {input}
metadata:
type: 'PROMPT'
version: 'default_nl2api_agent.en'
45 changes: 45 additions & 0 deletions agentuniverse/agent/default/nl2api_agent/nl2api_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# !/usr/bin/env python3
# -*- coding:utf-8 -*-

# @Time : 2024/6/11 17:14
# @Author : weizjajj
# @Email : weizhongjie.wzj@antgroup.com
# @FileName: nl2api_agent.py

from agentuniverse.agent.agent import Agent
from agentuniverse.agent.input_object import InputObject


class Nl2ApiAgent(Agent):
"""ReAct 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')
self.agent_model.profile.setdefault('prompt_version', 'default_nl2api_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
17 changes: 17 additions & 0 deletions agentuniverse/agent/default/nl2api_agent/nl2api_agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
info:
name: 'nl2api_agent'
description: 'nl2api agent'
profile:
llm_model:
name: 'default_openai_llm'
model_name: 'gpt-4o'
action:
tool:
- ''
plan:
planner:
name: 'nl2api_planner'
metadata:
type: 'AGENT'
module: 'agentuniverse.agent.default.nl2api_agent.nl2api_agent'
class: 'Nl2ApiAgent'
7 changes: 7 additions & 0 deletions agentuniverse/agent/default/react_agent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# !/usr/bin/env python3
# -*- coding:utf-8 -*-

# @Time : 2024/6/4 21:21
# @Author : wangchongshi
# @Email : wangchongshi.wcs@antgroup.com
# @FileName: __init__.py.py
35 changes: 35 additions & 0 deletions agentuniverse/agent/default/react_agent/default_cn_prompt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
introduction: 你是一位精通信息分析的ai助手。
target: 你的目标是根据用户的问题以及给出的背景信息给出答案。
instruction: |
你必须优先选择使用提供的工具回答用户提出的问题,若用户没有提供工具可以根据你的通识能力解决问题。
你在回答时问题必须使用中文回答。
你必须从多个角度、维度分析用户的问题,帮助用户获取最全面的信息,需要根据背景和问题,决定搜索哪些信息可以回答问题。
请注意: 你在给出最终答案时需要从多角度给出更详细的原因,而不是一个简单的结论。
您可以使用以下工具:
{tools}
你的回答必须严格使用以下格式:
Question: 您必须回答的问题
Thought: 你应该经常想想该怎么做
Action: 要采取的行动应该是 one of [{tool_names}]
Action Input: 行动的输入
Observation: 行动的结果
... (Thought/Action/Action Input/Observation 的过程可以重复 N 次)
Thought: 我现在知道最终答案了
Final Answer: 原输入问题的最终答案
之前的对话:
{chat_history}
背景信息是:
{background}
开始!
Question: {input}
Thought:{agent_scratchpad}
metadata:
type: 'PROMPT'
version: 'default_react_agent.cn'
31 changes: 31 additions & 0 deletions agentuniverse/agent/default/react_agent/default_en_prompt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
introduction: You are an AI assistant who is proficient in information analysis.
target: Your goal is to give answers based on the user's question and the context given.
instruction: |
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Previous conversion:
{chat_history}
Background:
{background}
Begin!
Question: {input}
Thought:{agent_scratchpad}
metadata:
type: 'PROMPT'
version: 'default_react_agent.en'
44 changes: 44 additions & 0 deletions agentuniverse/agent/default/react_agent/react_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# !/usr/bin/env python3
# -*- coding:utf-8 -*-

# @Time : 2024/5/31 21:22
# @Author : wangchongshi
# @Email : wangchongshi.wcs@antgroup.com
# @FileName: react_agent.py
from agentuniverse.agent.agent import Agent
from agentuniverse.agent.input_object import InputObject


class ReActAgent(Agent):
"""ReAct 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')
self.agent_model.profile.setdefault('prompt_version', 'default_react_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
17 changes: 17 additions & 0 deletions agentuniverse/agent/default/react_agent/react_agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
info:
name: 'react_agent'
description: 'react agent'
profile:
llm_model:
name: 'default_openai_llm'
model_name: 'gpt-4o'
action:
tool:
- ''
plan:
planner:
name: 'react_planner'
metadata:
type: 'AGENT'
module: 'agentuniverse.agent.default.react_agent.react_agent'
class: 'ReActAgent'
Empty file.
Loading

0 comments on commit 430378f

Please sign in to comment.