Modelscope Agent实操(六):添加涂鸦作画能力到Modelscope-Agent中

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,内容安全 1000次 1年
对象存储 OSS,恶意文件检测 1000次 1年
简介: 在本文中,我们将展示如何将一个包含异步的api接口封装成tool并被agent在chat过程中调用执行的过程

引导

在本文中,我们将展示如何将一个包含异步的api接口封装成tool并被agent在chat过程中调用执行的过程,具体包括如下几步:

  • 了解Agent原理, Modelscope-Agent框架
  • 测试并了解API特性
  • 将API封装到一个tool里面,并测试效果
  • 通过agent的方式进行调用
  • agentfabric中使用该API

本文添加的api接口即为涂鸦作画

涂鸦作画能力

这是一种图生图能力,一张草图搭配一段描述生成内容丰富的图片能力。

举例说明譬如:

image.png

选用相关接口采用 dashscope的涂鸦作画接口(https://help.aliyun.com/zh/dashscope/developer-reference/tongyi-wanxiang-api-for-doodle?spm=a2c4g.11186623.0.0.4e534393H1eB3n

环境准备

环境:

  • 系统:任何系统均可,推荐使用魔搭镜像
  • LLM:  Dashscope 上的qwen-max(需要申请账号有免费token使用)
  • 框架:Modelscope-Agent

其他:

可选参考图:http://synthesis-source.oss-accelerate.aliyuncs.com/lingji/datasets/QuickDraw_sketches_final/cat/4503626191994880.png

框架熟悉

  1. 熟悉Agent
  2. image.png
  1. 熟悉Modelscope-Agent代码框架:

https://github.com/modelscope/modelscope-agent

接口实验

  1. 异步请求生成图片
curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/image-synthesis' \
--header 'X-DashScope-Async: enable' \
--header 'Authorization: Bearer <your dashscope api token>' \
--header 'Content-Type: application/json' \
--header 'X-DashScope-OssResourceResolve: enable' \
--data '{
  "input": {
    "prompt": "绿色的猫",
    "sketch_image_url": "http://synthesis-source.oss-accelerate.aliyuncs.com/lingji/datasets/QuickDraw_sketches_final/cat/4503626191994880.png"
  },
  "model": "wanx-sketch-to-image-lite"
}'

返回

{"output":{"task_status":"PENDING","task_id":"76a71d5b-8fc5-4d47-8ef8-c16af80951f3"},"request_id":"1ad6a3f4-8a80-9118-b805-4515376a9404"}
  1. 状态查询
curl -X GET \
--header 'Authorization: Bearer <your dashscope api token>' \
https://dashscope.aliyuncs.com/api/v1/tasks/76a71d5b-8fc5-4d47-8ef8-c16af80951f3

返回

{"request_id":"5441c445-ec10-963e-9c74-8907e507d1e2","output":{"task_id":"76a71d5b-8fc5-4d47-8ef8-c16af80951f3","task_status":"SUCCEEDED","submit_time":"2024-07-02 23:07:03.292","scheduled_time":"2024-07-02 23:07:03.317","end_time":"2024-07-02 23:07:15.401","results":[{"url":"https://dashscope-result-hz.oss-cn-hangzhou.aliyuncs.com/1d/db/20240702/96f6710c/0b3c9685-1683-4843-87b6-f0ce9bfe8972-1.png?Expires=1720019235&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=kdVTIwCb9OTr6V0vTRnnqWqpt4Q%3D"}],"task_metrics":{"TOTAL":1,"SUCCEEDED":1,"FAILED":0}},"usage":{"image_count":1}}

新建工具到Modelscope-Agent

  1. 注册新工具链路解读:
  1. register_tool:用于框架层面注册tool,并唯一标识名字
  2. description,name 以及parameters对齐 openai的tool calling格式,方便tool args的生成
  3. call 具体执行 tool的入口function
import os
import time
import json
import requests
from modelscope_agent.constants import BASE64_FILES, LOCAL_FILE_PATHS, ApiNames
from modelscope_agent.tools.base import BaseTool, register_tool
from modelscope_agent.utils.utils import get_api_key, get_upload_url
from requests.exceptions import RequestException, Timeout
MAX_RETRY_TIMES = 3
WORK_DIR = os.getenv('CODE_INTERPRETER_WORK_DIR', '/tmp/ci_workspace')
@register_tool('sketch_to_image')
class SketchToImage(BaseTool):
    description = '调用sketch_to_image api通过图片加文本生成图片'
    name = 'sketch_to_image'
    parameters: list = [{
        'name': 'input.sketch_image_url',
        'description': '用户上传的照片的相对路径',
        'required': True,
        'type': 'string'
    }, {
        'name': 'input.prompt',
        'description': '详细描述了希望生成的图像具有什么样的特点',
        'required': True,
        'type': 'string'
    }]
    def call(self, params: str, **kwargs) -> str:
        pass
  1. 添加涂鸦作画能力核心链路到tool:

a. 解析入参,生成图片url

def _parse_input(self, *args, **kwargs):
        kwargs = super()._parse_files_input(*args, **kwargs)
        restored_dict = {}
        for key, value in kwargs.items():
            if '.' in key:
                # Split keys by "." and create nested dictionary structures
                keys = key.split('.')
                temp_dict = restored_dict
                for k in keys[:-1]:
                    temp_dict = temp_dict.setdefault(k, {})
                temp_dict[keys[-1]] = value
            else:
                # if the key does not contain ".", directly store the key-value pair into restored_dict
                restored_dict[key] = value
        kwargs = restored_dict
        image_path = kwargs['input'].pop('sketch_image_url', None)
        if image_path and image_path.endswith(('.jpeg', '.png', '.jpg')):
            # 生成 image_url,然后设置到 kwargs['input'] 中
            # 复用dashscope公共oss
            if LOCAL_FILE_PATHS not in kwargs:
                image_path = f'file://{os.path.join(WORK_DIR,image_path)}'
            else:
                image_path = f'file://{kwargs["local_file_paths"][image_path]}'
            image_url = get_upload_url(
                model=
                'wanx-sketch-to-image-lite',
                file_to_upload=image_path,
                api_key=os.environ.get('DASHSCOPE_API_KEY', ''))
            kwargs['input']['sketch_image_url'] = image_url
        else:
            raise ValueError('请先上传一张正确格式的图片')
        kwargs['model'] = 'wanx-sketch-to-image-lite'
        print('草图生图的tool参数:', kwargs)
        return kwargs

b.调用异步请求生成接口

def call(self, params: str, **kwargs) -> str:
        params = self._verify_args(params)
        if isinstance(params, str):
            return 'Parameter Error'
        if BASE64_FILES in kwargs:
            params[BASE64_FILES] = kwargs[BASE64_FILES]
        remote_parsed_input = self._parse_input(**params)
        remote_parsed_input = json.dumps(remote_parsed_input)
        url = kwargs.get(
            'url',
            'https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/image-synthesis'
        )
        try:
            self.token = get_api_key(ApiNames.dashscope_api_key, **kwargs)
        except AssertionError:
            raise ValueError('Please set valid DASHSCOPE_API_KEY!')
        retry_times = MAX_RETRY_TIMES
        headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {self.token}',
            'X-DashScope-Async': 'enable'
        }
        # 解析oss
        headers['X-DashScope-OssResourceResolve'] = 'enable'
        while retry_times:
            retry_times -= 1
            try:
                response = requests.request(
                    'POST', url=url, headers=headers, data=remote_parsed_input)
                if response.status_code != requests.codes.ok:
                    response.raise_for_status()
                origin_result = json.loads(response.content.decode('utf-8'))
                self.final_result = origin_result
                return self._get_dashscope_image_result()
            except Timeout:
                continue
            except RequestException as e:
                raise ValueError(
                    f'Remote call failed with error code: {e.response.status_code},\
                    error message: {e.response.content.decode("utf-8")}')
        raise ValueError(
            'Remote call max retry times exceeded! Please try to use local call.'
        )

c.调用同步请求结果轮训接口

def _get_dashscope_result(self):
        if 'task_id' in self.final_result['output']:
            task_id = self.final_result['output']['task_id']
        get_url = f'https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}'
        get_header = {'Authorization': f'Bearer {self.token}'}
        retry_times = MAX_RETRY_TIMES
        while retry_times:
            retry_times -= 1
            try:
                response = requests.request(
                    'GET', url=get_url, headers=get_header)
                if response.status_code != requests.codes.ok:
                    response.raise_for_status()
                origin_result = json.loads(response.content.decode('utf-8'))
                get_result = origin_result
                return get_result
            except Timeout:
                continue
            except RequestException as e:
                raise ValueError(
                    f'Remote call failed with error code: {e.response.status_code},\
                    error message: {e.response.content.decode("utf-8")}')
        raise ValueError(
            'Remote call max retry times exceeded! Please try to use local call.'
        )
    def _get_dashscope_image_result(self):
        try:
            result = self._get_dashscope_result()
            while True:
                result_data = result
                output = result_data.get('output', {})
                task_status = output.get('task_status', '')
                if task_status == 'SUCCEEDED':
                    print('任务已完成')
                    # 取出result里url的部分,提高url图片展示稳定性
                    output_url = result['output']['results'][0]['url']
                    return f'![IMAGEGEN]({output_url})'
                elif task_status == 'FAILED':
                    raise Exception(output.get('message', '任务失败,请重试'))
                else:
                    # 继续轮询,等待一段时间后再次调用
                    time.sleep(0.5)  # 等待 0.5 秒钟
                    result = self._get_dashscope_result()
                    print(f'Running:{result}')
        except Exception as e:
            raise Exception('get Remote Error:', str(e))
  1. 测试用例确保功能完善
import os
import pytest
from modelscope_agent.tools.dashscope_tools.sketch_to_image import SketchToImage
from modelscope_agent.agents.role_play import RolePlay  # NOQA
IS_FORKED_PR = os.getenv('IS_FORKED_PR', 'false') == 'true'
@pytest.mark.skipif(IS_FORKED_PR, reason='only run modelscope-agent main repo')
def test_sketch_to_image():
    # 图片默认上传到default work_dir
    params = """{'input.sketch_image_url': 'sketch.png', 'input.prompt': '绿色的猫'}"""
    style_repaint = SketchToImage()
    res = style_repaint.call(params)
    assert (res.startswith('![IMAGEGEN](http'))
@pytest.mark.skipif(IS_FORKED_PR, reason='only run modelscope-agent main repo')
def test_sketch_to_image_role():
    role_template = '你扮演一个绘画家,用尽可能丰富的描述调用工具绘制各种风格的图画。'
    llm_config = {'model': 'qwen-max', 'model_server': 'dashscope'}
    # input tool args
    function_list = ['sketch_to_image']
    bot = RolePlay(
        function_list=function_list, llm=llm_config, instruction=role_template)
    response = bot.run('[上传文件 "sketch.png"],我想要一只绿色耳朵带耳环的猫')
    text = ''
    for chunk in response:
        text += chunk
    print(text)
    assert isinstance(text, str)
  1. 额外添加到modelscope_agent/tools/base.py 中注册
# 
register_map = {
    'sketch_to_image':
    'SketchToImage',
    'amap_weather':
    'AMAPWeather',
    'storage':
    'Storage',
    'web_search':
    'WebSearch',
    'image_gen':
    'TextToImageTool',
    'image_gen_lite':
    'TextToImageLiteTool',
  1. 类引用路径添加懒加载:modelscope_agent/tools/__init__.py
import sys
from ..utils import _LazyModule
from .contrib import *  # noqa F403
_import_structure = {
    'amap_weather': ['AMAPWeather'],
    'code_interpreter': ['CodeInterpreter'],
    'contrib': ['AliyunRenewInstanceTool'],
    'dashscope_tools': [
        'ImageEnhancement', 'TextToImageTool', 'TextToImageLiteTool',
        'ParaformerAsrTool', 'QWenVL', 'SambertTtsTool', 'StyleRepaint',
        'WordArtTexture', 'SketchToImage'
    ],
  1. 提交代码pull request到代码仓库

在AgentFabric中应用

Agentfabric能力介绍:

image.png

应用新增涂鸦作画能力:

  1. 在代码中修改tool_config.json,确保新tool能够被调用:apps/agentfabric/config/tool_config.json

image.png

  1. 在agentfabric中添加并使用新生成的tool

image.png

总结

  1. agent的可以通过外部tool,将LLM能力快速扩展到更广泛的应用场景
  2. tool的正确调用依赖LLM的指令理解和指令生成,因此需要正确的定义指令
  3. tool的调用在agent内部是同步的,因此对于一些调用异步接口需要等待的场景可以有多种办法,一种是在单步调用tool的时候等待结果产生并抛出结果,还有就是通过多个tool来让agent帮助你去做结果的查询。
相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
相关文章
|
7月前
|
人工智能 API 决策智能
Modelscope结合α-UMi:基于Modelscope的多模型协作Agent
基于单个开源小模型的工具调用Agent,由于模型容量和预训练能力获取的限制,无法在推理和规划、工具调用、回复生成等任务上同时获得比肩大模型等性能。
|
7月前
|
开发框架 数据可视化 Windows
如何提升大模型Agent的能力 ——LLM Agent框架 Modelscope-Agent 实战
本文介绍Agent到底是什么 ,如何进行优化,以及如何使用Agen框架。
|
7月前
|
自然语言处理 数据可视化 数据挖掘
Agent实操(四):Code Interpreter,生成二维码、视频、PDF转txt等炸裂功能
本文介绍Agent自带的Code Interpreter有哪些高级而实用的能力
Agent实操(四):Code Interpreter,生成二维码、视频、PDF转txt等炸裂功能
|
7月前
|
开发框架 API 决策智能
ModelScope-Agent框架再升级!新增一键配置多人聊天,配套开源多智能体数据集和训练
ModelScope-Agent是魔搭社区推出的适配开源大语言模型(LLM)的AI Agent(智能体)开发框架,借助ModelScope-Agent,所有开发者都可基于开源 LLM 搭建属于自己的智能体应用。在最新升级完Assistant API和Tool APIs之后,我们又迎来了多智能体聊天室的升级,通过几分钟快速配置即可搭建一个全新的聊天室。
|
7月前
|
自然语言处理 API 开发者
Agent实操(五):如何在本地/云端创建并发布更定制化的Agent
利于模型的微调优化,工具的本地部署,魔改代码
Agent实操(五):如何在本地/云端创建并发布更定制化的Agent
|
数据可视化 测试技术 API
Modelscope Agent实操(三):将API注册为tool,成为smart API,方便社区开发者调用
大家通过写python代码的方式来定制自己的tool,进一步扩展Agent的能力。
|
7月前
|
自然语言处理
在ModelScope中,你可以通过设置模型的参数来控制输出的阈值
在ModelScope中,你可以通过设置模型的参数来控制输出的阈值
195 1
|
7月前
|
API 语音技术
ModelScope-FunASR**有支持热词又支持时间戳的模型**。
【2月更文挑战第30天】ModelScope-FunASR**有支持热词又支持时间戳的模型**。
227 2
|
7月前
|
文字识别 并行计算 语音技术
ModelScope问题之下载模型文件报错如何解决
ModelScope模型报错是指在使用ModelScope平台进行模型训练或部署时遇到的错误和问题;本合集将收集ModelScope模型报错的常见情况和排查方法,帮助用户快速定位问题并采取有效措施。
951 3
|
7月前
|
人工智能 达摩院 自然语言处理
超好用的开源模型平台,ModelScope阿里达摩院
超好用的开源模型平台,ModelScope阿里达摩院
533 1