本教程示範 Coding Agent 如何基於 llms.txt 索引自動檢索ApsaraVideo for VOD文檔,產生端到端可運行 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 │
│ ✓ "自動轉碼" → ApsaraVideo for Media Processing/工作流程.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,黑色背景上的 220dpSurfaceView正常出畫,畫面流暢、無黑屏。點擊互動直播觀眾端:AUI Kits 的
LiveRoomViewerActivity被拉起;AliPlayer 顯示主播畫面;右下方彈幕、點贊、禮物面板均可正常使用。logcat 關鍵日誌:
AliPlayer: prepared、AliPlayer: first frame與IMS: 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 初始化、ApsaraVideo for Media Processing工作流程)。
MUST 明確指出不要使用主帳號 AccessKey,應使用 RAM 子帳號。
如果三項都通過,則 Coding Agent 已具備運行本文 Demo 的能力。