资讯动态

在 Jupyter Notebook 中集成 DeepSeek:交互式 AI 开发实践

发布时间:2026/10/8 8:51:46 来源:尧图企业网站定制
简介面向希望在 Jupyter Notebook 中系统掌握 DeepSeek 应用开发的读者这份 PDF 从原理到实操提供了完整学习路径适合数据从业者、算法工程师以及刚接触交互式 AI 开发的初学者。文档共 27 页内容先梳理 DeepSeek 的技术架构与应用场景再讲解 Jupyter Notebook 的安装、单元格操作、魔法命令、数据可视化等基础随后聚焦两者集成时的环境准备、模型加载、调用测试和常见报错处理后续章节则围绕交互式数据探索、模型开发与训练、性能优化与调试技巧展开并给出文本分类、图像识别、时间序列预测、推荐系统等贴近实战的案例。资源为单个 1.83MB 的 PDF 文件页面正文、图表和目录均完整清晰既可通读建立知识体系也可按章节快速定位所需内容。该资源已有 176 人学习适合希望通过 Jupyter Notebook 提升 AI 开发效率的读者。1. 从 Notebook 里直接对话 DeepSeek为什么这件事值得搭把 DeepSeek 放进 Jupyter Notebook等于给数据分析流程装上一个能随时调用的“对话大脑”。你不需要切窗口、复制粘贴结果也不用把大模型的输出当黑匣子——每一次调用、每一轮上下文、每一条 Prompt 变更都变成单元格里可回放、可修改、可重跑的实验记录。这个组合特别适合做三件事用自然语言快速探查数据、批量评测不同 Prompt 的效果、把大模型输出直接喂给 pandas 做下一步处理。成本低、门槛低但前提是得把接入方式和工程细节理顺。这篇笔记就按我自己常用的落地路径来拆从 API 选型、对话器封装、Prompt 管理到批量评测和常见翻车点每一步都给可以直接抄的代码。2. 接入方式与最小对话单元先跑通再谈架构2.1 选型官方 API、本地部署还是第三方兼容层DeepSeek 的接入通道主要有三条。第一条是官方 API走 OpenAI 兼容协议base_url 指向https://api.deepseek.com就能用适合绝大多数场景尤其是刚起步时。第二条是本地部署用 vLLM 或 llama.cpp 把模型权重跑在自己机器上适合数据敏感或需要反复微调 Prompt 的场景但显存和运维成本得自己扛。第三条是第三方兼容层比如某些云平台托管的 DeepSeek 镜像接口格式一样但“稳定性”和“价格”要额外甄别。我的建议很直接先走官方 API 把流程跑通再根据瓶颈决定要不要切本地部署。原因很简单——Notebook 里的开发效率取决于迭代速度官方 API 的接入成本最低出问题也最好排查。等你要批量跑几百条评测、或者对延迟有硬性要求时再上 vLLM 部署。如果你已经决定了本地部署那也要在 Notebook 里保留一个统一的调用接口这样切后端时只需要改base_url和api_key两个变量代码主体不动。2.2 最小调用单元封装一个永远返回结构化结果的函数先把环境准备好。官方 API 的 key 不要硬编码进 Notebook用环境变量或者单独的配置文件管理避免分享笔记时把密钥带出去。import os from openai import OpenAI api_key os.environ.get(DEEPSEEK_API_KEY, ) base_url os.environ.get(DEEPSEEK_BASE_URL, https://api.deepseek.com) client OpenAI(api_keyapi_key, base_urlbase_url, timeout60.0) def chat_once(messages, modeldeepseek-chat, temperature0.7, max_tokens2048): try: resp client.chat.completions.create( modelmodel, messagesmessages, temperaturetemperature, max_tokensmax_tokens, ) return { ok: True, content: resp.choices[0].message.content, usage: resp.usage.model_dump(), # 记录 token 消耗 error: None, } except Exception as e: # 把错误也结构化返回方便在 Notebook 里继续处理而不是中断 return {ok: False, content: None, usage: None, error: str(e)} result chat_once( [{role: user, content: 用一句话解释什么是交互式 AI 开发}] ) print(result[content])这段代码有几个值得注意的点。timeout60.0是必须显式设置的默认值在长文本生成时很容易触发超时。返回值统一做成字典结构ok字段用来判断成功与否——Notebook 里最常见的翻车方式就是异常直接抛出导致后续单元格全部失效结构化返回可以让错误在数据层面被捕获后面做批量评测时这个设计会省很多事。另外usage字段一定要拿到并落盘它是后面算成本、调参的重要依据。最后验证一下连通性用一句话的请求确认 key、base_url、网络三个环节都通。curl https://api.deepseek.com/v1/models \ -H Authorization: Bearer $DEEPSEEK_API_KEY \ -s | head -c 500如果这条命令能返回模型列表说明网络和鉴权都正常。实际使用中我遇到过网络代理导致请求不可达的情况curl 是最快定位手段。3. 把静态对话变成可回放的实验流程会话状态、Prompt 模板与参数面板3.1 做一个会记事的对话器用数据类保存上下文chat_once只能做单轮问答交互式开发的核心优势是“上下文连贯”。在 Notebook 里最自然的做法是定义一个对话器类把消息历史放在内存中每次调用追加到上下文里。from dataclasses import dataclass, field dataclass class Session: system_prompt: str 你是一个严谨的技术助手。回答要准确、简洁有不确定的地方要明确指出。 history: list field(default_factorylist) token_budget: int 4000 # 粗略的上下文预算防止无限膨胀 def start(self): self.history [{role: system, content: self.system_prompt}] return self def ask(self, user_msg: str, **kwargs) - str: self.history.append({role: user, content: user_msg}) # 简单启发式截断先把最老的对话去掉保留 system prompt while self._estimate_tokens(self.history) self.token_budget and len(self.history) 2: self.history.pop(1) # 永远不删下标 0 的 system prompt resp chat_once(self.history, **kwargs) if resp[ok]: self.history.append({role: assistant, content: resp[content]}) else: # 失败时不把错误当回答写进历史否则下轮会污染上下文 raise RuntimeError(resp[error]) return resp[content] staticmethod def _estimate_tokens(messages) - int: # 粗略估计中文约 1.5 token/字英文约 1 token/词 total 0 for m in messages: total len(m[content]) * 1.5 return int(total)这段代码的核心是pop(1)——它只在上下文超限时丢弃最早的对话轮次永远不会丢弃系统提示词。日常使用中我发现两个问题第一不要过度依赖这个 token 估算它只是防止上下文无限膨胀的保险丝真要精确控制应该用模型自带的 tokenizer第二ask方法在请求失败时直接抛异常而不是把错误信息写进history——如果你把 “请求失败请重试” 这种内容写进历史下一轮模型会真的以为这是你的指令回答会变得很怪。这个坑我踩过后面还会细说。3.2 Prompt 模板化同一个问题换不同人设对比才有效交互式 AI 开发里最常做的事就是“同一个问题换 Prompt看差异”。如果每次都手写整个 Prompt对比时很难控制变量。我一般用string.Template或 f-string 做模板再配合一个简单的参数面板。from string import Template PROMPT_TEMPLATE Template( 你是一名资深数据分析师。 请基于以下背景回答用户问题。 背景 $background 用户问题 $question 回答要求 - 先给出结论再解释思路 - 如果信息不足明确指出缺什么 ) def make_prompt(background: str, question: str) - str: return PROMPT_TEMPLATE.substitute(backgroundbackground, questionquestion) # 典型的 Notebook 用法把参数集中放在一个单元格改完重跑 background 我们有一个电商订单表字段包括 order_id, user_id, amount, created_at question 如何识别刷单行为请给出 SQL 思路 prompt make_prompt(background, question) session Session(system_prompt你擅长 SQL 和数据分析) session.start() answer session.ask(prompt, temperature0.3, max_tokens1024) print(answer)这里把system_prompt和prompt分开了。system_prompt定义模型的人格与回答边界prompt定义当次任务的具体要求。对比实验时要改的是prompt而system_prompt保持不变这样结果差异才可归因。参数temperature0.3也是刻意设置的。做数据分析类任务时我会把温度调低来减少幻觉做创意类任务时才调高到 0.8 以上。如果某次实验忘了记录温度那结果几乎不可复现——同一段 Prompt 在不同温度下输出差异可能非常大。4. 单轮到批量在 Notebook 里搭一个可复现的评测管道4.1 批量评测从人工点按到数据集驱动交互式开发做到一定阶段你就会发现单条 Prompt 的问询不足以支撑决策。比如你改了系统提示词想知道整体效果是变好还是变坏得拿一二十条代表性问题上机器跑分。在 Notebook 里做批量评测核心是把输入数据、模型配置、输出结果三者分离。import pandas as pd import json import time # 评测集每条包含 id、question、reference可选标准答案 eval_set [ {id: 1, question: 什么是窗口函数, reference: 用于跨行计算的 SQL 函数}, {id: 2, question: LEFT JOIN 和 INNER JOIN 的区别, reference: 左连接保留左表全部行}, # 实际场景中建议至少 20 条覆盖不同难度 ] def run_evaluation(session_factory, eval_set, modeldeepseek-chat, temperature0.3): results [] for item in eval_set: sess session_factory() # 每个样本独立会话避免互相污染 sess.start() try: answer sess.ask(item[question], modelmodel, temperaturetemperature) results.append({ **item, answer: answer, latency: None, # 实际使用中可以用 time.time() 记录 ok: True, }) except Exception as e: results.append({**item, answer: None, latency: None, ok: False, error: str(e)}) time.sleep(0.2) # 简单限流避免触发 API 频率限制 return pd.DataFrame(results) df run_evaluation(lambda: Session(你是 SQL 教学助手), eval_set) df[[id, question, ok]].head()这里的关键设计是session_factory而不是直接传一个 Session 实例。批量评测时每个样本都应该用全新的会话否则前一个样本的对话历史会流进下一个样本结果就失真了。lambda: Session(...)这个写法就是用来保证每次调用都创建全新对象。time.sleep(0.2)是简单粗暴的限流。如果你用的是官方 API并发太高会被限流甚至封 key如果你用的是本地 vLLM 部署并发高可能直接把显存打爆。更正规的做法是用asyncio 信号量做并发控制但 Notebook 环境下我倾向于保守——跑得慢一点没关系跑挂了才麻烦。4.2 结果落盘与轻量评估JSONL 是你的后悔药批量跑完不能只看df.head()。我一般会把完整结果落成 JSONL 文件每条记录包含原始输入、输出、token 消耗、耗时和错误信息。这样做的价值在于任何一次评测结果都可以回溯你可以对比“昨天改 Prompt 之前”和“今天改完之后”的输出差异而不是靠记忆。import json from pathlib import Path out_path Path(./eval_results_jsonl/) out_path.mkdir(exist_okTrue) def save_results(df, tagbaseline): filename out_path / feval_{tag}_{int(time.time())}.jsonl with open(filename, w, encodingutf-8) as f: for _, row in df.iterrows(): record { id: row[id], question: row[question], answer: row[answer], reference: row.get(reference, None), ok: bool(row[ok]), tag: tag, model: deepseek-chat, temperature: 0.3, } f.write(json.dumps(record, ensure_asciiFalse) \n) print(f已保存 {len(df)} 条结果到 {filename}) save_results(df, tagv1_system_prompt)JSONL 比 CSV 更适合存这种半结构化数据因为 answer 字段里可能包含换行、引号CSV 处理起来麻烦JSONL 天然规避了这个问题。tag字段用来标记这是哪一轮实验建议用能表达语义的名字比如v1_system_prompt表示“第一版系统提示词”避免时间戳一多就分不清哪个是哪个。至于评估方式分两类。有标准答案的用代码自动算比如 F1、BLEU、语义相似度没有标准答案的我习惯把结果导出成 Markdown 表格人肉看一遍——注意是人肉看不是“看一眼”是逐条打分。如果你对 DeepSeek 的输出有信心也可以让它做裁判对结果打分但要记住“大模型评大模型”有偏差只能辅助不能替代。5. 交互式 AI 开发的 5 个常见坑从连不上到上下文污染5.1 现象第一个请求就超时卡了几分钟没反应原因OpenAI客户端的默认超时时间在长文本生成场景下偏短尤其是max_tokens设置较大时服务端生成耗时容易超过客户端等待时间。另外如果你所在网络需要通过代理访问外网代理本身的不稳定也会放大超时概率。解决把timeout显式调大。我一般设为 60 秒生成 2048 token 的常规回答足够。如果仍然超时把max_tokens降下来分段生成。代理问题用curl -v看连接过程能快速定位是哪一跳卡住了。5.2 现象同一个 Prompt 跑三次三次答案都不一样原因这是大模型的固有随机性不是 bug。temperature越高采样随机性越强另外模型推理时可能因为批次不同产生轻微差异。解决明确实验目的。做事实性问答时把temperature调到 0 或 0.1接近贪婪解码结果稳定得多。做创意生成时才调高温度。另外DeepSeek 官方 API 支持seed参数如果模型接受设置固定seed能进一步降低差异但注意seed不是绝对保证不要依赖它做完全复现。5.3 现象多轮对话后模型开始重复之前的错误结论原因上下文太长早期的错误信息没有被正确管理。最常见的操作失误是把一次失败的 API 返回结果当成正常内容写进了history或者把调试用的临时信息比如“这条是测试”也传给模型当正经指令。解决严格区分“用户消息”和“系统内部消息”。调试信息永远不要进入history。用前面第 3 章的Session类把失败分支单独处理不要写进上下文。另外定期清理历史——如果某个话题已经讨论完就调用session.start()重开别让旧话题干扰新话题。5.4 现象本地部署的模型回答风格和 API 差异很大原因本地部署时模型权重如果没有经过与官方 API 相同的对齐配置行为会有差异。更常见的原因是system_prompt被覆盖了——比如你用 vLLM 部署时--served-model-name参数设置不当或者默认模板里没有正确传递 system 消息导致模型根本看不到你的人设设定。解决部署时先做“裸奔测试”——不传 system_prompt只传一条 user 消息确认模型能正常回答。再传 system_prompt对比风格是否变化。如果没变化去检查部署框架的系统消息处理逻辑。不要一上来就怪模型先确认消息传到了。5.5 现象批量跑 50 条数据第 30 条断掉了前面的全白费原因批量评测时没有断点续跑。网络抖动、API 限流、甚至是 Notebook 内核崩溃都会导致任务中断。而df存在内存里内核一重启就没了。解决分批执行 每批落盘。把 50 条拆成 5 个批次每批跑完立刻写 JSONL。下次接着跑时先读取已有的结果文件跳过已完成的数据。下面的代码是这个思路的简化版def run_eval_with_resume(session_factory, eval_set, tag, batch_size10): result_file out_path / feval_{tag}.jsonl done_ids set() if result_file.exists(): for line in result_file.open(encodingutf-8): done_ids.add(json.loads(line)[id]) pending [e for e in eval_set if e[id] not in done_ids] print(f已完成 {len(done_ids)} 条剩余 {len(pending)} 条) for i in range(0, len(pending), batch_size): batch pending[i:ibatch_size] df_batch run_evaluation(session_factory, batch) save_results(df_batch, tagtag) # 追加模式见下方说明注意这个版本里save_results需要以追加模式打开文件原先的“覆盖写”要改成modea。这是最常见的血泪经验——第一次跑完 20 条第二次接着跑直接清空重来。文件追加模式是断点续跑的命根子。6. 收尾在验证上给 Notebook 流程加一层“断言式检查”整套流程跑通之后最容易被忽略的是“怎么证明它还在正确地工作”。我的习惯是写一个验证单元格放在每次批量实验之前用三个断言快速把环境、鉴权和基本能力都检查一遍。这不是形式主义——交互式 AI 开发里 80% 的问题是在改代码过程中不小心碰坏了环境变量、覆盖了函数定义、或者改了某个全局变量。def sanity_check(): # 1. 环境变量存在 assert os.environ.get(DEEPSEEK_API_KEY), 缺少 DEEPSEEK_API_KEY # 2. 客户端能连通 models client.models.list() assert len(models.data) 0, API 鉴权失败或网络不通 # 3. 最小对话能完成且返回结构正确 r chat_once([{role: user, content: 回复 OK 两个字母}], max_tokens10) assert r[ok], f对话失败: {r[error]} assert OK in r[content].upper(), 模型没有按预期回复 print(sanity check passed) sanity_check()这个检查函数放在 Notebook 最前面每次重跑全流程之前执行一遍。如果断言挂了说明环境变了不用继续往下跑省得浪费真金白银的 token。另一个习惯是每次实验开始前记录一下当前代码版本——Git 里打个 tag 或者把save_results的tag参数写得更有语义这样排查问题时能准确回答“我是在哪个版本上跑出这个结果的”。说到习惯我自己的经验是Notebook 里做 AI 开发最大的敌人不是模型不够聪明而是记录不够严谨。模型参数、Prompt 版本、评测集、结果文件——这四样东西哪怕有一项对不上后面做的所有对比分析都是空中楼阁。希望这套流程能帮你少走一些弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑