client-finder/references/query-expansion-spec.md

3.0 KiB

Client-Finder Query Expansion Spec (MVP)

Goal

Implement query expansion inside client-finder before calling /ecom/cold-outreach/run-flow.

Scope

  • Only changes inside agent-sandbox/agent_app/skills/client-finder.
  • No new backend API.
  • Expansion failure should fallback to original raw query A.

Input

  • client_key (required, from workflow input_data)
  • query (required)
  • country (optional, default us)
  • QUERY_EXPANSION_JSON (optional, must be valid JSON if provided)

Expansion Flow

  1. Normalize raw query.
  • trim spaces
  • remove optional prefix cold-outreach:
  1. Resolve expansion source.
  • If QUERY_EXPANSION_JSON is provided:
    • accept object or array
    • must produce non-empty expandedQueries + non-empty primaryQuery
    • source = llm
  • Else use built-in rule expansion
    • source = rule
  1. Validate expansion.
  • expandedQueries.length >= 1
  • primaryQuery non-empty
  • dedupe candidates case-insensitively
  1. Execute workflow with primaryQuery.
  • exchange client_key first via POST /auth/skill-credit/session
  • POST /auth/skill-credit/session
  • POST /ecom/cold-outreach/run-flow
  • return accepted immediately after workflowId is received

Fallback Policy

  • If expansion parsing/validation fails, use normalized raw query A as primaryQuery.
  • Set:
    • expansionStatus = "failed"
    • expansionSource = "raw_query"
    • usedFallbackQuery = true
    • expansionError with failure reason
  • Continue to run cold-outreach.

Output Contract

Always output strict JSON following ../output_schema.json.

Success example:

{
  "status": "success",
  "error": null,
  "inputQuery": "coffee",
  "expandedQueries": ["coffee shop US", "coffee roastery US"],
  "primaryQuery": "coffee shop US",
  "expansionStatus": "success",
  "expansionSource": "llm",
  "expansionError": null,
  "usedFallbackQuery": false,
  "runId": "",
  "workflowId": "outreach_xxx",
  "workflowStatus": "accepted",
  "businessesCount": 0,
  "contactsCount": 0,
  "uniqueContactDomains": 0,
  "billingReserveStatus": "SKIPPED",
  "billingFinalizeStatus": "SKIPPED"
}

Expansion fallback example:

{
  "status": "success",
  "error": null,
  "inputQuery": "coffee",
  "expandedQueries": ["coffee"],
  "primaryQuery": "coffee",
  "expansionStatus": "failed",
  "expansionSource": "raw_query",
  "expansionError": "query expansion failed: expandedQueries is empty; fallback to raw query",
  "usedFallbackQuery": true,
  "runId": "",
  "workflowId": "outreach_xxx",
  "workflowStatus": "accepted",
  "businessesCount": 0,
  "contactsCount": 0,
  "uniqueContactDomains": 0,
  "billingReserveStatus": "SKIPPED",
  "billingFinalizeStatus": "SKIPPED"
}

Acceptance

  1. Expansion success -> call cold-outreach using primaryQuery (webhook is key-bound).
  2. Expansion failure -> fallback to raw query and continue run when raw query is available.
  3. If normalized raw query is empty, return failed JSON and exit non-zero.
  4. On start success, return immediate workflowStatus = "accepted" (no local polling).
  5. No prose output mixed with JSON.