When the Global AI Hackathon Series with Qwen Cloud was announced, I set out to tackle a pervasive B2B sales problem: account executives and sales development reps (SDRs) spend over 70% of their working hours on manual research, contact hunting, and email drafting.
My goal was to build Ideal Customer Finder (Track 4: Autopilot Agent)—a 9-node autonomous state machine that takes a natural language Ideal Customer Profile (ICP), searches the live web, enriches contacts, detects buying signals, scores accounts using dual AI/vector reasoning, and drafts personalized outreach.
Moving from architectural theory to a live, production-ready system on Qwen Cloud and Alibaba Cloud involved real engineering friction, deep debugging, and critical architectural pivots. Here is the unvarnished build story.
1. Taming the Token Explosion: Optimizing MCP Tools
My pipeline relies on live web search to discover matching companies and executive contacts. To integrate web search, I used the Nimble MCP server exposed through Qwen's Responses API (type: "mcp" tool calling nimble_search).
On my initial test run for signal detection, I hit a massive roadblock: a single search call consumed over 35,000 tokens and took 97.6 seconds to execute.
By inspecting the raw parameter schemas of the Nimble MCP server (inspect_nimble_mcp_tools.py), I discovered why: nimble_search was defaulting to full-page markdown web extraction rather than lightweight search snippets.
I updated the agent's system prompt to explicitly pass strict search parameters:
{
"search_depth": "lite",
"time_range": "month",
"country": "US",
"max_results": 5
}
The Result: Token consumption dropped by 57% (from ~35,000 down to ~15,000 tokens), and wall-clock execution time dropped from 97.6s to 21.5s—a 4.5x performance gain.
2. Orchestrating High-Concurrency Tool Calls
When scaling from single-account tests to a full agent run, the pipeline needs to enrich 8 accounts and fire 32 concurrent signal detection queries (across funding, hiring, and expansion events) simultaneously.
When pushing this much parallel load through an LLM reasoning engine via asyncio.gather, relying solely on a single MCP tool-calling path introduces latency bottlenecks and risks hitting standard cloud concurrency limits.
The Engineering Pivot
To ensure production-grade reliability, I implemented a flexible BaseConnector pattern with an intelligent fallback mechanism (USE_NIMBLE_MCP):
-
Step 1 (Account Sourcing - Single Query): Routes through Qwen and the Nimble MCP server (
qwen3.7-plus), leveraging Qwen's native MCP orchestration to parse the initial search. -
Steps 2 & 3 (Contacts & Signals - High Parallel Load): Gracefully degrades to routing directly to the REST API via an
asyncio.Semaphore(6)concurrency cap.
This hybrid approach achieved the best of both worlds: utilizing Qwen's reasoning for complex tool orchestration, while preserving full pipeline execution speed (~33.3 seconds end-to-end) under heavy parallel load.
3. The xargs Environment Variable Trap
During local Docker testing, I noticed that vector embeddings were returning NULL in PostgreSQL despite OPENAI_API_KEY being specified in my .env file.
Inside the container's entrypoint.sh, environment variables were being exported using a common shell idiom:
# BROKEN: xargs splits on special characters
export $(grep -v '^#' /app/.env | xargs)
Because the Dashscope API key naturally contained special characters (+ and /), xargs truncated the key value during word-splitting. The Python runtime received a malformed key, causing embeddings.py to silently return None.
I replaced the loading logic with shell-native variable exporting:
# FIXED: Handles special characters seamlessly
set -a
source /app/.env
set +a
Once applied, text-embedding-v4 began generating 1536-dimensional vectors for both ICPs and accounts, enabling dual scoring in the UI: AI Reasoning Fit (Qwen LLM) + Semantic Closeness (pgvector cosine similarity).
4. Voice I/O: Navigating the Multimodal SDK
To make the agent experience feel truly autonomous, I wanted full Voice I/O: the ability for reps to dictate their ICP (Speech-to-Text), hear the agent narrate its progress (Text-to-Speech), and even clone their own voice for video outreach.
STT: Dodging the OSS Upload Trap
To implement the microphone input, I utilized qwen3-asr-flash—a purpose-built, highly accurate ASR model that costs a fraction of a cent per second.
I wired a MediaRecorder in the Next.js frontend to send an audio blob to FastAPI, saved it to a temp file, and handed it to the Dashscope SDK. Immediately, it crashed: UploadFileException: Get upload certificate failed.
The Fix: When the Dashscope SDK sees a local file:// URI, it conveniently attempts to upload the file to an Alibaba Cloud OSS bucket before passing the URL to the model. Because my API keys were scoped strictly for inference, this storage request was denied. To solve this, I bypassed the temp file entirely. By base64-encoding the audio blob into a data URI (data:audio/webm;base64,...) on the backend, the SDK recognized the format and smartly sent the audio bytes inline within the JSON payload. Instant transcripts, zero external storage required.
TTS: Realtime Narrations
For output, I integrated WebSocket-based realtime audio using Qwen3-TTS-Flash-Realtime.
[User Log In] ──► Dashboard ──► Audio: "Welcome, Solo Han. You have 5 outreach drafts awaiting approval." (0.8x speed)
[Agent Running] ──► Prospect UI ──► Audio: "Scanning live web for buying signals..." (1.0x speed)
To comply with modern browser autoplay policies (which block AudioContext without a prior user gesture), I chained the initial WebSocket connection to the user's login button click event, ensuring audio played smoothly without browser policy errors.
Voice Cloning: The Public URL & Codec Quirks
To make video hooks truly personalized, I added voice cloning via cosyvoice-v3-plus. Sales reps record a 10-second sample in the browser, and Qwen clones it to drive their avatar's speech.
The Issue (Public URL Constraint): Unlike the STT API which accepts base64 data URIs, Dashscope's VoiceEnrollmentService strictly requires a public HTTP/HTTPS URL to fetch the reference audio.
The Fix: I leveraged the Nginx reverse proxy. The backend temporarily saves the user's audio blob to disk, serves it over a public endpoint long enough for Dashscope to fetch it and generate the voice_id (about 30 seconds), and then safely deletes it in a finally block.
The Issue (Codec Whitelists vs. Browser Standards): Browser MediaRecorder defaults to compressed audio, which can slightly degrade voice cloning acoustics. I tried to force the browser to output uncompressed audio using audio/webm;codecs=pcm. However, when passed to Dashscope, the API outright rejected the Matroska/WebM container holding the PCM data with an UnsupportedFileFormat error.
The Fix: I reverted to the browser's native default codec. It served as a stark reminder: when bridging frontend Web APIs and backend AI models, strict cloud format whitelists often force you to rely on standard browser fallbacks.
5. The Multimodal Holy Grail: Chaining a 3-Model Talking-Head Video Pipeline
With the extension of the hackathon deadline, I decided to go all-in on multimodal personalization. When a sales rep reviews and approves an email draft, I wanted them to be able to generate a personalized, talking-head video hook to attach to that email.
This required orchestrating three entirely separate Qwen/Dashscope models into an asynchronous sequential chain.
[Value Hypothesis]
│
▼
qwen-flash-character ──► Generates conversational, human-voiced script
│
▼
qwen3-tts / cosyvoice ──► Synthesizes script (using preset or cloned voice) to WAV
│
▼
wan2.7-i2v ──► Animates static avatar with audio → MP4
Getting this pipeline to produce a production-grade, natural-looking video required solving some highly specific engineering hurdles.
Problem A: Multilingual Edge Cases & The Regex Cadence Hack
Standard LLMs write text optimized for eyes, not ears. To generate a spoken script, I utilized qwen-flash-character. Because Qwen models are highly capable multilingual engines, they would occasionally inject non-ASCII characters into the English script.
When fed directly to the TTS engine, these characters would interrupt the flow. However, if I simply stripped them out programmatically, adjacent words would run together without a space.
The Solution: I wrote a Python-based regex post-processing filter that intercepts the generated script before it hits the TTS engine:
# Replace any non-ASCII character with a comma and space
script = re.sub(r'[^\x00-\x7F]+', ', ', script)
# Collapse duplicate spaces
script = re.sub(r'\s+', ' ', script).strip()
This turned a formatting edge-case into a major asset: the non-ASCII character was transformed into a clean verbal pause (comma + space) in the audio, creating a natural cadence for the speaker.
Problem B: Programmatic Lip-Sync Padding
During initial testing with wan2.7-i2v, we noticed a jarring visual artifact: the generated video's lips would instantly snap open at frame 0 and freeze-cut the moment the audio ended.
To solve this, I wrote an in-memory audio-padding routine in Python. The script takes the raw PCM/WAV audio generated by qwen3-tts-flash-realtime and programmatically prepends 0.3 seconds of pure silence to the head and appends 0.2 seconds of silence to the tail before converting it to the final base64 data URI sent to the video model. This gave the video generator the required visual buffer to animate a smooth, natural facial transition.
Problem C: The UX of Long-Running Generative Tasks
Generating a 15-second high-resolution video takes between 110 and 150 seconds. During this window, a standard UI progress bar or a countdown is highly misleading—if the API experiences latency, the bar stalls or hits 0% while the task is still running, which looks broken.
Rather than "lying" with a progress bar, I built a live-ticking elapsed timer on the frontend that counts up (0:47 / ~2:00). I styled it with the Tailwind CSS tabular-nums class, which prevents the digits from wiggling or shifting width on the screen as they tick up, maintaining a rock-solid, professional UI.
6. Deployment Architecture: Alibaba Cloud ECS (Hong Kong)
For hosting the production backend, I selected Alibaba Cloud Elastic Compute Service (ECS) (ecs.e-c1m2.large - 2 vCPU / 4 GiB RAM).
┌─────────────────────────────────────────────────────────────────────────────────┐
│ ALIBABA CLOUD ECS (China — Hong Kong, Ubuntu 22.04 LTS) │
│ │
│ Nginx (host) ──► Let's Encrypt HTTPS (idealcustomerfinder.williamcheung.buzz) │
│ │ │
│ ▼ │
│ Docker Container (supervisord) │
│ ├── Next.js 14 (:3000) │
│ ├── FastAPI (:8000) │
│ ├── Celery Workers (concurrency=2) │
│ └── Redis Broker (:6379) │
│ │
│ PostgreSQL 17 + pgvector (host process — persists across container restarts) │
└─────────────────────────────────────────────────────────────────────────────────┘
The HTTPS / Microphone Constraint
While the core backend logic technically works over plain HTTP, implementing the Speech-to-Text (STT) and voice cloning features forced a strict architectural requirement. Modern browsers enforce secure contexts (window.isSecureContext) for hardware access. While MediaRecorder and getUserMedia worked perfectly on http://localhost:8080 during local development, deploying to a raw ECS public IP completely blocked microphone access.
This browser security policy was the driving force behind fronting the Docker container with an Nginx reverse proxy and a Let's Encrypt SSL certificate directly on the ECS host.
Why China (Hong Kong)?
- Low Latency Routing: Hong Kong provides direct, low-latency fiber connections (CN2 GIA) to mainland Dashscope API endpoints.
- No ICP Filing Delay: Because Hong Kong is outside the Chinese mainland regulatory zone, public web traffic on ports 80, 443, and 8080 can be served immediately without waiting weeks for mainland ICP license approvals.
To ensure data persistence across Docker container updates, PostgreSQL 17 and pgvector 0.8.0 were installed directly on the ECS host, while application services (Next.js, FastAPI, Celery, Redis) run inside a single supervisord-managed Docker container.
Key Takeaways: What I Learned Building This
-
Don't blindly trust default MCP schemas. When integrating external tools, reverse-engineer their parameters. Forcing explicit constraints (like
search_depth="lite") slashed my token consumption by 57%. -
Build fallbacks for parallel Agent tooling. High-concurrency agent workflows require resilient architecture. Designing a
BaseConnectorpattern that can gracefully degrade from MCP to direct REST calls is mandatory for production reliability. -
Beware of silent shell evaluation traps. A standard
xargscommand for loading.envfiles inside Docker silently corrupted API keys containing special characters. Using shell-native variable exporting (set -a) saved the vector embedding pipeline. - Keep multimodal payload handling stateless. Utilizing base64 data URIs for audio streams avoids the need for temporary cloud storage, keeping containerized workloads stateless and highly performant.
- Programmatically manage multimodal text. LLMs trained on multilingual data will occasionally output non-standard characters. Post-processing filters (like regex non-ASCII replacement) can ensure TTS engines maintain a natural cadence.
- Acknowledge the physical nuances of multimedia. Aligning audio with synthetic video requires understanding human motion. Pre-padding audio with programmatic silence is the difference between a jarring lip-sync cut and a professional video transition.
- Frontend enhancements can break backend whitelists. Attempting to force uncompressed PCM audio recording in Chrome for better voice cloning crashed the Dashscope API, which strictly rejects Matroska/WebM audio containers. Sometimes, you have to accept standard browser defaults to maintain API compatibility.
-
Be honest with your UI. For long-running asynchronous API tasks, don't fake progress with stall-prone bars. A live elapsed timer styled with
tabular-numsis much more trustworthy. - Hardware APIs dictate infrastructure. Browser policies for Speech-to-Text APIs strictly require secure contexts. Fronting containers with a reverse proxy (Nginx) and SSL isn't just a security best practice—it's a functional requirement for modern multimodal apps.
- Cloud region selection is an architectural feature. Deploying to Alibaba Cloud's Hong Kong region provided the perfect balance: ultra-low latency to Dashscope's mainland endpoints, while bypassing mainland deployment regulatory delays.
- Decouple state from container lifecycles. Running PostgreSQL directly on the VM host while containerizing the application logic provided the agility of Docker deployments without risking data persistence on restarts.
Building Ideal Customer Finder demonstrated that Qwen Cloud offers the reasoning performance, embedding capabilities, audio APIs, and advanced video generation models needed to power production-grade autonomous systems.
Top comments (0)