news 2026/6/24 2:41:56

Papermind(五):选中提问功能的设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Papermind(五):选中提问功能的设计与实现

一、需求背景

在开发 paperMind 学术论文阅读平台的过程中,我门团队注意到一个问题:如果让用户阅读 PDF 解析后的论文全文时,遇到不理解的段落(尤其是公式、方法描述),需要手动复制 → 切换到问答页面 → 粘贴 → 再输入问题。这个流程感觉太麻烦,而且也比较耽误阅读,打断阅读节奏。

理想的一种交互应该是:选中不懂的内容 → 原地提问 → 原地看答案,全程不离开阅读界面,因此对之前实现的QA问答功能模块,我们对此进行改进,复用之前实现的QA功能的核心代码,但是新增一个类似我们团队之前实现的翻译功能那样的鼠标选中即可实现用户提问

二、技术选型

1. 浏览器 Selection API

根据一些csdn的文章介绍浏览器原生提供 window.getSelection() 来获取用户选中的文本。它本质上是对 DOM Range 的一层封装,可以拿到选中内容的文本、所在节点、起止位置等。

选用它而不是第三方库的原因很简单:这是个轻交互功能,不需要引入富文本编辑器那套复杂体系,原生 API 足够且零依赖。这也是很多博主都推荐实现的一种技术路径

2. 绝对定位浮动面板

最初想过用 tooltip 组件库,但考虑到我们的面板需要承载输入框 + 按钮 + AI 回答(内容可能很长),tooltip 撑不下。最终选择手动 position: absolute 计算坐标,虽然可能技术算是比较落后的技术但可控性强。

坐标计算的核心思路:

const containerRect = markdownPreview.getBoundingClientRect(); const left = mouseX - containerRect.left + container.scrollLeft; const top = mouseY - containerRect.top + container.scrollTop - 42;

这里减 42 是把按钮显示在选中文字上方,体验上更自然。加 `scrollLeft/scrollTop` 是处理容器内滚动的情况,否则滚动后按钮位置会偏。

三、 DeepSeek + LangGraph 流水线

后端 AI 调用走的是已有的 chat 接口,底层是 LangGraph 构建的 Agent 流水线:

Router(意图分类) → Context Loader(加载论文) → QA Agent(检索+回答)

选中提问复用这条链路而不是单独写接口,原因:

聊天历史统一管理,可追溯
Router 有时会把问题路由到更合适的 Agent(比如识别为翻译需求就走翻译 Agent)
少写代码,维护简单

代价是多一次 Router 的 LLM 调用(增加约 1 秒),但是这个时间代价对于我们现在这个场景我认为可以接受。

四、Prompt 拼接策略

发给 LLM 的消息结构:

针对以下论文片段: "{用户鼠标选中的原文}" 我的问题是:{用户手动输入的问题}

这样做的好处是 LLM 能明确区分上下文和"问题",不会混淆。QA Agent 接到后还会把论文全文和向量检索到的相关片段一并附上,确保回答有据可依

五、关键实现

前端交互

选中检测与面板弹出的核心逻辑:

document.addEventListener("mouseup", () => { setTimeout(() => { const sel = window.getSelection(); const text = sel ? sel.toString().trim() : ""; if (text.length < 5) return; // 太短不触发 if (!markdownPreview.contains(sel.anchorNode)) return; // 只在论文区域生效 // 显示浮动按钮,存储选中文本 floatAskBtn.style.display = "flex"; floatAskBtn.dataset.text = text; }, 50); });

点击按钮后弹出带输入框的面板,用户输入问题后调用后端:

const question = `针对以下论文片段:\n"${selectedText}"\n\n我的问题是:${userInput}`; const res = await fetch("/api/v1/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ paper_id: currentPaperId, message: question }), });

后端(复用现有 chat 接口)

不需要新增任何后端代码。选中提问和普通聊天走同一个 /api/v1/chat端点,区别只在于前端拼接的 prompt 结构不同。这也是我选择复用的原因 —— 零后端改动量

开发过程中踩的坑

1. HuggingFace 连接超时导致请求卡死数分钟

这是这次开发遇到的最大坑。功能写好后测试,点击提问按钮后页面转圈好几分钟没有任何响应,一开始以为是 DeepSeek API 慢。

排查后发现根本不是 LLM 的问题。后端聊天流水线中,QA Agent 会调用 sentence-transformers 模型做向量检索(从论文中找相关片段辅助回答)。这个模型文件本地已经有了,但 sentence-transformers 库每次初始化时会尝试连 HuggingFace 检查模型更新。我的网络环境连不上 huggingface.co,库默认重试 5 次,每次 10 秒超时 + 退避等待,多个文件依次重试,加起来直接卡了好几分钟。

2. 端口冲突导致页面白屏

开发过程中多次重启服务器,有一次旧进程没有完全退出,两个进程同时监听 8000 端口。表现为页面能加载出标题栏(说明 HTML 已送达)但内容全白(请求被卡住的旧进程接管了)。排查时用 `netstat -ano | findstr ":8000"` 发现了两个 LISTENING 进程,全部 kill 后重新启动解决。

3. 选中检测的时序问题

mouseup 事件触发时,window.getSelection()可能还没有更新到最新状态。加了 50ms 的 setTimeout才能稳定拿到选中内容。类似地,隐藏按钮时如果不加延迟(200ms),点击按钮本身时mousedown会先触发把按钮隐藏掉,导致 click 事件永远到不了。

Vibe Coding 提示词记录

这个功能的开发只用了两轮 AI 对话(不算上修改错误,使用的模型是Claude opus 4.6):

第一轮:

你是一个后端开发专家,请你现在你需要帮我完成一个选中问答功能的开发,客户有时候可能会出现对某段理解不清楚,如果每次都复制粘贴到智能问答模块那太复杂了效率也低,所以我希望你可以完成客户鼠标选中然后就可以进行选择提问的功能。这里后端你复用我的之前QA实现的代码,鼠标点击使用Selection API,请你不得修改我的其他无关代码,开发时高内聚低耦合,符合软件工程范式,同时请你如果有疑问直接像我提出询问,不得擅自决定修改任意代码或者加入任意功能

在这一轮之后,第一版实现了选中即自动回答(AI 自行决定解释什么)。

第二轮:

提问选中应该可以支持用户提出问题,而不是直接由 AI 自己回答

修改为弹出输入框让用户自己输入问题,这更合理 —— 用户可能对同一段文字有不同的疑问角度。

反思:第一轮提示词没有明确"用户需要输入问题"这个细节,AI 按最简方案实现了自动解释。说明 vibe coding 时,交互细节还是要在提示词中说清楚,否则需要额外多轮迭代。

最后整体效果如下所示:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 11:47:01

Redis 从入门到实战:Python 开发者必备的高性能缓存指南

前言 在当今高并发、大数据量的互联网应用时代&#xff0c;系统性能成为了决定用户体验的关键因素。传统的关系型数据库&#xff08;如 MySQL、PostgreSQL&#xff09;虽然在数据持久化和复杂查询方面表现出色&#xff0c;但在面对每秒数万甚至数十万次的读写请求时&#xff0…

作者头像 李华
网站建设 2026/6/14 6:45:49

抖音无水印视频批量下载完整指南:告别繁琐手动操作

抖音无水印视频批量下载完整指南&#xff1a;告别繁琐手动操作 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support.…

作者头像 李华