最近在技术社区看到不少关于“AI 走不通互联网老路”的讨论,很多开发者,尤其是刚接触大模型应用落地的朋友,会感到困惑:我们过去十年积累的Web开发、微服务、高并发架构经验,在AI时代还管用吗?是不是所有东西都要推倒重来?
本文将从一名一线开发者的视角,结合具体的技术实践,来拆解这个问题。我们会发现,AI应用开发并非空中楼阁,它依然深深植根于我们熟悉的软件工程体系。所谓的“老路”,在AI时代不仅没有失效,反而以新的形式变得更加重要。本文将带你从架构设计、工程实践到具体代码,完整走一遍AI应用开发的“新旧结合”之路,让你能清晰地将既有技能平滑迁移到AI项目。
1. 核心概念:AI应用不是魔法,是系统工程
在深入技术细节前,我们首先要破除一个迷思:AI应用开发 ≠ 炼丹。它不是一个黑盒,输入需求就能吐出完美产品。相反,它是一个典型的软件系统工程,由多个标准化的组件和流程构成。
一个典型的AI应用(例如一个智能客服、内容生成工具或数据分析平台)通常包含以下几个层次:
- 交互层 (Presentation Layer):用户界面,可以是Web、移动端、API接口或聊天界面。这部分技术栈(React, Vue, Spring MVC, FastAPI)与互联网应用完全一致。
- 应用逻辑层 (Application Logic Layer):处理业务逻辑,编排工作流。例如,接收用户问题,调用不同的AI服务,处理返回结果,记录日志。这部分是我们的业务代码核心,使用Java、Python、Go等语言。
- AI能力层 (AI Capability Layer):提供具体的AI功能,如大语言模型(LLM)调用、图像识别、语音合成。这通常通过调用云端API(如OpenAI、通义千问)或部署本地模型来实现。
- 数据与基础设施层 (Data & Infrastructure Layer):向量数据库(用于知识库)、传统关系型数据库(存储业务数据)、缓存、消息队列、容器化部署等。这是互联网架构的基石,在AI时代同样关键。
为什么说“老路”依然重要? 因为AI能力层只是整个系统中的一个“组件”。如何让这个组件稳定、高效、可维护地集成到你的业务系统中,如何管理它的输入输出、处理它的异常、为它设计降级方案,这些恰恰是传统软件工程最擅长解决的问题。忽视这些“老路”,只关注模型本身,会导致项目难以维护、成本失控、用户体验糟糕。
2. 环境准备:一个融合新旧技术的项目骨架
我们以一个“智能技术问答助手”的后端项目为例,演示如何搭建一个融合AI能力与传统Web服务的工程环境。这个助手能根据用户的技术问题,从预设的知识库(向量化)和通用模型中综合给出答案。
技术栈说明:
- 后端框架:Python FastAPI。轻量、异步友好,适合AI应用频繁的IO操作。
- AI接口:OpenAI API (或兼容API,如Azure OpenAI)。作为核心AI能力提供方。
- 向量数据库:Chroma (本地轻量版) 或 Pinecone (云服务)。用于存储和检索本地知识库。
- 传统数据库:SQLite (开发) / PostgreSQL (生产)。存储用户对话历史、系统日志等结构化数据。
- 缓存:Redis。缓存频繁查询的AI结果,降低成本、提升响应速度。
- 开发与部署:Docker, Docker Compose。保证环境一致性。
项目初始化与依赖:
首先创建项目结构,并管理依赖。我们使用 pyproject.toml 和 uv(或 pip)进行依赖管理。
BASH
2
mkdir ai-tech-assistant && cd ai-tech-assistant
4
mkdir -p app/{api, core, services, models, utils} tests docs
5
touch app/__init__.py app/main.py .env.example README.md
pyproject.toml 内容示例:
TOML
2
name = "ai-tech-assistant"
4
description = "一个融合AI与传统Web架构的技术问答助手"
5
authors = [{name = "Your Name", email = "you@example.com"}]
8
"uvicorn[standard]>=0.24.0",
9
"openai>=1.0.0", # 用于调用大模型
10
"chromadb>=0.4.0", # 向量数据库客户端
11
"sentence-transformers>=2.2.0", # 文本嵌入模型
13
"sqlalchemy>=2.0.0", # ORM
14
"psycopg2-binary>=2.9.0", # PostgreSQL驱动
15
"pydantic>=2.0.0", # 数据验证
16
"pydantic-settings>=2.0.0", # 配置管理
17
"python-dotenv>=1.0.0", # 环境变量
18
"httpx>=0.25.0", # 异步HTTP客户端
20
requires-python = ">=3.10"
22
[project.optional-dependencies]
30
requires = ["setuptools>=61.0", "wheel"]
31
build-backend = "setuptools.build_meta"
.env.example 环境变量示例:
BASH
6
OPENAI_API_KEY=your_openai_api_key_here
7
OPENAI_API_BASE=https://api.openai.com/v1
8
OPENAI_MODEL=gpt-3.5-turbo
11
CHROMA_PERSIST_DIRECTORY=./chroma_db
12
EMBEDDING_MODEL=all-MiniLM-L6-v2
15
REDIS_URL=redis://localhost:6379/0
18
DATABASE_URL=sqlite:///./app.db
这个环境清晰地展示了“新旧融合”:我们既引入了 openai、chromadb 这样的AI时代新库,也保留了 fastapi、sqlalchemy、redis 这些互联网时代的成熟组件。依赖管理、环境隔离、配置分离这些“老路”实践,是项目可维护性的基石。
3. 架构拆解:用“旧”模式管理“新”能力
接下来,我们看看如何用经典的分层和设计模式来组织AI应用代码。核心思想是:将AI模型视为一个外部服务(类似第三方支付、短信服务),对其进行抽象、封装和容错管理。
3.1 服务抽象层:定义AI能力合约
我们不应该在业务代码中直接写死 openai.ChatCompletion.create()。应该定义一个接口(或抽象类),这样未来切换模型供应商(从OpenAI到文心一言或本地模型)时,业务逻辑无需改动。
app/core/llm_service.py - LLM服务抽象与实现
PYTHON
1
from abc import ABC, abstractmethod
2
from typing import List, Dict, Any, Optional
4
from pydantic import BaseModel
6
logger = logging.getLogger(__name__)
8
class Message(BaseModel):
13
class LLMResponse(BaseModel):
17
usage: Optional[Dict[str, int]] = None
18
finish_reason: Optional[str] = None
20
class BaseLLMService(ABC):
23
async def chat_completion(
25
messages: List[Message],
26
model: Optional[str] = None,
27
temperature: float = 0.7,
34
async def generate_embedding(self, text: str) -> List[float]:
38
class OpenAIService(BaseLLMService):
40
def __init__(self, api_key: str, base_url: Optional[str] = None, default_model: str = "gpt-3.5-turbo"):
42
self.client = openai.AsyncOpenAI(api_key=api_key, base_url=base_url)
43
self.default_model = default_model
45
async def chat_completion(self, messages: List[Message], model: Optional[str] = None, temperature: float = 0.7, **kwargs) -> LLMResponse:
47
chat_messages = [{"role": m.role, "content": m.content} for m in messages]
48
response = await self.client.chat.completions.create(
49
model=model or self.default_model,
50
messages=chat_messages,
51
temperature=temperature,
54
choice = response.choices[0]
56
content=choice.message.content,
58
usage=response.usage.dict() if response.usage else None,
59
finish_reason=choice.finish_reason
61
except Exception as e:
62
logger.error(f"OpenAI API调用失败: {e}")
66
async def generate_embedding(self, text: str) -> List[float]:
69
response = await self.client.embeddings.create(
70
model="text-embedding-3-small",
73
return response.data[0].embedding
这个设计模式是典型的“依赖倒置”。业务层只依赖 BaseLLMService 这个抽象,不关心底层是OpenAI还是其他。这带来了巨大的灵活性,也是互联网架构中管理第三方服务的标准做法。
3.2 知识库服务:向量检索与传统DB的结合
AI应用常需要“知识库”来提供领域特定信息。这通常通过“文本向量化 + 向量数据库检索”实现。但我们仍需传统数据库来管理知识库的元数据(如标题、来源、更新时间、访问权限)。
app/services/knowledge_base_service.py - 知识库服务
PYTHON
2
from chromadb.config import Settings
3
from sentence_transformers import SentenceTransformer
4
from typing import List, Dict, Any
6
from app.core.llm_service import BaseLLMService
8
logger = logging.getLogger(__name__)
10
class KnowledgeBaseService:
11
def __init__(self, persist_directory: str, embedding_model_name: str, llm_service: BaseLLMService):
13
self.chroma_client = chromadb.PersistentClient(
14
path=persist_directory,
15
settings=Settings(anonymized_telemetry=False)
18
self.collection = self.chroma_client.get_or_create_collection(name="tech_docs")
20
self.embedding_model = SentenceTransformer(embedding_model_name)
21
self.llm_service = llm_service
23
async def add_document(self, doc_id: str, text: str, metadata: Dict[str, Any]):
26
embedding = self.embedding_model.encode(text).tolist()
29
embeddings=[embedding],
33
logger.info(f"文档已添加: {doc_id}")
35
async def search_similar(self, query: str, n_results: int = 3) -> List[Dict[str, Any]]:
37
query_embedding = self.embedding_model.encode(query).tolist()
38
results = self.collection.query(
39
query_embeddings=[query_embedding],
44
if results['documents']:
45
for i in range(len(results['documents'][0])):
46
retrieved_docs.append({
47
'content': results['documents'][0][i],
48
'metadata': results['metadatas'][0][i],
49
'distance': results['distances'][0][i]
53
async def get_enhanced_answer(self, question: str, use_knowledge_base: bool = True) -> str:
56
if use_knowledge_base:
57
similar_docs = await self.search_similar(question)
59
context = "\n\n参考知识库:\n" + "\n---\n".join([doc['content'][:500] for doc in similar_docs])
61
prompt = f"""你是一个资深技术专家,请回答以下问题。
64
请提供准确、清晰、有实操性的回答。如果参考知识库中有相关信息,请优先依据它。"""
66
from app.core.llm_service import Message
68
Message(role="system", content="你是一个乐于助人的技术助手。"),
69
Message(role="user", content=prompt)
72
response = await self.llm_service.chat_completion(messages)
73
return response.content
这个服务类展示了混合数据栈的典型用法:用向量数据库做语义检索,用本地模型降低嵌入成本,最后将检索到的上下文注入给大模型生成最终答案。其背后的服务封装、依赖注入、日志记录,都是标准的软件工程实践。
4. 完整实战:构建问答API端点
现在,我们将上述服务整合到FastAPI应用中,提供一个完整的、生产可用的问答接口。这里会用到路由、依赖注入、中间件、异常处理等经典Web开发概念。
app/api/dependencies.py - 依赖注入容器
PYTHON
1
from functools import lru_cache
2
from app.core.llm_service import OpenAIService, BaseLLMService
3
from app.services.knowledge_base_service import KnowledgeBaseService
4
from app.core.config import settings
5
import redis.asyncio as redis
8
def get_llm_service() -> BaseLLMService:
11
api_key=settings.OPENAI_API_KEY,
12
base_url=settings.OPENAI_API_BASE,
13
default_model=settings.OPENAI_MODEL
17
def get_knowledge_base_service(llm_service: BaseLLMService = Depends(get_llm_service)) -> KnowledgeBaseService:
18
"""获取知识库服务单例,依赖LLM服务"""
19
return KnowledgeBaseService(
20
persist_directory=settings.CHROMA_PERSIST_DIRECTORY,
21
embedding_model_name=settings.EMBEDDING_MODEL,
22
llm_service=llm_service
26
def get_redis_client() -> redis.Redis:
28
return redis.from_url(settings.REDIS_URL, decode_responses=True)
app/api/routers/chat.py - 聊天问答路由
PYTHON
1
from fastapi import APIRouter, Depends, HTTPException, status
2
from typing import Optional
3
from pydantic import BaseModel, Field
5
from app.services.knowledge_base_service import KnowledgeBaseService
6
from app.api.dependencies import get_knowledge_base_service, get_redis_client
7
import redis.asyncio as redis
9
router = APIRouter(prefix="/chat", tags=["chat"])
10
logger = logging.getLogger(__name__)
12
class ChatRequest(BaseModel):
13
question: str = Field(..., min_length=1, max_length=1000, description="用户问题")
14
session_id: Optional[str] = Field(None, description="会话ID,用于多轮对话上下文")
15
use_kb: bool = Field(default=True, description="是否使用知识库增强")
17
class ChatResponse(BaseModel):
20
from_cache: bool = False
21
model_used: Optional[str] = None
23
@router.post("/query", response_model=ChatResponse)
24
async def query_assistant(
26
kb_service: KnowledgeBaseService = Depends(get_knowledge_base_service),
27
redis_client: redis.Redis = Depends(get_redis_client)
31
实现流程:缓存检查 -> 知识库检索增强 -> LLM调用 -> 结果缓存。
34
cache_key = f"chat:query:{hash(request.question)}"
35
cached_answer = await redis_client.get(cache_key)
37
logger.info(f"缓存命中: {cache_key}")
40
session_id=request.session_id or "default",
46
answer = await kb_service.get_enhanced_answer(
47
question=request.question,
48
use_knowledge_base=request.use_kb
52
await redis_client.setex(cache_key, 600, answer)
56
session_id=request.session_id or "default",
58
model_used="gpt-3.5-turbo"
61
except Exception as e:
62
logger.exception(f"处理问题失败: {request.question}, 错误: {e}")
65
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
66
detail="智能服务暂时不可用,请稍后重试。"
app/main.py - 应用主入口
PYTHON
1
from fastapi import FastAPI
2
from fastapi.middleware.cors import CORSMiddleware
4
from app.api.routers import chat
5
from app.core.config import settings
9
level=getattr(logging, settings.LOG_LEVEL.upper()),
10
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
13
app = FastAPI(title="AI技术问答助手API", version="0.1.0")
19
allow_credentials=True,
25
app.include_router(chat.router)
28
async def health_check():
30
return {"status": "healthy", "service": "ai-tech-assistant"}
34
return {"message": "AI Tech Assistant API is running."}
36
if __name__ == "__main__":
38
uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)
这个完整的API示例,从请求验证、缓存、服务调用、异常处理到响应返回,每一步都是经典的Web后端开发模式。AI能力(kb_service.get_enhanced_answer)只是业务流程中的一个环节,被成熟的工程实践所包裹。
5. 常见问题与排查思路
将AI能力集成到传统架构中,会遇到一些新老交织的问题。下表列出了一些典型问题及解决思路:
| 问题现象 |
可能原因 |
排查步骤与解决方案 |
| API响应慢 |
1. LLM API调用延迟高 2. 向量检索未优化 3. 网络问题 |
1. 监控与链路追踪:为LLM调用单独打点,记录耗时。 2. 引入缓存:如示例所示,对常见问题答案进行缓存。 3. 异步化:确保调用LLM API是异步操作,不阻塞主线程。 4. 优化检索:控制向量检索返回的文档片段大小和数量。 |
| 回答质量不稳定 |
1. Prompt设计不佳 2. 知识库数据噪声大 3. 模型参数(如temperature)不合适 |
1. Prompt工程:系统化设计、测试和迭代Prompt模板。 2. 数据清洗:对入库的文档进行预处理(去重、格式化、分段)。 3. A/B测试:对关键问题,尝试不同模型或参数,记录效果。 4. 人工评估:建立一个小型测试集,定期进行人工评估。 |
| Token消耗成本高 |
1. 输入上下文过长 2. 未对重复问题去重 3. 模型选型不经济 |
1. 上下文管理:精简输入给模型的上下文,只保留最相关的知识。 2. 缓存:这是最有效的成本优化手段,务必实施。 3. 模型分级:简单查询用轻量模型(如GPT-3.5),复杂任务再用重量模型(如GPT-4)。 4. 用量监控:建立API用量和成本监控告警。 |
| 向量检索不准 |
1. 嵌入模型与任务不匹配 2. 数据未正确预处理 3. 检索策略单一 |
1. 模型选型:针对中文、技术文档等垂直领域,选择或微调专用嵌入模型。 2. 混合检索:结合关键词检索(如BM25)和向量检索,提升召回率。 3. 重排序:对初步检索结果,用小模型或规则进行二次重排序。 |
| 服务不可用(LLM API失败) |
1. 第三方API限流或宕机 2. 本地网络问题 3. 密钥失效或额度不足 |
1. 重试机制:为API调用实现带退避策略的智能重试。 2. 熔断降级:当失败率超过阈值,暂时熔断,返回预设兜底答案。 3. 多路冗余:如有条件,接入多个LLM供应商作为备份。 4. 健康检查:定期检查API连通性和额度。 |
6. 最佳实践与工程建议
基于上述实践,我们可以总结出AI时代依然至关重要的“老路”经验,并给出新的工程建议。
6.1 架构与设计
- 抽象与封装:始终将AI模型视为外部服务,通过接口进行抽象。这为未来的模型切换、多模型路由、A/B测试打下基础。
- 无状态与可扩展:AI推理可能是计算密集型的,但你的应用服务器应该设计为无状态的。将状态(会话、缓存)外置到Redis或数据库,便于水平扩展。
- 异步非阻塞:LLM API调用是高延迟IO操作,务必使用异步框架(如FastAPI, asyncio)和非阻塞客户端,避免阻塞整个应用。
6.2 数据与知识管理
- 数据管道化:知识库的构建(爬取、清洗、向量化、入库)应设计成可重复、可监控的流水线,而不是手动脚本。
- 元数据管理:向量数据库存储向量和文本,但文档的元数据(来源、更新时间、权限、质量评分)应存在传统关系型数据库中,便于管理和查询。
- 版本控制:Prompt模板、模型参数、甚至知识库版本都应该进行代码化或配置化管理,使用Git进行版本控制,实现可追溯和回滚。
6.3 可观测性与运维
- 全面日志记录:记录每一次用户查询、使用的Prompt、调用的模型、消耗的Token、返回的答案摘要、耗时。这是优化和排查问题的黄金数据。
- 指标监控:监控QPS、响应延迟、错误率、Token消耗成本。为LLM API调用设置单独的慢查询和错误告警。
- 链路追踪:在分布式系统中,一个用户请求可能触发多次向量检索和LLM调用。使用OpenTelemetry等工具进行链路追踪,清晰看到时间消耗在哪个环节。
6.4 安全与合规
- 输入输出过滤:对用户输入进行严格的过滤和审查,防止Prompt注入攻击。对模型输出也要进行安全检查,避免生成有害或不适当内容。
- 数据隐私:如果使用云端AI服务,需确认用户数据是否会被用于模型训练。敏感数据应考虑本地模型或具有数据保护协议的商业服务。
- 权限控制:知识库访问、模型调用、管理功能都需要细粒度的权限控制,这与传统Web应用的权限系统设计无异。
6.5 成本控制
- 缓存为王:对于AI应用,缓存不仅能提升速度,更是降低成本的最有效手段。设计多级缓存策略。
- 用量配额:为不同用户或部门设置API调用配额和速率限制,防止资源滥用。
- 成本归属:在系统设计时就要考虑成本归属,能够按项目、团队或个人统计AI资源消耗。
7. 总结:AI时代的“老路”新走法
通过这个完整的项目拆解,我们可以清晰地看到,“互联网老路”在AI应用开发中非但没有过时,反而被赋予了新的内涵和更高的要求。
- MVC/分层架构 没有变,变的是Model层里多了“大模型调用”和“向量检索”这样的新组件。
- API设计、依赖注入、日志监控 没有变,变的是需要监控的对象从数据库慢查询变成了LLM API的延迟和Token消耗。
- 缓存、数据库、消息队列 没有变,变的是缓存里存的是AI生成的内容,数据库里需要关联向量ID和业务元数据。
AI没有颠覆软件工程的基本法则,它只是引入了新的、强大的、同时也更复杂的“组件”。 驾驭好这个新组件,恰恰需要更扎实的工程能力:清晰的架构、健壮的代码、完善的运维、严格的成本和安全控制。
因此,对于开发者而言,最有效的路径不是抛弃过去的一切去追逐最新的模型,而是巩固你的软件工程基本功,同时深入学习如何将AI能力作为一个服务组件,优雅、可靠、高效地集成到你熟悉的系统架构之中。这条路,才是通往AI时代成熟、可维护、有价值的产品开发的“高速公路”。