1. 项目概述
这个基于Spring Boot与LangChain4j的AI驱动新闻系统,是我最近完成的一个很有意思的实战项目。传统新闻系统在处理海量新闻内容时,往往会遇到几个痛点:分类不准确、内容清洗困难、推荐不够个性化。通过引入大语言模型能力,我们成功实现了新闻处理的智能化升级。
系统最核心的创新点在于:不是简单调用AI接口,而是将AI能力深度整合到新闻处理的各个环节。从新闻爬取、分类清洗,到个性化推荐,AI都扮演着关键角色。整个系统采用Spring Boot作为基础框架,通过LangChain4j来对接大模型能力,既保证了工程规范性,又实现了AI能力的灵活调用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 分层架构设计
系统采用经典的分层架构,但特别增加了AI能力层:
code复制┌─────────────────┐
│ 控制层 │ REST接口
├─────────────────┤
│ 业务层 │ 新闻爬虫、推荐、评论等
├─────────────────┤
│ 数据层 │ MyBatis持久化
├─────────────────┤
│ AI能力层 │ LangChain4j封装
└─────────────────┘
这种设计的优势在于:
- 业务逻辑与AI能力解耦
- 模型可以随时替换升级
- 统一管理Prompt和上下文
2.2 核心模块交互
各模块之间的交互流程非常清晰:
- 爬虫模块获取原始新闻
- AI模块进行分类和清洗
- 推荐模块根据用户兴趣推送
- 评论系统处理用户互动
3. AI能力集成实现
3.1 LangChain4j配置
通过Spring的依赖注入机制,我们实现了AI服务的统一管理:
java复制@Configuration
public class AiConfig {
@Bean
public ChatLanguageModel chatModel() {
return new OpenAiChatModel("你的API_KEY");
}
@Bean
public AiAssistant aiAssistant(ChatLanguageModel model) {
return AiServices.builder(AiAssistant.class)
.chatLanguageModel(model)
.build();
}
}
这样配置后,业务层只需要注入AiAssistant就可以使用AI能力,完全不需要关心底层实现。
3.2 新闻智能分类
传统的关键词分类方法维护成本高,泛化能力差。我们改用大模型进行分类:
java复制@SystemMessage("""
你是一个新闻内容分析专家。
请根据标题判断新闻类别,并严格返回JSON:
{"category":"分类","location":"地点关键词"}
""")
public interface NewsClassifier {
String classify(@UserMessage String title);
}
使用示例:
java复制String result = classifier.classify("北京冬奥会圆满闭幕");
// 返回: {"category":"体育","location":"北京"}
这种方法的优势:
- 无需维护分类规则
- 支持动态新增类别
- 输出结构化,便于程序处理
3.3 正文清洗与排版
不同网站的新闻正文结构差异很大,传统正则表达式难以应对。我们使用AI进行语义级清洗:
java复制@SystemMessage("""
你是专业新闻编辑。
请删除广告、版权说明等无关内容,
并确保每个自然段首行缩进2个全角空格。
""")
public interface ContentCleaner {
String cleanAndFormat(@UserMessage String rawContent);
}
实测下来,这种方法比正则表达式更健壮,能处理90%以上的新闻网站。
4. 新闻爬虫实现
4.1 多站点支持
系统支持多个主流新闻网站,核心处理流程统一:
java复制public void processNews(String url) {
if (newsMapper.countByUrl(url) > 0) {
return; // URL去重
}
Document doc = Jsoup.connect(url).get();
String title = doc.title();
String rawContent = doc.select(".article-content").text();
// AI处理
String category = classifier.classify(title);
String cleanContent = cleaner.cleanAndFormat(rawContent);
// 入库
News news = new News(title, cleanContent, category);
newsMapper.insert(news);
}
4.2 反爬策略
为了避免被目标网站封禁,我们实现了以下防护措施:
- 随机User-Agent
- 请求间隔30秒以上
- 自动重试机制
- IP代理池(可选)
java复制conn.setRequestProperty("User-Agent", getRandomUserAgent());
Thread.sleep(30000 + random.nextInt(10000));
5. 个性化推荐系统
5.1 用户兴趣建模
通过记录用户的点击行为来构建兴趣模型:
sql复制-- 用户兴趣表设计
CREATE TABLE user_interests (
user_id BIGINT,
category VARCHAR(50),
click_count INT,
PRIMARY KEY (user_id, category)
);
每次用户点击新闻时更新:
java复制public void recordUserInterest(Long userId, String category) {
userInterestMapper.upsert(userId, category);
}
5.2 混合推荐策略
推荐算法采用"兴趣优先+热度兜底"的策略:
java复制public List<News> recommendForUser(Long userId) {
List<News> recommendations = new ArrayList<>();
// 基于兴趣的推荐
List<String> topCategories = userInterestMapper.getTopCategories(userId, 3);
for (String category : topCategories) {
recommendations.addAll(newsMapper.getLatestByCategory(category, 2));
}
// 如果推荐不足,用热门新闻补全
if (recommendations.size() < 5) {
recommendations.addAll(newsMapper.getHotNews(5 - recommendations.size()));
}
return recommendations;
}
这种策略既考虑了个性化,又保证了推荐结果的多样性。
6. 评论系统实现
6.1 树形结构设计
评论系统支持多级回复,数据库设计如下:
sql复制CREATE TABLE comments (
id BIGINT PRIMARY KEY,
news_id BIGINT,
user_id BIGINT,
content TEXT,
parent_id BIGINT, -- 父评论ID
create_time DATETIME
);
查询时一次性构建评论树:
java复制public List<Comment> getCommentTree(Long newsId) {
List<Comment> roots = commentMapper.getRootComments(newsId);
for (Comment root : roots) {
root.setReplies(getReplies(root.getId()));
}
return roots;
}
private List<Comment> getReplies(Long parentId) {
List<Comment> replies = commentMapper.getByParentId(parentId);
for (Comment reply : replies) {
reply.setReplies(getReplies(reply.getId()));
}
return replies;
}
6.2 点赞一致性控制
点赞功能需要考虑并发问题,我们采用数据库唯一索引+原子操作:
java复制@Transactional
public void toggleLike(Long userId, Long newsId) {
// 检查是否已点赞
boolean hasLiked = likeMapper.exists(userId, newsId);
if (hasLiked) {
likeMapper.delete(userId, newsId);
newsMapper.decrementLikeCount(newsId);
} else {
likeMapper.insert(userId, newsId);
newsMapper.incrementLikeCount(newsId);
}
}
7. 语音识别集成
为了提升老年用户的使用体验,我们集成了语音识别功能:
java复制public String recognizeSpeech(byte[] audioData) {
AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
// 设置识别参数
HashMap<String, Object> options = new HashMap<>();
options.put("dev_pid", 1537); // 普通话模型
JSONObject res = client.asr(audioData, "wav", 16000, options);
return res.getJSONArray("result").getString(0);
}
关键参数说明:
- 必须使用wav格式
- 采样率固定16000Hz
- 普通话模型(1537)
8. 工程实践心得
在实际开发过程中,我总结了以下几点经验:
- AI服务稳定性:大模型API有时会超时或限流,必须实现重试机制和降级方案。我们使用Resilience4j实现了熔断:
java复制@CircuitBreaker(name = "aiService", fallbackMethod = "fallback")
public String classifyWithRetry(String title) {
return classifier.classify(title);
}
private String fallback(String title, Exception e) {
return "{\"category\":\"其他\"}";
}
- 爬虫健壮性:不同网站的HTML结构差异很大,需要准备多种选择器策略:
java复制String[] contentSelectors = {
".article-content",
"#content",
".main-text",
"div[itemprop=articleBody]"
};
for (String selector : contentSelectors) {
Elements elements = doc.select(selector);
if (!elements.isEmpty()) {
return elements.text();
}
}
-
推荐冷启动:新用户没有行为数据时,可以采用以下策略:
- 基于用户注册信息推测兴趣
- 展示全局热门内容
- 设计引导流程快速收集偏好
-
性能优化:AI服务调用较慢,我们做了以下优化:
- 对新闻分类结果进行缓存
- 批量处理待分类新闻
- 使用异步处理非实时任务
9. 扩展思考
这个系统还有很大的改进空间:
- 向量搜索:将新闻内容向量化存储,实现语义搜索
- 内容审核:利用AI自动识别违规内容
- 用户画像:结合浏览时长、分享行为等深化用户理解
- AB测试:对比不同推荐算法的效果
未来还计划增加:
- 新闻自动摘要生成
- 多语言支持
- 视频新闻处理
这个项目的完整代码已经开源,包含了详细的部署文档和API说明,对于想要学习Spring Boot和AI集成的开发者来说是个很好的参考案例。在实际应用中,这套系统已经成功支撑了日均10万级的新闻处理量,证明了其稳定性和实用性。
