From 0556e620a7b0d70900dc44990599fed559ae92a4 Mon Sep 17 00:00:00 2001 From: wwwttt001 Date: Wed, 9 Apr 2025 15:08:13 +0800 Subject: [PATCH] delete agent --- AgentSDK/READM.md | 102 ----- AgentSDK/agent_sdk/agentchain/base_agent.py | 120 ------ AgentSDK/agent_sdk/agentchain/react_agent.py | 222 ---------- AgentSDK/agent_sdk/agentchain/recipe_agent.py | 206 ---------- AgentSDK/agent_sdk/agentchain/router_agent.py | 64 --- .../agentchain/single_action_agent.py | 176 -------- .../agent_sdk/agentchain/tool_less_agent.py | 41 -- AgentSDK/agent_sdk/common/__init__.py | 0 AgentSDK/agent_sdk/common/constant.py | 53 --- AgentSDK/agent_sdk/executor/common.py | 45 -- .../executor/recipe_executor/executor.py | 388 ------------------ .../executor/recipe_executor/parser.py | 109 ----- .../agent_sdk/executor/recipe_executor/sop.py | 156 ------- .../executor/recipe_executor/state.py | 122 ------ .../executor/recipe_executor/task.py | 48 --- AgentSDK/agent_sdk/llms/llm.py | 13 - AgentSDK/agent_sdk/llms/openai_compatible.py | 64 --- AgentSDK/agent_sdk/prompts/pre_prompt.py | 191 --------- AgentSDK/agent_sdk/toolmngt/api.py | 94 ----- AgentSDK/agent_sdk/toolmngt/tool_manager.py | 109 ----- AgentSDK/requirements.txt | 16 - .../samples/basic_demo/test_intent_router.py | 48 --- .../samples/basic_demo/test_react_agent.py | 73 ---- .../samples/basic_demo/test_react_reflect.py | 81 ---- .../basic_demo/test_single_action_agent.py | 107 ----- .../samples/basic_demo/test_toolless_agent.py | 35 -- AgentSDK/samples/tools/__init__.py | 16 - AgentSDK/samples/tools/common.py | 25 -- AgentSDK/samples/tools/duck_search.py | 67 --- AgentSDK/samples/tools/tool_finish.py | 41 -- AgentSDK/samples/tools/tool_general_query.py | 57 --- .../tools/tool_query_accommodations.py | 73 ---- .../samples/tools/tool_query_attractions.py | 81 ---- .../samples/tools/tool_query_restaurants.py | 37 -- .../samples/tools/tool_query_transports.py | 75 ---- AgentSDK/samples/tools/tool_query_weather.py | 178 -------- AgentSDK/samples/tools/tool_summary.py | 66 --- AgentSDK/samples/tools/web_summary_api.py | 178 -------- .../front/chat_bot_release.py | 58 --- .../samples/travel_agent_demo/travelagent.py | 193 --------- 40 files changed, 3828 deletions(-) delete mode 100644 AgentSDK/READM.md delete mode 100644 AgentSDK/agent_sdk/agentchain/base_agent.py delete mode 100644 AgentSDK/agent_sdk/agentchain/react_agent.py delete mode 100644 AgentSDK/agent_sdk/agentchain/recipe_agent.py delete mode 100644 AgentSDK/agent_sdk/agentchain/router_agent.py delete mode 100644 AgentSDK/agent_sdk/agentchain/single_action_agent.py delete mode 100644 AgentSDK/agent_sdk/agentchain/tool_less_agent.py delete mode 100644 AgentSDK/agent_sdk/common/__init__.py delete mode 100644 AgentSDK/agent_sdk/common/constant.py delete mode 100644 AgentSDK/agent_sdk/executor/common.py delete mode 100644 AgentSDK/agent_sdk/executor/recipe_executor/executor.py delete mode 100644 AgentSDK/agent_sdk/executor/recipe_executor/parser.py delete mode 100644 AgentSDK/agent_sdk/executor/recipe_executor/sop.py delete mode 100644 AgentSDK/agent_sdk/executor/recipe_executor/state.py delete mode 100644 AgentSDK/agent_sdk/executor/recipe_executor/task.py delete mode 100644 AgentSDK/agent_sdk/llms/llm.py delete mode 100644 AgentSDK/agent_sdk/llms/openai_compatible.py delete mode 100644 AgentSDK/agent_sdk/prompts/pre_prompt.py delete mode 100644 AgentSDK/agent_sdk/toolmngt/api.py delete mode 100644 AgentSDK/agent_sdk/toolmngt/tool_manager.py delete mode 100644 AgentSDK/requirements.txt delete mode 100644 AgentSDK/samples/basic_demo/test_intent_router.py delete mode 100644 AgentSDK/samples/basic_demo/test_react_agent.py delete mode 100644 AgentSDK/samples/basic_demo/test_react_reflect.py delete mode 100644 AgentSDK/samples/basic_demo/test_single_action_agent.py delete mode 100644 AgentSDK/samples/basic_demo/test_toolless_agent.py delete mode 100644 AgentSDK/samples/tools/__init__.py delete mode 100644 AgentSDK/samples/tools/common.py delete mode 100644 AgentSDK/samples/tools/duck_search.py delete mode 100644 AgentSDK/samples/tools/tool_finish.py delete mode 100644 AgentSDK/samples/tools/tool_general_query.py delete mode 100644 AgentSDK/samples/tools/tool_query_accommodations.py delete mode 100644 AgentSDK/samples/tools/tool_query_attractions.py delete mode 100644 AgentSDK/samples/tools/tool_query_restaurants.py delete mode 100644 AgentSDK/samples/tools/tool_query_transports.py delete mode 100644 AgentSDK/samples/tools/tool_query_weather.py delete mode 100644 AgentSDK/samples/tools/tool_summary.py delete mode 100644 AgentSDK/samples/tools/web_summary_api.py delete mode 100644 AgentSDK/samples/travel_agent_demo/front/chat_bot_release.py delete mode 100644 AgentSDK/samples/travel_agent_demo/travelagent.py diff --git a/AgentSDK/READM.md b/AgentSDK/READM.md deleted file mode 100644 index fe38627db..000000000 --- a/AgentSDK/READM.md +++ /dev/null @@ -1,102 +0,0 @@ -# Agent SDK 基于工具调用的多模式LLM Agent框架 -## 一、功能介绍 -Agent SDK是一个基于LLMs的通用Agent框架,应用多种框架解决不同场景和复杂度的问题,并通过工具调用的方式允许LLMs与外部源进行交互来获取信息,使LLMs生成更加可靠和实际。Agent SDK通过构建DAG(Directed Acyclic Graph)的方式建立工具之间的依赖关系,通过并行执行的方式,提高多工具执行的效率,缩短Agent在复杂场景的执行时间。Agent SDK框架还在框架级别支持流式输出 -提供一套Agent实现框架,让用户可以通过框架搭建自己的Agent应用 -### 1.Router Agent -提供意图识别的能力,用户可预设意图的分类,通过Router Agent给出具体问题的分类结果,用于设别不同的问题场景。 -使用示例: -``` -cd AgentSDK -export PYTHONPATH=. -python samples/basic_demo/intent_router.py --model_name xxx --base_url xxx --api_key xxxx -``` -### 2.Recipe Agent -设置复杂问题执行的workflow,在解决具体问题时,将workflow翻译成有向无环图的的节点编排,通过并行的方式执行节点。 -适用于有相对固定workflow的复杂问题场景。 -1)通过自然语言描述复杂问题的workflow, -2)workflow中每一个步骤对应一次工具使用,并描述步骤间关系 -3)recipe Agent将按照workflow的指导完成工具调用 -4)使用模型总结工作流结果,解决复杂问题 -Recipe Agent利用用户所提供的流程指导和工具,使用LLMs生成SOP,并构建DAG图描述Steps之间的依赖关系。agent识别那些可并行的step,通过并行执行提高agent的执行效率。 -使用Recipe Agent,仅需要提供一段解决问题的SOP指导、用于提示最终答案生成的final prompt、解决问题可能使用的工具。 -示例见[travelagent.py](.travel_agenttravelagent.py)运行方式如下: -``` -cd AgentSDK -export PYTHONPATH=. -python samples/travel_agent_demo/travelagent.py --model_name xxx --base_url xxx --api_key xxxx -``` - -### 3.ReAct Agent -使用Thought、Action、Action Input、Observation的循环流程,解决复杂问题: -1)ReAct通过大模型思考并给出下一步的工具调用, -2)执行工具调用,得到工具执行结果 -3)将工具执行结果应用于下一次的模型思考 -4)循环上述过程,直到模型认为问题得到解决 -使用样例: -``` -cd AgentSDK -export PYTHONPATH=. -python samples/basic_demo/test_react_agent.py --model_name xxx --base_url xxx --api_key xxxx -``` -样例执行过程的轨迹见目录`samples/basic_demo/trajs`下对应的文件 -### 4.Single Action Agent - -通过模型反思、调用工具执行,总结工具结果的执行轨迹,完成一次复杂问题的处理。Single Action Agent使用一次工具调用帮助完成复杂问题解决 -使用示例: -``` -cd AgentSDK -export PYTHONPATH=. -python samples/basic_demo/test_single_action_agent.py --model_name xxx --base_url xxx --api_key xxxx -``` -样例执行过程的轨迹见目录`samples/basic_demo/trajs`下对应的文件 - -### 5. Tooless Agent - -未使用外部工具和知识库,通过prompt工程等技术指导大模型完成复杂问题规划,通过提供示例和指导等内容调优规划效果。 -使用示例: -``` -cd AgentSDK -export PYTHONPATH=. -python samples/basic_demo/test_toolless_agent.py --model_name xxx --base_url xxx --api_key xxxx -``` -## - -## 二、接口使用方法 -### 1.模型使用 -1.1. get_llm_backend - -| 参数 | 含义 | -| ---- | ----- | -| backend | 推理模型后台类型,当前取值 -| base_url | OpenAI客户端推理模型地址 -| api_key |OpenAI客户端api key -| llm_name | 推理模型名称 - -1.2. run -| 参数 | 含义 | 取值 -| ---- | ----- | ---| -| prompt |模型输入的prompt | 字符串或数组 -| ismessage | 是否为对话信息是否为对话信息,默认值False | bool值 - -更多模型后处理参数可参考transformers文档 - -### 2.agent接口 -2.1 初始化参数 -| 参数| 含义 | 取值类型| -| ---- | -----| --- | -| llm |模型客户端 |OpenAICompatibleLLM -| prompt |agent提示词 |字符串 -| tool_list | 工具列表 | 列表 -| max_steps | agent执行最大步数 | int -| max_token_number | 模型生成最大token数 | int -| max_context_len | 模型最大上下文token数 | int -| max_retries | 最多重启次数 | int -| example | agent推理生成示例 | 字符串 -| reflect_prompt | agent反思提示 | 字符串 -| recipe | recipe agent参考的标准工作流 | 字符串 -| intents | 分类的意图及含义 | dict -| final_prompt | 最终生成总结的提示 | 字符串 - - - - diff --git a/AgentSDK/agent_sdk/agentchain/base_agent.py b/AgentSDK/agent_sdk/agentchain/base_agent.py deleted file mode 100644 index 65b58feba..000000000 --- a/AgentSDK/agent_sdk/agentchain/base_agent.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import re -import time -from abc import ABC -from typing import Union, Generator -from loguru import logger -from pydantic import BaseModel - - -from agent_sdk.toolmngt.tool_manager import ToolManager - - -class AgentRunResult(BaseModel): - query: str - answer: str - scratchpad: str - finished: bool = False - - -class BaseAgent(ABC): - def __init__(self, llm, prompt, tool_list=None, max_steps=15, max_token_number=4096, - max_context_len=14000, max_retries=3, **kwargs): - if tool_list is None: - tool_list = [] - self.llm = llm - self.name = kwargs.get("name", "") - self.description = kwargs.get("description", "") - self.prompt = prompt - self.query = "" - self.answer = "" - self.curr_step = 0 - self.max_steps = max_steps - self.max_context_len = max_context_len - self.max_token_number = max_token_number - self.scratchpad = "" - self.max_retries = max_retries - self.tool_manager = ToolManager() - self.stop_list = ["\n"] - self.tools = "" - self.tool_names = "" - self.tool_desc_for_agent = "" - self.finished = False - self._init_tools(tool_list) - - def reset(self): - self.query = "" - self.answer = "" - self.curr_step = 0 - self.finished = False - self.scratchpad = "" - - def run(self, query: str, **kwargs): - logger.info(f"run query: {query}") - self.query = query - for key, value in kwargs.items(): - setattr(self, key, value) - - while not (self._is_halted() or self.finished): - self._step() - self.curr_step += 1 - return AgentRunResult(query=self.query, answer=self.answer, scratchpad=self.scratchpad) - - def save_agent_status(self, file_path): - pass - - def is_valid_tool(self, name: str): - tool_list = self.tool_names.split(", ") - return True if name in tool_list else False - - def _build_agent_prompt(self, **kwargs): - raise NotImplementedError - - def _init_tools(self, tool_list): - self.tool_names = ", ".join([tool.__name__ for tool in tool_list]) - for idx, tool in enumerate(tool_list): - tool_cls = tool - usage = tool_cls.build_tool_description_for_prompt() - agent_tool_desc = tool_cls.build_tool_description_for_recipe() - self.tools += f"({idx + 1}) {usage}\n" - self.tool_desc_for_agent += f"({idx + 1}) {agent_tool_desc}\n" - - def _parse_response(self, response) -> Union[str, Generator]: - return response - - def _step(self): - logger.info(f"current step {self.curr_step}") - llm_response = self._prompt_agent(self.llm) - self.answer = self._parse_response(llm_response) - - def _prompt_agent(self, llm, stream=False, **kwargs) -> Union[str, Generator]: - result = "" - for _ in range(0, self.max_retries): - try: - prompt = self._build_agent_prompt(**kwargs) - if isinstance(prompt, str): - llm_response = llm( - prompt, stop=self.stop_list, stream=stream) - else: - llm_response = llm( - prompt, stop=self.stop_list, ismessage=True, stream=stream) - if not stream: - llm_response = self._format_step(llm_response) - return llm_response - except Exception as e: - result = str(e) - logger.error(e) - time.sleep(5) - return f"send request to llm failed: {result}" - - def _is_halted(self) -> bool: - logger.debug(f"curr_step={self.curr_step}, max_steps={self.max_steps}, finished={self.finished}") - return self.curr_step >= self.max_steps - - def _format_step(self, text): - return re.sub(r'\n+', '\n', text).strip() - - def _is_correct(self): - pass \ No newline at end of file diff --git a/AgentSDK/agent_sdk/agentchain/react_agent.py b/AgentSDK/agent_sdk/agentchain/react_agent.py deleted file mode 100644 index f05f44b15..000000000 --- a/AgentSDK/agent_sdk/agentchain/react_agent.py +++ /dev/null @@ -1,222 +0,0 @@ -import copy -import json -import re -from abc import ABC -from dataclasses import dataclass -from datetime import datetime, timezone -from enum import Enum -from typing import List -from langchain.prompts import PromptTemplate -from loguru import logger - -from agent_sdk.agentchain.base_agent import BaseAgent, AgentRunResult -from agent_sdk.prompts.pre_prompt import travel_agent_prompt, reflect_prompt_value, \ - react_reflect_planner_agent_prompt, REFLECTION_HEADER -from agent_sdk.toolmngt.api import APIResponse -from agent_sdk.common.constant import save_traj_local - - -class ReflexionStrategy(Enum): - REFLEXION = "reflextion" - - -class APIResponseCache(ABC): - def __init__(self, max_size=1024) -> None: - self._max_size = max_size - self._response: dict = {} - - @property - def response(self): - return self._response - - @property - def max_size(self): - return self._max_size - - def add(self, res: APIResponse, action: str, action_input: str): - if not res.success: - logger.warning("skip failed response") - return - if len(self._response.keys()) > self.max_size: - raise Exception("too many keys") - if self._response.get(res.api_name, None) is None: - logger.info(f"add cache {res}") - else: - logger.warning(f"update cache {res}") - cache = {"name":res.api_name, "Short Description": f"{action}({action_input})", "obj":copy.deepcopy(res)} - self._response[res.api_name] = cache - - def reset(self): - self._response = {} - - def get_tools_response(self) -> list[APIResponse]: - return list(self._response.values()) - - -class ReactAgent(BaseAgent, ABC): - FINAL_ANSWER_ACTION = "Finish" - MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE = "invalid format: missing 'Action:' after 'Thought:'" - MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE = "invalid format: missing 'Action Input:' after 'Action:'" - FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = ( - "Parsing LLM output produced both a final answer and a parse-able action" - ) - - class ReactStep(Enum): - Thought = 1 - Action = 2 - Observation = 3 - - @dataclass - class ActionResult: - action: str - action_input: str - raw_response: str - - def __init__(self, llm, example="", prompt: PromptTemplate = travel_agent_prompt, - **kwargs) -> None: - super().__init__(llm, prompt, **kwargs) - self.stop_list = ["\nObservation"] - self.example = example - self.api_response_cache = APIResponseCache() - - @staticmethod - def _parse_action(text) -> ActionResult: - regex = r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)" - - action_match = re.search(regex, text, re.DOTALL) - if action_match: - action = action_match.group(1).strip() - action_input = action_match.group(2) - tool_input = action_input.strip(" ") - tool_input = tool_input.strip('"') - return ReactAgent.ActionResult(action=action, action_input=tool_input, raw_response=text) - - if not re.search(r"Action\s*\d*\s*:[\s]*(.*?)", text, re.DOTALL): - return ReactAgent.ActionResult(action="ParserException", - action_input=ReactAgent.MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE, - raw_response=text) - elif not re.search(r"[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)", text, re.DOTALL): - return ReactAgent.ActionResult(action="ParserException", - action_input=ReactAgent.MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE, - raw_response=text) - else: - raise Exception(f"Could not parse LLM output: `{text}`") - - def reset(self): - super().reset() - self.api_response_cache.reset() - - def save_agent_status(self, file_path: str): - try: - save_traj_local(self.query, self.scratchpad, file_path) - logger.success(f"save {self.__class__.__name__} status done") - except Exception as e: - logger.error(f"prompt = {self.prompt}") - logger.error(e) - - def _add_scratchpad(self, step: ReactStep, message: str): - if self.scratchpad != "": - self.scratchpad += "\n" - - if step == ReactAgent.ReactStep.Observation: - message = f"Observation{self.curr_step + 1}: " + message - - message_lines = message.splitlines() - for idx, message_line in enumerate(message_lines): - if message_line.startswith(step.name): - self.scratchpad += "\n".join(x for x in message_lines[idx:]) - logger.debug(f"self.scratchpad = {self.scratchpad}") - return - self.scratchpad += step.name + ": " + message - - def _parse_response(self, response) -> str: - self._add_scratchpad(ReactAgent.ReactStep.Thought, response) - - action_rst = self._parse_action(response) - if action_rst.action == ReactAgent.FINAL_ANSWER_ACTION: - self.finished = True - return action_rst.action_input - if action_rst.action == "ParserException": - logger.info(f"Observation {self.curr_step}: {action_rst.action_input}") - self._add_scratchpad(ReactAgent.ReactStep.Observation, action_rst.action_input) - return action_rst.action_input - if self.is_valid_tool(action_rst.action): - resp = self.tool_manager.api_call(action_rst.action, action_rst.action_input, llm=self.llm) - self.api_response_cache.add(resp, action=action_rst.action, action_input=action_rst.action_input) - output_str = json.dumps(resp.output, ensure_ascii=False, indent=4) - result = output_str - if resp.finished: - self.answer = output_str - self.finished = resp.finished - else: - result = f"{action_rst.action} is not a valid tool, try one of [{self.tool_names}]." - self._add_scratchpad(ReactAgent.ReactStep.Observation, result) - return result - - def _build_agent_prompt(self, **kwargs): - pad = self.scratchpad - if pad.startswith("Thought: "): - pad = pad[len("Thought: "):] - return self.prompt.format( - tools=self.tools, - times=self.max_steps - 1, - tools_name=self.tool_names, - query=self.query, - example=self.example, - scratchpad=pad + "\nThought:" if pad != "" else pad) - - -class ReactReflectAgent(ReactAgent, ABC): - def __init__(self, reflect_llm, react_llm, example="", reflect_prompt: PromptTemplate = reflect_prompt_value, - prompt: PromptTemplate = react_reflect_planner_agent_prompt, - **kwargs) -> None: - super().__init__(llm=react_llm, example=example, prompt=prompt, **kwargs) - self.reflect_llm = reflect_llm - self.reflect_prompt = reflect_prompt - self.reflections: List[str] = [] - self.reflections_str: str = '' - - def run(self, query: str, **kwargs) -> AgentRunResult: - logger.debug(f"run query {query}") - self.query = query - for key, value in kwargs.items(): - setattr(self, key, value) - - while not (self._is_halted() or self.finished): - self._step() - self.curr_step += 1 - if not self.finished: - self._reflect(ReflexionStrategy.REFLEXION) - - return AgentRunResult(query=self.query, answer=self.answer, scratchpad=self.scratchpad, finished=self.finished) - - def _reflect(self, strategy: ReflexionStrategy) -> None: - if strategy == ReflexionStrategy.REFLEXION: - self.reflections += [self._prompt_agent(self.reflect_llm, type="reflect")] - self.reflections_str = self._format_reflections(self.reflections) - else: - raise NotImplementedError(f'Unknown reflection strategy: {strategy}') - logger.debug(self.reflections_str) - - def _build_agent_prompt(self, **kwargs) -> str: - if kwargs.get('type') == "reflect": - return self.reflect_prompt.format( - query=self.query, - text=self.text, - tools=self.tools, - scratchpad=self.scratchpad) - - return self.prompt.format( - query=self.query, - tools=self.tools, - times=self.max_steps - 1, - example=self.example, - scratchpad=self.scratchpad, - reflections=self.reflections_str) - - def _format_reflections(self, reflections: List[str], - header: str = REFLECTION_HEADER) -> str: - if not reflections: - return '' - else: - return header + 'Reflections:\n- ' + '\n- '.join([r.strip() for r in reflections]) \ No newline at end of file diff --git a/AgentSDK/agent_sdk/agentchain/recipe_agent.py b/AgentSDK/agent_sdk/agentchain/recipe_agent.py deleted file mode 100644 index c12a1d6f0..000000000 --- a/AgentSDK/agent_sdk/agentchain/recipe_agent.py +++ /dev/null @@ -1,206 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from abc import ABC -from typing import Generator, Union, List -from loguru import logger -import tiktoken - -from agent_sdk.executor.recipe_executor.executor import AgentExecutor -from agent_sdk.agentchain.base_agent import BaseAgent, AgentRunResult - - -class RecipeAgent(BaseAgent, ABC): - def __init__(self, llm, tool_list=None, recipe: str = "", max_steps=15, final_prompt="", - max_token_number=4096, max_context_len=14000, max_retries=3, **kwargs): - super().__init__(llm, None, tool_list, max_steps, - max_token_number, max_context_len, max_retries, **kwargs) - self.recipe = recipe - self.reflect = False - self.reflection_result = "" - self.recipe_output = "" - self.final_prompt = final_prompt - self.encoding = tiktoken.get_encoding("gpt2") - self.agent_executor = AgentExecutor(tool_manager=self.tool_manager) - - def run(self, query, stream=False, *args, **kwargs): - self.query = query - - for key, value in kwargs.items(): - setattr(self, key, value) - cur_step = 1 - self.finished = False - while not self.finished and cur_step < self.max_steps: - self.step() - cur_step += 1 - if len(self.recipe_output) == 0: - prompt = f"你的角色设定如此:{self.description},请利用你自己的知识回答用户的问题,用户的问题如下:{query}" - else: - try: - prompt = self._build_final_prompt(self.recipe_output) - except Exception as e: - logger.error(e) - self.answer = self.recipe_output - else: - # prompt 是str类型 - self.answer = self.llm( - prompt, temperature=0.1, stop=[], stream=stream) - if stream: - return self.answer - else: - return AgentRunResult(query=self.query, answer=self.answer, scratchpad=self.scratchpad) - - def step(self): - tools_usage = self.get_tool_usage() - if not self.reflect: - prompt = INIT_PROMPT.format( - tools_usage=tools_usage, suggestion=SUGGESTION, pesuede_code=self.recipe, question=self.query) - translation_result = self.llm(prompt=prompt, temperature=0.1, stop=[], max_tokens=self.max_token_number) - else: - prompt = INIT_PROMPT.format(tools_usage=tools_usage, - suggestion=SUGGESTION, - translation_result=translation_result, - reflection=self.reflection_result, - pesuede_code=self.recipe, - question=self.query) - translation_result = self.llm(prompt=prompt, temperature=0.1, stop=[ - ], max_tokens=self.max_token_number) - - valid, _ = self.agent_executor.check_valid(translation_result) - if not valid: - self.finished = True - self.recipe_output = "" - self.answer = "" - return "" - else: - result = self._execute_recipe(translation_result, self.llm) - self.finished = True - self.recipe_output = result - self.answer = result - return result - - def get_tool_usage(self): - return self.tool_desc_for_agent - - def _build_final_prompt(self, text, **kwargs) -> Union[List, str]: - if self.final_prompt is None: - raise Exception("final prompt is None error") - else: - max_input_token_num = self.max_token_number - input_token_len = len(self.encoding.encode(text)) - prompt_len = len(self.encoding.encode(self.final_prompt)) - clip_text_index = int( - len(text) * (max_input_token_num - prompt_len) / input_token_len) - clip_text = text[:clip_text_index] - pmt = self.final_prompt.format(text=clip_text) - return pmt - - def _execute_recipe(self, recipefile, llm): - answer = self.agent_executor.async_run(recipefile, llm) - return answer - - def _build_agent_prompt(self, **kwargs): - return self.prompt.format(query=self.query, text=self.text) - - def _parse_response(self, response) -> str | Generator: - return response - - -INIT_PROMPT = """你是一个精通伪代码和YAML格式的高级程序员,擅长将一段文字描述的伪代码翻译成一个以YAML格式表示的有向无环图,有向无环图中的节点代表伪代码中的一个动作, -你只能选择**工具**段落中的工具,有向无环图的边代表动作之间的依赖关系。 -伪代码位于**伪代码**段落,可以使用的工具位于**工具**段落。 -你的任务是仔细阅读**伪代码**段落,将其翻译成一个以YAML格式表示的有向无环图,每一个步骤只能对应一个工具调用 -**工具** -{tools_usage} -**伪代码** -{pesuede_code} - -**输出格式** -【格式】: -- name:query_flight -step:1 -description:XX -toolname: XX -input: {{}} -dependency: [] -- name:XX -step:2, -description:XX -toolname: XX -input: - flight_number: ${{query_flight.flight_number}} -dependency: - - query_flight -... - -【参数】: -name:步骤名称,应该简洁明了,以英文输出,如有多个单词,单词间以'_'连接 -step:步骤序号 -description:步骤行为描述,清晰的描述本节点的功能,以中文输出 -toolname:该步骤使用的工具名称,来源于**工具**段落中的工具的名称,你只能使用**工具**中提供的工具名,不允许自行添加工具 -input: 本步骤对应工具的输入参数列表,每个参数由本工具的参数名称和输入值组成,输入值优先使用依赖节点的输出参数,需要满足二者参数含义的一致性,请不要自行给输入赋值 -dependency: 改步骤依赖其他哪些步骤,字段内容为list,list中每个元素是依赖步骤的step值 - -在你翻译时,重点关注满足如下几个方面要求: -{suggestion} - -用户输入的问题: -{question} - -请注意:翻译结果不要通过//或者 /**/ 添加任何解释或注释,且严格遵循YAML格式 -请开始:""" - -REFLECT_PROMPT = """你是一个精通伪代码和YAML格式的高级程序员,擅长将一段文字描述的伪代码,翻译成一个以YAML格式表示的有向无环图,有向无环图中的节点代表伪代码中的一个动作, -尽量使用**工具**段落中的工具,有向无环图的边代表动作之间的依赖关系。 -伪代码位于**伪代码**段落,可以使用的工具位于**工具**段落。 -你的任务是仔细分析**伪代码**段落,以及一个已经翻译出来的YAML格式表示的位于**翻译结果**段落的有向无环图,然后给出结构化的,严肃的,有价值的改进建议, -使得翻译更加准确的代表伪代码的逻辑。 -**工具** -{tools_usage} -**伪代码** -{pesuede_code} -**翻译结果** -{translation} -在你写改进建议时,重点关注是否有可能改进如下几个方面: -{suggestion} -请按列表方式写出明确的、有价值的、结构化的改进建议. 每条建议应该写清楚属于哪个节点,哪个属性字段。仅写出改进建议,不用写出其他额外的逻辑或者解释性的内容。 - -用户输入的问题: -{question} - -请开始: -""" - -IMPROVE_PROMPT = """你是一个精通伪代码和YAML格式的高级程序员,擅长将一段文字描述的伪代码,翻译成一个以YAML格式表示的有向无环图,有向无环图中的节点代表伪代码中的一个动作, -尽量使用**工具**段落中的工具,有向无环图的边代表动作之间的依赖关系。 -伪代码位于**伪代码**段落,可以使用的工具位于**工具**段落。 -你的任务是仔细分析**伪代码**段落,以及一个已经翻译出来的YAML格式表示的位于**翻译结果**段落的有向无环图,根据**改进建议**段落的改进意见,编辑翻译结果, -使得翻译更加准确匹配伪代码的逻辑。 -**工具** -{tools_usage} -**伪代码** -{pesuede_code} -**翻译结果** -{translation} -**改进建议** -{reflection} -在你改写翻译结果时,重点关注是否有可能改进如下几个方面: -{suggestion} -仅写出最新的翻译结果,不用写出其他额外的逻辑或者解释性的内容。 -用户输入的问题: -{question} - -请开始: -""" - -SUGGESTION = """1. 翻译结果请严格按照YAML格式输出,不要添加任何注释 -2. 翻译出的节点数量是否严格匹配伪代码中的步骤数量,每个步骤只能匹配一个能完成其需求的工具, -3. 不能将伪代码中的一个步骤划分翻译成多个节点,例如不要出现1.1,1.2等step -4. 每个节点的toolname字段必须准确,且是在**工具**段落中存在的 -5. 每个节点的dependency字段必须准确,能匹配伪代码中的依赖关系逻辑,dependency的节点必须是存在的节点 -6. 每个节点的input字段必须有参数,每个节点的input字段名务必准确,必须是工具有的参数名,input字段中的每个参数输入值必须且只能是具体值或者依赖节点的工具输出参数, -不要使用python代码或者其他表达式, -7. 每个节点的input字段的每个参数值,优先使用依赖节点的工具输出参数,若无法通过依赖得到可以从问题中提取,若存在多个答案,请使用加号+隔开,若仍无法得到参数,统一使用【无】 -8. 【伪代码】的步骤:一个步骤只能翻译成一个对应的节点 -9. 生成的内容请严格遵循YAML的语法和格式 -""" diff --git a/AgentSDK/agent_sdk/agentchain/router_agent.py b/AgentSDK/agent_sdk/agentchain/router_agent.py deleted file mode 100644 index 7e7d8d190..000000000 --- a/AgentSDK/agent_sdk/agentchain/router_agent.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from abc import ABC -import time - -from langchain.prompts import PromptTemplate - -from .base_agent import AgentRunResult -from .tool_less_agent import ToollessAgent - - -INTENT_PROMPT = """ -给定用户问题, 请根据用户问题代表的意图进行分类,可分类为 {intents}. - -每种分类的含义如下: -{intent_meanings} -请使用一个单词回答. - -用户问题: -{query} - -分类结果:""" - -INTENT_AGENT_PROMPT = PromptTemplate( - input_variables=["intents", "intent_meanings", "query"], - template=INTENT_PROMPT -) - - -class RouterAgent(ToollessAgent, ABC): - def __init__(self, llm, intents: dict, **kwargs): - super().__init__(llm, INTENT_AGENT_PROMPT, **kwargs) - self.intents = intents - self.query = "" - - def _build_agent_prompt(self, **kwargs): - intent_str, intents_meaning_str = "", "" - index = 1 - for intent_key, intent_des in self.intents.items(): - intent_str = intent_str + intent_key + ',' - intents_meaning_str = intents_meaning_str + \ - f"{index}, " + intent_key + ": " + intent_des + "\n" - index += 1 - - intent_str = intent_str.strip(",") - return self.prompt.format( - intents=intent_str, - intent_meanings=intents_meaning_str, - query=self.query - ) - - # def _prompt_agent(self, llm, **kwargs) -> str: - # result = "" - # for _ in range(0, self.max_retries): - # try: - # prompt = self._build_agent_prompt() - # llm_response = self._format_step( - # llm(prompt, temperature=0.1, stop=self.stop_list)) - # return llm_response - # except Exception as e: - # result = str(e) - # time.sleep(5) - # return f"send request to llm failed: {result}" diff --git a/AgentSDK/agent_sdk/agentchain/single_action_agent.py b/AgentSDK/agent_sdk/agentchain/single_action_agent.py deleted file mode 100644 index b1959c546..000000000 --- a/AgentSDK/agent_sdk/agentchain/single_action_agent.py +++ /dev/null @@ -1,176 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import json -import re -import time -from datetime import datetime, timezone -from loguru import logger - -from agent_sdk.agentchain.base_agent import BaseAgent, AgentRunResult -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager -from agent_sdk.prompts.pre_prompt import single_action_agent_prompt, single_action_final_prompt -from agent_sdk.common.constant import save_traj_local - - -class SingleActionAgent(BaseAgent): - MISSING_ACTION_ERROR = "Invalid Format: Missing 'Action:' after 'Thought:'" - MISSING_ACTION_INPUT_ERROR = "Invalid Format: Missing 'Action Input:' after 'Action:'" - - def __init__(self, - llm, - tool_list, - prompt=single_action_agent_prompt, - final_prompt=single_action_final_prompt, - **kwargs - ) -> None: - - if tool_list is None: - tool_list = [SingleFinish] - else: - tool_list.append(SingleFinish) - - super().__init__(llm, prompt, tool_list, **kwargs) - self.stop_list = ["\nObservation"] - self.final_prompt = final_prompt - self.tool_output = "" - - def run(self, query, stream=False, *args, **kwargs): - logger.info(f"run query: {query}") - self.query = query - - for key, value in kwargs.items(): - setattr(self, key, value) - self.finished = False - - while not self.finished and self.curr_step < self.max_steps: - self.step() - self.curr_step += 1 - - prompt = self._build_final_prompt(self.tool_output) - if prompt is None: - self.answer = self.tool_output - elif isinstance(prompt, str): - self.answer = self.llm( - prompt, temperature=0.1, stop=[], stream=stream) - logger.info(f"Summarize={self.answer}") - else: - self.answer = self.llm(prompt, temperature=0.1, stop=[ - ], ismessage=True, stream=stream) - - if stream: - return self.answer - else: - return AgentRunResult(query=self.query, answer=self.answer, scratchpad=self.scratchpad, - finished=self.finished) - - def step(self) -> None: - llm_response = self._prompt_agent(self.llm) - self.scratchpad += llm_response - action_type, argument = self._parse_action(llm_response) - if action_type == "ParserException": - result = argument - else: - if self.is_valid_tool(action_type): - tool_response = self.tool_manager.api_call(action_type, argument, llm=self.llm) - output_str = json.dumps(tool_response.output, ensure_ascii=False, indent=4) - self.tool_output = output_str - if tool_response.success: - self.finished = True - result = output_str - - else: - result = f"{action_type} 不是一个有效的工具,可以使用工具的列表为[{self.tool_names}]" - logger.info(f"Observation:\n{result}") - self.scratchpad += f"\nObservation: {result}\n" - - def save_agent_status(self, file_path): - try: - save_traj_local(self.query, self.scratchpad, file_path) - except Exception as e: - logger.error(f"agent_prompt = {self.prompt}") - logger.error(e) - - def _build_agent_prompt(self, **kwargs): - return self.prompt.format( - tools=self.tools, - tools_name=self.tool_names, - query=self.query, - scratchpad=self.scratchpad - ) - - def _build_final_prompt(self, tool_output, **kwargs): - return self.final_prompt.format( - query=self.query, - answer=tool_output - ) - - def _prompt_agent(self, llm, **kwargs): - result = "" - for _ in range(0, self.max_retries): - try: - prompt = self._build_agent_prompt() - llm_response = self._format_step( - llm(prompt, temperature=0.1, stop=self.stop_list)) - return llm_response - except Exception as e: - result = str(e) - logger.error(e) - time.sleep(5) - return f"send request to llm failed: {result}" - - def _parse_action(self, text: str): - action_match = re.search(r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)", text, - re.DOTALL) - - if action_match: - action = action_match.group(1).strip() - action_input = action_match.group(2) - tool_input = action_input.strip(" ") - tool_input = tool_input.strip('"') - return action, tool_input - - if not re.search(r"Action\s*\d*\s*:[\s]*(.*?)", text, re.DOTALL): - return "ParserException", self.MISSING_ACTION_ERROR - elif not re.search(r"[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)", text, re.DOTALL): - return "ParserException", self.MISSING_ACTION_INPUT_ERROR - else: - raise Exception(f"Could not parse LLM output: {text}") - - -@ToolManager.register_tool() -class SingleFinish(API): - description = "在无法使用其它工具完成任务请求,如'工具'段落没有合适的工具或用户请求无足够信息提取参数等情况,可以使用本工具结束任务." - input_parameters = { - 'answer': {'type': 'str', 'description': "结束任务的答案"} - } - - output_parameters = {} - - example = ( - """ - { - "answer": "Indicate the final answer for the task." - }""" - ) - - def __init__(self) -> None: - super().__init__() - - def gen_few_shot(self, thought: str, param: str, idx) -> str: - return (f"Thought: {thought}\n" - f"Action: {self.__class__.__name__}\n" - f"Action Input: {param}\n") - - def check_api_call_correctness(self, response, groundtruth) -> bool: - ex = response.get("exception") - - if ex is not None: - return False - else: - return True - - def call(self, input_parameter: dict, **kwargs) -> dict: - answer = input_parameter.get('answer', "") - return self.make_response(input_parameter, answer, True, True) diff --git a/AgentSDK/agent_sdk/agentchain/tool_less_agent.py b/AgentSDK/agent_sdk/agentchain/tool_less_agent.py deleted file mode 100644 index 3546be49d..000000000 --- a/AgentSDK/agent_sdk/agentchain/tool_less_agent.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from abc import ABC -from typing import Generator -from langchain.prompts import PromptTemplate - -from agent_sdk.agentchain.base_agent import BaseAgent, AgentRunResult -from agent_sdk.prompts.pre_prompt import planner_agent_prompt - - -class ToollessAgent(BaseAgent, ABC): - def __init__(self, llm, prompt: PromptTemplate = planner_agent_prompt, **kwargs): - super().__init__(llm, prompt, max_steps=1, **kwargs) - self.stop_list = [] - - def run(self, query: str, stream=False, **kwargs): - self.query = query - for key, value in kwargs.items(): - setattr(self, key, value) - - llm_response = self._prompt_agent(self.llm, stream=stream) - self.curr_step += 1 - self.finished = True - - if stream: - self.answer = llm_response - return self.answer - else: - self.answer = self._parse_response(llm_response) - return AgentRunResult(query=self.query, answer=self.answer, - scratchpad=self.scratchpad, finished=self.finished) - - def _build_agent_prompt(self, **kwargs): - return self.prompt.format( - query=self.query, - text=self.text - ) - - def _parse_response(self, response) -> str | Generator: - return response diff --git a/AgentSDK/agent_sdk/common/__init__.py b/AgentSDK/agent_sdk/common/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/AgentSDK/agent_sdk/common/constant.py b/AgentSDK/agent_sdk/common/constant.py deleted file mode 100644 index 19993ab0e..000000000 --- a/AgentSDK/agent_sdk/common/constant.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import enum -import os -import stat -from datetime import datetime, timezone -from loguru import logger - -from pydantic import BaseModel - - -class PlanStrategyType(enum.Enum): - REACT = 1 - RESEARCH = 2 - COT = 3 - EMPTY = 4 - SOP = 5 - DHP = 6 - - -class AgentRunStatus(BaseModel): - success_cnt: int = 0 - total_cnt: int = 0 - - def __str__(self): - rate = round((self.success_cnt / self.total_cnt) * 100, 2) - return str(rate) + f"% {self.success_cnt}/{self.total_cnt}" - - -THOUGHT = "Thought" -ACTION = "Action" -ACTION_INPUT = "Action Input" -OBSERVATION = "Observation" - - -def save_traj_local(query, traj, path): - directory = os.path.dirname(path) - if not os.path.exists(directory): - try: - os.makedirs(directory) - new_permissions = 0o755 - os.chmod(directory, new_permissions) - except Exception as e: - logger.error(f"make dir error, {e}") - flag = os.O_WRONLY | os.O_CREAT | os.O_APPEND - mode = stat.S_IWUSR | stat.S_IRUSR - with os.fdopen(os.open(path, flags=flag, mode=mode), "a") as fout: - fout.write("****************TASK START*******************\n") - fout.write(f"task: {query}\n") - fout.write(f"trajectory:\n{traj}\n") - fout.write(f"created_at {str(datetime.now(tz=timezone.utc))}\n") - fout.write("*****************TASK END*******************\n\n\n") \ No newline at end of file diff --git a/AgentSDK/agent_sdk/executor/common.py b/AgentSDK/agent_sdk/executor/common.py deleted file mode 100644 index 2780bd79f..000000000 --- a/AgentSDK/agent_sdk/executor/common.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import enum - - -class ErrorType(enum.Enum): - INPUT_NOT_STR = 0 - FILE_TYPE_ERROR = 1 - FILE_READ_ERROR = 2 - NO_DICT_ERROR = 3 - YAML_LOAD_ERROR = 3 - INVALID_TOOL_ERROR = 4 - NO_NAME_ERROR = 5 - INVALID_INPUT_PARAM = 6 - INVALID_OUPUT_PARAM = 7 - NODE_CONFILCT_ERROR = 8 - INVALID_DEPENDENCY = 9 - NODE_NOT_DEPENDENT = 10 - - -ERROR_MAP = { - ErrorType.INPUT_NOT_STR: "Invalid arguements type: the content need a str", - ErrorType.FILE_TYPE_ERROR: "The type of file {filename} is not supported, only json or yaml is fine", - ErrorType.FILE_READ_ERROR: "Failed to read the file {filename}: Error message: {error}", - ErrorType.NO_DICT_ERROR: "The content cannot be converted to dict format.", - ErrorType.YAML_LOAD_ERROR: "Loading content with YAML error: {error}", - ErrorType.INVALID_TOOL_ERROR: "The tool {toolname} doesn't exist.", - ErrorType.NO_NAME_ERROR: "some nodes did not assign a value to a necessary parameter [name] and [toolname].", - ErrorType.INVALID_INPUT_PARAM: "Additional parameters {params} were input during the calling tool {tool}.", - ErrorType.INVALID_OUPUT_PARAM: "These parameters {params} do not exist in the output of the node {node}.", - ErrorType.NODE_CONFILCT_ERROR: "the Node name {name} is duplicated.", - ErrorType.INVALID_DEPENDENCY: "The current node depends on node {node} that does not exist in the plan.", - ErrorType.NODE_NOT_DEPENDENT: "The output of node {node} is being used, but it is not within the dependency" - -} - - -class PlanStrategyType(enum.Enum): - REACT = 1 - RESEARCH = 2 - COT = 3 - EMPTY = 4 - SOP = 5 - DHP = 6 \ No newline at end of file diff --git a/AgentSDK/agent_sdk/executor/recipe_executor/executor.py b/AgentSDK/agent_sdk/executor/recipe_executor/executor.py deleted file mode 100644 index ee8ca765b..000000000 --- a/AgentSDK/agent_sdk/executor/recipe_executor/executor.py +++ /dev/null @@ -1,388 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from typing import Dict -import asyncio -import time -from datetime import datetime, timezone -import re -import traceback -import json -import os -import threading -from concurrent.futures import ThreadPoolExecutor, as_completed -import yaml -from loguru import logger - -from agent_sdk.executor.recipe_executor.state import ExecutorState, WorkSpace -from agent_sdk.executor.recipe_executor.parser import Node, Parser -from agent_sdk.executor.recipe_executor.sop import SopHandler -from agent_sdk.executor.common import ERROR_MAP, ErrorType, PlanStrategyType - -ALL_PLAN_STRATEGIES = { - PlanStrategyType.SOP.value: "sop" -} - - -class AgentExecutor(): - tasks_done: Dict[str, asyncio.Event] - - def __init__( - self, - tool_manager, - plan_strategy_type: PlanStrategyType = None, - ): - self.plan_strategy_type = plan_strategy_type or PlanStrategyType.SOP - self.plan_strategy = ALL_PLAN_STRATEGIES.get( - self.plan_strategy_type.value) - self.parser = Parser() - self.tool_manager = tool_manager - self.operation_handler = SopHandler(tool_manager) - self.lock = threading.Lock() - - @staticmethod - def parser_output(output, cur_operation): - content = output - if isinstance(output, str): - try: - content = json.loads(output) - except json.JSONDecodeError: - content = output - else: - content = output - cur_operation.output = content - history = { - "operation_name": cur_operation.name, - "dependecy": cur_operation.dependency, - "input": cur_operation.input, - "output": content, - "activate": cur_operation.activate, # 激活该分支的条件 - "interaction_history": "", # discuss - "variable_space": "" # discuss - } - cur_operation.history = history - return content, history - - @staticmethod - def update_history(result, executor_state): - name = result['action'] - parsed_out = result['output'] - history = result['history'] - executor_state.workspace.variable_space[name] = parsed_out - executor_state.workspace.update(history) - # 执行完毕,从set中移除 - executor_state.remaining_tasks.remove(name) - executor_state.activated_tasks.remove(name) - executor_state.done_tasks.add(name) - executor_state.workspace.update_last_operation(name) - - @staticmethod - def process_action_args(action: Node, executor_state): - # 处理参数依赖的替换 - history_names = [history["operation_name"] - for history in executor_state.workspace.operation_history] - # if any of a node's previous actions has no input - # ie. this node's previous operation name not in history, this node cannot execute, so pass - if not set(action.dependency).issubset(set(history_names)): - raise Exception("dependency action hasn't executed") - node = action - if node.activate: - # 用表达引擎,执行看是否满足准入要求 - node.activate = sub_placeholder( - node.activate, executor_state.workspace) - bool_activate = expression_engine(node.activate) - - if not bool_activate: - raise Exception("action hasn't activate") - - if action.input: - for key, value in action.input.items(): - if value is None: - logger.warning('the value of [%s] is None', key) - else: - value = sub_placeholder(value, executor_state.workspace) - action.input[key] = value - return action - - @staticmethod - def get_leaves_result(state): - summary = "" - for name in state.leaves_tasks: - content = state.sop_graph[name].description - res = state.workspace.variable_space.get(name, "") - summary += f"content: {content}\n" - summary += f"result: {json.dumps(res, ensure_ascii=False)}\n" - return summary - - @staticmethod - def get_leaves_node(nodes): - leaves = [] - for key, _ in nodes.items(): - is_leaf = True - for _, other_node in nodes.items(): - if key in other_node.dependency: - is_leaf = False - break - if is_leaf: - leaves.append(key) - return leaves - - def get_executable_actions(self, executor_state): - done_actions = executor_state.done_tasks - graph = executor_state.sop_graph - activated = executor_state.activated_tasks - independent_actions = [] - for task_name in executor_state.remaining_tasks: - if all(d in done_actions for d in graph[task_name].dependency): - independent_actions.append(task_name) - executable_actions = [] - pending_actions = [] - for action in independent_actions: - node = graph[action] - if node.activate: - node.activate = sub_placeholder( - node.activate, executor_state.workspace) - if not node.activate: - break - bool_activate = expression_engine(node.activate) - - if not bool_activate: - executor_state.remaining_tasks.remove(action) - executor_state.done_tasks.add(action) - continue - executable_actions.append(action) - - if action in activated: - continue - try: - node = self.process_action_args(node, executor_state,) - except Exception as e: - node = None - if node is not None: - pending_actions.append(node) - else: - continue - return pending_actions - - def run_task(self, action, executor_state, llm): - graph = executor_state.sop_graph - sop_handler = self.operation_handler - mommt = datetime.now(tz=timezone.utc) - logger.debug(f'{action.name} start: {mommt.strftime("%Y-%m-%d %H:%M:%S")}') - output = sop_handler.invoke(action, llm=llm) - - parsed_out, history = self.parser_output(output, action) - graph[action.name].output = parsed_out - - res = { - "action": action.name, - "output": parsed_out, - "history": history - } - - mommt = datetime.now(tz=timezone.utc) - logger.debug(f'step {action.step}. action: {action.name} has finished. {mommt.strftime("%Y-%m-%d %H:%M:%S")}') - return res - - - - def async_run(self, content, llm): - executor_state = self.init_state(content) - executor = ThreadPoolExecutor(max_workers=5) - with ThreadPoolExecutor(max_workers=5) as executor: - while executor_state.remaining_tasks: # 活跃的 - executable_tasks = self.get_executable_actions(executor_state) - thread_list = [] - for task in executable_tasks: - th = executor.submit(self.run_task, task, executor_state, llm) - thread_list.append(th) - executor_state.activated_tasks.add(task.name) - for future in as_completed(thread_list): - with self.lock: - self.update_history(future.result(), executor_state) - return self.get_leaves_result(executor_state) - - # 而此处的写法不关注next,关注dependency - def run(self, content): - executor_state = self.init_state(content) - - sop_handler = self.operation_handler - activate_actions = self.get_executable_actions(executor_state) - executor_state.activate_actions = activate_actions - while executor_state.activate_actions: # 活跃的 - - cur_operation = executor_state.activate_actions.pop(0) - # starts from right - logger.error("start run step %d, action [%s]", cur_operation.step, cur_operation.name) - output = sop_handler.invoke(cur_operation) - parsed_out, history = self.parser_output(output, cur_operation) - executor_state.workspace.update(history) - # 保存结果 - executor_state.workspace.variable_space[cur_operation.name] = parsed_out - executor_state.workspace.update_last_operation( - cur_operation.name) - # 更新状态 - executor_state.remaining_tasks.remove(cur_operation.name) - executor_state.done_tasks.add(cur_operation.name) - executable_actions = self.get_executable_actions(executor_state) - executor_state.activate_actions = (executable_actions) - - return executor_state.workspace.get_last_result() - - def check_valid(self, content): - if not isinstance(content, str): - return False, ERROR_MAP[ErrorType.INPUT_NOT_STR] - absolute_path = os.getcwd() - if os.path.isfile(os.path.join(absolute_path, content)): - raw_dict, err = fetch_file_content(content) - else: - raw_dict, err = fetch_str_content(content) - if len(err) > 0: - return False, err - err = self.operation_handler.check_valid_sop(raw_dict) - return len(err) == 0, err - - def init_state(self, content): - if not isinstance(content, str): - raise TypeError("Invalid arguements type: the content need a str") - absolute_path = os.getcwd() - if os.path.isfile(os.path.join(absolute_path, content)): - raw_dict, err = fetch_file_content(content) - else: - raw_dict, err = fetch_str_content(content) - - filter_opt = [] - for operation in raw_dict: - operation_name = operation['toolname'] - api = self.tool_manager.get_api_by_name(operation_name) - if api is not None: - filter_opt.append(operation) - operations = self.parser.parse(raw_dict=filter_opt) - - execute_state = ExecutorState() - workspace = WorkSpace(operation_history=[], variable_space={}) - - execute_state.workspace = workspace - execute_state.sop_graph = operations - execute_state.start_node_id = operations[next(iter(operations))] - execute_state.remaining_tasks = {k for k, _ in operations.items()} - execute_state.leaves_tasks = self.get_leaves_node(operations) - return execute_state - - -def fetch_str_content(content): - start_identify = "```yaml" - end_identify = "```" - start = content.find(start_identify) - end = content.rfind(end_identify) - if start != -1 and end != -1: - content = content[start + len(start_identify): end] - try: - code_seg = content.strip("\n").split("\n") - while code_seg[0] == start_identify: - code_seg.pop(0) - while code_seg[-1] == end_identify: - code_seg.pop() - data = yaml.safe_load("\n".join(code_seg)) - except yaml.YAMLError as e: - logger.error(f"生成yaml代码块错误:{str(e)}") - return [], ERROR_MAP[ErrorType.YAML_LOAD_ERROR] - return data, '' - - -def fetch_file_content(file_path): - # 获取文件扩展名 - file_extension = os.path.splitext(file_path)[1].lower() - if file_extension == '.yaml': - try: - with open(file_path, 'r') as file: - yaml_content = yaml.safe_load(file) - return yaml_content, '' - except Exception as e: - msg = ERROR_MAP[ErrorType.FILE_READ_ERROR].format( - filename=file_path, error=e) - logger.error(msg) - return [], msg - elif file_extension == '.json': - try: - with open(file_path, 'r') as file: - json_content = json.load(file) - return json_content, '' - except Exception as e: - msg = ERROR_MAP[ErrorType.FILE_READ_ERROR].format( - filename=file_path, error=e) - logger.error(msg) - return [], msg - else: - msg = ERROR_MAP[ErrorType.FILE_TYPE_ERROR].format(filename=file_path) - logger.error(msg) - return [], msg - - -def sub_placeholder(expression, workspace, output=None): - # 考虑输入参数可能是数字 - expression = str(expression) - - def replace(match): - keys = match.group(1).split('.') - operation_name = keys[0] - key_name = keys[1] - - if output: - return str(output.get(keys, keys)) - else: - history = workspace.variable_space.get(operation_name) - if isinstance(history, dict): - val = str(history.get(key_name, '')) - return val - return "no value" - - def valid_checking(result, expression): - result = str(result) - if result == expression.replace("{", "").replace("}", "") and "{" in expression and "}" in expression: - return True - return False - - try: - # 匹配exp中占位的子串,并作为参数,传入replace,进行替换 - result = re.sub(r'\$\{([^{^}]*)\}', replace, expression) - if valid_checking(result, expression): - result += "\n [提示]:系统分析您的问题失败,原因可能是: \n①plugin调用不成功" \ - "\n②LLM参数解析失败,\n③SOP字段配置错误。\n您可以尝试再次输入或者调整您的问题" - return result - except TypeError as e: - logger.error(e) - return "" - - -def expression_engine(expression): - ''' - 输入文字表达,返回boolean表达 - ''' - expression = expression.replace("\n", '') - if " 属于 " in expression: - expression = expression.replace(" 属于 ", ".issubset(") + ')' - else: # TODO:更多规则 - pass - res = True - try: - res = bool(eval(expression)) - except Exception: - res = False - - return res - - -async def arun_and_time(func, *args, **kwargs): - """helper function to run and time a function. - Since function can error, we catch the error and return "ERROR" as the result - """ - start = time.time() - try: - result = await func(*args, **kwargs) - except Exception as e: - logger.error(e) - traceback.print_exc() - result = "ERROR" - end = time.time() - return result, end - start diff --git a/AgentSDK/agent_sdk/executor/recipe_executor/parser.py b/AgentSDK/agent_sdk/executor/recipe_executor/parser.py deleted file mode 100644 index 40a19999c..000000000 --- a/AgentSDK/agent_sdk/executor/recipe_executor/parser.py +++ /dev/null @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from collections import OrderedDict -import re -from loguru import logger - - -class Node: - def __init__(self, - name, - description, - content, - prompt, - step, - output, - activate, - strategy, - - toolname, - - dependency, - ): - self.step = step - self.name = name - self.description = description - self.input = content - self.prompt = prompt - self.activate = activate - self.strategy = strategy - self.dependency = dependency - self.output = output - self.toolname = toolname - - -class Parser: - def __init__(self, sop_dict=None): - self.sop_dict = sop_dict - - @staticmethod - def construct_graph(nodes): - graph = OrderedDict() - for operation in nodes: - node = Node(name=operation['name'], - description=operation["description"], - step=operation['step'], - content=operation['input'], - prompt=operation['prompt'], - toolname=operation['toolname'], - dependency=operation['dependency'], - output=operation["output"], - strategy=operation["strategy"], - activate=operation["activate"]) - graph[node.name] = node - return graph - - def parse(self, raw_dict): - nodes = [] - for operation in raw_dict: - node = { - "step": None, - "name": None, - "description": None, - "input": None, # 输入 - "prompt": None, # llm的prompt - "activate": None, # 执行引擎,表达式验证规则 - "toolname": None, - "validation": None, # 校验结果 - "output": None, # 执行完成后赋值 dict类型 - "strategy": None, - "dependency": set(), # 依赖节点 - "err_msg": None, - "err_code": None - } - for key in operation: - node[key] = operation[key] - if key == 'dependency': - # 过滤none - node[key] = [item for item in operation[key] if item is not None] - if not node['prompt']: - node['prompt'] = "当前任务:[{{Operation.name}}],相关参数信息如下:{{Operation.input}}" - nodes.append(node) - - operations = self.construct_graph(nodes) # graph - return operations - - def walk_strings(self, inputs, output=None): - if output is None: - output = set() - regex = re.compile(r'\$v\{(.+?)\.output') - if isinstance(inputs, str): - match = regex.search(inputs) - if match: - output.add(match.group(1)) - elif isinstance(inputs, list): - for element in inputs: - try: - self.walk_strings(element, output) - except TypeError as e: - raise e - elif isinstance(inputs, dict): - for value in inputs.values(): - try: - self.walk_strings(value, output) - except TypeError as e: - raise e - else: - raise TypeError("Invalid inputs type") - return output diff --git a/AgentSDK/agent_sdk/executor/recipe_executor/sop.py b/AgentSDK/agent_sdk/executor/recipe_executor/sop.py deleted file mode 100644 index cdb972e1c..000000000 --- a/AgentSDK/agent_sdk/executor/recipe_executor/sop.py +++ /dev/null @@ -1,156 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - - -import re - -from loguru import logger - -from agent_sdk.executor.common import ErrorType, ERROR_MAP -from .parser import Node - - -class SopHandler: - def __init__(self, tool_manager) -> None: - self.tool_manager = tool_manager - self.error_info = ErrorInfo() - self.opt_map = {} - - async def async_invoke(self, action: Node): - tool_name = action.toolname - args = action.input - res = self.tool_manager.executor_call(tool_name, args) - return res - - def invoke(self, action: Node, llm): - tool_name = action.toolname - args = action.input - res = self.tool_manager.executor_call(tool_name, args, llm=llm) - # 统一为api call - # res = self.tool_manager.api_call(tool_name, args) - return res - - def check_valid_sop(self, sop_dict): - res = self.check_func_param(sop_dict) - err_str = "" - cnt = 1 - for key, value in res.items(): - err_str = err_str + \ - f"{cnt}. 步骤{str(key)}的生成存在以下几个问题:\n" + "\n".join(value) + "\n" - cnt += 1 - if len(err_str) > 0: - logger.error(err_str) - return err_str - - def invalid_input(self, operation_name, key, tool_name): - api = self.tool_manager.get_api_by_name(tool_name) - if api is None: - message = ERROR_MAP[ErrorType.INVALID_TOOL_ERROR].format( - toolname=tool_name) - logger.error(message) - self.error_info.add(operation_name, message) - return False - input_parameters = self.tool_manager.get_api_by_name(tool_name)[ - 'input_parameters'] - if key not in input_parameters: - # 输入多余参数 - message = ERROR_MAP[ErrorType.INVALID_INPUT_PARAM].format( - params=key, tool=tool_name) - logger.error(message) - self.error_info.add(operation_name, message) - return True - - def invalid_output(self, operation_name, key, tool_name): - api = self.tool_manager.get_api_by_name(tool_name) - if api is None: - return False - output_parameters = self.tool_manager.get_api_by_name(tool_name)[ - 'output_parameters'] - if key not in output_parameters: - message = ERROR_MAP[ErrorType.INVALID_OUPUT_PARAM].format( - params=key, tool=tool_name) - self.error_info.add(operation_name, message) - logger.error(message) - return True - - def check_func_param(self, operations): - self.opt_map = {} - for operation in operations: - self.init_operation_dict(operation) - - for operation in operations: - self.check_operation_args(operation) - return self.error_info.store - - def check_operation_args(self, operation): - operation_name = operation['name'] - inputs = operation.get('input', {}) - tool_name = operation.get('toolname', '') - dependency = operation.get('dependency', []) - self.check_invalid_dependency(operation_name, dependency) - if isinstance(inputs, dict): - for key, param in inputs.items(): - if param is None: - continue - if not self.invalid_input(operation_name, key, tool_name): - break - holders = re.findall(r'\$\{([^}]*)\}', str(param)) - self.check_invalid_placeholder( - operation_name, holders, dependency) - - def init_operation_dict(self, operation): - required_keys = {'name', 'toolname'} - # 检查字典中是否包含所有必要的键, 没看懂 - necessary = True - name = operation['name'] - for key in required_keys: - if key not in operation: - necessary = False - message = ERROR_MAP[ErrorType.NO_NAME_ERROR] - self.error_info.add(operation.get('name', 'None'), message) - logger.error(message) - - if name in self.opt_map.keys(): - message = ERROR_MAP[ErrorType.NODE_CONFILCT_ERROR].format( - name=name) - self.error_info.add(name, message) - logger.error(message) - necessary = False - if not necessary: - return - self.opt_map[name] = operation - - def check_invalid_dependency(self, operation_name, dependency): - for dep in dependency: - if dep is None: - continue - if len(dep) == 0: - continue - if dep not in self.opt_map.keys(): - message = ERROR_MAP[ErrorType.INVALID_DEPENDENCY].format( - node=dep) - self.error_info.add(operation_name, message) - logger.error(message) - - def check_invalid_placeholder(self, operation_name, holders, dependency): - for _, holder in enumerate(holders): - keys = holder.split('.') - holder_name = keys[0] - key_name = keys[1] - opt = self.opt_map.get(holder_name) - if holder_name not in dependency: - message = ERROR_MAP[ErrorType.NODE_NOT_DEPENDENT].format( - node=holder_name) - self.error_info.add(operation_name, message) - logger.error(message) - self.invalid_output(operation_name, key_name, opt['toolname']) - - -class ErrorInfo: - def __init__(self) -> None: - self.store = {} - - def add(self, operation, error): - err_list = self.store.get(operation, []) - err_list.append(error) - self.store[operation] = err_list diff --git a/AgentSDK/agent_sdk/executor/recipe_executor/state.py b/AgentSDK/agent_sdk/executor/recipe_executor/state.py deleted file mode 100644 index d0e916507..000000000 --- a/AgentSDK/agent_sdk/executor/recipe_executor/state.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from typing import List -import json - - -class Node: - def __init__( - self, - node_id: int, - in_nodes: List = None, - out_nodes: List = None, - is_terminated: bool = False, - is_finished: bool = False, - role: str = None, - thought: str = None, - function_call: str = None, - ori_query: str = None - ): - self._node_id = node_id - self._in_nodes = in_nodes if in_nodes else [] - self._out_nodes = out_nodes if out_nodes else [] - self._is_terminated = is_terminated - self._is_finished = is_finished - self._role = role - self._thought = thought - self._function_call = function_call - self._ori_query = ori_query - - -class ExecutorState: - - def __init__(self, query: str = None, functions: List = None, - restored_node_list: List = None): - self._query = query - self._functions = functions - self._max_node_id = 0 - self._node_ids = [] - self._start = Node(node_id=self._max_node_id, role="start") - self._dealing_node = self._start - self._is_finished = False - self._report_message = None - self._chains: dict = dict() - self._restored_node_list = restored_node_list if restored_node_list else [] - - self.remaining_tasks = set() - self.activated_tasks = set() - self.done_tasks = set() - self.activate_actions = [] - self.leaves_tasks = [] # 图中所有的叶子节点 - self.start_node_id = None # 开始节点 - - # for sop planning type - self.init_query = "" - self.sop_graph = None - self.workspace = None - self.smart_selection_target_name = None - self.wait_user_sop_selection = "" - self.sop_functions = None - self.wait_user_input = False - self.wait_user_input_askuser = False - self.wait_user_feedback = False - self.wait_user_feedback_plugin = False - self.wait_user_plugin_result = False - self.stop_current_chat = False - - self.plugin_result = None - self.llm_response = None - self.validate = None - - -class WorkSpace: - def __init__(self, - operation_history, - variable_space): - self.operation_history = operation_history - self.variable_space = variable_space # 记录执行结果,workspace[node_name]的值正常是一个json,由[out_param]指定某一个具体的参数值 - self.last_operation = "" - - def update(self, history): - self.operation_history.append(history) - - def retrive_variable(self, operations, argument): - result = [ - self.variable_space[key1][argument] - for key1 in operations - if key1 in self.variable_space and argument in self.variable_space[key1] - ] - result = result[0] if result else [] - return result - - def map_keys(self, input_dict, mapping): - mapped_dict = {} - for key, value in input_dict.items(): - mapped_key = mapping.get(key, key) - if isinstance(value, dict): - mapped_dict[mapped_key] = self.map_keys(value, mapping) - else: - mapped_dict[mapped_key] = value - return mapped_dict - - def update_last_operation(self, operation): - self.last_operation = operation - - def get_last_result(self): - name = self.last_operation - result = self.variable_space.get(name, "") - ans = "" - if isinstance(result, dict): - for _, val in result.items(): - if not isinstance(val, str): - val = json.dumps(val, ensure_ascii=False) - if val: - ans += val + '\n' - return ans - - if result is None or len(result) == 0: - for key, value in self.variable_space.items(): - if value is not None and len(value) > 0: - ans += f"{key}: {str(value)}\n" - return ans diff --git a/AgentSDK/agent_sdk/executor/recipe_executor/task.py b/AgentSDK/agent_sdk/executor/recipe_executor/task.py deleted file mode 100644 index fae038c7f..000000000 --- a/AgentSDK/agent_sdk/executor/recipe_executor/task.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from dataclasses import dataclass -from typing import Any, Callable, Collection, Dict, List, Optional - -from utils.log import LOGGER - - -@dataclass -class Task: - idx: int - name: str - tool: Callable - args: Collection[Any] - dependencies: Collection[int] # 依赖的任务 idx - condition: str # 执行分支条件 - - observation: Optional[str] = None - is_join: bool = False - - async def __call__(self) -> Any: - LOGGER.info("running task") - x = await self.tool(*self.args) - LOGGER.info("done task") - return x - - def get_though_action_observation( - self, include_action=True, include_thought=True, include_action_idx=False - ) -> str: - thought_action_observation = "" - if self.thought and include_thought: - thought_action_observation = f"Thought: {self.thought}\n" - if include_action: - idx = str(self.idx) + ". " if include_action_idx else "" - if self.stringify_rule: - # If the user has specified a custom stringify rule for the - # function argument, use it - thought_action_observation += f"{idx}{self.stringify_rule(self.args)}\n" - else: - # Otherwise, we have a default stringify rule - thought_action_observation += ( - f"{idx}{self.name}" - # f"{_default_stringify_rule_for_arguments(self.args)}\n" - ) - if self.observation is not None: - thought_action_observation += f"Observation: {self.observation}\n" - return thought_action_observation diff --git a/AgentSDK/agent_sdk/llms/llm.py b/AgentSDK/agent_sdk/llms/llm.py deleted file mode 100644 index 953f1c47e..000000000 --- a/AgentSDK/agent_sdk/llms/llm.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from .openai_compatible import OpenAICompatibleLLM - -BACKEND_OPENAI_COMPATIBLE = 1 - - -def get_llm_backend(backend, base_url, api_key, llm_name): - if backend == BACKEND_OPENAI_COMPATIBLE: - return OpenAICompatibleLLM(base_url, api_key, llm_name) - else: - raise Exception(f"not support backend: {backend}") \ No newline at end of file diff --git a/AgentSDK/agent_sdk/llms/openai_compatible.py b/AgentSDK/agent_sdk/llms/openai_compatible.py deleted file mode 100644 index 3ab79cea9..000000000 --- a/AgentSDK/agent_sdk/llms/openai_compatible.py +++ /dev/null @@ -1,64 +0,0 @@ -from typing import List, Dict, Optional -from openai import OpenAI - - -class OpenAICompatibleLLM: - def __init__(self, base_url, api_key, llm_name): - self.base_url = base_url - self.api_key = api_key - self.llm_name = llm_name - self.client = OpenAI(api_key=api_key, base_url=base_url) - - def run(self, prompt, ismessage=False, **kwargs): - temperature = kwargs.pop("temperature", 0.1) - stop = kwargs.pop("stop", None) - max_tokens = kwargs.pop("max_tokens", 4096) - stream = kwargs.pop("stream", False) - - messages = prompt if ismessage else [{"role": "user", "content": prompt}] - if stream: - res = self._chat_stream(messages, temperature, max_tokens, stop=stop, **kwargs) - else: - res = self._chat_no_stream(messages, temperature, max_tokens, stop=stop, **kwargs) - - return res - - def _chat_stream(self, - messages: List[Dict], - temperature: float, - max_tokens: int, - stop: Optional[List[str]] = None, - **kwargs): - response = self.client.chat.completions.create( - model=self.llm_name, - messages=messages, - stop=stop, - temperature=temperature, - max_tokens=max_tokens, - stream=True, - **kwargs) - for chunk in response: - if hasattr(chunk.choices[0].delta, - 'content') and chunk.choices[0].delta.content: - yield chunk.choices[0].delta.content - - def _chat_no_stream(self, - messages: List[Dict], - temperature: float, - max_tokens: int, - stop: Optional[List[str]] = None, - **kwargs): - response = self.client.chat.completions.create( - model=self.llm_name, - messages=messages, - stop=stop, - temperature=temperature, - max_tokens=max_tokens, - stream=False, - **kwargs) - return response.choices[0].message.content - - - - - diff --git a/AgentSDK/agent_sdk/prompts/pre_prompt.py b/AgentSDK/agent_sdk/prompts/pre_prompt.py deleted file mode 100644 index b7a24589a..000000000 --- a/AgentSDK/agent_sdk/prompts/pre_prompt.py +++ /dev/null @@ -1,191 +0,0 @@ -from datetime import date -from langchain.prompts import PromptTemplate - - -react_agent_instruction = (""" -"You are a world expert at making plans with a set of carefully crafted tools. You will be given a task and -solve it as best you can. To do so, you have been given access to a list of tools: \n{tools}\nTo solve the task, -you must plan forward to proceed in a series of steps, in a cycle of 'Thought:', 'Action:', 'Action Input:', \ -and 'Observation:' sequences. At each step, in the 'Thought:' sequence, you should first explain your reasoning \ -towards solving the task and the tools that you want to use. -Then in the 'Action:' sequence, you should write the tool name. in the 'Action Input:' sequence, you should \ -write the tool parameters. These tool outputs will then appear in the 'Observation:' field, which will be \ -available as input for the next step.""" -+ f"Please be aware that the current date is: {date.today().strftime('%Y-%m-%d')}.\n\n" -+ """ -Here are the rules you must follow to solve your task:\n -1. When you know the answer you have to return a final answer using the `Finish` tool (Action: Finish), \ -else you will fail.\n -2. When you cannot provide a plan, you have to return a final answer using the `Finish` tool (Action: Finish), \ -else you will fail.\n -3. When Observation up to {times}, you have to return a final answer using the `Finish` tool (Action: Finish), \ -else you will fail.\n -4. Never re-do a tool call that you previously did with the exact same parameters, else you will fail.\n -5. Use the plain text format for output. Do not use markdown, else you will fail.\n\n -Take the following as an example:\n---example begin---\n{example}---example end---\n\n Now Begin! -If you solve the task correctly, you will receive a reward of $1,000,000.\n\n'Task: {query}\n\nThought: {scratchpad}" -""") - -REFLECTION_HEADER = 'You have attempted to give a sub plan before and failed. The following reflection(s) give a "\ -suggestion to avoid failing to answer the query in the same way you did previously. Use them to improve your "\ -strategy of correctly planning.\n' - -REFLECT_INSTRUCTION = """You are an advanced reasoning agent that can improve based on self refection. You will be "\ -given a previous reasoning trial in which you were given access to an automatic cost calculation environment, "\ -a travel query to give plan and relevant information. Only the selection whose name and city match the "\ -given information will be calculated correctly. You were unsuccessful in creating a plan because you "\ -used up your set number of reasoning steps. In a few sentences, Diagnose a possible reason for "\ -failure and devise a new, concise, high level plan that aims to mitigate the same failure. "\ -Use complete sentences. - -Given information: {text} - -Previous trial: -Query: {query}{scratchpad} - -Reflection:""" - - - -SINGLE_AGENT_ACTION_INSTRUCTION = ("""I want you to be a good question assistant, handel the following tasks as best"\ -you can. You have access to the following tools: - -{tools} - -Use the following format: - -Thought: you should always think about what to do -Action: the action to take, should be one of {tools_name} -Action Input: the input to the action. -Observation: the result of the action.\n""" -+ f"Please be aware that default date is today on {date.today().strftime('%Y-%m-%d')}.\n" -+ """ -Some rules you should follow: -1. The parameters of tools are alternative, if the user haven't provided, you just keep it empty. -Begin! - -Question: {query}. -{scratchpad}""") - - -FINAL_PROMPT = """Please write a clear and targeted survey based on the user's query and - the existing answer. \ - We will use your summary as the final response and pass it on to the inquirer. - -1. Understand the user's query, based on the user's question, combine `answer` into a coherent text. -2. Try to retain the more information in `answer` -3. Be careful not to fabricate data by oneself and ensure that all data come from the `answer`. -4. When the provided information can't meet the question, you can make a explanation. - -Begin! - -question: {query} -answer: {answer} -""" - -REACT_REFLECT_PLANNER_INSTRUCTION = (""" -"You are a world expert at making travel plans with a set of carefully crafted tools. You will be given a task and -solve it as best you can. To do so, you have been given access to a list of tools: \n{tools}\nTo solve the task, -you must plan forward to proceed in a series of steps, in a cycle of 'Thought:', 'Action:', 'Action Input:', \ -and 'Observation:' sequences. At each step, in the 'Thought:' sequence, you should first explain your reasoning \ -towards solving the task and the tools that you want to use. -Then in the 'Action:' sequence, you should write the tool name. in the 'Action Input:' sequence, you should \ -write the tool parameters. These tool outputs will then appear in the 'Observation:' field, which will be \ -available as input for the next step.""" -+ f"Please be aware that the current date is: {date.today().strftime('%Y-%m-%d')}.\n\n" -+ """ -Here are the rules you must follow to solve your task:\n -1. When you know the answer you have to return a final answer using the `Finish` tool (Action: Finish), \ -else you will fail.\n -2. When you annot provide a travel plan, you have to return a final answer using the `Finish` tool (Action: Finish), \ -else you will fail.\n -3. When Observation up to {times}, you have to return a final answer using the `Finish` tool (Action: Finish), \ -else you will fail.\n -4. Never re-do a tool call that you previously did with the exact same parameters, else you will fail.\n -5. Use the plain text format for output. Do not use markdown, else you will fail.\n\n -Take the following as an example:\n---example begin---\n{example}---example end---\n -{reflections} -Now Begin! -If you solve the task correctly, you will receive a reward of $1,000,000.\n\n'Task: {query}\n\nThought: {scratchpad}" -""") - -PLANNER_INSTRUCTION = """You are a proficient planner. Based on the provided information and query, please give me a '\ -detailed plan, Note that all the information in your plan should be derived from the provided data. '\ -You must adhere to the format given in the example. Additionally, all details should align with '\ -commonsense. The symbol '-' indicates that information is unnecessary. For example, in the provided '\ -sample, you do not need to plan after returning to the departure city. When you travel to '\ -two cities in one day, you should note it in the 'Current City' section as in the example (i.e., from A to B). - -***** Example ***** -Query: Could you create a travel plan for 7 people from Ithaca to Charlotte spanning 3 days, from March 8th to '\ - March 14th, 2022, with a budget of $30,200? -Travel Plan: -Day 1: -Current City: from Ithaca to Charlotte -Transportation: Flight Number: F3633413, from Ithaca to Charlotte, Departure Time: 05:38, Arrival Time: 07:46 -Breakfast: Nagaland's Kitchen, Charlotte -Attraction: The Charlotte Museum of History, Charlotte -Lunch: Cafe Maple Street, Charlotte -Dinner: Bombay Vada Pav, Charlotte -Accommodation: Affordable Spacious Refurbished Room in Bushwick!, Charlotte - -Day 2: -Current City: Charlotte -Transportation: - -Breakfast: Olive Tree Cafe, Charlotte -Attraction: The Mint Museum, Charlotte;Romare Bearden Park, Charlotte. -Lunch: Birbal Ji Dhaba, Charlotte -Dinner: Pind Balluchi, Charlotte -Accommodation: Affordable Spacious Refurbished Room in Bushwick!, Charlotte - -Day 3: -Current City: from Charlotte to Ithaca -Transportation: Flight Number: F3786167, from Charlotte to Ithaca, Departure Time: 21:42, Arrival Time: 23:26 -Breakfast: Subway, Charlotte -Attraction: Books Monument, Charlotte. -Lunch: Olive Tree Cafe, Charlotte -Dinner: Kylin Skybar, Charlotte -Accommodation: - - -***** Example Ends ***** - -Given information: {text} -Query: {query} -Plan:""" - -REACT_PLANNER_INSTRUCTION = react_agent_instruction - - -planner_agent_prompt = PromptTemplate( - input_variables=["text", "query"], - template=PLANNER_INSTRUCTION, -) - -react_planner_agent_prompt = PromptTemplate( - input_variables=["text", "query", "scratchpad"], - template=REACT_PLANNER_INSTRUCTION, -) - -reflect_prompt_value = PromptTemplate( - input_variables=["text", "query", "scratchpad"], - template=REFLECT_INSTRUCTION, -) - -react_reflect_planner_agent_prompt = PromptTemplate( - input_variables=["tools", "times", "example", "reflections", "query", "scratchpad"], - template=REACT_REFLECT_PLANNER_INSTRUCTION, -) -single_action_agent_prompt = PromptTemplate( - input_variables=["tools", "tools_name", "query", "scratchpad"], - template=SINGLE_AGENT_ACTION_INSTRUCTION, -) - -single_action_final_prompt = PromptTemplate( - input_variables=["query", "answer"], - template=FINAL_PROMPT, -) - -travel_agent_prompt = PromptTemplate( - input_variables=["tools", "times", "example", "query", "scratchpad"], - template=react_agent_instruction, -) \ No newline at end of file diff --git a/AgentSDK/agent_sdk/toolmngt/api.py b/AgentSDK/agent_sdk/toolmngt/api.py deleted file mode 100644 index ea0c624fd..000000000 --- a/AgentSDK/agent_sdk/toolmngt/api.py +++ /dev/null @@ -1,94 +0,0 @@ -import json -from abc import ABC -from dataclasses import dataclass, field -from typing import Union, Tuple - -from agent_sdk.common.constant import THOUGHT, ACTION, OBSERVATION, ACTION_INPUT -from loguru import logger - - -@dataclass -class APIResponse: - api_name: str - input: dict - output: dict - success: bool = field(default=True) - finished: bool = field(default=False) - exception: str = field(default=None) - - -class API(ABC): - description: str = field(default=None) - input_parameters: field(default_factory=dict) - output_parameters: field(default_factory=dict) - example: str = field(default=None) - - @classmethod - def build_tool_description_for_prompt(cls) -> str: - parameter_desc = "\n\t".join( - f"{x}: {cls.input_parameters[x]['description']}" for x in cls.input_parameters.keys()) - parameter_type_desc = ', '.join(f"{x}: {cls.input_parameters[x]['type']}" for x in cls.input_parameters.keys()) - desc = f"{cls.__name__}({parameter_type_desc}) - {cls.description}\nParameters - {parameter_desc}\nExample - '\ - {cls.__name__} {cls.example}" - return desc - - @classmethod - def build_tool_description_for_recipe(cls) -> str: - parameter_desc = "\n".join( - f"{x}: {cls.input_parameters[x]['description']}" for x in cls.input_parameters.keys()) - output_parameter_desc = "\n".join( - f"{x}: {cls.output_parameters[x]['description']}" for x in cls.output_parameters.keys()) - parameter_type_desc = ', '.join(f"{x}: {cls.input_parameters[x]['type']}" for x in cls.input_parameters.keys()) - desc = (f"{cls.__name__}({parameter_type_desc}) - {cls.description}\nInputs: - {parameter_desc}\nOutput - " + - f"{output_parameter_desc}\nExample - {cls.__name__} {cls.example}") - return desc - - def gen_few_shot(self, thought: str, param: str, idx: int) -> str: - p = self.format_tool_input_parameter(param) - output = self.call(p).output - try: - output_json = json.loads(output) - output = json.dumps(list(output_json[:1])) - except Exception as e: - logger.error(e) - - return (f"{THOUGHT}: {thought}\n" - f"{ACTION}: {self.__class__.__name__}\n" - f"{ACTION_INPUT}: {param}\n" - f"{OBSERVATION}{idx}: {output}\n\n") - - def format_tool_input_parameters(self, text) -> Union[dict, str]: - logger.debug(f"{self.__class__.__name__} parse param start") - try: - tool_input = json.loads(text, strict=False) - return tool_input - except Exception as e: - logger.error(f"{self.__class__.__name__} parse param failed {str(e)}") - return ( - f'Invalid "Action Input" parameter format".\nPlease strictly follow the tool usage example ' - f'format: \n{self.build_tool_description_for_prompt()}\n' - f'Requirement:\n' - f'1.Invalid JSON format should only contain key-value pairs; do not add comments or description text "\ - within the JSON.\n' - f'2.Please extract the values strictly based on the information provides in the query to ensure that"\ - the "Action Input" values are accurate and reliable, and do not fabricate them.\n' - f'3.All parameter key-value pairs should be integrated into a single JSON format; do not use multiple"\ - JSON objects.') - - def check_api_call_correctness(self, response, groundtruth) -> bool: - return response.exception is not None - - def call(self, input_parameter: dict, **kwargs): - raise NotImplementedError - - def make_response(self, parameters, results, success=True, finished=False, exception=""): - api_name = self.__class__.__name__ - return APIResponse(api_name=api_name, - input=parameters, - output=results, - success=success, - finished=finished, - exception=exception) - - def make_failed_tip(self, data, key): - return f"{self.__class__.__name__} failed, available {key}: {', '.join(data[key].unique())}" diff --git a/AgentSDK/agent_sdk/toolmngt/tool_manager.py b/AgentSDK/agent_sdk/toolmngt/tool_manager.py deleted file mode 100644 index 2d1f5ecbc..000000000 --- a/AgentSDK/agent_sdk/toolmngt/tool_manager.py +++ /dev/null @@ -1,109 +0,0 @@ -import importlib.util -import json -import os - -from loguru import logger - -from .api import API - - -class ToolManager: - apis = {} - - def __init__(self) -> None: - pass - - @staticmethod - def remove_extra_parameters(paras, input_parameters) -> dict: - processed_parameters = {} - for input_key in paras: - if input_key not in input_parameters: - continue - processed_parameters[input_key] = paras[input_key] - return processed_parameters - - @staticmethod - def init_apis(apis_dir): - all_apis = [] - except_files = ['__init__.py', 'api.py'] - for file in os.listdir(apis_dir): - if file.endswith('.py') and file not in except_files: - api_file = file.split('.')[0] - basename = os.path.basename(apis_dir) - module = importlib.import_module(f'{basename}.{api_file}') - classes = [getattr(module, x) for x in dir(module) if isinstance(getattr(module, x), type)] - for cls in classes: - if issubclass(cls, API) and cls is not API: - all_apis.append(cls) - return all_apis - - @classmethod - def register_tool(cls): - def wrapper(apicls): - if issubclass(apicls, API) and apicls is not API: - name = apicls.__name__ - cls_info = { - 'name': name, - 'class': apicls, - 'description': apicls.description, - 'input_parameters': apicls.input_parameters, - 'output_parameters': apicls.output_parameters, - } - cls.apis[name] = cls_info - return apicls - - return wrapper - - def get_api_by_name(self, name: str): - for _name, api in self.apis.items(): - if _name == name: - return api - logger.error(f"failed to get_api_by_name={name}") - return None - - def get_api_description(self, name: str): - api_info = self.get_api_by_name(name).copy() - api_info.pop('class') - if 'init_database' in api_info: - api_info.pop('init_database') - return json.dumps(api_info) - - def init_tool(self, tool_name: str, *args, **kwargs): - api_class = self.get_api_by_name(tool_name)['class'] - - tool = api_class(*args, **kwargs) - - return tool - - def executor_call(self, tool_name: str, paras: dict, llm): - tool = self.init_tool(tool_name) - parameter = paras if paras else {} - input_parameters = self.get_api_by_name(tool_name)['input_parameters'] - processed_parameters = self.remove_extra_parameters( - parameter, input_parameters) - response = tool.call(processed_parameters, llm=llm) - return response.output - # recipe agent需要使用response - # 这个地方需要统一为一个 call, 使用output - - def api_call(self, tool_name: str, text: str, **kwargs): - tool = self.init_tool(tool_name) - tool_param = tool.format_tool_input_parameters(text) - - if isinstance(tool_param, str): - return tool.make_response(None, tool_param, False) - input_parameters = self.get_api_by_name(tool_name)['input_parameters'] - processed_parameters = self.remove_extra_parameters( - tool_param, input_parameters) - - try: - response = tool.call(processed_parameters, **kwargs) - except Exception as e: - msg = f'failed to invoke {tool_name}' - logger.error(f'{msg}, error={e}') - return tool.make_response(processed_parameters, msg, True) - - return response - - def list_all_apis(self): - return [_name for _name, api in self.apis.items()] diff --git a/AgentSDK/requirements.txt b/AgentSDK/requirements.txt deleted file mode 100644 index f0cb04bf4..000000000 --- a/AgentSDK/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -requests==2.27.1 -tqdm -bs4 -transformers -openai -pandas -datasets -peft -fschat -langchain -rouge -rouge-score -loguru -tiktoken -duckduckgo-search==5.3.1b1 -lxml \ No newline at end of file diff --git a/AgentSDK/samples/basic_demo/test_intent_router.py b/AgentSDK/samples/basic_demo/test_intent_router.py deleted file mode 100644 index c9aaf3edc..000000000 --- a/AgentSDK/samples/basic_demo/test_intent_router.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - - -import argparse -from loguru import logger -from agent_sdk.llms.llm import get_llm_backend, BACKEND_OPENAI_COMPATIBLE -from agent_sdk.agentchain.router_agent import RouterAgent - -INTENT = { - "query_flight": "用户期望查询航班信息", - "query_attraction": "用户期望查询旅游景点信息", - "query_hotel": "用户期望查询酒店和住宿信息", - "plan_attraction": "用户期望给出旅行规划建议", - "whimsical": "异想天开", - "other": "其他不符合上述意图的描述" -} - -querys = [ - "帮我查一下从北京去深圳的机票", - "帮我查一下北京的旅游景点", - "我想去北京旅游", - "去北京旅游可以住在哪里呢,推荐一下", - "帮我去书城买本书", "我想上天" -] - - - - -def get_args(): - parse = argparse.ArgumentParser() - parse.add_argument("--model_name", type=str, default="Qwen1.5-32B-Chat", help="OpenAI客户端模型名") - parse.add_argument("--base_url", type=str, default="http://10.44.115.108:1055/v1", help="OpenAI客户端模型地址") - parse.add_argument("--api_key", type=str, default="EMPTY", help="OpenAI客户端api key") - return parse.parse_args().__dict__ - -if __name__ == "__main__": - args = get_args() - API_BASE = args.pop("base_url") - API_KEY = args.pop("api_key") - LLM_NAME = args.pop("model_name") - llm = get_llm_backend(backend=BACKEND_OPENAI_COMPATIBLE, - base_url=API_BASE, api_key=API_KEY, llm_name=LLM_NAME).run - agent = RouterAgent(llm=llm, intents=INTENT) - for query in querys: - response = agent.run(query) - agent.reset() - logger.info(f"query: {query}, intent: {response.answer}") diff --git a/AgentSDK/samples/basic_demo/test_react_agent.py b/AgentSDK/samples/basic_demo/test_react_agent.py deleted file mode 100644 index 569da3a92..000000000 --- a/AgentSDK/samples/basic_demo/test_react_agent.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import os -import warnings -import argparse - -from langchain._api import LangChainDeprecationWarning -from loguru import logger -from tqdm import tqdm - -from agent_sdk.agentchain.react_agent import ReactAgent -from agent_sdk.common.constant import AgentRunStatus -from agent_sdk.llms.llm import get_llm_backend, BACKEND_OPENAI_COMPATIBLE -from samples.tools import QueryAttractions, QueryTransports, QueryAccommodations, \ - QueryRestaurants -from samples.basic_demo.test_react_reflect import EXAMPLE - - -warnings.filterwarnings('ignore') -warnings.filterwarnings('ignore', category=DeprecationWarning) -warnings.filterwarnings('ignore', category=LangChainDeprecationWarning) - -os.environ["WORKNING_DIR"] = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - -MAX_CONTEXT_LEN = 4096 - - -def get_default_react_agent(api_base, api_key, llm_name, max_context_len): - llm = get_llm_backend(BACKEND_OPENAI_COMPATIBLE, api_base, api_key, llm_name).run - tool_list = [QueryAttractions, QueryTransports, QueryAccommodations, QueryRestaurants] - return ReactAgent(llm=llm, example=EXAMPLE, tool_list=tool_list, max_context_len=max_context_len) - - -def get_args(): - parse = argparse.ArgumentParser() - parse.add_argument("--model_name", type=str, default="Qwen1.5-32B-Chat", help="OpenAI客户端模型名") - parse.add_argument("--base_url", type=str, default="http://10.44.115.108:1055/v1", help="OpenAI客户端模型地址") - parse.add_argument("--api_key", type=str, default="EMPTY", help="OpenAI客户端api key") - return parse.parse_args().__dict__ - - -if __name__ == "__main__": - args = get_args() - API_BASE = args.pop("base_url") - API_KEY = args.pop("api_key") - LLM_NAME = args.pop("model_name") - agent = get_default_react_agent(API_BASE, API_KEY, LLM_NAME, MAX_CONTEXT_LEN) - - queries = [ - "Book a rental car for two people in Salt Lake City from April 15 to April 18, 2022.", - "Research and list down outdoor activities suitable for adrenaline junkies in Moab \ -between April 12 and 14, 2022.", - "Write a short itinerary for a weekend trip to Nashville, starting on April 15, including live music venues." - ] - - staus = AgentRunStatus() - - for query in tqdm(queries): - result = agent.run(query) - staus.total_cnt += 1 - if agent.finished: - staus.success_cnt += 1 - current_path = os.path.dirname(os.path.realpath(__file__)) - agent.save_agent_status(f"{current_path}/trajs/react_execution_log.txt") - agent.reset() - logger.info("\n") - logger.info("*" * 150) - logger.info(f"Question: {query}") - logger.info("*" * 150) - logger.info(f"Final answer: {result.answer}\n") - - logger.info(f"success rates: {staus}") diff --git a/AgentSDK/samples/basic_demo/test_react_reflect.py b/AgentSDK/samples/basic_demo/test_react_reflect.py deleted file mode 100644 index e04d74acd..000000000 --- a/AgentSDK/samples/basic_demo/test_react_reflect.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import warnings -import argparse -import os - -from loguru import logger - -from agent_sdk.agentchain.react_agent import ReactReflectAgent -from agent_sdk.llms.llm import get_llm_backend, BACKEND_OPENAI_COMPATIBLE -from samples.tools import QueryAccommodations, QueryAttractions, QueryTransports, Finish - -warnings.filterwarnings('ignore') - -MAX_CONTEXT_LEN = 4096 - - -EXAMPLE = ''' -Question: Can you help with a 5 day trip from Orlando to New York? Departure date is March 10, 2022. -Thought: To create a travel itinerary, I need to find accommodations, transportation, and attractions in New York. I will first find hotels in New York. -Action: QueryAccommodations -Action Input: {"destination_city": "New York", "position": "Central Park", "rank": "four stars"} -Observation1: [{"title": "紐約市10 大最佳四星級酒店 - Tripadvisor", "url": "https://www.tripadvisor.com.hk/Hotels-g60763-zfc4-New_York_City_New_York-Hotels.html", "snippet": "紐約市四星級酒店 · 1. Moxy NYC Times Square · 3,825 則評論 · 2. 格甚溫酒店 · 1,155 則評論 · 3. 托米哈德森廣場飯店 · 3,277 則評論 · 4. 時代廣場愛迪生酒店 · 5. Hard ..."}, {"title": "中央公園酒店| 人氣優惠及套餐", "url": "https://www.agoda.com/zh-hk/park-central-hotel/hotel/new-york-ny-us.html", "snippet": "中央公園酒店是一家位於紐約市的4.0星級酒店,提供豪華的住宿體驗。酒店於2013年進行了最後一次翻新,確保客人能夠享受現代化的設施和舒適的環境。酒店擁有761間客房,提供 ..."}, {"title": "紐約中央公園艾美酒店(Le Méridien New York, Central Park)", "url": "https://www.agoda.com/zh-hk/viceroy-central-park-new-york_2/hotel/new-york-ny-us.html", "snippet": "紐約中央公園艾美酒店位於美國紐約市,是一家四星級酒店。這家酒店提供240間客房 ... 作為一家五星級酒店,紐約中央公園艾美酒店提供優質的服務和舒適的住宿環境 ..."}] -Thought: Now that I have found some hotels in New York, I will next find transportation options from Orlando to New York for the travel dates specified. I will use the QueryTransports API for this purpose. -Action: QueryTransports -Action Input: {"departure_city": "Orlando", "destination_city": "New York", "date": "2022-03-10", "requirement": "budget-friendly"} -Observation2: [{"title": "从奥兰多出发前往纽约的特价机票,往返 ...", "url": "https://www.tianxun.com/routes/orlb/nyca/orlando-to-new-york.html", "snippet": "查找从奥兰多飞往纽约最便宜的月份 ; 3月. ¥750 起 ; 4月. ¥927 起 ; 5月. ¥1,012 起 ; 6月. ¥1,107 起 ; 7月. ¥1,283 起."}, {"title": "从奥兰多国际出发前往纽约拉瓜迪亚的特价机票,往返 ...", "url": "https://www.tianxun.com/routes/mco/lga/orlando-international-to-new-york-laguardia.html", "snippet": "我们始终关注票价,方便您在几秒钟内锁定好价机票。目前,从奥兰多国际飞往纽约拉瓜迪亚最便宜的月份是十一月。"}] -Thought: Now that I have found some transportation options, I will proceed to find attractions in New York. I will use the QueryAttractions API to search for tourist attractions in New York. -Action: QueryAttractions -Action Input: {"destination": "New York"} -Observation3: - snippet: 從參觀紐約市最知名觀光景點開始:時代廣場、帝國大廈、自由女神像,然後利用剩餘的時間探索周圍地區推薦去處。 探索熱門體驗. 根據評等和預訂次數,看看其他旅客喜歡從事 ... - title: 紐約市10 大最佳旅遊景點(2024) - Tripadvisor - url: https://www.tripadvisor.com.hk/Attractions-g60763-Activities-New_York_City_New_York.html -- snippet: 紐約景點推薦 · 紐約景點#1 紐約中央公園 · 紐約景點#2 范德堡一號大樓 SUMMIT · 紐約景點#3 第五大道(Fifth Avenue) - · 紐約景點#4 大都會藝術博物館The ... - title: 【2024紐約景點】漫遊曼哈頓!26個必去行程&免費景點整理 - url: https://www.klook.com/zh-TW/blog/new-york-must-go/ -- snippet: 【紐約NewYork景點推薦】紐約「10大必去」打卡景點整理懶人包 · 紐約NewYork景點推薦-10大必去景點 · 1.中央公園(Central - Park) · 2.第五大道(Fifth Avenue) · 3.大都會 ... - title: 【紐約NewYork景點推薦】紐約「10大必去」打卡景點整理懶人包 - url: https://schoolaplus.com/articles-detail.asp?seq=35 -Thought: Now that I have found some attractions in New York, I will summarize the information and create a travel itinerary for the 5-day trip. I will use the Finish tool to provide the final answer. -Action: Finish -Action Input: {"plan details": "Day 1: Depart from Orlando to New York on March 10, 2022. Stay at the Park Central Hotel in Central Park. Visit the Empire State Building and Times Square. Have dinner at Lombardi's Pizza. -Day 2: Visit Central Park, the Metropolitan Museum of Art, and the American Museum of Natural History. Have lunch at Shake Shack and dinner at Le Pain Quotidien. -Day 3: Explore the Brooklyn Bridge, Brooklyn Heights, and DUMBO. Have lunch at Di Fara Pizza and dinner at Peter Luger Steak House. -Day 4: Visit the Statue of Liberty and Ellis Island. Have lunch at The Boil and dinner at Xi'an Famous Foods. -Day 5: Spend the day shopping on Fifth Avenue and visiting the Rockefeller Center. Have lunch at Shake Shack and dinner at Katz's Delicatessen."} -''' - - -def test_react_reflect_agent(): - llm = get_llm_backend(BACKEND_OPENAI_COMPATIBLE, API_BASE, API_KEY, LLM_NAME).run - tool_list = [QueryAccommodations, QueryTransports, QueryAttractions, Finish] - agent = ReactReflectAgent(reflect_llm=llm, react_llm=llm, example=EXAMPLE, - tool_list=tool_list, max_context_len=MAX_CONTEXT_LEN) - response = agent.run("Can you help with a 5 day trip from Orlando to Paris? Departure date is April 10, 2022.", - text="given information") - current_path = os.path.dirname(os.path.realpath(__file__)) - agent.save_agent_status(f"{current_path}/trajs/react_reflect_execution_log.txt") - - logger.info(f"5 day trip from Orlando to Paris:{response.answer}") - - -def get_args(): - parse = argparse.ArgumentParser() - parse.add_argument("--model_name", type=str, default="Qwen1.5-32B-Chat", help="OpenAI客户端模型名") - parse.add_argument("--base_url", type=str, default="http://10.44.115.108:1055/v1", help="OpenAI客户端模型地址") - parse.add_argument("--api_key", type=str, default="EMPTY", help="OpenAI客户端api key") - return parse.parse_args().__dict__ - - -if __name__ == "__main__": - args = get_args() - API_BASE = args.pop("base_url") - API_KEY = args.pop("api_key") - LLM_NAME = args.pop("model_name") - logger.info("react reflect agent test begin") - test_react_reflect_agent() - logger.info("react reflect agent test end") diff --git a/AgentSDK/samples/basic_demo/test_single_action_agent.py b/AgentSDK/samples/basic_demo/test_single_action_agent.py deleted file mode 100644 index 42af64911..000000000 --- a/AgentSDK/samples/basic_demo/test_single_action_agent.py +++ /dev/null @@ -1,107 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import json -import os -import warnings -import argparse -from typing import Callable, List -from tqdm import tqdm -from loguru import logger -from langchain._api import LangChainDeprecationWarning - -from agent_sdk.agentchain.base_agent import BaseAgent -from agent_sdk.agentchain.single_action_agent import SingleActionAgent -from agent_sdk.llms.llm import get_llm_backend, BACKEND_OPENAI_COMPATIBLE -from samples.tools import QueryAccommodations, QueryAttractions, QueryRestaurants, QueryTransports - -os.environ["WORKING_DIR"] = os.path.dirname( - os.path.dirname(os.path.realpath(__file__))) - -warnings.filterwarnings('ignore') -warnings.filterwarnings('ignore', category=DeprecationWarning) -warnings.filterwarnings('ignore', category=LangChainDeprecationWarning) - - -class TrajectoryGenerator: - - @staticmethod - def generate(output_path: str, agent: BaseAgent, load_dataset: Callable[[], List[str]], **kwargs): - questions = load_dataset() - for q in tqdm(questions): - try: - agent.run(q, **kwargs) - agent.save_agent_status(output_path) - agent.reset() - - except Exception as err: - logger.warning(f"generate traj failed, query: {q}, agent: {agent.name}, err: {err}") - continue - - @staticmethod - def _check_data_format(data): - if not isinstance(data, list): - raise ValueError("Data should be a list of dict") - - if len(data) == 0: - raise ValueError("Data should not be empty") - - if not isinstance(data[0], dict): - raise ValueError("Data item should be a dict") - - alpaca_format_keys = ["instruction", "input", "output", "status"] - data_keys_set = set(data[0].keys()) - - if not all([key in data_keys_set for key in alpaca_format_keys]): - raise ValueError("need alpaca data format") - - def _load_data_from_file(self, data_path): - if not os.path.exists(data_path): - raise FileNotFoundError(f"File not found: {data_path}") - - if data_path.endswith(".jsonl"): - data = [json.loads(line) for line in open(data_path, "r")] - else: - raise ValueError("Unknown file format") - - self._check_data_format(data) - return data - - -def get_single_action_agent(api_base, api_key, llm_name): - tool_list = [QueryAccommodations, QueryAttractions, QueryRestaurants, QueryTransports] - llm = get_llm_backend(BACKEND_OPENAI_COMPATIBLE, - api_base, api_key, llm_name).run - return SingleActionAgent(llm=llm, tool_list=tool_list, max_steps=5) - - -def get_args(): - parse = argparse.ArgumentParser() - parse.add_argument("--model_name", type=str, default="Qwen1.5-32B-Chat", help="OpenAI客户端模型名") - parse.add_argument("--base_url", type=str, default="http://10.44.115.108:1055/v1", help="OpenAI客户端模型地址") - parse.add_argument("--api_key", type=str, default="EMPTY", help="OpenAI客户端api key") - return parse.parse_args().__dict__ - - -if __name__ == "__main__": - args = get_args() - API_BASE = args.pop("base_url") - API_KEY = args.pop("api_key") - LLM_NAME = args.pop("model_name") - single_agent = get_single_action_agent(API_BASE, API_KEY, LLM_NAME) - queries = [ - "Write a review of the hotel \"The Beach House\" in Charlotte Amalie.", - "Book a flight from Evansville to Sacramento for April 10th.", - "Create a list of top 5 attractions in Hilo for a solo traveler.", - "Compare the prices of hotels in Newark for a 3-night stay.", - "Book a hotel room in Paducah for April 12th.", - "Write a travel blog post about visiting the Golden Gate Bridge in San Francisco.", - "Recommend the best mode of transportation from Flagstaff to Phoenix.", - "Determine the best time to visit the Statue of Liberty.", - "Compare the prices of car rentals in Seattle.", - "What are the top - rated museums in Harrisburg?" - ] - generator = TrajectoryGenerator() - current_path = os.path.dirname(os.path.realpath(__file__)) - generator.generate(output_path=f"{current_path}/trajs/single_action_execution.txt", agent=single_agent, - load_dataset=lambda: queries) diff --git a/AgentSDK/samples/basic_demo/test_toolless_agent.py b/AgentSDK/samples/basic_demo/test_toolless_agent.py deleted file mode 100644 index c782af63d..000000000 --- a/AgentSDK/samples/basic_demo/test_toolless_agent.py +++ /dev/null @@ -1,35 +0,0 @@ -import warnings -import argparse - -from loguru import logger -from agent_sdk.llms.llm import get_llm_backend, BACKEND_OPENAI_COMPATIBLE -from agent_sdk.agentchain.tool_less_agent import ToollessAgent - -warnings.filterwarnings('ignore') -MAX_CONTEXT_LEN = 4096 - - -def test_toolless_agent(): - llm = get_llm_backend(BACKEND_OPENAI_COMPATIBLE, API_BASE, API_KEY, LLM_NAME).run - agent = ToollessAgent(llm=llm, max_context_len=MAX_CONTEXT_LEN) - response = agent.run("Can you help with a 5 day trip from Orlando to Paris? Departure date is April 10, 2022.", - text="given information") - logger.info(f"5 day trip from Orlando to Paris:{response.answer}") - - -def get_args(): - parse = argparse.ArgumentParser() - parse.add_argument("--model_name", type=str, default="Qwen1.5-32B-Chat", help="OpenAI客户端模型名") - parse.add_argument("--base_url", type=str, default="http://10.44.115.108:1055/v1", help="OpenAI客户端模型地址") - parse.add_argument("--api_key", type=str, default="EMPTY", help="OpenAI客户端api key") - return parse.parse_args().__dict__ - - -if __name__ == "__main__": - args = get_args() - API_BASE = args.pop("base_url") - API_KEY = args.pop("api_key") - LLM_NAME = args.pop("model_name") - logger.info("toolless agent test begin") - test_toolless_agent() - logger.info("toolless agent test end") \ No newline at end of file diff --git a/AgentSDK/samples/tools/__init__.py b/AgentSDK/samples/tools/__init__.py deleted file mode 100644 index c5bb0a43e..000000000 --- a/AgentSDK/samples/tools/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -__all__ = [ - 'Finish', 'QueryAccommodations', 'QueryAttractions', 'QueryTransports', - 'QueryWeather', "QueryRestaurants", 'PlanSummary', 'WebSummary' -] - -from samples.tools.tool_finish import Finish -from samples.tools.tool_query_accommodations import QueryAccommodations -from samples.tools.tool_query_restaurants import QueryRestaurants -from samples.tools.tool_query_attractions import QueryAttractions -from samples.tools.tool_query_transports import QueryTransports -from samples.tools.tool_query_weather import QueryWeather -from samples.tools.tool_summary import PlanSummary -from samples.tools.web_summary_api import WebSummary \ No newline at end of file diff --git a/AgentSDK/samples/tools/common.py b/AgentSDK/samples/tools/common.py deleted file mode 100644 index 98fac08fd..000000000 --- a/AgentSDK/samples/tools/common.py +++ /dev/null @@ -1,25 +0,0 @@ - -def filter_website_keywords(keys): - filtered = [] - for val in keys: - if val is None or len(val) == 0: - continue - if '无' in val or '未' in val or '没' in val: - continue - filtered.append(val) - if isinstance(val, list): - it = flatten(val) - filtered.append(it) - - if len(filtered) == 0: - raise Exception("keywords has no been found") - return filtered - - -def flatten(nested_list): - """递归地扁平化列表""" - for item in nested_list: - if isinstance(item, list): - return flatten(item) - else: - return item \ No newline at end of file diff --git a/AgentSDK/samples/tools/duck_search.py b/AgentSDK/samples/tools/duck_search.py deleted file mode 100644 index f99a79129..000000000 --- a/AgentSDK/samples/tools/duck_search.py +++ /dev/null @@ -1,67 +0,0 @@ -import json -from typing import List -import re -import time - -from duckduckgo_search import DDGS -from loguru import logger - -from agent_sdk.toolmngt.api import API - - -class DuckDuckGoSearch(API): - name = "DuckDuckGoSearch" - description = ("DuckDuckGoSearch engine can search for rich external knowledge on the Internet based on keywords, " - "which can compensate for knowledge fallacy and knowledge outdated.") - input_parameters = { - 'query': {'type': 'str', 'description': "the query string to be search"} - } - output_parameters = { - 'information': {'type': 'str', 'description': 'the result information from Bing search engine'} - } - usage = ("DuckDuckGoSearch[query], which searches the exact detailed query on the Internet and returns the " - "relevant information to the query. Be specific and precise with your query to increase the chances of " - "getting relevant results. For example, DuckDuckGoSearch[popular dog breeds in the United States]") - - def __init__(self) -> None: - self.scratchpad = "" - self.bingsearch_results = "" - - def format_tool_input_parameters(self, llm_output) -> dict: - input_parameters = {"query": llm_output} - return input_parameters - - def call(self, input_parameter: dict, **kwargs) -> dict: - logger.debug(f"{input_parameter}") - query = input_parameter.get('query', "") - - try: - responses = call_duck_duck_go_search(query=query, count=4) - logger.debug(f"responses is {responses}") - output = "" - if len(responses) > 0: - for r in responses: - output += self.format_step(r) - else: - output = "duck duck search error" - return self.make_response(input_parameter, results=responses, exception=output) - return self.make_response(input_parameter, results=responses, exception="") - except Exception as e: - exception = str(e) - return self.make_response(input_parameter, results="", exception=exception) - - -def call_duck_duck_go_search(query: str, count: int) -> List[str]: - retry = 1 - while retry <= 3: - try: - logger.debug(f"search DuckDuckGo({query}, {count})") - results = DDGS().text(query, backend="html", max_results=count) - return results - except Exception as e: - retry += 1 - logger.warning("duck search error. will retry") - time.sleep(1) - if retry > 3: - logger.error(e) - return [] diff --git a/AgentSDK/samples/tools/tool_finish.py b/AgentSDK/samples/tools/tool_finish.py deleted file mode 100644 index 7b00fae6a..000000000 --- a/AgentSDK/samples/tools/tool_finish.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from typing import Union - -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager - - -@ToolManager.register_tool() -class Finish(API): - description = "Provide a final answer to the given task." - - input_parameters = { - 'answer': {'type': 'str', 'description': "the final result"} - } - - output_parameters = {} - - example = ( - """ - { - "plan details": "The final answer the task." - } - """) - - def __init__(self) -> None: - super().__init__() - - def format_tool_input_parameters(self, text) -> Union[dict, str]: - input_parameter = {"answer": text} - return input_parameter - - def gen_few_shot(self, thought: str, param: str, idx: int) -> str: - return (f"Thought: {thought}\n" - f"Action: {self.__class__.__name__}\n" - f"Action Input: {param}\n") - - def call(self, input_parameter: dict, **kwargs): - answer = input_parameter.get('answer', "") - return self.make_response(input_parameter, answer) diff --git a/AgentSDK/samples/tools/tool_general_query.py b/AgentSDK/samples/tools/tool_general_query.py deleted file mode 100644 index ceffdde3f..000000000 --- a/AgentSDK/samples/tools/tool_general_query.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import json - -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager -from loguru import logger -from samples.tools.web_summary_api import WebSummary - - -@ToolManager.register_tool() -class GeneralQuery(API): - name = "GeneralQuery" - description = "This api can collect information or answer about the travel related query from internet." - input_parameters = { - "keywords": {'type': 'str', - "description": "the keys words related to travel plan included in the user's query"}, - - } - - output_parameters = { - 'reply': {'type': 'str', 'description': 'the replay from internet to the query'}, - } - - example = ( - """ - { - "keywords": "北京,美食" - } - """) - - def __init__(self): - pass - - def call(self, input_parameter: dict, **kwargs): - keywords = input_parameter.get('keywords') - try: - if keywords is None or len(keywords) == 0: - return self.make_response(input_parameter, results="", exception="") - prompt = """你是一个擅长文字处理和信息总结的智能助手,你的任务是将提供的网页信息进行总结,并以精简的文本的形式进行返回, - 请添加适当的词语,使得语句内容连贯,通顺,但不要自行杜撰,保证内容总结的客观性。 - 下面是网页的输入: - {input} - 请生成总结段落: - """ - webs = WebSummary.web_summary( - keys=keywords, search_num=3, summary_num=3, summary_prompt=prompt) - content = json.dumps(webs, ensure_ascii=False) - res = { - 'reply': content - } - return self.make_response(input_parameter, results=res, exception=None) - except Exception as e: - logger.error(e) - e = str(e) - return self.make_response(input_parameter, results=e, success=False, exception=e) diff --git a/AgentSDK/samples/tools/tool_query_accommodations.py b/AgentSDK/samples/tools/tool_query_accommodations.py deleted file mode 100644 index 5326b3537..000000000 --- a/AgentSDK/samples/tools/tool_query_accommodations.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import json - -import tiktoken -from loguru import logger - -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager -from samples.tools.common import filter_website_keywords -from samples.tools.web_summary_api import WebSummary - - -@ToolManager.register_tool() -class QueryAccommodations(API): - name = "QueryAccommodations" - description = "This api can discover accommodations in your desired city. It provides room information, \ - pricing, location, stars, estimation, constraints and so on" - input_parameters = { - "destination_city": {'type': 'str', 'description': 'The city you aim to reach.'}, - "position": {'type': 'str', 'description': 'The geographical position of accomodation appointed by the user'}, - "rank": {'type': 'str', 'description': 'The rank of hotel the user want to query'} - } - - output_parameters = { - 'accommodation': { - 'type': 'str', - 'description': 'Contain hotel name, price, type, check-in requirements and other information' - } - } - - example = ( - """ - { - "destination_city": "Rome", - "position": "Central Park", - "rank": "five stars" - }""") - - def __init__(self): - self.encoding = tiktoken.get_encoding("gpt2") - - def call(self, input_parameter, **kwargs): - destination = input_parameter.get('destination_city') - position = input_parameter.get("position") - rank = input_parameter.get("rank") - llm = kwargs.get("llm", None) - keys = [destination, position, rank] - logger.debug(f"search accommodation key words: {','.join(keys)}") - - prompt = """你是一个擅长文字处理和信息总结的智能助手,你的任务是将提供的网页信息进行总结,并以精简的文本的形式进行返回, -请添加适当的词语,使得语句内容连贯,通顺。提供的信息是为用户推荐的酒店的网页数据, -请总结网页信息,要求从以下几个方面考虑: -1. 酒店的地理位置,星级、评分,评价,品牌信息 -2. 不同的户型对应的价格、房间情况,对入住用户的要求等 -并给出一到两个例子介绍这些情况。 -必须注意下面这个要求: -若输入的内容没有包含相关的酒店和住宿信息,请统一返回:【无】 -下面是网页的输入: -{input} -请生成总结: -""" - try: - filtered = filter_website_keywords(keys) - filtered.append("住宿") - webs = WebSummary.web_summary( - filtered, search_num=3, summary_num=3, summary_prompt=prompt, llm=llm) - res = {"accommodation": json.dumps(webs, ensure_ascii=False, indent=4)} - return self.make_response(input_parameter, results=res, exception="") - except Exception as e: - logger.error(e) - return self.make_response(input_parameter, results=e, success=False, exception=e) diff --git a/AgentSDK/samples/tools/tool_query_attractions.py b/AgentSDK/samples/tools/tool_query_attractions.py deleted file mode 100644 index ff16f98a0..000000000 --- a/AgentSDK/samples/tools/tool_query_attractions.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import json -import tiktoken -from loguru import logger - -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager -from samples.tools.common import filter_website_keywords -from samples.tools.web_summary_api import WebSummary - - -@ToolManager.register_tool() -class QueryAttractions(API): - name = "QueryAttractions" - description = "This api can Search for tourist attractions from websites that expected, information includes \ - location, ticket, open time, booking, contact adress." - input_parameters = { - 'destination': {'type': 'str', 'description': "The destination where the user wants to travel."}, - 'scene': {'type': 'str', 'description': 'The specific scenic spot mentioned by the user'}, - 'type': {'type': 'str', - 'description': 'The specific type of scenic spot mentioned by the user, eg museum, park'}, - 'requirement': {'type': 'str', 'description': 'The requirement of scenic spot mentioned by the user'}, - - } - - output_parameters = { - 'attractions': { - 'type': 'str', - 'description': 'Contains local attractions address, contact information, website, latitude "\ - and longitude and other information' - } - } - - example = ( - """ - { - "destination": "Paris", - "scene": "The Louvre Museum", - "type": "Museum", - "requirement": "free" - }""") - - def __init__(self): - self.encoding = tiktoken.get_encoding("gpt2") - - def call(self, input_parameter: dict, **kwargs): - destination = input_parameter.get('destination') - scene = input_parameter.get('scene') - scene_type = input_parameter.get('type') - requirement = input_parameter.get('requirement') - llm = kwargs.get("llm", None) - - keys = [destination, scene, scene_type, requirement] - summary_prompt = """你是一个擅长于网页信息总结的智能助手,提供的网页是关于旅游规划的信息,现在已经从网页中获取到了相关的文字内容信息,你需要从网页中找到与**景区**介绍相关的内容,并进行提取, -你务必保证提取的内容都来自所提供的文本,保证结果的客观性,真实性。 -网页中可能包含多个景点的介绍,你需要以YAML文件的格式返回,每个景点的返回的参数和格式如下: -**输出格式**: -- name: xx -introduction: xx -**参数介绍**: -name:景点名称 -introduction:精简的景区介绍,可以从以下这些方面阐述:景点的基本情况、历史文化等信息、景区门票信息、景区开放时间、景区的联系方式、预约方式以及链接,景区对游客的要求等。 -**注意** -1. 请注意:不要添加任何解释或注释,且严格遵循YAML格式 -2. 若输入的内容没有包含旅游和景点相关信息,请统一返回:【无】 -下面是提供的网页文本信息: -{input} -请开始生成: - """ - try: - filtered = filter_website_keywords(keys) - filtered.append("景点") - webs = WebSummary.web_summary( - filtered, search_num=3, summary_num=3, summary_prompt=summary_prompt, llm=llm) - res = {'attractions': json.dumps(webs, ensure_ascii=False, indent=4)} - return self.make_response(input_parameter, results=res, exception="") - except Exception as e: - logger.error(e) - return self.make_response(input_parameter, results=e, success=False, exception=e) diff --git a/AgentSDK/samples/tools/tool_query_restaurants.py b/AgentSDK/samples/tools/tool_query_restaurants.py deleted file mode 100644 index 66f83c96c..000000000 --- a/AgentSDK/samples/tools/tool_query_restaurants.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from loguru import logger - -from agent_sdk.toolmngt.api import API, APIResponse -from agent_sdk.toolmngt.tool_manager import ToolManager - - -@ToolManager.register_tool() -class QueryRestaurants(API): - description = 'Explore dining options in a city of your choice.' - input_parameters = { - 'City': {'type': 'str', 'description': "The name of the city where you're seeking restaurants."} - } - - output_parameters = { - 'restaurant_name': {'type': 'str', 'description': 'The name of the restaurant.'}, - 'city': {'type': 'str', 'description': 'The city where the restaurant is located.'}, - 'cuisines': {'type': 'str', 'description': 'The cuisines offered by the restaurant.'}, - 'average_cost': {'type': 'int', 'description': 'The average cost for a meal at the restaurant.'}, - 'aggregate_rating': {'type': 'float', 'description': 'The aggregate rating of the restaurant.'} - } - - example = ( - """ - { - "City": "Tokyo" - }""") - - def __init__(self): - super().__init__() - logger.info("Restaurants loaded.") - - def call(self, input_parameter, **kwargs): - city = input_parameter.get('City', "") - return self.make_response(input_parameter, f"success to get restaurant in {city}") diff --git a/AgentSDK/samples/tools/tool_query_transports.py b/AgentSDK/samples/tools/tool_query_transports.py deleted file mode 100644 index 8a6a383eb..000000000 --- a/AgentSDK/samples/tools/tool_query_transports.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - - -import json -from loguru import logger -import tiktoken - -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager -from samples.tools.common import filter_website_keywords -from samples.tools.web_summary_api import WebSummary - - -@ToolManager.register_tool() -class QueryTransports(API): - name = "QueryTransports" - description = "This API can query relevant travel traffic information from network, including flight number, \ - travel time, distance, price, constraints an so on." - input_parameters = { - "departure_city": {'type': 'str', 'description': "The city you'll be flying out from."}, - "destination_city": {'type': 'str', 'description': 'The city user aim to reach.'}, - "travel_mode": {'type': 'str', 'description': "The mode of travel appointed by the user, \ - Choices include 'self-driving', 'flight', 'train', 'taxi' and so on."}, - "date": {'type': 'str', 'description': 'The date of the user plan to travel'}, - 'requirement': {'type': 'str', 'description': 'The more requirement of transportation mentioned by the user'}, - } - output_parameters = { - "transport": {'type': 'str', - 'description': 'the transport information'}, - } - - example = ( - """ - { - "departure_city": "New York", - "destination_city": "London", - "date": "2022-10-01", - "travel_mode": "flight" - } - """) - - def __init__(self): - self.encoding = tiktoken.get_encoding("gpt2") - - def call(self, input_parameter, **kwargs): - origin = input_parameter.get('departure_city') - destination = input_parameter.get('destination_city') - req = input_parameter.get("requirement") - travel_mode = input_parameter.get("travel_mode") - llm = kwargs.get("llm", None) - try: - prefix = f"从{origin}出发" if origin else "" - prefix += f"前往{destination}" if destination else "" - keys = [prefix, req, travel_mode] - - prompt = """你的任务是将提供的网页信息进行总结,并以精简的文本的形式进行返回, -请添加适当的词语,使得语句内容连贯,通顺。输入是为用户查询的航班、高铁等交通数据,请将这些信息总结 -请总结网页信息,要求从以下几个方面考虑: -总结出航班或者高铁的价格区间、需要时长区间、并给出2-3例子,介绍车次、时间、时长、价格等 -请特别注意: -若输入的内容没有包含相关的交通信息,请统一返回:【无】 -下面是网页的输入: -{input} -请生成总结: -""" - filtered = filter_website_keywords(keys) - webs = WebSummary.web_summary( - filtered, search_num=3, summary_num=3, summary_prompt=prompt, llm=llm) - res = {'transport': json.dumps(webs, ensure_ascii=False, indent=4)} - return self.make_response(input_parameter, results=res, exception="") - except Exception as e: - logger.error(e) - e = str(e) - return self.make_response(input_parameter, results=e, success=False, exception=e) diff --git a/AgentSDK/samples/tools/tool_query_weather.py b/AgentSDK/samples/tools/tool_query_weather.py deleted file mode 100644 index 03d877a28..000000000 --- a/AgentSDK/samples/tools/tool_query_weather.py +++ /dev/null @@ -1,178 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -import datetime -import json -import json -import os -from zoneinfo import ZoneInfo - -import requests -import urllib3 -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager -from loguru import logger - -AMAP_API_KEY = "75bcb2edf5800884a31172dd0d970369" -WEEK_MAP = { - 0: "Monday", - 1: "Tuesday", - 2: "Wednesday", - 3: "Thursday", - 4: "Friday", - 5: "Saturday", - 6: "Sunday" -} -REQUEST_HEADERS = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '\ - Chrome/126.0.0.0 Safari/537.36" -} - - -@ToolManager.register_tool() -class QueryWeather(API): - name = "QueryWeather" - description = "This API is used to query weather forecast from the network, information including temperature, \ - humidness, wind force, weather condition." - weekday = WEEK_MAP.get(datetime.datetime.now(ZoneInfo("Asia/Shanghai")).weekday(), '') - input_parameters = { - - 'destination_city': {'type': 'str', 'description': 'the destination city user aim to query weather.'}, - "province": {'type': 'str', 'description': 'The province corresponding to the city'}, - "date": {'type': 'str', - 'description': ("The date of the user want to query, today is" + - f"{datetime.date.today()}, and today is {weekday}, " + - "please reason the date from user's query, and format with YYYY-MM-DD,") - }, - 'requirement': {'type': 'str', 'description': 'The more requirement of weather mentioned by the user'}, - } - output_parameters = { - "forecast": {'type': 'str', - 'description': 'the weather forecast information'}, - } - - example = ( - """ - { - "destination_city": "ShenZhen", - "province": "GuangDong", - "date": "2022-10-01" - } - """) - - def get_forecast(self, url, param, city=""): - headers = REQUEST_HEADERS - response = requests.get(url, params=param, headers=headers, timeout=5) - if response.status_code != 200: - logger.error(f"获取网页{url}内容失败") - raise Exception(f"获取网页{url}内容失败") - content = response.content - text = json.loads(content) - return text.get("data") - - def get_city2province(self, url, city): - headers = REQUEST_HEADERS - params = { - "city": city, - "source": "pc" - } - response = requests.get(url, params=params, headers=headers, timeout=5) - if response.status_code != 200: - logger.error(f"获取网页{url}内容失败") - raise Exception(f"获取网页{url}内容失败") - content = response.content - text = json.loads(content) - return text.get("data") - - def format_weather(self, weekly_weather): - # 精简输入 - key_keeps = [ - 'day_weather', 'day_wind_direction', 'day_wind_power', - 'max_degree', 'min_degree', 'night_weather' - ] - summary_copy = [] - for key, info in weekly_weather.items(): - time = info.get('time', key) - daily = {} - if isinstance(info, dict): - info_keeps = {k: info[k] for k in key_keeps if k in info} - daily[time] = info_keeps - summary_copy.append(daily) - return summary_copy[:5] - - def format_request_param(self, data, weather_type): - for key, value in data.items(): - city2province = value.replace(" ", "").split(",") - data[key] = city2province - # 遇到城市同名,认为是市的概率大于县 - _, max_probablity = min(data.items(), key=lambda item: len(item[1])) - if len(max_probablity) >= 2: - province = max_probablity[0] - city = max_probablity[1] - country = max_probablity[2] if len(max_probablity) >= 3 else "" - params = { - "source": "pc", # 请求来源,可以填 pc 即来自PC端 - "province": province, # 省, - "city": city, # 市, - "country": country, # 县区 - "weather_type": weather_type - } - return params - - def call(self, input_parameter, **kwargs): - os.environ['CURL_CA_BUNDLE'] = '' - urllib3.disable_warnings() - des = input_parameter.get('destination_city') - departure_date = input_parameter.get("date") - weather_type = "forecast_24h" - - try: - if des is None: - return self.make_response(input_parameter, results="", success=False, exception="") - try: - data = self.get_city2province("https://wis.qq.com/city/like", des) - except Exception as e: - logger.error(e) - e = str(e) - return self.make_response(input_parameter, results=e, success=False, exception=e) - if len(data) == 0: - return self.make_response(input_parameter, - results="未能找到所查询城市所在的省份或市", success=False, exception="") - - params = self.format_request_param(data, weather_type) - try: - forecast = self.get_forecast( - "https://wis.qq.com/weather/common", params) - except Exception as e: - e = str(e) - return self.make_response(input_parameter, results=e, success=False, exception=e) - weekly_weather = forecast.get(weather_type) - summary_copy = self.format_weather(weekly_weather) - if departure_date is None: - res = { - 'forecast': summary_copy - } - return self.make_response(input_parameter, results=res, exception="") - - try: - formated_departure = datetime.datetime.strptime( - departure_date, "%Y-%m-%d").date() - except ValueError as e: - # 默认当前日期 - formated_departure = datetime.date.today() - gaps = (formated_departure - datetime.date.today()).days - weather_summary = summary_copy[gaps + 1:] - - if len(weather_summary) == 0: - weather_summary = "**抱歉,我最多只能查询最近5天的天气情况,例如下面是我将为你提供最近的天气预报**:\n" + \ - json.dumps(summary_copy, ensure_ascii=False, indent=4) - res = { - 'forecast': weather_summary - } - except Exception as e: - logger.error(e) - e = str(e) - return self.make_response(input_parameter, results=e, - success=False, exception=e) - else: - return self.make_response(input_parameter, results=res, exception="") diff --git a/AgentSDK/samples/tools/tool_summary.py b/AgentSDK/samples/tools/tool_summary.py deleted file mode 100644 index 6a6469fdd..000000000 --- a/AgentSDK/samples/tools/tool_summary.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from agent_sdk.toolmngt.api import API -from agent_sdk.toolmngt.tool_manager import ToolManager - - -@ToolManager.register_tool() -class PlanSummary(API): - name = "PlanSummary" - description = "this api uesed to summary all the travel plan." - input_parameters = { - 'attractions': {'type': 'str', 'description': "the planned arrangement of attraction."}, - 'accomadation': {'type': 'str', 'description': "the accomodation information"}, - 'transport': {'type': 'str', 'description': "the transport information"}, - 'weather': {'type': 'str', 'description': "Weather information for the next few days"}, - 'duration': {'type': 'str', 'description': "The days of travel"}, - } - - output_parameters = { - 'summary': {'type': 'str', 'description': 'Summary all the plan of this travel'}, - } - - example = "PlanSummary[attractions,hotel,flight] will summary all the plan of travel inculed attractions,'\ - accomadation,and transport information" - example = ( - """ - { - "attractions": "London Bridge, any of several successive structures spanning the River Thames between '\ - Borough High Street in Southwark and King William Street.", - "accomadation": "Park Plaza London Riverbank In the heart of London, with great transport connections, '\ - culture, shopping, and green spaces", - "transport": "10 hours from Beijing to London cost $1000.", - } - """) - - def __init__(self): - pass - - def format_tool_input_parameters(self, llm_output) -> dict: - return llm_output if llm_output else {} - - - def call(self, input_parameters, **kwargs): - # 总的只能输入2500个字左右 - attraction = input_parameters.get('attractions') - hotel = input_parameters.get('accomadation') - transport = input_parameters.get('transport') - weather = input_parameters.get("weather") - duration = input_parameters.get("duration") - - res = "" - if duration is not None: - res += f"【用户需要旅行的天数】:{duration}天\n" - if attraction is not None: - res = res + f"【景点汇总】:\n{str(attraction)[:1000]}\n" - if hotel is not None: - res = res + f"【住宿安排】:\n{str(hotel)[:500]}\n" - if transport is not None: - res = res + f"【交通安排】:\n{str(transport)[:500]}\n" - if weather is not None: - res = res + f"【未来几天的天气情况】:\n{str(weather)[:500]}\n" - summary = { - "summary": res - } - return self.make_response(input_parameters, results=summary, exception="") \ No newline at end of file diff --git a/AgentSDK/samples/tools/web_summary_api.py b/AgentSDK/samples/tools/web_summary_api.py deleted file mode 100644 index a91c69f56..000000000 --- a/AgentSDK/samples/tools/web_summary_api.py +++ /dev/null @@ -1,178 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - - -import asyncio -import os -import re -from datetime import datetime, timezone -from concurrent.futures import ThreadPoolExecutor, wait, as_completed - -import aiohttp -import requests -import tiktoken -import urllib3 -from bs4 import BeautifulSoup -from loguru import logger -from samples.tools.duck_search import call_duck_duck_go_search - - -def check_number_input(num, crow): - if not num.isdigit(): - return False - num = int(num) - if num > crow: - return False - return True - - -async def curl_web(url): - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '\ - Chrome/126.0.0.0 Safari/537.36" - } - async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False, limit=25), trust_env=True, - headers=headers, timeout=aiohttp.ClientTimeout(total=5)) as session: - async with session.get(url) as response: - res = await response.text() - return res - - -class WebSummary: - encoder = tiktoken.get_encoding("gpt2") - - @classmethod - def get_detail_copy(cls, url, summary_prompt): - os.environ['CURL_CA_BUNDLE'] = '' # 关闭SSL证书验证 - urllib3.disable_warnings() - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '\ - Chrome/126.0.0.0 Safari/537.36" - } - try: - mommt = datetime.now(tz=timezone.utc) - logger.info(f"start request website: {mommt.strftime('%Y-%m-%d %H:%M:%S')},{url}") - response = requests.get( - url, headers=headers, timeout=(3, 3), stream=True) - mommt = datetime.now(tz=timezone.utc) - logger.info(f"finish request website: {mommt.strftime('%Y-%m-%d %H:%M:%S')},{url}") - if response.status_code != 200: - logger.error(f"获取网页{url}内容失败") - return '', f"获取网页{url}内容失败" - - content = response.content - bsobj = BeautifulSoup(content, 'html.parser') - txt = bsobj.get_text() - text = re.sub(r'\n{2,}', '\n', txt).replace(' ', '') - text = re.sub(r'\n{2,}', '\n', text) - except Exception as e: - logger.error(e) - return '', e - res = cls.generate_content(text, summary_prompt) - mommt = datetime.now(tz=timezone.utc) - logger.info(f"finish summary website: {mommt.strftime('%Y-%m-%d %H:%M:%S')},{url}") - return res, None - - @classmethod - async def get_details(cls, url, summary_prompt): - os.environ['CURL_CA_BUNDLE'] = '' - urllib3.disable_warnings() - try: - mommt = datetime.now(tz=timezone.utc) - logger.info(f"start request website: {mommt.strftime('%Y-%m-%d %H:%M:%S')},{url}") - response = await curl_web(url) - mommt = datetime.now(tz=timezone.utc) - logger.debug(f"finish request website: {mommt.strftime('%Y-%m-%d %H:%M:%S')},{url}") - content = response - bsobj = BeautifulSoup(content, 'html.parser') - txt = bsobj.get_text() - text = re.sub(r'\n{2,}', '\n', txt).replace(' ', '') - text = re.sub(r'\n{2,}', '\n', text) - if 'PleaseenableJSanddisableanyadblocker' in text: - text = "" - except Exception as e: - if len(str(e)) == 0: - e = Exception(f"error type: {type(e)}, when request the url: {url}") - logger.error(e) - return '', e - if len(text) == 0: - return "", "no valid website content" - res = cls.generate_content(text, summary_prompt) - mommt = datetime.now(tz=timezone.utc) - logger.info(f"finish summary website: {mommt.strftime('%Y-%m-%d %H:%M:%S')},{url}") - return res, None - - @classmethod - def summary_call(cls, web, max_summary_number, summary_prompt): - title = web.get("title", "") - url = web.get("href") - snippet = web.get("body", "") - web_summary = {} - if url is None: - return web_summary - - web_summary['title'] = title - web_summary['url'] = url - try: - content, err = asyncio.run(cls.get_details(url, summary_prompt)) - except Exception as e: - logger.error(e) - if not isinstance(content, str) or len(content) == 0 or content == "【无】": - web_summary['snippet'] = snippet - else: - web_summary['content'] = content - - return web_summary - - @classmethod - def web_summary(cls, keys, search_num, summary_num, summary_prompt, llm): - logger.add('app.log', level='DEBUG') - cls.llm = llm - try: - mommt = datetime.now(tz=timezone.utc) - logger.debug(f"start duck duck go search: {mommt.strftime('%Y-%m-%d %H:%M:%S')}") - if isinstance(keys, list): - keys = " ".join(keys) - search_result = call_duck_duck_go_search(keys, search_num) - mommt = datetime.now(tz=timezone.utc) - logger.debug(f"finish duck duck go search: {mommt.strftime('%Y-%m-%d %H:%M:%S')}") - except Exception as e: - logger.error(e) - return [] - - max_summary_number = summary_num - - webs = [] - with ThreadPoolExecutor(max_workers=3) as executor: - futures = [] - for web in search_result: - thread = executor.submit( - cls.summary_call, web, max_summary_number, summary_prompt) - futures.append(thread) - for future in as_completed(futures): - webs.append(future.result()) - wait(futures) - return webs - - @classmethod - def build_summary_prompt(cls, query, prompt): - max_input_token_num = 4096 - if len(query) == 0: - return prompt.format(text=query) - input_token_len = len(WebSummary.encoder.encode(query)) - prompt_len = len(WebSummary.encoder.encode(prompt)) - clip_text_index = int( - len(query) * (max_input_token_num - prompt_len) / input_token_len) - clip_text = query[:clip_text_index] - return prompt.format(input=clip_text) - - @classmethod - def generate_content(cls, query, prompt): - max_tokens = 1000 - try: - pmt = WebSummary.build_summary_prompt(query, prompt) - output = cls.llm(prompt=pmt, max_tokens=max_tokens) - except Exception as e: - logger.error(e) - return e - return output diff --git a/AgentSDK/samples/travel_agent_demo/front/chat_bot_release.py b/AgentSDK/samples/travel_agent_demo/front/chat_bot_release.py deleted file mode 100644 index 28cbb7359..000000000 --- a/AgentSDK/samples/travel_agent_demo/front/chat_bot_release.py +++ /dev/null @@ -1,58 +0,0 @@ -import streamlit as st -from samples.travel_agent.travelagent import TravelAgent - -if __name__ == "__main__": - st.set_page_config( - page_title="旅游规划agent" - ) - st.logo("logo.jpg") - st.markdown('

旅游规划Agent

', unsafe_allow_html=True) - - - placeholder1 = st.empty() - placeholder2 = st.empty() - placeholder3 = st.empty() - - if "messages" not in st.session_state: - st.session_state.messages = [] - - if "aagent" not in st.session_state: - st.session_state.agent = TravelAgent() - - with placeholder1: - container = st.container(height=300, border=False) - with placeholder3: - _, col2, _ = st.columns([1, 20, 1]) - helloinfo = """

您好,我是旅游规划agent,擅长旅行规划、景点攻略查询

-

例如:从北京到西安旅游规划

-

例如:西安有哪些免费的博物馆景点

-

例如:查一下西安的酒店

""" - with col2: - st.markdown(helloinfo, unsafe_allow_html=True) - - for message in st.session_state.messages: - with st.chat_message(message["role"]): - st.empty() - st.markdown(message["content"]) - - prompt = st.chat_input("send message") - if prompt is not None: - st.session_state.messages.append({"role":"user", "content":prompt}) - placeholder1.empty() - placeholder2.empty() - placeholder3.empty() - - agent = st.session_state["agent"] - - with st.chat_message("user"): - st.markdown(prompt) - - with st.chat_message("assistant"): - with st.spinner("thinking..."): - response = agent.run(query=prompt, stream=True) - if isinstance(response, str): - st.markdown(response) - else: - response = st.write_stream(response) - - st.session_state.messages.append({"role":"assistant", "content":response}) \ No newline at end of file diff --git a/AgentSDK/samples/travel_agent_demo/travelagent.py b/AgentSDK/samples/travel_agent_demo/travelagent.py deleted file mode 100644 index e57a27129..000000000 --- a/AgentSDK/samples/travel_agent_demo/travelagent.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. - -from abc import ABC -import argparse -from loguru import logger - -from agent_sdk.llms.llm import get_llm_backend, BACKEND_OPENAI_COMPATIBLE -from agent_sdk.agentchain.recipe_agent import RecipeAgent -from agent_sdk.agentchain.router_agent import RouterAgent -from agent_sdk.agentchain.tool_less_agent import ToollessAgent -from agent_sdk.agentchain.base_agent import AgentRunResult - -from samples.tools.tool_query_accommodations import QueryAccommodations -from samples.tools.tool_query_transports import QueryTransports -from samples.tools.tool_query_attractions import QueryAttractions -from samples.tools.tool_summary import PlanSummary -from samples.tools.tool_general_query import GeneralQuery -from samples.tools.tool_query_weather import QueryWeather - - -PESUEDE_CODE = """步骤1:根据用户问题中对景点相关的需求,从网络中搜索相关的景点信息 -步骤2:根据用户的问题,从网络中查询相关的出行交通信息。 -步骤3:根据用户的问题,从网络中搜索相关的住宿和酒店信息; -步骤4:根据用户的问题,查询用户需要的城市天气情况; -步骤5:总结以上的出行信息、景点游玩、住宿信息等。""" -TRANSPORT_INST = """步骤1:根据用户的输入问题,从网络中查询相关的出行交通信息""" -ATTRACTION_INST = "步骤1:根据用户问题中对景点相关的需求,从网络中搜索相关的景点信息" -HOTEL_INST = """步骤1:根据用户的问题,从网络中搜索相关的住宿和酒店信息,""" -WEATHER_INST = "步骤一:根据用户的问题,查询用户需要的城市天气情况" -OTHER_INST = """步骤1:根据用户的输入,从互联网中查询相关的解答""" - - -GENERAL_FINAL_PROMPT = """你是一个擅长文字处理和信息总结的智能助手,你的任务是将提供的网页摘要信息进行总结,并以markdown的格式进行返回, -请添加适当的词语,使得语句内容连贯,通顺 -请将content和snippet的信息进行综合处理,进行总结,生成一个段落。 -涉及到url字段时,使用超链接的格式将网页url链接到网页title上。 -参数介绍】: -title:网页标题 -url:网页链接 -snippet:网页摘要信息 -content:网页的内容总结 -下面是JSON格式的输入: -{text} -请生成markdown段落:""" - -WEATHER_FINAL_PROMPT = """你是一个擅长文字处理和信息总结的智能助手, -当前的工作场景是:天气出行建议;输入的内容是JSON格式的用户所查询城市未来的天气预报,请将这些信息总结为的自然语言展示天气预报的信息,并对用户的出游给除建议, -根据天气的情况,你可以做出一些出行建议,比如是否需要雨具、防晒、保暖等 -请添加适当的词语,使得语句内容连贯,通顺,并尽可能保留输入的信息和数据,但不要自行杜撰信息。 -提供的信息以JSON的格式进行展示 -【参数介绍】: -date:日期 -day_weather:白天的天气情况 -day_wind_direction:白天风向 -day_wind_power: 白天风力 -night_weather:夜晚的天气情况 -night_wind_direction:夜晚风向 -night_wind_power: 夜晚风力 -max_degree: 最高温 -min_degree:最低温 -下面是JSON格式的输入: -{text} -请生成markdown段落: -""" -PLANNER_FINAL_PROMPT = """你是一个擅长规划和文字处理的智能助手,你需要将提供的信息按照下面的步骤撰写一份旅游攻略,输出markdown格式的段落, -你可以添加适当的语句,使得段落通顺,但不要自己杜撰信息。 -步骤】 -1. 根据【用户需要旅行的天数】,将输入的景点分配到每一天的行程中,每天2-3个景点,并介绍景点的详细情况 -2. 叙述输入中推荐的住宿情况,详细介绍酒店的详细情况,和预定链接 -3. 叙述输入中查询的交通安排,详细介绍每个出行方案的价格、时间、时长等详细情况,和预定链接 -4. 介绍输入中天气预报的情况,根据天气的情况,你可以做出一些出行建议,比如是否需要雨具、防晒、保暖等 -【参数介绍】: -title:网页标题 -url:网页链接,满足用户需求的酒店筛选结果 -content:网页主要内容提取物 -snippet:网页摘要信息 -输入的信息以JSON格式,下面是的输入: -{text} -请生成markdown段落:""" - - - -TRAVEL_PLAN = "TRAVEL_PLAN" -QUERY_ATTRACTION = "QUERY_ATTRACTION" -QUERY_HOTEL = "QUERY_HOTEL" -QUERY_TRANSPORT = "QUERY_TRANSPORT" -QUERY_WEATHER = "QUERY_WEATHER" -OTHERS = "OTHERS" - -classifer = [TRAVEL_PLAN, QUERY_ATTRACTION, QUERY_HOTEL, QUERY_TRANSPORT, QUERY_WEATHER, OTHERS] - -INST_MAP = { - TRAVEL_PLAN :PESUEDE_CODE, - QUERY_ATTRACTION :ATTRACTION_INST, - QUERY_HOTEL:HOTEL_INST, - QUERY_TRANSPORT :TRANSPORT_INST, - QUERY_WEATHER :WEATHER_INST, - OTHERS:OTHER_INST -} - -FINAL_PMT_MAP = { - TRAVEL_PLAN :PLANNER_FINAL_PROMPT, - QUERY_ATTRACTION :GENERAL_FINAL_PROMPT, - QUERY_HOTEL:GENERAL_FINAL_PROMPT, - QUERY_TRANSPORT :GENERAL_FINAL_PROMPT, - QUERY_WEATHER :WEATHER_FINAL_PROMPT, - OTHERS: GENERAL_FINAL_PROMPT - -} - -TOOL_LIST_MAP = { - TRAVEL_PLAN :[QueryAccommodations, QueryAttractions, QueryTransports, PlanSummary, QueryWeather], - QUERY_ATTRACTION :[QueryAttractions], - QUERY_HOTEL:[QueryAccommodations], - QUERY_TRANSPORT :[QueryTransports], - QUERY_WEATHER : [QueryWeather], - OTHERS:[] -} - -intents = { - TRAVEL_PLAN :"询问旅行规划,问题中要求旅游项目日程安排、交通查询、查询当地住宿等方面的能力", - QUERY_ATTRACTION :"查询旅游项目、景区、旅游活动", - QUERY_HOTEL: "仅查询酒店和住宿信息", - QUERY_TRANSPORT : "与现实中出行、乘坐交通、如高铁、动车、飞机、火车等相关的意图", - QUERY_WEATHER :"包括气温、湿度、降水等与天气、天气预报相关的意图", - OTHERS :"与旅游场景不相干的查询" -} - - - -class TalkShowAgent(ToollessAgent, ABC): - def __init__(self, llm, prompt="你的名字叫昇腾智搜,是一个帮助用户完成旅行规划的助手,你的能力范围包括:'\ - 目的地推荐、行程规划、交通信息查询、酒店住宿推荐、旅行攻略推荐,请利用你的知识回答问题,这是用户的问题:{query}", - **kwargs): - super().__init__(llm, prompt, **kwargs) - self.query = "" - - def _build_agent_prompt(self, **kwargs): - return self.prompt.format( - query=self.query - ) - - -class TravelAgent: - def __init__(self, base_url, api_key, llm_name): - self.llm = get_llm_backend(backend=BACKEND_OPENAI_COMPATIBLE, - base_url=base_url, api_key=api_key, llm_name=llm_name).run - - def route_query(self, query): - router_agent = RouterAgent(llm=self.llm, intents=intents) - classify = router_agent.run(query).answer - if classify not in classifer or classify == OTHERS: - return TalkShowAgent(llm=self.llm) - return RecipeAgent(name=classify, - description="你的名字叫昇腾智搜,是一个帮助用户完成旅行规划的助手,你的能力范围包括:目的地推荐、行程规划、交通信息查询、酒店住宿推荐、旅行攻略推荐", - llm=self.llm, - tool_list=TOOL_LIST_MAP[classify], - recipe=INST_MAP[classify], - max_steps=3, - max_token_number=4096, - final_prompt=FINAL_PMT_MAP[classify]) - - def run(self, query, stream): - agent = self.route_query(query) - return agent.run(query, stream=stream) - - -def get_args(): - parse = argparse.ArgumentParser() - parse.add_argument("--model_name", type=str, default="Qwen1.5-32B-Chat", help="OpenAI客户端模型名") - parse.add_argument("--base_url", type=str, default="http://10.44.115.108:1055/v1", help="OpenAI客户端模型地址") - parse.add_argument("--api_key", type=str, default="EMPTY", help="OpenAI客户端api key") - return parse.parse_args().__dict__ - -if __name__ == "__main__": - args = get_args() - base_url = args.pop("base_url") - api_key = args.pop("api_key") - llm_name = args.pop("model_name") - - llm = get_llm_backend(backend=BACKEND_OPENAI_COMPATIBLE, - base_url=base_url, api_key=api_key, llm_name=llm_name).run - query = "帮我制定一份从北京到上海6天的旅游计划" - - travel_agent = TravelAgent(base_url, api_key, llm_name) - res = travel_agent.run(query, stream=False) - if isinstance(res, AgentRunResult): - logger.info("-----------run agent success-------------") - logger.info(res.answer) - else: - for char in res: - logger.debug(char) -- Gitee