各位同仁,下午好。
今天,我们将深入探讨一个在人工智能领域极具挑战性且日益重要的问题:如何处理“开集问题(Open-set Problems)”。我们将对比两种截然不同的范式——传统的专家系统(Expert Systems)和基于大型语言模型(LLM)的LangGraph框架,来揭示它们在应对这类问题时的本质差异。作为一名编程专家,我的目标是不仅阐明技术细节,更要帮助大家理解这些差异背后的哲学思考和工程取舍。
开集问题:定义与挑战
首先,我们必须清晰地定义什么是“开集问题”。在一个理想的“闭集(Closed-set)”环境中,我们假设所有可能遇到的输入、事件、状态和类别都是已知的、有限的,并且在系统设计时已被明确枚举和建模。例如,一个识别数字0-9的图像分类器,其分类空间是固定的10个数字。当它遇到一个字母A时,它会尝试将其归类到这10个数字中的一个,或者报告为“未知”,但它从未被训练去理解“A”是一个独立的、新的类别。
“开集问题”则恰恰相反。它指的是系统在部署和运行过程中,可能会遇到训练数据中从未出现过、在设计时也未被明确考虑过的输入、概念或情境。这些未知因素可能包括:
- 新颖的查询或请求: 用户提出的问题超出了系统知识库的范围。
- 未知的实体或概念: 在文本中提及的品牌、人物、技术或事件,系统从未见过。
- 意外的输入格式或数据结构: 用户输入的数据不符合预设模式。
- 不确定的语义或语境: 语言的歧义性导致系统无法明确理解意图。
- 动态变化的世界知识: 外部世界的知识不断更新,系统原有知识可能过时或不完整。
处理开集问题的核心挑战在于:系统不仅需要识别已知事物,更需要以某种方式应对未知。它不能简单地崩溃或给出无意义的答案,而应该能够:
- 识别未知: 知道自己不知道。
- 适应未知: 尝试理解、处理或获取关于未知的信息。
- 泛化未知: 在一定程度上,将已知知识泛化到新的情境。
- 优雅地失败: 当无法处理时,能够以用户友好的方式进行反馈,而不是崩溃。
现在,让我们从传统的视角开始,审视专家系统如何应对这一挑战。
传统专家系统:基于显式知识的封闭世界
专家系统是人工智能早期成功的典范,它旨在通过模拟人类专家的推理过程来解决特定领域的复杂问题。其核心思想是将领域专家的知识显式地编码成规则和事实,然后通过推理引擎进行逻辑推导。
专家系统的核心架构
一个典型的专家系统通常包含以下几个关键组件:
- 知识库 (Knowledge Base): 存储领域专家的知识,通常以规则(IF-THEN语句)和事实的形式存在。
- 事实 (Facts): 关于特定对象、事件或状态的基本断言。
- 规则 (Rules): 定义了在特定条件下如何从已知事实推导出新事实或采取行动的逻辑。
- 推理引擎 (Inference Engine): 负责根据知识库中的规则和当前已知事实进行推理。
- 前向链 (Forward Chaining): 从已知事实出发,应用规则推导出所有可能的新事实,直到达到目标或无法再推导。
- 后向链 (Backward Chaining): 从一个假设的目标出发,寻找能够证明该目标的规则,并递归地寻找证明这些规则前提的规则或事实。
- 工作内存 (Working Memory / Fact Base): 存储当前会话中的动态事实和用户输入,是推理引擎操作的对象。
- 解释模块 (Explanation Module): 能够向用户解释系统是如何得出结论的,提供了透明度。
- 知识获取模块 (Knowledge Acquisition Module): 辅助领域专家将知识输入到知识库中。
专家系统如何处理知识
专家系统的知识是显式的、符号化的、人工编码的。这意味着所有系统能够理解和操作的概念、关系和逻辑都必须由人类专家明确地定义和输入。
例如,在一个简单的医疗诊断专家系统中,知识库可能包含以下规则:
规则1: IF 病人有发烧 AND 病人有咳嗽 THEN 病人可能患有感冒
规则2: IF 病人有发烧 AND 病人有皮疹 THEN 病人可能患有麻疹
规则3: IF 病人可能患有感冒 AND 病人有流鼻涕 THEN 建议服用感冒药
专家系统在开集问题中的局限性
正是这种基于显式知识和符号逻辑的特性,决定了专家系统在处理开集问题时的根本性局限:
- 知识获取瓶颈 (Knowledge Acquisition Bottleneck): 专家系统构建的最大挑战在于将专家知识转换为形式化的规则。这是一个耗时、昂力、易错的过程。对于一个开放且不断变化的领域,手动编码所有可能的规则几乎是不可能的。
- 脆性 (Brittleness): 专家系统是“脆性”的。它们在预定义的问题域内表现出色,但一旦遇到知识库中没有显式规则覆盖的新情况、新概念或新组合,它们就会完全失效。它们缺乏泛化能力,无法从现有规则中推断出适用于新情况的解决方案。
- 例如,如果上述医疗系统中遇到一个病人有“低烧”和“肌肉酸痛”,但没有明确的规则来处理“低烧”或“肌肉酸痛”的组合,系统将无法给出任何诊断,因为它没有被告知如何将这些症状映射到已知的疾病。它不会去“猜测”或“联想”。
- 封闭世界假设 (Closed-World Assumption): 专家系统本质上是在“封闭世界”假设下运作的。它假设知识库包含了所有相关的事实和规则,任何未被明确声明为真的东西都被认为是假的。当遇到知识库之外的事物时,系统无法认识到那是一个“未知”,而是将其视为“不存在”或“不匹配”,从而导致失败。
- 缺乏常识和世界知识: 专家系统只拥有其知识库中显式编码的特定领域知识。它们不具备人类所拥有的广泛的常识推理能力,也无法理解语言的细微差别、隐喻或语境。
代码示例:一个简单的专家系统及其在开集问题上的局限
让我们用Python实现一个非常简化的诊断专家系统,并观察它如何处理开集问题。
import collections
class ExpertSystem:
def __init__(self):
self.facts = set() # 存储当前已知的事实
self.rules = [] # 存储规则
self.conclusions = set() # 存储推导出的结论
self.explanation_log = [] # 解释推理过程
def add_fact(self, fact):
"""添加已知事实."""
if fact not in self.facts:
self.facts.add(fact)
print(f"Adding fact: {fact}")
self.explanation_log.append(f"Fact added: {fact}")
def add_rule(self, antecedents, consequent, rule_name="Unnamed Rule"):
"""添加规则。antecedents是前提条件的列表,consequent是结论。"""
self.rules.append({
"name": rule_name,
"antecedents": set(antecedents),
"consequent": consequent
})
print(f"Rule added: {rule_name} (IF {', '.join(antecedents)} THEN {consequent})")
def infer(self):
"""执行前向链推理."""
new_facts_inferred = True
iteration = 0
while new_facts_inferred:
new_facts_inferred = False
iteration += 1
print(f"n--- Inference Iteration {iteration} ---")
for rule in self.rules:
# 检查规则的所有前提是否都已在当前事实中
if rule["antecedents"].issubset(self.facts):
consequent = rule["consequent"]
if consequent not in self.facts and consequent not in self.conclusions:
# 推导出新结论
self.conclusions.add(consequent)
self.facts.add(consequent) # 新结论也成为事实,供后续规则使用
new_facts_inferred = True
print(f"Rule '{rule['name']}' fired. Inferred: {consequent}")
self.explanation_log.append(f"Rule '{rule['name']}' fired: {', '.join(rule['antecedents'])} -> {consequent}")
else:
# print(f"Rule '{rule['name']}' not fired. Missing antecedents: {rule['antecedents'] - self.facts}")
pass # 规则未满足,不触发
print("n--- Inference Complete ---")
print(f"Final Conclusions: {self.conclusions}")
def get_explanation(self):
"""获取推理过程的解释."""
return "n".join(self.explanation_log)
# --- 构建一个简单的医疗诊断专家系统 ---
medical_es = ExpertSystem()
# 添加规则 (针对已知疾病)
medical_es.add_rule(["有发烧", "有咳嗽"], "可能患有感冒", "Rule_Cold_1")
medical_es.add_rule(["有发烧", "有皮疹"], "可能患有麻疹", "Rule_Measles_1")
medical_es.add_rule(["可能患有感冒", "有流鼻涕"], "建议服用感冒药", "Rule_Cold_Treatment_1")
medical_es.add_rule(["有头痛", "有恶心"], "可能患有偏头痛", "Rule_Migraine_1")
medical_es.add_rule(["可能患有偏头痛", "有畏光"], "建议休息并服用止痛药", "Rule_Migraine_Treatment_1")
print("n--- 场景一:闭集问题 (已知症状) ---")
medical_es.add_fact("有发烧")
medical_es.add_fact("有咳嗽")
medical_es.add_fact("有流鼻涕")
medical_es.infer()
print("n--- 解释 ---")
print(medical_es.get_explanation())
# 重置系统以进行新场景
medical_es_open = ExpertSystem()
medical_es_open.add_rule(["有发烧", "有咳嗽"], "可能患有感冒", "Rule_Cold_1")
medical_es_open.add_rule(["有发烧", "有皮疹"], "可能患有麻疹", "Rule_Measles_1")
medical_es_open.add_rule(["可能患有感冒", "有流鼻涕"], "建议服用感冒药", "Rule_Cold_Treatment_1")
medical_es_open.add_rule(["有头痛", "有恶心"], "可能患有偏头痛", "Rule_Migraine_1")
medical_es_open.add_rule(["可能患有偏头痛", "有畏光"], "建议休息并服用止痛药", "Rule_Migraine_Treatment_1")
print("nn--- 场景二:开集问题 (未知症状组合) ---")
# 引入新颖的、系统未被明确告知如何处理的症状组合
medical_es_open.add_fact("有低烧") # "低烧"与"发烧"不同,系统无法泛化
medical_es_open.add_fact("有肌肉酸痛")
medical_es_open.add_fact("有疲劳")
medical_es_open.infer()
print("n--- 解释 ---")
print(medical_es_open.get_explanation())
输出分析:
在“场景一”中,系统根据已有的规则成功诊断出“可能患有感冒”并“建议服用感冒药”。推理过程是清晰可解释的。
在“场景二”中,我们输入了“有低烧”、“有肌肉酸痛”、“有疲劳”这些症状。尽管这些症状在现实中可能与多种疾病相关,但由于我们的专家系统中没有明确的规则来匹配这些症状组合,系统无法推导出任何结论。它不会尝试将“低烧”泛化为“发烧”,也不会去外部寻找这些症状可能代表的疾病。它只是报告“Final Conclusions: set()”,这体现了其在处理开集问题时的脆性。系统“不知道”如何处理这些新信息,也没有机制去“学习”或“查找”。
LangGraph与LLM Orchestration:基于涌现知识的开放世界
现在,让我们转向现代的解决方案。随着大型语言模型(LLM)的兴起,我们有了一种处理语言和知识的全新范式。然而,仅仅依靠一个LLM的单次调用往往不足以解决复杂的、多步骤的问题,尤其是在需要与外部世界交互时。LangGraph正是在这种背景下诞生的,它提供了一个强大的框架来编排LLM、工具和自定义逻辑,以构建复杂的、有状态的、自治的代理(agents)。
LangGraph是什么?
LangGraph是LangChain生态系统的一部分,它允许开发者通过定义一个有向图来构建多步、有状态的LLM应用程序。这个图中的节点可以是LLM调用、工具调用、自定义函数或条件逻辑。LangGraph的核心优势在于它能够支持循环(cycles),这意味着代理可以根据需要反复执行某些步骤,例如:思考、行动、观察、再思考。这使得构建能够进行复杂推理、自我修正和动态规划的代理成为可能。
LangGraph如何利用LLM处理知识
LangGraph并非直接“存储”知识,而是利用LLM的涌现知识(Emergent Knowledge)和推理能力,并通过编排LLM与外部工具的交互来动态地获取和处理知识。
- LLM作为推理引擎和调度器: LLM在LangGraph中扮演着核心角色。它不仅仅是文本生成器,更是一个强大的推理引擎,能够理解自然语言输入,进行复杂的逻辑判断,并决定下一步应该采取什么行动(例如,调用哪个工具,如何重构问题,或者直接给出答案)。
- 工具使用 (Tool Use / Function Calling): 这是LangGraph和LLM处理开集问题的关键机制之一。LLM可以被赋予一系列“工具”(例如,搜索引擎API、数据库查询接口、计算器、自定义API等)。当LLM遇到它自身知识库中没有的信息,或者需要执行特定操作时,它会动态地选择并调用合适的工具来获取信息或执行任务。这极大地扩展了LLM的能力范围,使其能够与外部世界进行实时交互,获取最新、最准确的信息。
- 检索增强生成 (Retrieval Augmented Generation, RAG): 对于特定领域的知识,即使LLM经过了海量数据训练,也可能缺乏最新的或高度专业化的信息。RAG通过在生成响应之前,从外部知识库(如文档、数据库、网页)中检索相关信息,然后将其作为上下文提供给LLM,从而“增强”LLM的生成能力。这使得LLM能够基于事实生成更准确、更相关的回答,同时减少幻觉。
- 状态管理 (Stateful Execution): LangGraph允许在图的执行过程中维护和更新状态。这意味着代理可以记住过去的对话、观察结果和推理步骤,从而进行更复杂的、多轮的交互和决策。
- 自我修正和迭代: LangGraph的循环特性使得代理能够实现自我修正。例如,如果一个工具调用失败,或者LLM的初始回答不满意,代理可以重新评估情况,尝试不同的工具,或者重新规划推理路径。这模仿了人类解决问题时的迭代过程。
LangGraph在开集问题中的优势
LangGraph与LLM的结合,为处理开集问题带来了范式上的转变:
- 适应性与泛化能力: LLM的预训练使其具备了广泛的世界知识和强大的泛化能力。即使遇到从未见过的实体或情境,它也能尝试根据上下文进行合理的推断,或者通过工具获取更多信息。它不是硬编码的规则,而是基于海量数据学习到的模式。
- 动态知识获取: 通过工具使用和RAG,LangGraph代理能够主动地、实时地从外部世界获取信息。当面对未知时,它不会简单地失败,而是尝试“查询”或“搜索”答案,这极大地弥补了LLM训练数据时效性的不足,并扩展了其知识边界。
- 鲁棒性与柔性: LLM对自然语言的理解能力使其能够更好地处理模糊、不完整或非标准化的输入。它能从语境中推断意图,而不仅仅是匹配硬编码的模式。
- 减少知识获取瓶颈: 大部分“知识”已经通过LLM的预训练隐含地存在。开发者不再需要手动编码所有规则,而是专注于设计有效的工具和编排推理流程。
- 处理歧义和不确定性: LLM可以生成多个可能的解释或建议,并可以被引导去询问澄清性问题,以解决歧义。
代码示例:一个LangGraph代理及其处理开集问题的能力
我们将构建一个简单的LangGraph代理,它能够回答问题。如果它不知道答案,它会尝试使用一个“搜索引擎工具”来查找信息。
首先,确保安装必要的库:
pip install langchain_openai langgraph beautifulsoup4 duckduckgo-search
import operator
from typing import Annotated, Sequence, TypedDict, List
from langchain_core.messages import BaseMessage, FunctionMessage, HumanMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from langchain_community.tools import DuckDuckGoSearchRun
# 确保设置你的OpenAI API Key
# import os
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# 定义一个搜索引擎工具
search_tool = DuckDuckGoSearchRun()
@tool
def custom_calculator(expression: str) -> str:
"""计算数学表达式。例如:'2+2', '(5*3)-1', '100/4'"""
try:
result = eval(expression)
return str(result)
except Exception as e:
return f"Error calculating: {e}"
# 定义工具列表
tools = [search_tool, custom_calculator]
# 定义图的状态
class AgentState(TypedDict):
messages: Annotated[List[BaseMessage], operator.add]
# 我们也可以添加其他状态变量,例如:
# history: str
# current_plan: str
# 定义图中的节点
class AgentNodes:
def __init__(self, llm_model, tools_list):
self.llm = llm_model
self.tools = tools_list
self.prompt = ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的AI助手,可以回答问题和使用工具。"),
MessagesPlaceholder("messages"),
])
self.llm_with_tools = self.llm.bind_tools(self.tools)
def agent(self, state: AgentState):
"""代理节点:根据当前消息决定下一步行动."""
print("n--- Agent Node: Deciding Action ---")
messages = state["messages"]
response = self.llm_with_tools.invoke(messages)
print(f"Agent LLM Response: {response}")
return {"messages": [response]}
def execute_tools(self, state: AgentState):
"""工具执行节点:执行LLM建议的工具调用."""
print("n--- Execute Tools Node: Executing Tool Calls ---")
messages = state["messages"]
last_message = messages[-1]
if not last_message.tool_calls:
raise ValueError("No tool calls found in the last message.")
tool_outputs = []
for tool_call in last_message.tool_calls:
tool_name = tool_call.name
tool_args = tool_call.args
print(f"Calling tool: {tool_name} with args: {tool_args}")
try:
# 寻找并执行对应的工具
chosen_tool = next(t for t in self.tools if t.name == tool_name)
output = chosen_tool.invoke(tool_args) # DuckDuckGoSearchRun, custom_calculator 都是可调用的
tool_outputs.append(FunctionMessage(name=tool_name, content=str(output)))
except Exception as e:
tool_outputs.append(FunctionMessage(name=tool_name, content=f"Error executing tool {tool_name}: {e}"))
print(f"Tool Outputs: {tool_outputs}")
return {"messages": tool_outputs}
# 定义条件路由函数
def should_continue(state: AgentState):
"""根据LLM的响应决定是否继续执行工具或结束."""
last_message = state["messages"][-1]
# 如果LLM的响应包含工具调用,则继续执行工具
if last_message.tool_calls:
print("--- Router: LLM suggested tool calls. Routing to 'execute_tools' ---")
return "continue"
else:
# 否则,LLM已经生成了最终答案,结束
print("--- Router: LLM provided final answer. Routing to 'END' ---")
return "end"
# 初始化LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0) # 使用gpt-4o以获得更好的工具调用能力
# 创建节点实例
agent_nodes = AgentNodes(llm, tools)
# 构建LangGraph图
workflow = StateGraph(AgentState)
# 添加节点
workflow.add_node("agent", agent_nodes.agent)
workflow.add_node("execute_tools", agent_nodes.execute_tools)
# 设置入口点
workflow.set_entry_point("agent")
# 定义边
# 从 agent 节点,根据 should_continue 函数的结果决定去向
workflow.add_conditional_edges(
"agent",
should_continue,
{
"continue": "execute_tools", # 如果有工具调用,就去执行工具
"end": END # 如果没有工具调用,就结束
}
)
# 从 execute_tools 节点,总是回到 agent 节点,让LLM处理工具输出
workflow.add_edge("execute_tools", "agent")
# 编译图
app = workflow.compile()
# --- 场景一:闭集问题 (LLM自身知识可回答) ---
print("nn--- 场景一:闭集问题 (LLM自身知识可回答) ---")
inputs = {"messages": [HumanMessage(content="地球的卫星是什么?")]}
response = app.invoke(inputs)
print(f"nFinal Response: {response['messages'][-1].content}")
# --- 场景二:开集问题 (需要搜索工具) ---
print("nn--- 场景二:开集问题 (需要搜索工具) ---")
# 这是一个LLM训练数据可能不包含或不最新的问题
inputs = {"messages": [HumanMessage(content="2024年奥斯卡最佳影片是哪部?")]}
response = app.invoke(inputs)
print(f"nFinal Response: {response['messages'][-1].content}")
# --- 场景三:开集问题 (需要计算工具) ---
print("nn--- 场景三:开集问题 (需要计算工具) ---")
inputs = {"messages": [HumanMessage(content="计算 (15 * 3) + 7 的结果是多少?")]}
response = app.invoke(inputs)
print(f"nFinal Response: {response['messages'][-1].content}")
# --- 场景四:开集问题 (复杂查询,可能需要多步思考和工具调用) ---
print("nn--- 场景四:开集问题 (复杂查询,可能需要多步思考和工具调用) ---")
inputs = {"messages": [HumanMessage(content="法国的首都在哪里?它的标志性建筑埃菲尔铁塔高度是多少米?")]}
response = app.invoke(inputs)
print(f"nFinal Response: {response['messages'][-1].content}")
# --- 场景五:处理未知或模糊输入 ---
print("nn--- 场景五:处理未知或模糊输入 ---")
inputs = {"messages": [HumanMessage(content="我感觉不太舒服,有什么建议吗?")]}
response = app.invoke(inputs)
print(f"nFinal Response: {response['messages'][-1].content}")
输出分析:
在上述LangGraph示例中:
- 场景一 (地球的卫星): LLM直接使用其内部知识给出答案,不需要工具。
- 场景二 (2024年奥斯卡最佳影片): 这是一个LLM训练数据可能不包含的开集问题。LLM会识别到它无法直接回答,并决定调用
DuckDuckGoSearchRun工具。工具执行后,LLM会再次接收搜索结果,并根据结果生成最终答案。 - 场景三 (计算表达式): LLM识别到这是一个计算任务,并调用
custom_calculator工具来执行计算。 - 场景四 (法国首都及埃菲尔铁塔高度): LLM可以分解问题,首先回答法国首都(自身知识),然后识别出需要查找埃菲尔铁塔高度,并调用
DuckDuckGoSearchRun工具。 - 场景五 (我感觉不太舒服): 这是一个模糊的开集问题。LLM不会尝试“诊断”疾病,因为它没有被赋予医疗诊断的工具,但它会根据其常识和安全指南,给出通用的建议,如“建议咨询医生”。这体现了它在面对超出能力范围的问题时,能够给出负责任的、非崩溃的响应。
这个例子展示了LangGraph如何通过编排LLM的推理能力和外部工具,使其能够动态地适应新信息、获取外部知识,从而有效地处理各种开集问题。系统不再受限于预编码的规则,而是能够根据情境选择最佳的行动路径。
本质差异:处理未知的能力
现在,让我们通过一个对比表格来总结这两种范式在处理开集问题时的核心差异。
| 特性/维度 | 传统专家系统 (Expert Systems) | LangGraph (基于LLM的编排) |
|---|---|---|
| 知识表示 | 显式、符号化、人工编码的规则和事实 | 隐式、统计学模式学习的涌现知识 (LLM内部),通过嵌入和向量空间表示外部知识 (RAG) |
| 推理机制 | 确定性的逻辑推导 (前向/后向链) | 非确定性的、概率性的推理 (LLM),结合符号逻辑和工具调用 |
| 处理未知 | 根本性缺陷: 遇到未知或规则外情况时,无法泛化、无力应对,通常表现为失败或无响应。遵循“封闭世界假设”。 | 核心优势: 能够识别未知、主动获取信息(工具、RAG)、泛化处理、自我修正。遵循“开放世界假设”。 |
| 知识获取 | 知识获取瓶颈:需人工耗时构建和维护庞大规则库 | 知识获取相对容易:利用LLM预训练知识;通过RAG或工具动态注入领域知识。 |
| 可解释性 | 高:推理路径清晰,可追溯每一步规则触发 | 相对较低:LLM是黑盒模型,决策过程难以完全解释;但LangGraph的图结构有助于理解高层流程。 |
| 适应性/泛化 | 低:仅限于预定义领域,对新情况脆性 | 高:通过LLM的泛化能力和工具扩展,能适应广泛的新情境。 |
| 鲁棒性 | 低:对输入格式、语法的微小变化敏感 | 高:LLM对自然语言的理解使其能处理模糊、不完整输入。 |
| 复杂性 | 规则越多,系统维护和扩展越复杂 | 图的编排和Prompt Engineering是新的复杂性来源;LLM本身复杂性高。 |
| 开发成本 | 早期投入在知识工程上巨大 | LLM API成本,Prompt Engineering,以及工具开发。 |
| 核心哲学 | 预设所有可能,通过显式规则覆盖所有情况 | 承认未知不可避免,通过智能代理(LLM)与外部世界互动来应对未知。 |
LangGraph与专家系统在处理开集问题时的本质差异,归结于它们对“未知”的根本态度和处理机制。
-
专家系统面对未知,其反应是“我没有规则来处理这个,所以它不存在或我无法处理”。它是一个“封闭世界”系统,其智能完全来源于人类专家预设的、显式编码的知识。当遇到超出其显式知识边界的事物时,它会陷入瘫痪或给出无效的输出。它不会去“思考”如何获取新知识,也不会“理解”它所不知道的是什么。它缺乏一种“元认知”的能力。
-
LangGraph(通过LLM)面对未知,其反应是“我可能不知道,但我可以尝试去理解或找到答案”。它是一个“开放世界”系统,其智能一部分来源于LLM庞大的预训练知识(这种知识是涌现的,而非显式编码),另一部分来源于其通过工具与外部世界交互的能力。当遇到未知时,LLM可以:
- 识别未知: LLM可以根据其语言理解能力判断某个请求是否超出了其直接回答的范围。
- 规划行动: 它可以在LangGraph的框架下,决定调用哪个工具(例如,搜索引擎、数据库查询)来获取关于未知的信息。
- 整合信息: 获取到新信息后,LLM能够将其整合到当前的推理过程中,生成更全面的答案。
- 自我修正: 如果第一次尝试失败,它可以根据反馈重新规划。
这种差异可以比喻为:专家系统像一本百科全书,其知识是固定的,一旦遇到百科全书里没有的词条,就无能为力。而LangGraph代理则像一个聪明的人,他既有丰富的常识,又知道如何使用图书馆、互联网和各种工具去查找并理解新的信息。
实践意义与混合方法
在实际应用中,我们并非总是在两者之间做非此即彼的选择。理解它们的本质差异能帮助我们更好地进行系统设计:
-
何时选择专家系统?
- 当问题域是高度结构化、规则明确、变化缓慢且对确定性和可解释性要求极高时(例如,某些法规遵从性检查、设备故障诊断的特定子系统)。
- 当知识可以被清晰地、无歧义地编码成规则时。
-
何时选择LangGraph/LLM编排?
- 当问题域是开放的、动态变化的、包含大量非结构化数据或需要常识推理时(例如,智能客服、内容生成、复杂决策支持、研究助理)。
- 当需要系统具备学习、适应、与外部世界实时交互的能力时。
- 当知识获取和维护成本过高,不适合人工编码所有规则时。
-
混合方法:
在许多复杂的企业级应用中,最佳实践可能是采用混合方法。例如:- LLM作为元推理层: LangGraph代理可以作为顶层协调者,利用LLM的泛化能力处理高层决策和模糊输入。
- 专家系统作为专业工具: 在LangGraph的工具集中,可以包含一个或多个传统的专家系统。当LLM识别出某个子问题需要高度确定性、高可解释性的逻辑推理时,它可以调用对应的专家系统工具来处理。例如,一个LLM驱动的客服系统在遇到复杂的报销规则问题时,可以调用一个专门处理报销规则的专家系统来给出精确的答案。
- LLM辅助规则生成: LLM甚至可以辅助领域专家生成或优化专家系统的规则,从而缓解知识获取瓶颈。
这种混合方法能够结合两者的优点,即LLM的灵活性和泛化能力,以及专家系统的精确性和可解释性,构建出更加强大和鲁棒的AI系统。
从静态规则到动态适应:范式革新
回顾我们今天的探讨,传统专家系统在处理开集问题时,其固有的“封闭世界”假设和对显式规则的强依赖,使其表现出根本性的脆性。它们不知道如何应对未知的输入,也无法主动获取新知识。
而以LangGraph为代表的LLM编排框架,则彻底颠覆了这一范式。它通过赋予LLM强大的推理、工具使用和自我修正能力,将系统从一个静态的规则集合转变为一个能够动态适应、主动学习和与外部世界交互的智能代理。这种转变的核心在于,它从根本上改变了系统处理“未知”的方式——从彻底失败转变为有策略地应对、寻求和整合信息。
这不仅仅是技术上的进步,更是人工智能哲学上的一次深刻演进:从试图预设和编码所有可能,到拥抱不确定性,并通过智能化的动态适应来驾驭它。在面对现实世界中无处不在的开集问题时,LangGraph和LLM编排无疑为我们提供了更强大、更灵活、更具前瞻性的解决方案。