从想法到实践:在无序的生活里,试图用代码敲出一点秩序

grtsinry43
11/23/2025(更新于 11/23/2025
14 views
预计阅读时长 42 分钟

AI Summary

DEEPSEEK-R1

其实标题应该是 于痛苦中和解,或者说,只是为了让自己别停下来罢了

回头看了一眼,距离无法正常行动到现在,已经过去三个月了,直到月底,我也不知道我还能不能正常行走。

命运开了个无情的玩笑

那段时间的生活,怎么说呢,确实是烂透了。直到现在,我也依然无法摆脱每日的痛苦,夜里依然常常失眠,从那天开始就噩梦不断

但人嘛,总不能真就在泥坑里躺平了。

这三个月,虽然腿脚不便,但我强迫自己的脑子动起来,毕竟生活,时间都在继续,学期结束越来越近,秋招也越来越近了。既然生活里全是不可控的 Exception,那我至少要在 IDE 里找回一点能跑通的逻辑。这三个月折腾了一堆东西,但是始终没有精力,也没有心情打磨一个好的产品,之前好多计划的,还有合作的项目,都被我无奈 delay 了。

这篇文章可能很需要 AI 总结来导读,又长又流水帐,可能我现在没有力气来慢慢打磨了。

PureFlow:先试试手还在不在

最开始是 PureFlow(这个之前水过文章了,就不细说了)。

其实当时写这个没别的想法,就是学了 kmp 始终没写过啥成型的东西嘛,正好出不了门,在学校全天狠狠写了一周多,然后终于证明跨平台没那么简单,之后慢慢学,然后填坑吧。

PureFlow

Tangyuan 社区:帮别人点缀,顺便治愈自己

那时候自己其实挺迷茫的,也没什么好的 idea。不过恰逢我负责的学生部门招新,遇到了@XianlitiCN 同学。他有一群志同道合的伙伴,有共同的爱好,还是文科相关专业。

清水阁

线粒体同学的帖子,也是我为是什么想帮助他

我想到我小时候很喜欢文学,当时还去过什么汉字听写大会的市级海选,还很喜欢诗词大会。...然后发现风花雪月并给不了我生活的底气,所以还是选择作为爱好了。并且我一直以来还有一个想要维护一个社区的想法...(你怎么恰好这么多想法) 线粒体同学一直想做 Tangyuan 社区,而旧版用命令式写的 UI 有点过时并且不好维护,我想着,行吧,既然我自己也是一团乱麻,不如帮别人把想法落地。

其实核心就是用 Compose 构建 UI,通过 ViewModel 组织数据并传递到 UI 线程

  • UI 层:声明式构建 (Jetpack Compose)
    摒弃了传统的 View/XML 体系,全线采用 Compose 构建界面。作为声明式 UI 框架,Compose 允许通过 Kotlin 代码直接描述界面状态。这种“Code as UI”的方式,极大地减少了 findViewById 和手动操作 View 状态的代码量,让视图层的代码更加直观、紧凑。
  • 逻辑层:ViewModel 托管数据与状态
    为了实现 UI 与 逻辑的解耦,这里引入了 ViewModel。所有的业务逻辑、网络请求(Retrofit)、数据清洗都严格限制在 ViewModel 内部进行。ViewModel 的生命周期感知特性,确保了数据在配置更改时不会丢失。
  • 数据流:从 ViewModel 到 UI 线程的单向传递
    这是整个架构中最关键的一环。
    1. 数据获取:ViewModel 利用 viewModelScope 启动协程,在 IO 线程进行耗时的网络或数据库操作。
    2. 状态暴露:将处理后的结果封装在 StateFlow 或 LiveData 中,作为一个可观察的单一数据源(SSOT)。
    3. UI 渲染:在 Compose 界面中,通过 collectAsState() 监听数据流。一旦数据发生变化,Compose 会自动在 主线程(UI Thread) 触发重组(Recomposition),刷新界面。

具体的项目结构是这样的:

bash
1❯ tree -I build .
2.
3├── build.gradle.kts
4├── proguard-rules.pro
5├── release // 编译产物
6└── src
7    ├── androidTest // 测试
8    ├── main
9    │   ├── AndroidManifest.xml // Manifest
10    │   ├── java

看着大家在里面发帖交流,那种“被需要”的感觉,在当时真的是一剂良药。虽然现在回看代码可能还是堆了不少 <del> 新鲜热乎的屎山 </del>,但至少它跑起来了,还挺像模像样的。

AI 原型生成器:稍微膨胀了一下的野心,折腾的开始

到这个时候就是国庆假期了,身体稍微恢复一点,想搞什么东西的想法又上来了。这个 ai-proto-generator 还是有点东西可以讲的。这里我选择了 nextjs ktor 来构建这个项目,我们生态内有一个好用的工具:Koog

我们可以去看一下市面上的这种 ai 生成工具,原理就是通过对话,toolcall,在右侧打开一个 iframe,将远程开发服务器的网页传回来,然后通过命令不断更改即可看到效果。

首先是,为了构建一个项目,agent 需要在一个开发目录运行开发服务器,比如 nextjs vite 等,然后输出代码,我们可以为它提供比如 websearch shell read write 工具,为了我们环境的绝对隔离,这里最好的方法是使用容器技术,这里我为了方便使用了抽象程度最高的 docker(其实可以用低一层的 containerd,更轻量一些)。

为了方便管理,我们可以使用 go 写一个单独管理容器的工具(用 go 是因为 docker 所在的生态还是 go 最方便,我 kt 搞了半天都是很麻烦),导入包直接开干

go
1package handler
2
3import (
4	// ...其他依赖
5
6	"github.com/docker/docker/api/types/container"
7	"github.com/docker/docker/api/types/network"
8	"github.com/docker/docker/client"
9)
10

太长不看版:就是我们实现了一个快速创建容器的抽象

go
1package handler
2
3func (h *SandboxHandler) CreateSandbox(c *gin.Context) {
4	
5}
6
7func (h *SandboxHandler) ListSandboxes(c *gin.Context) {
8	
9}
10

而后我们可以让主服务去调用这个啦,就像这样:

bash
1┌─────────────────┐    HTTP     ┌─────────────────┐    gRPC     ┌─────────────────┐
2│                 │   Request   │                 │   Call      │                 │
3│   Frontend      │ ──────────→ │  Ktor Backend   │ ──────────→ │  Go Container   │
4(Next.js)     │             │   (Business)    │             │   Manager       │
5│                 │             │                 │             │                 │
6└─────────────────┘             └─────────────────┘             └─────────────────┘
7                                         │                               │
8                                         │                               │
9                                         ▼                               ▼
10                                 ┌─────────────────┐             ┌─────────────────┐

为了管理 docker 的流量转到前端,我们引入 traefik,这里就不多赘述了。待到写好基本的后端结构,然后我们便可以使用 koog 来顺畅调用 llm 的 api 了。

市面上的对话使用 sse 来实现流式输出,然后后端维护对话上下文,并且通过提取回应的字符串来实现 toolcall(也就是 MCP),借用 koog,我们可以方便的流式调用

kotlin
1                            // 执行LLM流式调用
2                            val llmResponse = StringBuilder()
3                            val flow = llm().executeStreaming(chatPrompt, GoogleModels.Gemini2_0FlashLite)
4
5                            flow.collect { chunk ->
6                                llmResponse.append(chunk)
7                                writeSseData("token", mapOf("token" to chunk))
8                            }
9
10                            val fullResponse = llmResponse.toString().trim()

在之前,我们写代码是为了人,基建调用,而这里我们写的都是为 llm 服务:我们可以维护一个工具集合,方便注册,根据项目切换,还有管理

kotlin
1package com.grtsinry43.ai
2
3import kotlinx.serialization.json.JsonObject
4import org.slf4j.LoggerFactory
5import java.util.concurrent.ConcurrentHashMap
6
7/**
8 * 默认的工具注册表实现
9 */
10class DefaultToolRegistry : ToolRegistry {

随后处理对话中的工具调用相关,来拿到我们想要的工具调用

kotlin
1    // 从响应中提取函数调用
2    fun extractFunctionCalls(response: String): List<FunctionCall> {
3        val json = Json { ignoreUnknownKeys = true }
4        return try {
5            val jsonResponse = json.parseToJsonElement(response).jsonObject
6            val functionCallsArray = jsonResponse["function_calls"]?.jsonArray ?: return emptyList()
7            
8            functionCallsArray.mapNotNull { element ->
9                try {
10                    val callObj = element.jsonObject

有了工具调用,接下来就是编写大量的工具集,然后不要一次性塞给AI,因为选择工具经常出现问题,我们需要的是根据项目类型自动推荐,然后分好类,比如我们这里有的 websearch shell read write 等等。

可惜理想是美好的,现实是残酷的,想实现这些效果,需要付出高昂的 tokens 成本,在 claude 小号被封之后,我的项目就搁置了,如果你恰巧财力雄厚,等我完善完我就开源出去可以调 api 慢慢玩。

Github Overview & UI 的“滑铁卢”

进入10月中旬,生活开始多线运行,虽然身体不行,但是空闲时间反而越来越少了,我开始转向轻量项目。在钱包受伤之后,紧接着,只能玩一玩比如api这种现成的,于是方向转向了 Github 仓库分析工具 (Overview)

这里我首次用fastify写大项目,也是首次尝试cc接管一切,配置好提示词,eslint规则,并且设置commit钩子,这种强制执行的限制对于llm还是挺有用的。

后端逻辑写得飞起,数据抓取也没问题。结果到了前端展示环节,UI 设计彻底把我整不会了

我是真的尽力了,但画出来的界面怎么看怎么丑,那种“脑子里有画面但手残画不出来”的挫败感,真的让人想砸键盘。这就好比当时的我,里子虽然还在,但面子上已经挂不住了。最后这个项目只能含泪鸽置,<del> 实在太丑了没眼看 </del>

不过最近Gemini 3 Pro 让我燃起了希望啊,这个可能近期我会写完。

总结了一个文档,希望能帮到你,如果你也在写相关的:

https://github.com/grtsinry43/proj-dash-backend/blob/main/GITHUB_API_FEASIBILITY.md

ELK 日志系统:既然脸不要了,那就搞内脏

在 UI 上碰得满头包之后,我产生了逆反心理:行,既然我画不好皮,那我就去搞最底层、最枯燥的后端基建。

于是我开始折腾 ELK (Elasticsearch, Logstash, Kibana) 日志系统。

这是一个相当“重”的项目,主要是java太吃内存了。两天时间配完,搓完bff,看着成千上万条杂乱无章的日志被 Logstash 吞进去,然后整整齐齐地吐出来,有点治愈的哈哈哈。

折腾也很简单,一个compose加上自己设计bff收集就行了,前后端都可以的。

yml
1services:
2  elasticsearch:
3    image: elasticsearch: 8.11.0
4    container_name: elasticsearch
5    environment:
6      - discovery.type = single-node
7      - xpack.security.enabled = false # 开发时关闭安全验证,简化操作
8      - "ES_JAVA_OPTS =-Xms1g -Xmx1g" # 建议分配 1G 内存
9    ports:
10      - "9200:9200"

Vespera LightMonitor:回归极简,来点 Rust 哲学

折腾完沉重的 ELK,再看看我手里那几台配置感人的小鸡(VPS),我又感觉自己有点好笑,这ELK根本没地方部署。

于是看着市面上眼花缭乱的服务器探针, Vespera LightMonitor 诞生了。

这个用了Axum sqlx 已经上线了,bug慢慢修,等我用了一段时间稳定就开源然后写文档。

Verpera | grtsinry43's Server Monitor

Design System:试图建立秩序

经历了这几个月的胡搞瞎搞:从社区到 AI,从 UI 碰壁到沉迷日志后端,再到极简监控...

我也发现了,我做的东西太碎了。就像那个死掉的 Github Overview 一样,我每次都在重复造轮子,还在纠结圆角是 4px 还是 8px 这种无聊的问题。

所以,最近我在研究和创造一套属于自己的 设计系统 (Design System)

碎碎念

三个月里,我的每个项目,都是挤时间,在难受的时候,在无聊的时候,在实在感觉不想继续下去的时候,就连这篇文章也一样,流水帐的就像我的生活一样,其实这里的每个项目都可以展开为一篇文章,都有很多可以讲的,但是我还是等有余力将它们打磨好一个好的产品再汇报给每一个人吧,写下这些文字也算是一种解脱,至少证明我的生活还在继续,在这个重要的节点依然在输出,当然也有更多的输入。

回头看看这三个月,痛苦消失了吗?
害,其实也没有。深夜破防的时候该 emo 还是会 emo。

但好在,我没停下来。
从想法到实践,这中间的距离,大概就是我与自己和解的过程吧。

代码还得写,生活还得过,只要键盘还在响,就不算太糟糕。希望重新健康的日子,能早一点来吧。

相关推荐

Redux 数据仓库的数据持久化和 Next.js 中使用流程 | JustPure 起始页的构建历程

# Redux 数据仓库的数据持久化和 Next.js 中使用流程 | JustPure 起始页的构...

grtsinry43
3/7/2025
551
8
4

登山节微信网页实现思路简述 | 实现定位和数据处理

去年 10 月份左右,学校组织活动需要,要求 7 天内实现一个登山节打卡用微信网页,包含定位打卡,排...

grtsinry43
3/23/2025
475
1
3
构建一站式个人数字平台:在 2C2G 服务器上自建 Memos、Joplin、Gitea 与 Wiki.js

构建一站式个人数字平台:在 2C2G 服务器上自建 Memos、Joplin、Gitea 与 Wiki.js

# 构建一站式个人数字平台:在 2C2G 服务器上自建 Memos、Joplin、Gitea 与 W...

grtsinry43
5/4/2025
800
3
4

「手搓系列 01」 从零搭建 Vue 文档站,学习从静态生成到语法解析

最近在实习,好久不更新了,休假回校之前,准备开一个新的合集 「手搓系列」,我们从头实现一些习以为常的...

grtsinry43
9/3/2025
379
0
5

使用Spring Security快速实现Oauth2客户端配置

在Spring的强大生态中,我们很多时候只需要简单的配置就可以完成想要的需求。在Grtblog中,我...

grtsinry43
12/15/2024
1853
0
0

发表评论

在这里畅所欲言吧!
支持 Markdown 语法0 / 3000

网站运行时间

0
0
0
0

在风雨飘摇之中

感谢陪伴与支持

愿我们不负热爱,继续前行