Best practices for integrating DAS Agent with the Chat API

Updated at:

The Chat API is an asynchronous interface for DAS Agent that supports knowledge-based Q&A, performance diagnostics, and multi-turn conversations. It returns the agent's reasoning process and final answer as a Server-Sent Events (SSE) stream. This topic describes how to integrate the Chat API by using the Java, Python, and Go SDKs, with complete examples for SSE event parsing and multi-turn conversations.

Prerequisites

  • DAS Agent is activated, and the region of the managed instance matches the country or region of DAS Agent. The instance is bound to DAS Agent.

  • The latest version of the Alibaba Cloud DAS SDK is installed.

  • The region is set to cn-shanghai and the endpoint is set to das.cn-shanghai.aliyuncs.com.

  • The ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured, or the Alibaba Cloud default credential chain is used.

Note

The Chat API is a paid interface that is billed based on the number of input and output characters. For more information, see DAS Agent billing.

Core events

The SSE stream follows the ag-ui protocol. The following table describes the main event types.

Event type

Key fields

Description

RUN_STARTED

RunId

Indicates that the task has started. Marks the beginning of the chat session.

RUN_FINISHED

RunId

Indicates that the task has ended. No more events are produced after this event.

TEXT_MESSAGE_START

MessageId, Role

Marks the beginning of a text message. Role=user echoes the user input and can be ignored. Role=assistant indicates model output.

TEXT_MESSAGE_CONTENT

MessageId, Delta

Contains an incremental text fragment. Concatenate the Delta values of events that share the same MessageId to obtain the full message.

TEXT_MESSAGE_END

MessageId

Marks the end of the text message.

ACTIVITY_DELTA

ActivityType, Patch

A heartbeat or status event from the agent, such as waiting_for_agent_thinking. This event can typically be ignored.

TOOL_CALL_START

ToolCallId, ToolCallName, ParentMessageId

Indicates that the agent has initiated a tool call, such as das_api.

TOOL_CALL_ARGS

ToolCallId, Delta

Streams tool parameters as JSON text fragments. Concatenate the Delta values for the same ToolCallId to obtain the complete parameter set.

TOOL_CALL_END

ToolCallId

Indicates that all tool parameters have been sent and the tool is about to execute.

TOOL_CALL_RESULT

ToolCallId, Content, MessageId

Returns the tool execution result. The Content field contains the result text.

Typical event sequence

The following example uses the prompt "Apply SQL throttling to instance rm-uf63bopu77b*******" to illustrate the complete SSE event sequence.

1. Task start

After the server receives the request, it sends a RUN_STARTED event that marks the beginning of the session. The client can use this event to start a timer or initialize the UI.

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

2. User input echo (ignorable)

The server echoes the user message as a text message with Role=user. The client typically does not need to display this message. Filter by Role to skip it.

{"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. Agent heartbeat (ignorable)

During the model's reasoning phase, ACTIVITY_DELTA events serve as heartbeat signals. Skip these events in the client.

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

4. Agent analysis output (Role=assistant)

The model streams its reasoning through TEXT_MESSAGE_CONTENT.Delta events. Concatenate the Delta values for the same MessageId to assemble the full response.

{"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. Agent tool call

When the agent invokes an external tool such as das_api, the events follow this sequence: TOOL_CALL_START → multiple TOOL_CALL_ARGSTOOL_CALL_ENDTOOL_CALL_RESULT.

Call start

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

Streaming parameters

Multiple TOOL_CALL_ARGS.Delta events must be concatenated by ToolCallId. After concatenation, parse the result as a complete JSON object:

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

Parameter end and execution result

{"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. Task end

A RUN_FINISHED event indicates the end of the SSE stream. The client can stop the timer and close the connection.

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

SDK examples

Java

Maven dependency

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

Sample code

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);
    }
}

Run command

mvn -q exec:java -Dexec.mainClass=ChatSample -Dexec.args="'Describe DAS Agent in about 1000 words'"

Python

Install the SDK

pip3 install alibabacloud_das20200116==3.0.0

Sample code

# -*- 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 sample')
    parser.add_argument(
        'query',
        nargs='?',
        default='Describe DAS Agent in about 1000 words',
        help='User query',
    )
    parser.add_argument('--session-id', dest='session_id', help='Session ID (UUID)')
    parser.add_argument('--agent-id', dest='agent_id', help='Agent ID')
    parser.add_argument('--summary', choices=['true', 'false'], help='Whether to return summary information')
    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,
    )

Run command

# Single-turn conversation
python3 chat_sample.py "Describe DAS Agent in about 1000 words"

# Multi-turn conversation (pass SessionId and AgentId)
python3 chat_sample.py "Tell me more about slow query analysis" \
    --session-id 123e4567-e89b-12d3-a456-xxxxxxxxxxxx \
    --agent-id ag-472T0DxtmjIxxxxx \
    --summary true

Go

Install the 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

Sample code

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", "", "Session ID (UUID)")
	agentID := fs.String("agent-id", "", "Agent ID")
	summary := fs.String("summary", "", "Whether to return 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, "request failed:", err)
		os.Exit(1)
	}
}

Run command

# Single-turn conversation
go run chat_sample.go "Describe DAS Agent in about 1000 words"

# Multi-turn conversation (pass SessionId and AgentId)
go run chat_sample.go "Tell me more about slow query analysis" \
    --session-id 123e4567-e89b-12d3-a456-xxxxxxxxxxxx \
    --agent-id ag-472T0DxtmjIxxxxx \
    --summary true

Usage notes

  • For multi-turn conversations, always pass the same SessionId. Otherwise, the model cannot retain context from previous turns.

  • The SSE stream contains heartbeat events (ACTIVITY_DELTA). Skip these events in the client.

  • The Chat API is billed based on the number of input and output characters. During development, start with simple test queries to avoid unexpected charges.