ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DeepSeek API调用指南:从文本到图像分类的统一结构化输出方案

DeepSeek API调用指南:从文本到图像分类的统一结构化输出方案 简介面向具备Python基础的技术开发者一份围绕DeepSeek智能接口的图像与文本分类调用指南系统演示如何通过POST请求完成云端模型接入。内容涵盖账号注册与接口密钥获取、requests和Pillow等程序库的安装、HTTP请求头与数据格式设置、图像文件上传与文本内容提交的差异、图像尺寸预处理、成功返回中标签与置信度的提取并针对无效密钥、请求负载格式错误等常见异常给出排查思路从环境准备到代码调试覆盖完整调用链路。阅读后可独立完成接口认证、请求构造、响应解析与错误定位并将图像分类、文本分类能力集成到内容审核、图片识别、文本理解等业务系统在原有系统中快速嵌入智能分类调度模块搭建自动化分类应用原型。资源为docx格式只有1个文件压缩包大小约17KB结构紧凑便于按步骤对照实现并复用示例代码。已有2675人学习适合正在集成云端智能服务能力的开发团队快速上手。1. DeepSeek API调用指南为什么图像分类和文本分类都值得用同一个模型接口做视觉和文本的工程师都清楚一条模型链路打天下的痛点从来不在模型精度而在工程化成本。DeepSeek API的入口并不复杂一个HTTP请求就能拿到推理结果但它真正让团队省事的点是文本分类可以直接构造Prompt图像分类也能绕开训练专用视觉模型统一走结构化输出。这套方案的边界是模型输入限制和任务对时序依赖的要求适合快速验证、中小流量场景也适合给非算法团队做标注辅助。本文的目标读者是两类人一类是刚接触API、想用DeepSeek把现有文本和图片分类能力补上的初级工程师另一类是已经部署过其他模型、想比较DeepSeek与本地推理方案差异的熟手。全文会先讲调用方式再分别给文本分类和图像分类的可复现步骤最后把最常翻车的错误、超时和上下文窗口问题一起排掉。2. 用Python跑通DeepSeek API最小调用代码与三个必须改的参数API调用的第一步不是写代码而是把请求模型、鉴权方式和返回解析三条链路理清。DeepSeek接口与常见大模型服务一致使用Bearer Token鉴权请求体是一个标准JSON里面承载模型名、消息列表和生成参数。我建议用OpenAI SDK兼容模式因为团队里如果已经有人写过GPT调用代码改动量最小如果你更喜欢直接用requests也完全可行只是要手动拼JSON并处理流式响应下面两种都会给出。2.1 拿到API Key后先验证连通性再写业务代码不先把连通性跑通就写上层逻辑等于把一个黑匣子塞进业务代码里后面出问题很难分清是网络、密钥还是模型问题。我一般先在一个空目录里建test_connection.py只做一次最小请求from openai import OpenAI client OpenAI( api_keysk-你的key, # DeepSeek控制台复制不要硬编码到仓库 base_urlhttps://api.deepseek.com # 官方兼容端点 ) resp client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: 你是分类助手只输出JSON。}, {role: user, content: 把今天天气不错分类为正面或负面。} ], temperature0, max_tokens200, response_format{type: json_object} ) print(resp.choices[0].message.content)这段代码的核心价值在于确认三件事网络能否到达api.deepseek.com、Key是否有权限、模型是否支持response_format参数。temperature0是分类任务的常规起点能降低输出随机性max_tokens给200足够一个标注结果如果请求报错优先检查base_url末尾是否多斜杠、Key前面是否有空格。最常见的失败是代理环境下requests走到了系统代理Python的NO_PROXY没把api域名排除返回的不是鉴权错误而是超时。确认连通后把api_key挪到环境变量或本地.env文件不要以字符串形式出现在代码里。团队协作时我会给.env.example模板并让CI里加一条env_check任务避免有人提交真实密钥导致泄露。这一步做完才能进入业务封装。2.2 三类必调参数temperature、max_tokens、response_format参数选错不会让模型报错但会让你的分类结果飘。我在生产环境里固定这套配置超过两个月踩过的坑都集中在下面三个参数上。参数推荐值影响踩坑点temperature0输出稳定类别不跳变调到0.7以上后同一句文本可能返回两个类别且JSON字段顺序不稳定max_tokens文本分类200图像分类400截断导致JSON不完整直接解析失败对长文本先截断到3000字符再让max_tokens覆盖输出余量response_format{type: json_object}保证返回可解析JSON不设置时模型偶尔会额外输出解释性文字增加解析成本有人担心temperature0会让模型变得死板但分类任务的本质是判别而不是创作随机性越低越可复现。如果你在跑小批量实验并想验证多样性可以把temperature在0到0.3之间微调超过0.5后对同一批样本的重复标注一致性明显下降。response_format这个参数有个隐含前提请求消息里必须包含“json”这个词否则服务端会拒绝并返回400。具体来说system提示词或user消息里至少要有一句“以JSON格式输出”这算接口的一个细节不读懂就容易翻车。另外如果解析失败时能看到原始返回内容八成是JSON键名里有中文引号混入这就是后面避坑章节要解决的解析问题。3. 文本分类应用从Few-shot模板到批量标注的完整链路文本分类是DeepSeek API最容易上手的场景因为模型的指令跟随能力直接替代了你训练一个BERT分类器的过程。常见的做法是用系统消息定义角色在用户消息里给几条样例和待分类文本最后让模型输出结构化JSON。关键是Few-shot样例的质量以及标签集合的定义是否互斥。下面是一个我实际用在工单分类和评论情感标注上的模板可直接复制修改。3.1 构造Few-shot提示词三个样例比十个样例更稳定给模型太多样例会让输出变长、延迟增加而且深层模型在一个上下文里看太多相似样例后会开始“模仿”样例的语气而不是执行分类逻辑。我测试过不同规模的few-shot三个正反样例交叉覆盖效果明显优于一个样例或十个样例。原因在于三个样例可以构成一个简单的决策边界模型能从中推断出类别分离的依据而不是单纯机械找相似文本。SYSTEM_PROMPT 你是一个文本分类引擎。只输出json_object不要输出其他任何内容。 分类规则 - 类别必须是 label 字段取值只能从 [投诉, 咨询, 表扬, 其他] 中选。 - confidence 字段表示置信度0到1之间保留两位小数。 - 如果文本疑似同时属于多个类别label 取最明显的一个并降低 confidence。 FEW_SHOT [ {role: user, content: 你们售后电话打了三次都打不通}, {role: assistant, content: {label: 投诉, confidence: 0.95}}, {role: user, content: 怎么修改收货地址}, {role: assistant, content: {label: 咨询, confidence: 0.90}}, {role: user, content: 快递包装很用心谢谢}, {role: assistant, content: {label: 表扬, confidence: 0.85}}, ] def classify_texts(texts: list[str]) - list[dict]: results [] for text in texts: messages [{role: system, content: SYSTEM_PROMPT}] messages.extend(FEW_SHOT) messages.append({role: user, content: f待分类文本{text}}) # 此处省略实际调用参考第二章代码 results.append(parse_json_response(resp)) return results这段代码没有把样例做成模板字符串拼接而是用list保存对话结构以便后续调整样例顺序做消融实验。样例顺序有讲究正例放中间会比全放前面效果好一些因为开头和结尾的样例更容易被模型记住。如果你发现内容安全相关文本被误分成“其他”尝试在系统提示里加一条“若文本包含色情、暴力或政治敏感内容归为‘涉嫌违规’类别”不要直接写敏感词样例。批量分类时要注意一个细节每条文本独立构造一个请求虽然慢但每个请求的上下文干净分类边界稳定。把100条文本塞进一个请求让模型逐条标注听起来省调用量但输出长度容易被max_tokens截断且一旦第一个JSON解析失败整批结果都报废。我跑过对比独立请求在100条数据上的耗时是串行的但可维护性和错误隔离价值更高。3.2 高效解析与批量封装兼容JSON解析函数模型输出JSON时最容易出现的问题有两个一是字段值带了额外的解释性文字二是字符串内部有未转义引号。我写了一个能容忍上述情况的解析函数放在团队的公共工具库里已经跑了很久效果稳定import json import re def parse_json_response(raw_text: str) - dict: if not raw_text: raise ValueError(空响应) # 先直接解析成功最快路径 try: return json.loads(raw_text) except json.JSONDecodeError: pass # 提取最外层 JSON 对象避免被多余文字干扰 match re.search(r\{.*\}, raw_text, re.DOTALL) if not match: raise ValueError(f响应中没有JSON对象: {raw_text[:200]}) try: return json.loads(match.group(0)) except json.JSONDecodeError as e: # 最后一个兜底修复明显的转义错误后重试 fixed re.sub(r(?!\\)(\w)(?!:), r\1, match.group(0)) return json.loads(fixed) def classify_batch(texts: list[str], concurrency: int 8) - list[dict]: import concurrent.futures with concurrent.futures.ThreadPoolExecutor(max_workersconcurrency) as ex: return list(ex.map(classify_texts, texts))解析函数采用三级策略直接解析、正则提取、转义修复。第三级的正则只处理最简单的引号错配遇到复杂错误宁可让它抛异常也不过度猜测因为错误响应放进数据标注里会造成静默误标。并发参数concurrency我建议控制在8到16之间调太高会遇到服务端限流返回429或连接被重置反而整体变慢。每轮调用之间加0.2秒到0.5秒的随机抖动能显著降低限流触发率。4. 图像分类应用DeepSeek不是视觉模型但三个方案能完成分类图像分类是DeepSeek API调用里最容易误用的一环因为DeepSeek这些文本模型本身不接收图片输入。检索热词里有人问“DeepSeek如何识别图片”或“DeepSeek API图像分类”这类问题核心要理解的是文本模型做图像分类必须走间接路径常见做法是“视觉编码器提特征 文本模型做语义分类”或“外部模型生成候选标签 DeepSeek做结构化选择”。不是把图片字节塞进请求那是做不通的。4.1 方案一用CLIP提取图像特征让DeepSeek基于类别文本打分这个方案的核心思路是CLIP模型把图片和文本映射到同一个向量空间然后我们把“类别标签”也写成文本计算图片特征与每个类别文本特征的相似度把CLIP给出的粗粒度结果交给DeepSeek做语义整合与结构化输出。ClIP负责“看图”DeepSeek负责“解释和决策”各干各擅长的事。import torch import clip from PIL import Image # 加载CLIP模型ViT-B/32是速度与精度较平衡的选择 device cuda if torch.cuda.is_available() else cpu model, preprocess clip.load(ViT-B/32, devicedevice) def image_to_clip_scores(image_path: str, labels: list[str]) - dict: image preprocess(Image.open(image_path)).unsqueeze(0).to(device) text_tokens clip.tokenize([fa photo of {label} for label in labels]).to(device) with torch.no_grad(): image_features model.encode_image(image) text_features model.encode_text(text_tokens) # 归一化后计算相似度 image_features image_features / image_features.norm(dim-1, keepdimTrue) text_features text_features / text_features.norm(dim-1, keepdimTrue) logits (image_features text_features.T).squeeze(0) probs logits.softmax(dim-1) return {label: float(prob) for label, prob in zip(labels, probs)}在图像分类场景里labels这部分决定了上限。如果标签描述太泛比如只写“猫”和“狗”CLIP的表现尚可如果标签是“售后工单截图”和“产品宣传图”那就要把描述写得更具体例如“一张包含订单编号和售后流程的截图”。另外CLIP类别描述建议统一加前缀a photo of这种前缀在很多开源项目里验证过但如果你想用于文档截图分类把前缀改成“a screenshot of”可以显著提升效果这一点我用在单据分类里验证过值得试。把CLIP相似度分数传给DeepSeek让模型根据分数做最终决策会比直接用CLIP结果更稳定。4.2 方案二用本地视觉模型生成标签候选再让DeepSeek做结构化决策如果团队里已经跑着YOLO或ResNet之类的本地视觉模型不必重复接入CLIP直接把它们输出的标签候选、置信度、目标框等结构化数据传给DeepSeek让DeepSeek基于这些数据做更高层的分类判断。这个方法避免了大图传输只传文本描述延迟更低也适合隐私敏感场景。# 假设已经用YOLO对图片做了一次推理得到如下结果 yolo_result [ {class: person, confidence: 0.92, bbox: [10, 20, 100, 180]}, {class: helmet, confidence: 0.87, bbox: [15, 22, 95, 175]}, {class: cell phone, confidence: 0.61, bbox: [200, 300, 240, 340]}, ] def build_image_classification_prompt(yolo_results: list[dict], task_desc: str) - str: return f 任务描述{task_desc} 模型检测到的目标与置信度如下 {json.dumps(yolo_results, ensure_asciiFalse)} 请根据检测目标判断图片整体类别只输出一个label字段取值从候选集合中二选一。 候选安全合规 / 违规。 这类方式的本质是“套壳决策层”。本地模型负责找到目标DeepSeek负责理解目标之间的关系。比如检测到人和头盔DeepSeek就知道画面大概率是安全作业场景但如果检测到手机在操作台上它可能判断为违规。加了bbox数据后模型还能根据目标坐标相对位置做辅助判断——比如手机在人员附近比手机出现在角落更可能构成违规。这种从结构化数据推语义的能力是传统规则判断很难覆盖的也是DeepSeek这类大模型在图像分类上的增量价值。5. DeepSeek API避坑指南4条让调用翻车的真实故障排查记录API接入的故障大多是等报错才发现的等线上挂了再回头看日志代价已经高了。下面四条是我在实际调用中遇到过的不属于官方文档里写得清楚的边界但每条都能让你少走半天弯路。5.1 400错误response_format要求消息里必须出现“json”这个词现象请求明明带了response_format{type: json_object}服务端依然返回400错误信息指向参数不合法或不兼容。原因DeepSeek的API并不是无条件接受json_object模式它要求请求消息中至少有一处包含“json”字样以此确保模型知道要输出JSON。解决在system提示词或user消息里明确写上“只输出JSON不要其他内容”。如果你把system提示词写成“你是分类助手”即使response_format配置正确也会偶发400加上“以json格式输出”后问题消失。这是接口设计的一个隐藏要求不踩一次很难想起来。5.2 1048576 token上下文窗口错误长文本批量输入被截断现象单次请求里塞了大量文本报错信息提示最大上下文长度是1048576个token超过限制。原因模型上下文窗口虽然有上限但单次请求的文本长度在某些路由配置下会被更严格限制或者你输入的长文本未经截断直接打满了窗口。解决对文本分段按标题、段落或固定长度截断到3000字符以内分类任务一般不需要全文参与前300个字符就够做类别判断。我在做长文档分类时先把正文按5000字符切块每块独立请求再用投票或取最高置信度来汇总。你也可以在请求里加truncate参数或手动切片但手动切更可控。5.3 超时重试导致重复写入加一个request_id做幂等现象一次请求因为网络抖动超时客户端重试后下游系统收到两条相同内容的分类结果产生重复标注。原因普通HTTP请求天然不幂等同一个提示词重发两次就是两次独立请求服务端不可能知道它们是否同源。解决在请求头带上自定义的X-Request-ID服务端或你的网关做幂等判定如果用的是消息队列驱动标注任务把request_id作为队列消息的业务键。我见过有团队用时间戳做标识并发一上来就冲突最后还是用UUID。每次请求前uuid.uuid4()生成一个就好了成本几乎为零。5.4 成本失控搭配免费大模型API或本地vLLM部署来分流现象批量分类任务跑完后账单比预期高一截尤其是图像分类方案里给DeepSeek发送了大段检测结果JSONtoken用量被低估。原因图像分类里传bbox坐标和置信度列表看起来是几行但实际一算几千token量一大成本直线上升。解决高频低难度样本走本地vLLM部署的DeepSeek蒸馏小模型或者用免费大模型API做初步粗分类只有低置信度样本才请求官方API。DeepSeek官方API的价格本身不算贵但如果每天百万级调用成本仍然不可忽视。我的常见做法是加一层前置规则文本长度小于20字符且含“谢谢”“好的”直接归为“其他”不进模型大概能省15%的调用量。5.5 模型返回JSON字段顺序不稳定解析容错设计现象同一提示词多次调用置信度和标签字段的顺序不一样有的返回里还多了空格或换行。原因模型生成是概率采样字段顺序不在训练目标里。解决解析时不要用json[label]这种硬编码顺序用data.get(label)和data.get(confidence)。如果字段被模型拼成了Label大写L再加一层小写键归一化。解析函数要写成幂等的即同一个字符串解析多次结果一致这样重试逻辑才不会把结果弄乱。6. 进阶把分类结果变成高质量训练数据——置信度校准与主动学习当你跑通文本和图像分类后下一步不是继续堆样本而是建一条“模型分类→人工审核→回流训练”的闭环。DeepSeek API返回的confidence字段并不代表真实概率它更像是模型内部自评的置信度直接用它当阈值选样本会稍微乐观。我需要校准一下抽200条结果按confidence分桶统计每个桶的人工复核准确率拟合一条校准曲线然后定一个符合业务要求的阈值。# 以置信度分桶的校准逻辑 def calibration_buckets(samples: list[dict], bins: int 5) - list[dict]: buckets {} for s in samples: conf s[confidence] bucket min(int(conf * bins), bins - 1) buckets.setdefault(bucket, []).append(s) result [] for bucket_idx in sorted(buckets): items buckets[bucket_idx] acc sum(1 for it in items if it[human_label] it[model_label]) / len(items) result.append({ bucket: bucket_idx, avg_confidence: sum(it[confidence] for it in items) / len(items), human_accuracy: acc, sample_count: len(items) }) return result校准之后你会看到类似“置信度0.9的桶人工准确率只有0.82”的现象这在长尾类别或多标签近义类别上尤其明显。治理方式很简单confidence低于0.8的样本不直接入库进入人工标注池人工修正后的样本重新组合进下一次提示词的few-shot里。用这种思路跑两轮到三轮分类准确率能提升到直接可用水平同时人工介入量逐步下降。图像分类场景里CLIP的原始分数和DeepSeek的置信度可以加权合并成一个综合分我一般给CLIP权重0.3DeepSeek权重0.7这条经验值适用于大多数供应链检测和单据分类场景。我的习惯是每跑完一批任务把所有解析失败或human_accuracy低的样本导出一份JSONL文件文件名带上日期和模型版本。下次换模型参数或升级模型版本时用这份文件重跑一次回归对比。Dify或FastAPI搭一个简单的前端标注界面给运营同事用比在代码里人工改JSON靠谱得多。这条路我也是一步一步走出来的中间翻车不少希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进