Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

上周被spring-ai调用模型api这个折腾的有点难受,这周又试了下用deepseek官方的api,但结果还是一样,不确定是啥原因,请求会一直卡着
后来想着本地有ollama,那是不是可以直接用ollama来调本地模型
这里我们用一个非常基础很小的模型

1
ollama run gemma3:1b

然后对应的配置是

1
2
3
4
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>

当然这里有个坑,因为目前还不成熟,所以这个包的依赖跟之前openai的是不兼容的
需要引用

1
2
3
4
5
6
7
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M1</version>
<type>pom</type>
<scope>import</scope>
</dependency>

属性配置就很简单了

1
2
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.model=gemma3:1b

然后就是简单写个Controller就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/demo")
public class OllamaController {
private static final Logger logger = LoggerFactory.getLogger(OllamaController.class);

@Autowired
private OllamaChatModel ollamaChatModel;


@RequestMapping("/ollama")
public Object ollama(String msg) {
String call = ollamaChatModel.call(msg);
return call;
}
}

这样我们的初始调用就完成了
比如我们提问 ‘how to learn java’

1
Okay, let's break down how to learn Java! It can seem daunting at first, but with a structured approach, you'll be coding in Java in no time. Here’s a breakdown of a recommended learning path, broken down into phases: **1. Foundation (2-4 Weeks)** * **Understand the Basics:** * **What is Java?** Java is a robust, object-oriented programming language known for its platform independence ("write once, run anywhere"). It’s used for enterprise applications, Android development, and more. * **Why Learn Java?** It’s a popular choice for beginners because it’s relatively easy to learn, has a large community, and is used in many real-world applications. * **Key Concepts:** * **Variables & Data Types:** Understand different data types (int, float, String, boolean, etc.). * **Operators:** Arithmetic, comparison, logical, assignment. * **Control Flow:** `if`, `else`, `for`, `while` – controlling the flow of your program. * **Basic Syntax:** The code structure of Java. * **Resources:** * **Tutorialspoint Java Course:** [https://www.tutorialspoint.com/java/index.htm](https://www.tutorialspoint.com/java/index.htm) - A good, comprehensive, and free overview. * **Codecademy's Learn Java:** [https://www.codecademy.com/learn/learn-java](https://www.codecademy.com/learn/learn-java) - Interactive, hands-on exercises. * **Google's Java Tutorial:** [https://developer.android.com/training/jdbc](https://developer.android.com/training/jdbc) (Java is built on JDBC, so this will give you a baseline) **2. Core Concepts (4-8 Weeks)** * **Data Structures:** * **Arrays:** Fundamental data structures. Understand how to declare, initialize, and use arrays. * **Linked Lists:** Another essential data structure, offering flexibility. * **Object-Oriented Programming (OOP) Fundamentals:** * **Classes and Objects:** The basis of OOP. Learn how to define classes, create objects, and interact with them. * **Encapsulation:** Bundling data and methods that operate on that data within a class. * **Inheritance:** Creating new classes based on existing classes. * **Polymorphism:** The ability of objects to take on many forms. * **Java Syntax & Semantics:** * **Statements:** The commands you write. * **Expressions:** Results of calculations. * **Keywords:** Reserved words with special meanings (e.g., `class`, `if`, `else`). * **Comments:** Explain your code. * **Resources:** * **Oracle Java Tutorials:** [https://docs.oracle.com/javase/tutorial/](https://docs.oracle.com/javase/tutorial/) – A more in-depth guide. * **FreeCodeCamp's Java Tutorial:** [https://www.freecodecamp.org/learn/java-tutorial/](https://www.freecodecamp.org/learn/java-tutorial/) - Great for visual learners. **3. Practical Application - Projects** * **Simple Programs:** Start with small, manageable projects: * **Number Guessing Game:** A classic introductory project. * **Rock-Paper-Scissors:** A basic example of OOP. * **Simple Calculator:** Reinforces fundamental concepts. * **Gradually Increase Complexity:** * **Print Statements:** A simple way to understand output. * **Palindrome Checker:** A challenge to practice string manipulation. * **Basic Console Applications:** Write programs that run directly in the console. **4. Intermediate Concepts (Ongoing)** * **Exception Handling:** Understanding and handling errors. * **Collections:** Learn about ArrayLists, LinkedLists, Sets, and Maps. * **String Manipulation:** Working with strings effectively. * **Multithreading:** Understanding how to run multiple tasks concurrently (basic concepts). * **Java Collections Framework:** The cornerstone of modern Java programming. **Resources for Continued Learning:** * **Stack Overflow:** [https://stackoverflow.com/](https://stackoverflow.com/) - An invaluable resource for solving coding problems and learning from others. * **Reddit's r/java:** [https://www.reddit.com/r/java/](https://www.reddit.com/r/java/) - A community forum with discussions and resources. * **YouTube:** Search for Java tutorials and courses (e.g., "Java for beginners"). **Tips for Success:** * **Practice Regularly:** Even 30 minutes a day is better than long, infrequent sessions. * **Write Code Frequently:** The more you write, the better you'll understand. * **Don't Be Afraid to Make Mistakes:** Mistakes are part of the learning process. Learn from them. * **Read Code:** Look at code written by others to understand different approaches. * **Join a Community:** Connecting with others can provide support and motivation. **To help me tailor my advice even more, could you tell me:** * **What's your background in programming?** (e.g., Have you programmed before? If so, what languages?) * **What are your goals for learning Java?** (e.g., Career change? Personal project? Specific application like Android?)

格式不太美观,但是至少是能输出来结果了,而且可以选择本地可用的最佳模型,话说openai的oss试了下,本地有点吃不消,不然可能是比较合适的

之前经常看一些LLM的工具,类似于ollama,chatbox等等,这些都类似于是个独立工具,真的在代码里使用的话可能没那么方便
正好看了下spring有个spring-ai的包,可以来使用试下
首先我们要用比较新的jdk版本,比如jdk17,然后需要使用springboot比较新的版本,比如3.2.3,但是不建议使用最新稳定版,3.4.x,因为会有bug,别问我怎么知道的。
真的是折腾,
简单分享下吧
需要在pom中增加仓库定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 仓库定义 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>

然后增加依赖

1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

引入主要的几个包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

代码也很简单,但是不确定是api限流太严重还是啥,响应慢的离谱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
public class ChatController {

private final ChatClient chatClient;

public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.defaultSystem("你是个资深服务端架构师").build();
}

@GetMapping("/demo/{message}")
public String demo(@PathVariable("message") String message) {

message = "怎么学习java";
String response = chatClient.prompt()
.user(message)
.call().content();

return response;
}
}

对的,甚至把prompt写死了

这里经过了好几个包版本的调整
原先比较早的0.8的不支持接口的自定义路径,因为要用火山的接口,它的接口跟openai是不一样的,后面有个v3啥的
所以需要增加最下面一行

1
2
3
spring.ai.openai.base-url=https://ark.cn-beijing.volces.com/api/v3
spring.ai.openai.chat.options.model=deepseek-r1-250120
spring.ai.openai.chat.completions-path=/chat/completions

但是可以从上面的图里看出来,响应非常慢,简直不能忍,有openai 权限的可以帮忙试下究竟是啥原因,抓包啥的也不太方便

上次介绍了n8n的安装和简单的工作流搭建,现在我们可以来看下一批免费开源的模板,也许它不能直接为我们所用,但是可以给我们搭建使用过程中提供一些帮助
就是这个仓库 https://github.com/wassupjay/n8n-free-templates
比如我们看下第一个目录,是关于机器学习的
我们可以直接直接把原始文件json格式的下载下来,

然后就可以按自己的需求进行调整,当然还是需要一些openai的api这些才能完全体跑起来,我本地受限制就暂时不完整演示了
而如果觉得这些模板可能离实际使用有点远的话,可以看下官方的模板库,发现官方的模板库是更适合我们初学者的
官方的可以在这里找到
https://n8n.io/workflows/
上半部分是如何学习n8n

下半部分是热门的template

比如我们来看下Chat with local LLMs using n8n and Ollama 这个
我们可以直接导入
然后配置ollama的端口和模型

这样我们就可以借助ollama来做个类似chatgpt的网页工具,当然这些只是举例,真正使用的还有待各自发掘和分享

开始研究学习下现在比较流行的工作流画布,用官方的话就是

Flexible AI workflow automation
for technical teams
根据中文教程的描述,它也可以认为是个低代码的画布工具,首先安装非常简单,咱们先玩玩本地的
前提是装有docker
然后就三个命令

1
2
3
docker pull n8nio/n8n
docker volume create n8n_data
docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

就安装好了,本地端口就是5678
在浏览器打开 http://localhost:5678/ 就可以看到了,我模仿中文的教程来记录下简单的工作流搭建流程,抓取即刻的帖子
第一步我们先建第一个节点,这个类似于是开关,怎么触发这个工作流,

这里也能初步看出n8n的强大,可以通过各种方式来触发,我们就先选一个手动触发
然后我们还需要能获取http内容的,
我们就添加一个http组件,通过搜索http,可以找到http的请求工具

这里我们只需要填入地址
比如跟教程中的一样 https://m.okjike.com/users/6d3698d6-0970-49a6-a19d-f8d3cfa33a6f
然后就是要把请求的内容解析出来
搜索html,我们就可以找到解析html内容的工具

从网页中找到对应的选择器,记得勾选 return array,否则只会取第一个

执行下当前组件,就能看到内容已经被抓下来了
接下去要分割下,选择 Data transformation 的工具中的split out
然后就把前面的正文和时间和拖进去

最后就是导出文件了
搜索 xlsx,然后出现convert to file工具,然后把对应的字段拖进去

非常直观和方便的就完成了一个简单的工作流搭建,当然这个还没用上AI工具,大模型和MCP等,但是已经把它的很多特点都介绍了,各种工具真的非常好用和傻瓜化
没有复杂难搞的配置和调试,对于一些文字处理表格处理真的很方便
后续我们再来看下怎么跟大模型接起来,让它更加的强大。

在实际开发中,我们经常遇到需要在分组内进行排序并获取特定排名记录的需求。比如查找每个班级年龄最大的学生,每个部门薪资最高的员工等。这类问题在MySQL 8.0前后有着截然不同的解决方案。

传统解决方案(MySQL 8.0之前)

表结构

1
2
3
4
5
6
7
8
9
CREATE TABLE IF NOT EXISTS `students` (
`id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) NOT NULL COMMENT '姓名',
`age` int(11) NOT NULL COMMENT '年纪',
`class` int(11) NOT NULL COMMENT '班级',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE current_timestamp() COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='学生表';

业务场景

假设我们有如下学生数据,我想找到每个班年纪最大的学生,可以用一些联表或者子查询方法

idnameageclass
1学生1101
2学生2102
3学生3111
4学生3112

子查询联表

在MySQL 8.0之前,我们需要使用相对复杂的子查询配合JOIN来实现:

1
2
3
4
5
6
7
8
SELECT ta.* 
FROM students ta
JOIN (
SELECT class, MAX(age) as max_age
FROM students
GROUP BY class
) as tb
ON ta.class = tb.class AND ta.age = tb.max_age;

分析:

  1. 子查询阶段SELECT class, MAX(age) as max_age FROM students GROUP BY class

    • 按班级分组,找出每个班级的最大年龄
    • 结果类似:{class: 1, max_age: 20}, {class: 2, max_age: 21}
  2. JOIN阶段:将原表与子查询结果关联

    • 关联条件:ta.class = tb.class AND ta.age = tb.max_age
    • 最终获得每个班级年龄最大的学生完整信息

局限性:

  • 查询逻辑相对复杂,可读性不佳
  • 如果存在同班级同年龄的多个学生,会返回多条记录
  • 性能上需要进行两次表扫描和一次JOIN操作

现代解决方案(MySQL 8.0+)

使用窗口函数优化

MySQL 8.0引入了窗口函数,让这类问题的解决变得更加优雅:

1
2
3
4
5
6
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY class ORDER BY age DESC) as row_num
FROM students
) t
WHERE row_num = 1;

核心概念解析:

  1. PARTITION BY class:按班级进行分区

    • 可以理解为逻辑上的分组,但不会像GROUP BY那样聚合数据
    • 每个分区内部可以独立进行排序和编号
  2. ORDER BY age DESC:在每个分区内按年龄降序排序

    • 年龄最大的学生排在第一位
  3. **ROW_NUMBER()**:为每个分区内的行分配唯一序号

    • 从1开始递增,即使有相同值也会分配不同序号
    • 这就解决了传统方法中重复值的问题
  4. 外层WHERE row_num = 1:筛选每个分区的第一条记录

其他窗口函数选择

除了ROW_NUMBER(),还可以根据业务需求选择其他窗口函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 使用RANK():相同值会有相同排名,但会跳跃序号
SELECT * FROM (
SELECT *,
RANK() OVER (PARTITION BY class ORDER BY age DESC) as rank_num
FROM students
) t
WHERE rank_num = 1;

-- 使用DENSE_RANK():相同值有相同排名,但不跳跃序号
SELECT * FROM (
SELECT *,
DENSE_RANK() OVER (PARTITION BY class ORDER BY age DESC) as dense_rank_num
FROM students
) t
WHERE dense_rank_num = 1;

性能对比与优势

窗口函数的优势:

  1. 代码简洁性:逻辑更直观,维护成本更低
  2. 性能优化:只需一次表扫描,避免了JOIN操作
  3. 功能丰富:提供多种排名函数应对不同场景
  4. 处理重复值ROW_NUMBER()确保每个分组只返回一条记录

适用场景扩展:

  • Top-N查询:每个分组的前N条记录
  • 数据去重:基于特定字段的去重逻辑
  • 百分位计算:使用PERCENT_RANK()等函数
  • 移动平均:结合ROWS BETWEEN进行滑动窗口计算

MySQL 8.0的窗口函数为分组排序查询提供了更现代化的解决方案。相比传统的子查询+JOIN方式,窗口函数不仅在语法上更加简洁直观,在性能上也有显著提升。对于需要在分组内进行复杂数据分析的场景,窗口函数已经成为不可或缺的利器。

0%