--- name: alibabacloud-vms-smart-call-by-tts description: | Alicloud VMS Smart Voice Call Skill. Use for making AI-powered voice calls with natural language intent. The system automatically matches contacts from the address book and generates call content via LLM. Triggers: "智能语音通知", "AI 打电话", "smart voice call", "SubmitIntent", "给某人打电话", "语音通知提醒". --- # Alibaba Cloud VMS Smart Voice Call Initiate **AI-generated voice calls** through Alibaba Cloud Voice Messaging Service (`dyvmsapi`) by describing the call intent in natural language. The system **automatically matches the addressee from the user's address book** and generates the spoken content with an LLM. **Architecture**: `Alibaba Cloud VMS (dyvmsapi) + SubmitIntent API + LLM Content Generation + Contact Book Matching` > **⚠️ Path-Selection Rules (MUST be followed in this exact order)**: > > 1. **Always try the intranet primary path first**: enter §1 and run the full intranet plugin install flow (`cli.aliyun-inc.com`). It is **not allowed** to skip §1 just because "this looks like an external network". > 2. **Switch to §1.alt only when §1.3 reports `lookup cli.aliyun-inc.com: no such host` or `connection timeout`**. Any other error MUST be triaged via `references/plugin-troubleshooting.md` and **MUST NOT** be treated as a network failure. > 3. **The external fallback only replaces `SubmitIntent`**: it routes through the script `scripts/dyvmsapi_rpc.py` to call the public gateway directly (`SubmitIntent` visibility is still **private**, with **no SLA**). `QueryCallDetailByCallId` is `public`, and the **public-release `dyvmsapi` CLI plugin already includes that subcommand** (`aliyun plugin install --names dyvmsapi`, no `--source-base` needed). In external mode, call-detail queries still go through the public CLI plugin; the script only handles `SubmitIntent`. > 4. **⚠️ The public-release plugin and the intranet-online plugin share the same name (`aliyun-cli-dyvmsapi`) and are mutually exclusive** — only one can be installed at a time, and the second install overwrites the first. **Priority: install the intranet-online version whenever possible (it ships both `SubmitIntent` and `QueryCallDetailByCallId`); only fall back to the public-release version when the intranet install fails** (which loses `SubmitIntent`, then the script fills the gap). **Never install the public-release version while the intranet-online version is available** — doing so loses the `submit-intent` subcommand. > 5. The full external-fallback execution manual lives in `references/external-network-fallback.md`. > 6. **Switch back to the primary path as soon as intranet connectivity is restored.** Do not stay on the fallback long-term. Switch back via: `aliyun plugin uninstall --name dyvmsapi` (note the singular `--name`), then re-run `aliyun plugin install ... --source-base ...` from §1.3 to reinstall the intranet-online version. Compared with the legacy `SingleCallByTts`, this capability: - ❌ Does **not** require enterprise-real-name verification - ❌ Does **not** require provisioning service instances or binding real outbound numbers - ❌ Does **not** require creating or reviewing voice templates - ✅ Only requires a configured address book and a stated intent in natural language > **Capability Boundary**: this Skill wraps two capabilities — **initiating a smart call** (`SubmitIntent`) and **querying the call detail** (`QueryCallDetailByCallId`). Address-book CRUD operations and phone-number ownership verification are **out of scope**; the user must perform those in the Alibaba Cloud Voice Service console. --- ## Operational Constraints - **Content compliance**: The spoken content is fully generated by the LLM and is double-gated by server-side sensitive-word screening + LLM safety review. The user **cannot** specify the literal playback text directly. - **Compliant use only**: Limited to lawful and legitimate notification scenarios. **Strictly prohibited**: debt collection, telecom fraud, harassment, or any other illegitimate use. - **Single-callee per invocation**: Each call matches exactly **one** contact. Group calls are not supported. Notify multiple recipients via separate invocations. - **Phone-number safety**: The phone number is **never** passed to the LLM. The server side performs number lookup and outbound dialing. - **Per-number rate limit**: Same number — 1 call/minute, 5 calls/hour, 20 calls/24-hour-window. - **Address-book size limit**: At most 200 contacts per user. --- ## 1. Installation > **[MUST] Execute steps 1.1 → 1.2 → 1.3 → 1.4 in strict order. Do not skip any step. On any failure, consult `references/plugin-troubleshooting.md`.** ### 1.1 [MUST] Verify Aliyun CLI is installed at version ≥ 3.3.8 ```bash # Note on --user-agent scope: # Aliyun CLI system / tooling commands (`version`, `configure`, `plugin`, # `upgrade`, `oss`, `auto-completion`, `mcp-proxy`, `go-migrate`, `otsutil`, # and any subcommand invoked with `--help`) DO NOT accept `--user-agent` and # MUST NOT carry it (passing it raises `unknown flag: --user-agent`). # The ONLY exception is `aliyun configure ai-mode set-user-agent`, which is # the dedicated command for declaring the AI-Mode global User-Agent (see §1.1.1). # Only BUSINESS calls (e.g. `aliyun dyvmsapi submit-intent ...`, # `aliyun dyvmsapi query-call-detail-by-call-id ...`) accept and MUST carry # --user-agent "AlibabaCloud-Agent-Skills/alibabacloud-vms-smart-call-by-tts/1.0.0" # so the call can be attributed to this Skill. aliyun version ``` - ✅ Output `3.3.15` or any version ≥ `3.3.8` → continue to 1.1.1 - ❌ Command not found / version < 3.3.8 → run the **environment-aware installer** shipped with this Skill. It picks the first strategy that matches the runtime (Homebrew → user-level signed-binary tarball under `$HOME/.local/bin`), is idempotent, self-verifies, and **never executes a remote script via shell pipe**: ```bash bash scripts/install_aliyun_cli.sh ``` > **Notes**: > - **Do not** run `curl -fsSL https://aliyuncli.alicdn.com/setup.sh | bash` to install the CLI: piping a remote script directly to a shell **bypasses integrity verification and is treated as a security violation** by static scanners; that script also writes to `/usr/local/bin/aliyun` and silently retries with `sudo`, which fails in non-interactive shells (agent sandboxes, CI runners) where no tty is available to type the password. The installer above downloads only the **signed binary tarball**, never executes a remote script, and routes around the sudo issue. > - On Apple Silicon Macs the installer pulls `aliyun-cli-macosx-latest-arm64.tgz`; **never** install the `amd64` build on arm64 hardware — it requires Rosetta 2 and is brittle. > - **Verify before continuing**: the installer exits non-zero if the resulting `aliyun version` is < `3.3.8`. If it fails, switch to the manual matrix in [`references/cli-installation-guide.md`](./references/cli-installation-guide.md) (it covers macOS amd64/arm64, Linux amd64/arm64, and Windows, each with a sudo-less variant). > **Why is the floor 3.3.8?** §1.3 uses `plugin install --source-base`, a flag first introduced in aliyun CLI **v3.3.7 (2026-04-16)** (PR #1299). The floor is set conservatively to **3.3.8**. Versions < 3.3.8 will fail §1.3 with `ERROR: invalid flag source-base` — **this is a tooling-version issue, not a network issue**; upgrade the CLI. ### 1.1.1 [MUST] Enable AI-Mode (executed immediately after CLI is verified) > Enable AI-Mode **as soon as** §1.1 confirms the CLI is available, and **before** any other `aliyun ...` invocation in this Skill. > > **User-Agent is declared per-command on BUSINESS calls only.** Every business `aliyun ...` invocation (e.g. `aliyun dyvmsapi submit-intent`, `aliyun dyvmsapi query-call-detail-by-call-id`) in SKILL.md and `references/*.md` MUST explicitly pass the per-Skill UA `"AlibabaCloud-Agent-Skills/alibabacloud-vms-smart-call-by-tts/1.0.0"` so the call can be attributed to this Skill. **System / tooling commands** (the categories listed in the scope-comment of §1.1) DO NOT accept this UA flag and MUST NOT carry it — passing it raises `unknown flag: --user-agent` and aborts the command. The single exception is `aliyun configure ai-mode set-user-agent`, used below to declare the AI-Mode default UA. ```bash # 1) Enable AI-Mode for this Skill session. aliyun configure ai-mode enable # 2) Declare the AI-Mode default UA so the gateway attributes every business # call below to this Skill. `ai-mode set-user-agent` is the ONLY # system/tooling command that legitimately accepts the UA flag. aliyun configure ai-mode set-user-agent \ --user-agent "AlibabaCloud-Agent-Skills/alibabacloud-vms-smart-call-by-tts" ``` > **[MUST] Disable AI-Mode at EVERY exit point** of this Skill (success, failure, or early STOP) by running `aliyun configure ai-mode disable` — see §6 step 2 / step 4 and §9 best-practice #5. ### 1.2 [MUST] Check whether the `submit-intent` subcommand already exists ```bash aliyun dyvmsapi submit-intent --help 2>&1 ``` Decide based on the output: | Output Pattern | Verdict | Next Step | |---|---|---| | Contains `API 版本: 2017-05-25` and contains `--user-message` | Plugin is ready | Skip 1.3, jump to §2 | | `unknown command "submit-intent"` / `command not found` | Plugin not installed | Go to 1.3 | | `aliyun: command not found` | CLI not installed | Back to 1.1 | | Other | Anomaly | Consult `references/plugin-troubleshooting.md` | ### 1.3 [MUST] Install the intranet-online `dyvmsapi` plugin > `SubmitIntent` is distributed only through the Alibaba **intranet-online** CLI plugin. The public-release `aliyun-cli-dyvmsapi` does **not** contain this subcommand. **Run the following command verbatim (no parameter may be omitted or modified)**: ```bash aliyun plugin install --names dyvmsapi \ --version 0.1.0 \ --source-base https://cli.aliyun-inc.com/registry_id/2/env/online/plugins ``` **[MUST] Hard requirements (a single mismatch will fail the install or pull the wrong build)**: | Parameter | Correct | Common Mistake | |---|---|---| | CLI version | **≥ 3.3.8** | < 3.3.8 → `ERROR: invalid flag source-base` — go upgrade in §1.1, do **NOT** switch to the external fallback | | `--names` | **plural `s`** | `--name` (install fails with `invalid flag`; the singular form is for `uninstall` only) | | `--version` | `0.1.0` (required) | Omitting it fetches the latest **public** build | | `--source-base` | the URL above (required) | Omitting it pulls from the public mirror, which lacks `submit-intent` | | URL path | `env/online/plugins` | `env/pre/plugins` is deprecated | | URL host | `cli.aliyun-inc.com` | Resolvable from Alibaba intranet only | **Expected output**: ``` Downloading aliyun-cli-dyvmsapi 0.1.0... Plugin aliyun-cli-dyvmsapi 0.1.0 installed successfully! ``` > If the output reads `plugin "aliyun-cli-dyvmsapi" is already installed (version X.Y.Z); continuing will replace it with version 0.1.0` → **expected behavior** (overwrite install). Let it finish. > > If the output reads `ERROR: invalid flag source-base` → **CLI version is < 3.3.8** and does not understand the flag. Re-run the **environment-aware fallback chain in §1.1** (it auto-picks brew / system-wide / user-level upgrade) to upgrade, then retry §1.3. **[FORBIDDEN]** It is strictly forbidden to switch to §1.alt due to this error — this is a tooling-version issue, not a network issue. The external fallback is reserved for `no such host` / `connection timeout` only. > > If the output reads `lookup cli.aliyun-inc.com: no such host` / `connection timeout` → the runtime is not on the Alibaba intranet. **Switch to §1.alt** and continue (functionally equivalent; no plugin install needed). > > Other errors → consult `references/plugin-troubleshooting.md`. ### 1.4 [MUST] Re-validate after install ```bash # `--help` is a help-mode invocation: it MUST NOT carry `--user-agent` # (passing it raises `unknown flag: --user-agent` and aborts the command). # See §1.1.1 for the full system-vs-business UA scope rule. aliyun dyvmsapi submit-intent --help ``` **Validation criteria (ALL must hold before proceeding)**: - Output contains `API 版本: 2017-05-25` - Output contains the parameter `--user-message` - Output contains the description `提交文本意图…` or equivalent If any criterion fails → see `references/plugin-troubleshooting.md`. --- ### 1.alt External Fallback Path (enter only when the primary path is unavailable) > **Trigger**: §1.3 reports `lookup cli.aliyun-inc.com: no such host`, or it has been confirmed that the runtime is on a public network without access to the Alibaba intranet/VPN. > **Do not enter this path while the primary plugin is functional.** **Prerequisites** (none may be missing): - Python 3.6+ (preinstalled on macOS/Linux; verify with `python3 --version`) — required by step 2 (script-based `SubmitIntent`) - The script `scripts/dyvmsapi_rpc.py` (zero third-party dependencies) shipped in this repo — handles `SubmitIntent` only - Aliyun CLI + the **public-release** `dyvmsapi` plugin — required by step 4 (CLI-based `QueryCallDetailByCallId`): ```bash # Refresh the public plugin index so the latest public-release dyvmsapi metadata is used. aliyun plugin update # Install the public-release dyvmsapi plugin (default source = public mirror; no --source-base). aliyun plugin install --names dyvmsapi ``` ⚠️ **Mutually exclusive with the intranet-online plugin**: if §1.3 successfully installed the intranet-online build (0.1.0), **do NOT install the public-release build here** — the intranet-online build already includes `query-call-detail-by-call-id`. Install the public-release build **only** when §1.3 failed with `no such host`. - Public-network reachability to `dyvmsapi.aliyuncs.com` **Validate the script is in place**: ```bash python3 scripts/dyvmsapi_rpc.py --help ``` If the output contains `dyvmsapi public RPC direct-call script (zero dependencies)`, proceed to §2 credentials validation. (On the external script path, credentials are auto-injected by the helper `scripts/run_with_aliyun_creds.py` from the current `aliyun configure` profile — see §2.) **Important constraints**: - After switching to the external path, the `aliyun dyvmsapi submit-intent` invocation in §6 step 2 MUST be replaced by the script invocation; step 4 (`aliyun dyvmsapi query-call-detail-by-call-id`) **still uses the CLI** with the public-release plugin installed above. Details: §6.alt and `references/external-network-fallback.md`. - AI-Mode: enabled once in §1.1.1 and **MUST** be disabled at every exit point of this Skill, regardless of whether step 2 runs via the script or step 4 runs the CLI. The per-command UA rule from §1.1.1 still applies: business `aliyun ...` calls (e.g. `aliyun dyvmsapi query-call-detail-by-call-id`) MUST carry the per-Skill UA; system / tooling commands MUST NOT — see §1.1.1. - Do not interleave the two paths. --- ## 2. Credentials Validation > **Pre-check: Alibaba Cloud Credentials Required** > > **Security Rules:** > - **NEVER** read, echo, or print AK/SK values (e.g., `echo $ALIBABA_CLOUD_ACCESS_KEY_ID` is FORBIDDEN) > - **NEVER** ask the user to input AK/SK directly in the conversation or command line > - **NEVER** use `aliyun configure set` with literal credential values > - **ONLY** use `aliyun configure list` to check credential status > > ```bash > aliyun configure list > ``` > Check the output for a valid profile (AK, STS, or OAuth identity). > > **Note**: `Valid` only means the local profile fields are well-formed; it does **not** prove the AK is still active server-side. If a downstream call returns `InvalidAccessKeyId.NotFound` / `SignatureDoesNotMatch`, the credentials are syntactically OK but have been disabled or rotated — cross-check status in the [AK Management Console](https://ram.console.aliyun.com/manage/ak). > > **If no valid profile exists, STOP here.** > 1. Obtain credentials from the [Alibaba Cloud Console](https://ram.console.aliyun.com/manage/ak) > 2. Configure credentials **outside of this session** by running `aliyun configure` in the user's own terminal (or by exporting environment variables in the shell profile). The interactive flow asks for several values including AK ID, AK Secret, and **Default Region Id**. Only the **Region** prompt typically blocks users (the CLI does not show candidates), so the agent **MUST proactively tell the user**: recommend `cn-hangzhou` — `dyvmsapi` is a centralized service that is not region-sensitive, so any valid region works; if the user's other Alibaba Cloud resources already live in another region (e.g. `cn-shanghai`, `cn-beijing`, `cn-shenzhen`, `cn-hongkong`, `ap-southeast-1`), they may pick that one instead. Full candidate list: [`references/cli-installation-guide.md`](./references/cli-installation-guide.md#default-region-id--common-choices). > 3. Return and re-run after `aliyun configure list` shows a valid profile --- ### 2.1 Primary Path (CLI) — Credentials On the primary path, `aliyun dyvmsapi submit-intent` / `query-call-detail-by-call-id` automatically read credentials from the current profile. **The agent does not need to take any extra action.** ### 2.2 §1.alt External Fallback (Script) — Default Auto-Injected Credentials > **Design**: The external-fallback script path no longer asks the user to manually `export` AK/SK. Instead, it **reuses the user's already-configured `aliyun configure` current profile**, and the helper `scripts/run_with_aliyun_creds.py` auto-injects credentials into the subprocess environment. Three reasons motivate this design: ① a user experience consistent with the primary path; ② cross-process shell `export` cannot pass credentials into the agent's subprocess (technically infeasible); ③ since the user has already provided credentials for this profile, asking again would constitute redundant input. > > **[MUST] Standard invocation pattern**: agents always go through the helper. **Do not extract credentials from `~/.aliyun/config.json` by hand.** > > ```bash > # 1) Pre-check: confirm the current profile meets auto-injection requirements > python3 scripts/run_with_aliyun_creds.py --check > # Expected: `OK profile=default mode=AK ready for auto-injection` → ready > > # 2) Tell the user which profile will be used (transparency; no credential values) > python3 scripts/run_with_aliyun_creds.py --print-profile > # Output e.g.: `profile=default mode=AK region=cn-hangzhou` > # The agent MUST tell the user explicitly: "I will call SubmitIntent with profile= mode=" — give the user a chance to refuse. > > # 3) Make the actual call (helper injects env; the script reads from env; no values are persisted) > python3 scripts/run_with_aliyun_creds.py -- \ > python3 scripts/dyvmsapi_rpc.py SubmitIntent -P UserMessage="" > ``` > > **Supported modes**: `AK` / `StsToken`. `StsToken` additionally injects `ALIBABA_CLOUD_SECURITY_TOKEN`, which the script then includes as a public parameter in the signature. > > **Unsupported modes** (`RamRoleArn` / `EcsRamRole` / `OAuth` / `External` / `CredentialsURI` …): the helper exits 3 and prompts the user to manually obtain temporary AK/SK/STS. In that case, fall back to the primary path (the CLI natively supports all modes) or have the user create a new AK profile. > > **[FORBIDDEN] Prohibited behaviors**: > - Bypassing the helper and reading `~/.aliyun/config.json` / `json.load(...)` directly inside agent code is **strictly prohibited** — three common failure modes follow: hardcoding `profiles[0]` instead of `current` (wrong profile), missing `StsToken`'s `sts_token`, and accidentally leaking credentials through debug output or script logs. > - Writing AK/SK/SecurityToken into `ran_scripts/*.sh` / `outputs/*.md` / any log / any git-visible file is **strictly prohibited** (placeholder comments such as `# AK=` are also forbidden — they mislead readers). > - Passing `SecurityToken` as a business parameter via `-P SecurityToken=...` is **strictly prohibited** — it is a public parameter and must participate in the signature; the helper handles this automatically. > - `echo` / `print` / `--debug`-printing credential values is **strictly prohibited**. > - Reverting to the legacy `read -s ... && export ...` pattern is **strictly prohibited** — cross-process shell environments cannot propagate to subprocesses; this is technically infeasible. --- ## 3. RAM Permissions RAM sub-accounts must be granted `dyvms:SubmitIntent` and `dyvms:QueryCallDetailByCallId`. Root accounts need no extra setup. See `references/ram-policies.md` for the full policy. > **[MUST] Permission Failure Handling:** When any command or API call fails due to permission errors at any point during execution, follow this process: > 1. Read `references/ram-policies.md` to get the full list of permissions required by this SKILL > 2. Use the `ram-permission-diagnose` skill to guide the user through requesting the necessary permissions > 3. Pause and wait until the user confirms that the required permissions have been granted --- ## 4. Prerequisite: Configure the Address Book > **Address-book management is OUT OF SCOPE for this Skill**. Before initiating a smart call, the user must complete the following in the **Alibaba Cloud Voice Service console**: > > 1. **Verify phone-number ownership**: complete ownership verification for every phone number to be added (verification is valid for ~6 months by default) > 2. **Add contacts**: assign each verified number a recognizable **Tag**, e.g. "Mom", "Manager Zhang", "Lao Wu" > 3. (Optional) Update or remove existing contacts > > **Address-book constraints**: > - `Tag` and `PhoneNumber` are strictly one-to-one > - Tag is the only signal the LLM uses for contact matching — use clear, distinctive names --- ## 5. Parameter Confirmation > **IMPORTANT: Parameter Confirmation** — Before executing the API call, > confirm the user's **intent** (who to call + what to convey). If the user's message already makes both clear, no confirmation is needed — proceed directly. > Do NOT iterate on wording or draft the spoken text — `UserMessage` is an intent description, > not the final playback script; the server-side LLM generates the actual spoken content (`Model.CallContent`). | Parameter | Required | Description | Default | |---|---|---|---| | `UserMessage` | **Yes** | Natural-language description of the call intent (NOT the literal spoken text) | None (user must provide) | > **`UserMessage` examples**: > - "Remind Mom I won't be home for dinner tonight" > - "Call Manager Zhang and let him know the meeting is at 10 AM tomorrow" > - "Tell Lao Wu to bring the contract this afternoon" > > These are **intent descriptions**. If who + what are already clear from the user's original message, proceed directly — do **not** echo back or ask for approval. Do **not** ask "shall I phrase it as X?" or iterate on wording — the server generates the spoken content. --- ## 6. Core Workflow > **AI-Mode and User-Agent**: AI-Mode was enabled in **§1.1.1** (executed immediately after the CLI was verified). Do **not** re-enable here unless it was previously disabled (see note below Step 3). Every **business** `aliyun ...` invocation below (e.g. `aliyun dyvmsapi submit-intent`, `aliyun dyvmsapi query-call-detail-by-call-id`) MUST explicitly pass the per-Skill UA `"AlibabaCloud-Agent-Skills/alibabacloud-vms-smart-call-by-tts/1.0.0"`. **System / tooling commands** DO NOT accept this flag and MUST NOT carry it — see §1.1.1. > > **[MUST] Disable AI-Mode at EVERY exit point** of this Skill — success, failure, early STOP, all exits. The disable command is: > ```bash > aliyun configure ai-mode disable > ``` ### Step 1: Extract User Intent Extract **who** to call and **what** to convey from the user's message. A natural-language reference is sufficient (e.g. "我爸", "Mom", "张经理") — it does **not** need to be the exact Tag literal stored in the address book; the server-side LLM performs **semantic matching** against the user's contact list. - **Intent already clear** (who + what both unambiguous) → skip confirmation, proceed to Step 2 immediately. Example: "Call my dad and tell him to go grocery shopping" — who=dad, what=go grocery shopping, fire directly. - **Intent partially unclear** (missing who, or what is too vague to act on) → ask **once** to clarify the missing part, then fire. Example: "Make a call" — who=? what=? ask once. > **[MUST] Zero or one round, then fire** — `UserMessage` is NOT the verbatim script that will be spoken; it is an intent description fed to the server-side LLM, which generates the actual spoken content. Once who + what are clear (whether from the original message or a single clarification), proceed to Step 2 immediately. Prohibited behaviors: > - Drafting a "notification script" and asking the user to review wording > - Asking "shall I say it like this?" or offering multiple phrasings > - Asking the user for the **exact Tag literal** in the address book (e.g. "你爸在通讯录里的 Tag 叫什么?") — the server does semantic matching; a natural-language reference is enough > - Confirming the contact's identity when it is already obvious ("爸"/"老爸"/"爸爸" are all the same role — do not ask "which one?") > - Looping back for a second confirmation unless the user **actively** says the intent is wrong > **[MUST] Never abort on unverifiable Tag** — The Agent has **no local API** to list or verify address-book entries (see §4: CRUD is out of scope). If the Agent cannot confirm whether the user-provided Tag exists, it **MUST still proceed to Step 2** and let the server validate. Halting the flow, asking the user to "go check the console first", or refusing to call because the Tag "might not exist" is **strictly prohibited** — the correct behavior is to fire the API and faithfully relay whatever the server returns (success or error). ### Step 2: Initiate the Smart Call ```bash aliyun dyvmsapi submit-intent \ --user-message "" \ --user-agent "AlibabaCloud-Agent-Skills/alibabacloud-vms-smart-call-by-tts/1.0.0" ``` > **§6.alt External Fallback** — If the runtime entered §1.alt (script path), replace this step with the helper-driven invocation (credentials **MUST** be auto-injected via the helper, see §2.2): > ```bash > python3 scripts/run_with_aliyun_creds.py -- \ > python3 scripts/dyvmsapi_rpc.py SubmitIntent \ > -P UserMessage="" > ``` > - The Key MUST be **PascalCase** (`UserMessage`, not `user-message`). Response shape is identical to the CLI; step 3's parsing logic is shared. > - **Direct invocation of `python3 scripts/dyvmsapi_rpc.py ...` without going through the helper is strictly prohibited** — the script does not read `~/.aliyun/config.json` itself; missing env will produce `[ERROR] AK/SK not provided`. > - Before the call, run the §2.2 [MUST] 3-step routine (`--check` → `--print-profile` to inform the user → actual call). > - Details in `references/external-network-fallback.md`. ### Step 3: Parse the Response - `Code == "OK"` and `Success == true`: the call has been initiated - Show `Model.Tag` (matched contact), `Model.CallContent` (spoken text), `Model.CallId` to the user - **For phone-number safety, the response does not contain the dialed number** - For error handling, see the error-code table in `references/related-commands.md` - **`Code != "OK"` is a valid execution result** — e.g. contact-not-found, quota exceeded, parameter invalid. Parse the error, report it to the user faithfully, and treat the Skill run as complete (with an error outcome). Do **not** retry automatically or loop back to Step 1 unless the user explicitly asks. > **AI-Mode lifecycle after Step 3**: > - If `Code != "OK"` or the user does not need Step 4 → this is the **exit point** → disable AI-Mode now. > - If `Code == "OK"` and Step 4 may follow (user wants to check call result) → do **NOT** disable yet. Keep AI-Mode enabled until Step 4 completes. > - If the Skill session was interrupted (e.g. Agent already disabled AI-Mode after Step 3, then the user later requests Step 4) → **re-enable** AI-Mode before Step 4, then disable again at the end. This is the only legitimate re-enable scenario. > **[FORBIDDEN] Strictly forbidden to fabricate responses or stage Mock deliveries** — When step 2 encounters an **infrastructure failure** (credential missing / expired, network unreachable, non-zero script exit code with no JSON body, `InvalidAccessKeyId`, `SignatureDoesNotMatch`): > - **Before STOP, ALWAYS run `aliyun configure ai-mode disable` first** (AI-Mode was enabled in §1.1.1; disable is mandatory at every exit point including failure paths). > - **STOP IMMEDIATELY**, report the error to the user faithfully and verbatim, and ask them to fix the issue and retry. > - Fabricating a `Code: OK` / fake `CallId` / fake `Model.Tag` / fake `Model.CallContent` is **strictly prohibited**. > - Writing falsified `call_result.json` / `task_summary.md` / `executed_actions.sh` to mimic a successful delivery is **strictly prohibited**. > - Wording such as "simulated" / "mock" / "assume the user has" / "since this is a simulated environment" used to bypass real invocation is **strictly prohibited**. > - Any of the above is treated as a fraudulent delivery. ### Step 4 (Optional): Query the Call Result > The `Model.CallId` returned by `SubmitIntent` only signifies that the call has been **accepted**, not that the callee has **answered**. If the user wants to confirm whether the call connected, invoke `query-call-detail-by-call-id`. ```bash # Take the CallId returned by SubmitIntent (format: ^, e.g. "1779785134472^9876543210"). CALL_ID="" # The first segment of CallId is already a Unix-millisecond timestamp; reuse it as query-date. # `--query-date` is an int (Unix-millisecond timestamp), NOT a yyyyMMdd string — do NOT call any datetime conversion. QUERY_DATE="${CALL_ID%%^*}" aliyun dyvmsapi query-call-detail-by-call-id \ --call-id "$CALL_ID" \ --prod-id 11000000300006 \ --query-date "$QUERY_DATE" \ --user-agent "AlibabaCloud-Agent-Skills/alibabacloud-vms-smart-call-by-tts/1.0.0" ``` **Hard requirements**: - `--prod-id` **MUST be `11000000300006`** (the Voice Notification product ID). `SubmitIntent` is dispatched under this product; any other prod-id will return `Code: OK` with no `Data`. - `--query-date` MUST be on the **same day** as the CallId (millisecond timestamp). - **CDR write delay**: For notification-style calls (typical duration 10–30 seconds), the CDR is generally available **about 40 seconds after the call ends**. If `Code: OK` returns with no `Data`, retry once after another 30–60 seconds. **Do not hard-wait 1–2 minutes blindly.** > **§6.alt External — step 4 still uses the CLI**. `QueryCallDetailByCallId` visibility is `public`; the public-release `aliyun-cli-dyvmsapi` plugin already includes it: > ```bash > aliyun plugin update > aliyun plugin install --names dyvmsapi # public-release; no --source-base > ``` > The public-release plugin should already be installed from §1.alt. If for any reason it is not yet present, run the two commands above first. After install, the `aliyun dyvmsapi query-call-detail-by-call-id ...` command above works as-is. On the external path, the script only replaces `SubmitIntent`; CDR queries still go through the CLI. Details in `references/external-network-fallback.md`. **Response parsing**: `Data` is a JSON **string** (requires a second parse) and contains: | Field | Meaning | |---|---| | `stateDesc` | Localized state description, e.g. `用户接听` (user answered) / `未接听` (no answer) / `呼叫中` (in progress) | | `state` | State code (`200000` = answered) | | `duration` | Call duration in seconds | | `bRingTime` / `bStartTime` / `bEndTime` | Ring / answer / hangup timestamps | | `callee` | Dialed number (**this CDR API returns the full number** — only the account owner can view their own CDR; this does not contradict the SubmitIntent rule that phone numbers are not passed to the LLM) | | `gmtCreate` | Time the call request was accepted by the platform | | `calleeShowNumber` | Caller-display number | > **[MUST] Readable Presentation** — Strictly forbidden to dump the raw `Data` JSON string to the user. After parsing, render the result as **three real Markdown tables** (NOT a fenced code block, NOT ASCII separator lines like `─────` — those get absorbed as `
` by Markdown renderers and reflow the rest into a single line): > > **Call Result** > > | | | > |---|---| > | ✅ Status | `` (state=``) | > | 👤 Callee | `` | > | 📲 Display No. | `` | > | ⏰ Duration | `` seconds | > > **Timeline** > > | | | > |---|---| > | 🕐 Accepted | `` | > | 🔔 Ringing | `` (+``s) | > | 📞 Answered | `` (+``s after ring) | > | 🔚 Hung up | `` (talk ``s) | > | 👋 Hangup by | `` (hangupDirection=`<0\|1>`) | > > **Reference** > > | | | > |---|---| > | 🆔 CallId | `` | > > **Rules**: > - Render real Markdown tables. Do **NOT** wrap the layout in a fenced code block, and do **NOT** use ASCII separators (`─────`, `===`, `---` inside cells, etc.) — they get absorbed as `
` by Markdown renderers and reflow the rest of the text into a single line. > - Show the localized `stateDesc` next to the numeric `state`. Don't show the raw number alone. > - The callee number MUST be masked: first 3, then `****`, then last 4 — this is what `` represents in the table. **Never output the full number.** > - Time fields must include relative offsets (accepted → ringing, ringing → answered) so the user can perceive the timeline. > - `hangupDirection`: `0` = callee hung up, `1` = caller hung up. > - For unanswered cases (`state ≠ 200000`), omit the **Timeline** answered/hung-up rows and use a one-line summary, e.g. "✗ Callee did not answer (rang for X seconds, then hung up)". For the full parameter reference and response examples, see `references/related-commands.md`. > **[MUST] Disable AI-Mode at EVERY exit point** — before delivering the final response for ANY reason, run `aliyun configure ai-mode disable` first. --- ## 7. Success Validation See `references/verification-method.md`. **Quick check**: `Code == "OK"` and a non-empty `Model.CallId` in the response = the call has been initiated successfully. --- ## 8. Command Reference See `references/related-commands.md`. --- ## 9. Best Practices 1. **`UserMessage` must reference a contact explicitly**: use a recognizable tag the user believes is in their address book (e.g. "Mom", "Manager Zhang"); avoid pronouns ("him", "that person"). 2. **Respect the rate limits**: per-number 1/min, 5/hour, 20/24h; for batch notifications, space invocations at least 60 seconds apart. 3. **Configure the address book before invocation**: ensure the contact is added in the console with valid phone-ownership verification. 4. **Compliant use only**: for lawful notification scenarios; debt collection, fraud, harassment, and the like are forbidden. 5. **AI-Mode MUST be disabled** at the end of every Skill execution (success or failure): run `aliyun configure ai-mode disable`. --- ## 10. Reference Links | File | Content | |---|---| | `references/cli-installation-guide.md` | Aliyun CLI installation guide | | `references/plugin-troubleshooting.md` | `dyvmsapi` plugin install troubleshooting (**MUST consult on any §1 failure**) | | `references/ram-policies.md` | RAM policy (minimum-Action) | | `references/related-commands.md` | CLI command reference, parameters, error codes | | `references/verification-method.md` | Verification methods | | `references/acceptance-criteria.md` | Acceptance criteria | | `references/external-network-fallback.md` | **External fallback execution manual** (enter only when the primary path is unavailable) | | `scripts/dyvmsapi_rpc.py` | Zero-dependency Python script that calls the public `dyvmsapi` gateway directly | | `scripts/run_with_aliyun_creds.py` | Credential-injection helper: auto-injects env from the `aliyun configure` current profile, then exec's the subcommand (mandatory entrypoint for the external fallback path) | | `scripts/install_aliyun_cli.sh` | Environment-aware installer / upgrader for the Aliyun CLI (Homebrew → user-level signed-binary tarball; idempotent and self-verifying). Invoked from §1.1 when the CLI is missing or below 3.3.8. | | `related_apis.yaml` | Dependent API inventory |