一、LangChain正则表达式解析概述
LangChain作为一个强大的语言模型应用框架,正则表达式在其解析模块中扮演着至关重要的角色。从用户输入的处理到模型输出的解析,从工具调用参数的提取到复杂文本结构的识别,正则表达式无处不在。本章将深入探讨LangChain中正则表达式的应用场景、设计理念以及核心接口定义。
1.1 正则表达式在LangChain中的核心作用
在LangChain的架构中,正则表达式主要用于以下几个关键场景:
输出格式解析:将模型生成的非结构化文本解析为结构化数据,例如从模型回答中提取特定格式的信息。
工具调用参数提取:在代理系统中,正则表达式用于识别和提取工具调用所需的参数。
文本预处理:对用户输入进行清洗和格式化,确保输入符合模型或后续处理的要求。
模板变量匹配:在提示模板中,使用正则表达式识别和替换变量占位符。
结构化数据提取:从复杂文本中提取特定模式的数据,如日期、数字、URL等。
1.2 正则表达式解析器的设计理念
LangChain的正则表达式解析器设计遵循以下几个核心原则:
灵活性:支持多种正则表达式语法和匹配模式,适应不同的解析需求。
可扩展性:易于扩展新的解析器类型,支持自定义解析逻辑。
健壮性:能够处理不规范的文本输入,提供合理的错误处理机制。
性能优化:在保证解析准确性的前提下,尽可能提高解析效率。
用户友好:提供简洁明了的API接口,降低用户使用难度。
1.3 正则表达式核心接口定义
LangChain定义了一系列接口和基类,用于规范正则表达式解析器的行为:
class BaseOutputParser(ABC): """所有输出解析器的基类,定义了解析器的基本接口。""" @abstractmethod def parse(self, text: str) -> Any: """将输入文本解析为特定格式的结构化数据。""" pass @abstractmethod def get_format_instructions(self) -> str: """获取格式说明,用于指导模型生成符合要求的输出。""" passclass 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类的详细实现:
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实例时,会立即编译正则表达式,并在解析过程中重复使用编译后的对象,避免了每次解析时的重复编译开销。
# 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中,输出解析器负责将语言模型生成的非结构化文本转换为结构化数据。这对于构建需要明确输入格式的应用程序(如工具调用、数据库查询等)尤为重要。
输出解析器的核心接口定义如下:
class BaseOutputParser(ABC): """所有输出解析器的基类。""" @abstractmethod def parse(self, text: str) -> Any: """将文本解析为特定格式。""" pass @abstractmethod def get_format_instructions(self) -> str: """获取格式说明,用于指导模型生成正确格式的输出。""" pass
3.2 正则表达式在输出解析中的应用场景
正则表达式在输出解析中有多种应用场景,包括:
提取特定信息:从模型输出中提取特定的字段或值。
验证格式:确保模型输出符合预期的格式。
分割文本:将复杂文本分割成多个部分。
转换为结构化数据:将非结构化文本转换为字典、列表等结构化数据。
3.3 正则表达式输出解析器示例
下面是一个使用正则表达式解析器提取日期和事件的示例:
# 创建一个正则表达式解析器,用于提取日期和事件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{ "name": "search", "parameters": { "query": "LangChain是什么" }}
为了从这种格式的文本中提取工具名称和参数,可以使用以下正则表达式:```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 复杂工具调用解析示例
下面是一个更复杂的工具调用解析示例,展示如何处理多个工具调用:
# 模拟模型输出,包含多个工具调用model_output = """```json{ "name": "search", "parameters": { "query": "LangChain官方文档" }}
根据搜索结果,我还需要调用另一个工具获取更多信息。
{ "name": "get_document", "parameters": { "id": "doc123", "format": "pdf" }}
五、提示模板中的正则表达式
5.1 提示模板的基本概念
提示模板是LangChain中的一个重要组件,用于生成向语言模型提供的提示文本。提示模板通常包含变量占位符,这些占位符在运行时会被实际值替换。
提示模板的基本接口定义如下:
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}
这种格式来表示变量占位符。下面是一个简化的实现,展示如何使用正则表达式识别和替换这些变量:
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格式通常如下:
Thought: 需要查找关于X的信息Action: searchAction Input: X
为了解析这种格式,可以使用以下正则表达式:
# 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 复杂代理输出解析示例
下面是一个更复杂的代理输出解析示例,展示如何处理包含多个工具调用的输出:
# 模拟代理输出agent_output = """Thought: 用户想了解LangChain是什么,我需要搜索相关信息Action: searchAction Input: LangChain是什么Observation: LangChain是一个用于开发由语言模型驱动的应用程序的框架。Thought: 现在我知道了LangChain的基本定义,用户可能还想了解它的功能Action: searchAction 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格式的输出:
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 处理不规范文本
模型输出可能不总是符合预期的格式,特别是在处理开放式问题时。为了处理不规范的文本,可以使用更灵活的正则表达式和启发式方法。
例如,处理可能包含额外文本的工具调用:
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 优化正则表达式性能
在处理大量文本或复杂模式时,正则表达式的性能可能成为瓶颈。以下是一些优化正则表达式性能的技巧:
预编译正则表达式:在使用正则表达式之前编译它们,避免重复编译。
简化模式:尽量使用简单的正则表达式模式,避免过度复杂的模式。
使用非贪婪匹配:在可能的情况下,使用非贪婪匹配(如.*?
)代替贪婪匹配(如.*
)。
避免回溯:复杂的正则表达式可能导致大量回溯,影响性能。
分阶段匹配:对于复杂的匹配任务,考虑分阶段使用多个正则表达式进行匹配。
下面是一个性能优化的示例:
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类中的错误处理实现:
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 健壮性设计策略
为了提高正则表达式解析的健壮性,可以采用以下策略:
默认值处理:在解析失败时提供默认值,避免抛出异常。
宽松匹配:使用更灵活的正则表达式模式,允许一定程度的格式变化。
多阶段解析:将复杂的解析任务分解为多个简单的步骤。
验证和清理:在解析后对结果进行验证和清理,确保数据质量。
错误日志记录:记录解析错误,便于后续分析和改进。
下面是一个健壮性设计的示例:
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 正则表达式性能分析
在处理大量文本或复杂模式时,正则表达式的性能可能成为瓶颈。以下是一些常见的性能问题和分析方法:
匹配时间过长:复杂的正则表达式可能需要很长时间才能完成匹配。
内存占用过高:某些正则表达式模式可能导致内存占用过高。
回溯过多:贪婪匹配和复杂的量词可能导致大量回溯,影响性能。