[LangChain / LangGraph / SpringAl / LangChain4J /¶
Llamalndex / AutoGen / CrewAI ÷FXJłk]¶
框架总览速查表¶

🔗 LangChain vs 🕸️ LangGraph:核心区别是什么?¶
▎一句话说清¶
LangChain 是“搭积木”的链式框架,LangGraph 是“画流程图”的有状态图框架。
前者解决“怎么把多个组件串起来”,后者解决“怎么让组件根据运行时状态循环、分支、暂停甚至回退”。
▎深度解析¶
-
执行模型不同
-
LangChain 的 LCEL(LangChain Expression Language)构建的是有向无环图(DAG)。链一旦 invoke,数据就从头流到尾,中间无法动态循环或根据中间结果回到上游。
-
LangGraph 基于状态图(State Graph),节点之间可以形成环,边上可以挂条件判断,甚至支持暂停等待人工确认后再恢复执行。这让 Agent 的“观察-思考-行动”循环天然可被建模。
-
状态管理能力
-
LangChain 的 Chain 本质是无状态的,每次调用独立,需要记忆时得额外接 Memory 组件。
-
LangGraph 把状态作为一等公民,每个节点都接收状态、返回状态更新,图自动管理状态的持久化和传递。这对于多步 Agent 或需要纠错的流程至关重要。
-
复杂度天花板
-
简单 RAG、单一工具调用、顺序流水线,LangChain 足够优雅。
-
多 Agent 协作、带人工审核的工作流、根据运行结果动态决定下一步(甚至重试),LangGraph 的图结构不会变成“意大利面条”,因为它本身就是可观察、可测试的图。
▎示例代码对比¶
LangChain:搭建一个简单的 poet chain(无循环)
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "你是一位诗人,用古诗风格回应。"),
("user", "{input}")
])
model = ChatOpenAI()
chain = prompt | model | StrOutputParser()
print(chain.invoke({"input": "春天"}))
这条链只能顺序执行:填 prompt → 调模型 → 解析输出,无法根据输出内容决定是否重新生成。
LangGraph:一个带自循环的简易 Agent(可重试直到满意)
from langgraph.graph import StateGraph, END
from typing import TypedDict
class AgentState(TypedDict):
query: str
answer: str
attempts: int
def generate_answer(state):
# 模拟调用LLM
state["answer"] = f"回答:{state['query']} (第{state['attempts']}次)"
state["attempts"] += 1
return state
def should_continue(state):
# 如果尝试次数<2且答案中不含“满意”,则循环回生成节点
if state["attempts"] < 2 and "满意" not in state["answer"]:
return "generate"
return END
graph = StateGraph(AgentState)
graph.add_node("generate", generate_answer)
graph.set_entry_point("generate")
graph.add_conditional_edges("generate", should_continue, {
"generate": "generate",
END: END
})
app = graph.compile()
print(app.invoke({"query": "今天天气", "attempts": 0}))
这里 should_continue 就是 LangGraph 的精髓——用代码控制工作流的循环与终止。LangChain 的 LCEL 做不到这一点。
🧠 面试这么说最加分 “LangChain 让我快速串联模型、检索、工具,适合单一方向的管道;而一旦需要 Agent 反复规划、自我修正,或设计多人协作流程,我就会选用 LangGraph,因为它的状态图原生支持循环和持久化,图结构本身就能当系统设计文档看。”
📦 LangChain vs 📑 LlamaIndex:定位有什么不同?¶
LangChain 是通用 LLM 应用框架,LlamaIndex 是数据索引与检索的“涡轮增压器”。
一个负责编排应用逻辑,一个负责把私有数据变成 LLM 能消化的高精度知识。
▎深度解析¶
-
解决的核心问题不同
-
LangChain 的初心:LLM 如何与外部世界交互? 于是有了 Agents、Tools、Chains、Memory,它像一个万能插座,把模型、数据库、API 连起来。
-
LlamaIndex 的初心:如何让 LLM 精准理解我的几百页文档? 于是它重兵投入数据连接器、索引结构(向量、树、关键词、图)、高级检索策略和查询引擎。
-
能力侧重点
-
LangChain 长于多步推理和工具调用:让模型自主决定先去查数据库,再调计算器,最后总结。
-
LlamaIndex 长于复杂数据场景下的深度检索:例如同时检索 PDF 和数据库,用递归检索+摘要的方式回答“总结上一季度各产品线的问题”,并在答案里附带上来源页码。
-
本质不是竞争关系
在生产项目中,两者经常组合使用:用 LlamaIndex 构建强悍的检索器(Retriever),把它封装成 LangChain 的一个 Tool,再交给 Agent 调度。这样数据引擎和业务编排各司其职。
▎示例代码对比¶
LlamaIndex:把文档变成可问答的知识库
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader("./policies").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("年假最多可以累积多少天?")
print(response)
它帮你自动搞定文档切分、向量化、检索、合成,但你很难直接让它在回答前先去调一个 HR 系统 API。
LangChain:用 Agent 调度检索工具
from langchain.agents import create_openai_functions_agent, AgentExecutor
# 可以把上面 LlamaIndex 的 query_engine 包装成一个 Tool
tools = [hr_policy_tool, payroll_api_tool] # 混合检索与API
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({"input": "我剩几天年假,请假会扣多少钱?"})
Agent 决定先去查政策文档,再根据查到的规则调薪酬 API 计算,这正是 LangChain 的编排能力。
🧠 面试这样说让面试官点头 “我会把 LangChain 看作应用的骨架和大脑,负责调度;LlamaIndex 则是消化系统,专门把原始数据转化成高纯度知识。单纯做企业知识库问答,直接上 LlamaIndex 更快;一旦需要多步、多工具的复杂业务,就必须引入 LangChain 来编排。”
📦 LangChain vs 📑 LlamaIndex:定位有什么不同?¶
LangChain 是通用 LLM 应用框架,LlamaIndex 是数据索引与检索的“涡轮增压器”。
一个负责编排应用逻辑,一个负责把私有数据变成 LLM 能消化的高精度知识。
▎深度解析¶
-
解决的核心问题不同
-
LangChain 的初心:LLM 如何与外部世界交互? 于是有了 Agents、Tools、Chains、Memory,它像一个万能插座,把模型、数据库、API 连起来。
-
LlamaIndex 的初心:如何让 LLM 精准理解我的几百页文档? 于是它重兵投入数据连接器、索引结构(向量、树、关键词、图)、高级检索策略和查询引擎。
-
能力侧重点
-
LangChain 长于多步推理和工具调用:让模型自主决定先去查数据库,再调计算器,最后总结。
-
LlamaIndex 长于复杂数据场景下的深度检索:例如同时检索 PDF 和数据库,用递归检索+摘要的方式回答“总结上一季度各产品线的问题”,并在答案里附带上来源页码。
-
本质不是竞争关系
在生产项目中,两者经常组合使用:用 LlamaIndex 构建强悍的检索器(Retriever),把它封装成 LangChain 的一个 Tool,再交给 Agent 调度。这样数据引擎和业务编排各司其职。
▎示例代码对比¶
LlamaIndex:把文档变成可问答的知识库
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader("./policies").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("年假最多可以累积多少天?")
print(response)
它帮你自动搞定文档切分、向量化、检索、合成,但你很难直接让它在回答前先去调一个 HR 系统 API。
LangChain:用 Agent 调度检索工具
from langchain.agents import create_openai_functions_agent, AgentExecutor
# 可以把上面 LlamaIndex 的 query_engine 包装成一个 Tool
tools = [hr_policy_tool, payroll_api_tool] # 混合检索与API
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({"input": "我剩几天年假,请假会扣多少钱?"})
Agent 决定先去查政策文档,再根据查到的规则调薪酬 API 计算,这正是 LangChain 的编排能力。
🧠 面试这样说让面试官点头 “我会把 LangChain 看作应用的骨架和大脑,负责调度;LlamaIndex 则是消化系统,专门把原始数据转化成高纯度知识。单纯做企业知识库问答,直接上 LlamaIndex 更快;一旦需要多步、多工具的复杂业务,就必须引入 LangChain 来编排。”
🌱 Spring AI vs ⚡ LangChain4j:定位差异及 Java 项目选型¶
Spring AI 是 Spring 生态的原生 AI 模块,LangChain4j 是 LangChain 理念的 Java 独立实现。
一个让你像用 JdbcTemplate 一样用 AI,一个让你在 Java 里“写 Python 版 LangChain 的代码风格”。
▎深度解析¶
-
设计哲学
-
Spring AI:遵循 Spring 的可移植服务抽象,定义
ChatClient、EmbeddingClient、VectorStore等接口,通过 starter 自动配置切换 Azure OpenAI、通义千问等后端。你改一行配置就能换模型,代码零改动。 -
LangChain4j:忠于 LangChain 的组件化链式组装思想,提供
ChatLanguageModel、EmbeddingModel、Tool、Chain等,API 风格接近 Python 版本,内置更丰富的模型集成(如直接对接各种大厂 API),同时对非 Spring 环境友好。 -
集成深度与约束
-
如果你的项目是典型的 Spring Boot 应用,Spring AI 能天然与
@Service、@RestController、配置中心、Micrometer 监控等融合,你甚至可以使用@Retryable注解处理限流。 -
LangChain4j 有 Spring Boot Starter,但它的核心库不依赖 Spring。它提供了诸如
InMemoryEmbeddingStore、AzureOpenAiChatModel等自家实现,需要你手动配置 Bean,反而给了你更多细颗粒度的控制权。 -
功能广度和社区
-
LangChain4j 在 AI 组件方面跑得更快,比如较早支持了 OpenAI 的函数调用、嵌入模型本地缓存、对话记忆等多种开箱即用实现。
-
Spring AI 背靠 Spring 团队,稳定性和生态整合是其王牌,但在小众模型的集成上可能慢半拍。
▎Java 项目如何选型?(决策路径)¶
| 考量维度 | 选 Spring AI | 选 LangChain4j |
|---|---|---|
| 项目技术栈 | 基于 Spring Boot 3.x +,大量使用 Spring 生态 | 非 Spring 项目,或需要极简依赖 |
| AI 集成广度 | 主流模型(Azure, OpenAI, 通义等)够用 | 需要 Hugging Face, Ollama, 各种国内模型原生支持 |
| 团队技能 | 熟悉 Spring 模板方法模式,追求最小学习成本 | 有 Python LangChain 经验,希望移植概念到 Java |
| 可移植性需求 | 强烈需求:一键切换模型提供商,代码不变 | 需要更细粒度的控制,不怕改配置文件或代码 |
| 长期维护 | 依赖 Spring 官方生命周期,稳定但更新偏保守 | 社区活跃,更新快,紧跟前沿 |
示例代码感受一下风格差异
Spring AI:极简配置 + 依赖注入
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String input) {
return chatClient.prompt().user(input).call().content();
}
}
完全 Spring 风格,感觉就像在使用 RestTemplate。
LangChain4j:显式构建,链式组合
OpenAiChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_KEY"))
.modelName("gpt-4o")
.build();
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.tools(new Calculator())
.build();
String answer = assistant.chat("计算 123*456 是多少?");
这里定义了一个带工具接口的 AI 服务,代码主见度很高,也更接近 LangChain 的“组装”概念。
实战建议:
-
新项目且是 Spring Boot,直接用 Spring AI 起步,快速将 AI 能力嵌入已有服务体系。
-
如果发现所需模型或高级 RAG 策略 Spring AI 暂不支持,可以引入 LangChain4j 作为补充,两者并不冲突(例如在同一个 Service 里分别调用两个框架的向量存储)。
-
非 Spring 或追求极致控制的项目,LangChain4j 是更轻量的选择。
🧠 面试加分表达 “我的选型逻辑是‘尊重技术栈,预留扩展点’。Spring AI 让 AI 能力在 Spring 体系内变得和数据库访问一样简单,而 LangChain4j 在模型和检索策略的丰富度上更胜一筹。我会根据项目对整合深度和前沿功能的需求来做决定,也会考虑二者共存的方案。”
🤝 AutoGen 和 CrewAI 的多 Agent 协作模式有什么区别?¶
这两个框架都旨在让多个 AI Agent 协作完成任务,但它们的协作哲学截然不同。用一句话概括:AutoGen 是“对话驱动的协作”,而 CrewAI 是“角色驱动的流水线”。

核心区别一览:
| 维度 | AutoGen | CrewAI |
|---|---|---|
| 协作机制 | 多 Agent 对话(Two-Agent Chat / Group Chat) | 任务分配与执行(Sequential / Hierarchical) |
| 控制流 | 对话流:Manager 动态选择下一个发言人 | 任务流:Task 的 context 参数定义依赖 |
| 角色定义 | 通过 system_message 描述角色 | 通过 role、goal、backstory 定义丰富人设 |
| 工具绑定 | 代码执行器(UserProxyAgent)是内置一等公民 | 工具通过 LangChain BaseTool 绑定给 Agent |
| 适用场景 | 需要“来回讨论”的开放式任务,特别是涉及代码生成/执行 | 结构清晰、步骤明确、角色分工固定的工作流 |
| 学习曲线 | 中等,需理解对话驱动机制和 initiate_chat | 较低,角色和任务的概念直观,上手快 |
示例代码对比:让两个框架完成同一个任务——“分析 sales.csv 并生成报告”
AutoGen 方式(对话驱动):
from autogen import AssistantAgent, UserProxyAgent
assistant = AssistantAgent("分析师", llm_config=...,
system_message="你是数据分析师,生成 Python 代码分析 sales.csv。")
user_proxy = UserProxyAgent("执行者", code_execution_config={"work_dir":"."})
user_proxy.initiate_chat(assistant, message="分析 sales.csv 并生成报告")
# 接下来它们自动对话:助手写代码 → 执行者运行 → 助手看结果修正 → 最终输出报告
CrewAI 方式(任务驱动):
from crewai import Agent, Task, Crew, Process
analyst = Agent(role="分析师", goal="分析销售数据", backstory="...", tools=[code_tool])
reporter = Agent(role="报告员", goal="生成分析报告", backstory="...")
task1 = Task(description="分析 sales.csv", expected_output="数据洞察列表", agent=analyst)
task2 = Task(description="根据洞察生成 Markdown 报告", expected_output="报告全文",
agent=reporter, context=[task1])
crew = Crew(agents=[analyst, reporter], tasks=[task1, task2], process=Process.sequential)
result = crew.kickoff()
选型直觉:
如果你的任务需要 Agent 之间“来回讨论、试错、根据结果调整”,例如写代码 → 运行 → 看报错 → 改代码,选 AutoGen,它的对话机制天然支持这种闭环。
如果你的任务可以画成一张清晰的甘特图,每一步的输入输出都明确,选 CrewAI,它的任务依赖模型更易于管理和监控。
🛠️你们项目用的是什么 AI 框架?为什么选它?有什么踩坑经验?¶
(这是一个典型的面试反问,你需要展示出 “在约束下做决策” 的工程思维。我以一个通用的“企业内部知识库问答”项目为例来说明,你可以替换成自己的真实经验。)
项目背景:为某金融机构构建内部合规知识库问答系统,需要对接已有的微服务、严格的安全审计、以及对延迟和吞吐的高要求。
我们选了 Spring AI + LangGraph 的组合。
为什么选 Spring AI?
-
团队技术栈:团队全员 Java/Spring 背景,Spring AI 让我们无需学习 Python 生态就能快速构建 AI 应用。引入成本极低。
-
生态集成:需要无缝对接已有的 Spring Cloud(服务发现、配置中心)、Spring Security(OAuth2 鉴权)、Micrometer(指标监控)。Spring AI 把这些能力天然带入了 AI 调用链,而不是另起炉灶。
-
生产级特性:Advisor 机制让我们轻松实现 RAG、限流、审计日志等横切关注点,所有 AI 调用都是 Spring 管理的 Bean,行为可预测、可观测。
为什么还需要 LangGraph?
-
Spring AI 解决的是“怎么调模型”,但我们的合规问答涉及多轮检索、结果校验、人工审批等复杂流程,不是一个简单的“查→答”链路。
-
我们用 LangGraph 把这些流程建模为状态图:检索 → 校验 → 生成 → 人工复核 → 终稿。每一步的状态都可以被 Checkpoint 持久化,支持中断和恢复(比如复核人过了一天才批准)。
-
LangGraph 的
StateGraph让我们能够精确控制“校验不通过就回到检索”这种循环逻辑,而不仅仅是线性执行。
踩坑经验:三个真实教训
-
RAG 检索结果的“看似相关实则无用”问题 直接用向量相似度检索回来的文档片段,经常不是回答问题真正需要的那段。例如用户问“合同违约金的法定上限是多少”,检索回来的是“违约金条款的定义”。我们的解决方式是:先让 LLM 改写用户问题为检索关键词(Query Rewriting),再检索;检索后又用一个轻量级 LLM 做相关性过滤,把不相关的片段直接在注入 Prompt 前剔除。这大幅提升了答案的准确率。
-
大模型延迟不可预测,需要完善的超时与降级 生产环境中 GPT-4 的响应时间从 2 秒到 30 秒不等。我们在 Spring AI 的
RestTemplate层面设置了 connect timeout 和 read timeout,并用 Resilience4j 的 断路器 和 舱壁模式 隔离调用。当 OpenAI 不可用时,断路器打开,自动降级到本地的 Qwen 模型(通过 Ollama 部署)。降级后的回答质量虽有下降,但系统依然可用,而不是直接报错。 -
上下文窗口管理的“沉默成本” 多轮对话中,我们把对话历史全量塞进 Prompt,很快 token 消耗飞涨且模型开始“遗忘”早期指令。后来实现了 滑动窗口 + 摘要混合记忆:最近 3 轮保留原文,更早的历史由一个小模型生成 50 字的摘要。这在 Spring AI 中通过自定义
ConversationMemoryAdvisor实现,token 成本降低了 60%,同时保持了对话连贯性。
一句话总结:选型不是选“最好的”框架,而是选“在当前团队技能、已有基础设施、任务复杂性三个约束下的最优解”。 我们选 Spring AI 是为了低摩擦上生产,选 LangGraph 是为了管好复杂流程,两者互补,缺一不可。
🔌 Semantic Kernel 是什么?和 LangChain 有什么区别?¶
Semantic Kernel (SK) 是微软推出的企业级 AI 编排框架,它的核心设计思想是“用你现有的代码来扩展 AI,而不是把 AI 作为中心”。
LangChain 哲学:AI 是大脑,一切围绕 AI 编排
AI 调用 → 工具调用 → 链/图/Agent → 输出
Semantic Kernel 哲学:你的代码是骨架,AI 只是其中一个可调用的 Plugin
你的业务逻辑 → 需要时调用 AI Plugin → AI 返回结果 → 继续你的逻辑
核心概念对比:
| 维度 | LangChain | Semantic Kernel |
|---|---|---|
| 语言 | Python / JavaScript | C# / Python / Java |
| 核心抽象 | Chain, Agent, Tool, Memory | Kernel, Plugin, Function, Planner |
| 扩展方式 | 通过 LangChain 的 Tool 接口 | 通过 [KernelFunction] 特性,将任意方法变为 Plugin |
| 编排模型 | 声明式管道 (LCEL)、图 (LangGraph) | Planner 自动编排、Handlebars 模板 |
| 目标用户 | AI 工程师、研究者 | .NET 企业开发者 |
| 生态绑定 | 独立,模型无关 | 深度集成 Azure / Microsoft 365 / Copilot Stack |
| 代表场景 | 通用 LLM 应用、RAG、Agent | 企业应用智能化改造、Copilot 类产品 |
一个直观的例子:用 SK 创建一个“摘要 Plugin”
public class TextPlugin
{
[KernelFunction, Description("将长文本总结为三句话")]
public async Task<string> SummarizeAsync(
[Description("要摘要的文本")] string text,
Kernel kernel)
{
var prompt = $"请用三句话总结以下内容:\n{text}";
return await kernel.InvokePromptAsync<string>(prompt);
}
}
// 使用
var builder = Kernel.CreateBuilder();
builder.Plugins.AddFromType<TextPlugin>();
var kernel = builder.Build();
var summary = await kernel.InvokeAsync("TextPlugin", "Summarize",
new() { ["text"] = longArticle });
关键区别:谁占主导地位
LangChain 的设计中,你的应用程序逻辑围绕 LLM 调用构建——你写一个 Chain,把 Prompt、LLM、Parser 串起来,工具只是这个 Chain 中的一个环节。而在 Semantic Kernel 中,你的应用程序逻辑是你原来的 C#/Java 代码,LLM 只是通过 Plugin 被调用的一个能力。这种“AI 作为依赖注入的一部分”的范式,让企业开发者感觉 LLM 像是一个新引入的“服务”,而不是重构整个应用的“中心”。
为什么选 Semantic Kernel?
-
你的团队是 .NET 技术栈,不想为 AI 引入 Python 微服务。
-
你需要深度集成 Microsoft 生态(Azure AI Search, Microsoft 365, Copilot)。
-
你的应用已经有成熟的业务逻辑,只需要在关键节点嵌入 AI 能力(如自动分类、生成摘要、翻译),而不是从零构建 AI 原生的系统。
为什么选 LangChain?
-
你需要构建复杂的 AI 原生工作流(多步推理、Agent 自主决策、复杂的 RAG 管道)。
-
你需要在模型、向量库、工具之间灵活切换,且希望社区有最丰富的集成库。
-
你的团队熟悉 Python 生态,且愿意用前沿框架。
两者可以共存: 在一些大型企业中,后端微服务用 Semantic Kernel 做轻量级 AI 嵌入(如邮件自动分类),而数据科学团队用 LangChain 构建复杂的分析 Agent,通过 REST/gRPC 互相调用。这不矛盾,只是在不同场景下选择了最顺手的工具。
📋团队新项目要做 RAG 问答系统,你会选哪个方案,理由是什么?¶
做这个决策之前,我会先看三个硬约束:
-
团队的技术栈(Java 还是 Python?前端同学能碰后端吗?)
-
项目的基础设施(已有 Spring Cloud 微服务?还是独立部署?)
-
对稳定性、权限、审计的要求(金融/医疗等强监管行业?)
基于这三条,我把市面上的主流方案拉出来,做一个快速的权衡分析。
方案一:Spring AI + LangGraph (Java 生态)
适合:团队以 Java/Spring 为主,已有微服务基础设施,对安全、审计、事务等有高要求。
优势:与 Spring Security/Cloud/Micrometer 无缝集成,Advisor 机制天然适合做 RAG 切面。
LangGraph 负责复杂流程(检索→校验→重试→审批),Spring AI 负责 AI 调用。
劣势:相比 Python 生态,前沿特性(如最新的 Multi-Vector Retriever)落地稍慢。
方案二:LangChain + LangGraph (Python 生态)
适合:团队 Python 能力强,希望快速迭代,愿意引入 Python 微服务。
优势:社区最活跃,组件最丰富,调试工具(LangSmith)成熟,大量现成模板可复用。
劣势:需要额外维护 Python 服务,与企业 Java 后端集成时要处理跨语言调用。
方案三:纯手工 + 向量库 SDK (如 Pinecone/Milvus)
适合:需求极其简单,只有一个“查文档→回答”的直线链路,且团队不想引入框架。
优势:无框架黑盒,完全掌控,包体小。
劣势:当需求演进到多路召回、混合检索、上下文压缩时,你会发现自己在重写半个 LangChain。
方案四:LlamaIndex (Python)
适合:项目以数据为中心,文档解析、索引结构是核心,RAG 链路重。
优势:对索引结构(树索引、关键词索引、知识图谱索引)的支持很强。
劣势:与 LangChain 功能重叠度高,社区略小,Agent 能力不如 LangChain。
我的推荐:对于大多数企业级 Java 项目,我会首选 Spring AI + LangGraph。
原因很直接:当公司已有的微服务体系、鉴权、配置中心都是 Spring 全家桶时,引入 Spring AI 的成本几乎为零——它就是一个 Starter,ChatModel、VectorStore 都是 Spring Bean,你可以用 @Service、@Transactional、@Cacheable 等所有你熟悉的注解去构建 AI 逻辑。而 LangGraph 弥补了 Spring AI 在“复杂流程编排”上的短板,比如“检索结果太差→自动改写查询→二次检索→仍为空→转人工”这种带循环和分支的逻辑,用 LangGraph 的 StateGraph 表达比写一堆 if-else 清晰得多。
// 用 Spring AI 的 Advisor 实现 RAG 切面
@Component
public class RagAdvisor implements RequestResponseAdvisor {
private final VectorStore vectorStore;
@Override
public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {
String query = request.userText();
List<Document> docs = vectorStore.similaritySearch(SearchRequest.query(query).withTopK(4));
String ctx = docs.stream().map(Document::getContent).collect(Collectors.joining("\n\n"));
return AdvisedRequest.from(request)
.withSystemText("基于以下参考资料回答:\n" + ctx + "\n\n" + request.systemText())
.build();
}
// ...
}
🔍如何评估和选择适合自己项目的 LLM 模型?¶
选模型不是跑个分就完事了。我的做法是建立一个评估矩阵,从五个维度量化候选模型,然后根据业务权重打分。
五个评估维度:
| 维度 | 指标 | 评测方式 |
|---|---|---|
| 能力契合度 | 在真实业务场景下的准确率、召回率 | 构建业务专属测试集,人工+自动双重打分 |
| 推理性能 | TTFT(首 token 延迟)、TPS(每秒生成 token)、端到端耗时 | 用 Locust/k6 压测,记录 P50/P95/P99 |
| 成本 | 每 1M token 价格、预估月消费 | 基于预估调用量计算,考虑 Input/Output 差异 |
| 上下文长度 | 最大上下文窗口,长文本下的检索准确率 | Needle-in-a-Haystack 测试 |
| 合规与安全 | 数据是否出境、模型是否私有化部署、内容安全能力 | 法务+安全团队评估 |
评估流程(我的实际做法):
1. 候选池筛选
根据合规要求(如数据不出境)和预算,从 GPT-4o、Claude 3.5 Sonnet、DeepSeek-V3、
Qwen-Max、Llama 3.1 等中圈定 3~5 个候选。
2. 业务测试集构建
从真实用户日志中抽取 200 条代表性 query,覆盖高频、长尾、多语言、多轮等场景。
标注标准答案和关键信息点。
3. 离线评测
编写自动化脚本,用每个候选模型回答这 200 条 query,收集结果。
自动指标:ROUGE-L、BERTScore、答案长度、响应时间。
人工打分:双盲评审,从准确性、完整性、流畅性三个角度 1-5 分。
4. 综合评分
根据业务侧重赋权。比如客服场景“安全性”权重高,创意写作场景“多样性”权重高。
最终选综合得分最高的模型,同时保留第二名作为降级备选。
示例代码:调用多个模型并记录结果
import time, json
from openai import OpenAI
models = ["gpt-4o", "claude-3-5-sonnet", "deepseek-chat"]
test_queries = [{"q": "合同违约金上限是多少?", "ref": "违约金不超过损失的30%"}]
results = []
for model_name in models:
client = OpenAI(base_url=get_base_url(model_name), api_key=get_key(model_name))
for item in test_queries:
start = time.time()
resp = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": item["q"]}],
temperature=0
)
elapsed = time.time() - start
results.append({
"model": model_name,
"query": item["q"],
"answer": resp.choices[0].message.content,
"latency": elapsed,
"tokens": resp.usage.total_tokens
})
# 后续可用另一个 LLM 或人工对 results 打分
选择决策树:
需要私有化部署? → 否 → 成本敏感? → 是 → 国产大模型 (DeepSeek/Qwen)
→ 否 → 需要强推理? → 是 → GPT-4o / Claude 3.5
→ 否 → GPT-4o-mini / Claude Haiku
需要私有化部署? → 是 → GPU 资源充足? → 是 → Llama 3.1 70B (微调)
→ 否 → Qwen2.5 7B / DeepSeek-V2-Lite
模型选择不是一次性的,上线后我会持续收集用户反馈(点赞/点踩),每个月复盘一次模型效果,必要时切换或引入 A/B 测试。模型是易耗品,选择框架才是长期投资。
⚡如何在 SpringAI 中实现流式输出(Streaming)?¶
流式输出的本质是服务端不等待模型生成完整结果,而是通过长连接逐 token 推送。在 Spring AI 中,ChatModel 接口提供了 Flux<ChatResponse> stream(Prompt prompt) 方法,底层基于 Project Reactor,天然非阻塞。
实现步骤:
第一步:确认模型支持流式
Spring AI 的 OpenAI Starter 默认支持流式,不需要额外配置。只需确保 ChatModel Bean 正常注入即可。
第二步:编写 Controller
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String q) {
// 构建 Prompt
Prompt prompt = new Prompt(new UserMessage(q));
// 调用流式方法,返回 Flux<String>
return chatClient.prompt()
.user(q)
.stream()
.chatResponse() // Flux<ChatResponse>
.map(resp -> resp.getResult().getOutput().getContent());
}
}
返回的 Flux<String> 会被 Spring WebFlux 自动序列化为 SSE 事件流。前端收到的原始数据格式为:
第三步:前端消费(Vue 3 示例)
const eventSource = new EventSource('/api/chat/stream?q=今天天气怎么样');
eventSource.onmessage = (event) => {
// event.data 就是后端每次推送的文本片段
document.getElementById('output').innerText += event.data;
};
eventSource.onerror = () => {
eventSource.close();
};
第四步:处理流式输出中的 Function Calling 和错误
当模型在流式模式下决定调用工具时,Spring AI 会先返回一个包含 tool_calls 的 ChatResponse,你需要中断流,执行工具,然后把结果追加到历史中,重新发起流式调用。这通常需要在前端实现“等待-执行-再连”的逻辑。
// 如果需要处理流式中的 tool calls,通常用更底层的 ChatModel.stream()
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
return flux.flatMap(response -> {
if (response.hasToolCalls()) {
// 执行工具,用结果构建新消息,重新 stream
return executeToolsAndRestream(response);
}
return Flux.just(response.getResult().getOutput().getContent());
});
第五步:性能与容错
-
超时:在
application.yml中设置spring.ai.openai.chat.options.timeout或使用Flux.timeout(Duration.ofSeconds(30))。 -
背压:Reactor 默认处理背压,无需额外配置。
-
取消:前端
EventSource.close()会断开连接,后端Flux自动取消,底层 HTTP 连接释放。
配置文件示例:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o
temperature: 0.7
stream: true # 可选,显式开启流式
收束:
Spring AI 的流式输出把 Reactor 的响应式能力搬到了 AI 领域。你不用自己管理 SSE 协议、不用处理异步线程的切换,只需要定义好 Flux 管道,Spring 会帮你把 token 流稳稳地送到前端。这对于提升用户体验、降低首字延迟至关重要——在真实的 RAG 应用中,流式输出不是“加分项”,而是“必须项”。