项目名称:ScholarCraft
定位:一个本地与远端混合模型驱动的科研调研Agent,能自主规划、调用工具、阅读论文并生成结构化报告。
技术栈:LangGraph + Ollama (Qwen3-4B) + 小米 MiMo API
进度:环境搭建、混合模型选型、核心闭环(任务规划→工具调用→联网检索→报告生成)、MCP协议集成、迭代式检索机制


前言

继之前的企业级RAG系统和大模型微调项目之后,这次我把目光投向了2026年的新风口——AI Agent。

网上的Agent教程虽然越来越多,但大部分还是教你如何调用一个API,或者用现成框架跑个Demo。真正从零开始,把任务规划、工具调用、状态管理、记忆机制这些核心模块都自己写一遍,并且把每一步的决策逻辑讲清楚的项目,其实并不多。

我决定自己动手。这个项目我命名为ScholarCraft,目标是构建一个能辅助科研调研的Agent。本文是系列的第一篇,主要记录基础环境搭建、模型选型策略、核心闭环的实现过程,以及后续迭代中引入的联网检索、MCP协议集成和迭代式检索机制。我的开发环境是一台Windows笔记本,配置为RTX 3060 Laptop(6GB显存)加上WSL2。


一、项目总览

1.1 项目定位与能力矩阵

ScholarCraft的核心目标是展示一个Agent工程师的五大核心能力:

能力维度 当前状态 实现方式
任务规划 ✅ 已完成 MiMo API 将模糊需求拆解为结构化步骤
工具调用 ✅ 已完成 本地检索 + arXiv联网检索 + 论文详情读取 + 报告保存
联网检索 ✅ 已完成 arXiv API,按日期降序,支持自动重试
MCP协议集成 ✅ 已完成 四个工具全部封装为标准MCP Server
迭代式检索 ✅ 已完成 搜索结果不足时自动换词重搜
记忆管理 ⬜ 规划中 短期记忆(滑动窗口+摘要压缩)+ 长期记忆(SQLite+ChromaDB)
上下文工程 ⬜ 规划中 Token预检 + 工具返回筛选 + 摘要兜底
安全与观测 ⬜ 规划中 熔断机制 + 权限模型 + 全链路Trace日志
评测体系 ⬜ 规划中 15个分级测试用例 + 自动化评测脚本

1.2 项目文件架构

text

scholar-craft/
├── app.py                          # CLI入口,接收用户输入并调用graph
├── requirements.txt                # 项目依赖(langgraph, ollama, httpx, chromadb等)
├── .env                            # API Key(MiMo),已加入.gitignore
├── .gitignore
│
├── data/
│   └── papers.json                 # 本地论文数据库(结构化JSON,含标题/摘要/方法/指标等)
│
├── reports/                        # 生成的调研报告(Markdown)
│   └── report_*.md
│
├── docs/
│   └── design.md                   # 设计决策记录(模型选型、架构权衡等)
│
├── evaluation/
│   ├── test_cases.json             # 15个分级测试用例(简单/多步/模糊/无结果/极长指令)
│   └── run_eval.py                 # 自动化评测脚本
│
└── src/
    ├── __init__.py
    │
    ├── graph/                      # LangGraph 编排引擎
    │   ├── __init__.py
    │   ├── state.py                # AgentState 定义(8个核心字段)
    │   ├── nodes.py                # 6个业务节点 + 2个路由函数
    │   └── builder.py              # StateGraph 组装(节点注册、条件边、循环逻辑)
    │
    ├── tools/                      # 工具层(已全部封装为MCP Server)
    │   ├── __init__.py
    │   ├── search_local.py         # 本地论文关键词检索
    │   ├── search_arxiv.py         # arXiv 联网检索(httpx + XML解析)
    │   ├── read_paper.py           # 论文详情读取
    │   ├── save_report.py          # 报告保存为Markdown
    │   ├── permission.py           # 工具权限装饰器(read/write/network)
    │   ├── circuit_breaker.py      # 熔断器(连续失败3次暂停执行)
    │   ├── mcp_server.py           # MCP Server(将4个工具注册为标准接口)
    │   └── test_mcp.py             # MCP Server 工具发现与调用测试
    │
    ├── models/                     # 模型层
    │   ├── __init__.py
    │   ├── ollama_client.py        # 本地 Qwen3-4B 调用封装
    │   └── xiaomi_client.py        # MiMo API 调用封装(OpenAI兼容)
    │
    ├── memory/                     # 记忆层(规划中)
    │   ├── __init__.py
    │   ├── short_term.py           # 短期记忆(SqliteSaver + 滑动窗口)
    │   ├── long_term.py            # 长期记忆(SQLite + ChromaDB 语义检索)
    │   └── compressor.py           # 摘要压缩
    │
    ├── context/                    # 上下文工程(规划中)
    │   ├── __init__.py
    │   └── manager.py              # ContextManager(Token计数、结果筛选、摘要兜底)
    │
    ├── planner/                    # 规划层
    │   ├── __init__.py
    │   └── planner.py              # MiMo API Prompt模板
    │
    ├── observability/              # 观测层(规划中)
    │   ├── __init__.py
    │   └── tracer.py               # 全链路Trace日志
    │
    └── api/                        # FastAPI接口(规划中)
        ├── __init__.py
        ├── main.py
        └── routes/

1.3 执行流程全景图

text

用户输入: "调研分子生成领域扩散模型的最新进展"
                │
                ▼
┌──────────────────────────────────────────────────────┐
│              LangGraph 编排引擎                        │
│                                                      │
│  [plan_node]                                         │
│  MiMo API 拆解任务 → 生成4-5个步骤(JSON)             │
│  "搜索本地 → 搜索arXiv → 读取论文 → 生成报告"           │
│           │                                          │
│           ▼                                          │
│  ┌───[ 执行循环 ]────────────────────────────────┐   │
│  │                                               │   │
│  │  [tool_select_node]                           │   │
│  │  本地Qwen3-4B: 根据当前步骤选择工具并生成参数    │   │
│  │  输出: {"query": "...", "max_results": 5}     │   │
│  │           │                                   │   │
│  │           ▼                                   │   │
│  │  [tool_call_node]                             │   │
│  │  通过 MCP 协议调用工具                          │   │
│  │  → search_local_papers / search_arxiv / ...   │   │
│  │           │                                   │   │
│  │           ▼                                   │   │
│  │  [evaluate_node]                              │   │
│  │  检查结果数量和质量                             │   │
│  │  ├── 有效论文 ≥ 3 → 前进到下一步               │   │
│  │  └── 不足 + 重试次数 < 2 → 回到tool_select     │   │
│  │       (生成新搜索词,再搜一轮)                  │   │
│  └───────────────────────────────────────────────┘   │
│           │                                          │
│           ▼                                          │
│  [report_node]                                       │
│  汇总所有tool_results → 本地LLM整合为Markdown报告     │
│           │                                          │
│           ▼                                          │
│  [save_node] → 写入 reports/ 目录                     │
│                                                      │
└──────────────────────────────────────────────────────┘

二、技术选型与架构设计

2.1 为什么选 LangGraph 而不是 LangChain?

这是面试中的高频问题。LangChain是"工具包",提供了模型调用、文档加载、Prompt模板等组件,擅长构建A→B→C的线性链。但Agent的执行流程充满了分支、循环和动态决策——比如"搜完发现结果不够,需要换个关键词再搜"——这种非线性流程需要的是状态机和图框架,而不是传送带。

LangGraph的三个核心概念——节点、边、状态——完美匹配Agent的需求。节点是执行单元,边定义流程路径(包括条件分支),State是集中式的数据中枢。所有状态更新都是显式且可追溯的,出问题时能立刻定位到具体步骤。这个选择背后的逻辑,在面试中往往比技术本身更被看重。

2.2 混合模型策略

模型 部署方式 用途 选择理由
Qwen3-4B-Instruct (4-bit) Ollama 本地 工具选择、步骤评估、报告整合 低延迟、离线可控、隐私保护
MiMo-V2.5-Pro 小米API 任务规划(初始拆解) 推理能力强,稳定输出JSON
nomic-embed-text Ollama 本地 长期记忆语义检索(后续使用) 本地运行,保持离线闭环

权衡逻辑:工具选择和评估在ReAct循环里被反复调用,需要毫秒级响应,所以放在本地;而任务拆解每次任务只调用一次,但对推理能力要求高,用远端API更划算。这种"本地优先、远端补强"的策略,同时平衡了成本、延迟和隐私。

2.3 MCP协议集成

MCP(模型上下文协议)正在成为AI Agent工具调用的行业标准。它解决的问题是:不同Agent框架、不同工具之间的接口不统一,每次新增工具都要改框架代码。

在ScholarCraft中,我把四个论文工具封装为MCP Server:

text

tool_call_node  →  MCP Client  →  MCP Server(注册了所有工具)  →  工具函数

核心价值:工具层和编排层彻底解耦。新增工具只需在MCP Server注册,nodes.py不用动。面试时能直接展示对行业标准的关注和实践。


三、基础环境准备

我的硬件环境是Windows 11系统,搭载NVIDIA GeForce RTX 3060 Laptop显卡(6GB显存),通过WSL2来运行Linux环境,Python版本选择的是3.11。

踩坑提示:强烈建议使用Python 3.10或3.11。AI生态圈对最新Python版本的兼容往往有延迟,追求过新的版本(比如3.12或3.13)容易在编译依赖时遇到令人头疼的报错。

创建Conda环境:

bash

conda create -n scholarcraft python=3.11 -y
conda activate scholarcraft

Ollama安装与模型拉取:

bash

ollama pull qwen3:4b-instruct
ollama pull nomic-embed-text

Qwen3-4B用了4-bit量化版本,大小约2.5GB,显存占用大约4GB,我的6GB卡跑起来绰绰有余。nomic-embed-text只有274MB,主要用于后续长期记忆的语义检索。

MiMo API完全兼容OpenAI接口格式,验证完毕后把API Key移到.env文件里保护起来。


四、核心闭环:从模糊需求到结构化报告

4.1 工具系统

最初我实现了三个基础工具:search_local_papers基于关键词检索本地论文库,read_paper_detail根据论文ID返回完整信息,save_report将最终报告保存为Markdown文件。

种子数据库papers.json包含3篇结构化论文,覆盖扩散模型、等变扩散和LoRA方向。每篇论文包含标题、作者、年份、关键词、摘要、方法、数据集、关键指标和对比基线。

4.2 状态机与节点设计

AgentState包含八个关键字段:user_query存放用户原始需求,plan存放任务规划生成的步骤列表,current_step_index标记当前执行进度,tool_results记录每个步骤的工具返回结果,messages用于消息历史,final_report存放最终报告,terminated标记任务是否结束,errors收集执行错误。

六个核心节点:plan_node调用MiMo API拆解任务,tool_select_node让本地LLM选择工具并生成参数,tool_call_node执行工具函数,evaluate_node评估结果并决定下一步,report_node汇总数据生成报告,save_node写入文件。

在tool_select_node中我做了一个重要优化:将LLM生成的参数存入current_params字段,避免下游节点重复解析JSON可能出错。

4.3 遇到的坑与解决方案

坑1:ollama.chat()返回值无法解析。现象是tool_select_node中报错"the JSON object must be str",原因是ollama.chat()返回的是ChatResponse对象而非纯字符串。解决方法是封装ollama_client.py,在内部提取message.content并返回字符串。

坑2:tool_call_node无法获取read_paper的paper_id参数。原因是参数依赖messages列表传递,不同节点间格式未对齐。解决方法是新增current_params字段,由tool_select_node直接写入、tool_call_node直接读取。

4.4 联网检索与迭代式检索

新增search_arxiv工具,调用arXiv API获取最新预印本,按日期降序排列确保时效性。更重要的是,引入了迭代式检索机制——让Agent在搜索结果不足时自动调整策略。

核心改动在evaluate_node:搜索步骤完成后检查有效论文数量。如果少于3篇且重试次数未满2次,回到tool_select_node让LLM根据上轮结果生成新的搜索词,再搜一轮。直到结果足够多或重试次数耗尽。

升级后的日志清晰展示了这一过程:

text

[EVAL] 只找到 1 篇有效论文,触发补充搜索(第1次重试)
[SELECT] 补充搜索模式,生成新查询词...
[CALL] 使用 arXiv 联网检索: 'diffusion model for molecule generation drug design 2025'
[EVAL] 找到 4 篇有效论文,搜索完成,继续下一步

4.5 端到端测试

运行python app.py "帮我调研分子生成领域扩散模型的最新进展",Agent完整执行:本地搜索→arXiv联网检索→读取论文详情→生成报告。

最终报告整合了本地论文库和arXiv的最新工作,所有数据可溯源到实际检索结果。对arXiv预印本诚实标注"指标未提供",不编造不存在的性能数据。方法对比表展示了本地综述论文与arXiv最新工作的整合分析,所有arXiv论文标题旁都标注了[arXiv],区分了数据源的权威性等级。


五、总结与下一步

本文记录了ScholarCraft从零开始的核心旅程:P0环境基建和模型验证、P1工具系统和状态机设计、P1.5联网检索和迭代式检索机制、P2 MCP协议集成。至此,Agent已具备任务规划、多步工具调用(本地+联网)、搜索质量反馈与自动重试、MCP标准化工具接口、基于真实数据的诚实报告生成等能力。

LangGraph的状态机模式让调试变得高效——出问题时能立刻定位到具体节点和状态。迭代式检索让Agent在搜索结果不够好时学会换词再搜。MCP集成让工具层和编排层彻底解耦,为后续扩展奠定了架构基础。

在下一篇文章中,我将继续迭代优化:引入LLM驱动的智能评估,让Agent判断搜索结果的真实相关性;加强流程控制,实现显式条件边和熔断机制;优化上下文管理,在多轮搜索中计算Token消耗并动态压缩历史信息。

完整的项目代码会同步更新到GitHub,欢迎关注和交流。

本文是ScholarCraft系列的第一篇,后续文章将陆续更新。如果有任何工程落地上的问题或建议,欢迎在评论区讨论。

Logo

更多推荐