技术方案 v5

AI 智搜 · 意图识别与期数检索

用户输入 → 抽取检索字段 → 调用期数 API 返回卡片;字段不足或非目标意图时,引导至「全部」Tab 带关键词搜索。

← 交互原型

方案范围
用户输入 → 识别是否「报读/课程/期数/导师」相关 → 是:抽字段 → 查期数 HTTP → 返回期数卡片
→ 否,或字段不够:小Q 说明 +「去全部看看」标签(带 keyword 跳转全部 Tab 搜索)

1. 意图识别总流程

flowchart TD
  A[用户输入 query] --> B{AI智搜 开关开?}
  B -->|关| ALL1[全部 Tab 搜索 keyword]
  B -->|开| C[Dify 节点:意图 + 抽字段 JSON]
  C --> D{intent = course_enroll?}
  D -->|否| F[小Q:暂不支持此类智搜]
  F --> G[展示标签:去全部看看]
  G --> H[点击 → 全部 Tab + keyword]
  D -->|是| E{字段是否够查期数?}
  E -->|够| I[BFF 调期数 API]
  I --> J[SSE 导语 + 期数 cards]
  E -->|不够| K[小Q:请补充城市/课名等]
  K --> L{仍无法补齐?}
  L -->|是| G
  L -->|否| I
      

说明:

2. 检索字段定义

由产品与研发共同维护字段表。模型从用户输入中填充字段,BFF 据此拼装 HTTP 请求。

字段名含义用户可能说法是否必填查期数 API 参数
course_id / course_name 哪门课 智慧父母一阶、高EQ领袖营、工作坊 与 city / instructor 至少一项 course_idq=课名
city 哪个城市/站 广州、南宁站、深圳 同上 city=广州
instructor_name 哪位导师 总导师、6Q张三、授课导师 同上 instructor=
phase_no 第几期 第533期、533期 phase_no=533
time_window_days 时间范围 最近、一个月内、近期开课 否(默认 30) start_within_days=30
keyword 原文关键词 整句保留 是(用于去全部) 跳转全部 Tab 时用

2.1 「够查期数」的最低条件(BFF 规则)

满足任一即可发起期数 API:

  1. course_id 或能匹配到标准课名的 course_name
  2. city + 句子里出现「开课 / 期数 / 课程 / 报读」类动作词
  3. instructor_name + 动作词
  4. 多轮:history 里已有 course/city,当前句是「还有吗 / 南宁站 / 报读」等续聊

以上都不满足 → 不查 API → 走「去全部看看」。

3. 知识库与实体词典

重要区分:

词典类型示例条目用途
课程标准名 + 别名 「智慧父母一阶」← 一阶、父母课、WFM L1 course_namecourse_id
城市 / 站点 「南宁站」→ city=南宁 归一化 city
导师 「总导师」→ 业务约定是否指某人或仅标签 instructor_name
动作触发词(不进库也行,写 Prompt) 报读、报名、开课、期数、还有名额吗 intent=course_enroll

维护方式:后台配置表或 Excel 同步至 Dify 知识库;或由 BFF 先做实体链接再调用 LLM(推荐词典 + 抽槽结合)。

4. Dify 提示词规范

工作流包含两个 LLM 节点:① 意图识别与字段抽取(JSON)② 流式导语(禁止输出具体期号)。

4.1 节点 ① 系统 Prompt

你是 6Q APP 的搜索助手,只做一件事:判断用户是否在问「课程 / 报读 / 期数 / 导师」,并从句子中抽取检索字段。

## 输出要求
- 只输出 JSON,不要其他文字。
- 字段说明见下方 schema。
- 用户句子和 history 里已有的 course/city 要合并;「还有吗」「南宁站」等续聊要继承上文。
- 不要把具体期数、价格、名额写进 JSON(你没有实时数据)。

## 意图
- course_enroll:与报读、课程、期数、导师、开课、报名相关。
- other:商城、音频、视频、文章、盲盒、闲聊,或与课程无关的泛搜。

## 实体归一化(对照知识库/词典)
- 课名:映射到 knowledge 中的标准 course_name;匹配不到则 course_name 填用户原话,course_id 填 null。
- 城市:「南宁站」→ city: "南宁"
- 导师:填 instructor_name;匹配不到填 null。

## 何时标记 ready_for_api
- true:至少能确定 course_name/course_id、或 city+开课意图、或 instructor+开课意图,或 history 已具备可续聊条件。
- false:intent 是 other,或只有「6Q」「课程」等过于模糊的词。

## 输入
session_id: {{session_id}}
query: {{query}}
history: {{history}}

4.2 节点 ① 输出 JSON Schema

{
  "intent": "course_enroll | other",
  "ready_for_api": true,
  "keyword": "用户原 query,用于跳转全部 Tab",
  "slots": {
    "course_id": null,
    "course_name": "智慧父母一阶",
    "city": "广州",
    "instructor_name": null,
    "phase_no": null,
    "time_window_days": 30
  },
  "missing": [],
  "confidence": 0.9
}

missing:列出缺失字段,如 ["city"];APP 可提示补充信息,仍无法补齐则展示「去全部看看」。

4.3 节点 ② 导语 Prompt(查完 API 后再调)

根据已返回的 API 摘要写 1~2 句导语。
- 只能使用 API 里的课名、城市、条数。
- 禁止列举具体期号、日期、名额(这些在 cards 里展示)。
- 语气简短,6Q 品牌口吻。

5. BFF 期数检索

sequenceDiagram
  participant APP
  participant BFF
  participant Dify
  participant Dict as 实体词典
  participant API as 期数API

  APP->>BFF: query + sessionId + history
  BFF->>Dify: 节点① 意图+slots
  Dify-->>BFF: JSON
  alt intent=other 或 ready=false
    BFF-->>APP: 小Q文案 + redirect 全部(keyword)
  else ready=true
    BFF->>Dict: 课名/导师 → id 补全
    BFF->>API: GET /sessions?...
    API-->>BFF: sessions[]
    BFF->>Dify: 节点② 流式导语
    BFF-->>APP: SSE text + event:cards
  end
      
GET /api/sessions?course_id=xxx&city=广州&start_within_days=30&status=open
GET /api/sessions?instructor=张三&start_within_days=30
GET /api/courses/search?q=智慧父母一阶  → 再查 sessions

6. 意图不完整时的降级策略

场景AI 表现用户点击标签后
intent = other(盲盒、音频、恋爱咨询等) 小Q:暂不支持此类智能检索 全部 Tab,搜索框 = keyword
intent = course_enroll 但 ready=false(只说「6Q」「课程」) 小Q:请说明课名或城市;或去全部看看 同上
API 返回 0 条 小Q:暂无匹配期数,引导至全部搜索 同上
// BFF 返回给 APP(非 SSE 或 event:redirect)
{
  "tab": "all",
  "keyword": "6Q潮玩盲盒",
  "message": "小Q暂不支持其他类型的智能检索哦",
  "action": { "label": "去全部看看", "type": "open_all_search" }
}

7. 附录:course_enroll 常见说法与字段映射

用户说法应抽字段
智慧父母一阶course_name
广州最近有哪些课在开课city + time_window_days
总导师的课程有哪些instructor_name(或业务规则映射)
南宁站还有吗(有 history)继承 course + city=南宁
6Q潮玩盲盒intent=other → 全部

8. 相关文档

APP 侧需实现:AI 开关、期数卡片展示、小Q 提示及「去全部看看」带 keyword 跳转。