基于 DeepSeek V4 + LangChain 的学术写作辅助 Agent,带 Gradio 对话界面。集成 长期记忆 与 复杂任务分步规划,可检索真实论文并导出 Word 文档。
写综述时,最耗神的往往不是想出观点,而是一堆「琐碎但必须做」的事:跨多个数据库反复检索同一批文献、几十篇引用的格式(APA / IEEE / MLA / GB-T 7714)调了三遍还没统一、长文写到后半程忘了前面定过什么框架。这些事不复杂,但极其耗时——真正需要思考的研究问题反而被挤没了。
所以我想做一个「学术导师」:像导师一样记得你的研究方向、帮你找到真实可引用的文献、按你偏好的格式整理引用、陪你把一篇长文从确认需求到分步写完。它不取代写作,而是把检索、格式、结构这些可自动化的环节接走,让你专注在论证本身。
这个项目不是一次性生成,而是经过六代迭代,每一代都解决上一代暴露的问题:
| 版本 | 形态 | 解决的问题 | 暴露的问题 |
|---|---|---|---|
| V1 | 直接包装 DeepSeek API 的聊天助手(一个 prompt + 一个 chat 函数) | 能用上 DeepSeek 对话 | 只会聊天:不会查文献、不会记忆、不会写长文 |
| V2 | 接入 Semantic Scholar 检索,从「聊天」到「能查文献」 | 用户需要搜到真实可引用的论文 | 检索结果用一次就丢:记不住用户偏好,也没有质量把关 |
| V3 | 首次加入用户画像记忆 | 记住研究方向、引用格式偏好 | 开发者自留备注:「V3 时的记忆模块需要与时俱进」——规则式关键词匹配太死板,换个说法就记不住 |
| V4 | 手写 ReAct Agent:自实现工具调用循环、自我审校(reviewer 二次校验)、记忆、Semantic Scholar + arXiv 双源检索兜底 | 真正「能干活」:自己搜论文、回复前自我检查 | 工具循环、审校、记忆全部手写,逻辑交错,维护成本高;长文写作仍是一次性输出 |
| V5 | 迁移到 LangChain(@tool + create_agent);重做记忆(规则式兜底 + LLM 提取双通道、防回显、文件锁) |
框架化可维护;记忆从「规则死板」变成「规则 + LLM」双保险 | 复杂写作任务仍一步到位:模型拿到指令就倾向一次写完,无视「先确认需求」 |
| V6 | 复杂任务状态机 + 两级意图路由 | 长文分步推进(确认→大纲→逐节→整合);意图识别成本可控 | — |
说明:当前实现对应 V6;代码文件沿用了早期版本命名
agent_v5_langchain.py。V1、V4 的原始源码已归档至Other version/目录;V2、V3 为阶段总结、无逐版可运行代码。
从 V4 到 V6,这个项目始终在打磨同一个公式:
Agent = LLM(推理) + 记忆(状态) + 工具(行动) + 提示词(策略) + 规划循环(控制)
- V4(手写):公式里的五个组件——LLM 调用、记忆、工具、提示词、规划循环——全部手写实现,每个都亲手做过一遍。
- V5(LangChain 标准化封装):用框架把 LLM + 工具封装成标准组件,省掉重复造轮子。
- V6(强化规划循环与提示词策略):重点强化公式中的两块——规划循环升级为状态机,提示词策略升级为两级意图路由。
每一代都不是「换个框架」,而是围绕公式里某个组件做一次针对性强化。
每个决策背后都有一次真实的踩坑或取舍,写出来比结论本身更有说服力:
1. 先手写 ReAct,再迁移到 LangChain(V4 → V5)
手写完整个 ReAct 循环后,我才真正理解 Agent 的本质不是模型调用,而是状态维护——每轮推理之后,系统要把工具结果、对话历史、当前进度当作状态管理起来,否则 agent 就是一次性的。所以 V5 迁到 LangChain 时,我判断它省掉的是重复造轮子;但状态管理、记忆持久化、意图路由这些核心逻辑,我选择自己掌控,而不是把决策权交给框架。
2. 意图识别用两级路由,而非直接交给 LLM(V6)
普通对话占请求的绝大多数。如果我让模型每轮都判断一次「这是不是复杂任务」,等于为 90% 的简单问答也付一次推理成本。所以我把明确句式收进规则层(零成本),只有拿不准的边界句才动用轻量 LLM——判断失败时默认降级为普通对话,最坏情况与不做路由一致。
3. 记忆持久化用多线程 + 文件锁(V5)
记忆更新放在后台线程,是为了不阻塞用户看到回答;但多线程一旦各自持有旧快照,就会互相覆盖——这个坑我踩过才记住。解法是让「读-改-写」整体进入全局锁,worker 永远基于最新文件状态。
技术实现细节见下方「设计要点」;意图路由的 20 条边界用例见
tests/test_intent_routing.py。
| 功能 | 说明 |
|---|---|
| 🔍 论文检索 | 调用 Semantic Scholar Graph API 搜索真实学术文献 |
| 📝 文档生成 | 用 python-docx 一键导出 Word 文档(支持 Markdown 标题转换) |
| 🧠 长期记忆 | 用户画像(user_profile.json)跨会话记住研究方向、引用格式、论文进度、关注关键词 |
| 📋 任务规划 | 复杂写作任务自动分步推进:确认需求 → 大纲 → 逐节 → 整合导出 |
| ✅ 学术质量自检 | 输出前强制检查:引用真实性 / 格式一致性 / 逻辑连贯 / 空洞表述 |
| 🔎 引用审计 | 回答中的引用与本次检索白名单比对,无法核实的标注 [需核实] |
| 💬 多轮对话 | 完整历史回传,保持会话上下文 |
- 模型:DeepSeek V4(OpenAI 兼容接口,思考模式)
- Agent 框架:LangChain 1.x
create_agent+@tool工具装饰器 - 前端:Gradio
ChatInterface+gr.State(跨轮状态) - 文档:python-docx
- 检索:Semantic Scholar Graph API
以下设计决策是本项目保持可维护、可验证的关键,也是代码实现的直接依据。
实测发现:create_agent 的模型在单个 invoke 内可自由决定执行多少步,即使系统提示词写明「必须先确认需求、禁止一次写完」,模型仍倾向一步到位生成全文。因此改用代码级状态机(idle → outline → writing → done),每一轮只向模型注入当前步骤的单一指令,配合 gr.State 保存跨轮进度,从根上杜绝「一步写完」。
入口用「两级意图路由」:状态机的触发不靠穷举关键词,而是「强信号直接判定 → 弱信号(边界句)才调用轻量 LLM 精判 → 无关直接放行」。普通对话零额外成本与延迟;LLM 只处理拿不准的句子(复用 DeepSeek flash 且禁用思考模式,单次约 0.6-1 秒);识别失败默认降级为普通对话,最坏情况与不做路由一致。
量化对比(52 条边界句实测,python tests/benchmark_intent_routing.py 可复现):
| 方案 | 准确率 | 平均延迟 | LLM 调用率 | 估算成本 |
|---|---|---|---|---|
| 纯关键词 | 86.5% | 0.0ms | 0% | ¥0 |
| 纯 LLM | 90.4% | 503ms | 92% | ¥0.010 |
| 两级路由 | 96.2% | 83ms | 13% | ¥0.0015 |
两级路由在准确率上反超纯 LLM(强信号规则层是确定性兜底,纯 LLM 反而在明确句上会波动),同时延迟降约 6 倍、成本省约 7 倍——用数据验证"规则层归规则、拿不准才归 LLM"的取舍。
⚠️ 基准说明:52 条为自建边界句样例(强信号段即规则词表本身),单次运行;LLM 层有随机性,数值会随运行波动、重跑可能不同。想得到稳定结论,建议混入真实用户对话样本、多次运行取均值±方差。
- 规则式:确定性更新核心字段(研究方向 / 引用格式 / 语言偏好),零成本、可预期。
- LLM 提取:负责开放字段(关键词、论文进度、重要发现),后台线程异步执行,不阻塞响应。
- 排除了两个坑:① LLM 会把 prompt 里的旧画像值「回显」覆盖新值 → 提取 prompt 不再包含当前画像、规则已处理的字段 LLM 不得覆盖;② 多线程各自持旧快照互相覆盖 → 读-改-写放进全局锁,worker 始终基于最新文件状态。
- Semantic Scholar 限流(429)时指数退避重试 + 友好降级,不让网络抖动打断整个 agent。
- Word 生成工具兼容 Markdown 标题语法。
# 1. 安装依赖(Python 3.11+)
pip install -r requirements.txt
# 2. 配置密钥
cp .env.example .env
# 编辑 .env,填入 DEEPSEEK_KEY 和 SEMANTIC_SCHOLAR_KEY
# 3. 运行
python agent_v5_langchain.py打开 http://127.0.0.1:7860 即可对话。
| 变量 | 获取地址 |
|---|---|
DEEPSEEK_KEY |
https://platform.deepseek.com |
SEMANTIC_SCHOLAR_KEY |
https://www.semanticscholar.org/product/api |
| 你输入 | Agent 行为 |
|---|---|
什么是城市热岛效应? |
直接解释概念(不走复杂流程) |
帮我找 2 篇图神经网络的论文 |
调用 search_papers 检索并整理 |
帮我写一篇关于城市热岛效应遥感监测的完整综述 |
触发分步流程:确认需求 → 大纲 → 逐节 → 导出 Word |
我的研究方向是城市热岛效应的遥感监测,引用格式用 APA |
写入长期记忆,后续回答自动个性化 |
├── agent_v5_langchain.py # 启动入口(薄壳,核心在 academic_mentor/)
├── academic_mentor/ # 核心包(依赖单向: config → tools/memory → citation_audit/router/state_machine → agent → ui)
│ ├── config.py # 环境变量 / 常量 / LLM 连接(含意图判断轻量实例)
│ ├── memory.py # 长期记忆:用户画像读写 / 规则+LLM 提取 / 文件锁
│ ├── tools.py # 论文检索 / Word 文档生成 / 检索白名单池
│ ├── citation_audit.py # 引用审计:正则提取引用 + 白名单校验
│ ├── router.py # 两级意图路由
│ ├── state_machine.py # 复杂任务状态机 + 各阶段提示词
│ ├── agent.py # 系统提示词 + create_agent
│ └── ui.py # Gradio 前端 + 对话回调
├── tests/
│ ├── test_intent_routing.py # 20 条路由边界用例(每条例标注路由层)
│ └── benchmark_intent_routing.py # 路由量化对比(52 条,准确率/延迟/成本)
├── Other version/ # 历史版本源码(V1 手写封装 / V4 手写 ReAct)
├── requirements.txt # 依赖清单
├── .env.example # 密钥模板(不含真实值)
└── README.md
- 单用户演示:长期记忆按单用户设计,未做多用户隔离,不适合直接多人使用。
- 引用审计覆盖面有限:只比对本次检索白名单,白名单外的引用无法自动核实。
- 无 CI:测试与基准需手动运行,未接入持续集成。
- 基准样本自建:52 条边界句为自建样例,未混入真实用户对话,指标为参考值(见上文基准说明)。
项目由本人与 Claude Code(Anthropic 的 AI 编码助手)协作开发:核心架构、状态机与两级路由设计由本人主导,Claude Code 协助实现、边界测试与文档迭代。