前后端分离:约束 AI 分工,避免接口耦合与职责错乱

AI 很会写代码。但如果没有明确的架构边界,它也很容易出现一个问题:

哪里写起来方便,就把代码写到哪里。

本应由后端负责的业务规则,可能被塞进 Vue 页面;本应统一封装的接口调用,可能散落在各个组件里;前端甚至可能根据页面需求,自己"发明"一套与后端不一致的数据结构。

代码可能能运行,但项目会逐渐失去边界。

前后端分离,就是给 AI 划下的第一条重要边界。


📌 技术名片

前后端分离 / Frontend-Backend Separation

前端主要负责页面展示、用户交互和客户端状态 ,后端负责业务逻辑、权限、安全和数据存储

双方不直接侵入彼此内部,而是通过稳定的 **API(即:应用接口)**进行通信。

💡 一句话理解

可以把一个软件系统理解成一家餐厅。

  • 前端是前厅:负责菜单展示、接待、点餐和把结果呈现给顾客;
  • 后端是后厨:负责食材、烹饪、库存和真正的业务处理;
  • API就是订单。

服务员可以把订单送到后厨,但不应该跑进去炒菜;厨师负责做菜,也不应该跑到大厅自己修改菜单页面。

所以:

前后端分离的本质,并不是把代码放进两个文件夹,而是把职责边界明确下来。

这件事对 AI 编程尤其重要。

因为人类程序员通常知道:

这段代码虽然写在这里也能运行,但不应该写在这里。

AI 如果没有得到这样的架构规则,却往往会优先选择:

最快完成当前任务的写法。

久而久之,就很容易出现接口重复、业务逻辑下沉到前端、权限判断散落、数据结构不统一等问题。


一、架构规范:先告诉 AI 谁负责什么

要让 AI 真正遵守前后端分离,不能只告诉它一句:

text 复制代码
本项目采用前后端分离架构。

这句话过于抽象。更有效的方式,是把它变成 AI 可以直接执行的规则。

下面是 miniagent 的前后端分离的架构示意图:

进一步,可以把它固化成下面这些规则。

规则 1:前端不能直接操作数据库

数据库属于后端。

前端需要数据时,只能通过 应用接口/API 获取。

前端不需要知道数据库(例如:SQLite、MySQL、PostgreSQL)是否发生了更换。


规则 2:核心业务规则以后端为准

例如:

用户是否允许删除?

这个判断不能只写在前端:

ts 复制代码
if (user.role !== "admin") {
  disableDeleteButton()
}

前端可以隐藏按钮,用于改善用户体验。

真正的权限判断必须发生在后端,否则用户完全可以绕过页面直接调用 API。

因此:

前端负责体验,后端负责权威。


规则 3:前端不要自己"发明"接口

假设 AI 正在开发:

删除用户。

如果没有规则,它很可能直接在组件中写:

ts 复制代码
axios.delete(`/user/delete?id=${id}`)

另一个 AI 在后端可能实现成:

text 复制代码
DELETE /api/v1/admin/users/{id}

两边代码分别看都没什么问题。

合起来却无法工作。

所以应该规定:

新增前端功能之前,先检查已有 API;

新增后端 API 之前,先检查现有接口规范。


规则 4:接口调用必须统一封装

不要让页面里到处出现:

ts 复制代码
axios.get(...)
axios.post(...)
fetch(...)

而应该形成:

text 复制代码
Vue Component
      ↓
API Module
      ↓
HTTP Client
      ↓
Backend API

这样以后 Token、错误处理、超时、刷新 Token、日志等逻辑都可以统一处理。

使用成熟的开源前端框架是一条捷径:它已经统一封装好了与后端交互的常用方法。


规则 5:后端不能反过来依赖具体页面

后端提供的是:

text 复制代码
用户
知识库
Agent
会话
工具
权限

而不是:

text 复制代码
"用户管理页面的数据"
"首页右侧卡片的数据"
"某个按钮的数据"

页面容易变化,但是要尽量保障能力稳定。

因此后端应该提供业务接口,而不是围绕某一个 Vue 页面设计业务逻辑。


二、这样做有什么好处?

前后端分离听起来像是一种技术上的"分工",但它带来的好处其实非常直观。

尤其是在 AI 编程中,它最大的价值不是"看起来更规范",而是让整个项目更不容易越写越乱

1. AI 更容易知道代码应该写在哪里

如果没有明确边界,AI 接到一个需求时,很容易哪里方便就写哪里。

例如增加一个"用户是否可以删除"的判断。

没有规则时,AI 可能直接把判断写进前端页面;有了前后端分离以后,职责就很清楚:

text 复制代码
前端:
是否显示删除按钮

后端:
这个用户到底有没有删除权限

AI 不需要每次重新猜,项目也会越来越稳定。


2. 修改前端,不容易把后端一起改坏

一个系统的界面经常变化。今天按钮放左边,明天改成右边;今天用表格,明天改成卡片。

如果业务逻辑都混在页面里,每次改界面都有可能误伤核心功能。

前后端分离以后:

text 复制代码
页面怎么显示 -> 前端决定 
业务到底怎么执行 -> 后端决定

只要 API 不变,两边就可以相对独立地修改。


3. 多个前端可以复用同一个后端

这也是前后端分离非常实用的一点。

同一个后端可以同时服务:

text 复制代码
管理后台
普通用户网站
手机 App
小程序
其他系统

因为它们调用的都是同一套 API。

也就是说,后端提供的是"能力",而不是某一个具体页面。


4. AI 不容易重复造轮子

如果没有统一接口规范,AI 很容易每做一个功能,就重新写一套:

text 复制代码
请求方法
错误处理
权限判断
返回格式
接口地址

时间久了,一个项目可能出现很多种写法。

前后端分离再配合统一 API Client 后,AI 更容易复用已有方式。

例如:

text 复制代码
页面 -> 已有 API 模块 -> 统一 HTTP Client -> 后端

这样新功能通常只需要在现有结构上扩展,而不是重新发明一套方案。


5. 出问题时更容易排查

如果用户点击按钮后出现错误,可以沿着清晰的链路检查:

text 复制代码
页面有没有发出请求? -> API 有没有收到? -> 业务逻辑有没有执行? -> 数据库有没有正确返回?

问题在哪一层,会比较容易判断。

对于 AI 也是一样。你可以直接告诉它:

"前端请求正常,后端返回 500,请只检查后端。"

AI 的搜索范围会大幅缩小。


6. 更适合多人和 AI 一起开发

未来越来越常见的开发方式,很可能不是一个程序员从头写到尾,而是:

text 复制代码
人类开发者
技术负责人
前端开发者
后端开发者
AI 编程助手
AI Agent

共同参与一个项目。

这时候最怕的不是大家不会写代码,而是:

每个人都按照自己的理解写代码。

清晰的前后端边界,就像提前划好的施工区域;谁负责什么,接口在哪里,数据怎么交换,都比较明确。

因此:

前后端分离不仅是在分代码,更是在分责任。

而责任越清楚,AI 越容易稳定地参与大型项目开发。


三、AI 最容易出现的"失控现场"

假设我们告诉 AI:

给用户管理页面增加删除用户功能。

如果没有前后端边界,AI 很可能一路按照"当前任务最方便"的方式完成。

最终可能变成:

text 复制代码
UserList.vue
│
├── 拼接删除 URL
├── 判断管理员权限
├── 处理 Token
├── 定义错误提示
├── 调用 axios
├── 判断后端返回值
└── 刷新列表

与此同时,后端又独立实现:

text 复制代码
DELETE /user/{id}

并返回:

json 复制代码
{
  "success": true
}

而项目原本可能已经规定统一返回:

json 复制代码
{
  "code": 200,
  "message": "success",
  "data": {}
}

结果就是:

每一个局部功能都能写出来,

但整个项目逐渐出现多套接口、多套返回格式、多套权限判断和多套调用方式。

这正是 AI 编程非常隐蔽的一类风险。

AI 的问题往往不是不会实现功能,而是过于擅长局部解决问题。

架构真正要做的,就是不断提醒它:

不要只考虑"这段代码怎么写",还要考虑"这段代码应该属于哪里"。


四、提示词 / Prompt 落地:把架构边界真正告诉 AI

因此,我们可以把前后端分离规则写进项目级规则,例如:

text 复制代码
## 前端/后端边界

本项目严格遵循前后端分离原则。

前端职责:
- UI渲染
- 用户交互
- 客户端状态管理
- 表单验证(提升用户体验)
- 调用后端API

后端职责:
- 业务逻辑
- 授权
- 安全验证
- 数据持久化
- 数据库访问

规则:
- 前端代码绝不能直接访问数据库。
- 核心业务规则不能仅存在于前端代码中。
- 前端和后端只能通过已定义的API进行通信。
- 重用项目现有的HTTP客户端和API模块。
- 遵循现有的API路径和响应模式。
- 创建新API之前,请检查是否可以重用现有的API。
- 后端代码不得依赖于特定的前端页面或组件。

以后再让 AI 开发功能时,就不再只是:

text 复制代码
实现删除用户功能。

而是:

text 复制代码
在现有前后端分离架构下实现删除用户功能。

严格遵守项目现有的 Frontend / Backend Boundaries、
API 规范和 HTTP Client 封装。

先检查现有接口和目录结构,
优先复用已有实现,不要自行创建新的调用方式。

两种 提示词 看起来只是多了几句话。

但给 AI 的上下文完全不同。

第一种告诉 AI:

我要什么。

第二种同时告诉 AI:

我要什么,以及你必须在什么边界内完成。

所以:

提示词 / Prompt 不是架构的替代品,而是把架构传递给 AI 的载体。


五、正面产出:看看 miniagent 是怎么做的

以实际项目 miniagent 为例。

miniagent 是一个智能体平台,当前项目本身就采用了明确的前后端分离结构:

text 复制代码
miniagent/
├── backend/       # FastAPI 后端
├── management/    # PureAdmin 管理端
└── workplace/     # 普通用户工作台

其中:

  • backend 使用 FastAPI;
  • management 使用 Vue 3 + TypeScript + PureAdmin;
  • workplace 是独立的 Vue 3 用户端。

两个前端都通过 FastAPI API 获取业务能力,而不是直接访问数据库。miniagent 的 README 也明确给出了 Admin → APIWorkplace → API、再进入应用核心和数据层的结构。


1. 后端统一定义 API 边界

例如 miniagent 的用户管理接口在 FastAPI 中统一挂载到:

python 复制代码
app.include_router(
    admin_user_router,
    prefix="/api/v1/admin/users",
    tags=["Admin - User"]
)

也就是说:

text 复制代码
/api/v1/admin/users

是后端定义好的用户管理边界。

具体的删除用户接口则是:

python 复制代码
@router.delete(
    "/{user_id}",
    response_model=ApiResponse,
    summary="Delete user"
)
async def delete_user(
    user_id: int,
    svc: UserService = Depends(get_service),
    caller_id: int = Depends(_delete),
):
    await svc.delete(user_id)
    return ApiResponse()

这里有几个非常重要的架构信号。

首先,权限属于后端

python 复制代码
caller_id: int = Depends(_delete)

删除用户不能因为前端显示了一个"删除按钮",就认为用户拥有删除权限,真正的权限仍然由后端验证。

其次,API 层也没有直接操作数据库:

python 复制代码
await svc.delete(user_id)

它把真正的业务操作交给:

text 复制代码
UserService

于是形成:

text 复制代码
Frontend
    ↓
FastAPI Route
    ↓
UserService
    ↓
Repository
    ↓
Database

这就把职责一层层划清楚了。


2. 前端统一通过 API 模块访问后端

miniagent 的管理端也没有让页面随意拼接后端地址。

例如登录接口:

ts 复制代码
export const getLogin = (data?: object) => {
  return http.request<UserResult>(
    "post",
    baseUrlApi("login"),
    { data }
  );
};

Token 刷新同样复用统一 HTTP Client:

ts 复制代码
export const refreshTokenApi = (data?: object) => {
  return http.request<RefreshTokenResult>(
    "post",
    baseUrlApi("refresh-token"),
    { data }
  );
};

也就是说,业务代码统一经过:

text 复制代码
http.request(...)

而不是每一个 Vue 页面自己创建 axios 或 fetch 调用。

API 前缀也没有散落硬编码,而是统一定义:

ts 复制代码
export const baseUrlApi = (url: string) =>
  `/api/v1/${url}`;

于是:

ts 复制代码
baseUrlApi("login")

得到:

text 复制代码
/api/v1/login

这实际上是在给 AI 一个非常明确的信号:

需要调用后端时,沿着现有 API 层扩展,而不是在组件里另起炉灶。


3. 后端内部继续保持自己的分层

miniagent 并不是简单地把 VuePython 分成两个目录就结束了。

它的后端内部继续划分为:

text 复制代码
app/api/           HTTP 接口
app/services/      业务逻辑
app/runtime/       Agent 运行时
app/repositories/  数据访问
app/schemas/       数据模型
app/infra/         基础设施

这些职责在项目 README 中也有明确说明。所以整个系统实际上形成:
#mermaid-svg-joqKJIldUzVtqjbC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-joqKJIldUzVtqjbC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-joqKJIldUzVtqjbC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-joqKJIldUzVtqjbC .error-icon{fill:#552222;}#mermaid-svg-joqKJIldUzVtqjbC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-joqKJIldUzVtqjbC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-joqKJIldUzVtqjbC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-joqKJIldUzVtqjbC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-joqKJIldUzVtqjbC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-joqKJIldUzVtqjbC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-joqKJIldUzVtqjbC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-joqKJIldUzVtqjbC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-joqKJIldUzVtqjbC .marker.cross{stroke:#333333;}#mermaid-svg-joqKJIldUzVtqjbC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-joqKJIldUzVtqjbC p{margin:0;}#mermaid-svg-joqKJIldUzVtqjbC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-joqKJIldUzVtqjbC .cluster-label text{fill:#333;}#mermaid-svg-joqKJIldUzVtqjbC .cluster-label span{color:#333;}#mermaid-svg-joqKJIldUzVtqjbC .cluster-label span p{background-color:transparent;}#mermaid-svg-joqKJIldUzVtqjbC .label text,#mermaid-svg-joqKJIldUzVtqjbC span{fill:#333;color:#333;}#mermaid-svg-joqKJIldUzVtqjbC .node rect,#mermaid-svg-joqKJIldUzVtqjbC .node circle,#mermaid-svg-joqKJIldUzVtqjbC .node ellipse,#mermaid-svg-joqKJIldUzVtqjbC .node polygon,#mermaid-svg-joqKJIldUzVtqjbC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-joqKJIldUzVtqjbC .rough-node .label text,#mermaid-svg-joqKJIldUzVtqjbC .node .label text,#mermaid-svg-joqKJIldUzVtqjbC .image-shape .label,#mermaid-svg-joqKJIldUzVtqjbC .icon-shape .label{text-anchor:middle;}#mermaid-svg-joqKJIldUzVtqjbC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-joqKJIldUzVtqjbC .rough-node .label,#mermaid-svg-joqKJIldUzVtqjbC .node .label,#mermaid-svg-joqKJIldUzVtqjbC .image-shape .label,#mermaid-svg-joqKJIldUzVtqjbC .icon-shape .label{text-align:center;}#mermaid-svg-joqKJIldUzVtqjbC .node.clickable{cursor:pointer;}#mermaid-svg-joqKJIldUzVtqjbC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-joqKJIldUzVtqjbC .arrowheadPath{fill:#333333;}#mermaid-svg-joqKJIldUzVtqjbC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-joqKJIldUzVtqjbC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-joqKJIldUzVtqjbC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-joqKJIldUzVtqjbC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-joqKJIldUzVtqjbC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-joqKJIldUzVtqjbC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-joqKJIldUzVtqjbC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-joqKJIldUzVtqjbC .cluster text{fill:#333;}#mermaid-svg-joqKJIldUzVtqjbC .cluster span{color:#333;}#mermaid-svg-joqKJIldUzVtqjbC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-joqKJIldUzVtqjbC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-joqKJIldUzVtqjbC rect.text{fill:none;stroke-width:0;}#mermaid-svg-joqKJIldUzVtqjbC .icon-shape,#mermaid-svg-joqKJIldUzVtqjbC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-joqKJIldUzVtqjbC .icon-shape p,#mermaid-svg-joqKJIldUzVtqjbC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-joqKJIldUzVtqjbC .icon-shape .label rect,#mermaid-svg-joqKJIldUzVtqjbC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-joqKJIldUzVtqjbC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-joqKJIldUzVtqjbC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-joqKJIldUzVtqjbC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} REST / SSE
REST / SSE
Management
Workplace
FastAPI API
Services
Runtime / Repository
Database / Vector DB

DuckDB / Files ...

当 AI 再接到:

增加用户功能

增加知识库功能

增加 Agent 管理功能

这样的需求时,它面对的就不再是一片空白,而是一个已经划好施工区域的项目:

text 复制代码
页面写在哪里
API 写在哪里
业务逻辑写在哪里
数据库操作写在哪里
权限在哪里检查
前端如何访问 API

都有现成边界可以遵循。


结语

前后端分离对 AI 编程最大的意义,并不是把项目拆成:

text 复制代码
frontend/
backend/

两个目录。

真正重要的是,它第一次明确告诉 AI:

什么事情属于前端,什么事情属于后端,以及两者只能通过什么方式协作。

AI 的能力越强,能够一次修改的代码越多,这种边界反而越重要。

因为优秀的软件架构从来不是为了限制开发效率,恰恰相反:

架构通过限制错误的自由,换来了正确的自由。

对于 AI 编程来说也是如此。

先划定边界,再让 AI 在边界内发挥能力。

这才是前后端分离在 AI 时代真正值得重新理解的地方。

开源代码

相关推荐
凌杰1 小时前
关于机器恐惧症的个人观点汇总
人工智能
IT_陈寒1 小时前
Vue的v-for不听话?我被这个Key的坑整懵了
前端·人工智能·后端
水獭比特1 小时前
localhost 不是安全边界:给 Agent Web 入口补上四层门禁
人工智能·python
赟爸2 小时前
直播切片素材杂乱不好复用,易元AI要怎么处理
大数据·人工智能·python
文叔叔2 小时前
我开源了一个 Codex Desktop ↔ Claude Desktop 双向调用工具
人工智能
YHL2 小时前
📚 RAG 文档切割详解
人工智能
刘立军2 小时前
领域驱动设计:给 AI 划定上下文边界,告别“大泥球”代码
架构·ai编程·领域驱动设计
qpsj2 小时前
让 LLM 操作 CAD:四条技术路线的取舍
人工智能·llm
东方小月2 小时前
从零开发一个 Coding Agent(十):实现 CLI 参数解析与静态命令
前端·人工智能·开源