系列文章:
AI Model Hub:一个高效的大模型应用开发集成框架(1)
AI Model Hub:一个高效的大模型应用开发集成框架(2)- 对接本地llama3
AI Model Hub:一个高效的大模型应用开发集成框架(3)- 存储设计
设计思路
使用salt-function-flow流程编排框架,将原来的LLM过程,包括:Agent配置获取、历史对话上下文构建、调用模型、存储对话记录等过程,重构进行编排串联,具体实现如下:
ai-model-hub github:https://github.com/flower-trees/ai-model-hub
salt-function-flow github:https://github.com/flower-trees/salt-function-flow
加入引用
<dependency>
<groupId>cn.fenglingsoftware</groupId>
<artifactId>salt-function-flow</artifactId>
<version>1.0.2</version>
</dependency>
定义节点功能
通用参数构建
@NodeIdentity(nodeId = "paramBuilder")
public class ParamBuilder extends FlowNode {
@Override
public void process(IContextBus iContextBus) {
//Init param
AiChatRequest aiChatRequest = (AiChatRequest) iContextBus.getParam();
AiChatDto aiChatDto = new AiChatDto();
aiChatDto.setId(aiChatRequest.getId());
aiChatDto.setSession(aiChatRequest.getSession());
aiChatDto.setAgent(aiChatRequest.getAgent());
if (aiChatDto.getMessages() == null) {
aiChatDto.setMessages(new ArrayList<>());
}
iContextBus.putTransmitInfo(AiChatDto.class.getName(), aiChatDto);
}
}
Agent配置构建节点
@NodeIdentity(nodeId = "agentConfiger")
public class AgentConfiger extends FlowNodeWithReturn<AiChatDto> {
@Autowired
AgentService agentService;
@Override
public AiChatDto doProcess(IContextBus iContextBus) {
//Init param
AiChatRequest aiChatRequest = (AiChatRequest) iContextBus.getParam();
AiChatDto aiChatDto = (AiChatDto) iContextBus.getTransmitInfo(AiChatDto.class.getName());
AgentVo agentVo = agentService.load(aiChatRequest.getAgent());
AgentVo.Configs configs = JsonUtil.fromJson(agentVo.getConfigs(), AgentVo.Configs.class);
assert configs != null;
aiChatDto.setVendor(configs.getVendor());
aiChatDto.setModel(configs.getModel());
return aiChatDto;
}
}
对话上下文构建节点
@NodeIdentity(nodeId = "contextBuilder")
public class ContextBuilder extends FlowNodeWithReturn<AiChatDto> {
@Autowired
ChatService chatService;
@Override
public AiChatDto doProcess(IContextBus iContextBus) {
//Init param
AiChatRequest aiChatRequest = (AiChatRequest) iContextBus.getParam();
AiChatDto aiChatDto = (AiChatDto) iContextBus.getTransmitInfo(AiChatDto.class.getName());
//Query and Add Context
List<ChatVo> chatVoList = chatService.queryLastList(aiChatRequest.getSession(), 3);
if (!CollectionUtils.isEmpty(chatVoList)) {
chatVoList.forEach(chatVo -> {
AiChatDto.Message messageUser = new AiChatDto.Message();
messageUser.setRole(RoleType.USER.getCode());
messageUser.setContent(chatVo.getQuestion());
aiChatDto.getMessages().add(messageUser);
List<AiChatResponse.Message> messages = JsonUtil.fromJson(chatVo.getAnswer(), new TypeReference<>() {});
if (!CollectionUtils.isEmpty(messages)) {
AiChatResponse.Message messageMarkdown = messages.stream().filter(message -> MessageType.MARKDOWN.equalsV(message.getType())).findFirst().orElse(null);
if (messageMarkdown != null) {
AiChatDto.Message message = new AiChatDto.Message();
message.setRole(RoleType.ASSISTANT.getCode());
message.setContent((String) messageMarkdown.getContent());
aiChatDto.getMessages().add(message);
}
}
});
}
//Add user questions
AiChatDto.Message message = new AiChatDto.Message();
message.setRole(RoleType.USER.getCode());
message.setContent(aiChatRequest.getContent());
aiChatDto.getMessages().add(message);
return aiChatDto;
}
}
模型调用节点
@NodeIdentity(nodeId = "llm")
public class LLM extends FlowNodeWithReturn<AiChatResponse> {
@Autowired
ChatGPTActuator chatGPTActuator;
@Autowired
DoubaoActuator doubaoActuator;
@Autowired
AliyunActuator aliyunActuator;
@Autowired
MoonshotActuator moonshotActuator;
@Autowired
OllamaActuator ollamaActuator;
@Override
public AiChatResponse doProcess(IContextBus iContextBus) {
//Init param
AiChatRequest aiChatRequest = (AiChatRequest) iContextBus.getParam();
AiChatDto aiChatDto = (AiChatDto) iContextBus.getTransmitInfo(AiChatDto.class.getName());
if (aiChatDto.getVendor().equals(VendorType.CHATGPT.getCode())) {
return chatGPTActuator.pursueSyc(aiChatDto, aiChatRequest.getResponder());
} else if (aiChatDto.getVendor().equals(VendorType.DOUBAO.getCode())) {
return doubaoActuator.pursueSyc(aiChatDto, aiChatRequest.getResponder());
} else if (aiChatDto.getVendor().equals(VendorType.ALIYUN.getCode())) {
return aliyunActuator.pursueSyc(aiChatDto, aiChatRequest.getResponder());
} else if (aiChatDto.getVendor().equals(VendorType.MOONSHOT.getCode())) {
return moonshotActuator.pursueSyc(aiChatDto, aiChatRequest.getResponder());
} else if (aiChatDto.getVendor().equals(VendorType.OLLAMA.getCode())) {
return ollamaActuator.pursueSyc(aiChatDto, aiChatRequest.getResponder());
}
return null;
}
}
对话记录保存节点
@NodeIdentity(nodeId = "chatSaver")
public class ChatSaver extends FlowNodeWithReturn<AiChatResponse> {
@Autowired
SessionService sessionService;
@Autowired
ChatService chatService;
@Autowired
ChatHisService chatHisService;
@Override
public AiChatResponse doProcess(IContextBus iContextBus) {
//Init param
AiChatResponse aiChatResponse = (AiChatResponse) iContextBus.getPreResult();
AiChatDto aiChatDto = (AiChatDto) iContextBus.getTransmitInfo(AiChatDto.class.getName());
if (!AiChatCode.ERROR.equalsV(aiChatResponse.getCode())) {
//Save conversation records
SessionVo sessionVo = sessionService.load(aiChatResponse.getSession());
if (sessionVo == null) {
sessionVo = new SessionVo();
sessionVo.setSessionId(aiChatResponse.getSession());
sessionVo.setAgentId(aiChatResponse.getAgent());
sessionService.create(sessionVo);
}
String question = aiChatDto.getMessages().get(aiChatDto.getMessages().size() - 1).getContent();
String answer = JsonUtil.toJson(aiChatResponse.getMessages());
//Save chat records
ChatVo chatVo = chatService.load(aiChatResponse.getId());
if (chatVo == null) {
chatVo = new ChatVo();
chatVo.setSessionId(aiChatResponse.getSession());
chatVo.setChatId(aiChatResponse.getId());
chatVo.setQuestion(question);
chatVo.setAnswer(answer);
chatService.create(chatVo);
} else {
chatVo.setQuestion(question);
chatVo.setAnswer(answer);
chatService.update(chatVo);
}
//Save chat his records
ChatHisVo chatHisVo = new ChatHisVo();
chatHisVo.setSessionId(aiChatResponse.getSession());
chatHisVo.setChatId(aiChatResponse.getId());
chatHisVo.setChatHisId(IdsUtil.newChatId());
chatHisVo.setQuestion(question);
chatHisVo.setAnswer(answer);
chatHisService.create(chatHisVo);
}
return aiChatResponse;
}
}
编排流程
@Slf4j
@Service
public class ChatHubService {
......
@Async
public void flow(AiChatRequest aiChatRequest, Consumer<AiChatResponse> responder) {
if (StringUtils.isBlank(aiChatRequest.getSession())) aiChatRequest.setSession(IdsUtil.newSessionId());
if (StringUtils.isBlank(aiChatRequest.getId())) aiChatRequest.setId(IdsUtil.newChatId());
aiChatRequest.setResponder(responder);
try {
//构建流程
FlowInstance flowInstance = flowEngine.builder()
.id(IdsUtil.newFlowId()) //定义流程ID
.next("paramBuilder") //构建参数
.next("agentConfiger") //查询Agent配置
.next("contextBuilder") //构建对话历史上下文
.next("llm") //调用模型
.next("chatSaver") //保存对话记录
.buildDynamic();
//执行流程
flowEngine.execute(flowInstance, aiChatRequest);
} catch (Exception e) {
log.error("ai models flow fail, e:", e);
AiChatResponse aiChatResponse = new AiChatResponse();
aiChatResponse.setCode(AiChatCode.ERROR.getCode());
aiChatResponse.setMessage(AiChatCode.ERROR.getMessage());
responder.accept(aiChatResponse);
}
}
}
调用测试
以豆包模型为例:
curl --location 'http://127.0.0.1:8080/ai-model-hub/ai/stream/chat/flow' \
--header 'Content-Type: application/json' \
--data '{
"agent": "2",
"content": "介绍一下军博,使用20个汉字以内"
}'
文件位置
ai-model-hub/
│
├── src/
│ └── main/
│ ├── java/
│ │ └── org/
│ │ └── salt/
│ │ └── ai/
│ │ └── hub/
│ │ │ ├── flow/