すべてのプロダクト
Search
ドキュメントセンター

:DAS Agent と Chat API を統合するためのベストプラクティス

最終更新日:Aug 29, 2026

Chat API は DAS Agent 用の非同期インターフェイスで、ナレッジベースの Q&A、パフォーマンス診断、複数ターンの会話をサポートします。エージェントの推論プロセスと最終的な回答をサーバー送信イベント (SSE) ストリームとして返します。本トピックでは、Java、Python、Go の SDK を使用して Chat API を統合する方法について、SSE イベントの解析と複数ターンの会話の完全な例を交えて説明します。

前提条件

  • DAS Agent が有効化され、マネージドインスタンスのリージョンが DAS Agent の国またはリージョンと一致していること。インスタンスが DAS Agent にバインドされていること。

  • Alibaba Cloud DAS SDK の最新バージョンがインストールされていること。

  • リージョンが cn-shanghai に、エンドポイントが das.cn-shanghai.aliyuncs.com に設定されていること。

  • ALIBABA_CLOUD_ACCESS_KEY_ID と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が設定されているか、Alibaba Cloud のデフォルトの認証情報チェーンが使用されていること。

説明

Chat API は、入出力文字数に基づいて課金される有料インターフェイスです。詳細については、「DAS Agent の課金」をご参照ください。

主要なイベント

SSE ストリームは ag-ui プロトコルに準拠しています。次の表に、主なイベントタイプを示します。

イベントタイプ

主要なフィールド

説明

RUN_STARTED

RunId

タスクが開始されたことを示します。チャットセッションの開始を示します。

RUN_FINISHED

RunId

タスクが終了したことを示します。このイベントの後にイベントは生成されません。

TEXT_MESSAGE_START

MessageId、Role

テキストメッセージの開始を示します。 Role=user はユーザー入力をエコーするため、無視できます。 Role=assistant はモデルの出力を示します。

TEXT_MESSAGE_CONTENT

MessageId、Delta

増分テキストフラグメントが含まれます。同じ MessageId を持つイベントの Delta の値を連結して、完全なメッセージを取得します。

TEXT_MESSAGE_END

MessageId

テキストメッセージの終了を示します。

ACTIVITY_DELTA

ActivityType、Patch

waiting_for_agent_thinking などのエージェントからのハートビートまたはステータスイベントです。通常、このイベントは無視できます。

TOOL_CALL_START

ToolCallId、ToolCallName、ParentMessageId

エージェントが das_api などのツール呼び出しを開始したことを示します。

TOOL_CALL_ARGS

ToolCallId、Delta

ツールパラメーターを JSON テキストフラグメントとしてストリーミングします。同じ ToolCallId の Delta の値を連結して、完全なパラメーターセットを取得します。

TOOL_CALL_END

ToolCallId

すべてのツールパラメーターが送信され、ツールが実行されようとしていることを示します。

TOOL_CALL_RESULT

ToolCallId、Content、MessageId

ツール実行結果を返します。 Content フィールドには結果のテキストが含まれます。

典型的なイベントシーケンス

次の例では、「インスタンス rm-uf63bopu77b******* に SQL スロットリングを適用する」というプロンプトを使用して、完全な SSE イベントシーケンスを示します。

1. タスクの開始

サーバーはリクエストを受信すると、セッションの開始を示す RUN_STARTED イベントを送信します。クライアントはこのイベントを使用して、タイマーを開始したり、UI を初期化したりできます。

{"Type":"RUN_STARTED","RunId":"58abc22e-5742-4e9b-802e-5f060a0ca2e3"}

2. ユーザー入力のエコー (無視可能)

サーバーは、Role=user を持つテキストメッセージとしてユーザーメッセージをエコーします。通常、クライアントはこのメッセージを表示する必要はありません。 Role でフィルタリングしてスキップできます。

{"Type":"TEXT_MESSAGE_START","Role":"user","MessageId":"20d2bc27-1644-47e5-8816-b0e764e84a6e"}
{"Type":"TEXT_MESSAGE_CONTENT","MessageId":"20d2bc27-1644-47e5-8816-b0e764e84a6e","Delta":"Apply SQL throttling to instance rm-uf63bopu77b*******"}
{"Type":"TEXT_MESSAGE_END","MessageId":"20d2bc27-1644-47e5-8816-b0e764e84a6e"}

3. エージェントのハートビート (無視可能)

モデルの推論フェーズ中、ACTIVITY_DELTA イベントはハートビート信号として機能します。クライアントではこれらのイベントをスキップしてください。

{"Type":"ACTIVITY_DELTA","ActivityType":"waiting_for_agent_thinking","Patch":[],"MessageId":""}

4. エージェントの分析出力 (Role=assistant)

モデルは TEXT_MESSAGE_CONTENT.Delta イベントを通じてその推論をストリーミングします。同じ MessageId の Delta の値を連結して、完全な応答を組み立てます。

{"Type":"TEXT_MESSAGE_START","Role":"assistant","MessageId":"36aaafdb-ea7f-4475-bad7-136e12117959"}
{"Type":"TEXT_MESSAGE_CONTENT","MessageId":"36aaafdb-ea7f-4475-bad7-136e12117959","Delta":"I need to check the SQL execution status of this instance first to determine which SQL statements require throttling. Let me query the recent SQL audit logs.\n\n"}
{"Type":"TEXT_MESSAGE_END","MessageId":"36aaafdb-ea7f-4475-bad7-136e12117959"}

5. エージェントのツール呼び出し

エージェントが das_api などの外部ツールを呼び出すと、イベントは次のシーケンスに従います: TOOL_CALL_START → 複数の TOOL_CALL_ARGS → TOOL_CALL_END → TOOL_CALL_RESULT。

呼び出しの開始

{"Type":"TOOL_CALL_START","ToolCallId":"call_0fd4d07290b54dd7b7064cc2","ToolCallName":"das_api","ParentMessageId":"36aaafdb-ea7f-4475-bad7-136e12117959"}

パラメーターのストリーミング

複数の TOOL_CALL_ARGS.Delta イベントは ToolCallId で連結する必要があります。連結後、結果を完全な JSON オブジェクトとして解析します:

{
  "command": "execute",
  "api_name": "getdassqlloghotdata",
  "parameters": {
    "instance_id": "rm-uf63bopu77b*******",
    "start": "2024-03-05T15:54:16+08:00",
    "end": "2024-03-05T16:54:16+08:00",
    "max_records_per_page": 10,
    "include_fields": ["sql_text", "execution_count", "avg_consume"],
    "security_risk": "LOW"
  }
}

パラメーターの終了と実行結果

{"Type":"TOOL_CALL_END","ToolCallId":"call_0fd4d07290b54dd7b7064cc2"}
{"Type":"TOOL_CALL_RESULT","ToolCallId":"call_0fd4d07290b54dd7b7064cc2","MessageId":"36aaafdb-ea7f-4475-bad7-136e12117959","Content":"API call succeeded. Response: ..."}

6. タスクの終了

RUN_FINISHED イベントは、SSE ストリームの終了を示します。クライアントはタイマーを停止し、接続を閉じることができます。

{"Type":"RUN_FINISHED","RunId":"58abc22e-5742-4e9b-802e-5f060a0ca2e3"}

SDK の使用例

Java

Maven 依存関係

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alibabacloud-das20200116</artifactId>
    <version>2.0.0</version>
</dependency>

サンプルコード

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.das20200116.AsyncClient;
import com.aliyun.sdk.service.das20200116.models.ChatRequest;
import com.aliyun.sdk.service.das20200116.models.ChatResponseBody;
import darabonba.core.ResponseIterable;
import darabonba.core.client.ClientOverrideConfiguration;

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

public class ChatSample {

    private static AsyncClient createClient() {
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                .accessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
                .build());

        return AsyncClient.builder()
                .region("cn-shanghai")
                .credentialsProvider(provider)
                .serviceConfiguration(Configuration.create().setSignatureVersion(SignatureVersion.V3))
                .overrideConfiguration(ClientOverrideConfiguration.create().setProtocol("HTTPS")
                        .setEndpointOverride("das.cn-shanghai.aliyuncs.com"))
                .build();
    }

    private static String buildMessage(String text) {
        String escaped = text.replace("\\", "\\\\").replace("\"", "\\\"");
        return String.format(
                "{\"id\":\"%s\",\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"%s\"}]}",
                UUID.randomUUID(),
                escaped);
    }

    private static ChatRequest buildRequest(String text, String sessionId, String agentId, String summary) {
        ChatRequest.Builder builder = ChatRequest.builder().message(buildMessage(text));
        if (sessionId != null && !sessionId.isEmpty()) {
            builder.sessionId(sessionId);
        }
        if (agentId != null && !agentId.isEmpty()) {
            builder.agentId(agentId);
        }
        if (summary != null && !summary.isEmpty()) {
            builder.summary(summary);
        }
        return builder.build();
    }

    private static void run(String text, String sessionId, String agentId, String summary) throws Exception {
        AsyncClient client = createClient();
        ChatRequest request = buildRequest(text, sessionId, agentId, summary);

        ResponseIterable<ChatResponseBody> iterable = client.chatWithResponseIterable(request);
        for (ChatResponseBody event : iterable) {
            String delta = event.getDelta();
            String content = event.getContent();
            String activity = event.getActivityType();
            String extName = event.getName();
            Object extValue = event.getValue();

            if (delta != null && !delta.isEmpty()) {
                System.out.print(delta);
            } else if (content != null && !content.isEmpty()) {
                System.out.println();
                System.out.println("[Content] " + content);
            } else if (activity != null && !activity.isEmpty()) {
                System.out.println();
                System.out.println("[Activity] " + activity);
                System.out.println();
            }

            if ("summary".equals(extName) && extValue != null) {
                System.out.println();
                System.out.println("[Summary] " + extValue);
            }
        }
        System.out.println();
        client.close();
    }

    private static class Args {
        String query = "Describe DAS Agent in about 1000 words";
        String sessionId;
        String agentId;
        String summary;
    }

    private static Args parseArgs(String[] argv) {
        Args args = new Args();
        List<String> positional = new ArrayList<>();
        for (int i = 0; i < argv.length; i++) {
            switch (argv[i]) {
                case "--session-id":
                    args.sessionId = argv[++i];
                    break;
                case "--agent-id":
                    args.agentId = argv[++i];
                    break;
                case "--summary":
                    args.summary = argv[++i];
                    break;
                default:
                    positional.add(argv[i]);
                    break;
            }
        }
        if (!positional.isEmpty()) {
            args.query = String.join(" ", positional);
        }
        return args;
    }

    public static void main(String[] argv) throws Exception {
        Args args = parseArgs(argv);
        run(args.query, args.sessionId, args.agentId, args.summary);
    }
}

実行コマンド

mvn -q exec:java -Dexec.mainClass=ChatSample -Dexec.args="'DAS Agent について 1000 ワード程度で説明してください'"

Python

SDK のインストール

pip3 install alibabacloud_das20200116==3.0.0

サンプルコード

# -*- coding: utf-8 -*-

import argparse
import json
import sys
import uuid

from alibabacloud_credentials.client import Client as CredentialClient
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_tea_openapi.client import Client as OpenApiClient
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient


class ChatSample:

    @staticmethod
    def create_client() -> OpenApiClient:
        credential = CredentialClient()
        config = open_api_models.Config(credential=credential)
        config.endpoint = 'das.cn-shanghai.aliyuncs.com'
        return OpenApiClient(config)

    @staticmethod
    def create_api_info() -> open_api_models.Params:
        return open_api_models.Params(
            action='Chat',
            version='2020-01-16',
            protocol='HTTPS',
            pathname='/chat',
            method='POST',
            auth_type='AK',
            style='RPC',
            req_body_type='json',
            body_type='sse',
        )

    @staticmethod
    def build_message(text: str) -> str:
        message = {
            'id': str(uuid.uuid4()),
            'role': 'user',
            'content': [{'type': 'text', 'text': text}],
        }
        return json.dumps(message, ensure_ascii=False)

    @staticmethod
    def build_request(
        text: str,
        session_id: str = None,
        agent_id: str = None,
        summary: str = None,
    ) -> open_api_models.OpenApiRequest:
        query = {'Message': ChatSample.build_message(text)}
        if not UtilClient.is_unset(session_id):
            query['SessionId'] = session_id
        if not UtilClient.is_unset(agent_id):
            query['AgentId'] = agent_id
        if not UtilClient.is_unset(summary):
            query['Summary'] = summary
        return open_api_models.OpenApiRequest(query=query, headers={})

    @staticmethod
    def parse_event_data(event) -> dict:
        if not hasattr(event, 'data'):
            return {}
        data = event.data
        if isinstance(data, str):
            try:
                data = json.loads(data)
            except json.JSONDecodeError:
                return {}
        return data if isinstance(data, dict) else {}

    @staticmethod
    def run(
        text: str,
        session_id: str = None,
        agent_id: str = None,
        summary: str = None,
    ) -> None:
        client = ChatSample.create_client()
        params = ChatSample.create_api_info()
        runtime = util_models.RuntimeOptions()
        request = ChatSample.build_request(text, session_id, agent_id, summary)

        response = client.call_sseapi(params, request, runtime)
        full_content = []

        for res in response:
            data = ChatSample.parse_event_data(res.event)
            if not data:
                continue

            activity = data.get('ActivityType')
            delta = data.get('Delta')
            content = data.get('Content')
            ext_name = data.get('Name')
            ext_value = data.get('Value')

            if delta:
                print(delta, end='', flush=True)
                full_content.append(delta)
            elif content and not delta:
                print(f"\n[Content] {content}", flush=True)
            elif activity:
                print(f"\n[Activity] {activity}", file=sys.stderr, flush=True)

            if ext_name == 'summary' and ext_value:
                print(f"\n[Summary] {ext_value}", flush=True)

        if full_content:
            print()


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description='DAS Chat API のサンプル')
    parser.add_argument(
        'query',
        nargs='?',
        default='Describe DAS Agent in about 1000 words',
        help='ユーザーのクエリ',
    )
    parser.add_argument('--session-id', dest='session_id', help='セッション ID (UUID)')
    parser.add_argument('--agent-id', dest='agent_id', help='エージェント ID')
    parser.add_argument('--summary', choices=['true', 'false'], help='概要情報を返すかどうか')
    return parser.parse_args()


if __name__ == '__main__':
    args = parse_args()
    ChatSample.run(
        text=args.query,
        session_id=args.session_id,
        agent_id=args.agent_id,
        summary=args.summary,
    )

実行コマンド

# シングルターンの会話
python3 chat_sample.py "DAS Agent について 1000 ワード程度で説明してください"

# 複数ターンの会話 (SessionId と AgentId を渡します)
python3 chat_sample.py "スロークエリ分析について詳しく教えてください" \
    --session-id 123e4567-e89b-12d3-a456-xxxxxxxxxxxx \
    --agent-id ag-472T0DxtmjIxxxxx \
    --summary true

Go

SDK のインストール

go mod init das_agent_chat_demo
go get github.com/alibabacloud-go/darabonba-openapi/v2/client
go get github.com/alibabacloud-go/tea-utils/v2/service
go get github.com/alibabacloud-go/tea/tea
go get github.com/google/uuid

サンプルコード

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"strings"

	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	openapiutil "github.com/alibabacloud-go/darabonba-openapi/v2/utils"
	"github.com/alibabacloud-go/tea/dara"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/google/uuid"
)

func createClient() (*openapi.Client, error) {
	config := &openapi.Config{
		AccessKeyId:     tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")),
		AccessKeySecret: tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")),
		Endpoint:        tea.String("das.cn-shanghai.aliyuncs.com"),
	}
	return openapi.NewClient(config)
}

func createAPIInfo() *openapi.Params {
	return &openapi.Params{
		Action:      tea.String("Chat"),
		Version:     tea.String("2020-01-16"),
		Protocol:    tea.String("HTTPS"),
		Pathname:    tea.String("/chat"),
		Method:      tea.String("POST"),
		AuthType:    tea.String("AK"),
		Style:       tea.String("RPC"),
		ReqBodyType: tea.String("json"),
		BodyType:    tea.String("sse"),
	}
}

func buildMessage(text string) (string, error) {
	payload := map[string]interface{}{
		"id":   uuid.NewString(),
		"role": "user",
		"content": []map[string]string{
			{"type": "text", "text": text},
		},
	}
	bs, err := json.Marshal(payload)
	if err != nil {
		return "", err
	}
	return string(bs), nil
}

func buildRequest(text, sessionID, agentID, summary string) (*openapi.OpenApiRequest, error) {
	msg, err := buildMessage(text)
	if err != nil {
		return nil, err
	}
	query := map[string]interface{}{
		"Message": msg,
	}
	if sessionID != "" {
		query["SessionId"] = sessionID
	}
	if agentID != "" {
		query["AgentId"] = agentID
	}
	if summary != "" {
		query["Summary"] = summary
	}
	return &openapi.OpenApiRequest{
		Query:   openapiutil.Query(query),
		Headers: map[string]*string{},
	}, nil
}

func parseEventData(raw *string) map[string]interface{} {
	if raw == nil || *raw == "" {
		return nil
	}
	var data map[string]interface{}
	if err := json.Unmarshal([]byte(*raw), &data); err != nil {
		return nil
	}
	return data
}

func run(text, sessionID, agentID, summary string) error {
	client, err := createClient()
	if err != nil {
		return err
	}
	params := createAPIInfo()
	request, err := buildRequest(text, sessionID, agentID, summary)
	if err != nil {
		return err
	}
	runtime := &dara.RuntimeOptions{}

	sseChan := make(chan *openapi.SSEResponse, 100)
	errChan := make(chan error, 1)
	go client.CallSSEApi(params, request, runtime, sseChan, errChan)

	for event := range sseChan {
		if event.Event == nil {
			continue
		}
		data := parseEventData(event.Event.Data)
		if data == nil {
			continue
		}

		delta, _ := data["Delta"].(string)
		content, _ := data["Content"].(string)
		activity, _ := data["ActivityType"].(string)
		extName, _ := data["Name"].(string)
		extValue := data["Value"]

		switch {
		case delta != "":
			fmt.Print(delta)
		case content != "":
			fmt.Println()
			fmt.Println("[Content]", content)
		case activity != "":
			fmt.Fprintln(os.Stderr, "[Activity]", activity)
		}

		if extName == "summary" && extValue != nil {
			fmt.Println()
			fmt.Println("[Summary]", extValue)
		}
	}
	fmt.Println()
	if err := <-errChan; err != nil {
		return err
	}
	return nil
}

func main() {
	fs := flag.NewFlagSet("chat_sample", flag.ExitOnError)
	sessionID := fs.String("session-id", "", "セッション ID (UUID)")
	agentID := fs.String("agent-id", "", "エージェント ID")
	summary := fs.String("summary", "", "概要を返すかどうか (true/false)")
	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [query] [--session-id ID] [--agent-id ID] [--summary true|false]\n", os.Args[0])
		fs.PrintDefaults()
	}
	if err := fs.Parse(os.Args[1:]); err != nil {
		os.Exit(2)
	}

	query := "Describe DAS Agent in about 1000 words"
	if fs.NArg() > 0 {
		query = strings.Join(fs.Args(), " ")
	}

	if err := run(query, *sessionID, *agentID, *summary); err != nil {
		fmt.Fprintln(os.Stderr, "リクエストに失敗しました:", err)
		os.Exit(1)
	}
}

実行コマンド

# シングルターンの会話
go run chat_sample.go "DAS Agent について 1000 ワード程度で説明してください"

# 複数ターンの会話 (SessionId と AgentId を渡します)
go run chat_sample.go "スロークエリ分析について詳しく教えてください" \
    --session-id 123e4567-e89b-12d3-a456-xxxxxxxxxxxx \
    --agent-id ag-472T0DxtmjIxxxxx \
    --summary true

使用上の注意

  • 複数ターンの会話では、常に同じ SessionId を渡してください。そうでない場合、モデルは前のターンのコンテキストを保持できません。

  • SSE ストリームにはハートビートイベント (ACTIVITY_DELTA) が含まれています。クライアントではこれらのイベントをスキップしてください。

  • Chat API は、入出力文字数に基づいて課金されます。開発中は、予期しない課金を避けるために、まず単純なテストクエリから始めるようにしてください。