跳转到内容

组件系统设计:契约、兼容与动态表单

学习目标

  • 理解 Component 基类的输入/输出契约
  • 掌握契约冻结清单:哪些属性一旦发布不可修改
  • 学会使用 legacy=True + replacement 安全替换旧组件
  • 了解组件自动发现注册机制

前置知识


项目实践

1. Component 基类

Componentsrc/lfx/src/lfx/custom/custom_component/component.py,2015 行)是所有组件的基类:

核心属性

属性含义是否冻结
name = "..."Flow JSON 中的组件标识符冻结
类名Python 类标识符冻结
inputs输入字段列表冻结(不可移除或重命名)
outputs输出字段列表及顺序冻结(不可重排序)
display_nameUI 显示名称可变(不影响 Flow JSON)
description组件描述可变
icon组件图标可变
default输入默认值视为冻结(改变默认影响已有 Flow)

2. 契约冻结清单

Langflow 的 CONTRACTS.md 定义了严格的用户契约。以下是看似无害但实际是回归的操作:

操作为什么是回归
重命名 input_valuetext100% 的保存 Flow 中引用该输入的连线丢失
重排 outputs = [a, b][b, a]使用默认选择的旧 Flow 现在路由到错误的输出
收紧 Output(types=["Message", "Data"])["Message"]解析为 Data 的边在加载时失效
改变 default="gpt-4o-mini"default="gpt-5"静默更改用户计费
移动组件文件路径用户 DB 中存储的 from langflow.components... 加载失败

3. 向后兼容:legacy=True 模式

需要修改组件时,不要就地修改,而是添加新组件 alongside:

# 旧组件(保留)
class OldCalculatorComponent(LCToolComponent):
name = "Calculator"
legacy = True
replacement = ["helpers.CalculatorComponent"]
# ... 原有代码不动
# 新组件(添加)
class CalculatorComponent(LCToolComponent):
name = "Calculator"
display_name = "Calculator"
# ... 新的实现

UI 行为

  • 旧组件在组件面板中标记为 “Legacy”,引导用户使用新版本
  • 画布上已存在的旧组件正常工作,不受影响
  • 用户看到 “Updates Available” 提示,可迁移到新组件

4. 组件自动发现

组件按类别存放在 src/lfx/src/lfx/components/<category>/ 中:

components/
├── openai/ # OpenAI 集成
├── anthropic/ # Anthropic 集成
├── vectorstores/ # 向量存储
├── tools/ # 工具组件
├── input_output/ # IO 组件
├── helpers/ # 辅助组件
└── __init__.py # 按字母序导入所有子目录

注册机制

  1. 每个 category 的 __init__.py 按字母序导入所有组件类
  2. 启动时,DirectoryReader 扫描所有组件目录
  3. 每个组件类通过反射提取 display_namedescriptionicon、输入输出 schema
  4. 生成 component_index.json 供前端组件面板使用

放置规则

  • Vendor 特定代码 → openai/, anthropic/, datastax/ 等 vendor 目录
  • 通用辅助组件 → helpers/, processing/, logic/
  • IO 原语 → input_output/
  • 禁止自创新 category(UI sidebar 可见,属于产品面)

5. 动态表单:update_build_config

def update_build_config(
self,
build_config: dict, # 当前表单配置
field_value: Any, # 变更后的字段值
field_name: str, # 变更的字段名
) -> dict:
"""根据字段值变化动态修改表单配置。"""
if field_name == "model_name":
if field_value.startswith("o"):
build_config["temperature"]["hidden"] = True
else:
build_config["temperature"]["hidden"] = False
return build_config

问题与规避

陷阱现象对策
忘记设置 name类名变化导致 Flow JSON 无法匹配始终显式设置 name = "...",不依赖 __class__.__name__
replacement 格式错误UI 不显示替换提示使用 ["category.NewClassName"] 格式(带 category 前缀)
新组件忘记继承旧行为旧 Flow 加载后行为异常新组件必须保持与旧组件兼容的输入输出行为
组件文件未更新 file_names_mapping测试通过但用户 DB 加载失败组件文件路径变更时更新所有测试版本的 file_names_mapping

设计取舍

就地修改 vs legacy 替换

维度就地修改legacy 替换
维护成本高(需要维护两套代码)
用户影响已有 Flow 静默破坏已有 Flow 正常,用户自行迁移
回滚难度高(需要 DB 迁移)低(删除新组件即可)
用户感知突然出错,无提示”Updates Available” 引导迁移

Langflow 选择 legacy 替换的核心原因是其十元原则之一:「Flow 是用户产物,不是实现细节」。Flow JSON 运行在生产环境中,向后兼容不可谈判。


参考来源

  • 源码:src/lfx/src/lfx/custom/custom_component/component.py
  • 组件开发指南:docs/agents/COMPONENTS.md
  • 用户契约:docs/agents/CONTRACTS.md