
1. 为什么我要折腾一套拍照解题系统拍照解题这个需求最早是我家孩子上初中之后冒出来的。每天晚上做作业遇到不会的题就喊我我过去一看有些题我还能讲有些题我自己都得想半天。后来我就琢磨能不能做一个东西拍张照片题目自动识别出来然后给出解题思路和答案。市面上确实有这类App但要么广告多要么答案质量参差不齐要么就是收费贵得离谱。作为一个搞技术的人我第一反应就是自己搭一套。这套系统的核心链路其实不复杂拍照上传 → OCR文字识别 → 大模型理解题目并解答 → 返回结构化结果。听起来简单但真要做稳定、做可用中间有不少坑。我选择的技术栈是DeepSeek Dify原因后面会详细说。DeepSeek负责题目理解和解答Dify负责编排整个工作流OCR环节我用的是PaddleOCR做本地识别整体跑在一台自己家里的服务器上。这篇文章我会把整个实验过程完整拆开从架构设计、环境准备、OCR接入、Dify工作流编排、DeepSeek提示词调优到实际踩过的坑和排查方法全部讲清楚。适合有一定动手能力、想自己搭一套智能解题系统的朋友参考。不需要你是AI专家但基本的Linux操作和Docker使用得会。注意本文涉及的所有操作均在自有设备上完成使用的模型和工具均为公开可获取的开源或商业API服务请确保你的使用场景符合相关服务条款。2. 整体架构设计与技术选型思路2.1 为什么是DeepSeek加Dify这个组合先说DeepSeek。我对比过几个主流大模型在数学题和理科题上的表现DeepSeek在中文理科题目上的理解能力确实突出尤其是带公式的题目它能比较准确地还原解题步骤。而且DeepSeek的API价格相对便宜对于我这种每天可能跑几十上百道题的使用频率来说成本可控。另外DeepSeek支持较长的上下文有些题目带图表描述或者多小问上下文长了也不会丢信息。再说Dify。Dify是一个开源的LLM应用开发平台核心价值在于它把工作流编排这件事做得足够直观。我不需要写大量的后端代码来串联OCR、提示词组装、模型调用、结果解析这些环节在Dify的可视化界面里拖拽节点就能完成。而且Dify支持知识库、变量传递、条件分支对于解题这种需要根据题目类型走不同处理逻辑的场景非常合适。OCR环节我选的是PaddleOCR原因是它对中文的识别效果好尤其是印刷体题目准确率很高。而且它可以本地部署不依赖外部服务隐私性更好。有些朋友可能想用云端的OCR服务也可以在Dify里换一个HTTP请求节点就行后面我会提一下怎么替换。2.2 整体数据流是怎么走的整个系统的数据流我画不了图但可以用文字描述清楚用户在手机或电脑上拍照通过一个简单的Web页面上传图片后端服务接收到图片调用PaddleOCR进行文字识别得到题目的文本内容将识别出的文本通过Dify的API触发工作流Dify工作流中先对文本做清洗和格式化然后组装成提示词调用DeepSeek的API进行题目解答将解答结果返回给前端展示给用户这个链路里Dify承担的是编排和调度的角色DeepSeek承担的是推理和生成的角色PaddleOCR承担的是感知的角色。三者各司其职耦合度低任何一个环节出问题都容易定位和替换。2.3 为什么不用端到端的多模态方案你可能会问现在多模态大模型这么强直接把图片丢给模型不就行了为什么还要OCR这一步我实际测试过多模态模型直接读题对于清晰的印刷体题目效果还行但一旦图片有倾斜、光照不均、手写痕迹识别准确率就明显下降。而且多模态模型的token消耗更大成本更高。用OCR先把文字提取出来相当于把感知和推理解耦OCR专注于把字认准大模型专注于把题解对各干各的擅长的活。实测下来这种方案的稳定性和成本都更优。3. 环境准备与基础服务搭建3.1 硬件和系统环境说明我用的是一台自己组的小服务器配置不算高Intel i5处理器16GB内存512GB固态硬盘没有独立显卡。这个配置跑PaddleOCR的CPU版本完全够用识别一张题目图片大概1到2秒。Dify和DeepSeek的调用都是走网络请求对本地硬件要求不高。操作系统用的是Ubuntu 22.04Docker和Docker Compose提前装好。如果你用的是其他Linux发行版或者Windows的WSL操作大同小异核心是Docker环境要正常。3.2 Dify的本地部署步骤Dify的部署官方推荐用Docker Compose我实际操作下来也是最省心的方式。步骤如下# 克隆Dify的代码仓库 git clone https://github.com/langgenius/dify.git # 进入docker目录 cd dify/docker # 复制环境变量文件 cp .env.example .env # 启动服务 docker compose up -d启动完成之后浏览器访问http://你的服务器IP:3000就能看到Dify的初始化页面。第一次访问需要设置管理员账号和密码设置好之后登录进去。注意Dify默认占用的端口是3000和5001如果你服务器上已经有其他服务占用了这些端口需要在.env文件里修改端口映射。我一开始就是3000端口被占用了排查了半天才发现。部署完成后你需要在Dify的设置里配置模型供应商。进入“设置”→“模型供应商”找到DeepSeek填入你的API Key。DeepSeek的API Key在官网注册后可以获取新用户通常有一定的免费额度后续按量计费。3.3 PaddleOCR服务的搭建PaddleOCR我单独部署成一个HTTP服务这样Dify可以通过HTTP请求节点调用它。安装PaddleOCR的步骤如下# 创建虚拟环境 python3 -m venv ocr_env source ocr_env/bin/activate # 安装PaddlePaddle和PaddleOCR pip install paddlepaddle pip install paddleocr # 安装Flask用于提供HTTP接口 pip install flask然后写一个简单的Flask服务来暴露OCR接口from flask import Flask, request, jsonify from paddleocr import PaddleOCR import os app Flask(__name__) ocr PaddleOCR(use_angle_clsTrue, langch) app.route(/ocr, methods[POST]) def recognize(): if image not in request.files: return jsonify({error: no image provided}), 400 file request.files[image] img_path /tmp/uploaded.jpg file.save(img_path) result ocr.ocr(img_path, clsTrue) texts [] for line in result[0]: texts.append(line[1][0]) full_text \n.join(texts) os.remove(img_path) return jsonify({text: full_text}) if __name__ __main__: app.run(host0.0.0.0, port5002)启动这个服务之后你就有了一个本地的OCR接口地址是http://你的服务器IP:5002/ocr。提示PaddleOCR第一次运行时会自动下载模型文件需要保证网络通畅。模型文件大概几百MB下载一次之后就会缓存在本地。4. Dify工作流的核心编排细节4.1 工作流节点的整体设计在Dify里创建一个新的工作流应用我给它起名叫“拍照解题助手”。整个工作流包含以下几个核心节点开始节点接收两个输入变量一个是image_url图片地址一个是question_text可选的文本题目用于纯文字输入的场景HTTP请求节点调用PaddleOCR服务把图片转成文字代码节点对OCR结果做清洗去掉多余的空格、换行合并断行的公式条件分支节点判断题目类型是数学题、物理题还是其他走不同的提示词模板LLM节点调用DeepSeek进行解答结束节点输出解答结果这个设计里条件分支节点是我后来加的。一开始所有题目都用同一个提示词后来发现数学题和文科题的解答方式差别很大数学题需要强调步骤推导文科题需要强调要点归纳所以拆成了不同的分支。4.2 OCR结果的清洗与格式化OCR出来的文本往往不干净常见的问题有公式里的符号识别错误、多行文字被拆散、题号后面多了空格、图片里的水印文字混进来了。我在代码节点里写了一段Python来做清洗def main(ocr_text: str) - dict: import re # 去掉多余的空格和换行 text re.sub(r\s, , ocr_text) # 去掉常见的页眉页脚水印 text re.sub(r第\d页, , text) text re.sub(r共\d页, , text) # 修复常见的OCR错误 text text.replace(×, x).replace(÷, /) # 在题号前加换行方便后续分段 text re.sub(r(\d[\.、]), r\n\1, text) return {cleaned_text: text.strip()}这段代码看起来简单但实际效果提升很明显。尤其是题号前加换行这一步让后续的提示词组装能更清晰地识别出题目的边界。实操心得OCR清洗不要过度有些“错误”其实是题目本身的特殊符号过度替换反而会改变题意。我的原则是只处理明确的格式问题不碰语义内容。4.3 提示词模板的设计与调优提示词是整个系统的灵魂。我前后改了十几版最终稳定下来的模板大概是这样的你是一位经验丰富的中学理科老师擅长用清晰的步骤解答题目。 请根据以下题目内容给出详细的解题过程。 要求 1. 先分析题目考查的知识点 2. 分步骤推导每一步都要说明依据 3. 最终给出明确答案 4. 如果题目信息不完整请指出缺少什么条件 题目内容 {{cleaned_text}}这个模板的关键在于角色设定和输出格式约束。角色设定为“中学理科老师”之后DeepSeek的回答风格明显更贴近教学场景不会跳步。输出格式的四条要求让答案结构统一前端展示的时候也更好排版。对于文科类题目我用了另一个模板重点放在要点归纳和答题框架上这里不展开。4.4 变量传递与上下文管理Dify工作流里变量传递是通过节点的输入输出映射来完成的。开始节点的image_url传给HTTP请求节点HTTP请求节点返回的OCR文本传给代码节点代码节点输出的cleaned_text传给LLM节点。每个节点的输出变量名要对应好不然会报变量未找到的错误。我踩过的一个坑是HTTP请求节点返回的是JSON格式如果直接在LLM节点里引用需要先解析出具体的字段。Dify的HTTP请求节点支持直接提取响应体中的字段在配置里指定response.body.text这样的路径就行。5. DeepSeek接入与解答质量调优5.1 DeepSeek API的配置要点在Dify的模型供应商设置里配置DeepSeek需要填API Key和API Base URL。DeepSeek的API兼容OpenAI的接口格式所以Dify里选择OpenAI兼容的供应商类型也能接。我直接选的DeepSeek供应商配置更简单。模型选择上DeepSeek有不同版本我日常用的是通用对话模型数学能力足够。如果你对推理能力要求更高可以切换到推理增强版本但响应时间会更长成本也更高。参数方面温度我设置在0.3左右。解题场景不需要太高的创造性低温度能让输出更稳定、更聚焦。最大token数根据题目复杂度设置一般800到1500够用复杂的多小问题目可以设到2000。5.2 解答质量的评估与迭代怎么判断解答质量好不好我的方法是准备一组测试题目涵盖不同难度和类型每次调整提示词或参数后跑一遍测试集人工评估答案的准确性和步骤完整性。我最初的一版提示词DeepSeek经常直接给答案不写过程。后来在提示词里明确要求“分步骤推导每一步说明依据”情况就好多了。还有一个问题是有些题目OCR识别有误模型会基于错误的信息强行解答后来我在提示词里加了“如果题目信息不完整或存在矛盾请指出”模型就会主动提示识别可能有问题。注意不要指望一次调优就完美。我的经验是提示词的迭代周期至少需要一周每天跑几十道题积累足够的bad case之后再针对性修改。5.3 成本控制的实际数据我统计过一段时间的使用数据平均每道题的OCR识别加DeepSeek解答成本大概在几分钱。如果每天跑100道题一个月下来也就几十块钱。相比市面上一些解题App的会员费这个成本是可以接受的。当然如果你用的是推理增强版本成本会翻几倍需要根据自己的需求权衡。6. 实操过程中踩过的坑与排查方法6.1 OCR识别不准的几种情况和处理OCR识别不准是最常见的问题我遇到的大致分三类第一类是图片质量问题比如拍照时光线太暗、角度太斜。这种情况没有太好的技术解法只能在前端加提示引导用户拍清楚。我在上传页面加了一行提示文字“请确保光线充足题目文字清晰可见”。第二类是公式和特殊符号识别错误比如分数线的位置、根号的范围。PaddleOCR对这类二维结构的识别确实有局限。我的处理方式是在清洗环节做常见符号的替换同时在提示词里告诉模型“OCR可能存在符号识别误差请结合上下文理解”。第三类是手写体识别。PaddleOCR对手写的支持有限如果是手写题目识别率会明显下降。这个目前没有特别好的办法只能建议用户尽量输入印刷体题目。6.2 Dify工作流常见的报错与解决Dify工作流跑起来之后我遇到过几个典型报错报错信息原因解决方法Variable not found节点间变量名不匹配检查每个节点的输入变量名是否与上游输出一致HTTP request timeoutOCR服务响应超时增加HTTP节点的超时时间或检查OCR服务是否正常Model API errorDeepSeek API Key无效或额度不足检查API Key和账户余额Workflow execution failed代码节点语法错误在Dify的代码编辑器里先测试运行这些报错在Dify的执行日志里都能看到详细信息排查的时候先看日志定位到具体节点再针对性处理。6.3 网络与部署相关的注意事项Dify部署在内网服务器上如果要从外网访问需要做端口映射或者反向代理。我用的是Nginx做反向代理配置里需要注意WebSocket的支持不然Dify的某些实时功能会不正常。另外DeepSeek的API调用需要服务器能访问外网。如果你的服务器网络环境有限制需要提前确认API地址是否可达。我一开始在内网测试环境里跑怎么都调不通后来发现是出口网络的问题。提示建议在Dify的工作流里加一个错误处理分支当LLM调用失败时返回一个友好的提示信息而不是直接报错。这样用户体验会好很多。7. 后续可以继续折腾的方向这套系统目前跑得比较稳定但还有不少可以优化的地方。我列几个自己打算继续做的方向供你参考。第一个是增加题目类型的自动分类。现在是用条件分支手动判断后续可以训练一个小的分类模型自动识别题目属于数学、物理、化学还是文科然后走对应的提示词模板。第二个是接入知识库。Dify本身支持知识库功能我可以把教材的公式表、常见题型解法整理成知识库让DeepSeek在解答时参考提高答案的准确性和一致性。第三个是增加错题本功能。把每次解答的题目和答案存下来用户可以回顾也可以定期复习。这个需要在Dify之外加一个数据库工作量稍大但价值很高。第四个是优化前端体验。现在的上传页面很简陋后续可以做成微信小程序或者App拍照直接上传体验会更流畅。这套东西我从开始折腾到基本可用大概花了两个周末的时间。中间踩了不少坑但跑通之后确实省心孩子现在遇到不会的题拍一下就能看到详细的解题步骤我也能从“人肉解题机”的角色里解放出来。如果你也想搭一套建议先从最小可用版本开始跑通主链路之后再逐步优化不要一上来就追求完美。