一、技术架构设计
1.1 整体架构
前端层(Vue3)
├── 搜索组件
├── 结果展示
└── 交互优化
AI 能力层
├── 语义理解(Claude API)
├── 向量检索(Embedding)
└── 智能排序
数据层
├── 文档库
├── 向量数据库
└── 搜索索引
1.2 核心功能模块
- 语义搜索:理解用户意图而非简单关键词匹配
- 搜索推荐:智能纠错、联想查询、相关推荐
- 向量检索集成:基于语义相似度的深度检索
二、语义搜索实现
2.1 技术实现原理
传统搜索 vs 语义搜索
- 传统:关键词精确匹配 → "苹果手机" 只匹配包含这4个字的文档
- 语义:理解查询意图 → "苹果手机" 能匹配 "iPhone"、"iOS设备" 等相关内容
实现思路
- 用户输入查询词
- AI 理解用户意图,扩展查询范围
- 从数据库检索相关文档
- AI 对结果进行语义排序
- 展示最相关结果
2.2 完整代码实现
<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 技术实现原理
核心功能
- 智能纠错:自动识别输入错误并提供正确建议
- 联想查询:根据输入实时推荐相关搜索
- 相关推荐:基于当前搜索推荐相似内容
3.2 完整代码实现
<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 技术实现原理
向量检索原理
- 将文本转为向量(Embedding)
- 计算向量间的相似度
- 返回最相似的结果
实现方案
- 使用简化的模拟向量相似度计算
- 真实场景需接入向量数据库(如 Pinecone、Milvus)
4.2 完整代码实现
<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%。
核心职责
- 设计并实现语义搜索功能,用户可用自然语言提问,系统理解意图后返回最相关结果
- 开发智能推荐系统,包括拼写纠错、实时联想、相关推荐、热门搜索等功能模块
- 集成向量检索能力,基于语义相似度深度匹配,识别出传统关键词搜索遗漏的相关内容
- 优化搜索性能,通过防抖、缓存、异步加载等手段,将搜索响应时间控制在 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、移动终端、智能设备等相关内容都能找到,虽然它们没有"手机"二字。
具体实现上:
- 查询理解:AI 会分析用户搜什么,扩展相关概念
- 语义匹配:不只看关键词重合度,而是算语义相似度
- 智能排序:结合上下文、用户意图等多维度综合排序
举个例子:用户搜"团队沟通效率低怎么办"。
- 传统搜索:找包含"团队"、"沟通"、"效率"这些词的文档
- 语义搜索:理解用户在找解决方案,能匹配到"如何开好会"、"协作工具选型"、"敏捷实践"等相关内容,虽然这些标题没有用户搜索的原词
这也是为什么语义搜索的召回率能提升 38% 的原因——它能找到那些真正相关但关键词不匹配的内容。"
面试官:你是怎么做搜索性能优化的?
回答话术: "搜索性能优化我主要从三个层面入手:
第一是请求优化。AI 调用是最耗时的环节,我做了几个优化:
- 防抖处理:用户输入时不立即调用 API,等停止输入 300ms 后再请求,减少无效调用
- 智能缓存:相同或相似的查询结果会缓存 5 分钟,命中率达到 35%
- 批量处理:把多个小请求合并成一个,减少网络开销
第二是异步加载。搜索结果分批加载,先显示本地缓存或快速结果,AI 增强的结果异步补充。这样用户感知的响应时间只有 200ms,但整体优化后的结果会在 500ms 内完整展示。
第三是降级策略。如果 AI 服务响应慢或不可用,自动降级到传统关键词搜索,保证基本可用。我会监控 AI 服务响应时间,超过 1 秒就触发降级。
通过这些优化,虽然增加了 AI 能力,但响应时间反而从原来的 800ms 降到了 500ms,用户体验更好了。另外 API 成本也降低了 40%,因为避免了很多无效调用。"
5.3 难点与亮点分析
难点1:语义理解的准确性
问题描述: AI 理解用户意图可能出现偏差,导致返回不相关的结果,反而不如传统搜索准确。
解决方案
- 双路径检索:同时进行传统关键词检索和语义检索,结果融合
- 置信度评估:AI 返回语义理解结果时附带置信度,低置信度时降权
- 用户反馈闭环:记录用户点击行为,不相关的结果会自动降权
- 领域知识注入:在 Prompt 中加入业务领域知识,提高理解准确性
技术实现
// 融合策略
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 调用,成本高且体验差。
解决方案
- 防抖策略:300ms 延迟,只在用户停止输入后触发
- 本地预判:输入少于 2 个字符不触发 AI,使用本地热词匹配
- 渐进式增强:先显示本地缓存结果,AI 结果异步补充
- 前端预测:基于输入历史和热门搜索做前端预测
技术实现
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:向量相似度计算的准确性
问题描述: 简单的向量相似度计算可能不准确,需要考虑业务场景和用户反馈。
解决方案
- 多维度计算:除了文本相似度,还考虑分类、标签、时间等因素
- 权重调整:不同字段的重要性不同,标题权重高于内容
- 用户反馈学习:记录点击行为,动态调整相似度算法
- A/B 测试:对比不同算法效果,选择最优方案
亮点1:意图理解的深度应用
不只是简单的查询扩展,而是真正理解用户想做什么:
- 搜"怎么办"类问题 → 推荐解决方案类文档
- 搜技术名词 → 推荐教程、最佳实践
- 搜概念对比 → 推荐对比分析类文章
亮点2:搜索推荐的智能性
多个推荐功能相互配合,形成完整的搜索引导:
- 拼写纠错:避免输入错误导致搜索失败
- 实时联想:帮助用户快速完成查询
- 相关推荐:引导用户探索更多相关内容
- 热门搜索:展示其他用户的搜索趋势
亮点3:双路径检索的稳定性
传统检索 + 语义检索双保险:
- AI 服务正常:语义结果为主,传统结果补充
- AI 服务异常:自动降级到传统检索
- 保证了系统的高可用性
5.4 避免 AI 化的表达技巧
❌ AI 化表达: "利用先进的自然语言处理技术和向量检索算法,构建了一个高度智能化的搜索系统,实现了语义理解、智能推荐和深度检索的全面集成。"
✅ 自然表达: "用户搜索时,系统会理解他真正想找什么,而不是简单匹配关键词。比如搜'手机',iPhone 相关的内容也能被找到,虽然标题里没有'手机'这个词。"
关键差异
- 用具体例子代替抽象描述
- 说用户视角而非技术视角
- 用简单动词而非专业术语
- 强调结果而非技术名词
5.5 完整简历示例
【智能搜索系统】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 环境准备
npm create vite@latest smart-search -- --template vue
cd smart-search
npm install
6.2 运行代码
- 将任一完整代码复制到
src/App.vue - 运行
npm run dev - 浏览器访问
http://localhost:5173
6.3 功能演示
- 语义搜索:输入自然语言问题,查看 AI 理解和语义匹配结果
- 智能推荐:输入关键词,查看联想推荐、拼写纠错、相关搜索
- 向量检索:输入复杂查询,对比传统搜索和语义检索的差异
核心要点
- 双路径检索保证准确性和稳定性
- 多层次优化控制成本和性能
- 用户体验优先,智能推荐不过度干扰
- 简历表达具体、自然、有数据支撑