返回笔记首页

20.2 智能搜索 - 完整实现方案

主题配置

一、技术架构设计

1.1 整体架构

plain
前端层(Vue3)
  ├── 搜索组件
  ├── 结果展示
  └── 交互优化

AI 能力层
  ├── 语义理解(Claude API)
  ├── 向量检索(Embedding)
  └── 智能排序

数据层
  ├── 文档库
  ├── 向量数据库
  └── 搜索索引

1.2 核心功能模块

  • 语义搜索:理解用户意图而非简单关键词匹配
  • 搜索推荐:智能纠错、联想查询、相关推荐
  • 向量检索集成:基于语义相似度的深度检索

二、语义搜索实现

2.1 技术实现原理

传统搜索 vs 语义搜索

  • 传统:关键词精确匹配 → "苹果手机" 只匹配包含这4个字的文档
  • 语义:理解查询意图 → "苹果手机" 能匹配 "iPhone"、"iOS设备" 等相关内容
实现思路
  1. 用户输入查询词
  2. AI 理解用户意图,扩展查询范围
  3. 从数据库检索相关文档
  4. AI 对结果进行语义排序
  5. 展示最相关结果

2.2 完整代码实现

vue
<template>
  <div class="semantic-search-container">
    <h2>智能语义搜索</h2>

    <div class="search-box">
      <div class="search-input-wrapper">
        <input
          v-model="searchQuery"
          @keyup.enter="performSearch"
          placeholder="输入任何问题,AI 会理解你的意图..."
          class="search-input"
        />
        <button @click="performSearch" class="search-btn" :disabled="searching">
          {{ searching ? '搜索中...' : '搜索' }}
        </button>
      </div>

      <div v-if="intentAnalysis" class="intent-display">
        <span class="intent-label">AI 理解:</span>
        <span class="intent-text">{{ intentAnalysis }}</span>
      </div>
    </div>

    <!-- 搜索建议 -->
    <div v-if="suggestions.length > 0" class="suggestions-panel">
      <div class="suggestions-title">你可能想搜索:</div>
      <div class="suggestions-list">
        <span
          v-for="(suggestion, index) in suggestions"
          :key="index"
          class="suggestion-item"
          @click="applySuggestion(suggestion)"
        >
          {{ suggestion }}
        </span>
      </div>
    </div>

    <!-- 搜索结果 -->
    <div v-if="searchResults.length > 0" class="results-section">
      <div class="results-header">
        <span class="results-count">找到 {{ searchResults.length }} 条相关结果</span>
        <span class="results-time">用时 {{ searchTime }}ms</span>
      </div>

      <div
        v-for="(result, index) in searchResults"
        :key="index"
        class="result-item"
      >
        <div class="result-title">{{ result.title }}</div>
        <div class="result-content">{{ result.content }}</div>
        <div class="result-meta">
          <span class="result-score">相关度:{{ result.score }}%</span>
          <span class="result-category">分类:{{ result.category }}</span>
          <span class="result-date">{{ result.date }}</span>
        </div>
        <div v-if="result.reason" class="result-reason">
          <span class="reason-label">推荐理由:</span>
          <span class="reason-text">{{ result.reason }}</span>
        </div>
      </div>
    </div>

    <!-- 空结果提示 -->
    <div v-if="searched && searchResults.length === 0" class="empty-state">
      <div class="empty-icon">🔍</div>
      <div class="empty-text">未找到相关结果</div>
      <div class="empty-suggestions">
        <p>试试这些搜索:</p>
        <button
          v-for="(example, index) in exampleQueries"
          :key="index"
          @click="applySuggestion(example)"
          class="example-query"
        >
          {{ example }}
        </button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const searchQuery = ref('')
const searching = ref(false)
const searched = ref(false)
const intentAnalysis = ref('')
const suggestions = ref([])
const searchResults = ref([])
const searchTime = ref(0)

// 示例查询
const exampleQueries = [
  '如何提高工作效率',
  '团队协作最佳实践',
  'Vue3 性能优化技巧',
  '产品需求管理方法'
]

// 模拟文档库
const documentDatabase = [
  {
    id: 1,
    title: 'Vue3 Composition API 最佳实践',
    content: '本文介绍了 Vue3 Composition API 的核心概念和使用技巧,包括响应式数据、生命周期钩子、组合式函数等内容。',
    category: '前端技术',
    date: '2024-11-15',
    tags: ['Vue3', 'JavaScript', '前端开发']
  },
  {
    id: 2,
    title: '团队协作工具选型指南',
    content: '选择合适的协作工具能显著提升团队效率。本文对比了 Slack、企业微信、钉钉等主流工具的优缺点。',
    category: '团队管理',
    date: '2024-11-10',
    tags: ['协作', '工具', '效率']
  },
  {
    id: 3,
    title: '敏捷开发实践心得',
    content: '敏捷开发强调快速迭代和持续交付。本文分享我们团队实践 Scrum 的经验和踩过的坑。',
    category: '项目管理',
    date: '2024-11-05',
    tags: ['敏捷', 'Scrum', '项目管理']
  },
  {
    id: 4,
    title: 'React vs Vue 性能对比分析',
    content: '从渲染性能、包体积、开发体验等维度对比两大前端框架,帮助团队做出技术选型决策。',
    category: '技术选型',
    date: '2024-10-28',
    tags: ['React', 'Vue', '性能优化']
  },
  {
    id: 5,
    title: '产品需求管理的艺术',
    content: '如何收集、评估、排期需求?本文介绍了 RICE 模型和 Kano 模型在需求管理中的应用。',
    category: '产品管理',
    date: '2024-10-20',
    tags: ['需求管理', '产品', '优先级']
  },
  {
    id: 6,
    title: 'TypeScript 进阶技巧',
    content: '深入探讨 TypeScript 的高级类型、泛型约束、装饰器等特性,提升代码质量。',
    category: '前端技术',
    date: '2024-10-15',
    tags: ['TypeScript', '类型系统', '代码质量']
  },
  {
    id: 7,
    title: '代码审查最佳实践',
    content: 'Code Review 不仅是发现 bug,更是知识分享和团队成长的机会。本文分享有效的审查技巧。',
    category: '工程实践',
    date: '2024-10-08',
    tags: ['Code Review', '代码质量', '团队协作']
  },
  {
    id: 8,
    title: 'AI 辅助编程实战',
    content: '探索如何用 GitHub Copilot 和 ChatGPT 提升编程效率,包括提示词技巧和最佳实践。',
    category: 'AI 应用',
    date: '2024-09-30',
    tags: ['AI', '编程效率', 'Copilot']
  }
]

// 执行搜索
const performSearch = async () => {
  if (!searchQuery.value.trim()) return

  searching.value = true
  searched.value = true
  intentAnalysis.value = ''
  searchResults.value = []
  const startTime = Date.now()

  try {
    // 第一步:理解用户意图
    await analyzeIntent()

    // 第二步:智能检索
    await intelligentSearch()

    searchTime.value = Date.now() - startTime
  } catch (error) {
    console.error('搜索失败:', error)
    alert('搜索服务暂时不可用')
  } finally {
    searching.value = false
  }
}

// 分析用户意图
const analyzeIntent = async () => {
  try {
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-20250514',
        max_tokens: 800,
        messages: [{
          role: 'user',
          content: `用户输入了搜索查询:「${searchQuery.value}」

请分析用户的搜索意图,并提供:
1. 用户真正想了解什么(简短描述)
2. 相关的扩展关键词(3-5个)
3. 可能感兴趣的相关主题(2-3个)

返回 JSON 格式:
{
  "intent": "用户意图描述",
  "keywords": ["关键词1", "关键词2", "关键词3"],
  "relatedTopics": ["主题1", "主题2"]
}

只返回JSON,不要其他内容。`
        }]
      })
    })

    const data = await response.json()
    const content = data.content[0].text
    const jsonMatch = content.match(/\{[\s\S]*\}/)

    if (jsonMatch) {
      const analysis = JSON.parse(jsonMatch[0])
      intentAnalysis.value = analysis.intent

      // 生成搜索建议
      suggestions.value = analysis.relatedTopics || []
    }
  } catch (error) {
    console.error('意图分析失败:', error)
    intentAnalysis.value = '正在搜索...'
  }
}

// 智能检索
const intelligentSearch = async () => {
  try {
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-20250514',
        max_tokens: 2000,
        messages: [{
          role: 'user',
          content: `用户搜索:「${searchQuery.value}」

文档库:
${JSON.stringify(documentDatabase, null, 2)}

请执行语义搜索:
1. 理解用户查询意图
2. 找出最相关的文档(按相关度排序)
3. 为每个结果计算相关度分数(0-100)
4. 说明推荐理由

返回 JSON 数组,格式:
[
  {
    "id": 1,
    "title": "文档标题",
    "content": "文档摘要(保留原内容前100字)",
    "category": "分类",
    "date": "日期",
    "score": 95,
    "reason": "推荐理由"
  }
]

只返回前5个最相关的结果。只返回JSON数组,不要其他内容。`
        }]
      })
    })

    const data = await response.json()
    const content = data.content[0].text
    const jsonMatch = content.match(/\[[\s\S]*\]/)

    if (jsonMatch) {
      searchResults.value = JSON.parse(jsonMatch[0])
    }
  } catch (error) {
    console.error('智能检索失败:', error)
  }
}

// 应用搜索建议
const applySuggestion = (suggestion) => {
  searchQuery.value = suggestion
  performSearch()
}
</script>

<style scoped>
.semantic-search-container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 40px 20px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

h2 {
  text-align: center;
  color: #2c3e50;
  font-size: 32px;
  margin-bottom: 40px;
}

.search-box {
  background: white;
  padding: 30px;
  border-radius: 16px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
  margin-bottom: 30px;
}

.search-input-wrapper {
  display: flex;
  gap: 12px;
}

.search-input {
  flex: 1;
  padding: 16px 20px;
  border: 2px solid #e0e0e0;
  border-radius: 12px;
  font-size: 16px;
  transition: all 0.3s;
}

.search-input:focus {
  outline: none;
  border-color: #409eff;
  box-shadow: 0 0 0 4px rgba(64, 158, 255, 0.1);
}

.search-btn {
  padding: 16px 32px;
  background: #409eff;
  color: white;
  border: none;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s;
  white-space: nowrap;
}

.search-btn:hover:not(:disabled) {
  background: #66b1ff;
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
}

.search-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.intent-display {
  margin-top: 15px;
  padding: 12px 16px;
  background: #f0f9ff;
  border-left: 3px solid #409eff;
  border-radius: 6px;
  font-size: 14px;
}

.intent-label {
  color: #409eff;
  font-weight: 600;
  margin-right: 8px;
}

.intent-text {
  color: #606266;
}

.suggestions-panel {
  background: white;
  padding: 20px;
  border-radius: 12px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
  margin-bottom: 30px;
}

.suggestions-title {
  color: #909399;
  font-size: 13px;
  margin-bottom: 12px;
}

.suggestions-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.suggestion-item {
  padding: 8px 16px;
  background: #f5f7fa;
  color: #606266;
  border-radius: 20px;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s;
}

.suggestion-item:hover {
  background: #409eff;
  color: white;
  transform: translateY(-2px);
}

.results-section {
  background: white;
  border-radius: 12px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
  overflow: hidden;
}

.results-header {
  display: flex;
  justify-content: space-between;
  padding: 20px 24px;
  background: #f5f7fa;
  border-bottom: 1px solid #e4e7ed;
}

.results-count {
  color: #303133;
  font-weight: 600;
  font-size: 15px;
}

.results-time {
  color: #909399;
  font-size: 13px;
}

.result-item {
  padding: 24px;
  border-bottom: 1px solid #f0f0f0;
  transition: background 0.2s;
}

.result-item:hover {
  background: #fafafa;
}

.result-item:last-child {
  border-bottom: none;
}

.result-title {
  color: #303133;
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 12px;
  cursor: pointer;
}

.result-title:hover {
  color: #409eff;
}

.result-content {
  color: #606266;
  font-size: 14px;
  line-height: 1.8;
  margin-bottom: 12px;
}

.result-meta {
  display: flex;
  gap: 20px;
  font-size: 13px;
  color: #909399;
}

.result-score {
  color: #67c23a;
  font-weight: 600;
}

.result-reason {
  margin-top: 12px;
  padding: 10px;
  background: #fef0f0;
  border-left: 3px solid #f56c6c;
  border-radius: 4px;
  font-size: 13px;
}

.reason-label {
  color: #f56c6c;
  font-weight: 600;
  margin-right: 6px;
}

.reason-text {
  color: #606266;
}

.empty-state {
  text-align: center;
  padding: 60px 20px;
  background: white;
  border-radius: 12px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
}

.empty-icon {
  font-size: 64px;
  margin-bottom: 20px;
}

.empty-text {
  color: #909399;
  font-size: 18px;
  margin-bottom: 30px;
}

.empty-suggestions {
  margin-top: 20px;
}

.empty-suggestions p {
  color: #606266;
  font-size: 14px;
  margin-bottom: 15px;
}

.example-query {
  margin: 5px;
  padding: 10px 20px;
  background: #f5f7fa;
  color: #606266;
  border: 1px solid #dcdfe6;
  border-radius: 20px;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s;
}

.example-query:hover {
  background: #409eff;
  color: white;
  border-color: #409eff;
}
</style>

三、搜索推荐实现

3.1 技术实现原理

核心功能

  1. 智能纠错:自动识别输入错误并提供正确建议
  2. 联想查询:根据输入实时推荐相关搜索
  3. 相关推荐:基于当前搜索推荐相似内容

3.2 完整代码实现

vue
<template>
  <div class="search-recommendation-container">
    <h2>智能搜索推荐</h2>

    <div class="search-section">
      <div class="search-input-group">
        <input
          v-model="searchInput"
          @input="handleInput"
          @keyup.enter="search"
          placeholder="输入关键词..."
          class="search-input"
        />
        <button @click="search" class="search-btn">搜索</button>
      </div>

      <!-- 拼写纠错提示 -->
      <div v-if="spellCorrection" class="correction-tip">
        <span class="tip-icon">💡</span>
        <span class="tip-text">你是不是要搜索:</span>
        <span class="correction-word" @click="applyCorrection">
          {{ spellCorrection }}
        </span>
      </div>

      <!-- 实时联想推荐 -->
      <div v-if="autoSuggestions.length > 0" class="auto-suggestions">
        <div
          v-for="(suggestion, index) in autoSuggestions"
          :key="index"
          class="suggestion-item"
          @click="selectSuggestion(suggestion)"
        >
          <span class="suggestion-icon">🔍</span>
          <span class="suggestion-text" v-html="highlightMatch(suggestion)"></span>
        </div>
      </div>
    </div>

    <!-- 热门搜索 -->
    <div class="hot-searches">
      <div class="section-title">热门搜索</div>
      <div class="hot-list">
        <span
          v-for="(hot, index) in hotSearches"
          :key="index"
          class="hot-item"
          @click="selectSuggestion(hot.keyword)"
        >
          <span class="hot-rank" :class="{ 'top-three': index < 3 }">
            {{ index + 1 }}
          </span>
          <span class="hot-keyword">{{ hot.keyword }}</span>
          <span class="hot-trend" :class="hot.trend">
            {{ hot.trend === 'up' ? '↑' : hot.trend === 'down' ? '↓' : '−' }}
          </span>
        </span>
      </div>
    </div>

    <!-- 搜索历史 -->
    <div v-if="searchHistory.length > 0" class="search-history">
      <div class="section-header">
        <span class="section-title">搜索历史</span>
        <span class="clear-history" @click="clearHistory">清空</span>
      </div>
      <div class="history-list">
        <span
          v-for="(history, index) in searchHistory"
          :key="index"
          class="history-item"
          @click="selectSuggestion(history)"
        >
          <span class="history-icon">🕐</span>
          {{ history }}
          <span class="history-delete" @click.stop="deleteHistory(index)">×</span>
        </span>
      </div>
    </div>

    <!-- 相关推荐 -->
    <div v-if="relatedSearches.length > 0" class="related-searches">
      <div class="section-title">相关搜索</div>
      <div class="related-list">
        <span
          v-for="(related, index) in relatedSearches"
          :key="index"
          class="related-item"
          @click="selectSuggestion(related)"
        >
          {{ related }}
        </span>
      </div>
    </div>

    <!-- 智能推荐理由 -->
    <div v-if="recommendationReason" class="recommendation-reason">
      <div class="reason-title">推荐理由</div>
      <div class="reason-content">{{ recommendationReason }}</div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const searchInput = ref('')
const spellCorrection = ref('')
const autoSuggestions = ref([])
const searchHistory = ref([])
const relatedSearches = ref([])
const recommendationReason = ref('')

let inputTimer = null

// 热门搜索数据
const hotSearches = reactive([
  { keyword: 'Vue3 教程', trend: 'up' },
  { keyword: 'React 最佳实践', trend: 'up' },
  { keyword: 'TypeScript 进阶', trend: 'stable' },
  { keyword: 'Node.js 性能优化', trend: 'down' },
  { keyword: '前端工程化', trend: 'up' },
  { keyword: 'Webpack 配置', trend: 'stable' },
  { keyword: 'CSS 动画技巧', trend: 'up' },
  { keyword: 'Git 工作流', trend: 'stable' }
])

// 处理输入
const handleInput = () => {
  clearTimeout(inputTimer)

  if (!searchInput.value.trim()) {
    autoSuggestions.value = []
    spellCorrection.value = ''
    return
  }

  // 防抖处理
  inputTimer = setTimeout(() => {
    generateSuggestions()
    checkSpelling()
  }, 300)
}

// 生成联想推荐
const generateSuggestions = async () => {
  try {
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-20250514',
        max_tokens: 600,
        messages: [{
          role: 'user',
          content: `用户正在输入搜索词:「${searchInput.value}」

基于这个不完整的输入,生成 5 个可能的搜索建议。
建议应该:
1. 补全用户可能想输入的内容
2. 相关且实用
3. 覆盖不同的搜索意图

返回 JSON 数组:
["建议1", "建议2", "建议3", "建议4", "建议5"]

只返回JSON数组,不要其他内容。`
        }]
      })
    })

    const data = await response.json()
    const content = data.content[0].text
    const jsonMatch = content.match(/\[[\s\S]*?\]/)

    if (jsonMatch) {
      autoSuggestions.value = JSON.parse(jsonMatch[0])
    }
  } catch (error) {
    console.error('生成建议失败:', error)
  }
}

// 拼写检查
const checkSpelling = async () => {
  try {
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-20250514',
        max_tokens: 300,
        messages: [{
          role: 'user',
          content: `检查这个搜索词是否有拼写错误:「${searchInput.value}」

如果有错误,返回正确的拼写。如果没有错误,返回 null。

返回 JSON:
{"correction": "正确拼写" 或 null}

只返回JSON,不要其他内容。`
        }]
      })
    })

    const data = await response.json()
    const content = data.content[0].text
    const jsonMatch = content.match(/\{[\s\S]*?\}/)

    if (jsonMatch) {
      const result = JSON.parse(jsonMatch[0])
      spellCorrection.value = result.correction || ''
    }
  } catch (error) {
    console.error('拼写检查失败:', error)
  }
}

// 执行搜索
const search = async () => {
  if (!searchInput.value.trim()) return

  // 添加到搜索历史
  if (!searchHistory.value.includes(searchInput.value)) {
    searchHistory.value.unshift(searchInput.value)
    if (searchHistory.value.length > 10) {
      searchHistory.value.pop()
    }
  }

  // 生成相关搜索
  await generateRelatedSearches()

  console.log('搜索:', searchInput.value)
}

// 生成相关搜索
const generateRelatedSearches = async () => {
  try {
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-20250514',
        max_tokens: 800,
        messages: [{
          role: 'user',
          content: `用户搜索了:「${searchInput.value}」

基于这个搜索,生成 6 个相关的搜索建议,并说明推荐理由。

返回 JSON:
{
  "related": ["相关搜索1", "相关搜索2", ...],
  "reason": "为什么推荐这些搜索的简短说明"
}

只返回JSON,不要其他内容。`
        }]
      })
    })

    const data = await response.json()
    const content = data.content[0].text
    const jsonMatch = content.match(/\{[\s\S]*?\}/)

    if (jsonMatch) {
      const result = JSON.parse(jsonMatch[0])
      relatedSearches.value = result.related || []
      recommendationReason.value = result.reason || ''
    }
  } catch (error) {
    console.error('生成相关搜索失败:', error)
  }
}

// 应用拼写纠正
const applyCorrection = () => {
  searchInput.value = spellCorrection.value
  spellCorrection.value = ''
  search()
}

// 选择建议
const selectSuggestion = (suggestion) => {
  searchInput.value = suggestion
  autoSuggestions.value = []
  search()
}

// 高亮匹配部分
const highlightMatch = (text) => {
  if (!searchInput.value) return text
  const regex = new RegExp(`(${searchInput.value})`, 'gi')
  return text.replace(regex, '<strong>$1</strong>')
}

// 清空搜索历史
const clearHistory = () => {
  searchHistory.value = []
}

// 删除单条历史
const deleteHistory = (index) => {
  searchHistory.value.splice(index, 1)
}
</script>

<style scoped>
.search-recommendation-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 40px 20px;
}

h2 {
  text-align: center;
  color: #2c3e50;
  font-size: 32px;
  margin-bottom: 40px;
}

.search-section {
  position: relative;
  background: white;
  padding: 30px;
  border-radius: 16px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
  margin-bottom: 30px;
}

.search-input-group {
  display: flex;
  gap: 12px;
}

.search-input {
  flex: 1;
  padding: 16px 20px;
  border: 2px solid #e0e0e0;
  border-radius: 12px;
  font-size: 16px;
  transition: all 0.3s;
}

.search-input:focus {
  outline: none;
  border-color: #409eff;
  box-shadow: 0 0 0 4px rgba(64, 158, 255, 0.1);
}

.search-btn {
  padding: 16px 32px;
  background: #409eff;
  color: white;
  border: none;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s;
}

.search-btn:hover {
  background: #66b1ff;
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
}

.correction-tip {
  margin-top: 15px;
  padding: 12px;
  background: #fff7e6;
  border-left: 3px solid #ff9800;
  border-radius: 6px;
  font-size: 14px;
}

.tip-icon {
  margin-right: 8px;
}

.tip-text {
  color: #606266;
  margin-right: 8px;
}

.correction-word {
  color: #409eff;
  font-weight: 600;
  cursor: pointer;
  text-decoration: underline;
}

.correction-word:hover {
  color: #66b1ff;
}

.auto-suggestions {
  position: absolute;
  top: 100%;
  left: 30px;
  right: 30px;
  margin-top: 8px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.15);
  z-index: 100;
  max-height: 400px;
  overflow-y: auto;
}

.suggestion-item {
  padding: 12px 16px;
  cursor: pointer;
  transition: background 0.2s;
  display: flex;
  align-items: center;
}

.suggestion-item:hover {
  background: #f5f7fa;
}

.suggestion-icon {
  margin-right: 12px;
  opacity: 0.5;
}

.suggestion-text {
  color: #606266;
  font-size: 14px;
}

.suggestion-text strong {
  color: #409eff;
  font-weight: 600;
}

.hot-searches,
.search-history,
.related-searches {
  background: white;
  padding: 24px;
  border-radius: 12px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
  margin-bottom: 20px;
}

.section-title {
  color: #303133;
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 16px;
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.clear-history {
  color: #909399;
  font-size: 14px;
  cursor: pointer;
}

.clear-history:hover {
  color: #409eff;
}

.hot-list,
.history-list,
.related-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.hot-item {
  display: flex;
  align-items: center;
  padding: 8px 14px;
  background: #f5f7fa;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.2s;
}

.hot-item:hover {
  background: #409eff;
  color: white;
  transform: translateY(-2px);
}

.hot-rank {
  width: 20px;
  height: 20px;
  line-height: 20px;
  text-align: center;
  background: #909399;
  color: white;
  border-radius: 50%;
  font-size: 12px;
  margin-right: 8px;
}

.hot-rank.top-three {
  background: #f56c6c;
  font-weight: 600;
}

.hot-keyword {
  font-size: 14px;
  margin-right: 6px;
}

.hot-trend {
  font-size: 16px;
  font-weight: 600;
}

.hot-trend.up {
  color: #f56c6c;
}

.hot-trend.down {
  color: #67c23a;
}

.hot-trend.stable {
  color: #909399;
}

.history-item,
.related-item {
  padding: 8px 16px;
  background: #f5f7fa;
  border-radius: 20px;
  font-size: 14px;
  color: #606266;
  cursor: pointer;
  transition: all 0.2s;
  display: flex;
  align-items: center;
}

.history-item:hover,
.related-item:hover {
  background: #409eff;
  color: white;
}

.history-icon {
  margin-right: 6px;
  opacity: 0.7;
}

.history-delete {
  margin-left: 8px;
  font-size: 18px;
  font-weight: bold;
  opacity: 0.5;
}

.history-delete:hover {
  opacity: 1;
}

.recommendation-reason {
  background: #ecf5ff;
  padding: 20px;
  border-radius: 12px;
  border-left: 4px solid #409eff;
}

.reason-title {
  color: #409eff;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 10px;
}

.reason-content {
  color: #606266;
  font-size: 14px;
  line-height: 1.8;
}
</style>

四、向量检索集成实现

4.1 技术实现原理

向量检索原理

  1. 将文本转为向量(Embedding)
  2. 计算向量间的相似度
  3. 返回最相似的结果
实现方案
  • 使用简化的模拟向量相似度计算
  • 真实场景需接入向量数据库(如 Pinecone、Milvus)

4.2 完整代码实现

vue
<template>
  <div class="vector-search-container">
    <h2>向量语义检索</h2>

    <div class="intro-section">
      <p>基于语义相似度的深度检索,理解内容本质而非表面文字</p>
    </div>

    <div class="search-panel">
      <textarea
        v-model="searchQuery"
        placeholder="输入你的问题或描述,AI 会找到语义最相关的内容..."
        rows="4"
        class="query-input"
      ></textarea>
      <button @click="performVectorSearch" class="search-btn" :disabled="searching">
        {{ searching ? '检索中...' : '语义检索' }}
      </button>
    </div>

    <!-- 检索结果 -->
    <div v-if="vectorResults.length > 0" class="results-container">
      <div class="results-header">
        <span class="header-title">找到 {{ vectorResults.length }} 条语义相关结果</span>
        <span class="header-mode">基于向量相似度排序</span>
      </div>

      <div
        v-for="(result, index) in vectorResults"
        :key="index"
        class="result-card"
      >
        <div class="card-header">
          <span class="result-rank">#{{ index + 1 }}</span>
          <span class="similarity-score">
            相似度:{{ result.similarity }}%
          </span>
        </div>
        <div class="result-title">{{ result.title }}</div>
        <div class="result-content">{{ result.content }}</div>
        <div class="result-meta">
          <span class="meta-item">{{ result.category }}</span>
          <span class="meta-item">{{ result.date }}</span>
        </div>
        <div class="similarity-reason">
          <strong>匹配原因:</strong>{{ result.matchReason }}
        </div>
      </div>
    </div>

    <!-- 对比传统搜索 -->
    <div v-if="comparisonData" class="comparison-section">
      <h3>传统搜索 vs 向量检索对比</h3>
      <div class="comparison-grid">
        <div class="comparison-item">
          <div class="comparison-title">传统关键词搜索</div>
          <div class="comparison-content">{{ comparisonData.traditional }}</div>
        </div>
        <div class="comparison-item highlight">
          <div class="comparison-title">向量语义检索</div>
          <div class="comparison-content">{{ comparisonData.vector }}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const searchQuery = ref('')
const searching = ref(false)
const vectorResults = ref([])
const comparisonData = ref(null)

// 模拟文档向量库
const documentVectors = [
  {
    id: 1,
    title: '如何提升团队协作效率',
    content: '团队协作的核心在于沟通透明和目标一致。建议使用敏捷方法论,每日站会同步进度,使用看板管理任务。',
    category: '团队管理',
    date: '2024-11-20',
    keywords: ['团队', '协作', '效率', '敏捷', '沟通']
  },
  {
    id: 2,
    title: 'Vue3 性能优化实战',
    content: 'Vue3 通过 Proxy 实现响应式,性能比 Vue2 提升 2 倍。优化要点:虚拟滚动、懒加载、Tree-shaking。',
    category: '前端技术',
    date: '2024-11-18',
    keywords: ['Vue3', '性能', '优化', '响应式', '前端']
  },
  {
    id: 3,
    title: '产品需求优先级评估方法',
    content: '使用 RICE 模型评估需求:Reach(触达)、Impact(影响)、Confidence(信心)、Effort(工作量)。',
    category: '产品管理',
    date: '2024-11-15',
    keywords: ['产品', '需求', '优先级', 'RICE', '评估']
  },
  {
    id: 4,
    title: '代码审查最佳实践',
    content: 'Code Review 不只是找 bug,更是知识分享。建议:小批量提交、自动化检查、友好的评论语气。',
    category: '工程实践',
    date: '2024-11-12',
    keywords: ['代码', '审查', 'Review', '质量', '协作']
  },
  {
    id: 5,
    title: '敏捷开发实施指南',
    content: 'Scrum 是最流行的敏捷框架。核心角色:Product Owner、Scrum Master、开发团队。关键仪式:Sprint Planning、Daily Standup、Review、Retrospective。',
    category: '项目管理',
    date: '2024-11-10',
    keywords: ['敏捷', 'Scrum', '迭代', '项目', '管理']
  }
]

// 执行向量检索
const performVectorSearch = async () => {
  if (!searchQuery.value.trim()) return

  searching.value = true
  vectorResults.value = []
  comparisonData.value = null

  try {
    // 调用 AI 进行语义匹配
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-20250514',
        max_tokens: 2000,
        messages: [{
          role: 'user',
          content: `用户查询:「${searchQuery.value}」

文档库:
${JSON.stringify(documentVectors, null, 2)}

请基于语义相似度进行检索:
1. 理解用户查询的深层语义
2. 计算每个文档与查询的语义相似度(0-100分)
3. 按相似度排序返回最相关的 5 个结果
4. 说明为什么这个文档与查询语义相关

返回 JSON 数组:
[
  {
    "id": 1,
    "title": "标题",
    "content": "内容",
    "category": "分类",
    "date": "日期",
    "similarity": 95,
    "matchReason": "匹配原因:为什么这个文档与查询语义相关"
  }
]

另外,对比传统关键词搜索和向量语义检索的差异:
{
  "results": [...],
  "comparison": {
    "traditional": "传统搜索会找到什么",
    "vector": "向量检索能额外发现什么"
  }
}

只返回JSON,不要其他内容。`
        }]
      })
    })

    const data = await response.json()
    const content = data.content[0].text
    const jsonMatch = content.match(/\{[\s\S]*\}/)

    if (jsonMatch) {
      const result = JSON.parse(jsonMatch[0])
      vectorResults.value = result.results || []
      comparisonData.value = result.comparison || null
    }
  } catch (error) {
    console.error('向量检索失败:', error)
    alert('检索服务暂时不可用')
  } finally {
    searching.value = false
  }
}
</script>

<style scoped>
.vector-search-container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 40px 20px;
}

h2 {
  text-align: center;
  color: #2c3e50;
  font-size: 32px;
  margin-bottom: 20px;
}

.intro-section {
  text-align: center;
  color: #606266;
  font-size: 15px;
  margin-bottom: 40px;
}

.search-panel {
  background: white;
  padding: 30px;
  border-radius: 16px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
  margin-bottom: 40px;
}

.query-input {
  width: 100%;
  padding: 16px;
  border: 2px solid #e0e0e0;
  border-radius: 12px;
  font-size: 15px;
  font-family: inherit;
  resize: vertical;
  transition: all 0.3s;
  box-sizing: border-box;
  margin-bottom: 16px;
}

.query-input:focus {
  outline: none;
  border-color: #409eff;
  box-shadow: 0 0 0 4px rgba(64, 158, 255, 0.1);
}

.search-btn {
  width: 100%;
  padding: 16px;
  background: #409eff;
  color: white;
  border: none;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s;
}

.search-btn:hover:not(:disabled) {
  background: #66b1ff;
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
}

.search-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.results-container {
  background: white;
  border-radius: 16px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
  overflow: hidden;
  margin-bottom: 40px;
}

.results-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 24px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

.header-title {
  font-size: 16px;
  font-weight: 600;
}

.header-mode {
  font-size: 13px;
  opacity: 0.9;
}

.result-card {
  padding: 24px;
  border-bottom: 1px solid #f0f0f0;
  transition: background 0.2s;
}

.result-card:hover {
  background: #fafafa;
}

.result-card:last-child {
  border-bottom: none;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.result-rank {
  display: inline-block;
  width: 32px;
  height: 32px;
  line-height: 32px;
  text-align: center;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border-radius: 50%;
  font-weight: 600;
  font-size: 14px;
}

.similarity-score {
  padding: 6px 12px;
  background: #e8f4fd;
  color: #409eff;
  border-radius: 20px;
  font-size: 13px;
  font-weight: 600;
}

.result-title {
  color: #303133;
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 12px;
  line-height: 1.4;
}

.result-content {
  color: #606266;
  font-size: 14px;
  line-height: 1.8;
  margin-bottom: 12px;
}

.result-meta {
  display: flex;
  gap: 16px;
  margin-bottom: 12px;
}

.meta-item {
  font-size: 13px;
  color: #909399;
}

.similarity-reason {
  padding: 12px;
  background: #f0f9ff;
  border-left: 3px solid #409eff;
  border-radius: 4px;
  font-size: 13px;
  color: #606266;
  line-height: 1.6;
}

.similarity-reason strong {
  color: #409eff;
  margin-right: 6px;
}

.comparison-section {
  background: white;
  padding: 30px;
  border-radius: 16px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
}

.comparison-section h3 {
  color: #2c3e50;
  font-size: 20px;
  margin-bottom: 24px;
  text-align: center;
}

.comparison-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.comparison-item {
  padding: 20px;
  background: #f5f7fa;
  border-radius: 12px;
  border: 2px solid #e4e7ed;
}

.comparison-item.highlight {
  background: #ecf5ff;
  border-color: #409eff;
}

.comparison-title {
  font-size: 15px;
  font-weight: 600;
  color: #303133;
  margin-bottom: 12px;
}

.comparison-content {
  font-size: 14px;
  color: #606266;
  line-height: 1.8;
}

@media (max-width: 768px) {
  .comparison-grid {
    grid-template-columns: 1fr;
  }
}
</style>

五、简历撰写指南

5.1 项目经验描述模板

项目名称: 企业级智能搜索系统

项目时间: 2024.07 - 2024.12

项目描述: 负责企业内部知识库的智能搜索系统开发,集成语义理解、智能推荐和向量检索能力。通过 Claude API 实现自然语言查询理解,用户搜索体验提升 80%,准确召回率提升 45%。

核心职责

  1. 设计并实现语义搜索功能,用户可用自然语言提问,系统理解意图后返回最相关结果
  2. 开发智能推荐系统,包括拼写纠错、实时联想、相关推荐、热门搜索等功能模块
  3. 集成向量检索能力,基于语义相似度深度匹配,识别出传统关键词搜索遗漏的相关内容
  4. 优化搜索性能,通过防抖、缓存、异步加载等手段,将搜索响应时间控制在 500ms 以内

技术栈: Vue3 Composition API、Claude API、防抖节流、异步请求优化、向量相似度计算

项目成果
  • 用户搜索满意度从 65% 提升至 92%
  • 搜索准确率提升 45%,召回率提升 38%
  • 日均搜索量增长 3 倍,用户停留时长增加 2 倍
  • 系统已服务公司 2000+ 员工,累计处理 50 万次搜索请求

5.2 SOP 标准回答话术

面试官:介绍一下你做的智能搜索项目

回答话术: "好的。这个项目是我负责的企业内部知识库的智能搜索系统,主要解决传统搜索准确率低、体验差的问题。

背景是这样的:我们公司有一个文档知识库,包含各种技术文档、项目记录、最佳实践等。传统搜索只能做关键词匹配,经常找不到想要的内容。比如用户搜"如何提高效率",传统搜索只能找标题或内容包含这几个字的文档,但实际上讲"时间管理"、"工具选型"的文档也很相关,却被遗漏了。

我们的解决方案分三个核心功能:

第一是语义搜索。不再做简单的关键词匹配,而是理解用户的搜索意图。用户输入查询后,我先调用 Claude API 分析用户真正想了解什么,扩展相关的关键词,然后基于语义相似度进行检索。比如搜"苹果手机",系统能理解这是在找关于 iPhone 的内容。这个功能让搜索准确率提升了 45%。

第二是智能推荐。我做了几个子功能:拼写纠错会自动识别用户的输入错误并提示正确写法;实时联想会根据用户正在输入的内容推荐可能的查询;相关搜索会在搜索后推荐类似的查询词。这些功能让用户搜索效率提升了很多。

第三是向量检索。传统搜索是文字匹配,向量检索是理解内容的本质。我将文档转为向量表示,用户查询也转为向量,然后计算相似度。这样能找到那些表面文字不同但语义相关的内容。实测能多召回 38% 的相关文档。

技术难点主要是性能优化。AI 调用比较耗时,我通过防抖、智能缓存、异步加载等方式把响应时间控制在了 500ms 以内。另外就是如何平衡推荐的丰富度和打扰度,我做了用户行为分析,动态调整推荐策略。

最终效果很好,用户搜索满意度从 65% 提升到 92%,现在系统服务公司 2000 多人,日均搜索量是以前的 3 倍。"

面试官:语义搜索和传统搜索有什么本质区别?

回答话术: "这是个很好的问题。两者最本质的区别在于理解方式不同:

传统搜索是字符串匹配,只看表面文字。比如搜"手机"只能找到包含"手机"这两个字的文档,即使文档在讲 iPhone、Android,但没有"手机"二字就匹配不上。

语义搜索是理解意图,看内容本质。同样搜"手机",它能理解你想找移动设备相关的内容,所以 iPhone、Android、移动终端、智能设备等相关内容都能找到,虽然它们没有"手机"二字。

具体实现上:

  1. 查询理解:AI 会分析用户搜什么,扩展相关概念
  2. 语义匹配:不只看关键词重合度,而是算语义相似度
  3. 智能排序:结合上下文、用户意图等多维度综合排序

举个例子:用户搜"团队沟通效率低怎么办"。

  • 传统搜索:找包含"团队"、"沟通"、"效率"这些词的文档
  • 语义搜索:理解用户在找解决方案,能匹配到"如何开好会"、"协作工具选型"、"敏捷实践"等相关内容,虽然这些标题没有用户搜索的原词

这也是为什么语义搜索的召回率能提升 38% 的原因——它能找到那些真正相关但关键词不匹配的内容。"

面试官:你是怎么做搜索性能优化的?

回答话术: "搜索性能优化我主要从三个层面入手:

第一是请求优化。AI 调用是最耗时的环节,我做了几个优化:

  1. 防抖处理:用户输入时不立即调用 API,等停止输入 300ms 后再请求,减少无效调用
  2. 智能缓存:相同或相似的查询结果会缓存 5 分钟,命中率达到 35%
  3. 批量处理:把多个小请求合并成一个,减少网络开销

第二是异步加载。搜索结果分批加载,先显示本地缓存或快速结果,AI 增强的结果异步补充。这样用户感知的响应时间只有 200ms,但整体优化后的结果会在 500ms 内完整展示。

第三是降级策略。如果 AI 服务响应慢或不可用,自动降级到传统关键词搜索,保证基本可用。我会监控 AI 服务响应时间,超过 1 秒就触发降级。

通过这些优化,虽然增加了 AI 能力,但响应时间反而从原来的 800ms 降到了 500ms,用户体验更好了。另外 API 成本也降低了 40%,因为避免了很多无效调用。"

5.3 难点与亮点分析

难点1:语义理解的准确性

问题描述: AI 理解用户意图可能出现偏差,导致返回不相关的结果,反而不如传统搜索准确。

解决方案
  1. 双路径检索:同时进行传统关键词检索和语义检索,结果融合
  2. 置信度评估:AI 返回语义理解结果时附带置信度,低置信度时降权
  3. 用户反馈闭环:记录用户点击行为,不相关的结果会自动降权
  4. 领域知识注入:在 Prompt 中加入业务领域知识,提高理解准确性
技术实现
javascript
// 融合策略
const finalResults = [
  ...traditionalResults.slice(0, 3),  // 前3个传统结果
  ...semanticResults.slice(0, 5),      // 前5个语义结果
].sort((a, b) => {
  // 综合排序:相关度 * 置信度 * 用户反馈分数
  return (b.score * b.confidence * b.userFeedback) -
         (a.score * a.confidence * a.userFeedback)
})

难点2:实时联想推荐的性能

问题描述: 用户每输入一个字都触发推荐,会导致大量 API 调用,成本高且体验差。

解决方案
  1. 防抖策略:300ms 延迟,只在用户停止输入后触发
  2. 本地预判:输入少于 2 个字符不触发 AI,使用本地热词匹配
  3. 渐进式增强:先显示本地缓存结果,AI 结果异步补充
  4. 前端预测:基于输入历史和热门搜索做前端预测
技术实现
javascript
let debounceTimer = null
const handleInput = () => {
  // 本地快速响应
  if (input.length < 2) {
    suggestions.value = localHotWords.filter(w => w.includes(input))
    return
  }

  // 防抖调用 AI
  clearTimeout(debounceTimer)
  debounceTimer = setTimeout(() => {
    fetchAISuggestions()
  }, 300)
}

难点3:向量相似度计算的准确性

问题描述: 简单的向量相似度计算可能不准确,需要考虑业务场景和用户反馈。

解决方案
  1. 多维度计算:除了文本相似度,还考虑分类、标签、时间等因素
  2. 权重调整:不同字段的重要性不同,标题权重高于内容
  3. 用户反馈学习:记录点击行为,动态调整相似度算法
  4. A/B 测试:对比不同算法效果,选择最优方案

亮点1:意图理解的深度应用

不只是简单的查询扩展,而是真正理解用户想做什么:

  • 搜"怎么办"类问题 → 推荐解决方案类文档
  • 搜技术名词 → 推荐教程、最佳实践
  • 搜概念对比 → 推荐对比分析类文章

亮点2:搜索推荐的智能性

多个推荐功能相互配合,形成完整的搜索引导:

  • 拼写纠错:避免输入错误导致搜索失败
  • 实时联想:帮助用户快速完成查询
  • 相关推荐:引导用户探索更多相关内容
  • 热门搜索:展示其他用户的搜索趋势

亮点3:双路径检索的稳定性

传统检索 + 语义检索双保险:

  • AI 服务正常:语义结果为主,传统结果补充
  • AI 服务异常:自动降级到传统检索
  • 保证了系统的高可用性

5.4 避免 AI 化的表达技巧

AI 化表达: "利用先进的自然语言处理技术和向量检索算法,构建了一个高度智能化的搜索系统,实现了语义理解、智能推荐和深度检索的全面集成。"

自然表达: "用户搜索时,系统会理解他真正想找什么,而不是简单匹配关键词。比如搜'手机',iPhone 相关的内容也能被找到,虽然标题里没有'手机'这个词。"

关键差异

  1. 用具体例子代替抽象描述
  2. 说用户视角而非技术视角
  3. 用简单动词而非专业术语
  4. 强调结果而非技术名词

5.5 完整简历示例

plain
【智能搜索系统】2024.07 - 2024.12

项目背景:
公司内部知识库包含大量技术文档和项目资料,传统关键词搜索准确率低,用户经常找不到需要的内容。

我的工作:
1. 语义搜索功能开发
   - 集成 Claude API 实现查询意图理解和语义扩展
   - 设计双路径检索机制(传统+语义),结果融合排序
   - 搜索准确率从 68% 提升至 92%,召回率提升 45%

2. 智能推荐系统实现
   - 开发拼写纠错、实时联想、相关推荐、热门搜索等功能模块
   - 实现防抖策略和智能缓存,API 调用成本降低 40%
   - 搜索转化率提升 60%,用户平均搜索次数从 3.2 次降至 1.8 次

3. 向量检索能力集成
   - 基于语义相似度实现深度内容匹配
   - 多维度相似度计算(文本+分类+标签+时间)
   - 能识别出传统搜索遗漏的 38% 相关内容

4. 性能优化与稳定性保障
   - 通过防抖、缓存、异步加载将响应时间从 800ms 降至 500ms
   - 实现降级策略,AI 服务异常时自动切换传统检索
   - 系统可用性达 99.9%

技术栈:Vue3、Claude API、向量相似度、防抖节流、异步优化

项目成果:
- 用户搜索满意度从 65% 提升至 92%
- 日均搜索量增长 3 倍,停留时长增加 2 倍
- 服务公司 2000+ 员工,累计处理 50 万次搜索请求
- 被评为年度最受欢迎内部工具

六、运行说明

6.1 环境准备

bash
npm create vite@latest smart-search -- --template vue
cd smart-search
npm install

6.2 运行代码

  1. 将任一完整代码复制到 src/App.vue
  2. 运行 npm run dev
  3. 浏览器访问 http://localhost:5173

6.3 功能演示

  • 语义搜索:输入自然语言问题,查看 AI 理解和语义匹配结果
  • 智能推荐:输入关键词,查看联想推荐、拼写纠错、相关搜索
  • 向量检索:输入复杂查询,对比传统搜索和语义检索的差异

核心要点

  • 双路径检索保证准确性和稳定性
  • 多层次优化控制成本和性能
  • 用户体验优先,智能推荐不过度干扰
  • 简历表达具体、自然、有数据支撑