☰
基于DeepSeek与Dify的拍照解题系统实战:OCR识别与大模型解答全流程
2026/10/9 3:45:27 网站建设 项目流程

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 整体数据流是怎么走的

整个系统的数据流我画不了图,但可以用文字描述清楚:

  1. 用户在手机或电脑上拍照,通过一个简单的Web页面上传图片
  2. 后端服务接收到图片,调用PaddleOCR进行文字识别,得到题目的文本内容
  3. 将识别出的文本通过Dify的API触发工作流
  4. Dify工作流中,先对文本做清洗和格式化,然后组装成提示词
  5. 调用DeepSeek的API进行题目解答
  6. 将解答结果返回给前端,展示给用户

这个链路里,Dify承担的是编排和调度的角色,DeepSeek承担的是推理和生成的角色,PaddleOCR承担的是感知的角色。三者各司其职,耦合度低,任何一个环节出问题都容易定位和替换。

2.3 为什么不用端到端的多模态方案

你可能会问,现在多模态大模型这么强,直接把图片丢给模型不就行了,为什么还要OCR这一步?我实际测试过,多模态模型直接读题,对于清晰的印刷体题目效果还行,但一旦图片有倾斜、光照不均、手写痕迹,识别准确率就明显下降。而且多模态模型的token消耗更大,成本更高。用OCR先把文字提取出来,相当于把感知和推理解耦,OCR专注于把字认准,大模型专注于把题解对,各干各的擅长的活。实测下来,这种方案的稳定性和成本都更优。

3. 环境准备与基础服务搭建

3.1 硬件和系统环境说明

我用的是一台自己组的小服务器,配置不算高:Intel i5处理器,16GB内存,512GB固态硬盘,没有独立显卡。这个配置跑PaddleOCR的CPU版本完全够用,识别一张题目图片大概1到2秒。Dify和DeepSeek的调用都是走网络请求,对本地硬件要求不高。

操作系统用的是Ubuntu 22.04,Docker和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_cls=True, lang='ch') @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, cls=True) 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(host='0.0.0.0', port=5002)

启动这个服务之后,你就有了一个本地的OCR接口,地址是http://你的服务器IP:5002/ocr。

提示:PaddleOCR第一次运行时会自动下载模型文件,需要保证网络通畅。模型文件大概几百MB,下载一次之后就会缓存在本地。

4. Dify工作流的核心编排细节

4.1 工作流节点的整体设计

在Dify里创建一个新的工作流应用,我给它起名叫“拍照解题助手”。整个工作流包含以下几个核心节点:

  1. 开始节点:接收两个输入变量,一个是image_url(图片地址),一个是question_text(可选的文本题目,用于纯文字输入的场景)
  2. HTTP请求节点:调用PaddleOCR服务,把图片转成文字
  3. 代码节点:对OCR结果做清洗,去掉多余的空格、换行,合并断行的公式
  4. 条件分支节点:判断题目类型,是数学题、物理题还是其他,走不同的提示词模板
  5. LLM节点:调用DeepSeek进行解答
  6. 结束节点:输出解答结果

这个设计里,条件分支节点是我后来加的。一开始所有题目都用同一个提示词,后来发现数学题和文科题的解答方式差别很大,数学题需要强调步骤推导,文科题需要强调要点归纳,所以拆成了不同的分支。

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,拍照直接上传,体验会更流畅。

这套东西我从开始折腾到基本可用,大概花了两个周末的时间。中间踩了不少坑,但跑通之后确实省心,孩子现在遇到不会的题,拍一下就能看到详细的解题步骤,我也能从“人肉解题机”的角色里解放出来。如果你也想搭一套,建议先从最小可用版本开始,跑通主链路之后再逐步优化,不要一上来就追求完美。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询