NAVIGATION SYS
首页折腾手记造点东西投资笔记做点音乐想点事情

NCC-1701-D // SYSTEM ONLINE

Deep Space Viewport
造点东西

AI开发配置系统(三):AI懂个锤子架构?ConfigBridge后端演进实录

舸扬
造点东西
发布: 2025-11-15
更新: 2026-02-02
标签configbridge系列配置管理
AI开发配置系统(三):AI懂个锤子架构?ConfigBridge后端演进实录
本文字数:3297预计阅读:9 分钟

📖 本文是「AI开发配置系统」系列第三篇。上一篇:AI开发配置系统(二):我与AI的72小时:从零打造ConfigBridge

TL;DR(太长不看版)

  • 核心论点:AI 是高效的"战术执行者",但架构决策仍需人类主导
  • 业务挑战:如何用一套模板管理 IPv6/IPv4 混排的多台服务器差异化配置
  • 解决方案:设计带上下文的"服务-服务器"关联模型,通过"四层优先级"实现变量覆盖
  • 技术实战:SQLAlchemy 2.0 异步重构 + Jinja2 结构化渲染上下文
  • 成果:三天完成 ConfigBridge 后端核心,重构了人机协作的开发流

写在前面:一个困扰我很久的问题

在开始技术细节之前,我想先聊聊一个更大的问题——AI 到底能做什么?

过去一年,我几乎所有的代码都是和 AI 一起写的。Cursor、Copilot、Claude... 这些工具让我的编码效率提升了好几倍。但用得越多,我越发现一个诡异的现象:

AI 写的代码看起来都很"正确",但合在一起却经常"不对"。

什么意思呢?比如我让 AI 写一个用户登录功能,它给的代码语法没问题、逻辑也通顺。但当我把它放到整个系统里,就开始出问题:它不知道我们用的是 JWT 还是 Session(身份验证方式);不知道密码要不要单独存一张表;不知道这个登录接口以后可能要支持第三方 OAuth 登录...

它只看到了"这一棵树",看不到"整片森林"。

这让我开始思考一个问题:如果 AI 擅长的是"执行"而不是"规划",那在软件开发的整个链条里,哪些环节适合交给 AI,哪些环节必须自己来?

带着这个问题,我开始了 ConfigBridge 后端的开发。一段时间下来,答案渐渐清晰了。

先说点背景:什么是"后端架构设计"?

如果你是前端工程师,或者刚入行的开发者,可能对"后端架构"这个词感到有点抽象。让我用大白话解释一下:

想象你要开一家餐厅。前端就是餐厅的门面——装修风格、菜单设计、服务员怎么跟客人说话。后端则是厨房——食材怎么存放、厨师怎么分工、订单怎么流转。

架构设计,就是在餐厅开业之前,先想清楚:

  • 厨房要分几个区域?冷菜和热菜要不要分开?
  • 食材从采购到上桌,经过哪些步骤?
  • 如果生意好了要扩张,现有的厨房能不能改造?

回到软件开发,后端架构要回答的问题是:

  • 数据怎么存? 用户信息、订单记录、配置文件... 这些数据之间是什么关系?怎么设计数据库表?
  • 逻辑怎么组织? 代码分成几层?哪些功能放在一起,哪些要分开?
  • 未来怎么扩展? 如果用户量翻 10 倍,现在的设计撑得住吗?

架构设计的难点在于:它没有标准答案。同一个需求,可以有十种不同的设计方案,每种都"能用",但有的容易维护、有的难以扩展、有的性能好、有的开发快...

选择哪种方案,取决于你对业务的理解、对未来的预判、以及你愿意在哪些地方做妥协。

这正是 AI 最不擅长的地方。

第一章:AI 的能力边界——一次真实的对话

让我给你看一段真实的交互记录。

当我开始做 ConfigBridge 后端时,我在 Cursor 里输入了这段话:

"我需要一个配置管理系统的后端。功能是:管理多个 Web 服务、管理多台服务器、根据模板生成配置文件。技术栈用 FastAPI + SQLAlchemy。"

三秒钟后,AI 开始疯狂输出。它创建了目录结构、定义了数据库模型、写好了 CRUD(增删改查)接口... 一口气输出了几百行代码。

看起来很厉害,对吧?

但当我仔细看这些代码时,问题来了:

python
# AI 给出的数据库设计 class Service(Base): id = Column(Integer, primary_key=True) name = Column(String, unique=True) port = Column(Integer) class Server(Base): id = Column(Integer, primary_key=True) hostname = Column(String) ip_address = Column(String) class ConfigFile(Base): id = Column(Integer, primary_key=True) # 主键 name = Column(String) template = Column(Text) service_id = Column(Integer, ForeignKey('services.id')) # 外键:关联到 Service 表

这个设计的问题是什么?

AI 把 ConfigFile(配置文件)设计成了属于某个特定 Service(服务)。这意味着:如果我有两个服务都需要用同一个 Nginx 模板,我得复制两份。

更深层的问题是:AI 不知道我真正想做的事情是——一套配置模板,能在多台服务器上生成不同的配置文件。比如,同样是 myapp 这个服务:

  • 在服务器 A(IPv6)上,Nginx 要监听 [::]:80
  • 在服务器 B(IPv4 + FRP)上,Nginx 只需要监听 80,而且后端端口要改成 FRP 转发的端口

这不是一张表能解决的问题,而是需要"多对多关联 + 上下文信息"的设计。

但 AI 怎么可能知道这些呢?它无法自发地考虑到如此周全。

第二章:一个关于 AI 的哲学思考

这次经历让我开始更深入地思考 AI 的本质。

AI 是"超级实习生",不是"资深架构师"

我见过很多对 AI 的误解。有人觉得它无所不能,有人觉得它一无是处。这两种看法都是错的。

更准确的类比是:AI 是一个知识渊博但缺乏经验的超级实习生。

它读过几乎所有的技术文档,背得滚瓜烂熟;它写过各种语言的代码片段,语法从不出错;它能在几秒钟内给你一个"标准答案"。

但它没有真正"做过"一个项目。它不知道:

  • 这个设计在三个月后会不会变成维护噩梦
  • 那个"更优雅"的方案是否真的适合这个团队的技术栈
  • 某个看似多余的抽象层,是为了应对半年后的需求变更

经验不是知识的堆砌,而是无数次"踩坑-反思-迭代"后沉淀下来的直觉。

AI 有知识,但没有直觉。

人类的价值在于"为什么",AI 的价值在于"怎么做"

换个角度理解:软件开发可以分成两个层面:

层面
核心问题
谁更擅长?
战略层为什么要做这个功能?用什么方案?如何权衡取舍?人类
战术层具体怎么实现?代码怎么写?语法怎么用?AI

当我告诉 AI"我需要一个配置管理系统"时,它能快速输出代码——这是战术层。

但它不会问我:

  • "你的服务器环境是什么?有没有 IPv6 的问题?"
  • "配置变更后需要立即生效还是可以批量部署?"
  • "未来可能接入多少台服务器?需要考虑多租户吗?"

这些"为什么"的问题,决定了架构的方向。方向错了,执行再快也没用。

这就是为什么我说:"AI 懂个锤子架构"——不是因为它笨,而是因为架构设计需要的不是知识,而是对业务的深度理解和对未来的预判

最佳协作模式:人类当"产品经理 + 架构师",AI 当"高级搬砖工"

想通了这一点,我的工作方式发生了变化:

  1. 我负责定义"规格说明书"——不是模糊的需求,而是详细到每个数据表、每个字段、每个约束条件
  2. AI 负责快速实现——根据我的规格生成代码、写测试、处理边界情况
  3. 我负责审阅和调整——检查它的实现是否符合预期,有没有遗漏或误解

这种模式下,我的角色不是写代码,而是设计系统、定义规则、把控质量

AI 把我从"高级打字员"变成了"技术决策者"。

第三章:数据库设计实战——从"能跑"到"好用"

好,哲学思考够多了。接下来进入硬核部分:我是怎么设计 ConfigBridge 数据库的?

需求再梳理

先把真正的需求写清楚:

  1. 一个服务(如 myapp)可以部署在多台服务器
  2. 一个配置模板(如 nginx-reverse-proxy.j2)可以被多个服务+服务器组合使用
  3. 同一个服务在不同服务器上,生成的配置内容可能不同(变量覆盖)
  4. 系统需要记录每次配置生成的历史

最终的数据模型

经过几轮迭代,我设计了这样的模型:

python
# 服务表 class Service(Base): __tablename__ = 'services' id = Column(Integer, primary_key=True) name = Column(String(100), unique=True, nullable=False) subdomain = Column(String(100), nullable=False) # 子域名 port = Column(Integer, nullable=False) # 默认端口 description = Column(Text) # 多对多关联 server_associations = relationship('ServiceServerAssociation', back_populates='service') # 服务器表 class Server(Base): __tablename__ = 'servers' id = Column(Integer, primary_key=True) name = Column(String(100), unique=True, nullable=False) hostname = Column(String(255), nullable=False) ipv6_enabled = Column(Boolean, default=False) # 关键字段:是否支持 IPv6 # 多对多关联 service_associations = relationship('ServiceServerAssociation', back_populates='server') # 服务-服务器关联表(核心!) class ServiceServerAssociation(Base): __tablename__ = 'service_server_associations' id = Column(Integer, primary_key=True) service_id = Column(Integer, ForeignKey('services.id'), nullable=False) server_id = Column(Integer, ForeignKey('servers.id'), nullable=False) enabled = Column(Boolean, default=True) # 是否启用 priority = Column(Integer, default=0) # 部署优先级 # 关系 service = relationship('Service', back_populates='server_associations') server = relationship('Server', back_populates='service_associations') # 唯一约束:同一个服务在同一台服务器上只能有一条关联 __table_args__ = (UniqueConstraint('service_id', 'server_id'),)

关键设计决策:关联表不只是关系

注意 ServiceServerAssociation 这张表。它不仅仅是存储"服务和服务器的多对多关系",还存储了这个组合特有的上下文信息

  • enabled:这个服务在这台服务器上是否启用?
  • priority:如果同时部署多台,优先级是什么?

这个设计是 AI 想不到的。 因为它需要业务理解——理解"服务在不同服务器上的状态是不同的"这个隐含需求。

变量继承:四层优先级系统

更复杂的是变量管理。我需要实现这样的逻辑:

TERMINAL
变量 backend_port 的值查找顺序: 1. myapp + server-b 的专属设置 → 8081 ✓(最优先) 2. server-b 的默认设置 → (未定义) 3. myapp 的默认设置 → 8080 4. 全局默认设置 → (未定义)

这需要专门的变量表和合并逻辑:

python
class ServiceVariable(Base): """服务级变量(可选绑定到特定服务器)""" __tablename__ = 'service_variables' id = Column(Integer, primary_key=True) service_id = Column(Integer, ForeignKey('services.id'), nullable=False) server_id = Column(Integer, ForeignKey('servers.id'), nullable=True) # None = 服务默认值 name = Column(String(100), nullable=False) value = Column(Text, nullable=False) # 唯一约束 __table_args__ = (UniqueConstraint('service_id', 'server_id', 'name'),)

变量合并的业务逻辑:

python
class VariableService: """四层变量优先级合并服务""" async def get_merged_variables( self, service_id: int, server_id: int, db: AsyncSession ) -> dict: """ 按优先级合并变量,返回最终的变量字典。 优先级:服务-服务器 > 服务器 > 服务 > 全局 """ result = {} # Layer 4: 全局变量(最低优先级) global_vars = await db.execute(select(GlobalVariable)) for var in global_vars.scalars().all(): result[var.name] = var.value # Layer 3: 服务默认变量 service_vars = await db.execute( select(ServiceVariable).where( ServiceVariable.service_id == service_id, ServiceVariable.server_id.is_(None) ) ) for var in service_vars.scalars().all(): result[var.name] = var.value # Layer 2: 服务器默认变量 server_vars = await db.execute( select(ServerVariable).where( ServerVariable.server_id == server_id ) ) for var in server_vars.scalars().all(): result[var.name] = var.value # Layer 1: 服务-服务器专属变量(最高优先级) ss_vars = await db.execute( select(ServiceVariable).where( ServiceVariable.service_id == service_id, ServiceVariable.server_id == server_id ) ) for var in ss_vars.scalars().all(): result[var.name] = var.value return result

这段代码 AI 实现得很好——语法正确、逻辑清晰、甚至加了完整的注释。但"四层优先级"这个设计思路是我给的。AI 不会自己想出这种方案,因为它不知道"同一个服务在不同服务器上配置可能不同"这个具体的业务场景。

第四章:配置渲染引擎——Jinja2 深度运用

数据模型设计好了,下一步是配置渲染。

需求

配置模板需要支持:

  • 变量替换:{{ vars.backend_port }}
  • 条件判断:{% if server.ipv6_enabled %}
  • 循环:{% for upstream in upstreams %}
  • 过滤器:{{ value | default('fallback') }}

Jinja2 是 Python 生态的事实标准,没什么好犹豫的。

关键设计:结构化上下文

为了让模板作者能清晰地访问不同来源的数据,我设计了这样的渲染上下文:

python
class ConfigRenderer: """配置渲染引擎""" def __init__(self): self.env = Environment( loader=BaseLoader(), autoescape=select_autoescape(), trim_blocks=True, lstrip_blocks=True, undefined=StrictUndefined # 未定义变量直接报错,避免生成无效配置 ) # 注册自定义过滤器 self.env.filters['quote'] = lambda v: f'"{v}"' self.env.filters['to_yaml'] = lambda v: yaml.dump(v, default_flow_style=False) def render( self, template_content: str, service: Service, server: Server, merged_variables: dict, global_config: dict ) -> str: """ 渲染配置模板。 模板中可用的命名空间: - {{ service.name }}, {{ service.port }}, {{ service.subdomain }} - {{ server.hostname }}, {{ server.ipv6_enabled }} - {{ vars.backend_port }}, {{ vars.listen_port }} - {{ global.base_domain }}, {{ global.ssl_enabled }} """ template = self.env.from_string(template_content) # 构建结构化上下文 context = { 'service': self._to_dict(service), 'server': self._to_dict(server), 'vars': merged_variables, 'global': global_config } return template.render(**context) def _to_dict(self, obj) -> dict: """将 SQLAlchemy 对象转换为字典""" return {c.name: getattr(obj, c.name) for c in obj.__table__.columns}

命名空间设计的好处:模板作者一眼就能知道每个变量从哪来——service 是服务本身的属性,server 是服务器的属性,vars 是合并后的自定义变量,global 是全局配置。

第五章:AI 的高光时刻——秒解技术卡点

(注:本章包含较多技术细节,非开发人员可仅阅读排查思路)

虽然架构决策需要人类主导,但 AI 在解决具体技术问题时的表现堪称惊艳。

案例 1:Alembic 迁移地狱

当我添加"服务变量可选绑定服务器"功能时,需要给 ServiceVariable 表加一个可空外键。运行迁移命令:

bash
alembic revision --autogenerate -m "add optional server_id"

报错:

TERMINAL
ERROR: Can't locate revision identified by 'a1b2c3d4e5f6'

我花了半小时查文档、搜 Stack Overflow,毫无头绪。

把错误丢给 AI,5 秒钟定位到问题:

"这是因为 alembic_version 表里记录的版本和本地 versions/ 目录不匹配。你可能删过某个迁移文件。"

并给出修复方案:

bash
# 查看当前版本 alembic current # 强制同步到最新 alembic stamp head # 重新生成迁移 alembic revision --autogenerate -m "add optional server_id"

一次跑通。如果没有 AI,这个问题可能要耗掉我一个下午。

案例 2:SQLAlchemy 2.0 异步查询

把项目改成异步时,遇到了一堆 greenlet_spawn has not been called 错误。

AI 不仅定位到问题(忘了用 await),还顺手重构了整个仓储层:

python
# Before: 同步 class ServiceRepository: def get_all(self, db: Session) -> list[Service]: return db.query(Service).all() # After: AI 重构的异步版本 class ServiceRepository: async def get_all(self, db: AsyncSession) -> list[Service]: result = await db.execute(select(Service)) return result.scalars().all()

案例 3:Pydantic V2 迁移

Pydantic(一个极其流行的数据验证库)从 V1 升级到 V2 时,废弃了 @validator,换成 @field_validator。AI 帮我批量重构了所有 Schema,连类型注解都加上了:

python
# Before (V1) @validator('port') def port_must_be_valid(cls, v): if not 1 <= v <= 65535: raise ValueError('Port must be between 1 and 65535') return v # After (V2) @field_validator('port') @classmethod def port_must_be_valid(cls, v: int) -> int: if not 1 <= v <= 65535: raise ValueError('Port must be between 1 and 65535') return v

结语:架构师没有失业,但得换个活法

几周下来,ConfigBridge 的后端完成了。回头看这段经历,我的感触是:

AI 是最强大的"执行者",但它不是"决策者"。

维度
人类主导
AI 辅助
理解业务需求❌ 只能处理明确描述的需求
架构设计决策⚠️ 能给建议,但缺乏上下文
代码实现⚠️ 能写但慢✅ 快速、准确、风格一致
Debug 定位⚠️ 需要时间✅ 秒级响应
技术选型✅ 需要考虑团队和生态⚠️ 只能给选项

架构师的新使命:不是写更多代码,而是做更好的决策。

把重复性的编码工作交给 AI,把精力放在真正重要的事情上:

  • 理解业务:用户到底需要什么?
  • 设计系统:怎么分拆、怎么扩展、怎么演进?
  • 定义规范:代码风格、API 契约、测试策略
  • 审阅把关:AI 的输出是否符合预期?

AI 把程序员从"高级打字员"变成了"技术决策者"。

这不是替代,是升级。

📌 本篇要点

话题
收获
AI 的能力边界擅长"怎么做",不擅长"为什么这样做"
数据库设计关联表可存储上下文信息,不只是关系
变量优先级四层覆盖机制解决多环境差异
配置渲染结构化命名空间让模板可读性大增
人机协作人类当架构师,AI 当高级搬砖工

📚 AI开发配置系统系列导航

下一篇(收官篇):AI开发配置系统(四):拒绝程序员审美:AI助力ConfigBridge前端逆袭

END OF LOG_
ID: CONFIGBR