side-by-side code

同一个任务,
三种代码形状

Feature matrix 永远比不过看代码本身。这里列出几个常见任务,每个用 2-3 个主流框架实现一次 —— 看哪种心智模型跟你当前问题最匹配。

选择任务

需求

给定一个话题,先用 web search 搜索相关资料,LLM 总结成要点,最后输出结构化报告(标题 / 摘要 / 发现列表 / 参考来源)。

49 行

显式图结构,每个步骤是一个节点,状态在节点间流转。适合后续要加 HITL 或错误恢复。

python
from typing import TypedDict
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
 
class Report(BaseModel):
    title: str
    summary: str
    findings: list[str]
    sources: list[str]
 
class State(TypedDict):
    query: str
    search_results: list
    summary: str
    report: Report | None
 
llm = ChatOpenAI(model="gpt-4o")
 
def search_node(s: State) -> State:
    s["search_results"] = web_search(s["query"])
    return s
 
def summarize_node(s: State) -> State:
    content = "\n".join(r["snippet"] for r in s["search_results"])
    s["summary"] = llm.invoke(f"总结: {content}").content
    return s
 
def structure_node(s: State) -> State:
    s["report"] = llm.with_structured_output(Report).invoke(
        f"转成结构化报告: {s['summary']}"
    )
    return s
 
graph = StateGraph(State)
graph.add_node("search", search_node)
graph.add_node("summarize", summarize_node)
graph.add_node("structure", structure_node)
graph.set_entry_point("search")
graph.add_edge("search", "summarize")
graph.add_edge("summarize", "structure")
graph.add_edge("structure", END)
 
app = graph.compile()
result = app.invoke({
    "query": "harness engineering trends 2026",
    "search_results": [], "summary": "", "report": None,
})
print(result["report"].title)

47 行

两个角色(researcher / analyst)接力工作。backstory 塑造行为,sequential process 按顺序执行。

python
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool
from pydantic import BaseModel
 
class Report(BaseModel):
    title: str
    summary: str
    findings: list[str]
    sources: list[str]
 
researcher = Agent(
    role="Senior Research Analyst",
    goal="挖掘 {topic} 领域最新、最可靠的信息",
    backstory="你在技术分析领域有 10 年经验。",
    tools=[SerperDevTool()],
    verbose=True,
)
 
analyst = Agent(
    role="Report Writer",
    goal="把研究发现整理成结构化报告",
    backstory="你写过 Stratechery 风格的深度分析。",
)
 
research_task = Task(
    description="调研 {topic},至少找 5 个权威来源",
    agent=researcher,
    expected_output="研究发现列表,每个带来源链接",
)
 
report_task = Task(
    description="基于研究,输出结构化 Report",
    agent=analyst,
    context=[research_task],
    output_pydantic=Report,
    expected_output="完整的 Report 对象",
)
 
crew = Crew(
    agents=[researcher, analyst],
    tasks=[research_task, report_task],
    process=Process.sequential,
)
 
result = crew.kickoff(inputs={"topic": "harness engineering"})
report: Report = result.pydantic
print(report.title)

一个 agent,让它自己写 Python 代码调工具。code-agent 范式,模型一次性规划多步。

python
from smolagents import CodeAgent, HfApiModel, tool
from pydantic import BaseModel
 
class Report(BaseModel):
    title: str
    summary: str
    findings: list[str]
    sources: list[str]
 
@tool
def web_search(query: str) -> list[dict]:
    """
    搜索网络,返回结果列表。
 
    Args:
        query: 搜索关键词
    """
    return search_api(query)
 
@tool
def final_report(
    title: str, summary: str,
    findings: list[str], sources: list[str],
) -> dict:
    """
    产出结构化 Report。
 
    Args:
        title: 报告标题
        summary: 摘要
        findings: 关键发现列表
        sources: 来源 URL 列表
    """
    return Report(
        title=title, summary=summary,
        findings=findings, sources=sources,
    ).model_dump()
 
agent = CodeAgent(
    tools=[web_search, final_report],
    model=HfApiModel("Qwen/Qwen2.5-72B-Instruct"),
    additional_authorized_imports=["json"],
)
 
# agent 自己生成 Python 代码调工具、做推理
result = agent.run(
    "调研 harness engineering 2026 最新趋势,"
    "产出含 title、summary、findings(5个)、sources 的 Report。"
)
print(result)

takeaway

LangGraph 显式、冗长、可控;CrewAI 角色化、易读、隐式编排;smolagents 一段代码 + code-agent,模型自己规划。三个方案都对,选哪个看你下一步要加什么:要加 HITL 和恢复用 LangGraph,要加更多角色用 CrewAI,要跑开源模型或做教学用 smolagents。