从零搭建:基于Dify工作流整合Ollama与DeepSeek-R1的联网搜索助手
1. 为什么你需要一个自己的联网搜索助手?
不知道你有没有过这样的经历:想查一下最新的技术动态,比如“2024年AI大模型有哪些新突破”,结果问了一圈AI助手,得到的回答要么是几个月前的旧闻,要么就是含糊其辞,因为它“不知道”今天发生了什么。或者,你想规划一次旅行,需要最新的酒店价格和景点开放信息,却发现AI给出的建议可能已经过时了。这就是传统大语言模型最大的痛点——它们被“困”在了训练数据截止的那个时间点,无法获取实时的、动态的网络信息。
这就像你有一个知识渊博但足不出户的朋友,他脑子里装满了2023年以前的百科全书,但对于窗外正在发生的世界,他一无所知。为了解决这个问题,我们通常需要手动打开浏览器搜索,再把结果复制粘贴给AI,过程繁琐又割裂。有没有一种方法,能让AI自己学会“上网”,实时获取信息并为你分析和总结呢?
当然有,而且今天我们就可以亲手搭建一个。这个方案的核心,就是结合三个强大的工具:Dify、Ollama和DeepSeek-R1,再通过BochaWebSearch这个搜索工具,为AI装上“眼睛”和“耳朵”。Dify是一个低代码的AI应用开发平台,你可以像搭积木一样,用可视化工作流把各个模块连接起来;Ollama让你能在自己的电脑上轻松运行各种开源大模型;而硅基流动提供的DeepSeek-R1,则是一个能力非常强悍的云端模型。最后,BochaWebSearch负责执行联网搜索,把最新的网页内容抓取回来。
我试过不少方案,有的配置复杂,有的费用高昂。而今天要分享的这套组合,最大的优点就是灵活、可控且成本友好。你既可以使用本地的Ollama模型保护隐私,也可以调用强大的云端模型处理复杂任务,还能根据需求随时切换。整个过程几乎不需要写代码,跟着步骤一步步来,小白也能轻松上手。接下来,我就带你从零开始,一步步搭建这个属于你自己的、具备实时信息获取能力的AI搜索助手。
2. 搭建前的准备:环境与账号配置
万事开头难,但准备工作做得好,后面就一路顺畅了。在开始拖动Dify工作流中的节点之前,我们需要先把几个核心“演员”请到舞台上,并给它们安排好位置。
2.1 部署与登录Dify
Dify是我们的“指挥中心”,所有工作流都在这里编排。首先,你需要一个Dify服务。最方便的方式是使用Dify官方提供的云服务,直接注册账号即可。如果你想完全掌控在自己手里,也可以在本地通过Docker部署。这里以本地部署为例,因为它更灵活,且没有网络限制。
假设你已经安装好了Docker和Docker Compose,那么部署Dify就是一行命令的事。打开你的终端,进入一个你喜欢的目录,然后执行:
git clone https://github.com/langgenius/dify.git cd dify/docker docker-compose up -d等待一阵子,当终端不再滚动新的日志时,就说明部署成功了。接下来,在浏览器中输入http://localhost或者http://127.0.0.1,就能看到Dify的登录界面。第一次访问会进入初始化设置页面,按照提示创建管理员账号即可。
登录成功后,你会进入Dify的主仪表盘。这里就是我们未来创作所有AI应用的大本营。我建议你先花几分钟熟悉一下界面,左侧是导航栏,包括“应用”、“工作流”、“知识库”等核心功能。我们这次的重点,就在“工作流”里。
2.2 配置模型供应商:本地Ollama与云端DeepSeek
模型是我们AI助手的大脑。在这个项目里,我们准备了两颗“大脑”:一颗是本地的,一颗是云端的。这样设计的好处是,你可以根据任务需求灵活切换。处理一些简单的、对隐私要求高的查询,用本地模型;需要强大推理和复杂总结时,就调用云端模型。
首先,配置本地的Ollama。确保你已经在电脑上安装并运行了Ollama。安装过程非常简单,去Ollama官网下载对应系统的安装包,一路下一步就行。安装完成后,在终端里运行ollama run qwen2.5:7b这样的命令,就可以拉取并运行一个模型。Ollama默认的服务地址是http://127.0.0.1:11434。
回到Dify,点击左下角的“设置”图标,进入“模型供应商”页面。点击“添加模型供应商”,在列表里找到“Ollama”。在配置页面,唯一需要填写的就是“基础URL”,这里就填入http://127.0.0.1:11434。如果你的Ollama服务跑在另一台机器上,就填写那台机器的IP地址和端口。配置完成后,点击“保存”,Dify就能识别到你本地的Ollama服务了。之后,你就可以在“模型”选项里看到你本地已经拉取的所有模型,比如qwen2.5:7b、llama3.2等等。
接着,配置云端的硅基流动DeepSeek。硅基流动是一个提供多种主流模型API服务的平台,DeepSeek-R1是其中性能非常突出的一个模型。首先,你需要访问硅基流动的官网注册一个账号。注册完成后,在个人中心找到“API密钥”管理页面,创建一个新的API Key,并妥善保存下来,它只会显示一次。
回到Dify的“模型供应商”页面,这次选择“硅基流动”。在配置界面,将你刚才复制的API Key粘贴到“API密钥”栏位。其他参数通常保持默认即可。保存之后,Dify就具备了调用DeepSeek-R1模型的能力。这样,我们的“双核大脑”就配置完毕了。在实际工作流中,我们可以自由选择使用哪一个,甚至可以根据条件判断来动态切换,这体现了Dify工作流设计的强大之处。
3. 为助手注入“眼睛”:集成BochaWebSearch工具
模型大脑准备好了,但它还缺少感知外界信息的能力。接下来,我们要集成一个关键的联网搜索工具——BochaWebSearch。你可以把它理解为AI助手的“眼睛”和“搜索引擎”,它负责根据用户的问题,去互联网上抓取最新、最相关的网页内容。
BochaWebSearch提供了清晰的API接口,我们需要将其以“自定义工具”的形式接入Dify。这样,在工作流中,它就能像一个标准的积木块一样被我们调用。这个过程听起来有点技术性,但跟着我做,其实很简单。
首先,在Dify左侧导航栏找到“工具”选项,点击进入后,选择“自定义工具”,然后点击“创建自定义工具”。这里我们需要填写一个OpenAPI格式的配置文档。别被这个词吓到,它其实就是用一种结构化的方式告诉Dify,这个工具叫什么、能做什么、需要什么参数、会返回什么结果。
你可以直接使用以下我提供的配置(这是一个简化版的示例,涵盖了核心功能):
openapi: 3.0.3 info: title: 博查搜索 description: 一个为AI应用提供的中文搜索引擎,能够从海量网页和生态内容源中获取高质量的世界知识。 version: 1.0.0 servers: - url: https://api.bochaai.com/v1 paths: /web-search: post: summary: 从博查搜索全网信息和网页链接,返回结果包括网页标题、URL、网页摘要、网站名称等。 operationId: BochaWebSearch requestBody: content: application/json: schema: type: object properties: query: type: string description: 搜索关键词或语句 summary: type: boolean description: 是否在搜索结果中包含摘要 default: true freshness: type: string description: 搜索指定时间范围内的网页 enum: [“noLimit”, “oneDay”, “oneWeek”, “oneMonth”, “oneYear”] default: “noLimit” count: type: integer description: 返回的搜索结果数量(1-10) default: 10 required: - query responses: ‘200’: description: 成功的搜索响应 content: application/json: schema: # 这里定义了返回数据的复杂结构,为了节省篇幅,结构描述从简。 # 核心是包含一个 `data.webPages.value` 的数组,里面有标题(name)、链接(url)、摘要(summary)等字段。将上述YAML内容复制到Dify自定义工具的“Schema”配置框中。接下来,你还需要一个BochaWebSearch的API Key。前往其官网,注册账号并创建一个API Key,通常会有一定的免费额度供测试使用。在Dify工具配置的“认证”部分,选择“API Key”,并将密钥填入。记得在“Headers”中设置Authorization为Bearer <你的API Key>。
配置完成后,点击“保存并测试”。你可以尝试输入一个简单的查询,比如“今天北京天气”,如果配置正确,你应该能看到返回的结构化搜索结果。至此,你的AI助手就拥有了“视力”,可以主动去互联网上寻找答案了。这个工具节点,将是我们工作流中至关重要的一环,它负责将用户的文字问题,转化为一堆富含信息的网页数据。
4. 构建核心工作流:像连接水管一样连接AI能力
工具和模型都就位了,现在进入最有趣的部分——用Dify的可视化工作流把它们“组装”起来。你可以把工作流想象成一个流水线,用户的问题像一颗原料,经过各个工位的处理,最终变成精美的答案成品。我们这次要搭建的流水线主要包含四个核心工位:开始->BochaWebSearch(搜索)->代码处理(格式化)->LLM(总结回答)->回复(输出)。
4.1 创建工作流并设置初始节点
在Dify的“工作流”页面,点击“创建新的工作流”。给它起个名字,比如“我的联网搜索助手”。你会看到一个空白的画布,右侧是节点库。
首先,从节点库拖拽一个“开始”节点到画布上。这个节点代表用户输入的起点。选中它,在右侧配置面板,你可以看到一个“变量”区域。这里我们暂时不需要额外设置,因为用户的问题会通过{{#sys.query#}}这个系统变量自动传递进来。这个节点就像流水线的入口,原料(用户问题)从这里进入。
接下来,我们需要把“原料”送到搜索引擎去查找资料。从节点库找到“工具”分类,将我们刚刚配置好的“BochaWebSearch”节点拖到画布上,放在“开始”节点右侧。然后,从“开始”节点右侧的圆点(输出点)拖出一条线,连接到“BochaWebSearch”节点左侧的圆点(输入点)。这条线就建立了数据流动的通道。
选中“BochaWebSearch”节点进行配置。最关键的是query参数,这里我们需要填入{{#sys.query#}},这样用户输入的问题就会作为搜索关键词传递给Bocha。其他参数如freshness(新鲜度,比如只搜一天内的结果)、count(返回结果数量)可以根据你的需求调整,也可以先保持默认。这样,当工作流运行时,用户的问题就会触发一次真实的网络搜索。
4.2 用代码节点处理原始搜索结果
BochaWebSearch返回的数据是结构化的JSON,包含了标题、链接、摘要等丰富信息,但直接扔给大模型可能不够“整洁”。我们需要一个“预处理”工位,把这些原始数据整理成一段易于模型理解的上下文文本。
从节点库拖拽一个“代码”节点到画布,放在“BochaWebSearch”节点右侧。将两者的输出和输入连接起来。这个节点允许我们编写一小段Python代码,对上游节点的输出进行加工。
点击配置代码节点,语言选择“Python3”。我们需要编写一个函数,来提取和格式化搜索结果。以下是我调试后觉得比较好用的一个版本:
def main(resp: str) -> dict: import json try: response = json.loads(resp) # 层层解析返回的JSON结构,找到网页列表 if “data” in response: data = response[“data”] if “webPages” in data: webPages = data[“webPages”] if “value” in webPages: contexts = webPages[“value”] # 限制最多处理10条结果,避免上下文过长 max_context = min(len(contexts), 10) format_contexts = [] for i in range(max_context): # 将每条结果格式化为一个清晰的文本块,并加上引用编号 formatted_context = f“[[引用:{i+1}]]\n网页标题:{contexts[i][‘name’]}\n网页链接:{contexts[i][‘url’]}\n网页内容:{contexts[i][‘summary’]}\n发布时间:{contexts[i].get(‘dateLastCrawled’, ‘N/A’)}\n网站名称:{contexts[i].get(‘siteName’, ‘N/A’)}” format_contexts.append(formatted_context) # 用两个换行符连接所有格式化后的结果 context = “\n\n”.join(format_contexts) return {“context”: context} return {“context”: “暂无搜索结果”} except Exception as e: return {“context”: f“处理搜索结果时出错: {str(e)}”}这段代码干了什么呢?它接收BochaWebSearch返回的原始响应(resp),然后像剥洋葱一样,解析出data -> webPages -> value这个数组。数组里的每个元素都是一条搜索结果。接着,它遍历这些结果(最多10条),把每条结果的标题、链接、摘要等信息拼接成一段规整的文字,并在开头加上[[引用:X]]的标记。最后,把所有格式化后的结果用空行隔开,合并成一个大的context字符串输出。
这个context字符串,就是经过清洗、整理后的“参考资料”,它将被喂给下一个工位——大语言模型。代码节点的输出变量名我们设置为context,后面会用到。
4.3 配置大语言模型节点进行智能总结
现在,我们有了用户的问题(sys.query)和丰富的网络参考资料(context),是时候请出我们的大脑——大语言模型来干活了。从节点库拖拽一个“LLM”节点到画布,放在代码节点右侧,并连接起来。
选中LLM节点,在配置面板的“模型”下拉框中,你可以看到之前配置好的Ollama和硅基流动的模型。这里我强烈推荐选择“siliconflow/deepseek-ai/DeepSeek-R1”。DeepSeek-R1在长文本理解、信息整合和遵循指令方面表现非常出色,特别适合处理我们这种“问题+多段参考资料”的输入场景。
模型选好后,最关键的是配置“提示词”。提示词就是指挥模型如何工作的指令。我们需要清晰地告诉它:你的角色是什么,你拥有哪些资料,以及你该如何回答。以下是一个经过精心设计的系统提示词示例,你可以直接复制使用:
# 角色 你是一个由博查搜索精心打造的 AI 搜索助手,能够在网络世界中精准搜索,并以中立客观、新闻式的专业语气为用户答疑解惑。 ## 技能 ### 技能 1: 精准响应用户提问 1. 当用户提出问题时,首先以一个单独的段落简洁直接地回答核心问题,随后通过若干独立的段落详细分析问题的各个方面和细节,确保答案完整且逻辑清晰。 2. 提供的答案应具备中等至较长的篇幅,信息量饱满且紧密关联,但切忌重复用户问题。 3. 结构化地拆解内容,明确表达内部的分类及逻辑关系,并以 markdown 格式提供详尽内容。 ### 技能 2: 在答案中适当地使用引用编号来引证上下文信息 1. 你将获得一组与该问题相关的上下文,每个上下文都以引用编号开头,例如 [[引用:x]],其中 x 是一个数字。请使用上下文进行参考作答,但无需显示引用的编号。 ### 技能 3: 不要盲目地重复上下文,提供扩展的见解和解释 1. 对上下文信息进行分析、综合和评价,提供更深入的见解和解释。 2. 融入你的专业知识和经验,以提供更丰富和有深度的答案。 3. 始终保持对用户问题的敏感度和对信息准确性的追求。 ## 限制 1. 禁止告知用户通过打开链接或访问网站获取答案,除非用户明确要求提供链接。 2. 必须基于上下文信息来回答问题并引用相关内容,然而无需在回应中提及上下文。 3. 只有在必要时,才使用无序列表、有序列表等格式。 4. 只有在必要时,才通过独立段落详细分析问题、总结关键点和主要信息。接下来,在“用户提示词”部分,我们需要把变量“组装”进去:
以下是上下文: {{#1740023701313.context#}} 以下是用户提问: {{#sys.query#}}注意,这里的1740023701313.context需要替换成你画布上那个“代码”节点的ID加上.context输出变量。你可以在代码节点的配置面板顶部找到它的ID(一串数字),替换上去即可。这样,模型就能同时看到网络搜索得到的上下文和用户的原始问题了。
最后,在“上下文”设置里,建议勾选“启用上下文”并设置一个合理的token限制(比如8000),以确保有足够的空间容纳搜索返回的长文本。温度(Temperature)参数可以设置为0.7,让回答既有一定创造性,又不会太天马行空。
4.4 完成闭环并测试
将LLM节点的输出,连接到最后一個“回复”节点。回复节点的配置很简单,在“答案”栏填入{{#llm.text#}},这意味着直接把LLM生成的文本作为最终答案返回给用户。
至此,整个工作流的骨架就搭建完成了。你的画布上应该有一条清晰的连线:开始 -> BochaWebSearch -> 代码 -> LLM -> 回复。现在,点击右上角的“保存”,然后点击“发布”。发布后,这个工作流就变成了一个可用的AI应用。
点击“预览”或直接访问应用链接,在聊天框里输入一个需要实时信息的问题,比如“帮我总结一下今天科技领域的主要新闻”。稍等片刻,你会看到助手首先会触发搜索(这可能需要几秒钟),然后基于搜索到的新闻链接,生成一份结构清晰、带有引用来源的总结报告。第一次看到它成功运行并给出带有时效性的答案时,那种成就感是非常棒的。
5. 进阶技巧与避坑指南
基础流程跑通后,我们可以玩点更花的,让这个助手变得更聪明、更强大。同时,我也分享几个我踩过的坑,帮你节省时间。
5.1 实现模型的热切换与路由
你可能会想:有时候问题简单,用本地Ollama模型快又省;有时候问题复杂,需要DeepSeek-R1出马。能不能自动判断?Dify工作流中的“条件判断”节点可以帮你实现。你可以在BochaWebSearch节点之后,添加一个“条件判断”节点。判断逻辑可以基于搜索结果的条数(如果结果很少,可能问题简单),或者基于用户问题的长度、关键词(例如包含“总结”、“分析”等词用DeepSeek)。然后,根据判断结果,将流程导向连接了Ollama模型的LLM节点分支,或者连接了DeepSeek-R1的LLM节点分支,最后再汇聚到同一个回复节点。这就实现了智能路由,让合适的模型处理合适的问题。
5.2 优化搜索策略与结果过滤
默认的搜索可能返回的信息比较杂。你可以通过调整BochaWebSearch节点的参数来优化。freshness参数非常有用,对于查询新闻、股价等时效性强的信息,设置为oneDay可以确保结果是最新的。count参数控制返回数量,不是越多越好,太多会导致上下文过长,影响模型处理速度和效果,一般5-10条足矣。
此外,你还可以在代码节点中增加更复杂的过滤逻辑。比如,只选择来自特定权威网站(如域名包含github.com,arxiv.org)的结果,或者根据摘要中的关键词进行排序和筛选。这需要你稍微修改一下之前的Python代码,增加一些条件判断。这样处理后的上下文质量更高,模型的回答也会更精准。
5.3 处理常见错误与稳定性保障
在实际使用中,可能会遇到一些问题。一个是搜索超时或失败。网络搜索API并不总是稳定的。为了提升体验,你可以在代码节点中加入异常捕获和重试机制,或者在工作流中添加一个“分支”:如果搜索节点返回错误,则直接跳过一个备用的LLM节点,让模型基于自身知识库回答,并告知用户“暂时无法获取实时信息”。
另一个是上下文过长。如果一次搜索返回了太多、太长的网页摘要,可能会超过模型的最大上下文长度。除了前面提到的限制结果数量,你还可以在代码节点中对每个摘要进行截断,比如只保留前200个字符。更高级的做法是,先让一个小模型(如Ollama上的小参数模型)对搜索结果进行初步筛选和摘要,再将精简后的上下文交给大模型做最终总结,这种“两级处理”模式效率更高。
最后,记得为你的应用设置一个友好的开场白,在Dify应用设置的“提示词编排”部分,可以写一句:“我是一个联网搜索助手,可以为您查询最新的网络信息。” 这能让用户立刻明白它的能力边界。经过这些优化,你的助手就不再是一个简单的原型,而是一个真正可靠、实用的生产力工具了。
