Function Call 架构链式思维分析
问题核心
核心问题:在线DeepSeek模型如何与本地函数结合,实现智能函数调用?
链式思维分析
第一步:理解Function Call的本质
1.1 Function Call是什么?
Function Call是一种AI模型与外部函数交互的机制:
- AI模型:负责理解用户意图,决定调用哪个函数
- 外部函数:负责执行具体的业务逻辑,返回结果
- 桥梁:通过标准化的接口实现两者之间的通信
1.2 为什么需要Function Call?
- AI模型的局限性:大模型无法直接访问实时数据、执行计算、调用外部API
- 本地函数的优势:可以访问本地资源、执行复杂计算、调用真实API
- 结合的价值:AI的智能理解 + 本地函数的执行能力 = 完整的智能系统
第二步:分析当前架构设计
2.1 整体架构图
用户输入 → 本地服务器 → DeepSeek API → 本地函数 → 结果整合 → 最终回复
2.2 详细流程分析
阶段1:用户输入处理
// 用户输入:"查询北京的天气"
FunctionCallRequestDTO request = new FunctionCallRequestDTO();
request.setUserInput("查询北京的天气");
阶段2:构建Function Call请求
// 关键:将本地函数定义发送给在线模型
Map<String, Object> functionCallRequest = buildFunctionCallRequest(request);
// 包含:
// - 系统消息:指导模型使用函数
// - 用户消息:原始输入
// - 函数定义:本地函数的描述和参数
// - 调用策略:auto
阶段3:在线模型决策
// DeepSeek API接收请求,分析用户意图
Map<String, Object> apiResponse = callDeepSeekAPI(functionCallRequest);
// 模型返回:
// - 是否需要调用函数
// - 调用哪个函数
// - 函数参数是什么
阶段4:本地函数执行
// 如果模型决定调用函数
if (result.getFunctionCalled()) {
// 在本地执行实际的函数
Map<String, Object> functionResult = executeFunction(
result.getFunctionName(),
result.getFunctionArguments()
);
}
阶段5:结果整合
// 将函数执行结果发送回模型,生成最终回复
String finalContent = callDeepSeekAPIWithFunctionResult(
request.getUserInput(),
result.getFunctionName(),
functionResult
);
第三步:深入理解结合机制
3.1 函数定义的作用
在线模型需要知道什么?
// 函数定义告诉模型:
{
"name": "get_weather",
"description": "获取指定城市的天气信息,包括温度、天气状况、湿度、风力等",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如:北京、上海、广州"
}
},
"required": ["city"]
}
}
关键理解:
- 模型不需要知道函数的具体实现
- 模型只需要知道函数的用途、参数、返回值格式
- 模型根据这些信息决定是否调用函数
3.2 本地函数执行机制
本地函数的特点:
private Map<String, Object> executeGetWeather(Map<String, Object> arguments) {
String city = (String) arguments.get("city");
// 本地函数可以:
// 1. 调用真实的天气API
// 2. 访问本地数据库
// 3. 执行复杂计算
// 4. 访问本地文件系统
// 5. 调用其他本地服务
Map<String, Object> weatherData = new HashMap<>();
weatherData.put("city", city);
weatherData.put("temperature", "25°C");
// ... 更多数据
return weatherData;
}
3.3 数据流转机制
数据流转图:
用户输入: "查询北京的天气"
↓
本地服务器构建请求
↓
发送给DeepSeek API:
{
"messages": [...],
"functions": [函数定义],
"function_call": "auto"
}
↓
DeepSeek返回:
{
"function_call": {
"name": "get_weather",
"arguments": "{\"city\":\"北京\"}"
}
}
↓
本地解析并执行函数
↓
函数返回结果: {"city":"北京","temperature":"25°C",...}
↓
发送给DeepSeek生成最终回复
↓
返回给用户: "根据查询结果,北京今天天气晴朗,温度25°C..."
第四步:关键技术点分析
4.1 函数定义标准化
为什么需要标准化?
- 在线模型需要理解函数的能力
- 需要统一的格式来描述函数
- 确保模型能正确解析参数
标准格式:
{
"name": "函数名称",
"description": "详细描述,包含触发条件",
"parameters": {
"type": "object",
"properties": {
"参数名": {
"type": "参数类型",
"description": "参数说明"
}
},
"required": ["必需参数列表"]
}
}
4.2 参数传递机制
参数提取:
// 从模型返回的JSON字符串中提取参数
JsonNode arguments = functionCall.get("arguments");
Map<String, Object> args = objectMapper.readValue(
arguments.asText(), Map.class
);
参数验证:
// 验证必需参数是否存在
String city = (String) arguments.get("city");
if (city == null) {
// 处理参数缺失情况
}
4.3 结果整合机制
函数结果格式:
// 函数返回结构化数据
Map<String, Object> functionResult = new HashMap<>();
functionResult.put("city", "北京");
functionResult.put("temperature", "25°C");
functionResult.put("weather", "晴天");
发送给模型:
// 将函数结果作为function消息发送给模型
messages.add(Map.of(
"role", "function",
"content", JSONUtil.toJsonStr(functionResult)
));
第五步:优势与挑战分析
5.1 架构优势
1. 职责分离
- 在线模型:专注于理解用户意图和生成自然语言回复
- 本地函数:专注于执行具体的业务逻辑和数据获取
2. 灵活性
- 可以轻松添加新的本地函数
- 可以集成各种外部API和服务
- 可以访问本地资源和数据库
3. 安全性
- 敏感操作在本地执行
- 可以控制数据访问权限
- 避免将敏感信息发送到外部
4. 实时性
- 本地函数可以获取实时数据
- 减少网络延迟
- 提高响应速度
5.2 技术挑战
1. 函数定义管理
- 需要维护函数定义的准确性
- 需要确保描述与实现一致
- 需要处理函数版本更新
2. 错误处理
- 模型可能返回错误的函数调用
- 本地函数可能执行失败
- 需要完善的错误恢复机制
3. 性能优化
- 两次API调用增加了延迟
- 需要优化函数执行效率
- 需要考虑缓存策略
4. 扩展性
- 函数数量增加时的管理
- 复杂参数的处理
- 多函数调用的协调
第六步:实际应用场景
6.1 天气查询场景
用户: "查询北京的天气"
模型: 识别需要调用get_weather函数
本地: 调用天气API获取真实数据
模型: 基于真实数据生成自然语言回复
用户: 获得准确的天气信息
6.2 数学计算场景
用户: "计算 2+3*4 的结果"
模型: 识别需要调用calculate函数
本地: 执行数学计算
模型: 基于计算结果生成回复
用户: 获得计算结果和解释
6.3 时间查询场景
用户: "现在几点了?"
模型: 识别需要调用get_time函数
本地: 获取系统当前时间
模型: 基于时间数据生成回复
用户: 获得当前时间信息
第七步:优化建议
7.1 函数定义优化
- 提供更详细的函数描述
- 包含具体的触发条件
- 添加参数示例和说明
7.2 错误处理增强
- 实现函数调用的重试机制
- 提供降级处理方案
- 完善错误日志记录
7.3 性能优化
- 实现函数结果缓存
- 优化API调用频率
- 考虑批量函数调用
7.4 扩展性设计
- 实现动态函数注册
- 支持函数组合调用
- 提供函数调用链
总结
Function Call架构的核心思想是:
- 在线模型负责理解:理解用户意图,决定调用策略
- 本地函数负责执行:执行具体业务逻辑,获取真实数据
- 标准化接口连接:通过统一的函数定义和调用格式实现连接
- 结果整合生成回复:将函数结果发送回模型,生成最终回复
这种架构实现了AI智能理解与本地执行能力的完美结合,为用户提供了既智能又实用的交互体验。
#具体java代码如下
package com.mybase.system.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybase.common.annotation.Log;
import com.mybase.common.model.R;
import com.mybase.common.utils.MyDateUtil;
import com.mybase.common.constant.PermissionConstants;
import com.mybase.system.config.FunctionCallConfig;
import com.mybase.system.dto.FunctionCallRequestDTO;
import com.mybase.system.dto.FunctionDefinitionDTO;
import com.mybase.system.vo.FunctionCallResultVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.*;
/**
* Function Call 控制器
* 基于DeepSeek大模型实现Function Call功能
*
* @author wangmingjie
* @since 2025-07-18
*/
@Slf4j
@RestController
@RequestMapping("/system/function-call")
@Tag(name = "Function Call", description = "基于DeepSeek大模型的Function Call功能")
public class FunctionCallController {
@Autowired
private FunctionCallConfig functionCallConfig;
@Autowired
private RestTemplate restTemplate;
@Autowired
private ObjectMapper objectMapper;
/**
* 预定义的函数定义列表
*/
private static final List<FunctionDefinitionDTO> PREDEFINED_FUNCTIONS = Arrays.asList(
// 天气查询函数
FunctionDefinitionDTO.builder()
.name("get_weather")
.description("获取指定城市的天气信息,包括温度、天气状况、湿度、风力等")
.parameters(Map.of(
"type", "object",
"properties", Map.of(
"city", Map.of(
"type", "string",
"description", "城市名称,如:北京、上海、广州"
),
"date", Map.of(
"type", "string",
"description", "查询日期,格式:YYYY-MM-DD,默认为今天"
)
),
"required", Arrays.asList("city")
))
.build(),
// 计算器函数
FunctionDefinitionDTO.builder()
.name("calculate")
.description("执行数学计算,支持加减乘除和基本数学运算")
.parameters(Map.of(
"type", "object",
"properties", Map.of(
"expression", Map.of(
"type", "string",
"description", "数学表达式,如:2+3*4、(10+5)/3"
)
),
"required", Arrays.asList("expression")
))
.build(),
// 时间查询函数
FunctionDefinitionDTO.builder()
.name("get_time")
.description("获取当前时间信息,当用户询问时间、几点、现在时间等问题时调用此函数")
.parameters(Map.of(
"type", "object",
"properties", Map.of(
"timezone", Map.of(
"type", "string",
"description", "时区,如:Asia/Shanghai、UTC、America/New_York,默认为Asia/Shanghai"
),
"format", Map.of(
"type", "string",
"description", "时间格式,如:yyyy-MM-dd HH:mm:ss、yyyy年MM月dd日,默认为yyyy-MM-dd HH:mm:ss"
)
),
"required", new ArrayList<>()
))
.build(),
// 翻译函数
FunctionDefinitionDTO.builder()
.name("translate")
.description("文本翻译功能,支持多种语言之间的翻译")
.parameters(Map.of(
"type", "object",
"properties", Map.of(
"text", Map.of(
"type", "string",
"description", "要翻译的文本"
),
"source_lang", Map.of(
"type", "string",
"description", "源语言,如:zh、en、ja、ko"
),
"target_lang", Map.of(
"type", "string",
"description", "目标语言,如:zh、en、ja、ko"
)
),
"required", Arrays.asList("text", "target_lang")
))
.build()
);
//https://api-docs.deepseek.com/zh-cn/guides/json_mode
//deepseek DeepSeek 提供了 JSON Output 功能,来确保模型输出合法的 JSON 字符串。
/**
* 执行Function Call。参考https://api-docs.deepseek.com/zh-cn/guides/function_calling
*
*/
@Log(module = "Function Call", operationType = "执行", description = "执行Function Call")
@Operation(summary = "执行Function Call", description = "基于DeepSeek大模型执行Function Call")
@PostMapping("/execute")
@SaCheckPermission(PermissionConstants.SYSTEM_FUNCTION_CALL_EXECUTE)
public R<FunctionCallResultVO> executeFunctionCall(@RequestBody FunctionCallRequestDTO request) {
try {
log.info("开始执行Function Call,用户输入:{}", request.getUserInput());
// 参数验证
if (StrUtil.isBlank(request.getUserInput())) {
return R.fail("用户输入不能为空");
}
// 记录开始时间
long startTime = System.currentTimeMillis();
// 构建Function Call请求
Map<String, Object> functionCallRequest = buildFunctionCallRequest(request);
// 第一次调用DeepSeek API - 判断是否需要调用函数
Map<String, Object> apiResponse = callDeepSeekAPIReal(functionCallRequest);
// 解析响应
FunctionCallResultVO result = parseFunctionCallResponse(apiResponse);
// 如果调用了函数,执行函数并获取结果
if (result.getFunctionCalled()) {
Map<String, Object> functionResult = executeFunction(result.getFunctionName(), result.getFunctionArguments());
result.setFunctionResult(functionResult);
// 第二次调用DeepSeek API - 将函数结果发送给模型生成最终回复
String finalContent = callDeepSeekAPIWithFunctionResult(
request, // 传递第一次调用的完整请求
apiResponse, // 传递第一次调用的完整响应
result,
functionResult
);
result.setContent(finalContent);
}
long endTime = System.currentTimeMillis();
result.setResponseTime((int) (endTime - startTime));
result.setModel(functionCallConfig.getDeepseek().getModelName());
log.info("Function Call执行完成,耗时:{}ms,函数调用:{}",
result.getResponseTime(), result.getFunctionCalled());
return R.ok(result);
} catch (Exception e) {
log.error("Function Call执行失败", e);
FunctionCallResultVO errorResult = new FunctionCallResultVO();
errorResult.setSuccess(false);
errorResult.setErrorMessage("Function Call执行失败: " + e.getMessage());
return R.ok(errorResult);
}
}
/**
* 测试Function Call连接
*/
@Operation(summary = "测试连接", description = "测试DeepSeek API连接状态")
@PostMapping("/test")
@SaCheckPermission(PermissionConstants.SYSTEM_FUNCTION_CALL_EXECUTE)
public R<Map<String, Object>> testConnection() {
try {
log.info("开始测试DeepSeek API连接");
// 构建简单的测试请求
Map<String, Object> testRequest = new HashMap<>();
testRequest.put("model", functionCallConfig.getDeepseek().getModelName());
testRequest.put("messages", Arrays.asList(
Map.of("role", "user", "content", "你好,请简单回复一下")
));
testRequest.put("max_tokens", 50);
testRequest.put("temperature", 0.7);
// 调用API
Map<String, Object> response = callDeepSeekAPIReal(testRequest);
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("message", "连接测试成功");
result.put("model", functionCallConfig.getDeepseek().getModelName());
result.put("response", response.get("content"));
log.info("DeepSeek API连接测试成功");
return R.ok(result);
} catch (Exception e) {
log.error("DeepSeek API连接测试失败", e);
Map<String, Object> result = new HashMap<>();
result.put("success", false);
result.put("message", "连接测试失败: " + e.getMessage());
return R.ok(result);
}
}
/**
* 获取配置信息
*/
@Operation(summary = "获取配置", description = "获取Function Call配置信息")
@GetMapping("/config")
@SaCheckPermission(PermissionConstants.SYSTEM_FUNCTION_CALL_CONFIG)
public R<Map<String, Object>> getConfig() {
Map<String, Object> config = new HashMap<>();
config.put("apiUrl", functionCallConfig.getDeepseek().getApiUrl());
config.put("modelName", functionCallConfig.getDeepseek().getModelName());
config.put("timeoutMs", functionCallConfig.getDeepseek().getTimeoutMs());
config.put("maxTokens", functionCallConfig.getDeepseek().getMaxTokens());
config.put("temperature", functionCallConfig.getDeepseek().getTemperature());
config.put("functionCount", PREDEFINED_FUNCTIONS.size());
return R.ok(config);
}
/**
* 获取预定义函数列表
*/
@Operation(summary = "获取函数列表", description = "获取所有预定义的函数列表")
@GetMapping("/functions")
@SaCheckPermission(PermissionConstants.SYSTEM_FUNCTION_CALL_CONFIG)
public R<List<FunctionDefinitionDTO>> getFunctions() {
return R.ok(PREDEFINED_FUNCTIONS);
}
/**
* 构建Function Call请求
*/
private Map<String, Object> buildFunctionCallRequest(FunctionCallRequestDTO request) {
Map<String, Object> requestBody = new HashMap<>();
// 设置模型
requestBody.put("model", functionCallConfig.getDeepseek().getModelName());
// 构建消息 - 添加系统消息来指导模型使用函数
List<Map<String, Object>> messages = new ArrayList<>();
// 添加系统消息,指导模型使用函数
messages.add(Map.of(
"role", "system",
"content", "你是一个智能助手,支持Function Call功能。当用户询问时间、天气、计算、翻译等问题时,你必须使用Function Call格式调用相应的函数,而不是在回复中提及函数。\n\n重要规则:\n1. 当用户询问时间相关问题时,必须调用get_time函数\n2. 当用户询问天气相关问题时,必须调用get_weather函数\n3. 当用户询问计算相关问题时,必须调用calculate函数\n4. 当用户询问翻译相关问题时,必须调用translate函数\n\n不要在任何情况下在回复中提及函数名称或代码,直接使用Function Call格式调用函数。"
));
// 添加用户消息
messages.add(Map.of("role", "user", "content", request.getUserInput()));
requestBody.put("messages", messages);
// 设置工具定义 - 使用tools而不是functions,符合DeepSeek API格式
List<Map<String, Object>> tools = new ArrayList<>();
for (FunctionDefinitionDTO function : PREDEFINED_FUNCTIONS) {
Map<String, Object> toolDef = new HashMap<>();
toolDef.put("type", "function");
Map<String, Object> functionDef = new HashMap<>();
functionDef.put("name", function.getName());
functionDef.put("description", function.getDescription());
functionDef.put("parameters", function.getParameters());
toolDef.put("function", functionDef);
tools.add(toolDef);
}
requestBody.put("tools", tools);
// 设置工具调用策略 - 让模型自动决定是否调用函数
requestBody.put("tool_choice", "auto");
// 设置其他参数
requestBody.put("max_tokens", functionCallConfig.getDeepseek().getMaxTokens());
requestBody.put("temperature", functionCallConfig.getDeepseek().getTemperature());
requestBody.put("stream", false);
log.debug("构建的Function Call请求: {}", requestBody);
return requestBody;
}
/**
* 调用真实的DeepSeek API
*/
private Map<String, Object> callDeepSeekAPIReal(Map<String, Object> requestBody) throws Exception {
log.debug("调用DeepSeek API,URL: {}, Model: {}",
functionCallConfig.getDeepseek().getApiUrl(), functionCallConfig.getDeepseek().getModelName());
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
if (StrUtil.isNotBlank(functionCallConfig.getDeepseek().getApiKey())) {
headers.setBearerAuth(functionCallConfig.getDeepseek().getApiKey());
}
// 发送请求
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.postForEntity(
functionCallConfig.getDeepseek().getApiUrl(), entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
String responseBody = response.getBody();
log.debug("callDeepSeekAPIReal DeepSeek API响应:{}", responseBody);
JsonNode jsonResponse = objectMapper.readTree(responseBody);
Map<String, Object> result = new HashMap<>();
// 提取回答内容
JsonNode choices = jsonResponse.get("choices");
if (choices != null && choices.isArray() && choices.size() > 0) {
JsonNode firstChoice = choices.get(0);
JsonNode message = firstChoice.get("message");
if (message != null) {
result.put("content", message.get("content").asText(""));
// 检查是否有工具调用 - 使用tool_calls而不是function_call
JsonNode toolCalls = message.get("tool_calls");
if (toolCalls != null && toolCalls.isArray() && toolCalls.size() > 0) {
log.info("检测到工具调用: {}", toolCalls.toString());
result.put("tool_calls", toolCalls);
// 提取第一个工具调用的ID
JsonNode firstToolCall = toolCalls.get(0);
if (firstToolCall.has("id")) {
result.put("tool_call_id", firstToolCall.get("id").asText());
}
} else {
log.info("未检测到工具调用,模型直接回复");
}
}
}
// 提取使用统计
JsonNode usage = jsonResponse.get("usage");
if (usage != null) {
Map<String, Object> usageMap = new HashMap<>();
usageMap.put("prompt_tokens", usage.get("prompt_tokens").asInt());
usageMap.put("completion_tokens", usage.get("completion_tokens").asInt());
usageMap.put("total_tokens", usage.get("total_tokens").asInt());
result.put("usage", usageMap);
}
return result;
} else {
throw new RuntimeException("DeepSeek API调用失败,状态码:" + response.getStatusCode());
}
}
/**
* 从用户输入中提取城市名称
*/
private String extractCityFromInput(String userInput) {
// 简单的城市提取逻辑
String[] cities = {"北京", "上海", "广州", "深圳", "杭州", "南京", "武汉", "成都", "西安", "重庆"};
for (String city : cities) {
if (userInput.contains(city)) {
return city;
}
}
return "北京"; // 默认返回北京
}
/**
* 从用户输入中提取数学表达式
*/
private String extractExpressionFromInput(String userInput) {
// 简单的表达式提取逻辑
if (userInput.contains("计算")) {
int start = userInput.indexOf("计算") + 2;
int end = userInput.indexOf("的结果");
if (end > start) {
return userInput.substring(start, end).trim();
}
}
// 提取数字和运算符
return userInput.replaceAll("[^0-9+\\-*/()]", "").trim();
}
/**
* 从用户输入中提取要翻译的文本
*/
private String extractTextFromInput(String userInput) {
// 简单的文本提取逻辑
if (userInput.contains("把") && userInput.contains("翻译")) {
int start = userInput.indexOf("把") + 1;
int end = userInput.indexOf("翻译");
if (end > start) {
return userInput.substring(start, end).replaceAll("['\"]", "").trim();
}
}
return "你好世界"; // 默认文本
}
/**
* 从用户输入中提取目标语言
*/
private String extractTargetLangFromInput(String userInput) {
if (userInput.contains("英文") || userInput.contains("英语")) {
return "en";
} else if (userInput.contains("中文") || userInput.contains("汉语")) {
return "zh";
}
return "en"; // 默认英文
}
/**
* 解析Function Call响应
*/
private FunctionCallResultVO parseFunctionCallResponse(Map<String, Object> apiResponse) {
FunctionCallResultVO result = new FunctionCallResultVO();
result.setSuccess(true);
result.setContent((String) apiResponse.get("content"));
result.setUsage((Map<String, Object>) apiResponse.get("usage"));
// 检查是否有工具调用 - 使用tool_calls而不是function_call
Object toolCallsObj = apiResponse.get("tool_calls");
if (toolCallsObj != null) {
result.setFunctionCalled(true);
if (toolCallsObj instanceof JsonNode) {
// 真实API返回的JsonNode
JsonNode toolCalls = (JsonNode) toolCallsObj;
if (toolCalls.isArray() && toolCalls.size() > 0) {
JsonNode firstToolCall = toolCalls.get(0);
JsonNode function = firstToolCall.get("function");
if (function != null) {
result.setFunctionName(function.get("name").asText());
// 解析函数参数
JsonNode arguments = function.get("arguments");
if (arguments != null) {
try {
Map<String, Object> args = objectMapper.readValue(
arguments.asText(), Map.class);
result.setFunctionArguments(args);
} catch (Exception e) {
log.warn("解析函数参数失败", e);
result.setFunctionArguments(new HashMap<>());
}
}
// 保存tool_call_id
if (firstToolCall.has("id")) {
result.setToolCallId(firstToolCall.get("id").asText());
}
}
}
} else if (toolCallsObj instanceof List) {
// 模拟模式返回的List
List<Map<String, Object>> toolCalls = (List<Map<String, Object>>) toolCallsObj;
if (!toolCalls.isEmpty()) {
Map<String, Object> firstToolCall = toolCalls.get(0);
Map<String, Object> function = (Map<String, Object>) firstToolCall.get("function");
if (function != null) {
result.setFunctionName((String) function.get("name"));
// 解析函数参数
String arguments = (String) function.get("arguments");
if (arguments != null) {
try {
Map<String, Object> args = objectMapper.readValue(arguments, Map.class);
result.setFunctionArguments(args);
} catch (Exception e) {
log.warn("解析函数参数失败", e);
result.setFunctionArguments(new HashMap<>());
}
}
// 保存tool_call_id
result.setToolCallId((String) firstToolCall.get("id"));
}
}
}
log.info("解析到函数调用: {},参数: {},tool_call_id: {}",
result.getFunctionName(), result.getFunctionArguments(), result.getToolCallId());
} else {
result.setFunctionCalled(false);
log.info("未解析到函数调用");
}
return result;
}
/**
* 执行函数调用
*/
private Map<String, Object> executeFunction(String functionName, Map<String, Object> arguments) {
log.info("执行函数:{},参数:{}", functionName, arguments);
try {
switch (functionName) {
case "get_weather":
return executeGetWeather(arguments);
case "calculate":
return executeCalculate(arguments);
case "get_time":
return executeGetTime(arguments);
case "translate":
return executeTranslate(arguments);
default:
log.warn("未知函数:{}", functionName);
return new HashMap<>(); // 返回空Map表示函数执行失败
}
} catch (Exception e) {
log.error("执行函数失败:{}", functionName, e);
Map<String, Object> errorResult = new HashMap<>();
errorResult.put("success", false);
errorResult.put("message", "函数执行失败: " + e.getMessage());
return errorResult;
}
}
/**
* 执行天气查询函数
*/
private Map<String, Object> executeGetWeather(Map<String, Object> arguments) {
String city = (String) arguments.get("city");
String date = (String) arguments.get("date");
log.info("查询天气:城市={},日期={}", city, date);
// 这里可以集成真实的天气API
// 目前返回模拟数据
Map<String, Object> weatherData = new HashMap<>();
weatherData.put("city", city);
weatherData.put("date", date != null ? date : "今天");
weatherData.put("temperature", "25°C");
weatherData.put("weather", "晴天");
weatherData.put("humidity", "60%");
weatherData.put("wind", "东南风 3级");
log.info("天气查询结果:{}", weatherData);
return weatherData;
}
/**
* 执行计算器函数
*/
private Map<String, Object> executeCalculate(Map<String, Object> arguments) {
String expression = (String) arguments.get("expression");
log.info("执行计算:表达式={}", expression);
try {
// 这里可以集成更安全的表达式计算库
// 目前使用简单的示例
double result = evaluateExpression(expression);
Map<String, Object> calcResult = new HashMap<>();
calcResult.put("expression", expression);
calcResult.put("result", result);
log.info("计算结果:{}", calcResult);
return calcResult;
} catch (Exception e) {
log.error("计算失败:{}", expression, e);
Map<String, Object> errorResult = new HashMap<>();
errorResult.put("success", false);
errorResult.put("message", "计算失败: " + e.getMessage());
return errorResult;
}
}
/**
* 执行时间查询函数
*/
private Map<String, Object> executeGetTime(Map<String, Object> arguments) {
String timezone = (String) arguments.get("timezone");
String format = (String) arguments.get("format");
log.info("查询时间:时区={},格式={}", timezone, format);
// 这里可以集成真实的时间API
// 目前返回模拟数据
Map<String, Object> timeData = new HashMap<>();
timeData.put("timezone", timezone != null ? timezone : "Asia/Shanghai");
timeData.put("format", format != null ? format : "yyyy-MM-dd HH:mm:ss");
timeData.put("current_time", MyDateUtil.getDateParseTime());
timeData.put("timestamp", System.currentTimeMillis());
log.info("时间查询结果:{}", timeData);
return timeData;
}
/**
* 执行翻译函数
*/
private Map<String, Object> executeTranslate(Map<String, Object> arguments) {
String text = (String) arguments.get("text");
String sourceLang = (String) arguments.get("source_lang");
String targetLang = (String) arguments.get("target_lang");
log.info("执行翻译:文本={},源语言={},目标语言={}", text, sourceLang, targetLang);
// 这里可以集成真实的翻译API
// 目前返回模拟数据
Map<String, Object> translateResult = new HashMap<>();
translateResult.put("original_text", text);
translateResult.put("source_language", sourceLang != null ? sourceLang : "auto");
translateResult.put("target_language", targetLang);
translateResult.put("translated_text", "这是翻译后的文本");
translateResult.put("confidence", 0.95);
log.info("翻译结果:{}", translateResult);
return translateResult;
}
/**
* 简单的表达式计算(仅用于演示)
*/
private double evaluateExpression(String expression) {
// 这里应该使用更安全的表达式计算库
// 目前只是简单的示例实现
if (expression.contains("+")) {
String[] parts = expression.split("\\+");
return Double.parseDouble(parts[0].trim()) + Double.parseDouble(parts[1].trim());
} else if (expression.contains("-")) {
String[] parts = expression.split("-");
return Double.parseDouble(parts[0].trim()) - Double.parseDouble(parts[1].trim());
} else if (expression.contains("*")) {
String[] parts = expression.split("\\*");
return Double.parseDouble(parts[0].trim()) * Double.parseDouble(parts[1].trim());
} else if (expression.contains("/")) {
String[] parts = expression.split("/");
return Double.parseDouble(parts[0].trim()) / Double.parseDouble(parts[1].trim());
} else {
return Double.parseDouble(expression.trim());
}
}
/**
* 第二次调用DeepSeek API,将函数结果发送给模型生成最终回复
*/
private String callDeepSeekAPIWithFunctionResult(FunctionCallRequestDTO request, Map<String, Object> apiResponse, FunctionCallResultVO result, Map<String, Object> functionResult) {
log.debug("第二次调用DeepSeek API,发送函数结果给模型生成最终回复");
// 构建完整的消息列表,包含完整的对话历史
List<Map<String, Object>> messages = new ArrayList<>();
// 1. 添加第一次调用的所有消息(系统消息 + 用户消息)
// List<Map<String, Object>> originalMessages = (List<Map<String, Object>>) functionCallRequest.get("messages");
// messages.addAll(originalMessages);
// 1、添加用户消息 (不需要系统消息)
messages.add(Map.of("role", "user", "content", request.getUserInput()));
// 2. 添加assistant的tool_calls消息
Object toolCallsObj = apiResponse.get("tool_calls");
if (toolCallsObj != null) {
Map<String, Object> assistantMessage = new HashMap<>();
assistantMessage.put("role", "assistant");
assistantMessage.put("content", ""); // 确保content为空字符串
assistantMessage.put("tool_calls", toolCallsObj);
messages.add(assistantMessage);
}
// 3. 添加tool消息(函数结果)- 根据OpenAI示例,tool消息不应该包含name字段
Map<String, Object> toolMessage = new HashMap<>();
toolMessage.put("role", "tool");
toolMessage.put("tool_call_id", result.getToolCallId());
// 移除name字段,只保留tool_call_id和content
toolMessage.put("content", JSONUtil.toJsonStr(functionResult));
messages.add(toolMessage);
// 构建请求体
Map<String, Object> requestBody = new HashMap<>();
// // 设置工具定义 - 使用tools而不是functions,符合DeepSeek API格式
// List<Map<String, Object>> tools = new ArrayList<>();
// for (FunctionDefinitionDTO function : PREDEFINED_FUNCTIONS) {
// Map<String, Object> toolDef = new HashMap<>();
// toolDef.put("type", "function");
//
// Map<String, Object> functionDef = new HashMap<>();
// functionDef.put("name", function.getName());
// functionDef.put("description", function.getDescription());
// functionDef.put("parameters", function.getParameters());
//
// toolDef.put("function", functionDef);
// tools.add(toolDef);
// }
// requestBody.put("tools", tools);
requestBody.put("model", functionCallConfig.getDeepseek().getModelName());
requestBody.put("messages", messages);
requestBody.put("max_tokens", functionCallConfig.getDeepseek().getMaxTokens());
requestBody.put("temperature", functionCallConfig.getDeepseek().getTemperature());
requestBody.put("stream", false);
log.debug("第二次调用请求体: {}", requestBody);
try {
return callDeepSeekAPIRealWithFunctionResult(requestBody);
} catch (Exception e) {
log.error("第二次DeepSeek API调用失败", e);
return "抱歉,我无法生成最终回复。";
}
}
/**
* 调用真实的DeepSeek API,发送函数结果
*/
private String callDeepSeekAPIRealWithFunctionResult(Map<String, Object> requestBody) throws Exception {
log.debug("调用DeepSeek API,发送函数结果,URL: {}, Model: {}",
functionCallConfig.getDeepseek().getApiUrl(), functionCallConfig.getDeepseek().getModelName());
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
if (StrUtil.isNotBlank(functionCallConfig.getDeepseek().getApiKey())) {
headers.setBearerAuth(functionCallConfig.getDeepseek().getApiKey());
}
// 发送请求
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.postForEntity(
functionCallConfig.getDeepseek().getApiUrl(), entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
String responseBody = response.getBody();
log.debug(" callDeepSeekAPIRealWithFunctionResult DeepSeek API响应:{}", responseBody);
JsonNode jsonResponse = objectMapper.readTree(responseBody);
JsonNode choices = jsonResponse.get("choices");
if (choices != null && choices.isArray() && choices.size() > 0) {
JsonNode firstChoice = choices.get(0);
JsonNode message = firstChoice.get("message");
if (message != null) {
return message.get("content").asText("");
}
}
} else {
throw new RuntimeException("DeepSeek API调用失败,状态码:" + response.getStatusCode());
}
return "抱歉,我无法生成最终回复。";
}
}