fix(bridge): 商机漏建兜底与匿名线索

This commit is contained in:
wangliang
2026-10-23 14:22:14 +08:00
parent 22d96edee6
commit 9ce3120648
6 changed files with 238 additions and 18 deletions
+10 -10
View File
@@ -221,12 +221,13 @@ const TOOL_DEFS = [
{
name: 'collect_info',
description:
'Record customer lead details incrementally across turns (company, contact name, phone, product need, quantity, budget, close date). Call this whenever the customer provides any of these; it persists them for the conversation so the final create_opportunity does not lose earlier facts. Does not create a CRM record.',
'Record customer lead details incrementally across turns (company, contact name, email, phone, product need, quantity, budget, close date). Call this whenever the customer provides any of these; it persists them for the conversation so the final create_opportunity does not lose earlier facts. Does not create a CRM record.',
parameters: {
type: 'object',
properties: {
company: { type: 'string', description: 'customer company name' },
contactName: { type: 'string', description: 'contact person full name' },
email: { type: 'string', description: 'contact email if the customer provided one' },
phone: { type: 'string', description: 'phone number if stated' },
productNeed: { type: 'string', description: 'what the customer asked for, in their own words' },
quantity: { type: 'string', description: 'quantity if stated' },
@@ -240,7 +241,7 @@ const TOOL_DEFS = [
{
name: 'send_verification_code',
description:
'Send a 6-digit email verification code to the customer. Only call after the customer provides their email address and private data (orders/opportunities) requires verification. Never invent an email address.',
'Send a 6-digit email verification code to the customer. Only call after the customer provides their email address and private data (orders) requires verification. Never invent an email address.',
parameters: {
type: 'object',
properties: {
@@ -253,7 +254,7 @@ const TOOL_DEFS = [
{
name: 'verify_email_code',
description:
'Verify the 6-digit code the customer received by email. Call after the customer replies with the code. On success the conversation becomes verified for private order/opportunity tools.',
'Verify the 6-digit code the customer received by email. Call after the customer replies with the code. On success the conversation becomes verified for private order tools.',
parameters: {
type: 'object',
properties: {
@@ -279,12 +280,13 @@ const TOOL_DEFS = [
{
name: 'create_opportunity',
description:
'Create a CRM opportunity in Twenty after you have collected the 3 required elements: company, contact name, and product/need. Call it exactly once per customer; if it returns created=false an opportunity already exists. Requires verified contact.',
'Create a CRM opportunity in Twenty once you know the company, a contact (name, email or phone) and the product/need. Call it exactly once per customer; if it returns created=false an opportunity already exists. Email verification is NOT required for lead creation.',
parameters: {
type: 'object',
properties: {
company: { type: 'string', description: 'customer company name' },
contactName: { type: 'string', description: 'contact person full name' },
email: { type: 'string', description: 'contact email if the customer provided one' },
productNeed: { type: 'string', description: 'what the customer asked for, in their own words' },
quantity: { type: 'string', description: 'quantity if stated' },
budget: { type: 'string', description: 'budget if stated, e.g. "€500"' },
@@ -293,7 +295,7 @@ const TOOL_DEFS = [
},
required: ['company', 'contactName', 'productNeed'],
},
requiresVerification: true,
requiresVerification: false,
},
];
@@ -341,7 +343,7 @@ function createTools(cfg, deps = {}) {
if (!conversationUuid) throw new ToolError(400, 'missing_conversation', 'conversation context is required');
const existing = await leadStore.get(conversationUuid);
const slots = { ...(existing ? existing.slots : {}) };
for (const key of ['company', 'contactName', 'phone', 'productNeed', 'quantity', 'budget', 'closeDate']) {
for (const key of ['company', 'contactName', 'email', 'phone', 'productNeed', 'quantity', 'budget', 'closeDate']) {
const value = args && args[key] != null ? String(args[key]).trim() : '';
if (value) slots[key] = value;
}
@@ -673,11 +675,9 @@ function createTools(cfg, deps = {}) {
}
async function createOpportunity(args, ctx) {
if (!ctx.verified) {
throw new ToolError(403, 'verification_required', 'Creating an opportunity requires a verified contact. Ask the customer to verify their email first.');
}
// 线索建商机不要求邮箱验证(对齐旧 bridge leadPipeline):公司 + 联系人 + 需求即可。
// 订单/邮箱历史仍要求 verified,邮箱只从身份头取;商机允许用客户在对话里给的 email 做去重/联系人。
const email = String(ctx.email || '').trim().toLowerCase();
if (!email) throw new ToolError(400, 'missing_email', 'The verified contact has no email on file.');
if (!cfg.twenty.apiKey) throw new ToolError(503, 'not_configured', 'TWENTY_API_KEY is not configured');
const conversationUuid = String(ctx.conversationUuid || '');
if (!conversationUuid) throw new ToolError(400, 'missing_conversation', 'conversation context is required');