同一个问题,网页版勾上"深度思考"再打开"联网搜索",给出的答案,常常让我这种习惯用API裸调的人怀疑是不是用了两个不同的产品。API请求明明也是走同一个官方接口,结果却像换了个画风:网页版会先铺一大段推理过程再去查资料,最后给出带小标题的综述;API默认返回却干巴巴的,既没有实时信息,也没有那种"绕几圈再回答"的思考痕迹。
问题出在哪?并不是模型的智商变了,而是你根本没有把网页版那两个开关对应的能力在API里逐一打开。这篇文章就围绕这件事展开:从模型路由、思考链读取、搜索工具的装配,到采样参数、提示词对齐和最终的同题复现验证,把"让API复现网页版深度思考+联网搜索行为"这件事讲透。适合正在接推理模型API、想给应用补上实时检索能力的开发者,也适合那些被"网页版很聪明、API很傻"困住的产品同学。
1. 网页版两个开关背后,API到底发生了什么
1.1 深度思考不是提示词,而是模型路由
很多第一次接API的人都会踩同一个坑:以为网页版的"深度思考"就是系统偷偷加了一句"请仔细想想再回答",于是自己也往prompt里写"一步步思考"。
字面上看好像有点用,实际上完全两码事。网页版那个开关背后是模型路由的切换:勾上之后,请求会从普通对话模型切换到带长思维链的推理模型。推理模型在解码时,会先生成一段内部推理过程(通常叫作reasoning content或思维链),再基于这段推理生成最终答案。这段"先想后说"的结构,才是"深度思考"观感的真正来源。
API想复现这个行为,第一步就是选对模型。在OpenAI兼容的调用方式里,模型名要填成推理模型对应的名称,而不是默认的对话模型。模型名选错,后面做再多都是白搭。
from openai import OpenAI client = OpenAI( api_key="你的key", base_url="https://api.deepseek.com" ) resp = client.chat.completions.create( model="deepseek-reasoner", # 推理模型,不是 deepseek-chat messages=[{"role": "user", "content": "今天A股成交量最大的板块是什么?"}], stream=True )如果这一步之后,你仍然只收到一段干巴巴的答案,说明你还没看到完整的返回结构。推理模型在流式返回里通常会拆成两类内容:一类是reasoning_content(推理过程),一类是content(最终答案)。网页版把前者折叠起来展示成"深度思考"的过程,API里它是单独字段,不会混在最终答案里。
所以"复现深度思考"的第一层对齐,不是模仿说话语气,而是让API确实跑在推理模型上,并把推理过程也一并接收下来。
1.2 联网搜索是工具,不是模型自带能力
第二个开关"联网搜索",容易让人误以为模型本身学会了查资料,接上API就能自动获得实时信息。
真实情况是:模型不具备联网能力,联网是一个外挂的检索工具。网页版勾选之后,前端会自动做两件事:把你的问题发给检索服务,把返回的网页摘要或片段塞进上下文,再把这些拼接好的文本交给模型生成回答。
API默认状态下,这条链路是断开的。你只发过去一段纯文本,没有任何检索结果进入上下文,模型当然只能依赖自己训练时学到的东西,知识截止日期之前的事情还好说,牵扯到新近事件就只能靠编或者靠泛化猜测。
1.3 为什么默认API调用和网页版结果不一样
把前面两点叠起来就清楚了:
| 维度 | 网页版(勾选两项) | 默认API调用 |
|---|---|---|
| 模型 | 推理模型 | 普通对话模型 |
| 推理过程 | 生成完整思维链再回答 | 直接输出答案 |
| 实时信息 | 先检索,再综合 | 无检索,纯靠记忆 |
| 上下文 | 含检索片段+历史对话 | 只有用户消息 |
| 采样参数 | 平台侧调校过的低随机性 | 开发者自己决定 |
所以,与其说"网页版更聪明",不如说网页版默认帮你把模型、工具、上下文、采样参数都调到了一个适合深度问答的状态。API用起来"笨",只是这些配置没有被传递过去。理解这一点,你就知道接下来的工作不是调prompt,而是把这四层配置在API请求里逐一重建。
2. 第一步对齐:让API输出具备深度思考的行为
2.1 选对推理模型,并正确处理思维链
我在实际项目中第一次切换推理模型时,也犯过一个低级错误:以为流式输出里把content拼起来就完事了,结果页面里只有最终答案,根本没有"思考过程"的效果。后来一查,推理过程要走另一个字段,而且不同版本的SDK对它的暴露方式还不一样。
正确做法是,在流式循环里同时接收两类字段:推理过程单独收,最终答案单独收。这里用chunk.choices[0].delta判断一下即可。
reasoning_parts = [] content_parts = [] for chunk in resp: delta = chunk.choices[0].delta if delta is None: continue if hasattr(delta, "reasoning_content") and delta.reasoning_content: reasoning_parts.append(delta.reasoning_content) if delta.content: content_parts.append(delta.content) reasoning_text = "".join(reasoning_parts) answer_text = "".join(content_parts)这里有个经验:不要把推理过程暴力丢弃,也不要直接展示给终端用户。Mind that reasoning content 在较长任务里可能覆盖大量篇幅,它适合作为"思考痕迹"展示,或者用来做答案可信度分析,但不适合原样堆在聊天界面上。网页版的折叠交互,本质上是让用户看到"它在想"而不是"它在啰嗦"。你如果自建前端,也应该用同样的思路处理。
2.2 温度、上下文长度、输出上限的默认值
网页版给人的"稳"感,很大程度来自采样参数偏保守。API默认的温度值有时偏"自由",尤其在做知识问答类任务时,温度太高,同一个问题跑三次能给你三种表达,甚至结论细节都不一致。
要对齐网页版行为,我建议把temperature控制在 0.0 到 0.3 之间。如果你压根不需要发散性创作,直接设 0.0 就行——确定性最强,也最容易在测试时复现。网页版在处理问答类问题时,走的也是这种偏保守路线。
max_tokens也要注意。推理模型会先产生一大段推理过程,再产生答案,两个部分的长度都会占用输出额度。如果你把上限设太短,可能出现"思维链没写完,答案被截断"的尴尬情况。一般建议把max_tokens放到 4000 以上,复杂分析类问题甚至可以更大,但也要权衡成本和响应速度。
2.3 保留历史对话会让行为更贴近网页版
网页版在连续对话时,会把之前的问答也一起发回去。很多人接API时只传当轮问题,等于每次都是"失忆式提问"。这不是深度思考的问题,但会造成结果风格大幅漂移。
对齐做法是维护一个消息列表,把最近几轮的用户问题、推理过程摘要和最终答案按顺序塞回去。这里有一点需要提醒:把完整思维链塞进下一轮上下文,既费token,也容易让模型在下一轮里过度纠结上一个问题。更稳的做法,是在历史里只放最终答案,不放推理过程,或者只放一句带结论的摘要。
3. 第二步对齐:在API请求里把"联网搜索"真正打开
3.1 搜索工具的开法:先确认能力,再看参数
要让API具备实时检索能力,需要在请求里装配搜索工具。不同版本对这个能力的封装不太一样,但原理都一致:在请求的tools参数里声明一个检索类工具,由模型决定何时发起检索、检索什么关键词、怎么使用回来的内容。
这里我先给一个结构性的示例,具体工具名以你调用的官方文档为准:
resp = client.chat.completions.create( model="deepseek-reasoner", messages=[{"role": "user", "content": "今年上半年发布的旗舰手机影像规格汇总"}], tools=[{"type": "web_search"}], # 联网搜索工具,不同版本命名可能不同 stream=True, )如果按这个结构报错,不用慌。先做两件事:查一下当前模型支持的能力列表,再看官方示例代码里的tools写法。很多"调用没效果"的案例,其实是模型名称和工具能力不匹配,或者工具名的大小写不对。
3.2 搜索结果怎么进入上下文:拼接轮次是核心
搜索工具触发后,通常会以工具返回消息的形式把检索结果回传给模型。先问一个问题:模型真的"看过"这些搜索结果了吗?
不一定。只有当你把返回结果作为一条tool消息,或者把搜索片段拼接到后续消息里再次发给模型,模型才真正"看"到。单纯发一个问题出去、收到一个带搜索标记的中间对象,却把它丢在一边,模型下一轮还是不知道这些内容。
所以我建议做一个简单的拼接函数:把搜索命中的标题、摘要、链接、正文片段组合成一段结构清晰的文本,放到上下文里,再让模型基于这些文本生成回答。
def assemble_search_context(results): chunks = [] for i, item in enumerate(results, 1): chunks.append( f"[{i}] 标题:{item.get('title')}\n" f"来源:{item.get('source')}\n" f"摘要:{item.get('snippet')}\n" ) return "\n\n".join(chunks)这段文本拼进去后,模型每一条结论都能回溯到对应的检索结果序号。网页版那份"带引用来源的答案",本质就是这套机制的产物。你如果不要这些来源信息,模型也能答,但回答的可信度和准确性会明显下降。
3.3 搜索时效性、范围和请求环境的注意事项
联网搜索有个容易被忽略的变量:同一问题,不同时间搜到的内容不一样,答案也就不同。这和网页版的行为完全一致,网页版在不同时段问同一个问题,引用的新闻、数字也可能不同。
所以,"一模一样"这个目标要定义清楚,不是字面一致,而是"检索到的同一批资料、在同样的推理路径下生成".你如果要做结果回归测试,除了固定提示词和温度,最好在一次完整对话内完成,避免跨时间比较陷入"搜索来源漂移"带来的差异。
另外,API所在环境的网络要能正常访问检索服务。这里不需要任何特殊操作,只提醒一点:如果你的服务部署在受限网络环境里,连可能HTTPS请求都出不去,那搜索工具大概率会静默失败或返回空结果。排查顺序是:先确认检索服务返回了真实命中结果,再谈答案差异。
4. 目标对齐:让最终文字也尽量贴近网页版
4.1 提示词风格会显著影响差异
模型和工具都对齐之后,你可能会发现结果还是和网页版有差别。这时候,问题多半出在提示词组织方式上。
网页版在回答问题之前,其实也有一套隐性的指令约束,它会倾向于先做背景说明、再分点展开、最后补充注意事项。你在API里用一大段自由prompt问,反而容易把模型带偏成"直给结论"的风格。
最稳的对齐方式,是用一个固定的 system prompt 设定回答结构,让每次请求都站在同一起跑线上:
你是一个擅长检索综合分析的研究助手。 请先理解问题,再根据检索材料组织回答。 要求: 1. 回答开头用一两句直接回应问题; 2. 之后分点展开,引用检索结果时标注来源序号; 3. 如果没有检索到相关信息,明确说明依据不足; 4. 整体风格清晰、客观,不堆砌术语。用同一段 system prompt 反复测试,结果的可复现性会明显提升。网页版那些看似"随机"的发挥,往往不是模型随机,而是没有显式约束,模型自由发挥的空间大。
4.2 历史轮次、模板变量和上下文顺序的差异
还有一个容易被忽略的差异来源:上下文顺序。
同一个问题,放在"全新会话第一条"和放在"讨论了很久之后中间穿插进来",模型的理解会不同。网页版每次对话默认带历史,所以你会发现网页版回答问题时经常说"结合刚才提到的……"。API想要对齐,就需要决定:你是要一个"带记忆的连续对话",还是要一个"只针对当前问题深度分析"的单轮任务。
我见过很多项目把多轮历史全塞进去,反而把模型搞糊涂,输出变得冗长且偏离主题。一个更合理的做法是按需裁剪历史:保留最近的3-4轮,中间的细节用摘要代替。这样既贴近网页版的连续性,又不至于让检索结果和旧对话互相干扰。
4.3 温度与随机性的精确匹配技巧
想进一步缩小"同问不同答"的差异,需要把随机性压到最低。推理模型在低温下依然会有一定的采样波动,但波动通常集中在措辞层面,结论和结构会稳定下来。
我在做回归测试时,会固定下面这一组参数:
| 参数 | 测试值 | 说明 |
|---|---|---|
| temperature | 0.0 | 不引入额外随机性 |
| top_p | 1.0 或不设置 | 避免和temperature叠加扰动 |
| max_tokens | 6000 左右 | 给推理和答案留足空间 |
| stream | true | 便于观察推理和答案的分界 |
| system prompt | 固定不变 | 保证行为基线一致 |
这组参数不等于"最优答案",但非常适合用来验证"你的链路是否和网页版对齐"。当你用这组参数跑同一个问题,发现检索来源一致、核心结论一致、结构和网页版接近,说明链路已经对齐。想要更发散的表达,再把温度往上调。
5. 同题复现测试与常见偏差的排查清单
5.1 设计一组有区分度的测试用例
不要拿"你好"这种问题来测试,深度思考和联网搜索的差异,必须靠有信息量的真实问题才能暴露出来。我建议准备三类问题:
第一类是需要实时信息的问题,比如"最近一个季度某行业发生的重要融资事件有哪些",这类问题能检验搜索工具是否真正生效。
第二类是需要多步推理的问题,比如"如果某产品降价30%,对毛利率和用户规模分别会有什么影响",这类问题能检验推理模型是否真的在走思维链。
第三类是带有一定歧义的问题,比如"讲一讲混合现实对教育的影响",这类问题最能暴露上下文和提示词风格差异。
每类准备两三个问题,同一批次里连跑三次,记录结果的差异点。
5.2 结果不一致时的排查顺序
如果你跑完发现API答案和网页版差异很大,按下面这个顺序排查,基本能定位问题:
- 先看模型名是不是推理模型。这个最基础,也最容易犯。
- 看
reasoning_content是否正常返回。没有这段,说明推理链路没有打开。 - 看检索工具是否真的返回了命中结果,以及这些命中结果是否被正确拼进后续消息。
- 检查 system prompt 是否给模型设置了和网页版不同的表达约束。
- 检查温度参数,温度偏高会放大表达差异。
- 最后看历史上下文。上下文里如果有错误信息,会直接把结论带偏。
这套顺序我用了很多次,十次里有八次都卡在前三步——模型没选对、推理字段没接住、搜索结果没送回去。
5.3 搜索质量差异带来的引用与偏差
最后说一个和结果质量直接相关的问题:搜索结果本身是有排序和质量的。
网页版展示的"回答+来源",可能融合了检索工具自己的排序策略,而你在API里收到的检索片段不一定覆盖同样的网页。如果搜索结果本身就不同,模型再厉害也不可能生成和网页版逐字一致的答案。这一点必须在预期里先放好——你的目标应该是"信息和结论对齐",而不是"字符串对齐"。
如果发现同一个问题,API搜索到的引用来源总是比网页版少,可以尝试在问题里补充搜索关键词提示,让检索工具的目标更明确。例如"请搜索2025年第一季度新能源汽车销量排名"就比"新能源汽车销量"更容易拿到高质量命中。
6. 从复现网页版到自建深度搜索问答工作流
6.1 把"深度思考+联网搜索"封装成自己的服务
做到这一步,你已经不是单纯"复现网页版",而是拥有了一条可控的深度搜索问答链路。建议这个阶段这么设计:外层接一个统一入口,入参只有一个问题,内部走"推理模型+搜索工具+上下文组装+终稿生成"的完整流程。
我用过的做法是:第一轮调用推理模型,让它判断是否需要搜索;如果需要,触发检索工具,并把搜索结果拼进上下文;第二轮再让模型基于搜索结果生成最终答案。这两次调用可以拆开,也可以在一次流式会话里完成,取决于你用的模型是否支持中间工具调用。
6.2 缓存、引用溯源和结果稳定性
要做成面向用户的稳定服务,还必须考虑两个工程问题:缓存和引用溯源。
检索结果的内容会变,但同一问题在短时间内查到的资料往往是稳定的。你可以把"规范化问题+检索结果摘要+最终答案"做成缓存,设置一个较短的过期时间,比如30分钟到几小时。这样既省搜索费用,也让相同问题在窗口期内的回答保持稳定。
引用溯源方面,我建议每一句关键结论都尽量对应到来源序号,并在最终答案后面附上原始链接列表。不只用用户体验好,排查模型幻觉时也容易回溯。
6.3 成本与并发控制的实战提醒
推理模型加联网搜索,意味着每次请求的token消耗和耗时都会明显高于普通对话。如果你做的是高并发的线上服务,务必加几个保护措施:一是限制单请求的最大输出长度,防止极端问题把输出额度打满;二是对搜索次数做频控,避免同一个用户反复触发检索;三是在流式接口里做好超时和断线重连逻辑,因为长思维链加检索的整体耗时往往在几十秒量级。
最后再分享一条个人经验:别执着于让API输出和网页版"逐字相同"。网页版和API本来就不是同一套工程链路,搜索排序可能浮动、采样有随机性、前端提示词版本也可能不一样。真正应该复现的,是那种"先系统思考、再基于实时资料回答"的能力结构。只要API跑在了推理模型上、检索结果真的进了上下文、答案能够溯源到搜索结果,这个目标就已经算实现了。这个链路跑通之后,你会发现,API端甚至可以做得比网页版更可控——你随时可以调温度、换提示词、决定要不要把思维链展示给用户,这些都是网页版给不了的自由度。