Langchain4j Function Calling(函数调用)
Function Calling 函数调用 也叫 Tools 工具.
不用函数调用存在的问题
测试计算问题
-
提供agent
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "qwenChatModel",chatMemory = "chatMemory",chatMemoryProvider = "chatMemoryProvider" ) public interface SeparateChatAssistant {@SystemMessage(fromResource = "systemMessage.txt")String chat(@MemoryId int memoryId, @UserMessage String userMessage);}
-
进行数学计算测试
@Resource private SeparateChatAssistant separateChatAssistant;@Test public void testCalculatorTools() {String answer = separateChatAssistant.chat(1, "1+2等于几,322233222345的平方根是多少?");//答案:567,655.901System.out.println(answer); }
根据模型回复,发现简单计算,模型是可以正常处理的,但是对于复杂计算就无能为力了。
函数调用
创建函数调用tools
@Component
public class CalculatorTools {@Tooldouble sum(double a, double b) {System.out.println("调用加法运算");return a + b;}@Tooldouble squareRoot(double x) {System.out.println("调用平方根运算");return Math.sqrt(x);}}
agent 使用tools
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "qwenChatModel",chatMemory = "chatMemory",chatMemoryProvider = "chatMemoryProvider",tools = "calculatorTools"
)
public interface SeparateChatAssistant {@SystemMessage(fromResource = "systemMessage.txt")String chat(@MemoryId int memoryId, @UserMessage String userMessage);}
进行数学计算测试
同样使用上面 出问题的测试用例,来对tools进行测试。
@Resourceprivate SeparateChatAssistant separateChatAssistant;@Testpublic void testCalculatorTools() {String answer = separateChatAssistant.chat(1, "1+2等于几,322233222345的平方根是多少?");//答案:567,655.901System.out.println(answer);}
根据模型输出,我们看到了在Tools
工具中输出的日志,发现模型的确调用了我们的工具,同时也看到了数据开方也返回了正确的结果。
函数调用流程
查看存储的记录信息
我们可以明显的看到type=AI
的记录信息,在toolExecutionRequests
中记录着我们的tools中的函数信息。
分析调用流程
Request:\- messages:\- SystemMessage:\- text: 系统定义AI的角色\- UserMessage:\- text: 用户提问\- AiMessage:\- toolExecutionRequests:\- ai获取提问信息组织参数调用工具方法\- ToolExecutionResultMessage:\- text: 工具方法执行
Response :\- AiMessage:\- text: 根据工具方法的执行ai再次组织结果返回
注解信息
-
@Tool注解
@Tool 注解有两个可选字段:
name:工具的名称。如果未提供该字段,方法名会作为工具的名称。
value:工具的描述信息。
根据工具的不同,即使没有任何描述,大语言模型可能也能很好地理解它(例如, add(a, b) 就很直观),但通常最好提供清晰且有意义的名称和描述。这样,大语言模型就能获得更多信息,以决定是否调用给定的工具以及如何调用。
@Tool(name = "sum", value = "sum(a,b)计算a和b的和") double sum(double a, double b) {System.out.println("调用加法运算");return a + b; }@Tool(name = "squareRoot", value = "squareRoot(x)计算x的平方根") double squareRoot(double x) {System.out.println("调用平方根运算");return Math.sqrt(x); }
-
@P 注解
方法参数可以选择使用 @P 注解进行标注。
@P 注解有两个字段:
value:参数的描述信息,这是必填字段。
required:表示该参数是否为必需项,默认值为 true ,此为可选字段。
@Tool(name = "sum", value = "sum(a,b)计算a和b的和") double sum(@P(value = "加数1",required = true) double a,@P(value = "加数2",required = true)double b) {System.out.println("调用加法运算");return a + b; }@Tool(name = "squareRoot", value = "squareRoot(x)计算x的平方根") double squareRoot(@P(value = "平方根数",required = true) double x) {System.out.println("调用平方根运算");return Math.sqrt(x); }
-
@ToolMemoryId 注解
如果你的AIService方法中有一个参数使用 @MemoryId 注解,那么你也可以使用 @ToolMemoryId 注解@Tool 方法中的一个参数。提供给AIService方法的值将自动传递给 @Tool 方法。如果你有多个用户,或每个用户有多个聊天记忆,并且希望在 @Tool 方法中对它们进行区分,那么这个功能会很有用。
@Tool(name = "sum", value = "sum(a,b)计算a和b的和") double sum(@ToolMemoryId int memoryId,@P(value = "加数1",required = true) double a,@P(value = "加数2",required = true)double b) {System.out.println("调用加法运算,memoryId: " + memoryId);return a + b; }@Tool(name = "squareRoot", value = "squareRoot(x)计算x的平方根") double squareRoot(@ToolMemoryId int memoryId,@P(value = "平方根数",required = true) double x) {System.out.println("调用平方根运算,memoryId: " + memoryId);return Math.sqrt(x); }
可以明显的看到
@ToolMemoryId
标记的字段,成功的拿到了请求的memoryId
.