人工编码和部署人工智能服务

部署人工智能服务的手动编码方法包括编写自定义代码来部署和管理人工智能服务。 这种方法能够完全控制部署过程,并允许定制以满足特定需求。

过程概述

下图展示了人工智能服务的编码过程。

您可以创建一个包含人工智能服务和项目内连接的笔记本。 人工智能服务能够捕捉RAG应用程序的逻辑,并包含生成函数,这是一个可部署的代码单元。 生成函数被提升到了部署空间,用于创建部署。 部署以REST API端点形式公开,可供其他应用程序访问。 您可以向REST API端点发送请求,使用部署的AI服务进行推理。 已部署的人工智能服务处理请求并返回响应。

手动编码使用案例

创建和部署人工智能服务的任务

请按照以下步骤创建、部署和管理人工智能服务:

  1. 创建人工智能服务 :使用 Python 在笔记本中定义人工智能服务。 人工智能服务必须满足作为人工智能服务部署的具体要求。
  2. 测试人工智能服务 :在本地测试人工智能服务的编码逻辑。
  3. 创建人工智能服务资产 :创建并测试人工智能服务后,必须将人工智能服务打包为可部署资产。
  4. 部署 AI 服务资产 :将 AI 服务资产作为在线或批处理部署。
  5. 测试人工智能服务的部署 :测试您部署的人工智能服务,用于在线推理或批量评分。
  6. 管理人工智能服务 :访问并更新部署详情。 从用户界面或通过编程缩放或删除部署。

在笔记本上创建人工智能服务

要部署人工智能服务,您可以直接在笔记本上创建人工智能服务。 您必须在 Python 中定义您的AI服务,并且必须满足特定要求。 要部署人工智能服务,您必须创建一个 Watson Machine Learning 资源库资产,并将 Python 文件上传到该资产。

使用 Python 客户端库定义人工智能服务

要使用 watsonx.ai Python 客户端库在笔记本中定义人工智能服务,请按照以下步骤操作:

  1. 要在 Python 中使用人工智能服务,请安装 ibm-watsonx-ai Python SDK:

    pip install ibm-watsonx-ai
    
  2. 安装 Python 客户端库后,初始化客户端并设置默认部署空间:

    from ibm_watsonx_ai import APIClient
    from ibm_watsonx_ai import Credentials
    
    credentials = Credentials(
        url=url, api_key=apikey
    )
    
    client = APIClient(credentials)
    client.set.default_space(space_id=space_id)
    
  3. 请使用以下布局在 Python 中定义您的AI服务。 根据您的使用情况,您必须至少包含以下功能之一作为嵌套功能:

    • generate()
    • generate_stream()
    • generate_batch()

    更多信息,请参阅创建人工智能服务的要求

    def basic_generate_demo(context, model="google/flan-t5-xl", **parameters):
    # "parameters" is a reserved argument and will be enabled in future
    
    # generate user token
    gen_token = context.generate_token()
    
        def generate(context):
            user_token = context.get_token()  # extract token from header
            user_headers = context.get_headers()
            json_body = context.get_json()
    
            # example 1: json
            return {
                "headers": {
                    "Content-Type": "application/json",
                    "user-custom-header": "my-header-x1",
                },
                "body": {
                    "model": model
                },
            }
    
        def generate_stream(context):
            user_token = context.get_token()  # extract token from header
            user_headers = context.get_headers()
            json_body = context.get_json()
    
            # return a generator
            data_to_stream = json_body.get("sse", "Default message!")
            for x in data_to_stream:
                yield x
    
        def generate_batch(input_data_references, output_data_reference):
            # generate user token
            gen_token = context.generate_token()
            # do something.
            # ...
    
        return generate, generate_stream, generate_batch
    

定义人工智能服务的要求

人工智能服务捕捉生成式人工智能用例(例如检索增强生成应用程序)的逻辑,并处理对部署端点 /ml/v4/deployments 的REST API调用。

请遵循以下准则来定义人工智能服务:

  • 创建一个 Python 功能。 您可以为函数指定任何名称。

  • 根据您的使用情况,您要部署的 Python 功能必须至少包含以下功能之一,作为其作用域中的嵌套函数:

    • generate()
    • generate_stream()
    • generate_batch()
  • 当您使用 watsonx.ai Python 客户端库保存包含外部函数引用的 Python 函数时,仅保存外部函数作用域(包括其嵌套函数)中的代码。 外部函数范围之外的代码不会被保存,因此在您部署函数时不可用。

定义generate()函数的准则

generate() 功能可用于处理您的授权令牌。 该函数处理对推理端点 /ml/v4/deployments/{id_or_name}/ai_service 的REST调用。

请按照以下指南在人工智能服务中定义 generate() 功能:

  • 您必须使用 generate 定义函数。
  • 您只能向 generate() 函数提供一个参数: context
  • generate() 函数必须返回数据类型为 dict (字典)的值。
  • 可选 :您可以选择指定 bodyheader 键。

示例

def generate(context):
    user_token = context.get_token()
    headers = context.get_headers()
    json_body = context.get_json()

    return {
        "headers": {
             "Content-Type": "text/plain"
        },
        "body": "Hello WatsonX"
    }

定义generate_stream()函数的准则

您可以使用 generate_stream() 功能来生成需要流式传输的AI用例。 该功能处理对服务器发送事件(SSE)推断端点 POST /ml/v4/deployments/{id_or_name}/ai_service_stream 的REST调用。

请按照以下指南在人工智能服务中定义 generate_stream() 功能:

  • 您必须使用 generate_stream 定义函数。
  • 您只能向 generate_stream() 函数提供一个参数: context

示例

def generate_stream(context):
    user_token = context.get_token()
    headers = context.get_headers()
    json_body = context.get_json()

    for x in ["Hello", "WatsonX", "!"]:
        yield x

输出

id: 1
event: message
data: 你好

id: 2
event: message
data: WatsonX

id: 3
event: message
data: !

id: 4
event: eos

如果流式传输的块中包含换行符 (\n),则换行符后面的字符在流式响应中可能无法正确显示。 出现这种情况是因为某些流式传输实现将换行符视为消息边界。

可能的解决方法:

1. 转义换行符:

DATA = {"a": ["1\\n2", "3"]}

2. 流式传输 JSON 对象(推荐):

DATA = {
    "a": [
        {"id": 1, "value": "Hello\nWorld"},
        {"id": 2, "value": "Foo\nBar"}
    ]
}

for chunk in DATA["a"]:
    yield chunk

流式传输结构化 JSON 对象:

  • 消除由换行符分隔符引起的歧义
  • 常用于现代 AI 流式传输 API

定义generate_batch()函数的指南

generate_batch() 功能可用于需要批量推理的用例。 此功能处理对作业端点 /ml/v4/deployments_jobs 的REST API调用。

请按照以下指南在人工智能服务中定义 generate_batch() 功能:

  • 您必须使用 generate_batch() 定义函数。

示例

def generate_batch(input_data_references: list[dict], output_data_reference: dict):
    # context from outer function is visible
    batch_token = context.generate_token()
    print(f"batch_token: {batch_token[-5:]}", flush=True)
    print(
        f"generate_batch:\n{input_data_references=}\n{output_data_reference=}",
        flush=True,
    )

使用异步 AI 服务

主要功能

  • generate_stream()generate()对所有三种 AI 服务方法(、和)均支持异步操作 generate_batch()
  • 运行时对同步和异步执行的自动检测
  • 支持流式处理和批处理工作流

示例:异步 AI 服务

async def qna_with_rag_ai_service(context):
    """
    Async AI Service example with all three methods
    """
    from typing import Any, Dict

    async def stream_chunks(text):
        for ch in text:
            yield ch

    async def get_value(param):
        return param

    # Async generate() - Standard inference
    async def generate(context: Any):
        result = await get_value("Generate Flow")
        return {"body": {"message": "This is an async AI service"}}

    # Async generate_stream() - Streaming inference
    async def generate_stream(context: Any):
        result = await get_value("Generate Stream Flow")
        llm_response = "This is a dummy AI generate stream"
        async for chunk in stream_chunks(llm_response):
            yield chunk

    # Async generate_batch() - Batch jobs
    async def generate_batch(
        input_data_references: dict, output_data_reference: dict
    ) -> None:
        batch_token = context.get_token()
        print(f"batch_token: {batch_token[-5:]}")
        print(
            f"generate_batch:\n"
            f"{input_data_references=}\n"
            f"{output_data_reference=}"
        )

    return generate, generate_stream, generate_batch

在此示例中:

  • 这三种方法(generate, generate_stream, generate_batch)均被定义为异步函数
  • 外部函数中的对象 context 可在所有方法中访问
  • 支持LLM流式响应、聊天机器人和批处理

创建人工智能服务的示例代码

示例代码定义了一个人工智能服务 deployable_ai_service_f1 。 当REST API请求发送到 /ml/v4/deployments 端点时,将调用 deployable_ai_service_f1 。 该函数接受JSON格式的输入,并包含以下嵌套函数作为其作用域的一部分:

deployable_ai_service_f1 定义了一个具备三项功能的人工智能服务:

外部函数:

外层函数初始化部署,并返回三个服务处理程序:

gen_token = context.generate_token()
return generate, generate_stream, generate_batch

generate() — 同步推理

generate():向 /ml/v4/deployments/{id_or_name}/ai_service 终端发送REST API调用。 它接收一个上下文对象,提取令牌、标头和JSON正文,并根据JSON正文中的模式键返回响应。 响应格式可以是JSON、字节或字符串,并带有可选的自定义标头。

请求 JSON 可以包含任何必需的值,例如:

{ "content-type": "<value>" }

模式决定了响应格式。 请参考此示例代码,其中提供了适用于不同内容类型的简化版本,并附有简短示例:

def deployable_ai_service_f1(context, params={"k1": "v1"}):
     """
    The outer function handles the REST call to the deployment endpoint
    POST /ml/v4/deployments

        context.generate_token() - generate a user token

    To use `generate` and `generate_stream`, the deployment has to be ONLINE
    To use `generate_batch`, the deployment has to be BATCH
    """
    gen_token = context.generate_token()
    print(f"outer function: {gen_token[-5:]}", flush=True)

     def generate(context) -> dict:
        //context is mandatory parameter for ai service
        """
        The `generate` function handles the REST call to the inference endpoint
        POST /ml/v4/deployments/{id_or_name}/ai_service

            context.get_token()     - get the Bearer token from the header of the request
            context.get_json()      - get the body of the request
            context.get_headers()   - get the headers of the request

        The generate function should return a dict
        The following optional keys are supported currently
        - body
        - headers

        This particular example accepts a json body of the format:
        { "content-type" : <value> }

        Depending on the <value> of the content type, it will return different response

        """
        user_token = context.get_token()
        headers = context.get_headers()
        json_body = context.get_json()
        print(f"my_generate: {user_token=}", flush=True)
        print(f"request headers: {headers=}", flush=True)
        print(f"json body: {json_body=}", flush=True)

         match json_body.get("content-type", "no-match"):
            case "json":
                # response Content-Type is "application/json"
                return {
                    "headers": {
                        "Content-Type": "application/json",
                        "User-Defined-Head": "x-genai",
                    },
                    "body": {
                        "user_token": user_token[-5:],
                        "gen_token": gen_token[-5:],
                        "json_body": json_body,
                        "params": params
                    },
                }

其他适用案例:

Content-Type: json-no-header:

case "json-no-header":
    return {"body": {"json_body": json_body}}

Content-Type: json-custom-header:

case "json-custom-header":
    return {
        "headers": {"Content-Type": "text/plain; charset=utf-8"},
        "body": {"json_body": json_body},
    }

内容类型:字节:

case "bytes":
    return {
        "headers": {"Content-Type": "application/octet-stream"},
        "body": b"12345678910",
    }

Content-Type: str:

case "str":
    return {
        "headers": {"Content-Type": "text/plain"},
        "body": f"Hello WatsonX: {json_body}",
    }

Content-Type: negative-str-return:

case "negative-str-return":
    return "Should give 400 bad request"

默认情况:

case _:
    return {"body": "No match"}

generate_stream() — SSE 流处理

generate_stream():向SSE(服务器发送事件)推断端点 /ml/v4/deployments/{id_or_name}/ai_service_stream 发出REST API调用。 它接收上下文对象,提取标记、标头和JSON正文,并返回由 eos (流结束)指示的SSE事件流。

generate_stream() 使用 yield 遍历“sse”列表中的每个元素:

def generate_stream(context):
        """
        The generate_stream function handles the REST call to the SSE inference endpoint
        POST /ml/v4/deployments/{id_or_name}/ai_service_stream

            context.get_token()     - get the Bearer token from the header of the request
            context.get_json()      - get the body of the request
            context.get_headers()   - get the headers of the request

        The generate_stream function be a python `generator` with yield
        The data in yield will the "data" for the SSE event

        Example: The following request json
            { "sse": ["Hello" , "", "WatsonX"," ", "!"]}
        will return the following stream of events
            --------------
            id: 1
            event: message
            data: Hello

            id: 2
            event: message
            data:

            id: 3
            event: message
            data: WatsonX

            id: 4
            event: message
            data:

            id: 5
            event: message
            data: !

            id: 6
            event: eos
            ---------------
        The end of the stream will be marked by the event "eos"

        """
        user_token = context.get_token()
        headers = context.get_headers()
        json_body = context.get_json()
        print(f"generate_stream: {user_token=}", flush=True)
        print(f"generate_stream: {headers=}", flush=True)
        print(f"generate_stream: {json_body=}", flush=True)

        import time
        for x in json_body.get("sse", ["default", "message"]):
            time.sleep(1)
            yield x

generate_batch() — 批量处理

generate_batch():向作业端点 /ml/v4/deployments_jobs 发出REST API调用。 它从请求的JSON正文中获取 input_data_referencesoutput_data_reference ,生成批处理令牌,并记录输入和输出数据引用。

generate_batch() 支持输入和输出引用,并对其进行记录:

    def generate_batch(input_data_references: list[dict], output_data_reference: dict) -> None:
        """
        The generate_batch function handles the REST jobs endpoint
        POST /ml/v4/deployments_jobs

            Arguments to the function are from the json body of the request to jobs
            - input_data_references : scoring.input_data_references
            - output_data_reference : scoring.output_data_reference

        context.generate_token() : can access context object
        from outer function scope if token is required
        """
        batch_token = context.generate_token()
        print(f"batch_token: {batch_token[-5:]}", flush=True)
        print(
            f"generate_batch:\n{input_data_references=}\n{output_data_reference=}",
            flush=True,
        )

    return generate, generate_stream, generate_batch

测试人工智能服务

创建AI服务后,您可以使用 watsonx.ai Python 客户端库测试AI服务的编码逻辑。

使用 Python 客户端库测试人工智能服务

要使用 watsonx.ai Python 客户端库中的 RuntimeContext 类在本地测试人工智能服务的逻辑,请按照以下步骤操作:

  1. 使用 Python 客户端库中的 RuntimeContext 类在本地测试您的AI服务:

    from ibm_watsonx_ai.deployments import RuntimeContext
    
    context = RuntimeContext(
        api_client=client, request_payload_json={}
    )
    
    # custom is optional argument which is specified during the time of creation of deployment
    custom_object = {"space_id": space_id}
    
    generate, generate_stream, generate_batch = basic_generate_demo(context, **custom_object)
    
    

    如需了解更多信息,请参阅 watsonx.ai Python 客户资料库文档,了解如何使用 RuntimeContext 获取人工智能服务

  2. 根据您的使用情况,您可以按如下方式测试 generate()generate_stream()generate_batch() 功能:

    • 测试generate()函数:

      context.request_payload_json = { "test": "ai_service inference payload"}
      print(generate(context))
      
    • 测试generate_stream()函数:

      context.request_payload_json = {"sse": ["ai_service_stream", "inference", "test"]}
      for data in generate_stream(context):
          print(data)
      
    • 测试generate_batch()函数:

      input_data_references = [
          {
              "type": "connection_asset",
              "connection": {"id": "2d07a6b4-8fa9-43ab-91c8-befcd9dab8d2"},
              "location": {
                  "bucket": "wml-v4-fvt-batch-pytorch-connection-input",
                  "file_name": "testing-123",
              },
          }
      ]
      output_data_reference = {
          "type": "data_asset",
          "location": {"name": "nb-pytorch_output.zip"},
      }
      
      generate_batch(input_data_references, output_data_reference)
      

创建人工智能服务资产

要部署人工智能服务,您必须在 Watson Machine Learning 中创建一个包含人工智能服务的资源库资产,并将 Python 文件上传到该资产。

创建人工智能服务资产的要求

当您使用集成开发环境(IDE)如VSCode、 Eclipse、 PyCharm, 或更多来构建生成式AI应用程序时,必须创建一个 Python 文件来存储您的AI服务。 定义功能后,必须压缩人工智能服务,以创建一个 gzip 存档( .gz 文件格式)。

当您使用 watsonx.ai Python 客户端库创建AI服务资产时,该库会自动将函数存储在 gzip 存档中。 但是,当您使用REST API创建AI服务资产时,必须按照手动压缩 Python 文件并将其打包为 gzip 文件的过程进行操作。

使用 Python 客户端库创建人工智能服务资产

您可以使用 watsonx.ai Python 客户端库中的 store_ai_service 功能来创建AI服务资产。

以下代码示例展示了如何使用 Python 客户端库创建AI服务资产:

documentation_request = {
    "application/json": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "parameters": {
                "properties": {
                    "max_new_tokens": {"type": "integer"},
                    "top_p": {"type": "number"},
                },
                "required": ["max_new_tokens", "top_p"],
            },
        },
        "required": ["query"],
    }
}

documentation_response = {
    "application/json": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "properties": {"query": {"type": "string"}, "result": {"type": "string"}},
        "required": ["query", "result"],
    }
}


meta_props = {
    client.repository.AIServiceMetaNames.NAME: "AI service example",
    client.repository.AIServiceMetaNames.DESCRIPTION: "This is AI service function",
    client.repository.AIServiceMetaNames.SOFTWARE_SPEC_ID: client.software_specifications.get_id_by_name(
        "<name of software specification>"
    ),
    client.repository.AIServiceMetaNames.REQUEST_DOCUMENTATION: documentation_request,
    client.repository.AIServiceMetaNames.RESPONSE_DOCUMENTATION: documentation_response,
}

stored_ai_service_details = client.repository.store_ai_service(
    basic_generate_demo, meta_props
)

ai_service_id = client.repository.get_ai_service_id(stored_ai_service_details)
print("The AI service asset id:", ai_service_id)
注:
  • REQUEST_DOCUMENTATIONRESPONSE_DOCUMENTATION 参数为可选参数。 您可以使用这些参数来存储 generategenerate_stream 函数的请求和响应模式。
  • 函数调用 client.repository.store_ai_service 将AI服务函数 basic_generate_demo 保存到内部 gzip 文件中。

如需了解更多信息,请参阅 watsonx.ai Python 客户资料库中有关创建人工智能服务资产的文档

使用REST API创建AI服务资产

您可以使用 /ml/v4/ai_services REST API 端点在 Watson Machine Learning 资源库中创建 AI 服务资产。

部署人工智能服务资产

根据您的使用情况,您可以从部署空间为您的AI服务资产创建在线部署或批量部署。 使用 watsonx.ai REST API或 Python 客户端库以编程方式部署您的AI服务。

人工智能服务的部署类型

根据您的使用情况,您可以在线或批量部署AI服务资产。 根据人工智能服务中使用的功能选择部署类型。

  • 您必须为您的AI服务资产创建一个在线部署,用于在线评分(AI服务包含 generate() 功能)或流媒体应用(AI服务包含 generate_stream() 功能)。
  • 您必须为您的AI服务资产创建批处理部署,以便进行批处理评分应用(AI服务包含 generate_batch() 功能)。

先决条件

  • 您必须将人工智能服务资产推广到您的部署空间。

通过 Python 客户端库部署人工智能服务

您可以使用 Python 客户端库为您的AI服务资产创建在线或批量部署。

创建在线部署

以下示例展示了如何使用 watsonx.ai Python 客户端库为您的AI服务创建在线部署:

deployment_details = client.deployments.create(
    artifact_id=ai_service_id,
    meta_props={
        client.deployments.ConfigurationMetaNames.NAME: "ai-service - online test",
        client.deployments.ConfigurationMetaNames.ONLINE: {},
        client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {
            "id": client.hardware_specifications.get_id_by_name("XS")
        },
    },
)
deployment_id = client.deployments.get_uid(deployment_details)
print("The deployment id:", deployment_id)

创建批量部署

以下示例展示了如何使用 watsonx.ai Python 客户端库为您的AI服务创建批处理部署:

deployment_details = client.deployments.create(
    artifact_id=ai_service_id,
    meta_props={
        client.deployments.ConfigurationMetaNames.NAME: f"ai-service - batch",
        client.deployments.ConfigurationMetaNames.BATCH: {},
        client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {
            "id": client.hardware_specifications.get_id_by_name("XS")
        },
    },
)
deployment_id = client.deployments.get_uid(deployment_details)
print("The batch deployment id:", deployment_id)

通过REST API部署人工智能服务

您可以使用 /ml/v4/deployments watsonx.ai REST API 端点为您的 AI 服务资产创建在线或批量部署。

创建在线部署

以下示例展示了如何使用REST API为您的AI服务创建在线部署:

# POST /ml/v4/deployments
response = requests.post(
    f'{HOST}/ml/v4/deployments?version={VERSION}',
    headers=headers,
    verify=False,
    json={
        "space_id": space_id,
        "name": "genai flow online",
        "custom": {
            "key1": "value1",
            "key2": "value2",
            "model": "meta-llama/llama-3-8b-instruct"
        },
        "asset": {
            "id": asset_id
        },
        "online": {}
    }
)

创建批量部署

以下示例展示了如何使用REST API为您的AI服务创建批处理部署:

response = requests.post(
    f'{HOST}/ml/v4/deployments?version={VERSION}',
    headers=headers,
    verify=False,
    json={
        "hardware_spec": {
          "id": "........",
          "num_nodes": 1
        },
        "space_id": space_id,
        "name": "ai service batch dep",
        "custom": {
            "key1": "value1",
            "key2": "value2",
            "model": "meta-llama/llama-3-8b-instruct"
        },
        "asset": {
            "id": asset_id
        },
        "batch": {}
    }
)
print(f'POST {HOST}/ml/v4/deployments?version={VERSION}', response.status_code)
print(json.dumps(response.json(), indent=2))

dep_id = response.json()["metadata"]["id"]

print(f"{dep_id=}")

用于创建和部署人工智能服务的样本笔记本

要了解如何以编程方式创建和部署人工智能服务,请参阅以下示例笔记本:

样本名称 框架 展示技术
使用 watsonx 和 granite-4-h-small 作为人工智能服务 Python 设置
创建AI服务
在本地测试AI服务的功能
部署AI服务
执行AI服务的示例
总结

了解更多