Initial commit: Fintec AI Framework with Agent, RAG, and MCP modules

This commit is contained in:
limqsh
2026-04-27 17:23:58 +08:00
parent a9a1441537
commit 69c5aacdc8
85 changed files with 7143 additions and 0 deletions

47
agent-demo/pom.xml Normal file
View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ccb.fintec</groupId>
<artifactId>fintec-framework-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>agent-demo</artifactId>
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Agent Starter -->
<dependency>
<groupId>com.ccb.fintec</groupId>
<artifactId>fintec-framework-agent-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,12 @@
package com.ccb.fintec.agent.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AgentDemoApplication {
public static void main(String[] args) {
SpringApplication.run(AgentDemoApplication.class, args);
}
}

View File

@@ -0,0 +1,180 @@
package com.ccb.fintec.agent.demo.controller;
import com.ccb.fintec.agent.autoconfigure.template.AgentTemplate;
import com.ccb.fintec.agent.demo.dto.UserInfo;
import com.ccb.fintec.core.dto.AiResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.util.HashMap;
import java.util.Map;
/**
* Agent 演示控制器 - 展示封装后的简化用法
*
* 核心优势:
* 1. 自动使用公司统一的默认系统提示词
* 2. 内置会话记忆,无需手动配置
* 3. 支持所有常用场景:基础问答、流式、工具调用、多模态、结构化输出
* 4. 提供逃生通道,可获取底层 ChatClient 进行高级定制
*/
@RestController
@RequestMapping("/api/agent")
public class AgentController {
private final AgentTemplate agentTemplate;
public AgentController(AgentTemplate agentTemplate) {
this.agentTemplate = agentTemplate;
}
/**
* 1. 最简单的问答 - 自动使用配置的默认系统提示词
*
* 示例GET /api/agent/ask?question=什么是Spring AI?
*/
@GetMapping("/ask")
public Map<String, Object> ask(@RequestParam String question) {
// ✅ 只需一行代码,自动应用公司统一的系统提示词
String answer = agentTemplate.ask(question);
Map<String, Object> result = new HashMap<>();
result.put("question", question);
result.put("answer", answer);
result.put("note", "使用了 fintec.ai.agent.default-system-prompt 配置的系统提示词");
return result;
}
/**
* 2. 带会话记忆的对话 - 自动管理上下文
*
* 示例:
* GET /api/agent/ask-with-memory?conversationId=user123&question=我叫张三
* GET /api/agent/ask-with-memory?conversationId=user123&question=我叫什么名字?
*/
@GetMapping("/ask-with-memory")
public Map<String, Object> askWithMemory(
@RequestParam String conversationId,
@RequestParam String question) {
// ✅ 自动使用会话记忆,无需手动配置 Advisor
String answer = agentTemplate.ask(question, conversationId);
Map<String, Object> result = new HashMap<>();
result.put("conversationId", conversationId);
result.put("question", question);
result.put("answer", answer);
result.put("note", "自动使用 MessageWindowChatMemory窗口大小由 fintec.ai.agent.memory-window-size 配置");
return result;
}
/**
* 3. 自定义系统提示词的问答
*
* 示例GET /api/agent/ask-custom?systemPrompt=你是数学老师&conversationId=math01&question=1+1=?
*/
@GetMapping("/ask-custom")
public Map<String, Object> askCustom(
@RequestParam String systemPrompt,
@RequestParam String conversationId,
@RequestParam String question) {
String answer = agentTemplate.ask(systemPrompt, question, conversationId);
Map<String, Object> result = new HashMap<>();
result.put("systemPrompt", systemPrompt);
result.put("conversationId", conversationId);
result.put("question", question);
result.put("answer", answer);
return result;
}
/**
* 4. 结构化输出 - AI 自动填充 Java 对象
*
* 示例GET /api/agent/ask-for-object?question=生成一个用户信息姓名李四年龄25岁邮箱lisi@example.com
*/
@GetMapping("/ask-for-object")
public UserInfo askForObject(@RequestParam String question) {
// ✅ AI 自动将回答转换为 UserInfo 对象
return agentTemplate.askForObject(question, UserInfo.class);
}
/**
* 5. 带元数据的问答(包含 Token 使用量、耗时等)
*
* 示例GET /api/agent/ask-with-metadata?question=解释一下量子计算
*/
@GetMapping("/ask-with-metadata")
public AiResponse askWithMetadata(@RequestParam String question) {
// ✅ 自动收集 Token 使用量、耗时等元数据
return agentTemplate.askWithMetadata(question);
}
/**
* 6. 流式输出 - Server-Sent Events
*
* 示例curl -N http://localhost:8083/api/agent/stream?question=讲个故事
*/
@GetMapping(value = "/stream", produces = "text/event-stream")
public Flux<String> stream(@RequestParam String question) {
// ✅ 自动应用默认系统提示词
return agentTemplate.stream(question);
}
/**
* 7. 带会话记忆的流式输出
*
* 示例curl -N "http://localhost:8083/api/agent/stream-with-memory?conversationId=user123&question=继续"
*/
@GetMapping(value = "/stream-with-memory", produces = "text/event-stream")
public Flux<String> streamWithMemory(
@RequestParam String conversationId,
@RequestParam String question) {
// ✅ 流式 + 记忆组合
return agentTemplate.stream(question, conversationId);
}
/**
* 8. 逃生通道:获取底层 ChatClient 进行高级定制
*
* 示例GET /api/agent/advanced-usage?question=测试高级用法
*/
@GetMapping("/advanced-usage")
public Map<String, Object> advancedUsage(@RequestParam String question) {
// ✅ 获取底层 ChatClient完全自定义
ChatClient client = agentTemplate.getChatClient();
String answer = client.prompt()
.system("完全自定义的系统提示词")
.user(question)
.call()
.content();
Map<String, Object> result = new HashMap<>();
result.put("question", question);
result.put("answer", answer);
result.put("note", "通过 getChatClient() 获取底层对象,进行高级定制");
return result;
}
/**
* 9. 健康检查
*/
@GetMapping("/health")
public Map<String, Object> health() {
Map<String, Object> result = new HashMap<>();
result.put("status", "UP");
result.put("service", "Agent Demo");
result.put("features", new String[]{
"基础问答",
"会话记忆JDBC/内存自动选择)",
"流式输出",
"结构化输出",
"工具调用",
"多模态支持",
"元数据收集",
"逃生通道"
});
return result;
}
}

View File

@@ -0,0 +1,67 @@
package com.ccb.fintec.agent.demo.controller;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 直接测试 OpenRouter API不经过 Spring AI
*/
@RestController
@RequestMapping("/api/test")
public class DirectOpenRouterTestController {
@GetMapping("/openrouter")
public Map<String, Object> testDirectCall(@RequestParam(defaultValue = "你好") String question) {
String apiKey = "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";
String baseUrl = "https://openrouter.ai/api/v1/chat/completions";
// 构造请求体(与 Python 相同)
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", "openrouter/free");
requestBody.put("messages", List.of(
Map.of("role", "system", "content", "你是一个助手"),
Map.of("role", "user", "content", question)
));
// 构造请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);
// OpenRouter 推荐的 headers
headers.set("HTTP-Referer", "http://localhost:8083");
headers.set("X-Title", "Fintec Agent Demo");
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
RestTemplate restTemplate = new RestTemplate();
try {
ResponseEntity<Map> response = restTemplate.exchange(
baseUrl,
HttpMethod.POST,
entity,
Map.class
);
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("statusCode", response.getStatusCode());
result.put("body", response.getBody());
return result;
} catch (Exception e) {
Map<String, Object> result = new HashMap<>();
result.put("success", false);
result.put("error", e.getMessage());
return result;
}
}
}

View File

@@ -0,0 +1,63 @@
package com.ccb.fintec.agent.demo.dto;
/**
* 用户信息 DTO用于结构化输出示例
*/
public class UserInfo {
private String name;
private Integer age;
private String email;
private String occupation;
public UserInfo() {}
public UserInfo(String name, Integer age, String email, String occupation) {
this.name = name;
this.age = age;
this.email = email;
this.occupation = occupation;
}
// Getters and Setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getOccupation() {
return occupation;
}
public void setOccupation(String occupation) {
this.occupation = occupation;
}
@Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", occupation='" + occupation + '\'' +
'}';
}
}

View File

@@ -0,0 +1,59 @@
# ============================================
# 服务器配置
# ============================================
server.port=8083
# ============================================
# 模型配置(业务系统根据需求选择模型提供商)
# ============================================
spring.ai.openai.api-key=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
# Spring AI OpenAI starter 会自动追加 /v1/chat/completions所以 base-url 只需要到 /api
spring.ai.openai.base-url=https://openrouter.ai/api
# 使用 openrouter/free 自动路由到免费模型
spring.ai.openai.chat.options.model=openrouter/free
# 单次请求最大 Token 数Spring AI 原生配置)
spring.ai.openai.chat.options.max-tokens=2048
# ============================================
# Agent 模块配置
# ============================================
# 默认系统提示词:定义 AI 助手的人设和行为准则
fintec.ai.agent.default-system-prompt=你是一个专业的订单助手,请用中文简洁准确地回答订单相关问题。
# 对话记忆窗口大小:保留最近 N 条消息作为上下文
fintec.ai.agent.memory-window-size=15
# ============================================
# 安全防护配置
# ============================================
# 启用安全关键词过滤
fintec.ai.safety.enabled=true
# 需要拦截的敏感关键词列表
fintec.ai.safety.block-keywords[0]=暴力
fintec.ai.safety.block-keywords[1]=色情
fintec.ai.safety.block-keywords[2]=赌博
# ============================================
# 限流配置(成本控制)
# ============================================
# 每分钟最大请求数
fintec.ai.rate-limit.max-requests-per-minute=30
# ============================================
# 重试策略配置
# ============================================
# 启用自动重试
fintec.ai.retry.enabled=true
# 最大重试次数
fintec.ai.retry.max-attempts=2
# 重试退避时间(毫秒)
fintec.ai.retry.backoff-ms=500
# ============================================
# 日志配置
# ============================================
logging.level.root=INFO
logging.level.com.ccb.fintec=DEBUG
# 启用 Spring AI 和 HTTP 客户端的详细日志
logging.level.org.springframework.ai=DEBUG
logging.level.org.springframework.web.client=DEBUG
logging.level.io.micrometer.common.util.internal.logging=OFF