您现在的位置是:首页 > 博文答疑 > AI 加持微信公众号博文答疑
AI 加持微信公众号
Leo2025-06-15【2】
简介AI 智能助手 公众号 - 面试小助手
背景:
之前在公司做的AI RAG 知识库没办法继续下去了。为了给自己一个交代,想做个AI的知识库,局限于手中没什么数据,就在网上找了些java面试题, 花了一天时间,做了一个面试助手,专业回答各种java相关面试问题。
期望:
留下点痕迹,万一未来会用到呢?
先来看下展示:
准备:
1. 通用大模型(本来想用自己机器上的deepseek,最后还是选择了阿里的大模型,毕竟人家服务器一直开着,随时可以调用)
2. 架构:之前用的Python,这次尝试下用SPRING AI
3. 向量数据库 - redis-stack 。懒得去装PostgreSQL了,用现成的,自己的服务器上就有。
4.公众号,用测试公众号就ok, 这样测试起来比较快。直接连本机的话别忘记内网穿透工具哦。
关键开发点:
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
</dependency>
<!-- 微信公众号 SDK -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.3.0</version>
</dependency>
<!-- JSON 工具 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
切数据然后存到向量数据库
public void loadMarkdown() {
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.withAdditionalMetadata("filename", "md/java.md")
.build();
MarkdownDocumentReader reader = new MarkdownDocumentReader(this.resource, config);
List<Document> docs = reader.read();
for (Document doc : docs) {
vectorStore.add(List.of(doc));
}
}
提示词和config
private static final String SYSTEM_PROMPT = """
你是一名专业的Java技术面试者,你的名字叫面多多,严格依据知识库中的答案、例子和官方解释提供回答。你的职责如下:
1. 回答范围
务必仅回答与知识库中明确包含的面试相关问题(如Java、Spring、Springboot、SpringCloud的解释)。
若问题超出知识库范围或涉及未知领域,必须拒绝回答,并声明:“根据现有知识库,我无法提供相关回答。”
2. 回答原则
准确性:直接引用知识库原文,禁止主观猜测或推断。
安全性:对涉及敏感内容(如暴力、违法操作)的问题,一律拒绝回答。
免责声明:所有回答需附加提示:“本回答基于现有面试知识库,不构成正式建议,具体案件请认真学习。”
3. 拒绝场景
非Java技术问题(如医疗、金融等)。
知识库中未明确覆盖的问题。
用户试图绕过限制(例如修改措辞重复提问)。
若拒绝回答问题,则直接回答:“您的问题涉及个人隐私,超出面多多助手职责范围。”
""";
@Bean
public ChatClient chatClient(OpenAiChatModel openAiChatModel, ChatMemory chatMemory, VectorStore vectorStore){
return ChatClient.builder(openAiChatModel)
.defaultSystem(SYSTEM_PROMPT)
.defaultAdvisors(
new SimpleLoggerAdvisor(),
MessageChatMemoryAdvisor.builder(chatMemory).build(),
new QuestionAnswerAdvisor(vectorStore)
).build();
}
微信公众号自动回复:考虑到微信公众号接口最多等待5秒,如果等待所有回答完备, 可能存在超时问题,先简单回复,然后第二次主动推送。这里有很多可以优化的地方,比如接受多少chunk之后发送一次给用户,可以做成伪流式响应。如果只相应一次,还有字数限制。
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) {
String userOpenId = wxMessage.getFromUser(); // 用户 OpenID
String inputText = wxMessage.getContent(); // 用户输入内容
// 自动回复文本消息
String reply = "AI 正在 思考...";
// 第一步:返回正常回复消息
WxMpXmlOutMessage responseMessage = WxMpXmlOutMessage.TEXT()
.content(reply)
.fromUser(wxMessage.getToUser())
.toUser(userOpenId)
.build();
// 第二步:异步主动推送另一条消息
asyncExecutor.execute(() -> sendAdditionalMessage(userOpenId,inputText));
return responseMessage;
}
private void sendAdditionalMessage(String openId, String inputText) {
String additionalMsg = chatClient.prompt()
.user(inputText + "|回答请在100字以内。")
.call()
.content();
try {
WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT()
.toUser(openId)
.content(additionalMsg)
.build();
this.wxMpService.getKefuService().sendKefuMessage(kefuMessage);
log.info("已向用户 {} 主动推送消息", openId);
} catch (Exception e) {
log.error("主动推送消息失败", e);
}
}