Files
agentUniverse/docs/guidebook/zh/How-to/定义与使用工具插件/工具插件适配API样例参考.md
2025-01-13 16:48:24 +08:00

19 KiB
Raw Permalink Blame History

Bing并发搜索api

api接口描述

使用该工具可以在bing中并发检索多条信息。

bing搜索需要https://www.searchapi.io/申请相应的api_key。该工具基于单个问题检索实现了并发多信息检索。

输入描述

入参inputs是一个json字符串内容是一个待检索list每个元素表示一条需要搜索的信息。

["贝多芬 交响乐", "肖邦 随想曲", "舒伯特 夜曲"]

输出描述

输出是对每个检索问题和结果的封装,以“--------------------------”隔开,格式如下

query: 贝多芬 交响乐
result: 世界音乐史上最伟大交响曲的缔造,被誉为“乐圣”的贝多芬,其一生中所创作的九大交响曲,“无人能望其项背”。其中有三首被世界纳入世界音乐史最伟大的十首交响曲之中,其余六首也在世界交响曲排名中名列前茅。
--------------------------
query: 肖邦 随想曲
result: E大调随想曲是肖邦作品中最著名的一首也是浪漫时期钢琴曲中的代表之一。
-------------------------
query: 舒伯特 夜曲
result: 《舒伯特小夜曲》是奥地利作曲家舒伯特1797-1828创作的歌曲他在欧洲音乐史上有“歌曲之王”的称誉。当时的民间传说认为天鹅将死的时候会唱出最动人的歌。

AU中封装该api工具

yaml配置

name: 'parallel_search_detail_api'
description: |
  使用该工具可以在bing中并发搜索多条信息
    <输入描述>
    入参inputs是一个json字符串内容是一个待检索list每个元素表示一条需要搜索的信息。
    
    <输出描述>
    输出是对每个检索问题和结果的封装,以“--------------------------”隔开。
    query: xxx
    result: xxx
    -------------------------
    query: xxx
    result: xxx
    -------------------------
    
    <工具输入示例>
      你想要搜索信息,如不同的几位音乐家的作品,且不需要返回详细结果的话,工具的输入应该是: 
        ["贝多芬 交响乐", "肖邦 随想曲", "舒伯特 夜曲"]
        
    <工具输出示例>
      工具的输出应该是: 
        query: 贝多芬 交响乐
        result: 世界音乐史上最伟大交响曲的缔造,被誉为“乐圣”的贝多芬,其一生中所创作的九大交响曲,“无人能望其项背”。其中有三首被世界纳入世界音乐史最伟大的十首交响曲之中,其余六首也在世界交响曲排名中名列前茅。
        --------------------------
        query: 肖邦 随想曲
        result: E大调随想曲是肖邦作品中最著名的一首也是浪漫时期钢琴曲中的代表之一。
        -------------------------
        query: 舒伯特 夜曲
        result: 《舒伯特小夜曲》是奥地利作曲家舒伯特1797-1828创作的歌曲他在欧洲音乐史上有“歌曲之王”的称誉。当时的民间传说认为天鹅将死的时候会唱出最动人的歌。
      
tool_type: 'api'
input_keys: ['inputs']
metadata:
  type: 'TOOL'
  module: 'au_expert_assistant.intelligence.agentic.tool.search.parallel_search_detail_api'
  class: 'ParallelSearchDetailApi'

代码实现参考

class ParallelSearchDetailApi(ZxzTool):
    async def execute(self, tool_input: ToolInput):
        try:
            json_input = tool_input.get_data("input")
            json_input = parse_and_check_json_markdown(json_input, ["input_params", "save_params"])
            query_list = json_input['input_params'].get('query', [])
            search_tool = ToolManager().get_instance_obj('knowledge_search_detail_api')

            executor_res = await asyncio.gather(
                *[search_tool.run(
                    query=query,
                    search_top=3
                ) for query in query_list]
            )
            result_str = ''

            for res in executor_res:
                result_str += f'query:' + res['query'] + '\n'
                result_str += f'result:' + res['search_results'][0] + '\n'
                result_str += '--------------------------\n'

            update_react_memory(
                name=json_input["save_params"]["name"],
                data=result_str,
                description=json_input["save_params"]["description"]
            )
            if json_input["save_params"].get("full_return"):
                return result_str
            return '执行成功,可继续下一步'
        except Exception as e:
            error_message = traceback.format_exc()
            return error_message

谷歌酒店搜索api

api接口描述

该api调用谷歌的开放api接口 https://serpapi.com/search 中的google_hotels引擎服务查询酒店相关信息该接口需要去谷歌api开放官网申请相应的api-key即可访问

输入描述

工具的输入input为json_markdown格式的字符串内容是下面的结构

{
  "input_params": {
    "query": "怡莱酒店南昌胜利路步行街店",
    "check_in_date": "2024-11-24",
    "check_out_date": "2024-11-25",
    "search_type": "name",
    "hotel_class": 3    
  },
  "save_params":{
    "name": "怡莱酒店南昌胜利路步行街店",
    "description":"记载了怡莱酒店南昌胜利路步行街店的具体住宿信息",
    "full_return": false
  }
}

必选参数:

query可以是具体酒店名称或者地名注意地名只能是巴黎、南昌这种地名。这个工具不支持复杂的搜索如巴黎民宿、南昌高档酒店

search_type有两种取值location和name当query是具体酒店名称时该取值为name地名的话则是location。

check_in_date和check_out_date为YYYY-MM-DD格式的入住和离店日期

可选参数:

hotel_class取值为范围为2-5表示酒店档次。

min_price和max_price表示筛选的价格区间但min_price和max_price取值不能为0。

save_params参数用于将最后结果归档。save_params包含三个属性归档名称name和结果内容的简要描述description以及是否表示需要返回完整结果的full_return

输出描述

输出为json字符串内容为检索到的hotel信息列表按匹配度从高到低排列每个hotel结构包含描述链接地址联系方式图片定位价格等信息。下面为示例

[{
  "type": "hotel",
  "name": "怡莱酒店(南昌胜利路步行街店)",
  "description": "怡莱酒店(南昌胜利路步行街店)位于南昌繁华市中心步行街建德观街,门即是中山路——胜利路步行街,门前是南昌知名小吃夜宵街——建德观街;紧靠八一大桥,距江南三大名...",
  "link": "https://www.h10hotels.com/en/barcelona-hotels/h10-port-vell?utm_source=google_my_business&utm_medium=boton_sitio_web&utm_campaign=hpv",
  "address": "0791-82075888",
  "phone": "+34 933 10 30 65",
  "phone_link": "tel:+34933103065",
  "gps_coordinates": {
    "latitude": 41.381571799999996,
    "longitude": 2.1838414999999998
  },
  "check_in_time": "3:00PM",
  "check_out_time": "12:00PM",
  "rate_per_night": {
    "lowest": "$123",
    "extracted_lowest": 123,
    "before_taxes_fees": "$100",
    "extracted_before_taxes_fees": 100
  },
  "total_rate": {
    "lowest": "$123",
    "extracted_lowest": 123,
    "before_taxes_fees": "$100",
    "extracted_before_taxes_fees": 100
  }
}]

AU中封装该api工具

name: 'hotel_search_api'
description: '使用该工具可以搜索酒店相关的信息。
    <输入描述>
    query可以是具体酒店名称或者地名注意地名只能是巴黎、南昌这种地名。这个工具不支持复杂的搜索如巴黎民宿、南昌高档酒店
    search_type有两种取值location和name当query是具体酒店名称时该取值为name地名的话则是location。
    check_in_date和check_out_date为YYYY-MM-DD格式的入住和离店日期
    可选参数:
      hotel_class取值为范围为2-5表示酒店档次。
      min_price和max_price表示筛选的价格区间但min_price和max_price取值不能为0。
    同时你也应当提供一个save_params参数用于将最后结果归档。save_params包含三个属性归档名称name和结果内容的简要描述description以及是否表示需要返回完整结果的full_return
    
    <输出描述>
    输出为json字符串内容为检索到的hotel信息列表按匹配度从高到低排列每个hotel结构包含描述链接地址联系方式图片定位价格等信息。
    
    <工具输入示例>
      你想要搜索指定酒店住宿信息时如怡莱酒店南昌胜利路步行街店2024-11-24到2024-11-25的房间信息且不需要返回详细结果的话工具的输入应该是: 
        ```json
        {
          "input_params": {
              "query": "怡莱酒店南昌胜利路步行街店",
              "check_in_date": "2024-11-24",
              "check_out_date": "2024-11-25",
              "search_type": "name",
              "hotel_class": 3    
          },
          "save_params":{
            "name": "怡莱酒店南昌胜利路步行街店",
            "description":"记载了怡莱酒店南昌胜利路步行街店的具体住宿信息",
            "full_return": false
          }
        }
        ```
    <工具输出示例>
      返回莱酒店南昌胜利路步行街店的检索结果为:
      [{
        "type": "hotel",
        "name": "怡莱酒店(南昌胜利路步行街店)",
        "description": "怡莱酒店(南昌胜利路步行街店)位于南昌繁华市中心步行街建德观街,门即是中山路——胜利路步行街,门前是南昌知名小吃夜宵街——建德观街;紧靠八一大桥,距江南三大名..."
        "link": "https://www.h10hotels.com/en/barcelona-hotels/h10-port-vell?utm_source=google_my_business&utm_medium=boton_sitio_web&utm_campaign=hpv",
        "address": "0791-82075888",
        "phone": "+34 933 10 30 65",
        "phone_link": "tel:+34933103065",
        "gps_coordinates": {
          "latitude": 41.381571799999996,
          "longitude": 2.1838414999999998
        },
        "check_in_time": "3:00PM",
        "check_out_time": "12:00PM",
        "rate_per_night": {
          "lowest": "$123",
          "extracted_lowest": 123,
          "before_taxes_fees": "$100",
          "extracted_before_taxes_fees": 100
        },
        "total_rate": {
          "lowest": "$123",
          "extracted_lowest": 123,
          "before_taxes_fees": "$100",
          "extracted_before_taxes_fees": 100
        }
      }]
    '
tool_type: 'api'
input_keys: ['input']
metadata:
  type: 'TOOL'
  module: 'au_expert_assistant.intelligence.agentic.tool.search.hotel_search_api'
  class: 'HotelSearchApi'

代码实现参考

class HotelSearchApi(Tool):
    api_key: Optional[str] = Field(
        default_factory=lambda: get_from_env("TOUR_SERP_API_KEY"))
    base_url: str = "https://serpapi.com/search"

    async def request_serpapi(self, url, name, check_in_date, check_out_date, **kwargs):
        language_version = FrameworkContextManager().get_context(
            "language_version")
        params = {
            "engine": "google_hotels",
            "q": name,
            "check_in_date": check_in_date,
            "check_out_date": check_out_date,
            "currency": "CNY" if language_version == "cn" else "USD",
            "gl": "cn",
            "hl": "zh-CN" if language_version == "cn" else "en",
            "api_key": self.api_key,
            **kwargs
        }
        for i in range(3):
            try:
                async with httpx.AsyncClient(verify=False) as client:
                    response = await client.get(url, params=params)
                    return json.loads(response.text)
            except Exception as e:
                print(e)
        return {'properties':[]}
    async def execute(self, tool_input: ToolInput):
        try:
            json_input = tool_input.get_data("input")
            json_input = parse_and_check_json_markdown(json_input, ["input_params", "save_params"])

            # 先获取酒店详细信息链接
            current_date = datetime.now()
            current_date = current_date.strftime("%Y-%m-%d")
            search_type = json_input["input_params"].pop('search_type')
            if search_type == 'name':
                json_input["input_params"].pop('hotel_class', None)
            if 'min_price' in json_input["input_params"]:
                json_input["input_params"]['min_price'] += 1
            check_in_date = json_input["input_params"].pop('check_in_date', current_date)
            check_in_date = check_in_date if check_in_date > current_date else current_date
            check_out_date = json_input["input_params"].pop('check_out_date', current_date)
            check_out_date = check_out_date if check_out_date > current_date else current_date

            result_dict = await self.request_serpapi(
                self.base_url,
                json_input["input_params"].pop('query'),
                check_in_date,
                check_out_date,
                **json_input["input_params"]
            )
            hotel_details = []

            keys = ['name', 'nearby_places', 'total_rate', 'amenities',
                    'excluded_amenities', "prices", "rate_per_night"]
            if not 'properties' in result_dict:
                print(result_dict)
                return "无相关酒店信息"
            for hotel in result_dict['properties']:
                if hotel['type'] in ['hotel', 'vacation rental'] and 'total_rate' in hotel and 'serpapi_property_details_link' in hotel:
                    hotel_details.append({key: hotel[key] for key in keys if key in hotel})
                    if search_type == 'name':
                        break
                    elif len(hotel_details)>7:
                        break

            if not hotel_details:
                return "无相关酒店信息"

            result_str = json.dumps(hotel_details, ensure_ascii=False)

            update_react_memory(
                name=json_input["save_params"]["name"],
                data=result_str,
                description=json_input["save_params"]["description"]
            )
            if json_input["save_params"].get("full_return"):
                return result_str
            return '执行成功,可继续下一步'
        except Exception as e:
            error_message = traceback.format_exc()
            return error_message

自定义api-宠物险咨询检索工具

api接口描述

基于ES自建索引实现对宠物险产品相关问题答案的检索。只针对“宠物医保”及“宠物医保体验版”产品有效。

输入描述

入参input为待检索的问题

“宠物医保”如何从基础版升级到尊享版?

输出描述

输出包含“提出的问题”以及“检索到的答案”两部分内容

提出的问题是:宠物医保投保对宠物年龄的要求是多少?

这个问题检索到的答案相关内容是:
knowledgeTitle: 多大年龄可以投保
knowledgeContent: <p>宠物医保这款产品的投、被保险人为具备完全民事行为的个人且须为同一人本产品仅限宠物主本人购买其承保的宠物须为被保险人以玩赏、陪伴为目的而合法饲养的、可明确鉴别身份的年龄为60天-10周岁的犬类或猫类宠物。</p>

检索答案包含knowledgeTitle和knowledgeContent分别代表检索的知识标题和内容最终回答一般取knowledgeContent即可。

AU中封装该api工具

yaml配置

name: 'pet_insurance_search_context_tool'
description: |
  #工具名称:宠物险产品信息检索工具
  
  #功能描述:提供宠物险产品相关问题答案的检索。只针对“宠物医保”及“宠物医保(体验版)”产品有效。
  
  #工具输入:待检索的问题。
  
  #工具输出:输出格式如下
  ------------------------
  提出的问题是:xxx

  这个问题检索到的答案相关内容是:
  knowledgeTitle: xxx
  knowledgeContent: xxx
  ------------------------
  其中检索答案包含knowledgeTitle和knowledgeContent分别代表检索的知识标题和内容最终回答一般取knowledgeContent即可。

  #工具输入输出示例:
    工具输入: 
    宠物医保投保对宠物年龄的要求是多少?
    
    工具输出: 
    提出的问题是:宠物医保投保对宠物年龄的要求是多少?
  
    这个问题检索到的答案相关内容是:
  
    knowledgeTitle: 多大年龄可以投保
    knowledgeContent: <p>宠物医保这款产品的投、被保险人为具备完全民事行为的个人且须为同一人本产品仅限宠物主本人购买其承保的宠物须为被保险人以玩赏、陪伴为目的而合法饲养的、可明确鉴别身份的年龄为60天-10周岁的犬类或猫类宠物。</p>

tool_type: 'api'
input_keys: ['input']
metadata:
  type: 'TOOL'
  module: 'sample_standard_app.intelligence.agentic.tool.pet_ins.pet_insurance_search_context_tool'
  class: 'SearchContextTool'

代码实现参考

class SearchContextTool(Tool):

    def execute(self, tool_input: ToolInput):
        question = tool_input.get_data('input')
        try:
            headers = {
                "Content-Type": "application/json",
            }
            # 要发送的数据
            data = {
                "sceneCode": "ant_fortune_insurance_property",
                "query": question,
                "decoderType": "ins_slot_v2",
                "inputMethod": "user_input",
                "userInfoMap": {
                    "consultantSceneCode": "ant_fortune_insurance_property",
                },
                "enterScene": {
                    "sceneCode": "ant_fortune_insurance_property",
                }
            }
            top_k = tool_input.get_data('top_k') if tool_input.get_data('top_k') else 2
            LOGGER.info(f"search context tool input: {data}")
            response = requests.post(PRE_API_URL, headers=headers, data=json.dumps(data, ensure_ascii=False))
            result = response.json()['result']
            recallResultTuples = result.get('recallResultTuples')

            context = f"提出的问题是:{question}\n\n这个问题检索到的答案相关内容是:\n\n"
            index = 0
            for recallResult in recallResultTuples:
                if index == top_k:
                    return context
                if recallResult.get('content'):
                    context += (f"knowledgeTitle: {recallResult.get('knowledgeTitle')}\n"
                                f"knowledgeContent: {recallResult.get('content')}\n\n")
                    index += 1
            return context
        except Exception as e:
            LOGGER.error(f"invoke search context tool failed: {str(e)}")
            raise e