当前位置: 首页 > news >正文

AI书签管理工具开发全记录(八):Ai创建书签功能实现

文章目录

  • AI书签管理工具开发全记录(八):AI智能创建书签功能深度解析
    • 前言 📝
    • 1. AI功能设计思路 🧠
      • 1.1 传统书签创建的痛点
      • 1.2 AI解决方案设计
    • 2. 后端API实现 ⚙️
      • 2.1 新增url相关工具方法
      • 2.1 创建后端api
      • 2.2 创建createAIBookmark
      • 2.3 初始化ai模型
      • 2.4 编写promot获取建议信息
      • 2.5 元数据处理
      • 2.6 将信息返回给前端
    • 3. 前端实现 💻
      • 3.1 增加api实现
      • 3.2 调用ai创建书签方法
      • 3.3 创建书签组件
    • 4. AI模型集成说明 🤖
      • 4.1 技术选型
    • 4. 效果展示 🤖
    • 总结 📚

AI书签管理工具开发全记录(八):AI智能创建书签功能深度解析

前言 📝

在前一篇文章中,我们完成了书签和分类管理的基础功能实现。本文将聚焦于项目中特色的功能之一,AI智能创建书签,详细解析如何利用AI技术实现智能化的书签创建流程,大幅提升用户操作效率。

1. AI功能设计思路 🧠

1.1 传统书签创建的痛点

在传统书签管理工具中,用户需要:

  1. 手动输入标题
  2. 复制粘贴URL
  3. 填写描述信息
  4. 选择或创建分类
    整个过程繁琐耗时,有时为了方便,除了url,其它就应付了事,造成后期维护不便。

1.2 AI解决方案设计

我们的AI智能创建功能将实现:

  1. ​自动提取元数据​​:从URL获取网页标题、描述等基础信息
  2. ​智能分类建议​​:基于网页内容自动推荐合适分类
  3. ​一键填充​​:自动填充表单字段
  4. ​分类联动​​:支持直接创建AI建议的分类

完整交互逻辑:
image.png

2. 后端API实现 ⚙️

为了后续和方便多个大模型进行对接,放弃了轻量级的http形式,使用eino框架,此处我们先对接openai模型,提供BaseUrl配置,任何和openai兼容的api都是可以使用的。

2.1 新增url相关工具方法

//internal/utils/url.go
package utilsimport ("bytes""fmt""io""net/http""net/url""regexp""strings""github.com/PuerkitoBio/goquery"
)func IsValidURL(urlStr string) bool {// 检查空字符串if urlStr == "" {return false}// 尝试解析URLu, err := url.ParseRequestURI(urlStr)if err != nil {return false}// 检查Schemeif u.Scheme != "http" && u.Scheme != "https" {return false}// 检查Hostif u.Host == "" {return false}// 简单验证域名格式domainRegex := `^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$`if matched, _ := regexp.MatchString(domainRegex, u.Host); !matched {return false}return true
}type WebpageInfo struct {URL   string `json:"url"`Title string `json:"title"`HTML  string `json:"html"`Text  string `json:"text"`
}func GetWebpageInfo(url string) (*WebpageInfo, error) {// Create a new HTTP clientclient := &http.Client{}// Create a new requestreq, err := http.NewRequest("GET", url, nil)if err != nil {return nil, err}// Set a custom User-Agent headerreq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")// Make the requestresp, err := client.Do(req)if err != nil {return nil, err}defer resp.Body.Close()// Check Content-Type is HTMLcontentType := resp.Header.Get("Content-Type")if !strings.Contains(contentType, "text/html") {return nil, fmt.Errorf("URL does not return HTML content")}// Read response bodybodyBytes, err := io.ReadAll(resp.Body)if err != nil {return nil, err}// Parse HTML documentdoc, err := goquery.NewDocumentFromReader(bytes.NewReader(bodyBytes))if err != nil {return nil, err}// Extract <title> tag contenttitle := doc.Find("title").Text()if title == "" {title = "Untitled"}// Clean up titletitle = strings.TrimSpace(title)title = strings.Join(strings.Fields(title), " ")// Get first 2000 characters of HTMLhtmlContent := string(bodyBytes)if len(htmlContent) > 2000 {htmlContent = htmlContent[:2000] + "..."}// Extract plain text (with HTML tags removed)textContent := doc.Text()// Clean up text contenttextContent = strings.TrimSpace(textContent)textContent = strings.Join(strings.Fields(textContent), " ")// Limit text content length if neededif len(textContent) > 2000 {textContent = textContent[:2000] + "..."}return &WebpageInfo{URL:   url,Title: title,HTML:  htmlContent,Text:  textContent,}, nil
}func TruncateURLForName(urlStr string) string {u, err := url.Parse(urlStr)if err != nil {return urlStr}// 使用域名作为名称host := u.Hostname()if strings.HasPrefix(host, "www.") {host = host[4:]}return host
}

2.1 创建后端api

//internal/api/api.gobookmark := api.Group("/bookmarks")
{...bookmark.POST("/ai", server.createAIBookmark)
}

2.2 创建createAIBookmark

//internal/api/api.go// CreateAIBookmark godoc
// @Summary 使用AI创建书签
// @Description 根据URL使用AI自动生成书签信息
// @Tags bookmarks
// @Accept json
// @Produce json
// @Param request body models.AIBookmarkRequest true "URL信息"
// @Success 200 {object} models.AIBookmarkResponse
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/bookmarks/ai [post]
func (s *Server) createAIBookmark(c *gin.Context) {// 返回网页信息和AI建议c.JSON(200, nil)
}

2.3 初始化ai模型

//internal/api/api.go// 初始化AI模型
ctx := context.Background()
config := common.AppConfigModel
maxTokens := config.AI.MaxTokens
temperature := float32(config.AI.Temperature)
model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{BaseURL:     config.AI.BaseURL,APIKey:      config.AI.PIKey,Timeout:     time.Duration(config.AI.Timeout) * time.Second,Model:       config.AI.Model,MaxTokens:   &maxTokens,Temperature: &temperature,
})
if err != nil {c.JSON(500, gin.H{"error": "AI模型初始化失败"})return
}

2.4 编写promot获取建议信息

//internal/api/api.go// 准备消息
messages := []*schema.Message{{Role: "system",Content: `你是一个专业的书签助手,负责分析网页内容并生成合适的书签信息。
请遵循以下规则:
1. 分类选择:
- 分类名称应该简洁明了,尽量在10个字符以内,或者2-4个汉字
- 避免使用测试、测试1等明显不合适的分类
- 现有分类中没有合适的分类,优先创建新分类2. 名称生成:
- 使用网页标题作为基础,但要去除网站名称、分隔符等无关信息
- 保持简洁,通常不超过10个汉字
- 如果标题不够清晰,可以根据内容补充关键信息3. 描述生成:
- 总结网页的核心内容和价值
- 突出最重要的2-3个要点
- 使用简洁的语言,不超过50个汉字
- 避免使用"这是一个..."等冗余表达
- 使用markdown格式请以JSON格式返回结果,格式如下:
{
"category": "分类名称",
"name": "书签名称",
"description": "书签描述"
}`,},{Role: "user",Content: fmt.Sprintf(`请分析以下网页内容并生成书签信息:现有分类列表:%v网页信息:
标题:%s
内容:%s请确保:
1. 避免使用测试、测试1等明显不合适的分类
2. 如果已经有适合的分类,不要创建重复的分类,例如已经有ai,就不要创建人工智能等分类
3. 生成的名称要简洁明了
4. 描述要突出网页的核心价值`, categoryNames, webpageInfo.Title, webpageInfo.Text),},
}// 生成回复
response, err := model.Generate(ctx, messages)
if err != nil {c.JSON(500, gin.H{"error": "AI生成失败"})return
}

2.5 元数据处理

//internal/api/api.go// 使用正则表达式去除 ```json 和 ```
re := regexp.MustCompile("(?s)^\\s*```json\\s*(.*?)\\s*```\\s*$")
matches := re.FindStringSubmatch(response.Content)
if len(matches) < 2 {c.JSON(500, gin.H{"error": "无法提取JSON内容"})return
}
cleanedJSON := matches[1]var suggestion models.BookmarkSuggestion
err = json.Unmarshal([]byte(cleanedJSON), &suggestion)
if err != nil {c.JSON(500, gin.H{"error": "解析AI响应失败"})return
}

2.6 将信息返回给前端

//internal/api/api.go// 返回AI建议
aiResp := models.AIBookmarkResponse{Suggestion: suggestion,Webpage:    models.WebpageInfo{Title: webpageInfo.Title, URL: webpageInfo.URL},
}// 返回网页信息和AI建议
c.JSON(200, aiResp)

3. 前端实现 💻

3.1 增加api实现

3.2 调用ai创建书签方法

//web/src/api/bookmark/index.js// 使用AI创建书签
export function createAIBookmark(data) {return request({url: '/api/bookmarks/ai',method: 'post',data})
}

3.3 创建书签组件

创建AICreateDialog,编写ai创建书签组件

<!--web/src/views/bookmark/components/AICreateDialog.vue-->
<template><el-dialogv-model="dialogVisible"title="AI创建书签"width="600px"><el-formref="formRef":model="form":rules="rules"label-width="80px"><el-form-item label="URL" prop="url"><el-input v-model="form.url" placeholder="请输入网页URL"><template #append><el-button @click="handleFetchMetadata" :loading="fetchingMetadata">获取信息</el-button></template></el-input></el-form-item><template v-if="form.metadata"><el-divider>网页信息</el-divider><el-descriptions :column="1" border><el-descriptions-item label="标题">{{ form.metadata.webpage.title }}</el-descriptions-item><el-descriptions-item label="URL">{{ form.metadata.webpage.url }}</el-descriptions-item></el-descriptions><el-divider>AI建议</el-divider><el-form-item label="分类" prop="category_id"><CategorySelectv-model="form.category_id":category-options="categoryOptions":ai-suggestion="form.metadata.suggestion.category"@update:category-options="categoryOptions = $event"/></el-form-item><el-form-item label="标题" prop="title"><el-input v-model="form.title" placeholder="请输入书签标题" /></el-form-item><el-form-item label="描述" prop="description"><el-inputv-model="form.description"type="textarea"placeholder="请输入书签描述"/></el-form-item></template></el-form><template #footer><span class="dialog-footer"><el-button @click="handleCancel">取消</el-button><el-button type="primary" @click="handleSubmit" :disabled="!form.metadata">确定</el-button></span></template></el-dialog>
</template><script setup>
import { ref, defineProps, defineEmits, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { createAIBookmark } from '/@/api/bookmark'
import CategorySelect from './CategorySelect.vue'const props = defineProps({modelValue: {type: Boolean,required: true},categoryOptions: {type: Array,required: true}
})const emit = defineEmits(['update:modelValue', 'success', 'update:categoryOptions'])// 对话框可见性
const dialogVisible = ref(props.modelValue)// 监听modelValue变化
watch(() => props.modelValue, (val) => {dialogVisible.value = val
})// 监听dialogVisible变化
watch(() => dialogVisible.value, (val) => {emit('update:modelValue', val)
})// 表单相关
const formRef = ref(null)
const fetchingMetadata = ref(false)
const form = ref({url: '',title: '',description: '',category_id: undefined,metadata: null
})// 移除不再需要的变量和函数
const categoryDialogVisible = ref(false)
const categoryForm = ref({name: '',description: ''
})// URL验证函数
const validateUrl = (rule, value, callback) => {...
}// 表单验证规则
const rules = {...
}// 获取网页元数据
const handleFetchMetadata = async () => {...
}// 处理提交
const handleSubmit = async () => {//...
}// 处理取消
const handleCancel = () => {...
}
</script><style scoped>
...
</style> 

4. AI模型集成说明 🤖

4.1 技术选型

我们采用了eino框架,很方便对接多种ai模型
根据需求可以采取不容策略。

  1. ​本地模型​​:使用ollama等可以方便运行多种本地ai模型,例如qwen3系列
    • 优点:数据隐私性好
    • 缺点:需要较强的服务器资源
  2. ​第三方API​​:如OpenAI、Google AI等
    • 优点:开发简单,效果较好
    • 缺点:有API调用成本

如果对隐私没有那么高需求,可以试试chatglmGLM-4-Flash-250414模型。开发阶段采用了该模型,对于这种简单需求基本够用,最重要的是免费。
如果对隐私要求极高,可以试试ollama,目前对推理模型没有做适配,需要修改代码。

4. 效果展示 🤖

点击ai创建书签,输入url
image.png

获取信息
image.png

对分类不满意,现存的分类也没有合适的,可以点击新建分类
image.png

可以选择新建的分类
image.png

总结 📚

本文深入实现了AI智能创建书签功能,主要包括:

  1. ​智能化流程​​:简化创建书签步骤
  2. ​精准分析​​:结合元数据提取和AI内容理解
  3. ​无缝体验​​:分类建议与创建的联动设计

相关文章:

  • MySQL事务与锁机制详解:确保数据一致性的关键【MySQL系列】
  • PostIn入门教程 - 使用IDEA插件快速生成API接口定义
  • Halcon
  • 力扣HOT100之动态规划:139. 单词拆分
  • 牛客周赛94
  • 极智项目 | 多模态大模型推理平台-Streamlit版(支持Qwen2.5/InternVL3/KimiVL三大模型)
  • 【CBAP50技术手册】#31 Observation(观察法):BA(业务分析师)的“现场侦探术”
  • 浮点数舍入规则_编程语言对比
  • CTFHub-RCE 命令注入-过滤运算符
  • [SC]SystemC在CPU/GPU验证中的应用(二)
  • R语言错误处理方法大全
  • CRISPR-Cas系统的小型化研究进展-文献精读137
  • python打卡day41
  • vue2源码解析——响应式原理
  • CentOS 7 安装docker缺少slirp4netnsy依赖解决方案
  • C51单片机
  • Python Day38 学习
  • Java BigInteger类详解与应用
  • 使用Yolov8 训练交通标志数据集:TT100K数据集划分
  • 【MLLM】多模态LLM 2025上半年技术发展(Better、Faster、Stronger)
  • 网站建设横幅标语/计算机培训班有用吗
  • 上海3d网站建设/网站流量统计分析
  • 交互设计 网站推荐/郑州seo公司
  • 微网站费用/自助建站系统代理
  • 中国建设银行网站密码/小说网站排名前十
  • 建设厅网站进不去/厦门百度关键词优化