人机协作台技能:用户自然语言销售指令由AI解析任务参数,调用deepsop接口提交并查询结果后推送。触发场景包括「帮我找客户」「挖掘XXX行业客户」「找XXX个客户」「提交任务」等客户挖掘/销售指令;以及「创建电话场景」「新建外呼话术」「新建电话机器人」「提交场景」等。
人机协作台(Human-AI Collaboration)是一项面向实际任务的技能,主要用于人机协作台是基于 deepsop 平台的智能销售任务助手,能够:;理解自然语言指令 :直接描述需求,如「帮我找50个美国做服装的客户」;
Frank :邮件销售;Fran :。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。
实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
人机协作台是基于 deepsop 平台的智能销售任务助手,能够:
本技能需要 API Key 授权才能调用 DeepSOP 接口。请按以下步骤获取:
sk- 开头的密钥DEEPSOP_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxx
所有 API 请求头需携带:
x-api-key: $DEEPSOP_API_KEYAPI Base URL:https://ai.deepsop.com/prod-api/
🔒 核心规则: 调用任何接口时,必须严格使用本文档对应步骤标注的完整 URL,不得做任何形式的改写、简化、猜测或自创。
请求前自检流程(每一次 HTTP 请求都必须执行):
- 在下方「API 路径权威清单」中找到对应步骤的接口;
- 将即将发出的完整 URL(含 host、path、query key 名与顺序、
pageNum/pageSize/platform等参数值)与清单中的Path列逐字符比对;- 完全一致才允许发出请求;任何偏差立即停止,按清单中的路径修正后再重试;
- 若某接口未在清单中列出 → 绝对禁止自行编造路径,必须先向用户确认。
禁止行为:
- ❌ 把
prod-api改成api/v1/prod/prodApi- ❌ 把 camelCase 改成 snake_case 或全小写(如
presetEmployee≠preset_employee≠presetemployee)- ❌ 把
outBound写成outbound、emailconfig写成emailConfig、authaccount写成authAccount(这三个偏偏就是全小写,特别注意)- ❌ 用同义词替换路径段(
getCustomerPoolDetail≠customerPoolDetail/getCustomerDetail;collaborationCallResult≠callResult)- ❌ 漏写或私自补加 query 参数(如漏
platform=1/status=1,或私自加pageSize=20改成pageSize=10)- ❌ 凭"上一次调用记得"或"经验"猜测路径,不回到本文档对照
路径错误是最常见、最可避免、影响最大的事故,必须零容忍。
https://ai.deepsop.com/prod-api)| # | 步骤 | 方法 | Path(不含 Base URL) |
|---|---|---|---|
| 1 | Step 1.5 数字员工可用性 | GET |
/ai/presetEmployee/list |
| 1.1 | Step 1.5.1① 签约套餐列表 | GET |
/ai/setting/list?packageType=3 |
| 1.2 | Step 1.5.1② 人民币→K币汇率 | GET |
/system/config/configKey/CNY_TO_KCOIN |
| 1.3 | Step 1.5.1③ K币余额查询 | GET |
/ai/vip/balance?userId={userId} |
| 1.4 | Step 1.5.1④ 提交签约(扣K币) | POST |
/ai/order/purchaseIndependentPackageByKToken |
| 2 | Step 3 提交任务 | POST |
/ai/presetEmployee/submitTask |
| 3 | Step 3 前置 A-0 外呼实例 | GET |
/ai/outBound/describeInstance |
| 4 | Step 3 前置 A-1 号码池 | GET |
/ai/outBound/callerNumber/list |
| 5 | Step 3 前置 A-2 场景库 | POST |
/ai/outBound/listScripts |
| 5.1 | Step 1.7 / 前置 A-2 兜底 创建+审核场景 | POST |
/ai/outBound/createOrModifyScriptAndSubmitScriptReview |
| 5.2 | Step 1.7 轮询场景审核状态 | POST |
/ai/outBound/describeScript |
| 5.3 | Step 1.7 修改场景时回填机器人设定 | POST |
/ai/outBound/getAgentProfile |
| 5.4 | Step 1.7 辅 单独重新提交场景审核 | POST |
/ai/outBound/submitScriptReview |
| 5.5 | Step 1.7 辅 撤销场景审核 | POST |
/ai/outBound/withdrawScriptReview |
| 6 | Step 3 前置 B0 邮箱绑定检查 | GET |
/ai/emailconfig/list?pageSize=1000&pageNum=1&status=1 |
| 7 | Step 3 前置 B 用户 Profile | GET |
/ai/user/profile |
| 8 | Step 3 前置 D-1 短信模板列表 | GET |
/ai/sms/querySmsTemplateList?pageNum=1&pageSize=20&pageNumber=1 |
| 12 | Step 5-A AiWa 客户池详情 | POST |
/ai/presetEmployee/getCustomerPoolDetail?pageNum=1&pageSize=10 |
| 13 | Step 5-B-1 Frank 邮件统计 | GET |
/ai/email/getTaskEmailCount?taskId={frankDagTaskId} |
| 14 | Step 5-B-2 Frank 邮件列表 | GET |
/ai/email/taskList?pageNum=1&pageSize=2000&taskId={frankDagTaskId} |
| 15 | Step 5-C-1 Fran 电话统计 | GET |
/ai/presetEmployee/collaborationTaskStatistics?taskId={franDagTaskId}&customerPoolId={franCustomerPoolId} |
| 16 | Step 5-C-2 Fran 电话详情 | POST |
/ai/presetEmployee/collaborationCallResult?pageNum=1&pageSize=10 |
| 17 | Step 5-D-1 Lisa 短信统计 | POST |
/ai/sms/getTaskSmsCount |
| 18 | Step 5-D-2 Lisa 短信详情 | POST |
/ai/sms/getSmsResultList?pageNum=1&pageSize=10 |
| 21 | Step 1.6 途径B 客户搜索(无 AiWa 销售任务) | POST |
/ai/customer/customerList?pageNum={pageNum}&pageSize=10 |
| 22 | Step 1.6 途径A xlsx 上传到 OSS | POST |
/system/fileUpload/upload(multipart/form-data, 字段名 file) |
| 23 | Step 1.6 途径A 下载「公司导入」模板 | POST |
/ai/customer/template(application/x-www-form-urlencoded, 空 body, 响应 blob) |
| 24 | Step 1.6 途径A 下载「地址簿导入」模板 | POST |
/ai/customer/contactImportTemplate(application/x-www-form-urlencoded, 空 body, 响应 blob) |
🔁 本清单与下文各 Step 中"接口:"标注的路径完全一致。如发现两处不一致,以下文 Step 中的标注为准(本清单仅为快速比对索引),同时立即向用户报告该不一致以便修正。
上述清单同时存在于
scripts/api_paths.py,构成"双轨强约束":
- LLM 直接发请求时(curl/工具调用): 必须对照本文档清单逐字符比对路径。
- 脚本调用 API 时(如
submit_task.py): 严禁脚本内硬编码 URL,必须从api_paths.py通过build_url("xxx")获取,并配合assert_url_matches()自检。- 新增/修改接口的强制流程: 先改 SKILL.md 这张表 → 再改
api_paths.py→ 然后才能在脚本里使用;遗漏任一步即视为 bug。- 漂移检测: 运行
python scripts/api_paths.py会扫描本文件中所有https://ai.deepsop.com/prod-api/...路径,若有未在api_paths.py登记的项目则非零退出。该命令应在每次修改路径后执行一次自检。
检查当前输入内容是否包含 [DeepSOP-AutoQuery] 标记:
taskId、aiwaDagTaskId、aiwaCustomerPoolId、frankDagTaskId、franDagTaskId、franCustomerPoolId、lisaDagTaskId、lisaCustomerPoolId、taskName、totalTarget、employeeList、feishuChatId),跳过 Step 1~4 直接执行 Step 5 的全部内容(查询接口 → 生成 xlsx → 发送文件 → 回复文字摘要),直到所有参与员工的结果都处理完毕。🔀 意图分流前置(在跑下方拆解 prompt 之前先判断):
如果用户输入明确包含以下关键词之一且不带销售目标数量(如「找 N 个客户」「发邮件」「打电话推销」),判定为「场景创建意图」:
- 「创建电话场景」「新建外呼话术」「新建电话机器人」「新建场景」「做一个外呼场景」
- 「场景审核」「提交场景审核」「话术审核」「让阿里云审一下」
- 「修改场景 / 修改话术 / 改一下场景库」(带或不带 scriptId)
- 「撤销场景审核 / 撤回场景审核 / 取消场景审核」 → 走 Step 1.7.6 辅助操作 中的
withdrawScriptReview分支- 「重新提交场景审核 / 重审已有场景」(用户明确说明是已有 scriptId、不要改内容) → 走 Step 1.7.6 辅助操作 中的
submitScriptReview分支命中此意图 → 跳过下方 Step 1 任务拆解 prompt + Step 1.5/1.6/2/3/3.5/4/5 的销售流程,直接进入 Step 1.7:电话场景创建/审核子流程。
若用户句子里同时有销售意图(如「先建一个外呼场景,再用它给我打 50 个家纺客户的电话」),按以下顺序处理:
- 先走 Step 1.7 完成场景创建+审核,拿到
scriptId / agentProfileId;- 再回到下方 Step 1 拆解销售意图,进入 Step 1.5/1.6/2/3 流程;
- 进入 Step 3 前置 A-2 时,把 Step 1.7 拿到的
scriptId / agentProfileId作为已选场景直接复用,不要再调listScripts让用户选。未命中场景创建关键词 → 进入下方常规销售任务拆解。
用以下 prompt 分析用户指令,严格返回 JSON,不含任何额外文字:
根据【指令】描述,Json格式返回数据
不需要多余的描述,不要过度解读,没有提及的内容请不要擅自理解,识别结果除了Json数据其他文字不要出现
规则如下:{
"taskName": "根据描述总结出一个简洁的任务名称"
"executionMode": "判断描述中是否明确提及每日/每天/周期性,如果提及则返回周期性任务,未提及则返回定额任务"
"totalTarget": "提取描述中提及的数量(无单位纯数字)"
"employeeList": "首先将描述按逗号、顿号等分隔符拆分成多个子任务,然后为每个子任务匹配对应员工:
- 挖掘客户职能(AiWa):匹配任何包含“找”、“开发”、“行业”、“客户”等与客户挖掘相关的描述,以及没有明确匹配其他职能的单子任务
- 邮件销售职能(Frank):匹配包含“邮件”、“发邮件”等关键词的描述
- 电话销售职能(Fran):匹配包含“电话”、“打电话”、“电话销售”等关键词的描述
- 短信销售职能(Lisa):匹配包含“短信”、“发短信”等关键词的描述
- 生产视频职能(Jack):匹配包含“视频”、“生产视频”等关键词的描述
- 智能SEO优化职能(Sophia):匹配包含“SEO”、“优化”、“搜索引擎”等关键词的描述
- AI剪辑师职能(Alex):匹配包含“剪辑”、“视频剪辑”等关键词的描述
- 独立站客服职能(Leo):匹配包含“客服”、“客户服务”、“咨询”等关键词的描述
如果拆分后只有一个子任务且没有匹配上员工,则默认匹配挖掘客户职能(AiWa)
最后汇总所有匹配到的员工名称组成一个,拼接的字符串并返回(去重)",
"language": "判断描述中是否明确提及国家或地区,若提及了国家或地区但和中国没有关联则返回'英文'其他情况返回'中文'",
}
解析结果字段(注意:这些只是 SKILL 内部用的解析变量,不要原样塞到最终 API 请求体里):
totalTarget:目标数量(数字)— 仅作为 employeeParams.AiWa.totalTarget 的值来源,不得作为根级字段employeeList:参与员工逗号字符串,如 "AiWa" 或 "AiWa,Frank" — 仅本 SKILL 内部用于决定要构造哪些 employeeParams.{Name} 子对象,绝不允许出现在最终请求体的任何层级language:"中文" 或 "英文" — 仅作为 employeeParams.Frank.language 的值,不得挂到根级或其他员工子对象taskName:任务名称(→ collaborationSubmitTaskParam.taskName)executionMode:中文字符串 "定额任务" 或 "周期性任务" — 这是 LLM 返回的内部变量,提交请求体时必须转换为数字:"周期性任务" = 0,"定额任务" = 1executionMode 一律硬编码为数字 1(即按定额任务下达)。"executionMode": "定额任务"),会被后端 schema 校验拒绝;也不得写成 "1"(带引号字符串)、true、null。员工组合校验:
不支持的员工拦截:当 employeeList 包含 Jack、Leo、Sophia、Alex 中的任意一个时,终止任务,回复:
⚠️ 数字员工「{员工名}」尚未接入人机协作台,当前支持的员工为:AiWa、Frank、Fran、Lisa。请调整指令后重试。
销售员工无 AiWa 协同时必须指定客户来源:当 employeeList 包含 Frank、Fran、Lisa 中的任意一个或多个,且不包含 AiWa 时,不再终止任务;改为进入「Step 1.6 客户来源选择」流程,由用户通过「上传 xlsx 文件」或「搜索选择公司」两种方式指定要执行销售动作的客户来源,待客户来源确认完成(fileList / addressFileList / suppurIds 三者中至少一个非空)后再继续后续步骤。
⚠️ 未指定任何客户来源,无法执行销售动作。请上传客户 / 地址簿 xlsx 文件,或搜索并选择目标公司后重试。 (Frank / Fran / Lisa 三位销售员工均支持单独下任务,无需强制搭配 AiWa;如果你本来就想顺带做客户挖掘,也可以在指令里追加「找 N 个 XX 客户」让 AiWa 协同。)
接口: GET https://ai.deepsop.com/prod-api/ai/presetEmployee/list
请求头: x-api-key: $DEEPSOP_API_KEY
响应 data 数组中每条记录关键字段:
name:员工名称(与 employeeList 中的名称对应,如 AiWa、Frank、Fran、Lisa)status:启用状态,0 = 启用,1 = 禁用remainingDays:剩余可用天数(可为 null)逐一检查 employeeList 中每个员工,规则如下:
禁用状态(status = 1)→ 终止任务,回复:
⚠️ 数字员工「{name}」当前处于禁用状态,无法执行任务。请联系管理员启用后再试。
未开通 / 已过期(status = 0 且 remainingDays 为 null 或 remainingDays ≤ 0)→ 进入签约流程(见下方 Step 1.5.1)。签约完成后重新拉取 /ai/presetEmployee/list 校验,通过后继续;用户放弃或余额不足则终止任务。
剩余天数不足(status = 0 且 remainingDays > 0 且 remainingDays ≤ 7)→ 提醒用户,但允许继续:
⚡ 提示:数字员工「{name}」剩余可用天数仅剩 {remainingDays} 天,建议尽快前往 https://ai.deepsop.com 续费,以免中断服务。
正常(status = 0 且 remainingDays > 7)→ 继续流程
所有员工均通过校验后,方可继续后续步骤。任一员工触发规则 1 立即停止;规则 2 必须走完签约流程且成功后才能继续。
按顺序执行,每一步失败或用户放弃均立即终止任务。
① 拉取套餐列表
接口:GET https://ai.deepsop.com/prod-api/ai/setting/list?packageType=3
请求头:x-api-key: $DEEPSOP_API_KEY
响应 data 为数组,每项结构:
{
presetEmployeeId, // 关联员工 ID,用于匹配当前待签约员工
packageOptions: [
{
id, // optionId(提交签约用)
packageId, // 套餐 ID(提交签约用)
description, // 套餐名称文案(如"月度套餐")
purchaseMonths, // 1 | 3 | 6 | 12
actualPrice, // 人民币实价(元)
discountRate, // 折扣率,100 = 无折扣
giftKToken // 赠送 K 币数量
}
]
}
根据当前待签约员工的 id 匹配对应条目,取其 packageOptions。
② 展示套餐让用户选择
先获取人民币→K币汇率:
接口:GET https://ai.deepsop.com/prod-api/system/config/configKey/CNY_TO_KCOIN
响应 msg 即为汇率(记为 rate)。
每个套餐的应付 K 币计算公式:
priceKCoin = actualPrice × (discountRate / 100) × rate
向用户展示(格式示例):
数字员工「{name}」尚未开通,请选择签约套餐(回复序号):
1. {description}({purchaseMonths}个月) — {priceKCoin} K币{折扣率≠100 时追加"({discountRate/10}折)"}{giftKToken>0 时追加",赠送 {giftKToken} K币"}
2. ...
回复「取消」放弃签约。
等待用户回复序号。用户选"取消"或无响应 → 终止任务并回复:
已取消签约,任务终止。
③ K 币余额校验
接口:GET https://ai.deepsop.com/prod-api/ai/vip/balance?userId={userId}
请求头:x-api-key: $DEEPSOP_API_KEY
其中 userId 取自 Step 3 前置 B /ai/user/profile 返回的 data.userId(若此前未调用则先调用获取)。响应 data 即为当前 K 币余额。
🔒 强制实时查询规则(极其重要,避免使用缓存余额):
- 每次进入本步骤 ③ 都必须重新调用一次
/ai/vip/balance接口,严禁复用本会话中任何先前一次查询到的balance值。- 典型踩坑场景:一次任务里有多个员工需要连续签约(例如先给 AiWa 签约扣了 K 币,紧接着又要给 Frank 签约);或同一员工首次提示余额不足、用户充值后让你重试 —— 此时上一轮的
balance已经过时,必须重新发请求拿最新值,不得沿用记忆里的旧数字做判断或在话术中展示。- 同样地,在 ④ 提交签约扣款成功之后,如果还有下一个员工要走签约流程,回到本步骤 ③ 时也必须重新查询余额,不得用「旧余额 - priceKCoin」自行推算。
- 用户口头告知"我已经充值了/余额已经够了"也不能作为跳过本接口的理由,必须以接口实时返回为准。
取余额 balance(本次接口调用的最新返回值),与所选套餐的 priceKCoin 比较:
balance < priceKCoin → 余额不足,终止任务,回复:
❌ 余额不足,签约失败。当前余额:{balance} K币,所需:{priceKCoin} K币。 请前往 https://ai.deepsop.com 登录后充值 K 币,充值完成后重新下达任务。
不要尝试任何充值接口,直接终止流程。
balance ≥ priceKCoin → 进入 ④
④ 提交签约(扣 K 币)
接口:POST https://ai.deepsop.com/prod-api/ai/order/purchaseIndependentPackageByKToken
请求头:x-api-key: $DEEPSOP_API_KEY
请求体:
{
"packageId": "<选中套餐的 packageId>",
"optionId": "<选中套餐的 id>"
}
✅ 「{name}」签约成功!套餐:{description},扣除 {priceKCoin} K币。
⑤ 回到 Step 1.5 重新拉取 /ai/presetEmployee/list 校验,该员工状态正常后继续剩余流程。
⚠️ 如果剩余 employeeList 中还有其他员工需要走签约流程,回到 Step 1.5.1 处理下一个员工时,步骤 ③ 必须重新调用
/ai/vip/balance拉取最新余额(因为本员工的 ④ 已经扣过 K 币,旧余额已失效)。禁止用「上一员工查到的 balance − 上一员工 priceKCoin」自行推算结果作为下一员工的余额判断依据。
本步骤用于在没有 AiWa 协同的销售任务里,由用户直接指定本次销售动作要面向的客户池。所有数据最终写入 collaborationSubmitTaskParam.sourceSettings 对象(结构见本步骤末尾「最终装配规则」)。
触发条件回顾: employeeList 不含 AiWa,且至少含 Frank / Fran / Lisa 之一。employeeList 含 AiWa(无论是否还含销售员工)时,跳过本步骤。
首次提问:让用户在两种来源中二选一(也可两者并用、累加生效):
🧭 检测到本次任务无 AiWa 客户挖掘协同,请选择客户来源(可二选一,也可两种叠加): 1. 上传 xlsx 文件 - 客户管理(公司导入)模板:调用 API 清单 #23 `POST /ai/customer/template` 下载(详见下方「模板下载接口」小节) - 地址簿(地址簿导入)模板:调用 API 清单 #24 `POST /ai/customer/contactImportTemplate` 下载(详见下方「模板下载接口」小节) 你可以直接上传 xlsx 文件,也可以以文字形式按模板格式提供客户/地址簿信息,由我代为整理成对应 xlsx 后上传。 2. 搜索选择公司 通过关键词搜索系统内已存在的公司(客户),由你勾选若干条,对应的客户 ID 会进入 suppurIds。 请回复「1」「2」或「1+2」,并附上你的具体输入。
两个模板不再从写死的 OSS 链接获取,必须改为调用后端接口实时下载(与前端 Vue 项目 handleDownloadTemplate / 通用 download(url, params, filename) 行为一致)。
接口:
POST https://ai.deepsop.com/prod-api/ai/customer/template # 公司导入模板(uploadType === 'fileList') POST https://ai.deepsop.com/prod-api/ai/customer/contactImportTemplate # 地址簿导入模板(uploadType !== 'fileList')
请求头:
x-api-key: $DEEPSOP_API_KEYContent-Type: application/x-www-form-urlencoded请求体: 空 form(params = {},序列化后为空字符串),不要带 JSON body。
响应: responseType: 'blob',二进制 xlsx 流。客户端按 导入模板_${Date.now()}.xlsx 命名保存即可。
Blob 校验规则(与前端 blobValidate(data) 一致):
PK.. zip 头)→ 视为成功,直接 saveAs(blob, filename)。JSON.parse,把 msg 原样回给用户,不要当成 xlsx 保存。等价 curl(用于排查/手工取模板):
curl --location --request POST 'https://ai.deepsop.com/prod-api/ai/customer/template'
--header 'x-api-key: $DEEPSOP_API_KEY'
--header 'Content-Type: application/x-www-form-urlencoded'
--output '导入模板_company.xlsx'
curl --location --request POST 'https://ai.deepsop.com/prod-api/ai/customer/contactImportTemplate'
--header 'x-api-key: $DEEPSOP_API_KEY'
--header 'Content-Type: application/x-www-form-urlencoded'
--output '导入模板_contact.xlsx'
禁止继续向用户提供
https://kocgo-ai-sales-test.oss-cn-hangzhou.aliyuncs.com/...公司导入模板.xlsx/...地址簿导入模板.xlsx这类硬编码 OSS 链接,模板内容以接口实时返回为准。
🛑 【最优先】xlsx 文件回显总开关(在本步骤任何子步骤之前先读这一条):
从用户上传 xlsx 那一刻起,到本任务结束为止,所有关于该 xlsx 的内容回显只允许出现在唯一一个位置:本步骤第 3 步「转 OSS 链接」上传成功之后、按"上传成功回复硬约束"输出的那一条消息里的「号源预览块」。除此以外的任何时机、任何形式的内容回显都属于 bug,包括但不限于:
- ❌ "文件已经解析出来了,内容如下:列:xxx | yyy | zzz,数据:..."(禁止的"解析预览"伪步骤,SKILL.md 根本没有这一步)
- ❌ 在追问用户文件归属(A/B 类型)之前先回显任何字段
- ❌ "我看到文件里有:张三 / 李四 / 王五 / 杨总 ..." 这种姓名预览
- ❌ "看列结构是 xxx,所以应该是地址簿对吧" 这种"先猜归属、再让用户确认"的反问 —— 必须直接按第 1 步原话反问,不得先暴露任何列名或行内容
- ❌ 多列并排回显(无论是表格、管道符
a | b | c、JSON、还是自然语言枚举)- ❌ 用
张三/李四/王五/示例/xxx/***等占位符 / 脱敏 / 改写版本"代替"回显 —— 占位符也算回显,且 100% 是幻觉- ❌ 在用户后续追问"刚才那个文件里第二行是谁"时再补回显
如果你(LLM)此刻有冲动想"让用户确认我解析对了"——立刻打住。LLM 没有可靠的 xlsx 解析能力,任何"我解析出来是 X"的话术都有极高概率把行/列错位、或把真实姓名替换成训练语料里的高频占位名(张三/李四)。正确做法是直接走第 1 步反问归属,不暴露任何列名/内容;解析校验交给第 3 步上传成功后的"号源预览块",并且严格遵守它的单列、原值、不超过 3 条、与员工渠道一一对应等约束。
(事故复盘:用户上传的地址簿是
杨总 + 13484093796 / 文总 + 16659106679,AI 在文件类型确认之前插入了一段未授权的"解析预览",把姓名整个换成了占位符张三 / 李四,并把姓名+职务+电话三列一起回显。这就是本总开关要堵死的具体场景。)
确认文件归属:用户每上传一个 xlsx 文件,必须先反问其归属于哪种模板(原话反问,不得在反问前/反问中夹带任何列名或行内容):
请确认本次上传的 xlsx 属于哪种类型?
- A. 客户管理(公司导入) —— 对应模板:
公司导入模板.xlsx- B. 地址簿(地址簿导入) —— 对应模板:
地址簿导入模板.xlsx
用户回复 A → 该文件归入「客户管理」;用户回复 B → 归入「地址簿」。不得自行猜测归属,不得用"我看列结构像是 B,对吧?"这种带预设答案的反问诱导用户。
文字形式补充:如果用户没有上传文件、而是以文字方式描述客户/地址簿信息,应按对应模板的列结构整理成 xlsx 文件,再走相同的上传 / 归属确认流程。整理时遵循模板列顺序与字段名,禁止自行新增列或合并列。
转 OSS 链接:调用 DeepSOP 的统一文件上传接口(API 清单 #22),后端会把文件落到阿里云 OSS 并返回公网可访问的 https://...aliyuncs.com/... 链接。
接口:
POST https://ai.deepsop.com/prod-api/system/fileUpload/upload
请求头:
x-api-key: $DEEPSOP_API_KEYContent-Type: multipart/form-data(让 HTTP 客户端自动带 boundary,不要手填)请求体(form-data):
file:xlsx 文件本身(字段名严格为 file,不得改成 upload / xlsx / attachment)等价 curl:
curl --location --request POST 'https://ai.deepsop.com/prod-api/system/fileUpload/upload'
--header 'x-api-key: $DEEPSOP_API_KEY'
--form 'file=@"<本地 xlsx 绝对路径>"'
成功响应示例:
{
"msg": "操作成功",
"code": 200,
"fileName": "公司导入.xlsx",
"url": "https://kocgo-ai-sales-test.oss-cn-hangzhou.aliyuncs.com/material/100/xxx.xlsx"
}
响应解析规则:
code === 200 视为成功;其它 code 一律按上传失败处理,把 msg 原样回给用户并要求重试,不得继续。url(不是 data.url,本接口的成功结构是平铺字段)作为 OSS 链接。https:// 开头 + 域名含 aliyuncs.com。不满足则视为上传失败,不得写入 fileList / addressFileList。🛑 上传成功回复硬约束(强制,违反即视为 bug):
用户上传 xlsx 成功后,只能给用户回以下格式的话(基础句式 + 可选号源预览):
✅ 文件
{fileName}上传成功,本次共导入 {N} 条{公司 / 联系人}数据。
{N}= 本地 xlsx 的数据行数(不含表头行);如果当前运行环境无法读取本地 xlsx,就只回「文件上传成功」,不要自己编一个数字。- 类型 A(公司导入)→ 后缀「条公司数据」;类型 B(地址簿导入)→ 后缀「条联系人数据」。
号源预览规则(按下任务员工展示对应渠道字段):
在能可靠解析本地 xlsx 的前提下(如运行环境提供了 xlsx 解析能力,且能确定地读出表头列名),按本次任务的销售执行员工追加号源预览片段,只展示与该员工渠道对应的那一列前 3 条非空值,不展示其他任何字段:
任务员工 展示列 列名匹配(命中任一即可,大小写不敏感) Frank(邮件销售) 邮箱 邮箱/邮件Fran(电话销售) 电话 phone/mobile/tel/电话/手机/手机号Lisa(短信销售) 电话 同上 命中后追加格式(注意:每条单独一行,前后用反引号包住,不带姓名/公司/序号以外的任何字段):
号源预览(仅展示 {Frank → 邮箱 / Fran/Lisa → 电话} 列前 3 条,供你核对,不代表全部):
{value_1}{value_2}{value_3}强约束(违反即视为 bug,全部并列生效):
- 只准展示当前员工对应的那一列:Frank 不展示电话列,Fran/Lisa 不展示邮箱列;绝对禁止把同一行的姓名、公司、地址、岗位、行业、备注等其他字段拼进来。
- 必须严格按 xlsx 中该列的原始顺序取前 3 条非空值,不得重排、去重、改写、脱敏、截断或"美化"为占位符。
- 同一任务命中多个销售员工(例如 Fran + Lisa)时,电话列只输出一次预览块;如果同时有 Frank 和 Fran/Lisa,则分别输出邮箱预览块与电话预览块(仍然各自只取该列前 3 条)。
- 任何无法可靠解析的情况一律退化为"只回文件名 + 条数"基础句式,不得输出号源预览块。包括但不限于:运行环境读不到本地 xlsx;找不到匹配的列名;该列前 3 行全部为空;列内容看起来与渠道不符(如声称是邮箱列但值里没有
@、声称是电话列但值里全是非数字字符)。- 绝对禁止凭记忆 / 凭印象 / 凭用户聊天里提过的样例编造号码或邮箱。哪怕只多写一个数字、改一个字母,也按 bug 处理。
- 绝对禁止在号源预览块里出现任何人物姓名、公司名、阶段标签、星级、地区、备注;如果用户后续追问"这是谁的号码 / 哪个公司的邮箱",统一回:"导入的明细以 DeepSOP 后台为准,提交任务后可在客户管理 / 地址簿模块查看,本助手只对你核对的渠道列(电话/邮箱)做有限回显,不关联到具体人物或公司,以免错位。"
历史事故复盘(必读): 早期实现里 AI 在"上传成功"提示里把内容回显成了
C 公司 + A 电话 / D 公司 + B 电话这种行/列错位结果(用户原文件其实是A 公司 + A 电话 / B 公司 + B 电话),导致用户去删数据 / 重传 / 改任务。根本原因:LLM 没有可靠的跨字段对齐能力,只要同时回显 ≥2 个字段就有概率串行。所以本规则刻意只允许单列回显、且是与当前员工渠道一一对应的那一列——既能让用户核对号源到底有没有传对,又把 LLM 的错位风险压到最低。
落位规则:
{ name, url } 对象追加到 sourceSettings.fileList 数组中。{ name, url } 对象追加到 sourceSettings.addressFileList 数组中。🛑 类型强约束(最常见、最高危错误):
fileList/addressFileList的元素必须是ExcelFile对象,对应后端 Java POJO:public static class ExcelFile { String name; // 文件名(含 .xlsx 后缀),如 "公司导入.xlsx" String url; // 阿里云 OSS 公网 URL(来自上传接口顶层 url 字段) }整体类型是
ExcelFile[](即Array<{name: string, url: string}>)。两字段约束完全对称,任一写错都会导致后端解析失败或客户来源丢失。元素装配规则: 上传接口
/system/fileUpload/upload成功响应的顶层字段:
fileName→ 装到 ExcelFile 的name字段(注意:上传响应叫fileName,但 ExcelFile 的字段叫name,需要重命名)url→ 装到 ExcelFile 的url字段
addressFileList(地址簿 / 地址簿导入)正确:
"addressFileList": [ { "name": "地址簿导入.xlsx", "url": "https://kocgo-ai-sales-test.oss-cn-hangzhou.aliyuncs.com/material/100/xxx.xlsx" } ]错误:
- ❌
"addressFileList": ["https://..."](裸 URL 字符串数组 — 后端反序列化为 ExcelFile 失败)- ❌
"addressFileList": [{ "url": "https://..." }](漏name)- ❌
"addressFileList": [{ "name": "...", "url": "...", "fileName": "..." }](多余fileName键 — 用name,不要再带fileName)- ❌
"addressFileList": [{ "fileName": "...", "url": "..." }](key 写成fileName— 必须是name)- ❌
"addressFileList": {}(空对象 — 类型必须是数组)- ❌
"addressFileList": [{}](空对象元素)- ❌
"addressFileList": null/ 省略字段(无地址簿文件时写成空数组[],不能为null或缺省)
fileList(客户管理 / 公司导入)正确:
"fileList": [ { "name": "公司导入.xlsx", "url": "https://kocgo-ai-sales-test.oss-cn-hangzhou.aliyuncs.com/material/100/yyy.xlsx" } ]错误: 错误模式与
addressFileList完全对称(裸字符串、漏name、key 写成fileName、{}/null/ 缺省、空对象元素等),不再赘述。此外:
- ❌ 把已经成功上传的 xlsx 文件 "忘了"放进去(最终请求体里
fileList: [],导致前端选了文件但后端实际收不到客户来源)统一规则:用户每上传一个 xlsx 并由其确认归属后,对应的
{ name, url }对象(name来自上传响应的fileName,url来自顶层url)必须作为 ExcelFile 元素 push 进对应数组(A 类→fileList,B 类→addressFileList),并在最终提交前肉眼核对sourceSettings中两字段的元素数量与已确认归属的文件数一致。禁止在最终请求体里把已成功上传的文件丢失为[],更禁止把字段写成{}/null/ 字符串数组 / 漏name或url任一键。
updateSupport(仅 fileList 非空时生效):
1 = 更新(默认);0 = 不更新。1;用户表达「不要覆盖 / 保留原数据 / 不要更新已有客户」等含义时改为 0。sourceType === 'search')此途径对应前端 Vue 项目的「客户搜索栏」逻辑(sourceType === 'search' 分支),与前端共用同一个客户列表接口。
接口(API 清单 #21):
POST https://ai.deepsop.com/prod-api/ai/customer/customerList?pageNum={pageNum}&pageSize=10
请求头:x-api-key: $DEEPSOP_API_KEY
Query 参数(与 body 中同名字段冗余传递,与前端 Vue 的 params 对齐):
pageNum:从 1 开始的页码pageSize:固定为 10,禁止改写Body(JSON):
{
"pageNum": <同上>,
"pageSize": 10,
"name": "<公司名搜索关键词,无关键词时填空字符串 "">",
"searchSort": "<排序字段:"" | "email" | "phone">",
"type": 0
}
🔒 Body 字段硬约束:
type恒为数字0(不是字符串、不是1),来自前端getCustomerList中{ ...customerQueryParams, type: 0 }的硬编码。searchSort仅允许三种值:""(默认不排序)、"email"(按邮箱排序)、"phone"(按电话排序)。任何其他值都属于编造。name为空时必须显式传"",不要省略键也不要传null。- 同一次会话中,每次发起搜索都需把上述 5 个键全部带上,缺键属于错误。
响应字段:
total:搜索到的总条数(数字)rows:当前页的客户数组,每条至少含:id:客户唯一 ID(用于装入 suppurIds)name:公司名countryName / countryValue:国家中文名 / 国家代码(如 cn、us,前端用作国旗图片名)email / phone:联系邮箱 / 电话(可能为空,空时取 aiCustomers[0] 的对应字段)stageLabel:客户阶段对象 { stageLabel, backgroundColor, color }(可空)groupName / groupId:客户分组(可空)level:客户星级(0–5)aiSuppurLabels:客户标签数组 [{labelName, backgroundColor, color}, ...]aiCustomers:联系人数组;当 email / phone 为空时,前端会取 aiCust 相关专题
热门下载
相关下载
精品课程
共6课时 | 54.7万人学习
共89课时 | 134.1万人学习
共49课时 | 82.6万人学习