目 录项目背景为什么简单 RAG 不够为什么引入 LangGraph当前架构图状态流转说明第一步接口层构造初始状态第二步choose_tool 写入 decision第三步execute_tool 写入 tool_result第四步generate_answer 写入 final_answer一句话总结这套状态流转这套设计的工程意义第一职责更清楚第二状态更透明第三后续扩展更自然项目背景在科研和论文写作过程中研究者往往需要频繁查找、理解和回溯大量参考文献。例如当某个研究想法需要理论支撑时常常需要快速定位“某个论点最早出自哪篇论文”“某种方法的动机是什么”“某篇参考文献解决了什么问题”。随着文献数量增加研究者容易遗忘论文的核心内容和出处导致检索、回顾和整理成本越来越高。基于此项目尝试构建一个面向科研场景的论文 RAG 问答与分析系统用于辅助完成文献检索、论文问答、多轮追问和知识回溯从而提升文献利用效率减少重复查找时间。为什么简单 RAG 不够基础 RAG 可以完成“检索相关片段并生成回答”的任务但在真实科研场景中往往还不够。一方面研究者的问题并不总是单轮的很多时候需要围绕同一篇论文持续追问例如论文背景、研究问题、方法设计、实验结论以及公式含义等。另一方面部分任务还需要结合额外工具例如时间查询、简单计算、概念解释等能力才能完成更完整的辅助分析。因此项目不只停留在单轮检索问答而是进一步引入多轮对话、工具调用和任务编排能力使系统能够更贴近真实使用场景。为什么引入 LangGraph在前期版本中系统已经通过手写方式实现了基础的 RAG、工具选择、工具执行和结果返回流程能够完成一个简化的 Agent 雏形。但随着功能逐渐增多若继续依赖手写串联逻辑编排层会越来越复杂工具扩展、节点维护和状态管理的成本也会逐步升高。引入 LangGraph 的目的是将原本分散的执行逻辑进一步节点化和状态化把工具选择、工具执行、结果生成等步骤整理为更清晰的工作流结构。这样做有助于提升系统的可维护性、可扩展性和工程展示性也更适合后续承接更复杂的 Agent 场景。当前架构图基础设施层数据处理层RAG 检索层能力层编排层会话管理层接口层ragllmcalculatortimeUser or ClientFastAPI API main.pyPOST askPOST clear sessionSessionManager session_manager.pyGet history by session_idAppend turnClear sessionAgentWorkflow invoke workflow.pyBuild AgentStateLangGraph StateGraph builder.pychoose_tool nodedecision tool and inputexecute_tool nodetool nameRAG toolLLM toolCalculator toolTime toolgenerate_answer nodefinal_answerrag ask with chat historygeneral LLM answerevaluate expressionget current timeretrieveFAISS indexquery embeddingtop k chunksrerankbuild context with sourceLLM generate answerdata pdf filesload_pdfs data_loader.pyclean_textsplit_text and process_documentschunksbuild_indexconfig.pyllm_utils.pylogger_config.pyReturn JSON response该系统采用“接口层 会话管理层 LangGraph 编排层 工具层 RAG 检索层 数据处理层”的结构。用户请求首先通过 FastAPI 进入系统结合 session_id 获取历史对话后交由 LangGraph 编排层处理。编排层基于状态对象 AgentState 串联 choose_tool、execute_tool 和 generate_answer 三个节点实现工具选择、工具执行和结果汇总。对于文档类问题系统会进一步调用 RAG 模块完成向量检索、重排序和基于上下文的回答生成对于通用问题则调用 LLM 或其他工具完成响应。整个系统支持多轮对话、工具扩展和后续更复杂 Agent 工作流的继续演进。状态流转说明为了让多个节点之间能够顺利协作这个项目定义了一个统一的状态结构AgentState。当前状态中主要包含以下字段session_id当前会话标识query用户当前输入的问题chat_history当前会话历史decision路由决策结果tool_result工具执行结果final_answer最终返回结果error异常信息字段从一次完整请求来看状态的大致流转过程如下。第一步接口层构造初始状态用户通过/ask接口提交session_id和question后系统会先通过SessionManager读取当前会话的历史消息然后把这些信息传给AgentWorkflow.invoke()。在invoke()中系统会初始化一个最初版本的状态对象state{session_id:session_id,query:query,chat_history:chat_history,}此时状态里还没有decision、tool_result和final_answer因为这些字段要在后续节点执行中逐步补全。第二步choose_tool 写入 decision图开始执行后首先进入choose_tool节点。这个节点会读取已有的query然后生成路由决策并把结果写入state[decision]执行完这一阶段后状态大致会变成{session_id:...,query:...,chat_history:[...],decision:{tool:rag,input:...}}如果这一阶段失败则除了decision外还会附带一个error字段。第三步execute_tool 写入 tool_result接下来进入execute_tool节点。这个节点会读取decision执行对应工具并把执行结果写入state[tool_result]执行完成后状态会进一步扩展为{session_id:...,query:...,chat_history:[...],decision:{...},tool_result:{tool_name:...,tool_input:...,tool_output:...}}如果工具不存在或者运行中发生异常那么还会同步写入error。第四步generate_answer 写入 final_answer最后generate_answer节点会读取tool_result或error产出最终结果并写入state[final_answer]此时一次完整的状态流转就完成了。最终状态中最核心的输出字段就是final_answer它会被workflow.invoke()取出并返回给接口层再由接口层写入会话历史并返回给前端。一句话总结这套状态流转如果用一句比较工程化的话来概括这套流程可以描述为接口层负责构造初始输入状态编排层负责逐节点补全中间状态最终由收口节点产出统一输出状态。也就是说query和chat_history是输入状态decision是路由中间状态tool_result是执行中间状态final_answer是最终输出状态这套设计的工程意义虽然当前图还比较简单但这种“节点职责清晰 状态逐步补全”的写法已经比把所有逻辑直接堆在一个函数里更适合扩展。它至少带来了三个好处第一职责更清楚工具选择、工具执行、最终收口被拆成了三个明确阶段而不是混在一起。第二状态更透明每一步输入什么、输出什么都能看清楚后面调试日志、定位异常会更方便。第三后续扩展更自然将来如果想加入条件分支、增加新节点、引入更复杂的多步推理或外部工具接入基于状态图继续扩展会比继续手写串联逻辑更稳。如果这篇文章对你有帮助可以点个赞完整代码地址https://github.com/1186141415/LangChain-for-A-Paper-Rag-Agent