智能体开发案例:美业门店使用智能体提升销售转化效率
以“美业门店使用智能体提升销售转化效率”的完整落地为主线,从系统架构设计 → 核心模块代码实现 → 数据库设计 → 部署与优化策略 → 完整系统工作流等方面,给出可直接工程化落地的方案与示例代码(Node.js/NestJS + React + PostgreSQL + Redis + 向量库 + LLM API)。
美业门店AI智能体销售转化系统的核心目标是通过技术手段解决传统美业“客户需求洞察难、销售话术标准化低、意向客户筛选效率低”三大痛点,最终提升客单价与转化效率。系统采用“前端交互层-智能体核心层-数据支撑层”三层架构,结合大模型、多模态感知、数据驱动等技术,实现从客户触达到成交的全流程智能化。
下面以“美业门店使用智能体提升销售转化效率”的完整落地为主线,从系统架构设计 → 核心模块代码实现 → 数据库设计 → 部署与优化策略 → 完整系统工作流等方面,给出可直接工程化落地的方案与示例代码(Node.js/NestJS + React + PostgreSQL + Redis + 向量库 + LLM API)。
一、场景与整体架构设计
这一部分概述业务场景与整体技术架构,帮助你从顶层把握系统形态和模块划分。
1.1 业务场景拆解(美业门店+智能体)
- 目标:提升门店销售转化效率与客单价,降低人工咨询与培训成本。
- 典型场景:
- 线上咨询引流:用户在小程序 / H5 / 企业微信中与“智能美容顾问”聊天,进行皮肤状况问诊、项目推荐。
- 套餐与话术推荐:智能体根据用户画像、历史消费与门店活动,自动给出推荐套餐,并给店员提供销售话术。
- 预约与到店转化:智能体引导用户完成预约、付定金,并在到店前后做提醒与二次营销。
- 服务后复购:根据项目效果周期,自动触发复购提醒及个性化推荐。
1.2 技术架构总览
采用典型的“前后端分离 + 多智能体编排 + RAG + 工具调用”的架构。
┌───────────────── 前端(小程序/H5/PC) ─────────────────┐
│ │
│ Chat UI 预约页面 个人中心 店员工作台 │
└───────────────▲───────────────────────────────┘
│ HTTP/WebSocket
▼
┌─────────────────────────────┐
│ API 网关 / BFF │
└───────▲───────────▲───────┘
│ │
REST │ │ WebSocket
│ │
┌─────────────┴───────────┴────────────────┐
│ 应用后端 │
│ (NestJS 微服务 / 模块化单体均可) │
│ │
│ - Auth & User Service │
│ - Customer Profile Service │
│ - Conversation / Session Service │
│ - Agent Orchestrator (智能体编排中心) │
│ - Tool Service(项目/库存/预约/券等工具) │
│ - Product & Project Catalog Service │
│ - Recommendation & Scoring Service │
│ - Workflow Engine (CRM 事件流/营销触发) │
└──────────▲─────────────▲───────────────┘
│ │
SQL/事务 │ │ 缓存/队列/状态
│ │
┌────────┴───────┐ ┌─┴─────────────┐
│ PostgreSQL │ │ Redis / MQ │
│ (业务&日志) │ └───────────────┘
└───────────────┘
▲
│向量检索/RAG
▼
┌───────────────────────────┐
│ 向量数据库(Milvus/pgvector)│
└───────────────────────────┘
▲
│LLM 调用
▼
┌─────────────────────┐
│ LLM Provider(OpenAI │
│ / DeepSeek / 自建) │
└─────────────────────┘
1.3 智能体角色设计
- 用户侧智能体(“美容顾问” Agent)
- 负责对C端顾客进行问诊、项目推荐、预约引导。
- 店员侧智能体(“销售助理” Agent)
- 给店员提供话术、搭配方案、异议处理建议。
- 后台运营智能体(“运营分析” Agent)
- 对转化漏斗进行分析,自动生成活动建议和A/B方案。
二、数据库设计(PostgreSQL+向量库)
这一部分给出主数据模型和核心表结构,保证智能体能“有记忆、有数据可查”。
2.1 核心业务表(关系型)
以 PostgreSQL 为例,简化但可直接落地。
-- 顾客基础信息
CREATE TABLE customers (
id BIGSERIAL PRIMARY KEY,
external_id VARCHAR(64), -- 小程序 openid / 手机号等
name VARCHAR(50),
gender VARCHAR(10),
birthday DATE,
phone VARCHAR(20),
skin_type VARCHAR(50), -- 干/油/混合/敏感 等
skin_issues TEXT, -- 主要皮肤问题描述
tags TEXT[], -- ['高客单','敏感肌','痘肌']
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 门店/项目/产品
CREATE TABLE stores (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100),
address TEXT,
city VARCHAR(50),
phone VARCHAR(20),
biz_hours VARCHAR(100),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE projects (
id BIGSERIAL PRIMARY KEY,
store_id BIGINT REFERENCES stores(id),
name VARCHAR(100),
category VARCHAR(50), -- 如: '清洁','补水','抗衰'
duration_min INT,
price NUMERIC(10,2),
description TEXT,
contra TEXT, -- 禁忌/不适用人群
effects TEXT, -- 预期效果
tags TEXT[],
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 会话与消息
CREATE TABLE conversations (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT REFERENCES customers(id),
channel VARCHAR(20), -- 'wechat','h5','in_store'
status VARCHAR(20), -- 'active','closed'
started_at TIMESTAMPTZ DEFAULT NOW(),
ended_at TIMESTAMPTZ
);
CREATE TABLE messages (
id BIGSERIAL PRIMARY KEY,
conversation_id BIGINT REFERENCES conversations(id),
role VARCHAR(20), -- 'user','assistant','staff'
content TEXT,
meta JSONB, -- 如 tool_calls, scores 等
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 预约与订单
CREATE TABLE appointments (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT REFERENCES customers(id),
store_id BIGINT REFERENCES stores(id),
project_id BIGINT REFERENCES projects(id),
schedule_time TIMESTAMPTZ,
status VARCHAR(20), -- 'pending','confirmed','completed','cancelled','no_show'
channel VARCHAR(20), -- 'ai_agent','staff','miniapp'
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT REFERENCES customers(id),
total_amount NUMERIC(10,2),
status VARCHAR(20), -- 'unpaid','paid','refunded'
source VARCHAR(20), -- 'ai_agent','offline','online'
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT REFERENCES orders(id) ON DELETE CASCADE,
project_id BIGINT REFERENCES projects(id),
quantity INT,
price NUMERIC(10,2)
);
2.2 智能体与知识库相关表
-- 预设话术、项目说明、门店SOP文档等,用于RAG检索
CREATE TABLE kb_documents (
id BIGSERIAL PRIMARY KEY,
store_id BIGINT REFERENCES stores(id),
title VARCHAR(255),
content TEXT,
doc_type VARCHAR(50), -- 'sop','faq','script','project_detail'
tags TEXT[],
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 向量表 (以 pgvector 为例)
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE kb_embeddings (
id BIGSERIAL PRIMARY KEY,
kb_doc_id BIGINT REFERENCES kb_documents(id) ON DELETE CASCADE,
chunk_index INT,
embedding vector(1536), -- 维度依据模型
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 对话级别的“智能体决策日志”
CREATE TABLE agent_actions (
id BIGSERIAL PRIMARY KEY,
conversation_id BIGINT REFERENCES conversations(id),
step_index INT,
agent_name VARCHAR(50), -- 'beauty_consultant','sales_assistant'
tool_called VARCHAR(100),
tool_input JSONB,
tool_output JSONB,
reasoning TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
三、后端核心模块与代码实现(NestJS 示例)
这一部分通过关键模块代码展示如何从零实现“智能美容顾问”智能体及工具调用。
3.1 NestJS 项目结构
backend/
src/
main.ts
app.module.ts
common/
config/
interceptors/
guards/
modules/
auth/
customers/
projects/
conversations/
agents/
tools/
kb/
workflow/
3.2 Conversation 模块(维护会话与消息)
3.2.1 conversation.entity.ts
// src/modules/conversations/entities/conversation.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, CreateDateColumn } from 'typeorm';
import { Customer } from '../../customers/entities/customer.entity';
import { Message } from './message.entity';
@Entity('conversations')
export class Conversation {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Customer, (c) => c.conversations)
customer: Customer;
@Column({ length: 20 })
channel: string; // 'wechat','h5'
@Column({ length: 20, default: 'active' })
status: string;
@CreateDateColumn()
startedAt: Date;
@Column({ type: 'timestamptz', nullable: true })
endedAt: Date | null;
@OneToMany(() => Message, (m) => m.conversation)
messages: Message[];
}
// src/modules/conversations/entities/message.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn } from 'typeorm';
import { Conversation } from './conversation.entity';
@Entity('messages')
export class Message {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Conversation, (c) => c.messages)
conversation: Conversation;
@Column({ length: 20 })
role: 'user' | 'assistant' | 'staff';
@Column({ type: 'text' })
content: string;
@Column({ type: 'jsonb', nullable: true })
meta: any;
@CreateDateColumn()
createdAt: Date;
}
3.2.2 conversation.service.ts
// src/modules/conversations/conversations.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Conversation } from './entities/conversation.entity';
import { Message } from './entities/message.entity';
@Injectable()
export class ConversationsService {
constructor(
@InjectRepository(Conversation)
private readonly convRepo: Repository<Conversation>,
@InjectRepository(Message)
private readonly msgRepo: Repository<Message>,
) {}
async createOrGetActive(customerId: number, channel: string) {
let conv = await this.convRepo.findOne({
where: { customer: { id: customerId }, status: 'active', channel },
});
if (!conv) {
conv = this.convRepo.create({
customer: { id: customerId } as any,
channel,
});
await this.convRepo.save(conv);
}
return conv;
}
async addMessage(conversationId: number, role: 'user' | 'assistant' | 'staff', content: string, meta?: any) {
const msg = this.msgRepo.create({
conversation: { id: conversationId } as any,
role,
content,
meta,
});
return this.msgRepo.save(msg);
}
async getMessages(conversationId: number, limit = 20) {
return this.msgRepo.find({
where: { conversation: { id: conversationId } },
order: { id: 'DESC' },
take: limit,
});
}
}
3.3 智能体编排模块(Agent Orchestrator)
这一模块负责根据对话上下文选择合适的智能体角色、构造 Prompt、调用 LLM 与工具。
3.3.1 Agent 定义与工具接口
// src/modules/agents/types.ts
export type ToolCall = {
name: string;
arguments: any;
};
export interface AgentContext {
customerId: number;
conversationId: number;
channel: string;
lastMessages: { role: string; content: string }[];
}
export interface AgentResult {
reply: string;
toolCalls?: ToolCall[];
}
// src/modules/tools/tools.interface.ts
export interface Tool {
name: string;
description: string;
schema: any; // JSON Schema
execute(args: any): Promise<any>;
}
3.3.2 工具实现示例(项目推荐 / 预约检查)
// src/modules/tools/tools.service.ts
import { Injectable } from '@nestjs/common';
import { Tool } from './tools.interface';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Project } from '../projects/entities/project.entity';
import { AppointmentsService } from '../appointments/appointments.service';
@Injectable()
export class ToolsService {
private tools: Map<string, Tool> = new Map();
constructor(
@InjectRepository(Project)
private readonly projectRepo: Repository<Project>,
private readonly appointmentsService: AppointmentsService,
) {
this.registerTools();
}
private registerTools() {
// 项目推荐工具
this.tools.set('recommend_projects', {
name: 'recommend_projects',
description: '根据肌肤问题、预算等推荐合适的门店项目',
schema: {
type: 'object',
properties: {
skin_issues: { type: 'string' },
budget: { type: 'number' },
store_id: { type: 'number' },
},
required: ['skin_issues', 'store_id'],
},
execute: async (args) => {
const qb = this.projectRepo
.createQueryBuilder('p')
.where('p.store_id = :storeId', { storeId: args.store_id })
.andWhere('p.is_active = true');
// 简单示例:根据关键词过滤
if (args.skin_issues?.includes('痘')) {
qb.andWhere(':tag = ANY(p.tags)', { tag: '祛痘' });
}
if (args.budget) {
qb.andWhere('p.price <= :budget', { budget: args.budget });
}
const projects = await qb.orderBy('p.price', 'ASC').limit(5).getMany();
return { projects };
},
});
// 预约创建工具
this.tools.set('create_appointment', {
name: 'create_appointment',
description: '为用户创建一次门店项目预约',
schema: {
type: 'object',
properties: {
customer_id: { type: 'number' },
store_id: { type: 'number' },
project_id: { type: 'number' },
schedule_time: { type: 'string', description: 'ISO 时间' },
},
required: ['customer_id', 'store_id', 'project_id', 'schedule_time'],
},
execute: async (args) => {
return this.appointmentsService.createFromAgent(args);
},
});
}
getTool(name: string): Tool | undefined {
return this.tools.get(name);
}
listTools(): Tool[] {
return Array.from(this.tools.values());
}
}
AppointmentsService.createFromAgent 示例:
// src/modules/appointments/appointments.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Appointment } from './entities/appointment.entity';
import { Repository } from 'typeorm';
@Injectable()
export class AppointmentsService {
constructor(
@InjectRepository(Appointment)
private readonly repo: Repository<Appointment>,
) {}
async createFromAgent(input: {
customer_id: number;
store_id: number;
project_id: number;
schedule_time: string;
}) {
const appointment = this.repo.create({
customer: { id: input.customer_id } as any,
store: { id: input.store_id } as any,
project: { id: input.project_id } as any,
scheduleTime: new Date(input.schedule_time),
status: 'pending',
channel: 'ai_agent',
});
return this.repo.save(appointment);
}
}
3.3.3 LLM 客户端封装
以 OpenAI 接口为例(可替换为任意 LLM 提供商)。
// src/modules/llm/llm.service.ts
import { Injectable } from '@nestjs/common';
import axios from 'axios';
@Injectable()
export class LlmService {
private apiKey = process.env.OPENAI_API_KEY!;
private baseUrl = 'https://api.openai.com/v1';
async chatCompletion(params: {
model: string;
messages: { role: string; content: string }[];
tools?: any[];
}) {
const resp = await axios.post(
`${this.baseUrl}/chat/completions`,
{
model: params.model,
messages: params.messages,
tools: params.tools,
tool_choice: 'auto',
},
{
headers: {
Authorization: `Bearer ${this.apiKey}`,
},
},
);
return resp.data;
}
}
3.3.4 Agent Orchestrator 实现
// src/modules/agents/agents.service.ts
import { Injectable } from '@nestjs/common';
import { ConversationsService } from '../conversations/conversations.service';
import { ToolsService } from '../tools/tools.service';
import { LlmService } from '../llm/llm.service';
import { AgentContext, AgentResult } from './types';
import { AgentActionsService } from '../agent-actions/agent-actions.service';
@Injectable()
export class AgentsService {
constructor(
private readonly convService: ConversationsService,
private readonly toolsService: ToolsService,
private readonly llmService: LlmService,
private readonly agentActionsService: AgentActionsService,
) {}
private buildSystemPrompt(): string {
return `
你是一名资深的“智能美容顾问”,在美业门店工作,目标是:
1)帮助用户识别皮肤问题;
2)为用户推荐合适的门店项目和套餐;
3)在合适的时机引导用户预约和下单;
4)始终遵守安全与禁忌原则,不做医疗承诺。
回答要求:
- 使用亲切自然的中文,可以适当使用表情符号但不过度;
- 在推荐项目时,解释推荐理由(与用户问题、预算、肤质的关联);
- 避免夸大效果,不涉及医学诊断;
- 在准备预约时,可以调用工具完成预约创建;
- 如需查门店项目信息,请通过工具调用,不要编造。
`;
}
async handleUserMessage(params: {
customerId: number;
channel: string;
userMessage: string;
}): Promise<AgentResult> {
const conv = await this.convService.createOrGetActive(params.customerId, params.channel);
await this.convService.addMessage(conv.id, 'user', params.userMessage);
const lastMessages = (await this.convService.getMessages(conv.id, 10))
.reverse()
.map((m) => ({ role: m.role === 'assistant' ? 'assistant' : 'user', content: m.content }));
const tools = this.toolsService.listTools().map((t) => ({
type: 'function',
function: {
name: t.name,
description: t.description,
parameters: t.schema,
},
}));
const messages = [
{ role: 'system', content: this.buildSystemPrompt() },
...lastMessages,
];
const completion = await this.llmService.chatCompletion({
model: 'gpt-4o-mini',
messages,
tools,
});
const choice = completion.choices[0];
const msg = choice.message;
// 是否调用工具?
if (msg.tool_calls?.length) {
const toolResults = [];
for (const tc of msg.tool_calls) {
const tool = this.toolsService.getTool(tc.function.name);
if (!tool) continue;
const args = JSON.parse(tc.function.arguments || '{}');
const output = await tool.execute(args);
await this.agentActionsService.logAction({
conversationId: conv.id,
agentName: 'beauty_consultant',
toolCalled: tool.name,
toolInput: args,
toolOutput: output,
reasoning: msg.content || '',
});
toolResults.push({ name: tool.name, output });
}
// 二次调用 LLM,总结工具结果并生成最终回复
const toolMessages = toolResults.map((tr) => ({
role: 'tool',
content: JSON.stringify(tr.output),
name: tr.name,
})) as any;
const finalCompletion = await this.llmService.chatCompletion({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: this.buildSystemPrompt() },
...lastMessages,
msg,
...toolMessages,
],
});
const finalReply = finalCompletion.choices[0].message.content;
await this.convService.addMessage(conv.id, 'assistant', finalReply);
return { reply: finalReply };
} else {
const reply = msg.content;
await this.convService.addMessage(conv.id, 'assistant', reply);
return { reply };
}
}
}
AgentActionsService 简单记录日志:
// src/modules/agent-actions/agent-actions.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AgentAction } from './entities/agent-action.entity';
import { Repository } from 'typeorm';
@Injectable()
export class AgentActionsService {
constructor(
@InjectRepository(AgentAction)
private readonly repo: Repository<AgentAction>,
) {}
async logAction(params: {
conversationId: number;
agentName: string;
toolCalled: string;
toolInput: any;
toolOutput: any;
reasoning: string;
}) {
const count = await this.repo.count({
where: { conversation: { id: params.conversationId } as any },
});
const action = this.repo.create({
conversation: { id: params.conversationId } as any,
stepIndex: count + 1,
agentName: params.agentName,
toolCalled: params.toolCalled,
toolInput: params.toolInput,
toolOutput: params.toolOutput,
reasoning: params.reasoning,
});
await this.repo.save(action);
}
}
3.4 知识库与向量检索(RAG)
3.4.1 文档入库与切片
// src/modules/kb/kb.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { KbDocument } from './entities/kb-document.entity';
import { KbEmbedding } from './entities/kb-embedding.entity';
import { LlmService } from '../llm/llm.service';
import { chunkText } from '../../utils/text-chunk';
@Injectable()
export class KbService {
constructor(
@InjectRepository(KbDocument)
private readonly docRepo: Repository<KbDocument>,
@InjectRepository(KbEmbedding)
private readonly embRepo: Repository<KbEmbedding>,
private readonly llmService: LlmService,
) {}
async addDocument(input: {
storeId: number;
title: string;
content: string;
docType: string;
tags?: string[];
}) {
const doc = this.docRepo.create(input);
await this.docRepo.save(doc);
const chunks = chunkText(input.content, 500); // 每段500字
let idx = 0;
for (const chunk of chunks) {
const embedding = await this.generateEmbedding(chunk);
const emb = this.embRepo.create({
kbDoc: doc,
chunkIndex: idx++,
embedding,
});
await this.embRepo.save(emb);
}
return doc;
}
private async generateEmbedding(text: string): Promise<number[]> {
// 调 Embedding 接口,伪代码
const resp = await this.llmService.embedding({
model: 'text-embedding-3-small',
input: text,
});
return resp.data[0].embedding;
}
}
chunkText 简单实现:
// src/utils/text-chunk.ts
export function chunkText(text: string, maxLen: number): string[] {
const chunks: string[] = [];
let start = 0;
while (start < text.length) {
chunks.push(text.slice(start, start + maxLen));
start += maxLen;
}
return chunks;
}
3.4.2 向量检索接口
// src/modules/kb/kb.service.ts (补充)
import { DataSource } from 'typeorm';
@Injectable()
export class KbService {
constructor(
// ...前略
private readonly dataSource: DataSource,
) {}
// 查询与问题最相关的门店知识片段
async searchRelevant(storeId: number, query: string, topK = 5) {
const queryEmb = await this.generateEmbedding(query);
const result = await this.dataSource.query(
`
SELECT d.id, d.title, d.content, d.doc_type,
1 - (e.embedding <=> $1::vector) AS score
FROM kb_embeddings e
JOIN kb_documents d ON e.kb_doc_id = d.id
WHERE d.store_id = $2
ORDER BY e.embedding <=> $1::vector
LIMIT $3
`,
[queryEmb, storeId, topK],
);
return result;
}
}
在智能体中将 searchRelevant 作为一种“隐式工具”,在构造 Prompt 时把检索出的内容一起塞到系统 / 上下文中,使回答更贴合门店自身项目与话术。
四、前端关键实现(H5/小程序 Chat UI)
这一部分以 React H5 为例,展示如何将后端智能体能力以“聊天窗口 + 预约引导 UI”形式呈现给用户。
4.1 Chat 组件基本结构
// src/components/ChatWidget.tsx
import React, { useState, useEffect } from 'react';
import { sendMessageToAgent } from '../api/agent';
type Message = {
id: string;
role: 'user' | 'assistant';
content: string;
};
export const ChatWidget: React.FC<{ customerId: number }> = ({ customerId }) => {
const [messages, setMessages] = useState<Message[]>([
{
id: 'welcome',
role: 'assistant',
content: '你好呀,我是你的专属智能美容顾问~可以简单和我说说你的肤质和困扰吗?😊',
},
]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const handleSend = async () => {
if (!input.trim() || loading) return;
const userMsg: Message = {
id: Date.now().toString(),
role: 'user',
content: input.trim(),
};
setMessages((prev) => [...prev, userMsg]);
setInput('');
setLoading(true);
try {
const resp = await sendMessageToAgent({
customerId,
channel: 'h5',
message: userMsg.content,
});
const aiMsg: Message = {
id: Date.now().toString() + '-ai',
role: 'assistant',
content: resp.reply,
};
setMessages((prev) => [...prev, aiMsg]);
} finally {
setLoading(false);
}
};
return (
<div className="chat-widget">
<div className="chat-messages">
{messages.map((m) => (
<div key={m.id} className={`msg msg-${m.role}`}>
<div className="msg-bubble">{m.content}</div>
</div>
))}
{loading && (
<div className="msg msg-assistant">
<div className="msg-bubble typing">正在为你分析中...</div>
</div>
)}
</div>
<div className="chat-input">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="描述一下你的皮肤情况、预算或想了解的项目~"
/>
<button onClick={handleSend} disabled={loading}>
发送
</button>
</div>
</div>
);
};
对应的 API 封装:
// src/api/agent.ts
import axios from 'axios';
export async function sendMessageToAgent(payload: {
customerId: number;
channel: string;
message: string;
}) {
const resp = await axios.post('/api/agent/chat', payload);
return resp.data;
}
五、API 接口设计(BFF 层)
这一部分展示对前端暴露的关键 REST 接口,包括聊天、预约确认等。
5.1 Chat 接口
// src/modules/agents/agents.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AgentsService } from './agents.service';
@Controller('agent')
export class AgentsController {
constructor(private readonly agentsService: AgentsService) {}
@Post('chat')
async chat(@Body() body: { customerId: number; channel: string; message: string }) {
const result = await this.agentsService.handleUserMessage({
customerId: body.customerId,
channel: body.channel,
userMessage: body.message,
});
return result;
}
}
5.2 店员工作台接口(生成话术示例)
// src/modules/agents/staff-assistant.service.ts
import { Injectable } from '@nestjs/common';
import { LlmService } from '../llm/llm.service';
import { CustomersService } from '../customers/customers.service';
@Injectable()
export class StaffAssistantService {
constructor(
private readonly llmService: LlmService,
private readonly customersService: CustomersService,
) {}
async generateSalesScript(params: {
customerId: number;
projectIds: number[];
}) {
const customer = await this.customersService.findById(params.customerId);
// 查询项目信息略...
const projectsDesc = '...';
const systemPrompt = `
你是一名经验丰富的美业门店金牌顾问,现在你的任务是:
- 为店员生成销售话术,用于向${customer.name}介绍项目;
- 话术要自然、真诚,不夸大效果,要根据客户的肤质标签、年龄和价格敏感度进行调整;
- 如果有多个项目,帮店员设计“主推+附加项目”的组合策略。
`;
const userPrompt = `
客户信息:
- 性别:${customer.gender}
- 肤质:${customer.skin_type}
- 主要困扰:${customer.skin_issues}
- 标签:${(customer.tags || []).join(',')}
待推荐项目:
${projectsDesc}
请输出:
1)开场白
2)项目介绍话术(包含效果+适合人群+次数建议)
3)套餐/组合推荐话术
4)异议处理话术(如价格、效果担心等)
`;
const completion = await this.llmService.chatCompletion({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt },
],
});
return { script: completion.choices[0].message.content };
}
}
六、部署与优化策略
这一部分从部署架构、可用性、性能和成本角度给出实战建议。
6.1 部署基础(Docker + K8s/容器服务)
- 容器化:
- 为
backend、frontend、向量库(如 Milvus)分别构建 Docker 镜像。
- 为
- API 网关 & HTTPS:
- 使用 Nginx/Ingress 替前端与后端提供 HTTPS 和路径路由。
- 数据库与缓存:
- PostgreSQL 部署在云数据库或自建高可用集群。
- Redis 用于:
- 会话状态临时缓存;
- 对话结果缓存(prompt + user input → output);
- 队列(延迟任务、营销触达)。
6.2 性能优化
- 会话上下文截断:
- 仅保留最近 N 轮对话 + 关键摘要,减少 token 消耗和延迟。
- RAG 缩小检索范围:
- 根据门店 ID、项目类别先过滤,再做向量检索。
- 预计算与缓存:
- 常用问题(“敏感肌适合什么项目?”)可预生成高质量回答,接入缓存。
- 并发与限流:
- 对单客户/单 IP 的并发聊天数做限流,防止滥用。
- 对 LLM 调用设置熔断和退避重试。
6.3 可靠性与监控
- 日志与追踪:
- 将
conversations、messages、agent_actions与业务日志对齐,支持链路追踪。
- 将
- 指标监控:
- 关注:LLM 调用成功率、平均响应时延、RPS、token 消耗。
- AB 实验与策略优化:
- 对不同 Prompt、不同推荐策略进行 AB 实验,比较:
- 预约率;
- 到店率;
- 客单价;
- 复购率。
- 对不同 Prompt、不同推荐策略进行 AB 实验,比较:
七、完整系统工作流(从用户进线到成交)
这一部分通过“时序流程”来串联整个系统的工作机制。
7.1 用户侧咨询与推荐流程
[1] 用户进入门店小程序/H5 → 打开"智能顾问"
↓
[2] 前端创建/拉取 customerId (登录或匿名标识)
↓
[3] 用户输入皮肤问题
↓
[4] 前端调用 POST /api/agent/chat
↓
[5] 后端:
- 根据 customerId + channel 找/建 conversation
- 记录 user message
- 准备最近N条对话上下文
- 构造 system prompt (美容顾问角色 + 安全规范)
- 构建 tools 列表: recommend_projects, create_appointment, ...
↓
[6] 调用 LLM(chatCompletion + tools)
↓
[7] LLM 判断是否需要调用工具:
- 若需要: 返回 tool_calls,后端执行 ToolsService 相应方法
- 记录 agent_actions (包括 reasoning 和输入输出)
- 再次调用 LLM,总结工具结果 → 最终自然语言回复
- 若不需要: 直接返回自然语言回复
↓
[8] 后端存 assistant message,并把 reply 返回前端
↓
[9] 前端渲染回复,如含有推荐项目,则可解析成卡片形式展示
↓
[10] 若回复中包含“可立即为你预约”等 CTA:
- 前端展示“去预约”按钮
- 点击后进入预约确认 UI (选择时间/门店/项目)
- 点击确认 → 调用预约 API 或由智能体工具完成
7.2 预约与到店转化流程
[1] 用户在智能体引导下完成预约 (工具 create_appointment)
↓
[2] 预约写入 appointments 表,状态 pending
↓
[3] 工作流引擎监听预约事件 (如 MessageQueue)
↓
[4] 若门店采用人工确认:
- 推送到店员工作台 → 店员确认时间/资源 → 更新 status=confirmed
- 向用户发送确认通知 (模板消息/短信)
↓
[5] 到店前X小时:
- 工作流引擎触发提醒 (由运营智能体生成个性化提醒文案)
↓
[6] 到店服务完成 → 订单与项目核销记录写入 orders/order_items
↓
[7] 服务后Y天:
- 根据项目效果周期,工作流引擎触发“复购/维护”营销
- 由运营智能体生成个性化文案 → 通过企业微信/小程序消息触达
7.3 店员工作台 + 销售助理智能体流程
[1] 店员在PC/Pad 打开"智能销售助理"
↓
[2] 选择某位顾客 + 选中准备主推的项目/套餐
↓
[3] 前端调用 /api/staff/assistant/script
↓
[4] 后端:
- 查询顾客画像(customer表) + 历史消费(orders)
- 查询项目详情(projects)与门店SOP(kb_documents)
- 组合成 prompt → 调用 LLM
↓
[5] 返回销售话术建议:
- 开场/项目介绍/组合搭配/异议处理
↓
[6] 店员在实际沟通中参考或稍作修改使用
↓
[7] 若店员有新的有效话术,可提交回知识库,进入 KB 文档并自动切片入向量库
八、补充:安全合规与可拓展方向
8.1 安全与合规
- 用户隐私:
- 对手机号、敏感标签等字段做脱敏或加密存储;
- 数据最小化:仅获取完成推荐与预约所需信息。
- 医疗相关边界:
- 明确在 Prompt 中“非医疗、不做诊断,仅提供护理建议”;
- 不输出具体药品推荐。
8.2 可扩展方向
- 图像分析:接入皮肤照片上传与图像模型,自动识别痘痘/斑点/毛孔等。
- 多门店多品牌场景:增加品牌/连锁维度;智能体根据品牌定位调整语气与推荐策略。
- 自动训练与迭代:收集高转化对话,定期让运营智能体生成新 SOP/话术并人工审核入库。
九、总结
- 技术上,通过 NestJS + PostgreSQL + Redis + 向量库 + LLM API,可以构建一个面向美业门店的、真正可落地的多智能体系统,覆盖顾客咨询、项目推荐、预约、店员销售辅助、运营分析等全链路。
- 实现上,核心在于:合理的数据建模、稳健的会话与工具调用编排、贴合业务的 Prompt 与 RAG 方案,以及面向门店的可观测与可运营能力。
- 按上述架构与代码骨架,你可以在现有技术栈基础上快速搭建 MVP,并在真实门店环境中通过 AB 测试和数据反馈,逐步打磨出高转化的“智能美容顾问”与“销售助理”。
更多推荐



所有评论(0)