ladydd

MCP

传统 SaaS 转向 AI 时代,我目前的一点理解:先把数据能力变成 Agent 可调用的基础设施

最近我一直在思考一个问题:传统 SaaS 到底应该怎么转向 AI? 一开始很容易想到的方向是:给原来的系统加一个 AI 助手。 比如在页面右下角放一个聊天框,让用户可以问数据、生成报告、总结内容、解释指标。这个当然有价值,但我现在越来越觉得,这只是比较表层的一种转型。 真正的变化,可能不是“在 SaaS 里面加 AI”,而是 SaaS 本身的能力形态发生变化。 过去的 SaaS,核心是给人使用。 人登录系统,看页面、点按钮、筛选数据、导出报表、判断问题,然后再去做决策。数据库是给 Web 页面供数的,后端 API 是给前端页面服务的,整个产品的中心是“人如何操作软件”。 但 AI 时代,尤其是 Agent 逐渐发展之后,

By ladydd

Python

对 Python 应用场景的一次重新思考:FastAPI、协程、线程、数据库与任务系统边界

最近在重新设计一个任务系统时,我顺便把自己对 Python,尤其是 CPython 应用场景的理解重新梳理了一遍。 这次讨论的背景是一个典型的异步任务服务: 上游提交任务 API 立即返回 task_id 后台 worker 慢慢执行 用户通过 task_id 查询任务状态 任务主要是 LLM 调用、图片下载、外部 HTTP 请求这类 I/O 型工作。 一开始关注的是队列、Redis、PostgreSQL、worker 并发控制这些问题。但聊到后面,其实更核心的问题变成了: Python 到底应该放在什么位置? 哪些并发适合 Python? 哪些并发不要硬塞给 Python? FastAPI、协程、线程、数据库之间应该怎么分工? 这篇文章就是这次思考的整理。 一、我不想抛弃 Python,

By ladydd

Go

Go 和 Python 的并发模型对比:进程、线程、协程、并发和并行到底怎么理解?

最近我在写 worker 任务系统的时候,重新理解了一遍 Python 和 Go 的并发差异。 以前写 Python,多 worker 经常要考虑: 多进程怎么管理? 日志会不会串? 一个 worker 崩了怎么办? 怎么吃满多核心? 后来换成 Go,发现一个进程里开多个 goroutine worker 就很自然: go worker(1) go worker(2) go worker(3) go worker(4) 日志也好管,状态也好管,而且单进程还能利用多个 CPU 核心。 一开始很容易误会成: Python 不行,Go 行 但更准确的理解应该是: Python 和

By ladydd

Go

Python 进程和 Go 进程的区别:为什么 Go 单进程多 worker 用起来更爽?

最近我在做 worker 任务系统的时候,突然意识到一个很关键的问题: 以前写 Python,多 worker 的时候经常要小心日志串、文件切割乱、时间不好管理。 但是换成 Go 以后,一个进程里开多个 goroutine worker,反而可以比较自然地写到同一个日志文件里。 一开始我以为这是“Python 和 Go 写日志能力不一样”,后来想明白了,核心不是日志本身,而是: Python 常见 worker 模型:多进程 Go 常见 worker 模型:单进程 + 多 goroutine 这背后其实是两个语言在并发模型上的巨大差异。 一、进程、线程、goroutine 先分清楚 先把几个概念捋一下。 进程:操作系统分配资源的单位 线程:CPU 调度执行的基本单位

By ladydd

Go

从 Redis + Channel 到 Redis Stream:一次 Go 任务队列方案的重新理解

在讨论 PostgreSQL queue 方案之后,我又回头看了一下之前自己设想过的 Go + Redis 任务架构。 最早我脑子里的方案大概是: POST 创建任务 Redis 存状态 Go goroutine 直接处理 进程内 channel 控制并发 这个方案很直观,也很符合 Go 的写法。 用户请求进来后,API 生成一个 task_id,把任务状态写进 Redis,然后把任务塞进 Go 的 channel,由后台 goroutine 消费执行。并发控制就靠一个固定大小的 channel 或 worker pool,比如最多 20 个 goroutine 同时执行任务。 这个设计在单进程里确实很舒服。 但当我开始思考多进程、多容器、

By ladydd

架构

Just Use Postgres:用一个数据库吃掉你架构里 80% 的中间件

"What can't Postgres do?" —— 这两年技术圈最流行的一句话 "Use one database, but use it really well." —— Hacker News 上一条 2k+ 赞的评论 写在最前面:一张让人窒息的架构图 先看一张图,看看你眼熟不: [客户端] │ ▼ [API Gateway] ┌─────────────────┼─────────────────┐ ▼ ▼ ▼ [业务服务] [认证服务] [搜索服务] │ │ │ ├──→ MySQL ├──→ Redis ├──→ Elast

By ladydd

架构

从 Redis 到 PostgreSQL:一次需求驱动的任务系统架构升级

最近我对一个 LLM 任务服务做了一次比较完整的架构升级。 这个服务本身并不复杂:上游提交任务,我返回一个 task_id,后续上游再通过 task_id 查询任务状态和结果。任务的执行内容主要是调用 LLM 或相关多模态接口,单个任务耗时通常在几十秒以内。 最开始的实现是比较常见的组合: FastAPI + Redis + Docker API 接到请求后生成任务 ID,把状态写入 Redis,然后由后台逻辑继续执行任务。这个方案在早期足够轻量,开发速度也很快。 但随着需求继续演进,我开始更明确地追求几个能力: 1. 上游可以瞬间提交大量任务 2. API 必须快速返回 task_id 3. 真正执行的任务数量必须全局可控 4. 任务状态要可查询、可恢复、可追踪 5. worker 崩溃后任务不能永久卡死 6. 未来希望减少组件数量,尽量 all

By ladydd

MCP

JSON-RPC / MCP 为什么看起来很像“固定一个 POST 接口”?

理解 MCP、JSON-RPC、Streamable HTTP 的过程中,有一个很自然的联想: 这不就是固定一个 POST 接口,然后传入不同 JSON,根据请求体里的字段返回不同响应吗? 这个直觉是对的。 尤其是当 MCP 走 Streamable HTTP 的时候,表面上看确实很像: POST /mcp 然后请求体里传: { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "search", "arguments"

By ladydd

MCP

从 MCP 到 JSON-RPC,再到 Streamable HTTP:理清 Agent 工具调用里的协议关系

做 MCP 相关项目时,经常会遇到几个词: MCP JSON-RPC Transport stdio SSE Streamable HTTP 这些词经常一起出现,如果不把层次拆开,很容易混在一起。 这篇文章尝试从工程开发的角度,把它们之间的关系梳理清楚。重点不是翻译标准文档,而是建立一套比较稳定的理解模型。 一、先分清三层:MCP、JSON-RPC、Transport 可以先记住这句话: MCP 负责:Agent 能调用什么能力 JSON-RPC 负责:调用消息长什么样 Transport 负责:消息怎么传过去 也就是: MCP = 协议语义层 JSON-RPC = 消息格式层 Transport = 传输层 更直观一点: MCP: 我要调用工具、读取资源、获取 prompt JSON-RPC: method 是什么,

By ladydd

AI

vLLM 四卡部署 Embedding 模型实战:离线部署、Nginx 负载均衡、FastAPI 256D 网关与 systemd 自启

最近把一套 Embedding 服务完整落地了一遍:4 张显卡分别启动 vLLM 实例,用 Nginx 做统一入口和故障切换,再在上层挂一个 FastAPI 网关,把原始向量统一裁剪成 256 维并归一化,最终形成一套比较完整、可直接对外提供服务的 Embedding 架构。 这篇文章把整个过程完整整理一下,包含环境准备、离线模型部署、多卡启动、Nginx 配置、systemd 开机自启,以及业务网关设计。 整套架构的目标很明确: * 提供标准 HTTP Embeddings API * 支持四卡并行 * 支持统一入口与负载均衡 * 单实例故障时自动 failover * 支持开机自启 * 保留日志,便于运维与统计 一、整体架构 先看整体结构: Client │ ▼ FastAPI Gateway(8681) ← 推荐对外入口 │ ▼ Nginx(

By ladydd

Python

多 worker 场景下,Python 日志按天落盘为什么会“串日期”

在一个多 worker 的 Python 服务里,日志按天落盘本来是件很普通的事,但真正跑进 Docker 和多进程环境之后,事情往往没有想象中那么简单。我们这次遇到的,就是一个非常典型、也非常容易被忽视的问题:日志文件日期彻底混乱了。 最离谱的时候,目录里会看到一个名字像这样的文件: app.log.2026-04-18 但打开之后,里面却混进了 2026-04-23 的日志。 这种现象第一眼看上去很玄学,实际上原因并不玄学。核心就是一句话: 多 worker 进程同时写同一个轮转日志文件,天然就容易出事。 这篇文章把这个问题的成因、误区,以及我们当前先落地的临时方案整理一下。也提前说明:这不是最终版日志架构,只是先把现在线上最痛的跨天串文件问题止住。 背景 项目运行在 Docker 里,服务启动方式是: uvicorn src.main:app --host 0.0.0.0

By ladydd

AI

一次看起来像“模型不兼容”,其实是 `max_tokens` 把输出截断了

最近在给这套商品图提示词服务接入新的 OpenAI-compatible 供应商时,我们踩了一个很典型、也很容易误判的坑: * 同样是 /chat/completions * 同样是 OpenAI 兼容请求格式 * 旧供应商 qwen3-vl-plus 跑得很正常 * 新供应商 gemini-3-flash-preview 却频繁只返回半句话 最开始看现象,直觉很容易往这几个方向怀疑: * 这个供应商的 OpenAI 兼容实现不完整 * 多模态图片传递格式不对 * 响应解析逻辑没兼容到位 * 模型本身不稳定 但最后复盘下来,真正的主因比想象中直接很多:我们显式传了 max_tokens,而这个第三方 OpenAI-compatible 供应商上的 gemini-3-flash-preview 组合,很可能把图片理解和推理消耗也算进了输出预算里,导致最终文本阶段只剩下一小截。 这篇文章就把整个问题、排查过程和最终处理方法完整记录一下。 背景 我们的服务很简单: 1. 前端提交商品标题、图片 URL、模型配置 2. 后端调用 Op

By ladydd
陕公网安备61011302002223号 | 陕ICP备2025083092号