code_snippets, tsne_fig_path, ablation_ideas 等等。# llm_config.py
from config import api_type, api_key, api_base, api_version, model_name
from langchain.chat_models import AzureChatOpenAI
def build_llm():
return AzureChatOpenAI(
openai_api_base=api_base,
openai_api_version=api_version,
deployment_name=model_name,
openai_api_key=api_key,
openai_api_type=api_type,
temperature=0.2,
)这里不再单独搞一个 AgentExecutor,而是直接在 node 函数里 call LLM + arxiv 工具(你也可以复用上一篇 paper_hunter_agent,我这里写个轻量版示意)。
# nodes.py
from typing import Dict
from langgraph.graph import END
from langchain.utilities import ArxivAPIWrapper
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from research_state import ResearchState
from llm_config import build_llm
llm = build_llm()
arxiv = ArxivAPIWrapper()
# ===== 1) PaperHunter Node =====
def paper_hunter_node(state: ResearchState) -> ResearchState:
topic = state["topic"]
query = (
f"{topic}, focus on works since 2022, "
"especially those using CLIP or vision-language models."
)
result = arxiv.run(query)
state["papers_raw"].append(result)
state["logs"].append(f"[PaperHunter] searched arxiv with query: {query}")
return state# nodes.py (继续)
paper_analyze_prompt = PromptTemplate(
input_variables=["paper_info"],
template=(
"你是一名科研助理,下面是若干论文的检索结果:\n{paper_info}\n\n"
"请将这些论文整理为 JSON 数组,每个元素包含字段:"
'name, task, method, datasets, metrics, key_ideas, pros, cons。\n'
"只输出 JSON,不要额外解释。"
),
)
paper_analyze_chain = LLMChain(llm=llm, prompt=paper_analyze_prompt)
def paper_analyst_node(state: ResearchState) -> ResearchState:
# 简单地把 papers_raw 拼起来丢给总结链
paper_info = "\n\n".join(state["papers_raw"])
summary_json = paper_analyze_chain.run(paper_info=paper_info)
state["papers_struct"].append(summary_json)
state["logs"].append("[PaperAnalyst] summarized papers into structured JSON.")
return state# nodes.py (继续)
from langchain.prompts import PromptTemplate
experiment_prompt = PromptTemplate(
input_variables=["paper_summary_json"],
template=(
"下面是关于某个研究主题的论文结构化总结(JSON):\n{paper_summary_json}\n\n"
"我的资源:单卡 RTX 3090(24GB),两周时间,每天训练时间约 6 小时。\n"
"请设计一个可行的 baseline 实验方案,用 Markdown 输出,包含:\n"
"1. 推荐复现的论文(1~2 篇)\n"
"2. 数据集与预处理策略\n"
"3. 模型与关键超参数\n"
"4. 预估训练时间(粗略即可)\n"
"5. TODO checklist(用 [ ] 列出来)\n"
),
)
experiment_chain = LLMChain(llm=llm, prompt=experiment_prompt)
def experiment_planner_node(state: ResearchState) -> ResearchState:
# 简单起见,我们只拿最后一次的结构化结果
paper_summary_json = state["papers_struct"][-1] if state["papers_struct"] else ""
plan_md = experiment_chain.run(paper_summary_json=paper_summary_json)
state["plan"] = plan_md
state["logs"].append("[ExperimentPlanner] generated baseline experiment plan.")
return state科研场景经常需要你自己看一眼,这个在 LangGraph 里可以做一个「pause / checkpoint」式节点(这里先写一个简单的占位):
def human_review_node(state: ResearchState) -> ResearchState:
# 实战中可以把 state 持久化,等待前端/人工修改后再继续
state["logs"].append("[HumanReview] checkpoint reached, please review papers_struct.")
return state核心步骤:
StateGraph(ResearchState);paper_hunter / paper_analyst / human_review / experiment_planner;# graph_build.py
from langgraph.graph import StateGraph, START, END
from research_state import ResearchState
from nodes import (
paper_hunter_node,
paper_analyst_node,
experiment_planner_node,
human_review_node,
)
def build_research_graph():
builder = StateGraph(ResearchState)
# 注册节点
builder.add_node("paper_hunter", paper_hunter_node)
builder.add_node("paper_analyst", paper_analyst_node)
builder.add_node("human_review", human_review_node)
builder.add_node("experiment_planner", experiment_planner_node)
# 定义流程:START -> hunter -> analyst -> review -> planner -> END
builder.add_edge(START, "paper_hunter")
builder.add_edge("paper_hunter", "paper_analyst")
builder.add_edge("paper_analyst", "human_review")
builder.add_edge("human_review", "experiment_planner")
builder.add_edge("experiment_planner", END)
graph = builder.compile()
return graph调用:
# main.py
from graph_build import build_research_graph
def main():
graph = build_research_graph()
init_state = {
"topic": "CLIP for deepfake detection and multimodal forgery since 2022",
"papers_raw": [],
"papers_struct": [],
"plan": None,
"logs": [],
}
final_state = graph.invoke(init_state)
print("==== Logs ====")
for log in final_state["logs"]:
print(log)
print("\n==== Plan ====\n")
print(final_state["plan"])
if __name__ == "__main__":
main()这样你就有了一个完整的:
科研 Multi-Agent × LangGraph State Machine
CodeGenNode,负责生成训练脚本;human_review 变成条件分支(需要人工确认时才走这一节点)。你之前问过两个典型面经问题:
用 LangGraph + Multi-Agent 的话,你可以这样回答——
在 LangGraph 方案中:
papers_struct 存在时才会运行;如果要更严一点,可以:
papers_struct 必须是合法 JSON);paper_extract_node / paper_rank_node / title_node;一句话总结:
在 LangGraph 里,planning 粒度是通过“图上节点的拆分粒度”来控制的,而不是完全由 LLM 自由发挥。
给你几段可以直接背的回答。
Q1:你在项目里是怎么管理 Multi-Agent 的流程的?为什么选 LangGraph?
我这边是用 LangGraph 做底层 workflow, 把整个科研 Multi-Agent 抽象成一张 StateGraph:
每个 Agent(比如文献检索、论文分析、实验规划)都是一个 Graph Node; 所有中间结果都挂在一个共享 State 上,比如 topic、papers_raw、papers_struct、plan 等; 控制流由 Graph 显式定义,比如 START → PaperHunter → PaperAnalyst → ExperimentPlanner → END;
这样相比纯 prompt-based 的 ReAct,有几个优点:
控制流是可视化、可追踪的,很容易 debug; 可以插入人工审阅节点,实现「AI + Human in the loop」; 一旦要改流程,比如加一个 CodeGen 节点,只要改图结构,不用推倒所有 prompt。
Q2:LangGraph 和 LangChain 的 AgentExecutor 相比,有什么优势?
LangChain 的 AgentExecutor 更像是「单 Agent 带工具」, 流程主要靠 LLM 的 Thought/Action 循环,核心是一条“对话录”。
LangGraph 则更偏向「流程编排」:
提供 StateGraph,支持有向图、分支、循环、条件边; 状态是一个 TypedDict,显式管理,而不是塞到 prompt 里; 更适合复杂的 Multi-Agent 系统,比如科研流水线、商用流程自动化;
我这个科研项目里,两者是配合使用的:
某些 Node 内部仍然用 LangChain Agent(比如 PaperHunter 里用带工具的 Agent); LangGraph 则在更高一层做“节点级调度”。
Q3:如果后续要在这个科研 Multi-Agent 里加入“在线强化学习 / 评分器调整策略”,LangGraph 还能 hold 住吗?
可以的,LangGraph 的思想跟 RL 里的 policy graph、option framework 其实挺像:
每个 Node 类似一个 option 或 sub-policy; State 里可以加入 reward、history 等信息; 甚至可以在图上加一个「策略更新节点」,定期根据失败的轨迹更新 prompt 或 routing 策略。
真要搞 Agentic RL 的话,LangGraph 可以作为环境里的「流程引擎」, 上面再跑一层 RL 来调整路径或参数,这也正好和我在做的 Agentic RL 专题呼应。
这一篇,我们做了几件事:
ResearchState,让所有节点共享一个“科研黑板”;原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。