Alicloud VMS Smart Voice Call Skill. Use for making AI-powered voice calls with natural language intent. The system automatically matches contacts from the a...
---
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=<name> mode=<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="<the user's intent>"
> ```
>
> **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=<from-config>` 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-confirmed natural-language intent>" \
--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="<user-confirmed natural-language intent>"
> ```
> - 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: <ms-timestamp>^<call-sequence>, e.g. "1779785134472^9876543210").
CALL_ID="<Model.CallId from step 2 response>"
# 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 `<hr>` by Markdown renderers and reflow the rest into a single line):
>
> **Call Result**
>
> | | |
> |---|---|
> | ✅ Status | `<stateDesc>` (state=`<state>`) |
> | 👤 Callee | `<callee_masked>` |
> | 📲 Display No. | `<calleeShowNumber>` |
> | ⏰ Duration | `<duration>` seconds |
>
> **Timeline**
>
> | | |
> |---|---|
> | 🕐 Accepted | `<gmtCreate>` |
> | 🔔 Ringing | `<bRingTime>` (+`<offset_after_accepted>`s) |
> | 📞 Answered | `<bStartTime>` (+`<offset_after_ringing>`s after ring) |
> | 🔚 Hung up | `<bEndTime>` (talk `<duration>`s) |
> | 👋 Hangup by | `<callee-initiated\|caller-initiated>` (hangupDirection=`<0\|1>`) |
>
> **Reference**
>
> | | |
> |---|---|
> | 🆔 CallId | `<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 `<hr>` 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 `<callee_masked>` 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 |
don't have the plugin yet? install it then click "run inline in claude" again.