妙笔API最佳实践

本文提供妙笔写作链路 API的几个最佳实践,帮助您快速入门并开发您自己的业务应用。

前提条件

<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>alibabacloud-aimiaobi20230801</artifactId>
  <version>1.0.11</version>
</dependency>

1、实现直接生成文章

基于妙笔提供的API,本节通过直接生成文章场景来帮助您熟悉API的使用。

生成调用demo如下:

  • Java:

package com.aliyun.sdk.service.aimiaobi20230801;


import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.gateway.pop.Configuration;
import com.aliyun.sdk.gateway.pop.auth.SignatureVersion;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunWritingRequest;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunWritingResponseBody;
import com.aliyun.sdk.service.aimiaobi20230801.AsyncClient;

import com.google.gson.Gson;
import darabonba.core.ResponseIterable;
import darabonba.core.ResponseIterator;
import darabonba.core.client.ClientOverrideConfiguration;

import java.util.ArrayList;

import java.util.List;

/**
 * packageName com.dayouz.lightapp
 *
 * @author dayouz
 * @version JDK 8
 * @className runStyleWriting
 * @date 2024/8/13
 * @description 直接写作demo
 */
public class RunWritingTest {

    public static void main(String[] args) {
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(Constant.accessKeyId)
                .accessKeySecret(Constant.accessKeySecret)
                .build());

        AsyncClient client = AsyncClient.builder()
                .region("cn-beijing")
                .credentialsProvider(provider)
                .serviceConfiguration(Configuration.create().setSignatureVersion(SignatureVersion.V3)).overrideConfiguration(ClientOverrideConfiguration.create().setProtocol("HTTPS").setEndpointOverride("aimiaobi.cn-beijing.aliyuncs.com"))
                .build();

        //写作时的文体、篇幅、输出语言等控制参数
        List<RunWritingRequest.Tags> tags = new ArrayList<>();

        //构建文章篇幅字数300字左右 ,可根据实际需要填写字数,可选 800字,1000字,2000字
        tags.add(RunWritingRequest.Tags.builder().tag("gcNumberSizeTag").keyword("300字").build());

        //构建写作文体为新闻评论
        //可以通过接口获取系统内置的文体类型 https://help.aliyun.com/zh/model-studio/api-aimiaobi-2023-08-01-listbuildconfigs?spm=a2c4g.11186623.help-menu-2400256.d_1_6_3_2_0_3_3_13.79502533pwFhax
        //注意需要获取  "Tag": "writingStyle" 的keyword ,keyword 赋值为 Key 字段
        tags.add(RunWritingRequest.Tags.builder().tag("writingStyle").keyword("新闻评论").build());


        RunWritingRequest request = RunWritingRequest.builder()
                .workspaceId(InitProvider.workspaceId)
                .prompt("写一篇关于国足对战日本0:7 失败的报道")
                .writingConfig(RunWritingRequest.WritingConfig.builder()
                        //设置传媒领域的写作  可选 media:传媒 government:政务 market:营销 custom 自定义文体
                        .domain("media")
                        //设置为自动补充素材,通过互联网检索
                        .useSearch(true)
                        //设置写作时的文体、篇幅、输出语言等控制参数
                        .tags(tags).build())
                .build();


        ResponseIterable<RunWritingResponseBody> x = client.runWritingWithResponseIterable(request);

        ResponseIterator<RunWritingResponseBody> iterator = x.iterator();
        while (iterator.hasNext()) {
            System.out.println("----event----");
            RunWritingResponseBody event = iterator.next();
            System.out.println(new Gson().toJson(event));
        }

        System.out.println("ALL***********************");
        System.out.println("请求成功的请求头值:");
        System.out.println(x.getStatusCode());
        System.out.println(x.getHeaders());
    }
}
  • Python:

import os
from alibabacloud_tea_openapi_sse.client import Client as OpenApiClient
from alibabacloud_tea_openapi_sse import models as open_api_models
from alibabacloud_tea_util_sse import models as open_api_util_models
import asyncio
import json
import dotenv

dotenv.load_dotenv()

endpoint = 'aimiaobi.cn-beijing.aliyuncs.com'

# 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
# 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html。
access_key_id = os.environ.get('accessKeyId')
access_key_secret = os.environ.get('accessKeySecret')
workspace_id = os.environ.get('WorkspaceId')

# 详情接口文档请参考:https://api.aliyun.com/api/AiMiaoBi/2023-08-01/RunWritingV2?spm=a2c4g.11186623.0.0.201c715eKrroz5&RegionId=cn-beijing

def _create_client() -> OpenApiClient:
    config = open_api_models.Config(
        access_key_id=access_key_id,
        access_key_secret=access_key_secret,
        endpoint=endpoint
    )
    return OpenApiClient(config)


def create_sse_api_info(action) -> open_api_models.Params:
    """
    API 相关
    @param path: params
    @return: OpenApi.Params
    """
    params = open_api_models.Params(
        # 接口名称
        action=action,
        # 接口版本
        version='2023-08-01',
        # 接口协议
        protocol='HTTPS',
        # 接口 HTTP 方法
        method='POST',
        auth_type='AK',
        style='RPC',
        pathname="",
        # 接口请求体内容格式,
        req_body_type='formData',
        # 接口响应体内容格式,
        body_type='sse'
    )
    return params


class SseClient:
    def __init__(self) -> None:
        # 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
        # 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html。
        self.endpoint = endpoint
        self._runtime = open_api_util_models.RuntimeOptions(read_timeout=1000 * 100)
        self._client = _create_client()

    async def run_writing(self, body):
        request = open_api_models.OpenApiRequest(body=body)
        return self._client.call_sse_api_async(params=create_sse_api_info("RunWritingV2"), request=request,
                                               runtime=self._runtime)


async def test_run_writing():
    client = SseClient()

    writing_params = {
        "WorkspaceId": workspace_id,
        "Prompt": "写一篇关于中美关系的新闻评论"
    }

    async for item in await  client.run_writing(writing_params):
        try:
            data = json.loads(item.get('event').data)
            print(data)
        except json.JSONDecodeError:
            print('------json.JSONDecodeError--------')


if __name__ == '__main__':
    asyncio.run(test_run_writing())

2、实现自定义文体生成文章

基于妙笔提供的API,本节通过自定义文体生成文章场景来帮助您熟悉API的使用。

生成调用demo如下:

package com.aliyun.sdk.service.aimiaobi20230801;


import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.gateway.pop.Configuration;
import com.aliyun.sdk.gateway.pop.auth.SignatureVersion;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunWritingRequest;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunWritingResponseBody;
import com.aliyun.sdk.service.aimiaobi20230801.AsyncClient;

import com.google.gson.Gson;
import darabonba.core.ResponseIterable;
import darabonba.core.ResponseIterator;
import darabonba.core.client.ClientOverrideConfiguration;

import java.util.ArrayList;

import java.util.List;

/**
 * packageName com.dayouz.lightapp
 *
 * @author dayouz
 * @version JDK 8
 * @className runStyleWriting
 * @date 2024/8/13
 * @description 直接写作demo
 */
public class RunWritingTest {

    public static void main(String[] args) {
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(Constant.accessKeyId)
                .accessKeySecret(Constant.accessKeySecret)
                .build());

        AsyncClient client = AsyncClient.builder()
                .region("cn-beijing")
                .credentialsProvider(provider)
                .serviceConfiguration(Configuration.create().setSignatureVersion(SignatureVersion.V3)).overrideConfiguration(ClientOverrideConfiguration.create().setProtocol("HTTPS").setEndpointOverride("aimiaobi.cn-beijing.aliyuncs.com"))
                .build();

        //写作时的文体、篇幅、输出语言等控制参数
        List<RunWritingRequest.Tags> tags = new ArrayList<>();

        //构建文章篇幅字数300字左右 ,可根据实际需要填写字数,可选 800字,1000字,2000字
        tags.add(RunWritingRequest.Tags.builder().tag("gcNumberSizeTag").keyword("300字").build());

        //构建写作文体为已经创建好的自定义文体
        //可以通过接口获取系统内置的文体类型 https://help.aliyun.com/zh/model-studio/api-aimiaobi-2023-08-01-listbuildconfigs?spm=a2c4g.11186623.help-menu-2400256.d_1_6_3_2_0_3_3_13.79502533pwFhax
tags.add(RunWritingRequest.Tags.builder().tag("writingStyle").keyword("111111(通过上述接口返回的自定义文体的ID)").build());


        RunWritingRequest request = RunWritingRequest.builder()
                .workspaceId(InitProvider.workspaceId)
                .prompt("写一篇关于国足对战日本0:7 失败的报道")
                .writingConfig(RunWritingRequest.WritingConfig.builder()
                        //设置自定义文体的写作  可选 media:传媒 government:政务 market:营销 custom 自定义文体
                        .domain("custom")
                        //设置为自动补充素材,通过互联网检索
                        .useSearch(true)
                        //设置写作时的文体、篇幅、输出语言等控制参数
                        .tags(tags).build())
                .build();


        ResponseIterable<RunWritingResponseBody> x = client.runWritingWithResponseIterable(request);

        ResponseIterator<RunWritingResponseBody> iterator = x.iterator();
        while (iterator.hasNext()) {
            System.out.println("----event----");
            RunWritingResponseBody event = iterator.next();
            System.out.println(new Gson().toJson(event));
        }

        System.out.println("ALL***********************");
        System.out.println("请求成功的请求头值:");
        System.out.println(x.getStatusCode());
        System.out.println(x.getHeaders());
    }
}

3、分步骤创作文章

package org.example.miaobi;

import com.alibaba.fastjson2.JSONObject;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.gateway.pop.Configuration;
import com.aliyun.sdk.gateway.pop.auth.SignatureVersion;
import com.aliyun.sdk.service.aimiaobi20230801.AsyncClient;
import com.aliyun.sdk.service.aimiaobi20230801.models.*;
import darabonba.core.ResponseIterable;
import darabonba.core.client.ClientOverrideConfiguration;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 妙笔AI分步写作示例程序(无需JUnit)
 * 功能:生成大纲 → 摘编 → 正文
 * 
 * 运行前请设置以下环境变量:
 *   accessKeyId
 *   accessKeySecret
 *   domain = aimiaobi.cn-hangzhou.aliyuncs.com
 *   WorkspaceId
 */
@Slf4j
public class RunStepByStepWritingMain {

    public static void main(String[] args) {
        try {
            // 初始化客户端
            AsyncClient asyncClient = createAsyncClient();

            // 从环境变量获取参数
            String workspaceId = System.getenv("WorkspaceId");
            String prompt = "中美关系如何破局"; // 可替换为命令行参数

            if (workspaceId == null || workspaceId.isEmpty()) {
                log.error("环境变量 'WorkspaceId' 未设置!");
                return;
            }

            log.info("开始执行分步写作,主题:{}", prompt);

            // 1. 生成大纲
            OutlineGenerateResult outlineResult = runOutlineGenerateResult(asyncClient, workspaceId, prompt);
            if (!isSuccess(outlineResult.latestResponse)) {
                log.error("大纲生成失败: {}", JSONObject.toJSONString(outlineResult.latestResponse));
                return;
            }

            log.info("大纲生成成功");
            log.info("检索Query: {}", outlineResult.searchQuery);
            log.info("检索文章: {}", formatArticles(outlineResult.articles));
            log.info("MiniDoc: {}", outlineResult.miniDoc);
            log.info("大纲: {}", outlineResult.outlines);

            // 2. 生成摘编
            SummarizationResult summarizationResult = runSummarizationGenerate(
                    outlineResult.outlines, asyncClient, workspaceId,
                    outlineResult.miniDoc, prompt);

            if (!isSuccess(summarizationResult.latestResponse)) {
                log.error("摘编生成失败: {}", JSONObject.toJSONString(summarizationResult.latestResponse));
                return;
            }

            log.info("摘编生成成功");
            log.info("Cached Summarization: {}", summarizationResult.cachedSummarization);

            // 3. 生成正文
            RunWritingResult writingResult = runWriting(
                    asyncClient, workspaceId, prompt,
                    outlineResult.miniDoc,
                    convertToOutlineRequests(outlineResult.outlines),
                    summarizationResult.cachedSummarization);

            if (!isSuccess(writingResult.latestResponse)) {
                log.error("正文生成失败: {}", JSONObject.toJSONString(writingResult.latestResponse));
                return;
            }

            String finalText = writingResult.latestResponse.getPayload().getOutput().getText();
            log.info("最终生成的正文:\n{}", finalText);

        } catch (Exception e) {
            log.error("程序执行异常", e);
        }
    }

    /**
     * 创建异步客户端
     */
    private static AsyncClient createAsyncClient() {
        String accessKeyId = System.getenv("accessKeyId");
        String accessKeySecret = System.getenv("accessKeySecret");
        String domain = System.getenv("domain");

        if (accessKeyId == null || accessKeySecret == null) {
            throw new IllegalArgumentException("环境变量 accessKeyId 或 accessKeySecret 未设置!");
        }

        if (domain == null) {
            domain = "aimiaobi.cn-hangzhou.aliyuncs.com"; // 默认值
        }

        return AsyncClient.builder()
                .credentialsProvider(StaticCredentialProvider.create(Credential.builder()
                        .accessKeyId(accessKeyId)
                        .accessKeySecret(accessKeySecret)
                        .build()))
                .serviceConfiguration(Configuration.create()
                        .setSignatureVersion(SignatureVersion.V3))
                .overrideConfiguration(ClientOverrideConfiguration.create()
                        .setProtocol("HTTPS")
                        .setEndpointOverride(domain))
                .build();
    }

    /**
     * 判断响应是否成功
     */
    private static boolean isSuccess(RunStepByStepWritingResponseBody response) {
        if (response == null) return false;
        String errorCode = response.getHeader().getErrorCode();
        return errorCode == null || errorCode.isEmpty();
    }

    /**
     * 格式化文章列表输出
     */
    private static String formatArticles(List<RunStepByStepWritingResponseBody.Articles> articles) {
        if (articles == null) return "null";
        return articles.stream()
                .map(a -> a.getTitle() + " -> " + a.getUrl())
                .collect(Collectors.joining("; "));
    }

    /**
     * 转换字符串列表为请求对象列表
     */
    private static List<RunStepByStepWritingRequest.Outlines> convertToOutlineRequests(List<String> outlines) {
        return outlines.stream()
                .map(o -> RunStepByStepWritingRequest.Outlines.builder().outline(o).build())
                .collect(Collectors.toList());
    }

    // ==================== 核心方法实现 ====================

    public static class OutlineGenerateResult {
        public final String searchQuery;
        public final List<RunStepByStepWritingResponseBody.Articles> articles;
        public final List<String> miniDoc;
        public final List<String> outlines;
        public final RunStepByStepWritingResponseBody latestResponse;

        public OutlineGenerateResult(String searchQuery,
                                     List<RunStepByStepWritingResponseBody.Articles> articles,
                                     List<String> miniDoc,
                                     List<String> outlines,
                                     RunStepByStepWritingResponseBody latestResponse) {
            this.searchQuery = searchQuery;
            this.articles = articles;
            this.miniDoc = miniDoc;
            this.outlines = outlines;
            this.latestResponse = latestResponse;
        }
    }

    private static OutlineGenerateResult runOutlineGenerateResult(AsyncClient asyncClient,
                                                                  String workspaceId,
                                                                  String prompt) {
        ResponseIterable<RunStepByStepWritingResponseBody> iterable = asyncClient.runStepByStepWritingWithResponseIterable(
                RunStepByStepWritingRequest.builder()
                        .workspaceId(workspaceId)
                        .prompt(prompt)
                        .writingConfig(RunStepByStepWritingRequest.WritingConfig.builder()
                                .step("OutlineGenerate")
                                .build())
                        .build()
        );

        String searchQuery = null;
        List<RunStepByStepWritingResponseBody.Articles> articles = null;
        List<String> miniDoc = null;
        List<String> outlines = null;
        RunStepByStepWritingResponseBody latestResponse = null;

        for (RunStepByStepWritingResponseBody response : iterable) {
            latestResponse = response;
            RunStepByStepWritingResponseBody.Output output = response.getPayload().getOutput();

            if (articles == null && output != null && output.getArticles() != null) {
                articles = output.getArticles();
            }
            if (miniDoc == null && output != null && output.getMiniDoc() != null) {
                miniDoc = output.getMiniDoc();
            }
            if (searchQuery == null && output != null && output.getSearchQuery() != null) {
                searchQuery = output.getSearchQuery();
            }

            // 大纲结束事件
            if ("task-outline-end".equals(response.getHeader().getEvent())) {
                String text = output.getText();
                if (text != null && !text.trim().isEmpty()) {
                    outlines = Arrays.asList(text.trim().split("\n"));
                }
            }
        }

        return new OutlineGenerateResult(searchQuery, articles, miniDoc, outlines, latestResponse);
    }

    public static class SummarizationResult {
        public final List<String> cachedSummarization;
        public final RunStepByStepWritingResponseBody latestResponse;

        public SummarizationResult(List<String> cachedSummarization,
                                   RunStepByStepWritingResponseBody latestResponse) {
            this.cachedSummarization = cachedSummarization;
            this.latestResponse = latestResponse;
        }
    }

    private static SummarizationResult runSummarizationGenerate(List<String> outlines,
                                                                AsyncClient asyncClient,
                                                                String workspaceId,
                                                                List<String> miniDoc,
                                                                String prompt) {
        List<RunStepByStepWritingRequest.Outlines> outlineList = convertToOutlineRequests(outlines);

        ResponseIterable<RunStepByStepWritingResponseBody> iterable = asyncClient.runStepByStepWritingWithResponseIterable(
                RunStepByStepWritingRequest.builder()
                        .workspaceId(workspaceId)
                        .prompt(prompt)
                        .referenceData(RunStepByStepWritingRequest.ReferenceData.builder()
                                .miniDoc(miniDoc)
                                .outlines(outlineList)
                                .build())
                        .writingConfig(RunStepByStepWritingRequest.WritingConfig.builder()
                                .step("MiniDocSummary")
                                .build())
                        .build()
        );

        List<String> cachedSummarization = null;
        RunStepByStepWritingResponseBody latestResponse = null;

        for (RunStepByStepWritingResponseBody response : iterable) {
            latestResponse = response;
            RunStepByStepWritingResponseBody.Output output = response.getPayload().getOutput();
            if (output != null && output.getExtraOutput() != null) {
                List<String> sum = output.getExtraOutput().getSummarization();
                if (sum != null && !sum.isEmpty()) {
                    cachedSummarization = sum;
                }
            }
        }

        return new SummarizationResult(cachedSummarization, latestResponse);
    }

    public static class RunWritingResult {
        public final RunStepByStepWritingResponseBody latestResponse;

        public RunWritingResult(RunStepByStepWritingResponseBody latestResponse) {
            this.latestResponse = latestResponse;
        }
    }

    private static RunWritingResult runWriting(AsyncClient asyncClient,
                                               String workspaceId,
                                               String prompt,
                                               List<String> miniDoc,
                                               List<RunStepByStepWritingRequest.Outlines> outlineList,
                                               List<String> cachedSummarization) {
        ResponseIterable<RunStepByStepWritingResponseBody> iterable = asyncClient.runStepByStepWritingWithResponseIterable(
                RunStepByStepWritingRequest.builder()
                        .workspaceId(workspaceId)
                        .prompt(prompt)
                        .referenceData(RunStepByStepWritingRequest.ReferenceData.builder()
                                .miniDoc(miniDoc)
                                .outlines(outlineList)
                                .summarization(cachedSummarization)
                                .build())
                        .writingConfig(RunStepByStepWritingRequest.WritingConfig.builder()
                                .step("Writing")
                                .build())
                        .build()
        );

        RunStepByStepWritingResponseBody latestResponse = null;
        for (RunStepByStepWritingResponseBody item : iterable) {
            log.info("流式响应: {}", JSONObject.toJSONString(item));
            latestResponse = item;
        }

        return new RunWritingResult(latestResponse);
    }
}

4、AI工具箱

AI工具箱包括内容续写、摘要生成、标题生成、内容缩写、内容扩写、关键词抽取等功能,最新实时能力可参考妙笔写作页面的AI工具箱。本节通过实现摘要生成场景来帮助您熟悉API的使用。

生成调用demo如下:

package com.aliyun.sdk.service.aimiaobi20230801;


import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.gateway.pop.Configuration;
import com.aliyun.sdk.gateway.pop.auth.SignatureVersion;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunSummaryGenerateRequest;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunSummaryGenerateResponseBody;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunWritingRequest;
import com.aliyun.sdk.service.aimiaobi20230801.models.RunWritingResponseBody;
import com.google.gson.Gson;
import com.aliyun.sdk.service.aimiaobi20230801.AsyncClient;
import darabonba.core.ResponseIterable;
import darabonba.core.ResponseIterator;
import darabonba.core.client.ClientOverrideConfiguration;

import java.util.ArrayList;
import java.util.List;

/**
 * packageName com.dayouz.lightapp
 *
 * @author dayouz
 * @version JDK 8
 * @className runStyleWriting
 * @date 2024/8/13
 * @description 摘要生成Demo
 */
public class RunSummaryGenerateTest {

    public static void main(String[] args) {
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(Constant.accessKeyId)
                .accessKeySecret(Constant.accessKeySecret)
                .build());

        AsyncClient client = AsyncClient.builder()
                .region("cn-beijing")
                .credentialsProvider(provider)
                .serviceConfiguration(Configuration.create().setSignatureVersion(SignatureVersion.V3)).overrideConfiguration(ClientOverrideConfiguration.create().setProtocol("HTTPS").setEndpointOverride("aimiaobi.cn-beijing.aliyuncs.com"))
                .build();


        RunSummaryGenerateRequest request = RunSummaryGenerateRequest.builder()
                .workspaceId(Constant.workspaceId)
                .prompt("请为上述内容生成一段摘要,字数在100~200字以内。")
                .content("云服务器ECS(Elastic Compute Service)是阿里云提供的性能卓越、稳定可靠、弹性扩展的IaaS(Infrastructure as a Service)级别云计算服务。云服务器ECS免去了您采购IT硬件的前期准备,让您像使用水、电、天然气等公共资源一样便捷、高效地使用服务器,实现计算资源的即开即用和弹性伸缩。阿里云ECS持续提供创新型服务器,解决多种业务需求,助力您的业务发展。")
                .build();


        ResponseIterable<RunSummaryGenerateResponseBody> x = client.runSummaryGenerateWithResponseIterable(request);

        ResponseIterator<RunSummaryGenerateResponseBody> iterator = x.iterator();
        while (iterator.hasNext()) {
            System.out.println("----event----");
            RunSummaryGenerateResponseBody event = iterator.next();
            System.out.println(new Gson().toJson(event));
        }

        System.out.println("ALL***********************");
        System.out.println("请求成功的请求头值:");
        System.out.println(x.getStatusCode());
        System.out.println(x.getHeaders());
    }
}