core: RunnablePick may not return a dict if keys is a string

用户在使用 LangChain 的 RunnablePick 工具时触发。该工具用于从 dict 类型的输入中筛选指定 key 的数据。当 keys 参数传入一个字符串(如 "foo" )时,返回结果是该 key 对应的值( "bar" ),而不是一个 dict {"foo": "bar"} 。此行

core: RunnablePick may not return a dict if keys is a string

core: RunnablePick may not return a dict if keys is a string

快速结论:该问题描述的是 LangChain 中 RunnablePickkeys 参数为字符串(而非列表)时,其返回类型与类型标注/文档不符——它返回的是单个键对应的值,而不是一个 dict。优先排查 keys 参数的类型是否为字符串,并关注核心库对返回类型的处理。

问题场景

用户在使用 LangChain 的 RunnablePick 工具时触发。该工具用于从 dict 类型的输入中筛选指定 key 的数据。当 keys 参数传入一个字符串(如 "foo")时,返回结果是该 key 对应的值("bar"),而不是一个 dict {"foo": "bar"}。此行为与类签名中标注的返回类型不符。

报错原文

# 按类和链式调用:
# 预期返回类型为 dict[str, Any],实际返回单个值(如字符串 "bar")
# 因此可能在使用链式调用的后续步骤中触发类型错误,例如:
# TypeError: string indices must be integers
# 或
# AttributeError: 'str' object has no attribute 'get'

# 本质问题类型签名与实际行为不一致:
class RunnablePick(RunnableSerializable[dict[str, Any], dict[str, Any]]):
    # 如果 keys 是字符串,实际返回非 dict 类型(如 Any)
    # 链式调用中下游可能隐含对 dict 类型的依赖

原因分析

这是 LangChain 核心库中 RunnablePick 的一个设计/实现缺陷。在 keys 参数为字符串时,其行为 直接返回该 key 对应的值(类型为 Any),而不是始终返回一个 dict。这与类签名中声明的 dict[str, Any] 返回类型不匹配。可能原因如下:

  • 实现时为了兼容字符串参数,直接返回了键值(类似于 dict[key] 的便捷行为)。
  • 类型系统无法表达“当 keys 是字符串时返回 Any,是列表时返回 dict”的这一条件类型(社区讨论确实提到类型依赖难以实现)。
  • 这是一个历史遗留行为,在类型标注强化后暴露出不一致性。

环境排查

  • Python 版本:3.13.2(Issue 中报告的环境)
  • langchain-core 版本:0.3.47(Issue 中报告的版本)
  • langchain 版本:0.3.21
  • langchain-community 版本:0.3.20
  • 操作系统:macOS Darwin(Issue 中报告)
  • 其他相关包:langsmith、aiohttp、numpy 等

解决步骤

  1. 确认 keys 参数类型:检查代码中 RunnablePickkeys 参数是字符串还是字符串列表。如果是字符串,你正在使用一种触发此不一致行为的方式。
  2. 方案A(推荐,非破坏性):将 keys 参数改为字符串列表,即使只选取一个 key:RunnablePick(["foo"])。这样返回始终是 dict。
  3. 方案B(等待官方修复,可能涉及 breaking change):关注 Issue 中提到的两个修复路线:
    • PR #31321:将签名改为 RunnablePick(RunnableSerializable[dict[str, Any], Any]),承认返回 Any,不破坏现有行为。此方案已被合并。
    • PR #32389:改变行为使其始终返回 dict(破坏性变更)。社区讨论可能延迟到 v1.0 版本。
  4. 方案C(代码级规避):在调用 RunnablePick 之前,预先处理字符串为列表,或在使用返回值时手动判断类型。

验证方法

编写并运行回归测试,确认 keys 参数为字符串时的返回类型与预期一致:

from langchain_core.runnables import RunnablePick

chain = RunnablePick(["foo"])  # 使用列表
result = chain.invoke({"foo": "bar", "baz": 42})
# 预期:{"foo": "bar"}
assert isinstance(result, dict)
assert result == {"foo": "bar"}

# 如果仍使用字符串,确认你的下游代码能处理返回的任意类型
chain2 = RunnablePick("foo")
result2 = chain2.invoke({"foo": "bar"})
# 当前行为返回 "bar"(字符串),不是 dict
# 确认这不会导致后续步骤报错

此外,运行完整的单元测试套件(pytest tests/unit_tests/runnables/test_pick.py)确认没有回归。

参考来源

langchain-ai/langchain #31309

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 7691

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注