feat(ai): FluxAI Level 2 — smart recommender, funnel-aware SPIN, contextual quick-replies
Deploy to VPS / deploy (push) Has been cancelled
Deploy to VPS / deploy (push) Has been cancelled
Three improvements to the FluxAI sales intelligence: 1. New `recommend_application` tool: analyzes prospect's industry, problem, and process keywords against the database to rank-match the best FLUX products with confidence scores. Bridges the gap between "I have a problem" and "here's our solution." 2. Funnel-aware system prompt: replaces flat SPIN with 4-stage pipeline (Qualify → Recommend+Educate → Quantify+Prove → Specify+Convert) with clear rules for when to ask vs. act. 3. Contextual quick-reply buttons: after each AI response, dynamic suggestions appear based on which tools were used — guiding the prospect through the natural next step in the funnel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -187,6 +187,11 @@ export default function SilentObserver() {
|
||||
const DataToolWorking = ({ label }: { label: string }) => (<div key={key} className="self-start flex items-center gap-2 py-1"><Database size={10} className="text-[#0066CC]/40 dark:text-[#4DA6FF]/40 animate-pulse" /><span className="text-[10px] text-[#86868B]/60 dark:text-[#A1A1A6]/40 italic">{label}</span></div>);
|
||||
const ToolError = ({ msg }: { msg?: string }) => (<div key={key} className="text-[11px] text-red-400 dark:text-red-300/80 self-start py-1">Analysis unavailable. {msg}</div>);
|
||||
|
||||
if (part.type === "tool-recommend_application") {
|
||||
if (part.state === "input-streaming" || part.state === "input-available") return <DataToolWorking key={key} label="Analyzing your needs..." />;
|
||||
if (part.state === "output-available") return null;
|
||||
if (part.state === "output-error") return <ToolError key={key} msg={(part as any).errorText} />;
|
||||
}
|
||||
if (part.type === "tool-search_installations") {
|
||||
if (part.state === "input-streaming" || part.state === "input-available") return <DataToolWorking key={key} label="Searching installation database..." />;
|
||||
if (part.state === "output-available") return null;
|
||||
@@ -237,6 +242,41 @@ export default function SilentObserver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ═══ Contextual Quick-Replies based on last assistant message ═══
|
||||
function getContextualSuggestions(): string[] {
|
||||
if (isLoading || messages.length === 0) return [];
|
||||
const lastAssistant = [...messages].reverse().find(m => m.role === "assistant");
|
||||
if (!lastAssistant?.parts) return [];
|
||||
|
||||
const toolTypes = new Set(
|
||||
lastAssistant.parts
|
||||
.filter((p: any) => p.type?.startsWith("tool-") && p.state === "output-available")
|
||||
.map((p: any) => p.type)
|
||||
);
|
||||
|
||||
// Priority order: suggest the next logical funnel step
|
||||
if (toolTypes.has("tool-schedule_consultation")) return []; // End of funnel
|
||||
if (toolTypes.has("tool-show_equipment_specs"))
|
||||
return ["Schedule a consultation", "Compare with traditional methods"];
|
||||
if (toolTypes.has("tool-show_case_study"))
|
||||
return ["Show me equipment specs", "Calculate savings for my operation", "Schedule a consultation"];
|
||||
if (toolTypes.has("tool-energy_savings_calculator"))
|
||||
return ["Show me a real installation", "See equipment specs", "How does RF heating work?"];
|
||||
if (toolTypes.has("tool-process_comparison_table"))
|
||||
return ["Calculate savings for my operation", "Show me proven installations"];
|
||||
if (toolTypes.has("tool-rf_technology_explainer"))
|
||||
return ["What would I save in energy costs?", "Show me real installations"];
|
||||
if (toolTypes.has("tool-recommend_application") || toolTypes.has("tool-get_application_knowledge"))
|
||||
return ["Calculate energy savings", "Show me case studies", "Compare RF vs my current method"];
|
||||
if (toolTypes.has("tool-navigate_to_section"))
|
||||
return ["Tell me more about this", "How much energy can I save?"];
|
||||
|
||||
// Default: if assistant responded with text only (Stage 1 qualification)
|
||||
return [];
|
||||
}
|
||||
|
||||
const suggestions = getContextualSuggestions();
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatePresence>
|
||||
@@ -323,6 +363,24 @@ export default function SilentObserver() {
|
||||
{[0, 1, 2].map((i) => (<motion.div key={i} className="w-1.5 h-1.5 rounded-full bg-[#0066CC] dark:bg-[#4DA6FF]" animate={{ opacity: [0.2, 1, 0.2], scale: [0.8, 1, 0.8] }} transition={{ duration: 1, repeat: Infinity, delay: i * 0.2 }} />))}
|
||||
</div>
|
||||
)}
|
||||
{suggestions.length > 0 && !isLoading && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, delay: 0.2 }}
|
||||
className="flex flex-wrap gap-1.5 pt-1 pb-2"
|
||||
>
|
||||
{suggestions.map((s) => (
|
||||
<button
|
||||
key={s}
|
||||
onClick={() => { sendMessage({ text: s }); }}
|
||||
className="px-3 py-1.5 rounded-full text-[11px] font-medium bg-[#0066CC]/[0.06] dark:bg-[#4DA6FF]/[0.08] border border-[#0066CC]/10 dark:border-[#4DA6FF]/10 text-[#0066CC] dark:text-[#4DA6FF] hover:bg-[#0066CC]/10 dark:hover:bg-[#4DA6FF]/15 hover:border-[#0066CC]/20 dark:hover:border-[#4DA6FF]/20 active:scale-95 transition-all duration-200"
|
||||
>
|
||||
{s}
|
||||
</button>
|
||||
))}
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<form onSubmit={onSubmit} className="relative z-10 px-4 py-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] md:pb-3 border-t border-black/[0.04] dark:border-white/[0.06] bg-white/40 dark:bg-white/[0.02] flex items-center gap-2.5 transition-colors duration-300">
|
||||
|
||||
Reference in New Issue
Block a user