尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

分层整洁架构:标准化工程目录结构,防范 AI 越界调用

分层整洁架构:标准化工程目录结构,防范 AI 越界调用 文章目录 技术名片 一句话理解一、为什么 AI 特别需要“分层”二、架构规范给每一层明确职责1. API 层负责“接待”2. Service 层负责“业务”3. Repository / 数据访问层负责“怎么拿数据”4. Schema / DTO负责数据长什么样5. Runtime负责复杂运行能力6. Infrastructure负责技术基础设施三、真正重要的是调用要有方向规则 1API 不直接操作数据库规则 2API 不承载核心业务逻辑规则 3Repository 不负责业务决策规则 4上层通过稳定接口调用下层规则 5禁止为了方便跨层调用四、这样做有什么好处1. AI 更容易判断文件应该放在哪里2. 修改范围更小3. 更容易测试4. 更换技术实现时影响更小5. AI 更容易理解已有项目6. 多个 AI 更容易保持一致五、AI 最容易出现的“失控现场”六、提示词落地把层级规则写给 AI七、正面产出miniagent 的真实分层是怎样的1. API 层明确声明这里只处理 HTTP2. 业务逻辑进入 AgentService3. 删除 Agent 的业务动作也发生在 Service4. Service 再调用数据访问能力5. 复杂能力单独进入 Runtime八、分层和上一篇 DDD 到底有什么区别结语开源代码AI 写代码有一个很常见的问题它知道功能应该怎么实现却不一定知道代码应该放在哪里。比如你告诉 AI“增加一个删除 Agent 的功能。”如果没有清晰的工程结构它可能直接在 API 接口里查数据库、判断业务规则、清理缓存…功能可能很快就跑起来了。但随着 AI 一次次这样写项目最终会变成API 里有业务逻辑 Service 里也有数据库操作 Repository 里又夹着业务判断 工具类到处被引用这时候我们就需要另一种非常基础、却极其重要的架构思想分层。 技术名片分层架构 / Layered Architecture将系统按照不同职责划分成若干层例如接口层、业务层、数据访问层、基础设施层并规定各层应该负责什么、可以调用谁。MVC、整洁架构Clean Architecture虽然具体形式不同但都包含一个共同思想不同类型的代码不要混在一起。 一句话理解可以把一个软件系统想象成一家餐厅。顾客不会直接跑进仓库拿食材服务员也不会跑进厨房自己炒菜。通常是顾客 ↓ 服务员 ↓ 厨师 ↓ 仓库每一层都有自己的工作。软件也是如此用户请求 ↓ API / Controller ↓ Service ↓ Repository ↓ Database最重要的不是“多建几个文件夹”。而是谁负责接请求谁负责业务谁负责数据库要提前规定清楚。对于 AI 来说这相当于在项目里画出了明确的楼层你可以在自己的楼层工作但不要随便穿墙。一、为什么 AI 特别需要“分层”人类开发者看到一段代码时往往会凭经验判断“这段 SQL 不应该写在 Controller 里。”AI 如果没有明确规则却可能认为“写在这里最快而且能够完成任务。”它更关注当前问题是否解决而不是整个项目半年以后会不会变乱。所以如果一个项目没有明确分层AI 很容易逐渐写成API ├── 参数校验 ├── 权限 ├── 业务逻辑 ├── SQL ├── 缓存 └── 第三方 API最后一个接口文件几百甚至几千行。分层架构真正解决的就是先规定代码应该住在哪里。二、架构规范给每一层明确职责一个容易理解的 Web 后端可以先简化成下面几层API / Controller ↓ Service ↓ Repository ↓ Database复杂一些的系统还可以加入Schema Runtime Infrastructure但原则没有变化。1. API 层负责“接待”API 层主要负责接收请求 参数转换 身份 / 权限入口 调用 Service 返回结果它不应该负责核心业务。比如router.delete(/{agent_id})asyncdefdelete_agent(agent_id:int,svc:AgentServiceDepends(get_service),):awaitsvc.delete_agent(agent_id)returnApiResponse()从阅读角度看非常简单收到请求 ↓ 调用 AgentService ↓ 返回结果这就是一个健康的 API 层。2. Service 层负责“业务”Service 回答的问题是这件事应该怎么做例如删除 Agent 可能不仅仅是DELETE FROM agents还可能包括检查 Agent 是否存在 删除数据 清理关系 失效缓存 记录业务结果这些都属于业务过程。因此应该集中在 Service而不是散落在 API。3. Repository / 数据访问层负责“怎么拿数据”Repository 负责查询 新增 更新 删除 事务 数据库访问它更关心数据怎样存、怎样查。而不是这个业务为什么要这样做。例如Service 删除 Agent 后要清缓存 Repository 执行 Agent 删除操作两者职责不同。4. Schema / DTO负责数据长什么样例如AgentCreate AgentUpdate AgentOut它们负责描述输入参数 输出数据 字段类型 校验结构这样就不会让各种dict在项目中自由传播。5. Runtime负责复杂运行能力对于普通增删改改查类的管理功能这一层可能并不需要单独存在。但 AI Agent、RAG、工作流等系统通常会有AgentRunner Retrieval Pipeline LLM Runtime Conversation Runtime Tool Execution这些东西既不是普通数据库的增删改查功能也不是简单 API因此可以独立形成 Runtime 层。6. Infrastructure负责技术基础设施Infrastructure 通常包括数据库连接 缓存 日志 配置 事件总线 外部服务连接 文件存储这些属于系统运行需要的技术能力业务代码应该使用它们。三、真正重要的是调用要有方向仅仅建立api/ services/ repositories/几个目录还不够更重要的是规定哪些层可以调用哪些层。最容易理解的规则是API ↓ Service ↓ Repository ↓ Database而不要变成API ───────→ Database ↑ ↓ Repository ← Service否则目录虽然分层了代码实际上还是一团乱。因此可以给 AI 几条很直接的规则。规则 1API 不直接操作数据库错误router.delete(/{id})asyncdefdelete(id:int):awaitdb.execute(...)推荐awaitservice.delete(id)规则 2API 不承载核心业务逻辑不要写成ifagent.is_active:...ifhas_tools:...ifuser.role:...awaitdb...cache.clear()这些应该进入 Service。规则 3Repository 不负责业务决策Repository 可以查 Agent 删 Agent 更新 Agent但尽量不要在里面决定“管理员不能删除默认 Agent。”这是业务规则更适合 Service。规则 4上层通过稳定接口调用下层例如API ↓ AgentService.delete_agent()API 不需要知道 Service 内部究竟调用几个 Repository 是否清缓存 是否发事件这样以后 Service 内部改变API 可以保持稳定。规则 5禁止为了方便跨层调用这是对 AI 非常重要的一条。例如 AI 在 API 中发现container.agent_db就在 Router 里直接调用。虽然“拿得到”并不代表“应该用”。架构规则应该明确可访问不等于允许访问。四、这样做有什么好处分层带来的好处不只是代码看起来漂亮对于 AI 编程它会直接影响代码长期质量。1. AI 更容易判断文件应该放在哪里当 AI 要增加Agent 查询功能它就可以判断HTTP 接口 → api 业务规则 → services 数据库查询 → repositories 输入输出模型 → schemas不需要每次重新设计工程结构。2. 修改范围更小如果只是修改Agent 的业务规则通常重点检查AgentService而不必把 API、数据库和前端全部推翻。这使 AI 更容易执行“小范围修改”。3. 更容易测试Service 不依赖 HTTP 后就可以单独测试Repository 可以单独测试数据库。API 可以测试路由是否正确 权限是否正确 输入输出是否正确测试目标会非常清楚。4. 更换技术实现时影响更小例如以后SQLite ↓ PostgreSQL理想情况下主要修改Repository / Infrastructure而不是把业务代码一起重写。同样如果FastAPI未来更换其他接口框架核心业务层也不应该全部推倒。5. AI 更容易理解已有项目对 AI 来说目录本身就是一种信息。看到api/ services/ repositories/ schemas/ runtime/ infra/它马上能推断这是一个有明确职责分层的系统。比所有 Python 文件都堆在app/下面更容易理解。6. 多个 AI 更容易保持一致今天使用一个模型明天换另一个模型后天可能使用 Coding Agent 自动修改代码。只要架构规则稳定API 就是 API Service 就是 Service Repository 就是 Repository不同 AI 的代码风格虽然可能不同但整体工程结构不容易漂移。五、AI 最容易出现的“失控现场”假设我们告诉 AI增加一个删除 Agent 的接口。如果没有分层规则AI 很可能写成router.delete(/{agent_id})asyncdefdelete_agent(agent_id:int):agentawaitdb.get_agent(agent_id)ifnotagent:raiseHTTPException(404)awaitdb.delete_agent(agent_id)awaitdb.delete_agent_tools(agent_id)awaitdb.delete_user_agent_relations(agent_id)cache.remove(fagent:{agent_id})return{success:True}从功能角度看好像完全正确。但它已经把HTTP 业务规则 数据库 关系维护 缓存 异常全部塞进一个 Router。下一次让 AI批量删除 Agent。它可能复制一遍。再下一次修改 Agent。又复制类似逻辑。久而久之Router API Service Repository Cache所谓“分层”彻底失效。更危险的是AI 会学习项目已有代码。一旦项目里已经出现大量错误范例后续 AI 很可能继续模仿这种写法…六、提示词落地把层级规则写给 AI只告诉 AI“使用整洁架构。”依然太抽象。真正有用的是明确职责和调用方向,例如可以把下面的规则放入项目规则## 后端分层规则 后端采用严格的分层架构。 层级 - app/api HTTP 路由、请求解析、授权入口、依赖注入、响应转换。 - app/services 业务逻辑和用例编排。 - app/repositories 数据库访问和持久化操作。 - app/schemas 请求/响应 DTO 和验证模型。 - app/runtime 智能体、对话、LLM、检索和其他长时间运行的运行时功能。 - app/infra 数据库、缓存、日志记录、配置、存储和基础设施集成。 规则 - API 路由不得直接访问数据库。 - API 路由必须将业务操作委托给服务。 - 服务不得依赖于 FastAPI 请求或 HTTP 详细信息。 - 仓库代码应专注于持久化而非业务规则。 - 不要仅仅因为可以直接访问存储库或数据库对象就绕过服务。 - 在创建新的服务和存储库之前请优先重用现有的服务和存储库。 - 保持依赖关系在已建立的架构中流动。 - 在编写代码之前请确定每个职责对应的正确层级。然后任务 提示词 可以这样写为 Agent 增加删除功能。 严格遵守项目现有的分层架构 API 只负责路由、权限和响应 业务逻辑放在 AgentService 数据库操作复用现有数据访问层 不要在 Router 中直接访问数据库或处理缓存。 实现之前先检查现有 Agent API、Service、Schema 和数据访问代码。这时候 AI 得到的是一条非常清晰的施工路线先找 API ↓ 再找 Service ↓ 需要数据时找 Repository ↓ 需要基础设施时走现有能力而不是“哪个对象拿得到就调用哪个。”七、正面产出miniagent 的真实分层是怎样的以实际项目miniagent为例。miniagent 当前后端并不是传统三层 MVC即Model、View、Controller 架构而是针对 Agent 系统的复杂度采用下面的分层架构REST / SSE前端应用Management / Workplace接口层 API Layerapp/api路由 · 参数解析 · 权限入口 · 响应转换业务服务层 Service Layerapp/services业务逻辑 · 用例编排 · 跨模块协调运行时层 Runtime Layerapp/runtimeAgentRunner · Conversation · LLM · Retrieval · Tool数据访问层 Repository Layerapp/repositories查询 · 新增 · 更新 · 删除 · 持久化数据模型 Schema / DTOapp/schemas请求模型 · 响应模型 · 数据校验核心能力 Coreapp/core配置 · 安全 · DI · i18n · 日志基础设施层 Infrastructure Layerapp/infraORM · 数据库初始化 · 缓存 · 存储数据与外部资源SQLite · DuckDB · ChromaDB · BM25 · Files · LLM APIs可以简化为接口层app/apiHTTP 路由 · 请求 / 响应处理业务服务层app/services业务逻辑 · 用例编排运行时 / 数据访问层app/runtime · app/repositoriesAgent 运行时 · 数据访问基础设施 / 数据层app/infraSQLite · DuckDB · ChromaDB · 文件存储这不是为了目录漂亮而是在告诉开发者和 AI不同代码应该在哪一层工作。1. API 层明确声明这里只处理 HTTPminiagent 当前的 Agent API 文件backend/app/api/admin/agent.py文件开头直接写着# Agent API Router – HTTP layer only,# all logic lives in AgentService这句话本身其实就是非常好的AI 架构提示词这里只处理 HTTP所有业务逻辑进入 AgentService。例如删除 Agentrouter.delete(/{agent_id},response_modelApiResponse,summaryDelete agent [agent:delete])asyncdefdelete_agent(agent_id:int,svc:AgentServiceDepends(get_service),caller_id:intDepends(_delete),):awaitsvc.delete_agent(agent_id)returnApiResponse()可以看到 Router 做的事情很少接收 agent_id ↓ 权限检查 ↓ 取得 AgentService ↓ 调用 delete_agent() ↓ 返回 ApiResponse它没有自己操作 Agent 数据库 清理缓存 实现 Agent 业务规则这就是非常典型的“守住 API 层”。2. 业务逻辑进入 AgentService对应的backend/app/services/admin/agent.py文件开头同样明确说明# Agent Service – business logic layer# (no HTTP / FastAPI imports)而AgentService的说明则是classAgentService: Encapsulates all business logic for the Agent resource. 也就是说Service 层明确不依赖 HTTP / FastAPI并负责 Agent 业务逻辑。这是一个非常重要的边界。如果以后把 AgentService 用在HTTP API 后台任务 脚本 测试 其他内部服务它都不需要知道当前请求是不是来自 FastAPI。3. 删除 Agent 的业务动作也发生在 Service例如asyncdefdelete_agent(self,agent_id:int)-None:awaitself._agent_db.delete_agent(agent_id)self._cache.on_agent_changed(agent_id)这里就可以看出职责区别。API 层只知道我要删除 AgentService 则知道删除 Agent Agent 发生变化后让相关缓存失效以后如果再增加记录审计 发布事件 清理关联资源这些业务动作仍然可以由 Service 组织API 不需要因此越来越胖。4. Service 再调用数据访问能力AgentService初始化时取得self._agent_dbcontainer.agent_db self._user_agent_relation_dbcontainer.user_agent_relation_db self._agent_tool_relation_dbcontainer.agent_tool_relation_db self._tool_dbcontainer.tool_db self._cachecontainer.object_cache_invalidator于是形成一条非常清楚的调用链Agent API ↓ AgentService ↓ Agent DB / Relation DB ↓ Database同时缓存也是通过现有基础能力处理AgentService ↓ Object Cache Invalidator而不是 API 自己redis.delete(...)5. 复杂能力单独进入 Runtimeminiagent 和普通管理系统还有一个不同点它真正需要运行Agent LLM RAG Retrieval Conversation Tool SQL Agent因此项目把这些长期运行或执行型能力单独放在app/runtime/Runtime 包含 Agent、Session、LLM、Retrieval 等运行时组件。这样就不会把AgentRunner RetrievalPipeline LLM Client硬塞进普通 Service。这也是一种很重要的分层思想架构应该服务于业务复杂度而不是机械套模板。八、分层和上一篇 DDD 到底有什么区别这两个概念非常容易混在一起。可以用两个问题区分DDD 主要回答这段业务属于谁例如Agent Knowledge Base Tool Conversation User这是业务边界。而分层架构主要回答这段代码属于哪一层例如API Service Repository Runtime Infrastructure这是技术职责边界。可以简单理解成DDD 解决横向边界 分层架构 解决纵向边界两个结合以后AI 得到的是一张更加清晰的地图先判断 这是哪个领域 再判断 这是哪一层代码结语分层架构看起来只是几个目录api/ services/ repositories/但它真正的意义远不止如此它是在持续告诉 AI接请求的地方不要写业务写业务的地方不要关心 HTTP需要数据时走数据访问层需要基础设施时复用统一能力。AI 的编码能力越强一次能够修改的文件越多这种边界反而越重要。否则一个错误的“方便调用”可能很快被 AI 复制到几十个地方。因此标准化目录结构只是表象真正重要的是标准化职责和依赖方向。对于 AI 编程来说可以把它总结成一句非常简单的话DDD 告诉 AI“这是谁的事”分层架构告诉 AI“这件事应该在哪一层做”。两者结合AI 才真正拥有了一张可以长期施工的工程地图。开源代码githubgitee祝您好运
返回列表