langchain4j [特性] 支持在openai工具/函数接口中使用自定义类型,

a64a0gku  于 3个月前  发布在  其他
关注(0)|答案(3)|浏览(93)

您的功能请求是否与问题相关?请描述。

目前,只有Java的内置类型(boolean、byte、short、int、long、float、double、BigDecimal、BigInteger、String、Array和类似数组)作为工具或函数的参数得到支持。然而,为了方便起见,可能还需要自定义类型。

  • 示例*:假设我正在开发数据库工具;每个工具都与特定的列类型一起工作。如果没有自定义类型,我将需要在每个工具的接口中反复指定数据库、表和列名。
class DatabaseToolkit {
    @Tool
    void listByDatetime(
        @P("Database name, it can be ....")
        String dbName,
        @P("Table name, it can be ....")
        String tableName,
        @P("Column name, it can be ....")
        String columnName,
        long start, long end) {
        // ...
    }

    @Tool
    void listByMatch(
        // bad: repeated definition and description
        @P("Database name, it can be ....")
        String dbName,
        @P("Table name, it can be ....")
        String tableName,
        @P("Column name, it can be ....")
        String columnName,
        String pattern) {
        // ...
    }
   
    // more repeated... 
}

通过将这些名称组织在一个类似于Column的自定义类型中,我可以避免在每个工具或函数接口中重复它们。

class Column {
        @P("Database name, it can be ....")
        String dbName;
        @P("Table name, it can be ....")
        String tableName;
        @P("Column name, it can be ....")
        String columnName;
        // ...
}

class DatabaseToolkit {
    @Tool
    void listByDatetime(
        Column column,
        long start, long end) {
        // ...
    }

    @Tool
    void listByMatch(
        Column column,
        String pattern) {
        // ...
    }
   
    // more repeated... 
}

此外,我可以通过使用Column示例列表一次操作多个列来实现这一点。如果没有自定义类型,实现这一功能将会很具挑战性。

描述您希望的解决方案

当前的实现通过 JsonSchema 描述了工具或函数接口,包括其所有参数,这是OpenAI doc的要求。Python LangChain是这样做的。遵循这一约定,我们可以在两个步骤中支持将自定义类型作为工具/函数参数:

  • 构建一个JsonSchema生成器,将正确注解的自定义类型转换为JsonSchema;
  • 构建一个JsonSchema验证器,将由LanguageModel生成的参数验证并强制转换为自定义类型。

要做到这一点,可以通过生成自定义类型的JsonSchema来扩展 ToolSpecifications 。为了将JsonSchema属性绑定到自定义类型及其字段上,可以引入新的注解,或者直接采用现有的注解,如 Jackson-Annotations 。在 DefaultToolExecutor 中,应添加代码以根据JsonSchema或相应的注解验证参数,然后将它们强制转换为相应的类型,包括自定义类型。
有几点需要考虑:

  • 组合:当自定义类型派生时,应使用JsonSchema中的 allOf 属性递归地组合其超类的模式。
  • 多态性:如果自定义类型是抽象的或者是可以接受其子类示例的超类,则应使用JsonSchema中的 oneOf 属性。
  • 好处*:这种解决方案轻量级且对当前代码更改最少。它还避免了向langchain4j核心库添加额外的依赖项。
  • 缺点*:可能会重蹈覆辙。
  • 注意*:支持自定义类型参数将破坏 OpenAiTokenier 的令牌计数过程,因为该过程假定在估计工具规范中的令牌时参数只能是Java内置类型。一旦支持自定义类型,我们就应该更新OpenAiTokenizer的代码逻辑。
    描述您考虑过的替代方案

由于Java中有许多开源的JsonSchema生成器,因此提出的解决方案可能是在重蹈覆辙。以下是几个替代方案:

  • victools/jsonschema-generator 工具支持根据来自Jackson、Jakarta、javax和Swagger的注解生成和验证JsonSchema。
  • networknt/json-schema-validator 工具支持基于Jackson的 JSON Schema Core Draft v4, v6, v7, v2019-09 and v2020-12 规范进行JSON模式验证。
  • 好处*:采用这些现有方法的一个巨大好处是,一旦验证成功,您可以直接反序列化JSON字符串回bean,因为它们尊重像Jackson这样的序列化框架。
  • 缺点*:然而,使用现有的JsonSchema生成器可能会向核心库引入更多的依赖项,而且它们的功能对于工具/函数调用情况可能过于广泛。
    附加上下文

我的担忧包括:

  • langchain4j真的需要自定义类型参数吗?
  • 如果需要,是否有当前的过程来支持自定义类型参数?
  • 如果不需要,首选的方法是在langchain4j中直接实现一个轻量级的JsonSchema(类似于初始解决方案)还是使用现有的JsonSchema生成器(如其他选项)?
kmpatx3s

kmpatx3s1#

非常感谢!
一些想法:

  • 虽然支持将自定义POJO作为@Tool方法参数确实可以简化许多事情,但通常建议将模式展平以便于函数调用,因为LLM更容易遵循扁平模式而不是深嵌套模式。这可能在长期内不成立,所以仍然是一个很好的功能,但可能不是最高优先级。
  • 关于重塑轮子,我完全同意。当前实现有错误且缺乏许多功能。它最初是出于一个非常简单的用例而创建的,现在已经发展成为一个混乱的代码堆栈。我想查看现有库来委托此功能是个好主意。关于额外依赖问题:我们可以通过SPI将其作为单独的可插拔依赖项。因此,默认情况下,只执行现有的原始逻辑(不使用自定义POJO)。如果用户将自定义POJO定义为工具参数,则会抛出异常,提示“请添加langchain4j-tools-schema-generator依赖...”或类似内容。

WDYT?
部分在#708中解决
#859相关

djp7away

djp7away2#

你好@langchain4j,
我想知道你链接的问题是否也涵盖了列表参数。目前我遇到了错误
array schema missing items ...
解决方法是告诉AI将值作为单个字符串中的逗号分隔列表传递。

shyt4zoc

shyt4zoc3#

非常感谢!
一些想法:

  • 虽然支持将自定义POJO作为@Tool方法参数来简化开发人员的事情是正确的,但通常建议将模式展平以便于LLM遵循扁平化模式,而不是深嵌套的模式。这可能在长期内不成立,因此仍然是一个很好的功能,但可能不是最高优先级。
  • 关于重造轮子,我完全同意。当前的实现有bug并且缺乏很多功能。它最初是为了一个非常简单的用例而创建的,现在已经发展成为一个混乱的代码堆栈。我想查看现有的库来委托这个功能是个好主意。关于额外依赖的问题:我们可以通过SPI将其作为单独的可插拔依赖项。因此,默认情况下只执行现有的基本逻辑(没有自定义POJO)。如果用户将自定义POJO定义为工具参数,则会抛出异常,提示“请添加langchain4j-tools-schema-generator依赖...”或类似内容。

WDYT?
部分在#708中解决与#859相关
感谢您的出色工作!抱歉给您带来了延迟。我认为SPI解决方案非常好。我已经提交了一个PR,将ToolSpecification生成和ToolExecutionRequest反序列化委托给ToolJsonSchemas,并使用JsonSchemaService实现了VictoolsJsonSchemaService,该实现使用victools/jsonschema-generator提供了更完整的JSON模式功能。除了SPI之外,还可以通过AiServices#jsonSchemaServiceJsonSchemaService提供实现。

相关问题