1. 这个工具到底解决什么问题:我先把自己的需求边界定死了
做答题辅助软件这件事,起因是知识竞赛的训练群。群里有支队伍准备参加单位组织的比赛,每周都要在群里做模拟抢答,主持人念题目,大家拼手速打字抢答。连续练了两次我发现一个问题:很多题目不是难,而是“知道在哪本书里但想不起来”,等翻书查到答案,别人早就答完了。
于是我想做一个答题辅助软件,目标很明确:不是代替人思考,而是把“回忆”和“查找”这两个环节压缩到一秒以内。题目出现时,工具自动识别文字、检索本地题库,然后给出参考答案提示。人只需要用那零点几秒判断一下提示是否合理,再按键作答。整个链路下来,抢答反应时间从原来的5到10秒缩短到1秒左右。
1.1 为什么选择“半自动”而不是“全自动”
这是整个项目里最关键的一个决定。市面上很多同类工具追求“全自动答题”:截屏、识别、定位选项、模拟点击一气呵成,人什么都不用干。我一开始也想这么做,但认真分析后放弃了,原因有三点:
第一,场景不允许。知识竞赛的规则对“用工具辅助”向来敏感,你手动看一眼提示再作答,还能解释成“熟悉资料”;如果是程序自己在点击,性质就完全不同了。做工具的人首先要学会保护使用者。
第二,工程复杂度完全不同。全自动答题意味着要处理按钮坐标、页面结构变化、倒计时同步、误点风险,这套东西开发周期至少是三倍以上,而且任何一次页面改版都可能让功能瘫痪。
第三,半自动的准确率更高。全自动一旦识别出错,程序会直接点到错误选项;半自动则有人工这一道“最后防线”,机器给一个参考结论,人去复核,容错率大大提升。
所以我给这个软件的定位是“速查辅助工具”,不是“抢答作弊器”。它做三件事:读取题目、检索答案、提示选项。至于最终点哪个按钮,永远由人来决定。
1.2 目标用户和使用场景
做工具最忌讳“什么都想覆盖”。我梳理了三类明确的适用场景,每种场景的输入方式和期望结果都不一样:
| 场景 | 题目来源 | 期望结果 | 核心痛点 |
|---|---|---|---|
| 知识竞赛模拟抢答 | 主持人念题、聊天栏文字 | 1秒内给出参考答案 | 手速竞争、回忆延迟 |
| 课程在线测验自测 | 网页题目截图或复制文本 | 显示选项对应关系 | 重复刷题费时 |
| 单位培训考核前温习 | 整理好的题库文档 | 批量核对答案 | 题库体量大、记忆混淆 |
这里要特别说明:我做的题库都是团队自己整理的公开资料,工具也只用于内部训练场景,不碰任何正式考试。边界划清楚,工具才能做得心安理得。
1.3 功能清单与非目标
最终交付的功能清单如下:
- 支持三种输入方式:剪贴板文本、截图识别、手动输入
- 本地题库检索,支持按主题分库
- 网页搜索补充,用于本地题库未命中的情况
- 快捷键呼出悬浮窗口,不影响当前页面操作
- 错题记录与导出
而明确不做的事情也写在文档第一页:不自动点击页面元素、不尝试绕过验证码或人机校验、不共享任何人的账号信息、不提供所谓的“全自动托管模式”。这些约束不是技术做不到,是没必要。答题辅助的价值在于提升信息检索效率,而不是制造虚假成绩。
2. 核心链路拆解:从看见题目到给出提示,中间隔了四道工序
整个工具本质上是一条流水线:采集题目文字、抽取关键信息、检索匹配答案、展示推荐结果。这四道工序每一步都有坑,我先讲整体架构,再逐个拆开讲细节。
2.1 输入方式的取舍:文本优先、截图兜底
开发初期我把截图识别作为首要输入方式,后来发现自己做了一个愚蠢的决定。截图识别是“技术上好听,体验上难用”的方案:每次作答先按快捷键截屏、等OCR跑完、再校验识别结果,这一套流程下来最快也要1.5秒。而知识竞赛里聊天框发出来的题目,本身就是文字,直接复制进剪贴板不就完了?
于是我把输入逻辑改成优先读取剪贴板,再提供截图兜底。剪贴板读取是毫秒级的,几乎零成本。聊天群里自然复制、网页里Ctrl+C、PDF文档里选中文字,任何能复制的内容都走文本通道。只有遇到图片形式的题目(比如翻拍的PPT讲义),才触发截图识别。
这个设计带来的收益非常明显:日常使用中约有70%的题目走文本通道,平均响应时间不到200毫秒;剩下30%的图片题才用到OCR,耗时在1秒到2秒之间。开发过程中很多优化其实不是技术优化,而是流程优化——让高频动作走最快的路。
2.2 OCR识别:本地优先,别把题目发给第三方
有些工具直接把截图发给云端OCR,速度快是快,但我在意两点:一是网络波动会导致识别结果不稳定,场内几百人同时用网络时,你的请求可能排在后面;二是隐私问题,内部训练资料不应该流到外部服务。
所以我坚持用本地OCR引擎。部署形态是这样:
// OCR调用伪代码,实际实现依赖所用引擎的API const result = await localOcr.recognize({ image: capturedImage, language: 'chinese_simplified', preprocess: { enlargeScale: 2, // 放大两倍,提高小字号识别率 grayscale: true, denoise: 'medium' } });本地部署要注意两件事。第一,字体渲染差异。很多OCR引擎在标准宋体下表现好,但网页题目常用微软雅黑或思源黑体,识别前最好先把图片转成灰度并做二值化处理,去除背景干扰。第二,低分辨率截图。屏幕上题目区域如果字体小,直接识别经常丢字,我的做法是先按2倍放大再送识别,虽然耗时增加约300毫秒,但准确率从87%提到96%以上。
2.3 关键词抽取:不整句匹配,拆成“主词 + 条件”
一开始我犯了个新手错误:把整道题的文字直接拿去做搜索和匹配。结果发现同样的意思换个说法,匹配率就跌到谷底。比如“我国最大的淡水湖是哪个”和“中国淡水湖面积排名第一的是”,整句匹配时相似度很低,但核心实体都是“淡水湖”和“最大”。
所以我做了一层轻量级的关键词抽取。规则并不复杂,本质上是做三件事:
- 去掉疑问词和语气词:“哪个”“什么是”“属于”“下列”之类的词汇直接删除,它们不携带答案信息
- 提取主干名词:优先提取题干中的专有名词、地名、数字、年份,这些是匹配的核心
- 保留限定条件:题干里的否定词(“不是”“不包括”)必须保留,同时把“错误的一项是”这类反向题目标记出来
实际操作中,关键词抽取用词表加规则就够用,没必要上分词模型。我维护了一个约300个常见疑问词和连接词的黑名单,再配合正则表达式抽取数字和引号内的内容,效果已经相当好。一个典型的题目会生成类似这样的结构:
{ "question": "下列哪个城市不属于长三角城市群?", "keywords": ["长三角", "城市群"], "excludeWords": ["不属于"], "questionType": "negative" }“不属于”被单独标记出来,意味着检索到的答案需要进行反向匹配——如果题库里存的是“属于长三角城市群的城市列表”,那答案就要取列表之外的城市。
2.4 匹配与展示:从“检索集合”到“唯一推荐”
关键词抽取完成后,下一步是在题库和搜索引擎里找答案。这一步我设计了三级降级策略:
- 第一级:本地题库精确匹配,置信度高,直接展示答案
- 第二级:本地题库模糊匹配,多条结果按相似度排序,等用户确认
- 第三级:本地未命中,调网页搜索接口,截取搜索结果片段摘要
调度逻辑用一段伪代码可以说明:
def lookup_answer(question_data): local_hits = search_local_db(question_data.keywords) if local_hits and local_hits[0].score >= 0.92: return render_result(local_hits[0], confidence='high') if local_hits: return render_result_list(local_hits, confidence='medium') web_hits = search_web(question_data.keywords) return render_result_list(web_hits, confidence='low')这里最关键的是置信度阈值。0.92这个值是我做了57道真实题目回归测试后定下来的:阈值太高,许多相近说法会漏掉;阈值太低,错误答案会混进来。如果你要复刻这套逻辑,建议也先整理一批自己领域的真实题目做阈值校准,不要想当然。
3. 答案从哪来:本地题库为主,网页检索为补充
这是答题辅助软件的“燃料系统”。没有好的答案来源,前面链路再丝滑都是空转。
3.1 本地题库的结构设计
本地题库是整个系统的核心资产。我早期用它存题库时用的是纯文本文件,每行一道题,结果题目量超过5000条之后,检索慢得没法用。后来改成了结构化的数据存储(JSON或SQLite都可以),核心字段如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | string | 题目唯一编号 |
| question | text | 完整题干 |
| options | array | 选项列表,如["A. 北京","B. 上海"] |
| answer | string | 正确答案 |
| tags | array | 主题标签,如["地理","历史"] |
| source | string | 来源,如"内部培训资料2024" |
| last_updated | datetime | 最近更新时间 |
| hit_count | integer | 被命中次数,用于统计分析 |
我特别强调hit_count这个字段的使用:每一次匹配命中都会增加计数,三个月迭代下来,就能知道哪些题目是高频训练点、哪些题目从未被用到。这为后面做题目难度分层和复习计划提供了数据基础。
3.2 匹配算法:三层相似度计算,从表面到语义
题库检索不能只用一种匹配方式,我设计了三个层次:
第一层:字段精确匹配。题干一模一样或仅去除标点、空格后一致,直接判定为命中。这是最高置信度的匹配,适用于团队内部已经整理好的标准题库。
第二层:编辑距离匹配。用于应对轻微错别字和语序调整。编辑距离小于等于2的,认为是同一道题。比如题干是“我国现行宪法是哪一年颁布的”,对方写成“我国现行宪法是哪年颁布的”,编辑距离就很小,可以匹配上。
第三层:关键词加权打分。这是最常用到的层面。具体方法是:从题目中抽取关键词,每个关键词根据类型分配权重——专有名词权重5分、普通名词3分、数字和年份4分、连接词不计分。然后遍历题库,计算每道题命中的关键词权重总和,除以总权重,得到一个0到1的相似度系数。
代码示意如下:
def keyword_similarity(keywords, db_record): total_score = 0 matched_score = 0 for kw in keywords: weight = get_weight(kw) total_score += weight if kw in db_record.keyword_index: matched_score += weight return matched_score / total_score if total_score > 0 else 0三层计算从快到慢、从严格到宽松依次执行。前一层命中就不需要走后面的逻辑,这样可以控制整体耗时在50毫秒以内。注意,关键词检索一定要预处理出索引字段,运行时直接查索引,不要在每次查询时去切词,否则大题库性能会非常难看。
3.3 网页检索作为兜底:结果包装比结果本身重要
本地题库总有覆盖不到的地方,特别是新出现的题目。网页检索这层我称为“兜底层”,但最花功夫的地方不在搜索本身,而在结果的处理。
搜索引擎返回的摘要通常是一段碎片文字,里面可能包含答案,也可能只是相关内容。我采取的策略是:不直接强调某一个答案,而是把相关句子展示给用户,让人自己判断。展示时对原文做高亮标记,并明确标注“此结果来自网页,未被本地题库验证”。
运行时间久了会发现一个规律:网页检索的命中率其实不高,约为45%,但它的价值不在于直接给答案,而在于帮人快速回忆知识上下文。很多时候看到一句相关的背景描述,人的记忆就恢复了,根本不需要现成答案。
3.4 判断题、多选题、反向题的处理分支
普通单选题是最容易处理的,但实际工作中会遇到其他题型,需要单独做分支逻辑:
| 题型 | 处理方式 | 注意事项 |
|---|---|---|
| 单选题 | 取相似度最高记录直接推荐 | 选项文字若与题库不同,需按选项内容匹配 |
| 多选题 | 展示题库答案后,人工核对选项数量 | 多选题易漏选,提示语强调“逐项核对” |
| 判断题 | 检索题库中对应题干,显示“正确/错误” | 题库中判断题答案有时藏在对错选项里,需转换 |
| 反向题 | 标记为负向,推荐时反转结论 | 如“不属于”“错误的一项是” |
| 图片题 | 先OCR,再走文本链路 | OCR结果中选项行需要做对齐处理 |
判断题和反向题是误判的重灾区。有一次模拟赛,题库里存的是“长江是世界第三长河”,题目出的却是“长江是世界第二长河”,如果直接检索相似句子,很可能把“正确”推给用户。后来我专门加了一条规则:当题干中出现否词时,自动检索题库中对应正面描述,并在展示界面用红黄双色强调“题库原题与本题表述不同”。这个小功能在实际使用中帮了大忙。
4. 浏览器扩展实现方案:为什么选这种形态,以及关键代码
整体架构想清楚之后,具体实现形态也需要认真选择。我最终做了浏览器扩展,而不是桌面程序或手机App,理由很朴素:知识竞赛和在线测验的使用场景都在浏览器里,扩展程序不需要切换窗口、不需要占用额外屏幕,快捷键一按,悬浮窗口就浮在当前页面上。
4.1 扩展程序的部署结构与权限
我做的扩展是一个典型的三部分结构:manifest.json声明配置、内容脚本负责交互界面、后台脚本负责OCR和题库检索。最关键的是把权限控制到最小范围,只申请需要用到的权限,这样无论是从合规角度还是安全角度都更稳妥。
清单配置文件如下:
{ "manifest_version": 3, "name": "答题辅助工具 - 模拟训练版", "version": "1.2.0", "permissions": ["activeTab", "clipboardRead", "storage"], "host_permissions": ["<all_urls>"], "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"], "run_at": "document_idle" } ], "commands": { "open-helper": { "suggested_key": { "default": "Alt+Shift+H" }, "description": "打开答题辅助面板" } } }这里没有申请tabs权限,也没有申请webRequest权限。剪贴板读取需要clipboardRead,这是使用快捷粘贴输入的前提。host_permissions写成"<all_urls>"使用范围广,但如果你只想在特定的几个站点使用,建议收窄到具体域名,避免不必要的安全审视。
4.2 悬浮面板的核心交互逻辑
内容脚本负责在页面右侧悬浮一个小面板,核心交互是接收题目、展示结果、记录错题。面板的DOM结构是运行时动态创建的,不依赖页面的现有结构。
操作流程是这样的:
- 按
Alt+Shift+H,面板从页面右侧滑出 - 焦点自动落在输入框,等待题目文字
- 用户把题目文本粘贴进来,或按
Alt+Shift+S截取当前可见区域 - 面板展示匹配结果、置信度标识和相关附加信息
- 用户点击“记录错题”或直接关闭面板
代码层面最需要注意的,是处理快捷键冲突和面板焦点问题。有些网页自身注册了全局快捷键,为了避免冲突,我特意在拓展设置里允许用户自定义热键。另一个细节是:面板弹出后必须防止页面滚动事件穿透,否则用户滚动页面时面板位置会飘。
// content.js 部分核心逻辑 function createHelperPanel() { const panel = document.createElement('div'); panel.id = 'answer-helper-panel'; panel.style.cssText = 'position:fixed; right:20px; top:80px; z-index:999999; width:320px;'; // 设置样式后,绑定鼠标事件,阻止事件冒泡 panel.addEventListener('mousedown', (e) => e.stopPropagation()); panel.addEventListener('wheel', (e) => e.stopPropagation()); document.documentElement.appendChild(panel); }这段代码虽然简单,但wheel事件阻止冒泡是血泪教训。没有这行代码时,用户在面板里滚动结果列表,背后的答题页面也会跟着滚动,体验非常差。
4.3 截图的实现与坐标对齐
扩展程序里实现截图,和桌面软件不一样。桌面软件可以直接截全屏,扩展程序通常只能用chrome.tabs.captureVisibleTab捕获当前活动标签页的可见区域。这个API返回的是整张页面的截图,不是用户框选的区域。
我的处理方法是:在监听命令事件后捕获当前可见区域,然后把结果传给后台的OCR引擎。为了减少OCR的处理面积,我会先缩放图片到合适大小,再裁剪出页面中间80%的区域,因为答题面板通常位于页面中部偏上的位置。虽然不能做到像素级选区,但90%的场景已经够用。
chrome.commands.onCommand.addListener(async (command) => { if (command === 'capture-screen') { const url = await captureVisibleTab(); const cropped = await cropToCenter(url, 0.8); const text = await runOcr(cropped); showResult(text); } });4.4 面板交互的经验:一切为了毫秒级
“用起来快”是这类工具的生死线。我做了两个细节优化,效果非常明显:
第一个是结果显示的优先级排序。展示区域最上方只放一个最佳匹配的答案,字号加大加粗,附带来源标签;其它备选答案折叠在下方,需要时才展开。许多用户反馈说这个设计很关键——答案一多反而会犹豫,只有一个推荐时反而敢下手。
第二个是剪贴板的自动监听。我扩展了读取逻辑:面板打开状态下,如果系统剪贴板内容发生变化,并且新内容和当前展示题目都不匹配,就自动识别为新题目并触发检索。这意味着用户在网页里选中题目按Ctrl+C,面板就自动更新结果,完全不需要手动粘贴和确认。这个体验已经接近“零操作”了。
5. 开发过程中踩过的坑:每个坑都值得单独写一段复盘
任何工具的开发都有大量“当时以为完美、实际被打脸”的瞬间。答题辅助软件也不例外,尤其是处理真实网络环境和真实页面时,坑比想象中多得多。
5.1 字形相近导致的OCR误判:防不胜防
OCR识别的最大问题不是字体,而是汉字间的形近字。截图中“己”“已”“巳”三个字经常被混淆,直接导致检索结果错误。我遇到过一个最典型的案例:题目是“某事件发生于哪一年”,OCR把年份里的“7”认成了“1”,数据库里自然没有匹配项。
解决思路分三层:
- 预处增补:截图放大、灰度化、去噪点。这一步可以减少约40%的误判
- 多引擎对比:同一张截图用两个本地OCR引擎同时识别,结果不一致时选择置信度更高的那个
- 人工兜底:在面板上展示原文识别的字样,用户可以一眼看出识别是否出错,如果错了可以直接手工改
其中第三层是最实用的。我始终强调“人做最后一道校验”,不追求OCR百分之百正确,而是让错误在1秒内暴露在用户眼前,这远比追求完美识别更有效。
5.2 题库编码问题:Excel导出的坑
题库格式我一开始用的是Excel管理,方便团队里非技术同事维护。但Excel导出CSV时默认用GBK编码,而我程序里读的是UTF-8,结果就是一堆乱码。排查了很久才发现问题不是程序逻辑出错,而是编码不一致。
后来我总结了三条铁律:
- 所有题库文件统一存成UTF-8(带BOM),避免Windows记事本无BOM导致的识别异常
- 导入工具自动检测编码,检测到GBK就先转码再入库
- Excel不是最终存储格式,入库后转为结构化数据库存储,Excel只作为编辑入口
这三条规则后来帮我避免了连环踩坑。尤其是“Excel只做编辑入口”这个决定,让题库的权威源从文件变成了数据库,后续做版本对比、增量更新都方便得多。
5.3 动态加载页面:题目没渲染完就截图的崩溃时刻
有些在线测验页面,题目是异步加载的,左侧题目列表先出现,右侧内容区过一会儿才渲染。有一次我按快捷键截图,截下来的图片里题目区域是空的——因为数据还没加载到页面。
这个问题的本质是时序问题。解决办法是加一个“截屏前等待”机制:快捷键触发截屏前,先检测页面中是否包含题目容器元素,如果容器存在但内容为空,就等待200毫秒再截;如果连续检测3次都为空,才提示用户页面可能不存在题目。
如果页面使用了复杂的前端框架(比如动态渲染的表格),可以考虑监听DOM变化,等目标节点数量稳定后再触发截图。这是从数据可视化工具里借鉴来的等待策略,能用代码轮询解决的事,不要让人去等。
5.4 反自动化识别:如何让工具看起来“正常”
虽然我的软件不自动提交答案,但频繁的截屏操作和键盘输入仍然可能被页面里的行为监控脚本注意到。有一段时间,团队反馈工具在某个平台上频繁失效,后来定位到是页面脚本检测到了异常频繁的剪贴板读取。
这里牵扯到如何让行为更接近真实用户的问题,我做了几个调整:
- 每次操作之间加入300到800毫秒的随机延迟,模拟人的反应时间
- 不要长按快捷键连续触发,改为间隔2秒以上
- 剪贴板读取失败时不自动重试,避免异常请求特征
- 不在页面打开时立即注入面板,等用户首次按键后才加载
你会发现这本质上是“行为特征模拟”的范畴。但我要说清楚:这些手段是为了让自己的测试环境不被干扰,而不是为了规避正式的、公平的考试纪律。工具必须服务于合法的训练场景,越过红线就毫无意义。
5.5 长尾题目匹配不到:从“每次失败”到“沉淀资产”
最让团队头疼的是长尾题目——数据量大、出处杂、题库里没有记录的那些题。早期每次匹配失败就直接展示网页搜索,用户体验很差,因为搜索引擎的摘要经常答非所问。
后来我设计了一个“未命中题目回收机制”:匹配失败时,把题目文本、抽取的关键词、时间戳自动保存到一个等待人工处理的表。每周末花一个多小时,把本周未命中的题目统一补进题库,并标记上来源。
这个机制运行了一个月后,本地题库的命中率从71%提升到89%。而且更妙的是,hit_count字段会告诉你哪些补充的题目被频繁命中,形成正循环。与其追求一次做到100%的覆盖,不如建立一个可持续迭代的机制。
6. 实测数据:这套方案究竟能跑多快,效果如何
数据是检验工具价值的标准。以下是我们用三个场景各测试30道题后得到的统计:
| 场景 | 平均响应时间 | 本地命中率 | 用户确认用时 | 最终正确率 |
|---|---|---|---|---|
| 文本粘贴题 | 0.19秒 | 92% | 0.4秒 | 100% |
| 截图识别题 | 1.42秒 | 87% | 0.5秒 | 97% |
| 网页兜底题 | 1.8秒 | 无法统计 | 1.2秒 | 72% |
数据里有个有意思的发现:最终正确率比本地命中率高出一截。原因就是半自动流程的“人机协同”——即使工具推荐了错误答案,熟悉题目内容的用户在确认时也会发现问题,最终选择正确的选项。这说明设计时坚持“给推荐、不给结论”的思路是对的。
6.1 实战中的一次惊险时刻:反向题差点酿成大错
我记得有一次模拟比赛,某道题是“下列不属于五大湖区的是哪一个?”题库里存了一道正向题“下列属于五大湖区的是哪一个?”,两者关键词几乎重合。相似度计算得出的结果是0.89,略低于0.92的置信阈值,于是走了模糊匹配路线,推荐了几条相似题目供用户参考。
结果界面上一字排开的全是“属于”的题目,用户差点直接选错。幸好后来我专门为“反向题”增加了识别分支,同时把此类匹配结果单独标记为红色提示边框,从那次以后,反向题的翻车事故就再也没有出现过。
6.2 界面迭代:从“十字枪膛”到“极简卡片”
虽然我是个技术导向的人,但界面体验对答题辅助软件的影响远超预期。最初面板上堆了关键词、来源、相似度、备选列表等信息,测试组成员反馈“看得眼花缭乱,根本来不及判断”。
我后来的设计原则是:紧急状态下,人只需要一张信息卡。面板中央显示答案文字,左上角显示置信度颜色(绿=高置信、黄=中置信、红=低于70%)。其它信息通过点击“详情”展开,默认全部隐藏。极简界面让确认时间几乎减半,实测中用户平均确认用时从0.85秒降到了0.4秒。
6.3 数据统计功能反哺题库建设
工具上线一个月后,我统计了系统收集的使用数据:总共检索2000多次,本地命中率88%,平均响应时间0.32秒。更重要的是,通过hit_count找到了20道高频题目,这些题在题库里命中次数超过30次,说明团队训练重点就在这些题上。
于是我做了一个“备考热力榜”,把这些高频错题、高频匹配题生成一个独立子库,用于每次比赛前的快速模拟训练。这个功能原本不在计划内,但它成了一个出人意料的杀手锏——训练效率提升了一大截,团队一致认为比增加OCR准确率更有价值。
7. 给想复刻的人留几句实在话
先说一句最实在的总结:这个项目里最值钱的部分不是OCR怎么调优、不是相似度算法怎么写,而是对“人工确认环节”的坚持。就在上周,我和团队里那位负责出题的同事聊天,他说了一句话让我印象很深:“工具确实能帮我们查得快,但真正拿高分的还是那些理解知识点、能一眼判断推荐答案对错的人。”
如果你也想复刻类似的答题辅助软件,我有几条经验供你参考:
第一,先建题库再写代码。题库是内容是根本,代码只是载体。我一开始先花了一周整理题库,后面开发时直接拿真实数据测试,效率碾压用假数据开发。
第二,阈值要拿真实题目校准。0.92这个数字不是拍脑袋来的,而是反复调出来的。你的领域可能不一样,务必用自己整理的题目集去校准。
第三,只要做“推荐”就永远有底气。人永远是决策者,工具永远只是辅助。保持这个边界感,工具做出来大家用得安心,你也能睡得着觉。
最后再分享一个维护上的小技巧:代码仓库里放一个CHANGELOG.md,每调整一版算法、每修掉一个识别坑,就顺手记一笔。半年下来回看,那整份变更历史本身就是你踩过所有坑的总结,比任何讲解文章都珍贵。
这个软件目前还在持续迭代中,下一步我计划加入语音播报功能,让主持人在读题时工具能把检索到的答案转为语音提示,进一步缩短手动确认的时间。但那是后话了,先把当前半自动版本用好,已经能帮上大忙。