一、LangChain正则表达式解析概述
LangChain作为一个强大的语言模型应用框架,正则表达式在其解析模块中扮演着至关重要的角色。从用户输入的处理到模型输出的解析,从工具调用参数的提取到复杂文本结构的识别,正则表达式无处不在。本章将深入探讨LangChain中正则表达式的应用场景、设计理念以及核心接口定义。
1.1 正则表达式在LangChain中的核心作用
在LangChain的架构中,正则表达式主要用于以下几个关键场景:
-
输出格式解析:将模型生成的非结构化文本解析为结构化数据,例如从模型回答中提取特定格式的信息。
-
工具调用参数提取:在代理系统中,正则表达式用于识别和提取工具调用所需的参数。
-
文本预处理:对用户输入进行清洗和格式化,确保输入符合模型或后续处理的要求。
-
模板变量匹配:在提示模板中,使用正则表达式识别和替换变量占位符。
-
结构化数据提取:从复杂文本中提取特定模式的数据,如日期、数字、URL等。
1.2 正则表达式解析器的设计理念
LangChain的正则表达式解析器设计遵循以下几个核心原则:
-
灵活性:支持多种正则表达式语法和匹配模式,适应不同的解析需求。
-
可扩展性:易于扩展新的解析器类型,支持自定义解析逻辑。
-
健壮性:能够处理不规范的文本输入,提供合理的错误处理机制。
-
性能优化:在保证解析准确性的前提下,尽可能提高解析效率。
-
用户友好:提供简洁明了的API接口,降低用户使用难度。
1.3 正则表达式核心接口定义
LangChain定义了一系列接口和基类,用于规范正则表达式解析器的行为:
python
class BaseOutputParser(ABC):
"""所有输出解析器的基类,定义了解析器的基本接口。"""
@abstractmethod
def parse(self, text: str) -> Any:
"""将输入文本解析为特定格式的结构化数据。"""
pass
@abstractmethod
def get_format_instructions(self) -> str:
"""获取格式说明,用于指导模型生成符合要求的输出。"""
pass
class RegexParser(BaseOutputParser):
"""基于正则表达式的输出解析器,使用正则表达式从文本中提取信息。"""
def __init__(self, regex: str, output_keys: List[str], default_value: Optional[Any] = None):
"""
初始化正则表达式解析器。
Args:
regex: 用于匹配文本的正则表达式。
output_keys: 匹配结果对应的键名列表。
default_value: 匹配失败时的默认值。
"""
self.regex = regex
self.output_keys = output_keys
self.default_value = default_value
self.compiled_regex = re.compile(regex)
def parse(self, text: str) -> Dict[str, Any]:
"""使用正则表达式解析文本并返回结构化结果。"""
match = self.compiled_regex.search(text)
if not match:
if self.default_value is not None:
return {key: self.default_value for key in self.output_keys}
raise ValueError(f"文本不匹配正则表达式: {self.regex}")
groups = match.groups()
if len(groups) != len(self.output_keys):
raise ValueError(f"正则表达式匹配的组数({len(groups)})与输出键数({len(self.output_keys)})不匹配")
return {key: value for key, value in zip(self.output_keys, groups)}
def get_format_instructions(self) -> str:
"""返回格式说明,指导模型生成符合正则表达式的输出。"""
return f"你的回答应匹配正则表达式: {self.regex}"
二、正则表达式基础与LangChain实现
2.1 正则表达式基础
正则表达式是一种强大的文本模式匹配工具,它使用特定的字符序列来定义搜索模式。在Python中,正则表达式通过re
模块实现,支持多种元字符和特殊序列,用于匹配不同类型的文本模式。
以下是一些常见的正则表达式元字符和功能:
-
字符类 :
[ ]
用于定义字符类,匹配方括号内的任意一个字符。例如,[abc]
匹配'a'、'b'或'c'。 -
量词 :
*
、+
、?
、{n}
、{n,}
、{n,m}
用于指定匹配的次数。例如,a+
匹配一个或多个'a'。 -
特殊字符 :
.
匹配任意字符(除了换行符),^
匹配字符串的开头,$
匹配字符串的结尾。 -
分组 :
( )
用于创建捕获组,可以将匹配的部分提取出来。例如,(ab)+
匹配一个或多个连续的'ab'。 -
转义字符 :
\
用于转义特殊字符,例如\.
匹配实际的点号。 -
预定义字符类 :
\d
匹配数字,\w
匹配单词字符,\s
匹配空白字符等。
2.2 LangChain中的正则表达式实现
LangChain在多个组件中使用正则表达式,其中最核心的是RegexParser类。这个类封装了正则表达式的编译和匹配过程,提供了简洁的API接口。
下面是RegexParser类的详细实现:
python
class RegexParser(BaseOutputParser):
"""使用正则表达式解析LLM输出的解析器。"""
regex: str
"""用于匹配输出的正则表达式。"""
output_keys: List[str]
"""匹配结果对应的键名列表。"""
default_value: Optional[Any] = None
"""匹配失败时的默认值。"""
compiled_regex: Pattern
"""编译后的正则表达式对象。"""
def __init__(self, regex: str, output_keys: List[str], default_value: Optional[Any] = None):
"""
初始化正则表达式解析器。
Args:
regex: 用于匹配输出的正则表达式字符串。
output_keys: 与正则表达式捕获组对应的输出键名列表。
default_value: 当匹配失败时返回的默认值。
"""
super().__init__()
self.regex = regex
self.output_keys = output_keys
self.default_value = default_value
# 编译正则表达式以提高性能
try:
self.compiled_regex = re.compile(regex, re.DOTALL)
except re.error as e:
raise ValueError(f"无效的正则表达式: {regex}") from e
# 验证正则表达式中的捕获组数量与输出键数量一致
group_count = self.compiled_regex.groups
if group_count != len(output_keys):
raise ValueError(f"正则表达式中的捕获组数量({group_count})与输出键数量({len(output_keys)})不匹配")
def parse(self, text: str) -> Dict[str, Any]:
"""
使用正则表达式解析文本并返回结构化结果。
Args:
text: 要解析的文本。
Returns:
包含匹配结果的字典,键为初始化时指定的output_keys。
"""
# 执行正则表达式匹配
match = self.compiled_regex.search(text)
if match is None:
# 匹配失败,返回默认值或抛出异常
if self.default_value is not None:
return {key: self.default_value for key in self.output_keys}
raise ValueError(f"文本不匹配正则表达式: {self.regex}")
# 提取匹配的组
groups = match.groups()
# 构建结果字典
result = {}
for key, value in zip(self.output_keys, groups):
# 处理空匹配
if value is None:
value = ""
result[key] = value.strip() if isinstance(value, str) else value
return result
def get_format_instructions(self) -> str:
"""
返回格式说明,指导模型生成符合正则表达式的输出。
Returns:
格式说明字符串。
"""
return (
f"你的回答应符合以下格式,并匹配正则表达式 `{self.regex}`。"
f"提取的字段将被映射到以下键: {', '.join(self.output_keys)}"
)
def __str__(self) -> str:
"""返回解析器的字符串表示形式。"""
return f"RegexParser(regex='{self.regex}', output_keys={self.output_keys})"
2.3 正则表达式的编译与优化
在LangChain中,正则表达式的编译和优化是提高性能的关键。当创建RegexParser实例时,会立即编译正则表达式,并在解析过程中重复使用编译后的对象,避免了每次解析时的重复编译开销。
python
# RegexParser类中的初始化部分
try:
self.compiled_regex = re.compile(regex, re.DOTALL)
except re.error as e:
raise ValueError(f"无效的正则表达式: {regex}") from e
这里使用了re.DOTALL
标志,使得.
元字符可以匹配包括换行符在内的任意字符,这在处理多行文本时非常有用。
三、输出解析器中的正则表达式应用
3.1 输出解析器的作用
在LangChain中,输出解析器负责将语言模型生成的非结构化文本转换为结构化数据。这对于构建需要明确输入格式的应用程序(如工具调用、数据库查询等)尤为重要。
输出解析器的核心接口定义如下:
python
class BaseOutputParser(ABC):
"""所有输出解析器的基类。"""
@abstractmethod
def parse(self, text: str) -> Any:
"""将文本解析为特定格式。"""
pass
@abstractmethod
def get_format_instructions(self) -> str:
"""获取格式说明,用于指导模型生成正确格式的输出。"""
pass
3.2 正则表达式在输出解析中的应用场景
正则表达式在输出解析中有多种应用场景,包括:
-
提取特定信息:从模型输出中提取特定的字段或值。
-
验证格式:确保模型输出符合预期的格式。
-
分割文本:将复杂文本分割成多个部分。
-
转换为结构化数据:将非结构化文本转换为字典、列表等结构化数据。
3.3 正则表达式输出解析器示例
下面是一个使用正则表达式解析器提取日期和事件的示例:
python
# 创建一个正则表达式解析器,用于提取日期和事件
parser = RegexParser(
regex=r"日期: (.*?), 事件: (.*)",
output_keys=["date", "event"]
)
# 模拟模型输出
model_output = "日期: 2023年10月15日, 事件: 参加机器学习会议"
# 解析输出
result = parser.parse(model_output)
# 输出结果: {'date': '2023年10月15日', 'event': '参加机器学习会议'}
print(result)
在这个示例中,正则表达式r"日期: (.*?), 事件: (.*)"
用于匹配以"日期: "开头,后跟日期信息,然后是", 事件: ",最后是事件描述的文本。捕获组(.*?)
和(.*)
分别提取日期和事件信息。
四、工具调用参数解析中的正则表达式
4.1 工具调用的基本原理
在LangChain的代理系统中,工具是执行特定任务的组件。当代理决定调用某个工具时,需要从模型输出中提取工具名称和参数。正则表达式在这个过程中扮演着关键角色。
工具调用的基本流程如下:
-
代理接收用户输入并决定调用哪个工具。
-
代理生成包含工具调用信息的文本。
-
正则表达式解析器从文本中提取工具名称和参数。
-
执行工具并获取结果。
4.2 工具调用格式与正则表达式
LangChain中常用的工具调用格式是类似函数调用的语法,例如:
json
```json
{
"name": "search",
"parameters": {
"query": "LangChain是什么"
}
}
less
为了从这种格式的文本中提取工具名称和参数,可以使用以下正则表达式:
```python
# 用于提取JSON格式工具调用的正则表达式
TOOL_CALL_REGEX = r"```json\s*\{[^}]*"name"\s*:\s*"([^"]*)"\s*,[^}]*"parameters"\s*:\s*\{([^}]*)\}[^}]*\}\s*```"
# 编译正则表达式
compiled_regex = re.compile(TOOL_CALL_REGEX, re.DOTALL)
# 从文本中提取工具调用
def extract_tool_calls(text: str) -> List[Dict[str, Any]]:
tool_calls = []
for match in compiled_regex.finditer(text):
tool_name = match.group(1)
parameters_str = match.group(2)
# 解析参数JSON
try:
parameters = json.loads(f"{{{parameters_str}}}")
except json.JSONDecodeError:
parameters = {}
tool_calls.append({
"name": tool_name,
"parameters": parameters
})
return tool_calls
4.3 复杂工具调用解析示例
下面是一个更复杂的工具调用解析示例,展示如何处理多个工具调用:
python
# 模拟模型输出,包含多个工具调用
model_output = """
```json
{
"name": "search",
"parameters": {
"query": "LangChain官方文档"
}
}
根据搜索结果,我还需要调用另一个工具获取更多信息。
json
{
"name": "get_document",
"parameters": {
"id": "doc123",
"format": "pdf"
}
}
五、提示模板中的正则表达式
5.1 提示模板的基本概念
提示模板是LangChain中的一个重要组件,用于生成向语言模型提供的提示文本。提示模板通常包含变量占位符,这些占位符在运行时会被实际值替换。
提示模板的基本接口定义如下:
python
class BasePromptTemplate(ABC):
"""所有提示模板的基类。"""
input_variables: List[str]
"""模板中期望的输入变量列表。"""
@abstractmethod
def format(self, **kwargs: Any) -> str:
"""使用提供的变量值格式化模板。"""
pass
@abstractmethod
def format_prompt(self, **kwargs: Any) -> PromptValue:
"""使用提供的变量值格式化模板并返回PromptValue对象。"""
pass
5.2 正则表达式在提示模板中的应用
在提示模板中,正则表达式主要用于以下几个方面:
-
变量识别:使用正则表达式识别提示模板中的变量占位符。
-
格式验证:验证输入变量是否符合预期的格式。
-
模板解析:将复杂的提示模板解析为可执行的格式。
5.3 变量识别与替换的实现
LangChain中的提示模板通常使用{variable_name}
这种格式来表示变量占位符。下面是一个简化的实现,展示如何使用正则表达式识别和替换这些变量:
python
class SimplePromptTemplate(BasePromptTemplate):
"""一个简单的提示模板实现,使用正则表达式识别和替换变量。"""
template: str
"""提示模板字符串。"""
# 用于识别变量占位符的正则表达式
VARIABLE_REGEX = re.compile(r"\{([^}]+)\}")
def __init__(self, template: str, input_variables: List[str]):
"""
初始化提示模板。
Args:
template: 模板字符串。
input_variables: 模板中包含的变量名称列表。
"""
super().__init__()
self.template = template
self.input_variables = input_variables
# 验证模板中的变量与提供的input_variables一致
self._validate_template()
def _validate_template(self) -> None:
"""验证模板中的变量与提供的input_variables一致。"""
# 使用正则表达式找出模板中的所有变量
matches = self.VARIABLE_REGEX.findall(self.template)
template_variables = set(matches)
# 检查所有变量都在input_variables中
for var in template_variables:
if var not in self.input_variables:
raise ValueError(f"模板中发现未声明的变量: {var}")
# 检查所有input_variables都在模板中
for var in self.input_variables:
if var not in template_variables:
raise ValueError(f"声明的变量 {var} 未在模板中使用")
def format(self, **kwargs: Any) -> str:
"""使用提供的变量值格式化模板。"""
# 检查是否提供了所有必需的变量
missing_vars = set(self.input_variables) - set(kwargs.keys())
if missing_vars:
raise ValueError(f"缺少必需的变量: {', '.join(missing_vars)}")
# 使用正则表达式替换变量占位符
def replace_variable(match: re.Match) -> str:
var_name = match.group(1)
return str(kwargs.get(var_name, ""))
return self.VARIABLE_REGEX.sub(replace_variable, self.template)
def format_prompt(self, **kwargs: Any) -> PromptValue:
"""使用提供的变量值格式化模板并返回PromptValue对象。"""
text = self.format(**kwargs)
return StringPromptValue(text=text)
六、代理系统中的正则表达式应用
6.1 代理系统的基本原理
在LangChain中,代理系统允许语言模型自主决定何时以及如何使用工具来解决问题。代理系统的核心是能够理解模型输出中的工具调用意图,并正确解析出工具名称和参数。
代理系统的基本工作流程如下:
-
接收用户输入。
-
语言模型生成包含工具调用的思考过程。
-
解析器使用正则表达式从思考过程中提取工具调用信息。
-
执行工具并获取结果。
-
将工具结果反馈给语言模型,继续生成下一步思考。
6.2 ReAct框架与正则表达式
ReAct(Reasoning and Acting)是LangChain中常用的代理框架,它通过让模型生成思考和行动序列来解决复杂问题。在ReAct框架中,模型的输出通常遵循特定的格式,正则表达式用于解析这种格式。
ReAct格式通常如下:
yaml
Thought: 需要查找关于X的信息
Action: search
Action Input: X
为了解析这种格式,可以使用以下正则表达式:
python
# ReAct格式解析器
class ReActOutputParser(BaseOutputParser):
"""解析ReAct格式输出的解析器。"""
# 用于识别Thought行的正则表达式
THOUGHT_REGEX = re.compile(r"Thought: (.*)")
# 用于识别Action行的正则表达式
ACTION_REGEX = re.compile(r"Action: (.*)")
# 用于识别Action Input行的正则表达式
ACTION_INPUT_REGEX = re.compile(r"Action Input: (.*)")
def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
"""解析输出文本为代理动作或完成信号。"""
lines = text.strip().split("\n")
# 如果最后一行是Final Answer,则表示代理完成
if lines[-1].startswith("Final Answer:"):
final_answer = lines[-1][len("Final Answer:"):].strip()
return AgentFinish(return_values={"output": final_answer}, log=text)
# 解析Thought、Action和Action Input
thought = ""
action = ""
action_input = ""
for line in lines:
line = line.strip()
# 匹配Thought行
thought_match = self.THOUGHT_REGEX.match(line)
if thought_match:
thought = thought_match.group(1)
continue
# 匹配Action行
action_match = self.ACTION_REGEX.match(line)
if action_match:
action = action_match.group(1)
continue
# 匹配Action Input行
action_input_match = self.ACTION_INPUT_REGEX.match(line)
if action_input_match:
action_input = action_input_match.group(1)
continue
# 如果找到了Action和Action Input,则返回AgentAction
if action and action_input:
return AgentAction(tool=action, tool_input=action_input, log=text)
# 否则,无法解析
raise ValueError(f"无法解析代理输出: {text}")
def get_format_instructions(self) -> str:
"""获取格式说明。"""
return """
回答应遵循以下格式:
Thought: 你应该总是思考你需要做什么
Action: 工具名称
Action Input: 工具输入
Observation: 工具结果
... (这个思考/行动/行动输入/观察可以重复N次)
Thought: 我现在知道最终答案
Final Answer: 对原始问题的最终答案
"""
6.3 复杂代理输出解析示例
下面是一个更复杂的代理输出解析示例,展示如何处理包含多个工具调用的输出:
python
# 模拟代理输出
agent_output = """
Thought: 用户想了解LangChain是什么,我需要搜索相关信息
Action: search
Action Input: LangChain是什么
Observation: LangChain是一个用于开发由语言模型驱动的应用程序的框架。
Thought: 现在我知道了LangChain的基本定义,用户可能还想了解它的功能
Action: search
Action Input: LangChain的主要功能有哪些
Observation: LangChain的主要功能包括提示管理、链、记忆和代理等。
Thought: 现在我有了足够的信息来回答用户的问题
Final Answer: LangChain是一个用于开发由语言模型驱动的应用程序的框架。它的主要功能包括提示管理、链、记忆和代理等。
"""
# 创建解析器
parser = ReActOutputParser()
# 解析输出
steps = []
lines = agent_output.strip().split("\n")
current_step = ""
for line in lines:
if line.startswith("Observation:"):
# 遇到Observation时,解析当前步骤
if current_step:
try:
action = parser.parse(current_step)
steps.append(action)
except ValueError:
pass
current_step = ""
current_step += line + "\n"
else:
current_step += line + "\n"
# 解析最后一步
if current_step:
try:
action = parser.parse(current_step)
steps.append(action)
except ValueError:
pass
# 输出解析结果
for step in steps:
if isinstance(step, AgentAction):
print(f"工具: {step.tool}, 输入: {step.tool_input}")
elif isinstance(step, AgentFinish):
print(f"最终答案: {step.return_values['output']}")
七、正则表达式解析的高级技巧
7.1 处理复杂文本结构
在实际应用中,模型输出可能具有复杂的结构,包含嵌套的格式、特殊字符等。为了处理这些情况,可以使用更复杂的正则表达式和解析策略。
例如,处理嵌套JSON格式的输出:
python
class NestedJSONParser(BaseOutputParser):
"""解析嵌套JSON格式的解析器。"""
# 用于匹配最外层JSON的正则表达式
JSON_REGEX = re.compile(r"```json\s*(\{.*\})\s*```", re.DOTALL)
def parse(self, text: str) -> Dict[str, Any]:
"""解析嵌套JSON格式的文本。"""
# 查找最外层的JSON块
match = self.JSON_REGEX.search(text)
if not match:
raise ValueError("未找到JSON格式的输出")
json_str = match.group(1)
try:
# 解析JSON
data = json.loads(json_str)
return data
except json.JSONDecodeError as e:
raise ValueError(f"解析JSON失败: {e}") from e
def get_format_instructions(self) -> str:
"""获取格式说明。"""
return """
回答应采用以下JSON格式:
```json
{
"key1": "value1",
"key2": ["value2", "value3"],
"key3": {
"nestedKey": "nestedValue"
}
}
```
"""
7.2 处理不规范文本
模型输出可能不总是符合预期的格式,特别是在处理开放式问题时。为了处理不规范的文本,可以使用更灵活的正则表达式和启发式方法。
例如,处理可能包含额外文本的工具调用:
python
class FlexibleToolCallParser(BaseOutputParser):
"""灵活解析工具调用的解析器,能够处理不规范的格式。"""
# 宽松匹配工具调用的正则表达式
TOOL_CALL_REGEX = re.compile(
r"工具:\s*([^\n]+)\s*参数:\s*([^\n]+)",
re.IGNORECASE
)
def parse(self, text: str) -> Dict[str, Any]:
"""解析工具调用,处理不规范格式。"""
# 尝试严格匹配
match = self.TOOL_CALL_REGEX.search(text)
if not match:
# 尝试更宽松的匹配
tool_match = re.search(r"工具\s*[:=]\s*([^\n]+)", text, re.IGNORECASE)
param_match = re.search(r"参数\s*[:=]\s*([^\n]+)", text, re.IGNORECASE)
if not tool_match or not param_match:
raise ValueError("无法识别工具调用")
tool_name = tool_match.group(1).strip()
parameters = param_match.group(1).strip()
else:
tool_name = match.group(1).strip()
parameters = match.group(2).strip()
# 尝试解析参数为JSON
try:
params = json.loads(parameters)
except json.JSONDecodeError:
# 如果不是有效的JSON,尝试解析为键值对
params = {}
pairs = re.findall(r"(\w+)\s*[:=]\s*([^,\n]+)", parameters)
for key, value in pairs:
params[key] = value.strip()
return {
"name": tool_name,
"parameters": params
}
def get_format_instructions(self) -> str:
"""获取格式说明。"""
return """
回答应采用以下格式之一:
工具: 工具名称
参数: {"param1": "value1", "param2": "value2"}
或更简单的格式:
工具=工具名称
参数=param1=value1,param2=value2
"""
7.3 优化正则表达式性能
在处理大量文本或复杂模式时,正则表达式的性能可能成为瓶颈。以下是一些优化正则表达式性能的技巧:
-
预编译正则表达式:在使用正则表达式之前编译它们,避免重复编译。
-
简化模式:尽量使用简单的正则表达式模式,避免过度复杂的模式。
-
使用非贪婪匹配 :在可能的情况下,使用非贪婪匹配(如
.*?
)代替贪婪匹配(如.*
)。 -
避免回溯:复杂的正则表达式可能导致大量回溯,影响性能。
-
分阶段匹配:对于复杂的匹配任务,考虑分阶段使用多个正则表达式进行匹配。
下面是一个性能优化的示例:
python
class OptimizedRegexParser(BaseOutputParser):
"""优化性能的正则表达式解析器。"""
# 预编译正则表达式
NUMBER_REGEX = re.compile(r"\d+")
WORD_REGEX = re.compile(r"\b\w+\b")
EMAIL_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
def parse(self, text: str) -> Dict[str, Any]:
"""解析文本,提取数字、单词和电子邮件。"""
# 使用预编译的正则表达式
numbers = self.NUMBER_REGEX.findall(text)
words = self.WORD_REGEX.findall(text)
emails = self.EMAIL_REGEX.findall(text)
return {
"numbers": numbers,
"words": words,
"emails": emails
}
def get_format_instructions(self) -> str:
"""获取格式说明。"""
return "回答可以包含数字、单词和电子邮件地址。"
八、错误处理与健壮性设计
8.1 正则表达式解析中的常见错误
在使用正则表达式进行解析时,可能会遇到以下常见错误:
-
匹配失败:正则表达式无法匹配输入文本,可能是因为文本格式不符合预期。
-
解析错误:虽然匹配成功,但无法将匹配结果转换为预期的数据结构。
-
性能问题:复杂的正则表达式可能导致匹配时间过长或占用过多内存。
-
不规范输入:模型输出可能不严格遵循预期格式,导致解析困难。
8.2 错误处理机制
LangChain提供了完善的错误处理机制,确保在解析过程中能够优雅地处理各种异常情况。
以下是RegexParser类中的错误处理实现:
python
class RegexParser(BaseOutputParser):
"""使用正则表达式解析LLM输出的解析器。"""
# ... 其他代码 ...
def parse(self, text: str) -> Dict[str, Any]:
"""
使用正则表达式解析文本并返回结构化结果。
Args:
text: 要解析的文本。
Returns:
包含匹配结果的字典,键为初始化时指定的output_keys。
"""
# 执行正则表达式匹配
match = self.compiled_regex.search(text)
if match is None:
# 匹配失败,返回默认值或抛出异常
if self.default_value is not None:
return {key: self.default_value for key in self.output_keys}
raise ValueError(f"文本不匹配正则表达式: {self.regex}")
# 提取匹配的组
groups = match.groups()
# 验证匹配的组数与输出键数量一致
if len(groups) != len(self.output_keys):
raise ValueError(
f"正则表达式匹配的组数({len(groups)})与输出键数量({len(self.output_keys)})不匹配"
)
# 构建结果字典
result = {}
for key, value in zip(self.output_keys, groups):
# 处理空匹配
if value is None:
value = ""
result[key] = value.strip() if isinstance(value, str) else value
return result
# ... 其他代码 ...
8.3 健壮性设计策略
为了提高正则表达式解析的健壮性,可以采用以下策略:
-
默认值处理:在解析失败时提供默认值,避免抛出异常。
-
宽松匹配:使用更灵活的正则表达式模式,允许一定程度的格式变化。
-
多阶段解析:将复杂的解析任务分解为多个简单的步骤。
-
验证和清理:在解析后对结果进行验证和清理,确保数据质量。
-
错误日志记录:记录解析错误,便于后续分析和改进。
下面是一个健壮性设计的示例:
python
class RobustOutputParser(BaseOutputParser):
"""健壮的输出解析器,能够处理各种异常情况。"""
# 用于匹配数值的正则表达式,允许一定的格式变化
NUMBER_REGEX = re.compile(r"[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?")
# 用于匹配日期的正则表达式,支持多种常见格式
DATE_REGEX = re.compile(
r"(?:\d{4}[-/年]?\d{1,2}[-/月]?\d{1,2}[日]?)"
r"|(?:\d{1,2}[-/月]\d{1,2}[日]?[-/年]?\d{2,4})"
)
def parse(self, text: str) -> Dict[str, Any]:
"""解析文本,提取数值和日期,处理各种异常情况。"""
result = {
"numbers": [],
"dates": [],
"text": text
}
try:
# 提取数值
numbers = self.NUMBER_REGEX.findall(text)
result["numbers"] = [float(num) for num in numbers]
except Exception as e:
logger.warning(f"提取数值失败: {e}")
result["numbers"] = []
try:
# 提取日期
dates = self.DATE_REGEX.findall(text)
# 简单的日期格式标准化
standardized_dates = []
for date in dates:
# 简单的日期格式转换逻辑
standardized = self._standardize_date(date)
standardized_dates.append(standardized)
result["dates"] = standardized_dates
except Exception as e:
logger.warning(f"提取日期失败: {e}")
result["dates"] = []
return result
def _standardize_date(self, date_str: str) -> str:
"""将不同格式的日期字符串标准化为YYYY-MM-DD格式。"""
# 简单的日期格式转换实现
# 实际应用中可能需要更复杂的逻辑或使用日期解析库
return date_str.replace("年", "-").replace("月", "-").replace("日", "")
def get_format_instructions(self) -> str:
"""获取格式说明。"""
return "回答可以包含数值和日期,系统会尽力解析它们。"
九、性能分析与优化
9.1 正则表达式性能分析
在处理大量文本或复杂模式时,正则表达式的性能可能成为瓶颈。以下是一些常见的性能问题和分析方法:
-
匹配时间过长:复杂的正则表达式可能需要很长时间才能完成匹配。
-
内存占用过高:某些正则表达式模式可能导致内存占用过高。
-
回溯过多:贪婪匹配和复杂的量词可能导致大量回溯,影响性能。