ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

ai-engineering-hub:用 CrewAI + LinkUp + DeepSeek-R1 搭建 MCP 驱动的多智能体深度研究系统

ai-engineering-hub:用 CrewAI + LinkUp + DeepSeek-R1 搭建 MCP 驱动的多智能体深度研究系统 ai-engineering-hub用 CrewAI LinkUp DeepSeek-R1 搭建 MCP 驱动的多智能体深度研究系统【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub本文基于仓库中的Multi-Agent-deep-researcher-mcp-windows-linux项目讲解如何从零搭建一个支持深度网络搜索的 Agentic Deep Researcher以 CrewAI 编排搜索—分析—写作三名智能体以 LinkUp 作为搜索工具以本地 Ollama 部署的 DeepSeek-R1 作为推理 LLM并支持 Streamlit 交互界面与 MCP Server 两种使用方式。读完后你可以完整理解多智能体研究流水线的 CrewAI 实现细节并学会把整个研究系统以 MCP 工具的形式暴露给任意支持 MCP 的客户端。系统概览一条搜索 → 分析 → 写作的智能体流水线该项目的目标见 README是构建一个由 MCP 驱动的多智能体深度研究员Agentic Deep Researcher能够执行深度网络搜索。整体技术栈由四部分组成LinkUp提供网络搜索能力的搜索工具SDK 依赖linkup-sdkCrewAI负责智能体的角色定义、任务编排与委派机制依赖crewaiDeepSeek R1驱动各智能体的推理 LLM实际通过本地 Ollama 调用Streamlit把研究逻辑包装成交互式聊天界面。从源码结构看整个项目只有 4 个核心 Python 文件职责划分非常清晰文件职责agents.py定义 LinkUp 搜索工具、LLM 客户端、三个智能体与任务编排对外暴露run_research(query)server.py基于 FastMCP 的 stdio MCP 服务把研究流水线封装为名为crew_research的工具app.pyStreamlit 聊天界面负责 API Key 配置与结果展示pyproject.toml依赖声明要求 Python 3.11两种使用入口最终都汇聚到同一个函数run_research(query)Streamlit 应用在聊天流程中直接调用它MCP Server 则在工具回调中调用它保证了界面版和工具版行为一致。环境准备依赖、本地模型与 API Key依赖安装在项目根目录执行 README 给出的安装命令uv syncpyproject.toml 中声明的运行时依赖为依赖版本约束用途crewai0.114.0多智能体编排框架linkup-sdk0.2.4LinkUp 搜索客户端mcp1.6.0MCP 协议服务端FastMCPopenai1.75.0LLM 调用底层客户端CrewAI 经由 OpenAI 协议对接 Ollamapython-dotenv1.1.0从.env文件加载环境变量streamlit1.44.1交互式 UIstreamlit-crewai-process-output0.1.1CrewAI 过程输出在 Streamlit 中的展示组件项目要求Python 3.11见 pyproject.toml 第 6 行uv sync会依据uv.lock在隔离环境中安装全部依赖。两个运行前提本地 DeepSeek-R1 与 LinkUp API Key源码中有两个 README 未展开、但运行前必须满足的前提本地 Ollama 模型。agents.py 第 13–18 行 中的 LLM 客户端配置如下def get_llm_client(): Initialize and return the LLM client return LLM( modelollama/deepseek-r1:7b, base_urlhttp://localhost:11434 )也就是说虽然技术栈写的是DeepSeek R1 (LLM)但实际推理发生在**本地 Ollama**localhost:11434上模型为 deepseek-r1:7b。运行前需要先用 Ollama 拉取并启动该模型。LinkUp API Key。搜索工具在运行时通过os.getenv(LINKUP_API_KEY)读取密钥见 agents.py 第 44 行文件头部还调用了load_dotenv()第 10 行因此可以把LINKUP_API_KEY写入项目根目录的.env文件也可以在 Streamlit 界面侧边栏中直接填入见下文。核心实现解析LinkUp 搜索工具与三名智能体本节全部对应 agents.py 的源码是整个系统的主干。LinkUp 搜索工具一个带参数 Schema 的 CrewAI Tool智能体能否会搜索取决于工具定义。项目把 LinkUp 封装为标准的 CrewAIBaseTool其输入参数由 Pydantic 模型LinkUpSearchInput描述agents.py 第 23–29 行class LinkUpSearchInput(BaseModel): Input schema for LinkUp Search Tool. query: str Field(descriptionThe search query to perform) depth: str Field(defaultstandard, descriptionDepth of search: standard or deep) output_type: str Field( defaultsearchResults, descriptionOutput type: searchResults, sourcedAnswer, or structured)三个参数的含义与默认值query必填要执行的搜索查询词由 Web Searcher 智能体在运行时自行改写depth默认standard搜索深度可选standard常规搜索或deep深度搜索这是Deep Researcher名称中deep的能力来源——智能体可以为复杂问题自主选择更深度的搜索output_type默认searchResults返回形态可选searchResults原始搜索结果列表、sourcedAnswer带来源引用的答案、structured结构化输出。工具主体LinkUpSearchTool的_run方法agents.py 第 40–55 行逻辑为用环境变量中的密钥初始化LinkupClient调用linkup_client.search(query..., depth..., output_type...)发起搜索将响应str(search_response)后返回给智能体任何异常都不会抛出而是返回形如Error occurred while searching: ...的错误字符串让智能体能看到失败并自行调整策略而不是中断整个流水线。这个错误也作为观察结果返回的写法是 Agentic 工具封装中值得借鉴的健壮性设计。三名智能体角色、目标与委派边界create_research_crew(query)函数agents.py 第 58–126 行定义了三个角色全部挂载同一个本地 DeepSeek-R1 客户端1. Web Searcher网络搜索员—— 唯一持有搜索工具的智能体web_searcher Agent( roleWeb Searcher, goalFind the most relevant information on the web, along with source links (urls)., backstoryAn expert at formulating search queries and retrieving relevant information. Passes the results to the Research Analyst only., verboseTrue, allow_delegationTrue, tools[linkup_search_tool], llmclient, )其 goal 明确要求连同来源链接URL一起找到信息——这是整条流水线引用可追溯要求的第一环。2. Research Analyst研究分析师—— 负责把原始搜索结果合成结构化洞察research_analyst Agent( roleResearch Analyst, goalAnalyze and synthesize raw information into structured insights, along with source links (urls) as citations., backstory... If required, can delagate the task of fact checking/verification to Web Searcher only. Passes the final results to the Technical Writer only., verboseTrue, allow_delegationTrue, llmclient, )注意它的 backstory 设定了一个委派边界仅在需要事实验证时才可以把任务委派回 Web Searcher且最终结果只能交给 Technical Writer。这种用自然语言 backstory 显式约束委派对象与输出流向的写法是 CrewAI 多智能体系统中控制信息流向的常见手段。3. Technical Writer技术写作者—— 只写不搜technical_writer Agent( roleTechnical Writer, goalCreate well-structured, clear, and comprehensive responses in markdown format, with citations/source links (urls)., backstoryAn expert at communicating complex information in an accessible way., verboseTrue, allow_delegationFalse, llmclient, )它的allow_delegationFalse即不允许委派任何任务职责是把分析结果组织成带引用链接的 Markdown 长文。任务链与顺序执行流程三个智能体被三条Task串联agents.py 第 97–116 行search_task Task( descriptionfSearch for comprehensive information about: {query}., agentweb_searcher, expected_outputDetailed raw search results including sources (urls)., tools[linkup_search_tool] ) analysis_task Task( descriptionAnalyze the raw search results, identify key information, verify facts and prepare a structured analysis., agentresearch_analyst, expected_outputA structured analysis of the information with verified facts and key insights, along with source links, context[search_task] ) writing_task Task( descriptionCreate a comprehensive, well-organized response based on the research analysis., agenttechnical_writer, expected_outputA clear, comprehensive response that directly answers the query with proper citations/source links (urls)., context[analysis_task] ) crew Crew( agents[web_searcher, research_analyst, technical_writer], tasks[search_task, analysis_task, writing_task], verboseTrue, processProcess.sequential )关键编排细节每个任务都声明了expected_output用来锚定该环节的产物形态带 URL 的原始结果 → 结构化分析 → 带引用的完整回答后一个任务通过context[前一个任务]声明数据依赖搜索任务的结果流入分析任务分析结果再流入写作任务processProcess.sequential指定顺序流程任务按定义顺序逐个执行而非由智能体自由协商并行。最终执行入口是run_researchagents.py 第 129–136 行构建 Crew、调用crew.kickoff()并返回result.raw纯文本结果异常时同样以Error: ...字符串返回保持了对上层调用方Streamlit / MCP一致的返回契约。使用方式一Streamlit 交互应用按 README 的说明启动应用streamlit run app.pyapp.py 实现了单页聊天界面要点如下侧边栏 API Key 配置app.py 第 31–37 行用户在侧边栏输入 LinkUp API Key 后会被写入会话状态并同步设置到环境变量if linkup_api_key: st.session_state.linkup_api_key linkup_api_key # Update the environment variable os.environ[LINKUP_API_KEY] linkup_api_key由于LinkUpSearchTool._run是在每次搜索时才读取os.getenv(LINKUP_API_KEY)所以界面中输入的 Key 能即时生效无需重启进程。未填 Key 时提问会得到 Please enter your Linkup API Key in the sidebar. 的提示app.py 第 70–71 行。聊天主流程app.py 第 65–83 行用户提问后界面在 Researching... 的 spinner 下调用run_research(prompt)把返回的 Markdown 结果以 assistant 消息追加进st.session_state.messages历史支持连续多轮提问与一键清空Clear 按钮触发reset_chat。页面通过st.set_page_config(page_title Agentic Deep Researcher, layoutwide)使用宽布局展示长文结果。由于 DeepSeek-R1 在本地推理、搜索走 LinkUp 云端该界面适合在联网开发机上直接体验完整的提问 → 多智能体研究 → 带引用长文流程。使用方式二暴露为 MCP Server 供其他客户端调用这是本项目标题中MCP二字的落点整个多智能体研究系统被封装为一个标准的stdio 传输 MCP Server任意支持 MCP 的客户端如 Cursor都可以把crew_research当作一个工具来调用。服务端实现server.py 只有约 20 行核心代码from mcp.server.fastmcp import FastMCP from agents import run_research # Create FastMCP instance mcp FastMCP(crew_research) mcp.tool() async def crew_research(query: str) - str: Run CrewAI-based research system for given user query. Can do both standard and deep web search. Args: query (str): The research query or question. Returns: str: The research response from the CrewAI pipeline. return run_research(query)要点FastMCP(crew_research)创建了名为crew_research的 MCP 服务实例mcp.tool()装饰器把crew_research(query)注册为工具其 docstring 会成为工具描述的一部分——其中明确告知调用方Can do both standard and deep web search对应 LinkUp 工具的depth参数工具是async的内部同步调用run_research完整复用 Streamlit 应用同一套智能体流水线服务以mcp.run(transportstdio)启动server.py 第 22–23 行即通过标准输入/输出与宿主客户端通信这是本地 MCP Server 最常见的传输方式。客户端配置README 给出的 MCP 客户端配置以mcpServers片段为例如下{ mcpServers: { crew_research: { command: uv, args: [ --directory, ./Multi-Agent-deep-researcher-mcp-windows-linux, run, server.py ], env: { LINKUP_API_KEY: your_linkup_api_key_here } } } }配置解读与实操注意commandargs客户端用uv --directory 项目目录 run server.py拉起服务。uv --directory保证在项目自己的虚拟环境uv.lock锁定的依赖里运行server.py无需手动激活环境路径必须改成你自己机器上的位置--directory参数指向的是本仓库的Multi-Agent-deep-researcher-mcp-windows-linux目录README 中写的是相对仓库根的路径server.py 第 26–42 行 的注释示例则展示了配置绝对路径的写法。实际使用时请替换为本机克隆仓库后对应目录的绝对路径env通过 MCP 客户端直接注入LINKUP_API_KEY服务端在启动进程中即可通过环境变量拿到密钥与.env文件方式二选一即可该目录同时支持 Windows 与 Linux目录名mcp-windows-linux即为此意配置片段可放入对应客户端的 MCP 配置文件例如 Cursor 的./.cursor/mcp.json。配置完成后宿主客户端中的模型即可像调用普通工具一样发起crew_research(query)背后自动完成Web Searcher 深度搜索 → Research Analyst 综合分析 → Technical Writer 成文的全过程并把带引用链接的 Markdown 研究报告作为工具返回值交还。设计要点小结从这套实现中可以提炼出几个可复用的模式职责分段 数据依赖显式化用Task.context把上游任务产物显式传给下游配合Process.sequential保证执行顺序确定避免了自由协商式流程在简单场景下的不可控引用贯穿全链从 goal 到expected_output每个环节都强制要求携带 source URLs使最终报告句句可溯源工具错误不抛异常搜索失败时返回错误描述字符串让智能体有机会改写查询重试同一核心、两种门面run_research()作为唯一入口被 Streamlit 与 MCP 两个壳层复用既提供人类交互界面又向 AI 客户端开放为工具本地小模型驱动通过ollama/deepseek-r1:7b在本地完成推理搜索知识则外包给 LinkUp 云端本地推理 云端检索的组合降低了 API 成本与数据出境面。相关文件索引README.md安装、启动与 MCP 配置说明agents.py工具、智能体、任务与 Crew 定义server.pyFastMCP stdio 服务端app.pyStreamlit 聊天界面pyproject.toml依赖与 Python 版本约束需要说明的是运行本系统依赖外部服务LinkUp API与本地 Ollama 上的deepseek-r1:7b模型且依赖版本以当前仓库 pyproject.toml 声明的下限crewai0.114.0、mcp1.6.0 等为准CrewAI 与 MCP SDK 迭代较快升级依赖后需回归验证Task.context、Process.sequential等 API 的兼容性。【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

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