面向智能体的最佳实践

更新时间:
复制 MD 格式

本教程演示 Coding Agent 如何基于 llms.txt 索引自动检索视频点播文档,生成端到端可运行 Demo。内容涵盖服务端批量 URL 上传与自动转码、Android 客户端单视频起播,以及基于 AUI Kits 的互动直播观众端集成。

服务端 Demo

服务端 Demo 包含三个核心模块:批量 URL 上传并指定转码模板组、HTTP 回调服务接收事件通知、转码完成后获取播放地址。

批量 URL 上传

request = UploadMediaByURLRequest()
request.set_UploadURLs(','.join([urllib.parse.quote(url, safe='') for url in urls]))  # URL必须编码!
request.set_UploadMetadatas(json.dumps([{'SourceURL': urllib.parse.quote(u, safe=''), 'Title': f'视频{i}'} for i, u in enumerate(urls)]))
request.set_TemplateGroupId('your_template_group_id')  # 关键:指定后上传完自动转码
response = json.loads(client.do_action_with_exception(request))
# 返回 JobId 列表,后续通过事件通知感知进度

HTTP 回调服务

立即返回 200,避免服务端触发重试:

class CallbackHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        body = json.loads(self.rfile.read(int(self.headers['Content-Length'])))
        self.send_response(200)  # 必须立即返回200,超5秒会重试
        self.end_headers()

        if body['EventType'] == 'UploadByURLComplete' and body['Status'] == 'success':
            print(f"上传完成: VideoId={body['VideoId']},等待转码...")

        elif body['EventType'] == 'TranscodeComplete' and body['Status'] == 'success':
            # 全部转码完成 → 获取播放地址
            play_info = get_play_info(body['VideoId'])
            for stream in play_info['PlayInfoList']['PlayInfo']:
                print(f"  [{stream['Definition']}] {stream['PlayURL']}")

获取播放地址

request = GetPlayInfoRequest()
request.set_VideoId(video_id)
request.set_Formats('mp4,m3u8')
request.set_AuthTimeout(3600)  # 地址有效期 1 小时
response = json.loads(client.do_action_with_exception(request))
# response['PlayInfoList']['PlayInfo'] → 各清晰度的 PlayURL

执行结果

提交批量上传

$ python vod_batch_upload.py

[1/2] 初始化VOD客户端... ✓ (region: cn-shanghai)
[2/2] 提交批量上传任务 (3个视频)... ✓
  RequestId: 25818875-5F78-4AF6-D7393642CA58****
  JobId: ad90a501b1b9****  ← sample1.mp4
  JobId: bd81b612c2c8****  ← sample2.mp4
  JobId: ce72c723d3d9****  ← sample3.mp4

后续流程通过事件通知自动触发:
  UploadByURLComplete → 自动转码 → TranscodeComplete → GetPlayInfo

回调服务接收事件

$ python callback_server.py
回调服务启动: http://0.0.0.0:8080 | 等待回调...

[UploadByURLComplete] VideoId=93ab850b**** | Status=success
  → 上传完成,等待自动转码...

[StreamTranscodeComplete] VideoId=93ab850b**** | Status=success
  → LD清晰度转码完成(已可播放)

[TranscodeComplete] VideoId=93ab850b**** | Status=success
  → 全部转码完成,获取播放地址:
    [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 文档检索可视化

以下流程图展示 Agent 基于 llms.txt 索引自动检索文档并生成代码的过程(仅供理解,无需手动操作):

Prompt: "批量上传+转码+播放"
  │
  ▼
┌── llms.txt (Quick start 匹配 + Common mistakes 约束) ────────┐
│  ✓ "URL 批量上传"  → 媒体上传/URL拉取上传.md                  │
│  ✓ "事件通知"       → 事件通知/事件通知_2.md                  │
│  ✓ "播放地址"       → API参考/获取音视频播放地址.md           │
│  ✓ "Python 初始化" → API参考/初始化_2.md                      │
│  ✓ "自动转码"       → 媒体处理/工作流.md                      │
│  ✗ #1 不用主账号  ✗ #3 不轮询  ✗ #5 URL编码  ✗ #6 QPS退避     │
└────────────────────────┬─────────────────────────────────────┘
                         │
   ┌─────────────────────┼─────────────────────┐
   ▼                     ▼                     ▼
URL拉取上传.md       事件通知_2.md       获取音视频播放地址.md
- UploadMediaByURL  - 事件类型列表       - 请求/返回参数
- URL必须编码        - HTTP回调格式       - 清晰度枚举
- 地域限制           - 超时&重试策略      - QPS=360/s
- 最多20个URL        - 5秒超时           - Status=Normal
   │                     │                     │
   ▼                     ▼                     ▼
   └────► 初始化_2.md(SDK 客户端构造) ◄────┘
                         │
                         ▼
                  工作流.md(TemplateGroupId 自动转码)
                         │
                         ▼
                生成完整可运行代码

关键设计决策

下表为本 Demo 的具体决策追溯,与角色分工对应:角色分工说明谁做什么,本表说明针对本场景,为什么这样选。

决策点

Agent 的选择

文档依据

上传方式

UploadMediaByURL

需求是 URL 列表,文档明确此 API 适用公网 URL 场景

转码触发

上传时指定 TemplateGroupId

文档:设置后上传完自动转码,无需额外 API 调用

状态感知

HTTP 事件回调

Common Mistakes 第 3 条:不要轮询

播放地址时机

TranscodeComplete 后

文档:Status=Normal 才能 GetPlayInfo

AccessKey 管理

环境变量

Common Mistakes 第 1 条:不硬编码

URL 处理

quote(url, safe='')

Common Mistakes 第 5 条:必须编码

地域

cn-shanghai

URL 拉取仅支持华东 2(上海)

容错

指数退避重试

Common Mistakes 第 6 条:Throttling 退避

服务端约束执行清单

Agent 在生成服务端代码时 MUST 逐条自检以下约束:

级别

约束

自检方式

NEVER

使用主账号 AccessKey(C-1)

检查代码中无硬编码 AK,改用环境变量 + RAM 子账号

NEVER

轮询任务状态(C-3)

检查代码中无 while/sleep 循环查询状态,改用 HTTP 回调

MUST

URL 编码(C-4)

检查所有传入 UploadMediaByURL 的 URL 已调用 quote(url, safe='')

MUST

复用 Credential(C-5)

检查 AcsClient 初始化仅一次,后续请求复用

MUST

QPS 退避(C-6)

检查代码包含 Throttling 异常的指数退避重试逻辑

MUST

region=cn-shanghai(C-8)

检查 AcsClient region_id 为 cn-shanghai

MUST

回调 5 秒内返回 200(C-9)

检查 do_POST 在解析 body 后立即 send_response(200)

MUST

TranscodeComplete 后才 GetPlayInfo(C-10)

检查 GetPlayInfo 仅在 TranscodeComplete 事件回调中调用

客户端 Demo:Android 播放器与互动直播观众端

在打通服务端链路的基础上,本模块以 Android 平台为例提供客户端可运行 Demo:涵盖"20 行代码起播 MP4"到"基于 AUI Kits 接入互动直播观众端"。同时演示 Agent 如何利用客户端 llms.txt 索引按场景定位文档并生成代码。

提交给 Agent 的 Prompt

请基于客户端llms索引(端侧播放器/、端侧上传/、端侧短视频/ 等目录),用 Android 实现一个 Demo App。
具体需求为:
1.选取最小依赖包,播放服务端Demo GetPlayInfo 返回的播放地址。
2.在同一个 App 中再加一个入口,使用 AUI Kits 进入互动直播观众端(拉流播放 + 弹幕 + 点赞 + 礼物)。
3.包名、License、AppServer 域名均做成可配置,避免硬编码。

Agent 执行过程

阶段 1:读取客户端 llms 索引,场景匹配

Quick start 匹配结果:
  "Android 起播 VOD"   → 端侧播放器/player-atomic-android.md
  "包/依赖怎么选"       → 端侧播放器/player-overview.md
  "License 怎么接"     → 端侧播放器/player-common-license.md
  "互动直播观众端"      → 端侧播放器/player-kits-aui.md
  "错误码 4400/4013"   → 端侧播放器/player-common-error-codes.md

Common mistakes 命中的约束:
  ✗ License 在播放器创建之后才 init      → 必须在 Application.onCreate
  ✗ Surface 还没 ready 就 setSurface     → 必须等 surfaceCreated
  ✗ 直接拿全功能包                        → 按场景选最小包
  ✗ AppServer 域名硬编码                  → 抽到 BuildConfig / gradle.properties
  ✗ 同一直播间多次 PrivateService.init   → 仅在 SampleApp 里 init 一次

阶段 2:按需加载详细文档

文档

获取目的

端侧播放器/player-overview.md

选包决策(BasicLive / UGC / InteractiveLive / Standard)

端侧播放器/player-atomic-android.md

gradle 依赖、Manifest 权限、混淆、最小起播代码

端侧播放器/player-common-license.md

License 接入时机与 4400/4013 错误排查

端侧播放器/player-kits-aui.md

AUI Kits 客户端模块、AppServer 协作模型

端侧播放器/player-common-error-codes.md

起播失败错误码速查

阶段 3:提取关键决策

选包:
  仅播放          → AliVCSDK_BasicLive
  播放 + 短视频    → AliVCSDK_UGC
  含连麦/互动直播  → AliVCSDK_InteractiveLive   ← 本 Demo 选它
  全功能          → AliVCSDK_Standard / Premium

License:
  PrivateService.initService(ctx, licenseFile, licenseKey)
  必须在第一次 createAliPlayer 之前完成

AUI Kits:
  AppServer + 客户端双侧;AppServer 颁发 身份Token / 直播间Token / IMS Token
  客户端:AUILiveRoomCore + AUILiveRoomViewer + AUIInteraction 三个 module

执行结果

播放器快速集成——工程依赖

在项目级 build.gradle 文件中:

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

在应用级 build.gradle 文件中:

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'
}

权限与混淆

AndroidManifest.xml 文件中:

<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" />

proguard-rules.pro 文件中:

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

License 初始化

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

AndroidManifest.xml 文件中注册:

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

单视频起播界面

生成 activity_player.xml 文件:

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

生成 SimplePlayerActivity.java 文件:

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();
        // 来源:2.1 节 GetPlayInfo 返回的 PlayURL(已带 auth_key)
        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();
        }
    }
}

在日志中看到 prepared 与首帧事件,SurfaceView 正常出图,即代表底层链路通畅。

AUI Kits 互动直播观众端

引入 AUI Kits 模块

settings.gradle 文件中:

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

在应用级 build.gradle 文件中:

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

AppServer 协作模型

客户端不直接持有 IMS Token 等敏感凭证,由 AppServer 鉴权后下发。一次完整的进入直播间请求流程如下:

[Android App]                                [AppServer]
   │                                              │
   │── GET /api/v1/live/viewer/token?roomId=xxx ─►│
   │                                              ├── 鉴权 + 颁发:
   │                                              │     userId / nick / pullUrl / imsToken
   │◄──────── 200 OK + ViewerTokenInfo ───────────┤
   │
   │── LiveRoomViewerActivity.start(params) ──►(AUI Kits 内部)
   │                                              │
   │   (AliPlayer 拉流 + IMS 弹幕)                 │
  • Android App 携带登录态向 AppServer 发起 GET /api/v1/live/viewer/token?roomId=xxx

  • AppServer 校验登录态后,颁发 userId/nick/pullUrl/imsToken

  • App 收到 ViewerTokenInfo 后,调用 LiveRoomViewerActivity.start(params) 进入直播间。

  • AUI Kits 内部用 AliPlayer 拉流,并通过 IMS 接入弹幕、点赞、礼物。

AppServerClient(OkHttp 实现)

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;   // rtmp/flv/m3u8/artc 任一
    public String imsToken;  // 互动消息 SDK Token
}

两个 Demo 入口合一

生成 activity_main.xml 文件:

<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="单视频起播" />

    <Button android:id="@+id/btnLiveRoom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="互动直播观众端" />
</LinearLayout>

生成 MainActivity.java 文件:

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);
            // 实际使用时把 2.1 节 GetPlayInfo 的 PlayURL 透传过来
            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, "进入失败:" + msg, Toast.LENGTH_LONG).show();
            }
        });
    }
}

验证清单

完整 Demo 跑通后预期看到的界面与日志:

  • 点击单视频起播:进入 SimplePlayerActivity,黑色背景上的 220dp SurfaceView 正常出画,画面流畅、无黑屏。

  • 点击互动直播观众端:AUI Kits 的 LiveRoomViewerActivity 被拉起;AliPlayer 显示主播画面;右下方弹幕、点赞、礼物面板均可正常使用。

  • logcat 关键日志:AliPlayer: preparedAliPlayer: first frameIMS: connected 三条同时出现,代表拉流互动两侧均就绪。

Agent 文档检索可视化

以下流程图展示 Agent 基于客户端 llms.txt 索引自动检索文档并生成 Android Demo 的过程(仅供理解,无需手动操作):

Prompt: "Android 起播 + 进入互动直播间"
  │
  ▼
┌── 客户端llms.txt(Quick start + Common mistakes) ───────────┐
│  ✓ "Android 起播"        → player-atomic-android.md         │
│  ✓ "包怎么选"             → player-overview.md               │
│  ✓ "License"             → player-common-license.md          │
│  ✓ "AUI Kits 互动直播"    → player-kits-aui.md               │
│  ✓ "错误码 4400/4013"     → player-common-error-codes.md     │
│  ✗ License 必须在播放器创建前 init                            │
│  ✗ Surface 未 ready 不可 setSurface                          │
│  ✗ 不要直接拿全功能包                                          │
│  ✗ AppServer 域名不要硬编码                                    │
│  ✗ PrivateService.init 仅在 Application 内一次               │
└────────────────────────┬────────────────────────────────────┘
                         │
  ┌─────────────┬────────┴─────────┬─────────────┐
  ▼             ▼                  ▼             ▼
player-      player-atomic-   player-common-  player-kits-aui.md
overview.md  android.md       license.md      - AppServer 协作
- 选包矩阵   - gradle 依赖     - PrivateService - 三类 Token
- 场景对应   - Manifest 权限   - 4400/4013     - 模块拆分
- SDK 体积   - 起播代码        - 包名/BundleID  - 客户端 4 步集成
  └─────────────┴────────┬─────────┴─────────────┘
                         │
                         ▼
              player-common-error-codes.md
              (4400/4013/4034/4036 处理指引)
                         │
                         ▼
              生成完整可运行 Android Demo

关键设计决策

决策点

Agent 的选择

文档依据

SDK 组合包

AliVCSDK_InteractiveLive

player-overview.md 选包矩阵:含连麦/互动直播

License 时机

Application.onCreate

player-common-license.md:必须早于 createAliPlayer

Surface 绑定

surfaceCreated 回调内 setSurface

player-atomic-android.md 标准模板

AppServer 域名

BuildConfig + gradle.properties

Common Mistake:不硬编码

与服务端联动

复用服务端 Demo GetPlayInfo 返回的 PlayURL

端到端一致性

互动直播观众端

AUI Kits 三模块(Core / Viewer / Interaction)

player-kits-aui.md:客户端集成主线

错误处理

setOnErrorListener 通用回调打点;4400/4013/4034/4036 留作业务侧分支扩展

player-common-error-codes.md

客户端约束执行清单

Agent 在生成客户端代码时 MUST 逐条自检以下约束:

级别

约束

自检方式

MUST

License 在 createAliPlayer 之前初始化(C-11)

检查 PrivateService.initService 在 Application.onCreate 中调用

MUST

Surface 绑定在 surfaceCreated 回调内(C-12)

检查 setSurface 在 SurfaceHolder.Callback.surfaceCreated 内调用

NEVER

直接使用全功能 SDK 包(C-13)

检查 gradle 依赖为场景最小包(如 InteractiveLive 而非 Standard)

NEVER

硬编码 AppServer 域名(C-14)

检查域名通过 BuildConfig / gradle.properties 配置

MUST

PrivateService.initService 仅调用一次(C-15)

检查仅在 Application.onCreate 中调用,不在 Activity 中重复调用

MUST

错误码监听(4400/4013/4034/4036)

检查 setOnErrorListener 已注册并对关键错误码做分支处理

PREFER

abiFilters 限制为 armv7 + arm64

检查 ndk { abiFilters } 配置,避免包含 x86 增大包体

上线前 Checklist(Android)

级别

维度

检查项

MUST

License

测试与生产 License 已分别申请并填入对应 BuildConfig

MUST

包体

abiFilters 限制为 armv7 + arm64;按场景选择最小 SDK 组合包

MUST

起播

监听 4400/4013/4034/4036 并打点

MUST

直播间

IMS Token 过期自动续签;AppServer 已开启 HTTPS 与 CORS

MUST

隐私

录音、网络、媒体读取权限均于隐私协议明示

PREFER

灰度

新版本先 5% 灰度,48 小时观察首帧与失败率再放量

PREFER

监控

启用阿里云播放质量服务(QoS)与单点追查

最终目录结构

AliPlayerSampleApp/
├── app/
│   ├── src/main/
│   │   ├── assets/license.crt
│   │   ├── java/com/example/aliplayer/sample/
│   │   │   ├── SampleApp.java
│   │   │   ├── MainActivity.java
│   │   │   ├── SimplePlayerActivity.java         ← 单视频起播
│   │   │   ├── AppServerClient.java              ← 互动直播
│   │   │   ├── ViewerTokenInfo.java
│   │   │   └── LoginManager.java
│   │   └── AndroidManifest.xml
│   └── build.gradle
├── AUILiveRoomCore/                               ← 互动直播
├── AUILiveRoomViewer/                             ← 互动直播
├── AUIInteraction/                                ← 互动直播
├── gradle.properties (APP_SERVER_HOST / LICENSE_KEY)
├── settings.gradle
└── build.gradle

至此,服务端 Demo客户端 Demo 构成完整闭环:涵盖上传、自动转码、获取播放地址、单视频起播及 AUI Kits 互动直播观众端,全流程均由 Agent 基于 llms.txt 索引按需检索文档生成。

附录:Coding Agent 选型与自检

本附录面向尚未使用或安装 Coding Agent 的开发者。

何时需要 Coding Agent

使用场景

是否需要

想让 AI 直接读 llms.txt、按需要展开子文档、生成可运行项目工程

需要

仅想看本文档了解架构、复制代码后人工修改

不需要,浏览器即可

想让 AI 在本地仓库里直接增删改文件、运行命令进行验证

需要

推荐选项

分类

名称

形态

说明

推荐

Qoder / Cursor / Claude Code

桌面 IDE 或 CLI

原生支持 llms.txt 类长文档检索,对国内云厂商 SDK 适配较好;本文 Demo 基于 Qoder 验证。

其他

-

-

任何能联网读取 URL、能在本地写文件、能调用 Shell 的 Coding Agent 均可。

说明

安装链接以产品官网为准,避免使用第三方镜像。

安装后的最小自检

将以下 Prompt 发送给安装好的 Coding Agent,对照预期答案验证能力。MUST 三项全部通过才视为具备运行本文 Demo 的能力:

请帮我读取 https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/vod/llms/llms.txt,
告诉我:
1.这份索引一共有几个一级模块?分别是什么?
2."批量上传 + 自动转码 + 获取播放地址"这条链路涉及哪几个子文档?请列出相对路径。
3.Common mistakes to avoid 中和 "AccessKey" 相关的有哪几条?

预期答案

  • MUST 正确列出 llms.txt 的一级模块清单。

  • MUST 给出与服务端 Demo 中按需加载详细文档表大致一致的子文档清单(媒体上传、事件通知、获取音视频播放地址、Python SDK 初始化、媒体处理工作流)。

  • MUST 明确指出不要使用主账号 AccessKey,应使用 RAM 子账号。

如果三项都通过,则 Coding Agent 已具备运行本文 Demo 的能力。