岗位清洗,差岗位技能提取

This commit is contained in:
zk
2026-03-18 21:33:07 +08:00
parent f0eec5fe3e
commit 6bddeecb7a
12 changed files with 997 additions and 7 deletions
@@ -0,0 +1,77 @@
package org.jiayunet.ai;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.jiayunet.tool.HttpTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* AI 对话能力封装(OpenAI 兼容)
* <p>支持多供应商配置,不传 key 时使用第一个 provider</p>
*
* @author zk
*/
@Slf4j
@Component
public class AiChatAbility {
@Autowired
private AiChatConfig aiChatConfig;
/**
* 使用默认 provider 发送对话
* <p>默认取 providers 配置中的第一个</p>
*/
public String chat(String systemPrompt, String userMessage) {
String defaultKey = aiChatConfig.getProviders().keySet().stream()
.findFirst()
.orElseThrow(() -> new RuntimeException("未配置任何 AI provider"));
return chat(defaultKey, systemPrompt, userMessage);
}
/**
* 使用指定 provider 发送对话
*
* @param providerKey 供应商标识,对应 yml 中 providers 的 key
* @param systemPrompt 系统提示词
* @param userMessage 用户消息
* @return AI 返回的文本内容
*/
public String chat(String providerKey, String systemPrompt, String userMessage) {
AiChatConfig.ProviderConfig config = aiChatConfig.getProviders().get(providerKey);
if (config == null) {
throw new RuntimeException("AI provider 不存在: " + providerKey);
}
String url = config.getBaseUrl() + "/chat/completions";
log.info("AI 请求 URL: {}, model: {}", url, config.getModel());
Map<String, Object> body = new HashMap<>();
body.put("model", config.getModel());
body.put("messages", List.of(
Map.of("role", "system", "content", systemPrompt),
Map.of("role", "user", "content", userMessage)
));
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + config.getApiKey());
try {
String response = HttpTool.sendJsonPost(body, url, headers);
JsonNode root = HttpTool.objectMapper.readTree(response);
String content = root.path("choices").path(0).path("message").path("content").asText();
if (content == null || content.isBlank()) {
throw new RuntimeException("AI 返回内容为空");
}
return content;
} catch (Exception e) {
log.error("AI 调用失败, provider={}, model={}", providerKey, config.getModel(), e);
throw new RuntimeException("AI 调用失败: " + e.getMessage(), e);
}
}
}
@@ -0,0 +1,33 @@
package org.jiayunet.ai;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* AI 多供应商配置
* <p>读取 app.ai.providers,第一个为默认 provider</p>
*
* @author zk
*/
@Data
@Component
@ConfigurationProperties(prefix = "app.ai")
public class AiChatConfig {
/** 供应商配置,key 为供应商标识,第一个为默认 */
private Map<String, ProviderConfig> providers = new LinkedHashMap<>();
@Data
public static class ProviderConfig {
/** API 地址 */
private String baseUrl;
/** API Key */
private String apiKey;
/** 模型名称 */
private String model;
}
}
@@ -76,7 +76,7 @@ public class HttpTool {
if (response.getStatusLine().getStatusCode() == 200) {
return bodyStr;
}
throw new IOException("Http请求出现错误,响应码:" + response.getStatusLine().getStatusCode());
throw new IOException("Http请求出现错误,响应码:" + response.getStatusLine().getStatusCode() + ",响应体:" + bodyStr);
}
} catch (IOException e) {