105 lines
3.0 KiB
Markdown
105 lines
3.0 KiB
Markdown
|
|
# 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:`
|
||
|
|
|
||
|
|
2. 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`
|
||
|
|
|
||
|
|
3. Validate expansion.
|
||
|
|
- `expandedQueries.length >= 1`
|
||
|
|
- `primaryQuery` non-empty
|
||
|
|
- dedupe candidates case-insensitively
|
||
|
|
|
||
|
|
4. 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:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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.
|