All Products
Search
Document Center

ApsaraVideo VOD:Best practices for agents

Last Updated:Aug 20, 2026

In this tutorial, you use an AI Coding Agent to build an end-to-end ApsaraVideo VOD workflow. The server-side demo batch-uploads videos by URL, automatically transcodes them with a specified transcoding template group, and retrieves playback URLs through event callbacks. The client-side demo provides an Android app with single video playback and an interactive live streaming viewer powered by AUI Kits.

Prerequisites

Server-side demo

  • An AccessKey pair for a RAM user, stored as environment variables. Do not use a root account AccessKey.

  • Python with the Alibaba Cloud SDK for VOD (aliyunsdkcore and aliyunsdkvod) installed.

  • A publicly accessible HTTP endpoint to receive event notification callbacks.

  • VOD region set to cn-shanghai (URL-based uploads are only supported in the China (Shanghai) region).

Client-side demo

  • Android Studio with compileSdk 34 and minSdk 21.

  • An AliPlayer license file (license.crt) placed in app/src/main/assets/.

  • AUI Kits modules (AUILiveRoomCore, AUILiveRoomViewer, AUIInteraction) imported into the project.

  • An AppServer endpoint that issues viewer tokens for the interactive live streaming viewer.

Server-side demo: Batch upload, transcode, and playback

The following code submits a batch upload job with a transcoding template group. Specifying TemplateGroupId during upload triggers automatic transcoding, eliminating the need for a separate API call.

request = UploadMediaByURLRequest()
request.set_UploadURLs(','.join([urllib.parse.quote(url, safe='') for url in urls]))  # URLs must be encoded!
request.set_UploadMetadatas(json.dumps([{'SourceURL': urllib.parse.quote(u, safe=''), 'Title': f'Video{i}'} for i, u in enumerate(urls)]))
request.set_TemplateGroupId('your_template_group_id')  # Key: Specify this to auto-transcode after upload
response = json.loads(client.do_action_with_exception(request))
# Returns a list of JobIds. Track progress via event notifications.

HTTP callback service (key logic): Returns HTTP 200 immediately to prevent server-side retries (constraint S-7).

class CallbackHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        body = json.loads(self.rfile.read(int(self.headers['Content-Length'])))
        self.send_response(200)  # Must return 200 immediately; retry is triggered after 5s timeout
        self.end_headers()

        if body['EventType'] == 'UploadByURLComplete' and body['Status'] == 'success':
            print(f"Upload complete: VideoId={body['VideoId']}, waiting for transcoding...")

        elif body['EventType'] == 'TranscodeComplete' and body['Status'] == 'success':
            # All transcodings complete -> Get playback URLs
            play_info = get_play_info(body['VideoId'])
            for stream in play_info['PlayInfoList']['PlayInfo']:
                print(f"  [{stream['Definition']}] {stream['PlayURL']}")

Get playback URL (key logic):

request = GetPlayInfoRequest()
request.set_VideoId(video_id)
request.set_Formats('mp4,m3u8')
request.set_AuthTimeout(3600)  # URL is valid for 1 hour
response = json.loads(client.do_action_with_exception(request))
# response['PlayInfoList']['PlayInfo'] → playback URLs for each definition

Execution results

Submitting batch upload:

$ python vod_batch_upload.py

[1/2] Initializing VOD client... ✓ (region: cn-shanghai)
[2/2] Submitting batch upload job (3 videos)... ✓
  RequestId: 25818875-5F78-4AF6-D7393642CA58****
  JobId: ad90a501b1b9****  ← sample1.mp4
  JobId: bd81b612c2c8****  ← sample2.mp4
  JobId: ce72c723d3d9****  ← sample3.mp4

Subsequent steps are triggered automatically by event notifications:
  UploadByURLComplete → Auto Transcode → TranscodeComplete → GetPlayInfo

Callback service receives events:

$ python callback_server.py
Callback service started: http://0.0.0.0:8080 | Waiting for callbacks...

[UploadByURLComplete] VideoId=93ab850b**** | Status=success
  → Upload complete, waiting for auto-transcoding...

[StreamTranscodeComplete] VideoId=93ab850b**** | Status=success
  → LD definition transcoding complete (ready for playback)

[TranscodeComplete] VideoId=93ab850b**** | Status=success
  → All transcodings complete, getting playback URLs:
    [LD] https://vod.example.com/****/sample1-ld.mp4?auth_key=****
    [SD] https://vod.example.com/****/sample1-sd.mp4?auth_key=****
    [HD] https://vod.example.com/****/sample1-hd.mp4?auth_key=****

Agent document retrieval

Prompt: "Batch upload + transcode + playback"
  │
  ▼
┌── llms.txt (Quick start matching + Common mistakes constraints) ──┐
│  ✓ "Batch URL upload"         → media-upload/url-based-upload.md  │
│  ✓ "Event notification"       → event-notification/event-notification_2.md  │
│  ✓ "Playback URL"             → api-reference/get-playback-url.md   │
│  ✓ "Python init"              → api-reference/initialization_2.md   │
│  ✓ "Auto transcode"           → media-processing/workflow.md        │
│  ✗ #1 Don't use root AK ✗ #3 Don't poll ✗ #5 URL encode ✗ #6 QPS backoff │
└────────────────────────┬─────────────────────────────────────┘
                         │
   ┌─────────────────────┼─────────────────────┐
   ▼                     ▼                     ▼
url-based-upload.md  event-notification_2.md get-playback-url.md
- UploadMediaByURL  - Event type list       - Request/response params
- URL must be encoded - HTTP callback format    - Definition enums
- Region limit      - Timeout & retry policy - QPS=360/s
- Max 20 URLs       - 5 sec timeout         - Status=Normal
   │                     │                     │
   ▼                     ▼                     ▼
   └────► initialization_2.md (SDK client constructor) ◄────┘
                         │
                         ▼
                  workflow.md (Auto-transcode with TemplateGroupId)
                         │
                         ▼
                Generate complete runnable code

Key design decisions

The following table lists key design decisions for this demo, with explicit constraint IDs traced back to the structured constraint reference.

Decision point

Agent's choice

Basis in documentation

Upload method

UploadMediaByURL

The documentation specifies this API for batch uploading from a list of public URLs.

Transcoding trigger

Specify TemplateGroupId during upload

The documentation states that specifying TemplateGroupId during upload triggers automatic transcoding, eliminating the need for a separate API call.

Status tracking

HTTP event callback

Constraint S-3 (MUST): Do not poll.

Timing for getting playback URL

After TranscodeComplete

The documentation states that GetPlayInfo requires the video status to be Normal.

AccessKey management

Environment variables

Constraint S-1, S-2 (MUST): Do not hardcode.

URL handling

quote(url, safe='')

Constraint S-5 (MUST): Must be encoded.

Region

cn-shanghai

The documentation states that URL-based uploads are only supported in the China (Shanghai) region.

Fault tolerance

Exponential backoff retry

Constraint S-6 (MUST): Backoff for throttling.

Client-side demo: Android player and live viewer

This section builds a runnable Android demo on top of the server-side workflow. The demo plays an MP4 file with minimal code and integrates an interactive live streaming viewer with AUI Kits.

Prompt submitted to the agent:

Based on the client-side llms index (in directories like `client-side-player/`,
`client-side-upload/`, `client-side-short-video/`), implement a demo app for Android.
Specific requirements:
1. Select the minimum required dependency package to play the playback URL returned
   by the server-side demo's GetPlayInfo response.
2. In the same app, add another entry point to enter an interactive live streaming
   viewer using AUI Kits (with stream pulling, live comments (danmaku), likes,
   and gifts).
3. Make the package name, license, and AppServer domain configurable to avoid
   hardcoding.

Agent execution process

Phase 1: Read the client-side llms index and match scenarios

Quick start matching results:
  "Start VOD playback on Android"   → client-side-player/player-atomic-android.md
  "How to choose packages/dependencies" → client-side-player/player-overview.md
  "How to integrate the license"      → client-side-player/player-common-license.md
  "Interactive live streaming viewer" → client-side-player/player-kits-aui.md
  "Error codes 4400/4013"           → client-side-player/player-common-error-codes.md

Common mistakes matched:
  ✗ Initializing the license after creating the player → Must be done in Application.onCreate (C-1)
  ✗ Calling setSurface before the Surface is ready      → Must wait for surfaceCreated (C-2)
  ✗ Using the full-featured package by default        → Choose the minimal package for the scenario (C-3)
  ✗ Hardcoding the AppServer domain                   → Abstract to BuildConfig / gradle.properties (C-4)
  ✗ Calling PrivateService.init multiple times        → Initialize only once in the Application class (C-5)

Phase 2: Load detailed documentation on demand

Document

Purpose

client-side-player/player-overview.md

Package selection decision (BasicLive / UGC / InteractiveLive / Standard)

client-side-player/player-atomic-android.md

Gradle dependencies, Manifest permissions, obfuscation, and minimal playback code

client-side-player/player-common-license.md

License integration timing and troubleshooting for errors 4400/4013

client-side-player/player-kits-aui.md

AUI Kits client-side modules and AppServer collaboration model

client-side-player/player-common-error-codes.md

Quick reference for playback start failures

Phase 3: Extract key decisions

Package selection:
  Playback only          → AliVCSDK_BasicLive
  Playback + short video   → AliVCSDK_UGC
  Includes co-hosting/interactive live streaming  → AliVCSDK_InteractiveLive   ← This demo chooses this one
  Full-featured          → AliVCSDK_Standard / Premium

License:
  PrivateService.initService(ctx, licenseFile, licenseKey)
  Must be completed before the first call to createAliPlayer

AUI Kits:
  AppServer + client-side; AppServer issues Identity Token, Live Room Token, and IMS Token.
  Client: Three modules - AUILiveRoomCore, AUILiveRoomViewer, and AUIInteraction.

Set up project dependencies

In the project-level build.gradle file:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://maven.aliyun.com/nexus/content/repositories/releases" }
    }
}

In the app-level build.gradle file:

android {
    compileSdk 34
    defaultConfig {
        applicationId "com.example.aliplayer.sample"
        minSdk 21
        targetSdk 34

        ndk { abiFilters "armeabi-v7a", "arm64-v8a" }

        buildConfigField "String", "APP_SERVER_HOST",
                "\"${project.findProperty('APP_SERVER_HOST') ?: 'https://your-appserver.example.com'}\""
        buildConfigField "String", "APP_SERVER_API_PREFIX", "\"/api/v1/live/\""
        buildConfigField "String", "LICENSE_KEY",
                "\"${project.findProperty('LICENSE_KEY') ?: ''}\""
    }
    buildFeatures { buildConfig true }
}

dependencies {
    implementation 'com.aliyun.aio:AliVCSDK_InteractiveLive:6.0.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}

Configure permissions and obfuscation

In the AndroidManifest.xml file:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

In the proguard-rules.pro file:

-keep class com.alivc.**{*;}
-keep class com.aliyun.**{*;}
-keep class com.cicada.**{*;}
-dontwarn com.alivc.**
-dontwarn com.aliyun.**
-dontwarn com.cicada.**

Initialize the license

Create the SampleApp.java file:

public class SampleApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // Place license.crt in app/src/main/assets/
        PrivateService.initService(getApplicationContext(),
                "file:///android_asset/license.crt",
                BuildConfig.LICENSE_KEY);
    }
}

Register SampleApp in the AndroidManifest.xml file:

<application
    android:name=".SampleApp"
    android:label="@string/app_name"
    android:theme="@style/Theme.MaterialComponents.DayNight">
    <activity android:name=".MainActivity" android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".SimplePlayerActivity" />
</application>

Build the single video playback UI

Create the activity_player.xml layout file:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="220dp"
        android:layout_gravity="center" />
</FrameLayout>

Create the SimplePlayerActivity.java file:

public class SimplePlayerActivity extends AppCompatActivity {

    public static final String EXTRA_PLAY_URL = "play_url";

    private AliPlayer aliPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        SurfaceView surfaceView = findViewById(R.id.surfaceView);
        aliPlayer = AliPlayerFactory.createAliPlayer(getApplicationContext());

        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override public void surfaceCreated(SurfaceHolder h) {
                aliPlayer.setSurface(h.getSurface());
            }
            @Override public void surfaceChanged(SurfaceHolder h, int f, int w, int hh) {
                aliPlayer.surfaceChanged();
            }
            @Override public void surfaceDestroyed(SurfaceHolder h) {
                aliPlayer.setSurface(null);
            }
        });

        aliPlayer.setOnPreparedListener(() -> aliPlayer.start());
        aliPlayer.setOnErrorListener(err ->
                Log.e("AliPlayer", "code=" + err.getCode() + " msg=" + err.getMsg()));

        UrlSource source = new UrlSource();
        // Source: The playback URL (with auth_key) from the server-side demo's GetPlayInfo response
        String playUrl = getIntent().getStringExtra(EXTRA_PLAY_URL);
        source.setUri(playUrl != null ? playUrl : "https://your.cdn/test.mp4");
        aliPlayer.setDataSource(source);
        aliPlayer.setAutoPlay(true);
        aliPlayer.prepare();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (aliPlayer != null) {
            aliPlayer.stop();
            aliPlayer.release();
        }
    }
}

If the prepared event and the first frame event appear in the logs and the SurfaceView renders the image correctly, the underlying link is clear.

Integrate the AUI Kits live viewer

Import AUI Kits modules

In the settings.gradle file:

include ':app',
        ':AUILiveRoomCore',
        ':AUILiveRoomViewer',
        ':AUIInteraction'

In the app-level build.gradle file, add the following dependencies:

implementation project(':AUILiveRoomCore')
implementation project(':AUILiveRoomViewer')
implementation project(':AUIInteraction')

AppServer collaboration model

The client never holds sensitive credentials directly. Instead, the AppServer issues them after authentication. The following diagram shows the complete workflow for entering a live room:

[Android App]                                [AppServer]
   │                                              │
   │── GET /api/v1/live/viewer/token?roomId=xxx ─►│
   │                                              ├── Authenticates and issues:
   │                                              │     userId / nick / pullUrl / imsToken
   │◄──────── 200 OK + ViewerTokenInfo ───────────┤
   │
   │── LiveRoomViewerActivity.start(params) ──►(Inside AUI Kits)
   │                                              │
   │   (AliPlayer for stream pulling + IMS for live comments)
  1. The Android app sends a GET /api/v1/live/viewer/token?roomId=xxx request to the AppServer with the login state.

  2. The AppServer verifies the login status and issues the userId, nick, pullUrl, and imsToken.

  3. After the app receives ViewerTokenInfo, it calls LiveRoomViewerActivity.start(params) to enter the live room.

  4. Internally, AUI Kits uses AliPlayer for stream pulling and connects to the Interactive Messaging Service (IMS) for live comments (danmaku), like, and gift features.

AppServerClient (OkHttp implementation)

Create the AppServerClient.java and ViewerTokenInfo.java files:

public class AppServerClient {

    public interface Callback {
        void onSuccess(ViewerTokenInfo info);
        void onFail(String msg);
    }

    private static final OkHttpClient client = new OkHttpClient();

    public static void fetchViewerToken(String roomId, Callback cb) {
        HttpUrl url = HttpUrl.parse(
                BuildConfig.APP_SERVER_HOST + BuildConfig.APP_SERVER_API_PREFIX + "viewer/token")
                .newBuilder()
                .addQueryParameter("roomId", roomId)
                .build();

        Request req = new Request.Builder()
                .url(url)
                .header("Authorization", "Bearer " + LoginManager.getToken())
                .build();

        client.newCall(req).enqueue(new okhttp3.Callback() {
            @Override public void onFailure(Call call, IOException e) {
                cb.onFail(e.getMessage());
            }
            @Override public void onResponse(Call call, Response resp) throws IOException {
                if (!resp.isSuccessful() || resp.body() == null) {
                    cb.onFail("HTTP " + resp.code());
                    return;
                }
                ViewerTokenInfo info = new Gson().fromJson(
                        resp.body().string(), ViewerTokenInfo.class);
                cb.onSuccess(info);
            }
        });
    }
}

public class ViewerTokenInfo {
    public String roomId;
    public String userId;
    public String nick;
    public String pullUrl;   // Any of rtmp, flv, m3u8, or artc
    public String imsToken;  // Interactive Messaging Service (IMS) Token
}

Combine the demo entries

Create the activity_main.xml layout file:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:padding="24dp"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button android:id="@+id/btnSimple"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Simple Playback" />

    <Button android:id="@+id/btnLiveRoom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Interactive Live Viewer" />
</LinearLayout>

Create the MainActivity.java file:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btnSimple).setOnClickListener(v -> {
            Intent intent = new Intent(this, SimplePlayerActivity.class);
            // In a real application, pass the playback URL from the server-side demo's GetPlayInfo response here
            intent.putExtra(SimplePlayerActivity.EXTRA_PLAY_URL,
                    "https://vod.example.com/****/sample1-hd.mp4?auth_key=****");
            startActivity(intent);
        });

        findViewById(R.id.btnLiveRoom).setOnClickListener(v -> enterRoom("demo_room_001"));
    }

    private void enterRoom(String roomId) {
        AppServerClient.fetchViewerToken(roomId, new AppServerClient.Callback() {
            @Override public void onSuccess(ViewerTokenInfo info) {
                LiveRoomEnterParams params = new LiveRoomEnterParams.Builder()
                        .roomId(info.roomId)
                        .userId(info.userId)
                        .userNick(info.nick)
                        .pullUrl(info.pullUrl)
                        .imsToken(info.imsToken)
                        .build();
                LiveRoomViewerActivity.start(MainActivity.this, params);
            }
            @Override public void onFail(String msg) {
                Toast.makeText(MainActivity.this, "Failed to enter room: " + msg, Toast.LENGTH_LONG).show();
            }
        });
    }
}

Verification checklist

After running the demo, verify the following UI behavior and log output:

  • Click Simple Playback: The SimplePlayerActivity starts, and the video plays smoothly in the SurfaceView without a black screen.

  • Click Interactive Live Streaming Viewer: The AUI Kits LiveRoomViewerActivity is launched. AliPlayer displays the host's video feed. The live comments (danmaku), like, and gift panels in the bottom right corner are functional.

  • Key logcat logs: The simultaneous appearance of AliPlayer: prepared, AliPlayer: first frame, and IMS: connected indicates that both stream pulling and interaction are ready.

Agent document retrieval

Prompt: "Android playback + enter interactive live room"
  │
  ▼
┌── Client-side llms.txt (Quick start + Common mistakes) ────────┐
│  ✓ "Android playback"        → player-atomic-android.md         │
│  ✓ "How to choose package"   → player-overview.md               │
│  ✓ "License"                 → player-common-license.md          │
│  ✓ "AUI Kits interactive"    → player-kits-aui.md               │
│  ✓ "Error codes 4400/4013"   → player-common-error-codes.md     │
│  ✗ License must be initialized before player creation           │
│  ✗ Do not call setSurface before Surface is ready               │
│  ✗ Do not use the full-featured package by default              │
│  ✗ Do not hardcode the AppServer domain                         │
│  ✗ Call PrivateService.init only once in Application          │
└────────────────────────┬────────────────────────────────────┘
                         │
  ┌─────────────┬────────┴─────────┬─────────────┐
  ▼             ▼                  ▼             ▼
player-      player-atomic-   player-common-  player-kits-aui.md
overview.md  android.md       license.md      - AppServer collab
- Package matrix - gradle deps    - PrivateService - Three token types
- Scenario map - Manifest perms - 4400/4013     - Module breakdown
- SDK size     - Playback code  - Package/BundleID - 4-step client integration
  └─────────────┴────────┬─────────┴─────────────┘
                         │
                         ▼
              player-common-error-codes.md
              (Guidance for 4400/4013/4034/4036)
                         │
                         ▼
              Generate complete runnable Android Demo

Key design decisions

Decision point

Agent's choice

Basis in documentation

SDK bundle

AliVCSDK_InteractiveLive

The selection matrix in player-overview.md recommends this bundle for interactive live streaming scenarios (constraint C-3).

License timing

Application.onCreate

Constraint C-1 (MUST): Must be called before createAliPlayer.

Surface binding

setSurface within the surfaceCreated callback

Constraint C-2 (MUST): player-atomic-android.md standard template.

AppServer domain

BuildConfig + gradle.properties

Constraint C-4 (MUST): Do not hardcode.

Server-side integration

Reuse the playback URL from the server-side demo's GetPlayInfo response

This ensures end-to-end consistency between the server-side and client-side demos.

Interactive live viewer

AUI Kits three modules (Core / Viewer / Interaction)

player-kits-aui.md: Main client-side integration guide.

Error handling

setOnErrorListener is a general callback for event reporting. Codes 4400, 4013, 4034, and 4036 are reserved for custom client-side handling.

player-common-error-codes.md

Pre-launch checklist

Dimension

Check item

License

Test and production licenses have been applied for and configured in their respective BuildConfigs.

Package size

abiFilters are limited to armv7 + arm64. The minimal SDK bundle is selected based on the scenario.

Playback start

Listeners for 4400/4013/4034/4036 are implemented for monitoring.

Live room

Automatic renewal for expired IMS Tokens is implemented. AppServer has HTTPS and CORS enabled.

Privacy

Permissions for audio recording, network access, and media reading are all declared in the privacy policy.

Canary release

First, release the new version to 5% of users. Monitor the first-frame time and failure rates for 48 hours before a full rollout.

Monitoring

Enable Alibaba Cloud's player quality monitoring service (QoS) and single-point tracing.

Final directory structure

AliPlayerSampleApp/
├── app/
│   ├── src/main/
│   │   ├── assets/license.crt
│   │   ├── java/com/example/aliplayer/sample/
│   │   │   ├── SampleApp.java
│   │   │   ├── MainActivity.java
│   │   │   ├── SimplePlayerActivity.java         ← Simple Playback
│   │   │   ├── AppServerClient.java              ← Interactive Live Streaming
│   │   │   ├── ViewerTokenInfo.java
│   │   │   └── LoginManager.java
│   │   └── AndroidManifest.xml
│   └── build.gradle
├── AUILiveRoomCore/                               ← Interactive Live Streaming
├── AUILiveRoomViewer/                             ← Interactive Live Streaming
├── AUIInteraction/                                ← Interactive Live Streaming
├── gradle.properties (APP_SERVER_HOST / LICENSE_KEY)
├── settings.gradle
└── build.gradle

The server-side and client-side demos form a complete, closed-loop workflow. Using the llms.txt index, the Agent covers video uploading, automatic transcoding, playback URL retrieval, basic video playback, and an interactive live streaming viewer with AUI Kits.

Appendix: Agent selection and self-check

This section is for developers who have not installed a Coding Agent.

When to use a Coding Agent

Use case

Is it needed?

You want the AI to read llms.txt, expand sub-documents as needed, and generate a runnable project.

Yes

You only want to read this document to understand the architecture, then copy and manually modify the code.

No. A web browser is sufficient.

You want the AI to directly add, delete, and modify files in your local repository and run commands to validate the changes.

Yes

Recommended options

Category

Name

Type

Description

Recommended

Qoder / Cursor / Claude Code

Desktop IDE or CLI

Natively supports long-document retrieval using llms.txt-style indexes and works well with SDKs from Chinese cloud providers. Qoder was used to verify the demos in this document.

Other

-

-

Any Coding Agent that can access URLs, write local files, and call shell commands.

Note

Use the installation links from the official product website. Avoid using third-party images.

Minimum self-check

Send the following prompt to your Coding Agent to verify its capabilities:

Please read https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/vod/llms/llms.txt,
and tell me:
1. How many top-level modules are in this index? What are they?
2. Which sub-documents are involved in the "batch upload + automatic transcoding + get audio/video playback URL" workflow? Please list their relative paths.
3. In the "Common mistakes to avoid" section, what are the notes related to "AccessKey"?

Expected answers:

  • Correctly lists the top-level modules from llms.txt.

  • Lists sub-documents largely consistent with the on-demand loading table in the server-side demo (media upload, event notification, get audio/video playback URL, Python SDK initialization, media processing workflow).

  • Clearly states: Do not use an Alibaba Cloud account AccessKey. Use a RAM user instead.

An agent that passes all three checks is ready for the demos above.