Skip to content
Tech Blog
Go back

From Rule Explosion to Runtime Discovery: Building a Universal Cloud Agent in One Day

Edit page

Author: Robin
Date: February 25, 2026
Project: hc-agent + OpenClaw Integration


The Problem: The 600-Rule Nightmare

When we first designed hc-agent, we made the classic mistake: hardcoding every possible operation.

# The traditional approach
if service == "ecs" and action == "create":
    if region == "la-south-2":
        params = validate_ecs_create_params(...)
    elif region == "sa-east-1":
        params = validate_ecs_create_params_brazil(...)
    ...

The math doesn’t work:

Even worse:

We needed a fundamentally different approach.


The Insight: Let the SDK Tell Us What It Can Do

Instead of hardcoding knowledge, what if we discover it at runtime?

Traditional Approach (❌ Doesn’t Scale)

Developer codes → 600 if-else branches → Ship to production
↓ (3 months later)
API changes → All rules break → 3 months to fix

Runtime Discovery (✅ Adapts Automatically)

SDK installed → Runtime introspection → Discover all methods → LLM plans execution
↓ (3 months later)
API changes → SDK updated → Automatic discovery → No code changes

The Architecture: 7-Step Universal Flow

We redesigned hc-agent as a unified 7-step state machine powered by OpenClaw:

Step 1: Intent Recognition
  ↓ "Create an ECS instance in Santiago"
Step 2: Context Discovery (SDK introspection)
  ↓ Query: list_flavors(), list_images(), list_vpcs()
Step 3: Dynamic Parameter Collection
  ↓ Collect: name, flavor, image, vpc, subnet
Step 4: Smart Recommendations
  ↓ LLM suggests optimal configuration
Step 5: Preflight Safety Check
  ↓ Preview payload + impact analysis
Step 6: Execution
  ↓ Call SDK: client.create_server(...)
Step 7: Error Recovery
  ↓ If failed: Analyze → Fix → Retry

Key Innovation: Each step is data-driven, not code-driven.


How It Works: Runtime SDK Discovery

1. Automatic SDK Introspection

On startup, hc-agent scans installed Huawei Cloud SDK packages:

// Discover available methods at runtime
const catalog = discoverSDKMethods([
  '@huaweicloud/huaweicloud-sdk-ecs',
  '@huaweicloud/huaweicloud-sdk-vpc',
  '@huaweicloud/huaweicloud-sdk-rds',
  // ... 200+ packages
]);

// Result:
// {
//   "ListFlavors": { package: "ecs", client: "EcsClient", type: "list" },
//   "CreateServers": { package: "ecs", client: "EcsClient", type: "create" },
//   ...
// }

Advantages:

2. LLM-Driven Planning

Instead of if-else trees, we let the LLM plan operations:

// Step 2: LLM generates inventory query plan
const plan = await llm.plan({
  intent: "Create ECS instance in Santiago",
  availableMethods: catalog, // from SDK discovery
  context: { region: "la-south-2" }
});

// LLM output (structured JSON):
// {
//   "required_queries": [
//     { method: "ListFlavors", purpose: "sizing options" },
//     { method: "ListImages", purpose: "OS selection" },
//     { method: "ListVpcs", purpose: "network context" }
//   ],
//   "execution_outline": "1. Query inventory → 2. Collect params → 3. Create server"
// }

Why this works:

3. Dynamic Capability Cards

Service-specific logic lives in declarative YAML files, not code:

# capabilities/ecs.yaml
service: ECS
actions:
  create:
    required_slots:
      - name: { type: string, regex: "^[a-z][a-z0-9-]{0,62}$" }
      - flavor: { type: string, source: inventory, method: ListFlavors }
      - image: { type: string, source: inventory, method: ListImages }
      - vpc_id: { type: string, source: inventory, method: ListVpcs }
      - subnet_id: { type: string, source: inventory, method: ListSubnets }
    
    inventory_queries:
      - ListFlavors: { cache_ttl: 300 }
      - ListImages: { cache_ttl: 3600 }

Adding new services:

  1. Install SDK package (npm install @huaweicloud/huaweicloud-sdk-xxx)
  2. Create capability card (15 lines of YAML)
  3. Done. No code changes.

The MVP: ECS Creation in One Day

Challenge: Implement a working ECS instance creation flow from scratch.

Timeline:

Result: Fully functional ECS creation with:

Conversation Example:

User: Create a VM in Santiago for web hosting
Agent: [Step 1] Understood: ECS instance creation, region la-south-2
Agent: [Step 2] Discovered 87 flavors, 45 images, 3 VPCs
Agent: [Step 3] What should I name this instance?
User: web-prod-01
Agent: [Step 4] Recommended: c6.large.2 (2vCPU, 4GB) + Ubuntu 22.04
       Reason: Good balance for web workloads, cost-effective
User: Sounds good
Agent: [Step 5] Preflight Check:
       - Instance: web-prod-01
       - Flavor: c6.large.2 (¥0.15/hour)
       - Image: Ubuntu 22.04
       - Network: vpc-prod (10.0.0.0/16)
       Confirm? (yes/no)
User: yes
Agent: [Step 6] Creating... ✓ Instance created (ID: abc123)
Agent: [Step 7] Public IP: 176.52.134.61, SSH ready

The Key Differences

Traditional Hardcoded Approach

# Each service needs custom code
def create_ecs(name, flavor, image, vpc):
    # 200 lines of validation
    # 100 lines of parameter mapping
    # 50 lines of error handling
    ...

def create_rds(name, engine, version, ...):
    # Another 350 lines
    ...

# 200 services × 350 lines = 70,000 LOC

Problems:

Runtime Discovery Approach

// Universal flow for ALL services
async function executeCloudOperation(intent: string) {
  const catalog = await discoverSDK();
  const plan = await llm.plan(intent, catalog);
  const slots = await collectParameters(plan);
  const config = await llm.recommend(slots);
  const preflight = await validateSafety(config);
  if (await confirm(preflight)) {
    return await execute(config);
  }
}

// Works for ECS, RDS, VPC, CCE... (200+ services)
// Total code: ~2,000 LOC (35x reduction)

Advantages:


Real-World Impact: From 3 Months to 30 Minutes

Before (Hardcoded Approach):

After (Runtime Discovery):

Why it works:

  1. SDK introspection discovers all CCE methods automatically
  2. LLM planner generates execution logic on-demand
  3. Capability card declares parameters (no coding needed)
  4. Unified flow handles safety/retry/recovery

Architecture Principles: What We Got Right

1. LLM-First Planning

Instead of hardcoding decision trees, let LLM plan dynamically:

// ❌ Traditional
if (service === "ecs" && action === "create") {
  if (hasVpc()) { askSubnet(); }
  if (hasKeyPair()) { askKeyPair(); }
  ...
}

// ✅ LLM-First
const plan = await llm.plan("Create ECS", { discovered_methods, context });
// LLM figures out: "Need VPC first, then subnet, then create"

2. Safety Gates (Non-Negotiable)

Mutating operations always require explicit confirmation:

Step 5: Preflight
  ├─ Preview: What will be created/deleted
  ├─ Impact: Cost estimate, dependencies affected
  ├─ Risks: What could go wrong
  └─ Confirm: Explicit yes/no (never auto-proceed)

3. Stay-in-Flow Error Recovery

Errors don’t kill the session—they teach it:

[Execution fails: "Invalid flavor for region"]

[Step 7: Analyze error]
  - Error type: parameter_recoverable
  - Fix: Return to Step 3 with corrected flavor list

[User corrects flavor selection]

[Retry execution] ✓ Success

4. Bounded Learning (Phase 1)

Agents learn from success, not failure:

// On success
await memory.record({
  intent: "Create ECS for web hosting",
  slots: { name, flavor, image, vpc },
  result: { instance_id, public_ip },
  duration: "45s"
});

// On failure
await diagnostics.record({
  error: "DBS.280285: Invalid AZ for HA flavor",
  context: { ... }
});
// Note: Failures stored for debugging, not auto-promoted

Lessons Learned

✅ What Worked

  1. Runtime Discovery: No more hardcoded service lists
  2. LLM Planning: Adapts to new services automatically
  3. Capability Cards: Declarative > imperative for service logic
  4. 7-Step Flow: Universal pattern works for all operations
  5. One Day MVP: Proof that the architecture scales

⚠️ What Was Hard

  1. SDK Stability: Not all Huawei Cloud SDKs have stable TypeScript types
  2. LLM Consistency: JSON schema validation critical (LLM sometimes deviates)
  3. Error Classification: Hard to distinguish “fatal” from “recoverable” errors
  4. Cache Invalidation: 5-minute TTL is a guess (needs tuning per service)

🔄 What We’d Change

  1. Phase 2: Failure Learning - Currently manual; should auto-suggest fixes
  2. Step 3 Optimization - Bundle input (one-shot name+sizing) reduces turns
  3. Multi-Cloud: Architecture works for AWS/Azure, but needs separate capability cards
  4. Cost Estimation: Should show real-time pricing before execution


The Most Important Lesson: Get the Plan Model Right

After building dozens of LLM-powered features, I learned a hard truth: Spending 2 hours on a clear plan model saves 20 hours of debugging broken code.

What is a “Plan Model”?

A plan model is the structured contract between you and the LLM. It defines:

The Rule: If you can’t write 3 clear examples, your plan model is wrong.


Example 1: The Wrong Way (Vague Instructions)

Initial prompt (❌ Too vague):

"Generate a plan to create cloud resources based on user intent."

LLM output:

{
  "plan": "Create a server",
  "steps": ["setup", "configure", "deploy"]
}

Problem:

Result: 3 days debugging why execution fails randomly.


Example 2: The Right Way (Concrete Plan Model)

Step 1: Define the scenario clearly

Scenario: User says "Create an ECS instance for web hosting in Chile"

Expected outcome:
1. Discover available flavors/images in la-south-2
2. Recommend suitable configuration (2 vCPU, 4GB RAM)
3. Collect missing parameters (name, VPC, subnet)
4. Generate executable API payload

Step 2: Design the plan model with examples

// Plan Model Schema
interface InventoryPlan {
  intent: {
    action: "create" | "delete" | "update" | "list";
    service: string;      // e.g., "ECS"
    region: string;       // e.g., "la-south-2"
  };
  required_queries: Array<{
    method: string;       // e.g., "ListFlavors"
    purpose: string;      // e.g., "Get available instance types"
    cache_ttl?: number;   // Optional: 300 (seconds)
  }>;
  execution_outline: string[];
}

Step 3: Provide 3 real examples

Example A: Create ECS

// Input
{
  "user_message": "Create a VM for web hosting in Chile",
  "discovered_methods": ["ListFlavors", "ListImages", "CreateServers"]
}

// Expected Output
{
  "intent": {
    "action": "create",
    "service": "ECS",
    "region": "la-south-2"
  },
  "required_queries": [
    { "method": "ListFlavors", "purpose": "Get sizing options" },
    { "method": "ListImages", "purpose": "Get OS choices" }
  ],
  "execution_outline": [
    "Query available flavors and images",
    "Recommend c6.large.2 + Ubuntu 22.04",
    "Collect VPC/subnet information",
    "Create server via CreateServers API"
  ]
}

Example B: Delete RDS

// Input
{
  "user_message": "Delete the MySQL database in Brazil",
  "discovered_methods": ["ListInstances", "DeleteInstance"]
}

// Expected Output
{
  "intent": {
    "action": "delete",
    "service": "RDS",
    "region": "sa-east-1"
  },
  "required_queries": [
    { "method": "ListInstances", "purpose": "Find target database" }
  ],
  "execution_outline": [
    "Query all RDS instances in sa-east-1",
    "Identify MySQL instance by name/tags",
    "Show impact analysis (data loss warning)",
    "Require explicit confirmation",
    "Execute DeleteInstance API"
  ]
}

Example C: List VPCs

// Input
{
  "user_message": "Show me all VPCs in Santiago",
  "discovered_methods": ["ListVpcs"]
}

// Expected Output
{
  "intent": {
    "action": "list",
    "service": "VPC",
    "region": "la-south-2"
  },
  "required_queries": [
    { "method": "ListVpcs", "purpose": "Retrieve all VPCs" }
  ],
  "execution_outline": [
    "Query ListVpcs API",
    "Format as table (name, CIDR, status)",
    "Return results"
  ]
}

Step 4: Test with real LLM calls

// Validate LLM output against schema
const result = await llm.plan(userInput, discoveredMethods);

// Schema validation (TypeScript)
const validated = InventoryPlanSchema.parse(result);
// If parsing fails → LLM output is wrong → fix prompt or examples

Key Insight: Ask Until You Understand

When LLM uses confusing technical terms, don’t guess—keep asking.

Real conversation example:

Me: What does "reconcile state" mean in your execution outline?

LLM: It means comparing the desired configuration with the actual cloud 
     resources and identifying differences.

Me: Give me a concrete example with ECS instances.

LLM: Example:
     Desired: { name: "web-01", flavor: "c6.large.2", count: 2 }
     Actual: { 
       "web-01-instance-1": { flavor: "c6.large.2", status: "ACTIVE" },
       "web-01-instance-2": { flavor: "c6.small.1", status: "ACTIVE" }
     }
     Reconciliation: Instance 2 has wrong flavor → Resize needed

Me: Perfect! Now I understand. Can you update the execution outline 
    to say "Compare desired vs actual instance configurations" instead 
    of "reconcile state"?

LLM: [Updates outline with clear language]

Why this matters:


Design First, Code Second

The 2-Hour Rule:

Before writing ANY code, spend 2 hours on:

  1. Write the scenario (what the user wants to do)
  2. Design the plan model (input/output structure)
  3. Create 3 examples (real inputs → expected outputs)
  4. Ask LLM to explain (any confusing terms)
  5. Test with real LLM (validate output matches schema)

What happens if you skip this?

❌ No plan model:
Day 1: Write code → LLM output is garbage → Debug
Day 2: Fix prompt → Different format → More bugs
Day 3: Rewrite everything → Still broken
Day 4: Discover root cause (vague schema)
Day 5: Start over with proper plan model
Total: 5 days

✅ With plan model:
Hour 1: Design plan model + 3 examples
Hour 2: Test with LLM → Fix schema once
Hour 3: Write code that implements schema
Hour 4: Code works first try
Total: 4 hours

30x faster. Same result.


The “3 Examples Rule”

If you can’t write 3 diverse examples, your plan model is incomplete:

❌ Incomplete:

Example 1: Create ECS (success case)

✅ Complete:

Example 1: Create ECS (basic success case)
Example 2: Create ECS with custom VPC (parameters vary)
Example 3: Create CCE cluster (different service, same pattern)

Why 3?


Real Impact: hc-agent Planner

Before plan model (Week 1):

After plan model (Week 2):

Code change:

- const plan = await llm.plan("Create VM in Chile");
+ const plan = InventoryPlanSchema.parse(
+   await llm.plan(userInput, {
+     examples: PLAN_EXAMPLES,  // 5 real examples
+     schema: InventoryPlanSchema
+   })
+ );

Time saved: 12 hours/week on debugging.


Checklist: Is Your Plan Model Ready?

Before writing code, ask yourself:

If any answer is “no”, spend 30 more minutes on the plan model.

Trust me: 30 minutes now saves 3 days later.

The Numbers: Before vs After

MetricHardcoded (Before)Runtime Discovery (After)
Code per Service350 lines0 lines
Config per Service045 lines YAML
Time to Add Service3 months30 minutes
Developers Needed121
Adaptation to API ChangesManual (weeks)Automatic (instant)
Total Maintenance LOC70,000+~2,000
Test Coverage60%95% (unified flow)

Open Questions for the Community

  1. Multi-Cloud Portability: How do we unify AWS/Azure/GCP capability cards?
  2. Cross-Service Dependencies: How to handle “Create VPC → Create Subnet → Create VM” chains?
  3. Cost Optimization: Should agents auto-suggest cheaper alternatives?
  4. Security Boundaries: How granular should preflight impact analysis be?

Conclusion: The Future is Runtime-Driven

We proved that runtime discovery beats hardcoded logic for cloud automation:

The key insight: Modern cloud SDKs are rich enough to describe themselves. We just needed to listen.


Try It Yourself

hc-agent is designed to be open-sourced. We’re finalizing:

Repo: github.com/huaweicloud/hc-agent (coming soon)

OpenClaw Integration: github.com/openclaw/openclaw


Appendix: Technical Architecture

System Components

┌─────────────────────────────────────────────┐
│           OpenClaw Agent (LLM)              │
│  - Intent recognition                       │
│  - Planning & reasoning                     │
│  - Error analysis                           │
└─────────────────┬───────────────────────────┘
                  │ JSON Bridge
┌─────────────────┴───────────────────────────┐
│         hc-agent Execution Engine           │
│  ┌─────────────────────────────────────┐   │
│  │ Step 1: Intent Recognition          │   │
│  └──────────────┬──────────────────────┘   │
│  ┌──────────────┴──────────────────────┐   │
│  │ Step 2: SDK Runtime Introspection   │   │
│  │  - Discover methods                 │   │
│  │  - Execute inventory queries        │   │
│  └──────────────┬──────────────────────┘   │
│  ┌──────────────┴──────────────────────┐   │
│  │ Step 3: Dynamic Parameter Collection│   │
│  │  - Load capability card             │   │
│  │  - Fetch slot candidates            │   │
│  └──────────────┬──────────────────────┘   │
│  ┌──────────────┴──────────────────────┐   │
│  │ Step 4: Smart Recommendations       │   │
│  │  - LLM suggests optimal config      │   │
│  └──────────────┬──────────────────────┘   │
│  ┌──────────────┴──────────────────────┐   │
│  │ Step 5: Preflight Safety Check      │   │
│  │  - Validate payload                 │   │
│  │  - Estimate impact                  │   │
│  │  - Require confirm                  │   │
│  └──────────────┬──────────────────────┘   │
│  ┌──────────────┴──────────────────────┐   │
│  │ Step 6: Execution                   │   │
│  │  - Call SDK methods                 │   │
│  └──────────────┬──────────────────────┘   │
│  ┌──────────────┴──────────────────────┐   │
│  │ Step 7: Error Recovery              │   │
│  │  - Classify errors                  │   │
│  │  - Bounded retry                    │   │
│  │  - Stay in flow                     │   │
│  └─────────────────────────────────────┘   │
└─────────────────────────────────────────────┘

┌─────────────────┴───────────────────────────┐
│        Huawei Cloud SDK (200+ packages)     │
│  - @huaweicloud/huaweicloud-sdk-ecs         │
│  - @huaweicloud/huaweicloud-sdk-vpc         │
│  - @huaweicloud/huaweicloud-sdk-rds         │
│  - ...                                      │
└─────────────────────────────────────────────┘

Key Technologies

OpenClaw Core:

hc-agent Engine:

Huawei Cloud SDK:


Questions? Feedback?


Published: February 25, 2026
Last updated: February 25, 2026


Edit page
Share this post on:

Previous Post
Chilean Spanish ASR Model Benchmark Report: Comprehensive Evaluation of Open-Source Speech Recognition Models
Next Post
Building hc-agent: Lessons from Rapid Prototyping to Production