第13章:AI异步与生产部署 —— 让 AI 服务稳定高效地面向用户

本章定位:解决「AI 功能写好了,但无法面向用户」这个从开发到生产最关键的跨越。

核心主题 :用三项技术 解决三个具体问题,让你的 AI 服务能支撑真实用户访问。


前期回顾

AI入门开发系列文章合集


开篇:一个真实困境

你花了几周时间,终于用 LangChain 搭好了一个 AI 问答机器人。本地运行一切正常:输入问题,等待 2 秒,得到答案。

然后你把它部署到服务器,迎来了第一批用户------

用户 A 发来问题,等了 2 秒,得到答案。😊

用户 B、C、D 同时 发来问题......用户 B 等了 2 秒,用户 C 等了 4 秒,用户 D 等了 6 秒。😡

用户 E 问了一个「什么是 Python 装饰器」------这个问题你的系统今天已经回答了 500 次,每次都花 2 秒、调用 1 次付费 API。💸

前端同学想接入你的 AI,问你 API 地址,你愣了:我是个 Python 脚本,没有 API......😅

这三个场景,浓缩了 AI 服务从"能跑"到"能用"之间的三道坎:

问题 现象 根本原因
多用户并发慢 10 个用户排队等,第 10 个等 20 秒 同步调用:一次只能处理一个请求
无法对外提供服务 前端/App/其他系统无法调用 AI 没有 HTTP API,只是个脚本
重复调用浪费钱 同一个问题每天调用 1000 次 没有缓存,每次都去调用付费 API

本章的目标,就是用三项技术逐一解决这三个问题,最终搭出一个可以真正面向用户的生产级 AI 服务


本章你将学到

  • 异步(async/await):让 10 个并发请求从等待 20 秒变成等待 2 秒
  • FastAPI:用 20 行代码把 AI 能力封装成任何客户端都能调用的 REST API
  • LLM 缓存:让重复问题 0 延迟、0 费用,把 API 成本降低 30-70%
  • 三者整合:搭建完整的生产级 AI 服务架构

前置条件

要求 说明
Python 版本 3.10+
已完成章节 第1章(LLM 基础调用)、第3章(Chain 链式调用)
环境变量 DASHSCOPE_API_KEY 已配置
安装依赖 uv sync(基础)、uv sync --extra api(FastAPI 端点)

全局架构:三项技术如何协作

在深入每项技术之前,先看清楚它们共同构成的完整系统:

读图方式

  • 用户请求进入 FastAPI(解决"可被调用"问题)
  • FastAPI 先检查 缓存(解决"重复调用"问题)
  • 缓存未命中,用 异步 方式调用 LLM(解决"并发慢"问题)

三项技术各司其职,串联成完整的服务链路。


一、问题一:并发慢 → 用异步解决

1.1 同步调用为什么慢?

LLM API 的调用分为两个阶段:

  1. 网络 I/O 等待:发送请求、等服务器响应(~95% 的时间)
  2. CPU 计算:解析响应、处理数据(~5% 的时间)

同步调用的问题是:在等待网络 I/O 期间,线程什么也做不了,只是阻塞地等待:

ini 复制代码
同步处理 4 个请求(每个 2 秒):

时间轴 →  0s         2s         4s         6s         8s
请求1:     [───等待 LLM 响应───]✓
请求2:                           [───等待───]✓
请求3:                                       [───等待───]✓
请求4:                                                   [───等待───]✓
总耗时:8 秒(4 × 2秒)

1.2 异步调用如何提速?

异步(async/await)的核心思想:等待期间,不是发呆,而是去处理其他请求

ini 复制代码
异步处理 4 个请求(每个 2 秒):

时间轴 →  0s                     2s
请求1:     [───发出,等待响应─────]✓
请求2:     [───发出,等待响应─────]✓  ← 同时发出!
请求3:     [───发出,等待响应─────]✓
请求4:     [───发出,等待响应─────]✓
总耗时:~2 秒(≈ 最慢的那一个)

提速原理 :4 个请求同时发出,同时在"等",但等待是交错的------等待期间线程会切换去处理其他协程。这正是 Python asyncio 事件循环干的事。

1.3 Python asyncio 三个核心概念(速览)

理解异步只需要掌握三个概念:

python 复制代码
# ① async def ------ 定义一个"可以暂停的函数"(协程)
async def fetch_answer(question: str) -> str:
    response = await llm.ainvoke(question)   # ← await 是暂停点
    return response.content

# ② await ------ "暂停当前函数,让出控制权,等待结果"
#   只能在 async def 内部使用
#   在等待期间,事件循环可以去执行其他协程

# ③ asyncio.gather() ------ "同时执行多个协程,等所有都完成"
async def main():
    results = await asyncio.gather(
        fetch_answer("问题1"),   # 三个协程同时运行
        fetch_answer("问题2"),
        fetch_answer("问题3"),
    )
    return results

# asyncio.run() ------ 同步代码进入异步世界的入口
answers = asyncio.run(main())

1.4 LangChain 异步 API 完整对照表

LangChain 所有调用方法都有对应的异步版本,用法完全相同:

同步方法 异步对应 适用场景
llm.invoke(msg) await llm.ainvoke(msg) 单个请求,需要完整响应
llm.stream(msg) async for chunk in llm.astream(msg) 流式输出,打字机效果
chain.batch(inputs) await chain.abatch(inputs) 批量并发处理多条数据

1.5 代码实战:性能对比

代码文件:lessons/13_production/01_async_llm.py

python 复制代码
import asyncio
import os
import time
from typing import Optional

from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()

llm = ChatOpenAI(
    model="qwen-plus",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    temperature=0.3,
)

# ── 同步版本:逐个等待,串行执行 ────────────────────────────────
def sync_batch(questions: list[str]) -> list[str]:
    """同步处理:每个问题等待完成,才处理下一个。"""
    results = []
    for q in questions:
        response = llm.invoke([HumanMessage(content=q)])  # 阻塞 2 秒
        results.append(response.content[:50])
    return results

# ── 异步版本:同时发出所有请求 ──────────────────────────────────
async def async_batch(questions: list[str]) -> list[str]:
    """异步并发:同时发出所有请求,等最慢的那个返回。"""

    async def one(q: str) -> str:
        response = await llm.ainvoke([HumanMessage(content=q)])  # 非阻塞
        return response.content[:50]

    results = await asyncio.gather(*[one(q) for q in questions])
    return list(results)

# ── 测试 ────────────────────────────────────────────────────────
questions = [
    "用一句话介绍 Python",
    "用一句话介绍 LangChain",
    "用一句话介绍向量数据库",
    "用一句话介绍 Transformer",
]

print(f"处理 {len(questions)} 个问题\n")

t0 = time.time()
sync_batch(questions)
print(f"同步耗时:{time.time() - t0:.2f}s")   # 预期 ~8 秒

t0 = time.time()
asyncio.run(async_batch(questions))
print(f"异步耗时:{time.time() - t0:.2f}s")   # 预期 ~2 秒

实测结果参考(因网络状况而异):

并发数 同步耗时 异步耗时 提速倍数
4 个请求 ~8 秒 ~2 秒 4x
10 个请求 ~20 秒 ~2-3 秒 7-10x
翻译 50 条 ~100 秒 ~5-8 秒 12-20x

1.6 生产必备:超时控制 + 并发限制

直接 asyncio.gather 有两个生产问题:

  • 没有超时限制:一个慢请求会"拖死"整批任务
  • 没有并发限制:100 个请求同时发出,可能触发 API 速率限制(429 错误)
python 复制代码
async def safe_invoke(question: str, timeout: float = 15.0) -> Optional[str]:
    """带超时控制的安全调用------生产环境必须使用这个版本。"""
    try:
        # asyncio.wait_for:超过 timeout 秒则抛 asyncio.TimeoutError
        response = await asyncio.wait_for(
            llm.ainvoke([HumanMessage(content=question)]),
            timeout=timeout,
        )
        return response.content
    except asyncio.TimeoutError:
        print(f"  ⏰ 超时(>{timeout}s):{question[:30]}...")
        return None
    except Exception as e:
        print(f"  ❌ 调用失败:{e}")
        return None


async def batch_with_limit(
    questions: list[str],
    max_concurrent: int = 5,   # 同时最多 5 个请求
) -> list[Optional[str]]:
    """带并发限制的批量处理------防止触发 API 速率限制。"""
    # Semaphore(信号量):控制同时持有"通行证"的协程数量上限
    semaphore = asyncio.Semaphore(max_concurrent)

    async def one_with_sem(q: str) -> Optional[str]:
        async with semaphore:     # 获取通行证(超限则排队等)
            return await safe_invoke(q)
        # with 块结束自动释放通行证,下一个排队协程可以进来

    return list(await asyncio.gather(*[one_with_sem(q) for q in questions]))

1.7 流式输出:打字机效果

python 复制代码
async def stream_response(question: str) -> None:
    """流式输出:边生成边显示,不用等全部完成才显示。"""
    print("回答:", end="", flush=True)
    async for chunk in llm.astream([HumanMessage(content=question)]):
        # 每个 chunk 是 1-3 个字符,end="" 不换行,flush=True 立即刷新
        print(chunk.content, end="", flush=True)
    print()  # 结束换行

asyncio.run(stream_response("用 100 字介绍什么是 RAG"))

二、问题二:无法被调用 → 用 FastAPI 解决

2.1 为什么需要 API?

异步解决了并发问题,但你的 AI 还只是个 Python 脚本。

前端工程师用 React,他们不能 import 你的 Python 模块。移动 App 用 Swift / Kotlin,同样无法调用 Python 代码。其他后端服务要接入你的 AI 能力,也需要一个标准接口。

解决方案:将 AI 能力封装为 HTTP API。

HTTP API 是互联网通信的通用语言------任何语言、任何平台,只要能发 HTTP 请求,就能调用你的 AI。

2.2 FastAPI 是什么?为什么选它?

FastAPI 是 Python 最流行的现代 Web 框架,三个核心优势让它特别适合 AI 服务:

特性 说明 AI 服务的价值
原生异步 基于 Python asyncio,天然支持 async def 与 LangChain 异步 API 无缝结合
自动文档 访问 /docs 自动生成可交互的 Swagger UI 无需手写 API 文档
Pydantic 校验 请求/响应自动类型校验和转换 防止非法输入崩溃 AI 服务

2.3 SSE(Server-Sent Events):流式输出的标准协议

普通 HTTP 请求是"问-答"模式:发请求 → 等完整响应。但 AI 流式输出需要"持续推送":服务端边生成边推送文字片段。

SSE(Server-Sent Events) 正是为此设计的:浏览器/客户端发起一个 HTTP 连接,服务端通过这个连接持续推送数据片段,直到发送结束信号。ChatGPT 的打字机效果用的就是 SSE。

2.4 完整 FastAPI 应用代码

代码文件:lessons/13_production/02_fastapi_app.py

python 复制代码
import asyncio
import os
from typing import AsyncGenerator, Optional

import uvicorn
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from dotenv import load_dotenv

load_dotenv()

# ── FastAPI 应用初始化 ─────────────────────────────────────────
app = FastAPI(
    title="AI 问答服务",
    description="基于 LangChain + 阿里云百炼 的 AI 问答 API",
    version="1.0.0",
)

# CORS 中间件:允许浏览器跨域调用(生产环境应替换 * 为具体域名)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ── LLM 工厂(每次请求动态创建,支持按请求自定义 temperature)─
def create_llm(temperature: float = 0.7) -> ChatOpenAI:
    api_key = os.getenv("DASHSCOPE_API_KEY")
    if not api_key:
        raise ValueError("DASHSCOPE_API_KEY 环境变量未设置")
    return ChatOpenAI(
        model="qwen-plus",
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
        api_key=api_key,
        temperature=temperature,
    )

# ── Pydantic 请求/响应模型(自动校验输入,防止非法参数)────────
class ChatRequest(BaseModel):
    message: str = Field(..., description="用户消息", min_length=1, max_length=4000)
    system_prompt: Optional[str] = Field(
        default="你是一个有帮助的 AI 助手,请用中文回答。",
        description="系统提示词",
    )
    temperature: float = Field(default=0.7, ge=0.0, le=2.0)

class ChatResponse(BaseModel):
    answer: str = Field(description="AI 的回答")
    model: str = Field(description="使用的模型")

# ── 端点一:健康检查(监控/负载均衡探针使用)──────────────────
@app.get("/health")
async def health_check() -> dict:
    """返回服务状态,用于 Kubernetes 就绪探针或负载均衡健康检查。"""
    return {
        "status": "healthy",
        "llm_configured": bool(os.getenv("DASHSCOPE_API_KEY")),
    }

# ── 端点二:标准聊天(等待完整回答)──────────────────────────
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest) -> ChatResponse:
    """等待 LLM 生成完整回答后返回。
    
    适用:需要完整答案后再处理的场景(如结果分析、格式化输出)。
    """
    try:
        llm = create_llm(temperature=request.temperature)
        response = await llm.ainvoke([
            SystemMessage(content=request.system_prompt or "你是一个有帮助的 AI 助手。"),
            HumanMessage(content=request.message),
        ])
        return ChatResponse(answer=response.content, model="qwen-plus")
    except ValueError as e:
        raise HTTPException(status_code=500, detail=str(e)) from e
    except Exception as e:
        raise HTTPException(status_code=502, detail=f"LLM 调用失败: {str(e)}") from e

# ── 端点三:流式聊天(SSE 逐字推送)──────────────────────────
@app.post("/chat/stream")
async def chat_stream(request: ChatRequest) -> StreamingResponse:
    """以 SSE 格式逐块推送 AI 回答,实现打字机效果。
    
    适用:交互式聊天界面,长文本生成,需要减少首字节延迟的场景。
    
    SSE 数据格式(每行):data: <内容>\\n\\n
    结束信号:data: [DONE]\\n\\n
    """
    async def generate() -> AsyncGenerator[str, None]:
        try:
            llm = create_llm(temperature=request.temperature)
            messages = [
                SystemMessage(content=request.system_prompt or "你是一个有帮助的 AI 助手。"),
                HumanMessage(content=request.message),
            ]
            async for chunk in llm.astream(messages):
                if chunk.content:
                    # SSE 标准格式:每条消息以 "data: " 开头,以 "\n\n" 结尾
                    yield f"data: {chunk.content}\n\n"
            yield "data: [DONE]\n\n"
        except Exception as e:
            yield f"data: [ERROR] {str(e)}\n\n"

    return StreamingResponse(
        generate(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",    # 禁止代理缓存
            "X-Accel-Buffering": "no",      # 禁止 Nginx 缓冲(确保实时推送)
        },
    )

# ── 启动服务 ──────────────────────────────────────────────────
if __name__ == "__main__":
    print("🚀 AI 问答服务启动中...")
    print("   文档地址:http://localhost:8000/docs")
    uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info")

2.5 测试你的 API

bash 复制代码
# 1. 安装依赖
uv sync --extra api

# 2. 启动服务
python 02_fastapi_app.py

# 3. 浏览器打开 http://localhost:8000/docs (Swagger UI)

# 4. 命令行测试------标准聊天
curl -X POST http://localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "用一句话介绍 RAG 技术"}'

# 5. 命令行测试------流式聊天(--no-buffer 禁用 curl 的输出缓冲)
curl -X POST http://localhost:8000/chat/stream \
  -H "Content-Type: application/json" \
  -d '{"message": "详细介绍一下 Python 的异步编程"}' \
  --no-buffer

预期输出(流式接口):

kotlin 复制代码
data: Python
data: 的
data: 异步
data: 编程
data: 基于
data: asyncio
...
data: [DONE]

三、问题三:重复调用浪费 → 用缓存解决

3.1 重复调用有多浪费?

先用数字说话:

arduino 复制代码
场景:一个 FAQ 问答系统,用户每天问 10,000 个问题
其中 30% 是重复问题(如"什么是 Python"被问了 3,000 次)

没有缓存:
  10,000 次 × 0.01元/次 = 100元/天
  其中 3,000 次重复调用 = 30元/天(纯浪费)

有缓存:
  第一次问"什么是 Python":调用 LLM(0.01元)
  后续 2,999 次:命中缓存(0元,0延迟)
  节省:2,999 × 0.01 = 29.99元/天

一年下来:29.99 × 365 ≈ 11,000 元

缓存的价值不只是省钱,命中缓存的响应时间从 ~2000ms 降至 ~1ms------用户完全感知不到延迟。

3.2 LangChain 缓存的工作原理

ini 复制代码
缓存键 = hash(模型名称 + 提示词 + temperature)
            ↓
相同模型 + 相同问题 + 相同 temperature = 相同 key = 命中缓存

注意:temperature > 0 时,同一问题每次输出不同,
      缓存的意义是保证"第二次问同样的问题,返回第一次的答案"
      仅适合 temperature=0 的确定性场景

开启缓存只需一行代码,之后所有 LLM 调用自动使用缓存,无需修改业务代码:

python 复制代码
from langchain_core.globals import set_llm_cache
set_llm_cache(InMemoryCache())   # 这一行之后,所有 llm.invoke() 自动缓存

3.3 两种内置缓存选型

方案 A:InMemoryCache(开发/测试首选)

python 复制代码
# 进程内存缓存------重启后失效,适合开发调试
import os
import time
from langchain_core.globals import set_llm_cache
from langchain_community.cache import InMemoryCache
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()

llm = ChatOpenAI(
    model="qwen-plus",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    temperature=0,           # ← 缓存必须用 temperature=0(确定性输出)
)

set_llm_cache(InMemoryCache())   # 开启内存缓存

question = "Python 中什么是装饰器?用一句话回答"

# 第一次调用:缓存未命中,实际调用 LLM
t0 = time.time()
r1 = llm.invoke([HumanMessage(content=question)])
print(f"第一次(LLM 调用):{time.time()-t0:.3f}s  → {r1.content[:60]}")

# 第二次调用:相同问题,命中缓存,直接返回
t0 = time.time()
r2 = llm.invoke([HumanMessage(content=question)])
print(f"第二次(缓存命中):{time.time()-t0:.3f}s  → {r2.content[:60]}")

# 典型输出:
# 第一次(LLM 调用):2.135s  → 装饰器是一种函数,用于在不修改原函数代码的情况下...
# 第二次(缓存命中):0.001s  → 装饰器是一种函数,用于在不修改原函数代码的情况下...

方案 B:SQLiteCache(单机生产推荐)

python 复制代码
# SQLite 文件缓存------程序重启后缓存仍然有效
from langchain_community.cache import SQLiteCache

# 缓存数据存储在本地 SQLite 文件
# 程序重启后,已缓存的答案依然存在
set_llm_cache(SQLiteCache(database_path="./llm_cache.db"))

questions = [
    "什么是 LangChain?一句话",
    "什么是向量数据库?一句话",
    "什么是 RAG?一句话",
]

print("第一轮(实际调用 LLM,写入缓存):")
for q in questions:
    t0 = time.time()
    r = llm.invoke([HumanMessage(content=q)])
    print(f"  [{time.time()-t0:.2f}s] {q[:20]}... → {r.content[:40]}")

print("\n第二轮(全部命中 SQLite 缓存,即使重启程序也一样快):")
for q in questions:
    t0 = time.time()
    r = llm.invoke([HumanMessage(content=q)])
    print(f"  [{time.time()-t0:.3f}s] {q[:20]}... → {r.content[:40]}")

set_llm_cache(None)  # 测试完毕关闭缓存

3.4 缓存选型指南

场景 推荐方案 原因
本地开发、快速调试 InMemoryCache 无需文件,进程内零配置
单机生产部署 SQLiteCache 重启后缓存保留,无需额外服务
多进程 / 多机部署 RedisCache(langchain-community) 分布式共享,进程间隔离安全

注意:多个 uvicorn worker 进程共写同一个 SQLite 文件会有锁竞争。多进程生产环境必须用 Redis 缓存。


四、三者整合:完整生产级 AI 服务

现在把三项技术整合成一个服务,看看它们如何协同工作。

代码文件:lessons/13_production/02_fastapi_app.py(启动即包含完整功能)

python 复制代码
"""
三技术整合示意(不可单独运行,仅展示整合逻辑):
FastAPI(接口层)+ 异步(并发处理)+ 缓存(重复命中)
"""
from langchain_core.globals import set_llm_cache
from langchain_community.cache import SQLiteCache

# ① 应用启动时:开启全局缓存(一次配置,所有请求自动受益)
set_llm_cache(SQLiteCache(database_path="./llm_cache.db"))

# ② FastAPI 端点中:异步调用 LLM(LangChain 内部自动检查缓存)
@app.post("/chat")
async def chat(request: ChatRequest) -> ChatResponse:
    llm = create_llm(temperature=0)   # temperature=0 才能命中缓存
    # ainvoke 内部流程:
    #   1. 检查缓存 → 命中 → 直接返回(0ms,0成本)
    #   2. 未命中 → 异步调用 LLM → 存入缓存 → 返回
    response = await llm.ainvoke(messages)
    return ChatResponse(answer=response.content, model="qwen-plus")

三技术协同的效果

场景 无任何优化 三技术整合后
10 用户并发同一问题 第 10 人等 20 秒 第一人等 2 秒,其余 9 人等 <1ms
10 用户并发不同问题 第 10 人等 20 秒 所有人等 ~2 秒
前端如何接入 无法接入(Python 脚本) 调用 HTTP API 即可
1000 次重复问题/天 1000 次 API 调用 1 次 LLM + 999 次缓存命中

4.1 生产启动配置

bash 复制代码
# 开发模式(单进程,热重载)
python 02_fastapi_app.py

# 生产模式------方式1:uvicorn 直接启动(单机推荐)
uvicorn 02_fastapi_app:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 1     # 使用 SQLiteCache 时保持单进程;用 Redis 时可增加

# 生产模式------方式2:Gunicorn + Uvicorn(更健壮的进程管理)
# 需先 pip install gunicorn
gunicorn 02_fastapi_app:app \
    --workers 1 \
    --worker-class uvicorn.workers.UvicornWorker \
    --bind 0.0.0.0:8000 \
    --timeout 60 \
    --access-logfile -

五、常见错误与排查

错误 原因 解决方法
RuntimeError: This event loop is already running 在 Jupyter Notebook 或已有事件循环的环境里调用 asyncio.run() 改为直接 await,或安装 nest_asyncio 后调用 nest_asyncio.apply()
RateLimitError: 429 并发请求超过 API 速率限制 asyncio.Semaphore(5) 控制最大并发数
流式响应前端收不到数据 Nginx 等代理缓冲了 SSE 响应 响应头加 X-Accel-Buffering: no;Nginx 配置 proxy_buffering off
SQLiteCache 多进程冲突 多个 uvicorn worker 同时写同一个 SQLite 文件 多进程部署换用 RedisCache
缓存命中但答案对不上 temperature > 0,缓存存储了某一次随机输出 只在 temperature=0 的确定性场景使用缓存
asyncio.TimeoutError 频繁触发 超时时间设置过短,或 LLM API 响应偏慢 调大 timeout 参数;先监控平均响应时间再设阈值

六、关键决策速查

bash 复制代码
我的 AI 服务要对外提供接口吗?
  → 是 → 用 FastAPI(02_fastapi_app.py)
  → 否,只是批量脚本 → 跳过 FastAPI,只用 asyncio

我需要支持打字机效果吗?
  → 是 → 用 /chat/stream 端点 + SSE(astream + StreamingResponse)
  → 否 → 用 /chat 端点(ainvoke + 完整响应)

我有并发用户吗?
  → 是 → 必须用 async def 端点 + ainvoke(FastAPI 默认就是异步)
  → 否,批处理脚本 → asyncio.gather + Semaphore 限速

我的 API 调用重复率高吗?(FAQ 问答、文档分析等)
  → 高(>20%)→ 加缓存,明显降低成本和延迟
  → 低(创意写作、随机生成)→ 不适合缓存

单机还是多机部署?
  → 单机 → SQLiteCache
  → 多机 / 多进程 → RedisCache

本章小结

r 复制代码
学了什么                    解决什么                    带来什么
──────────────────────────────────────────────────────────────
异步 (ainvoke/astream)  →  并发慢(N×T 秒)         →  ~T 秒(近似并发数无关)
FastAPI + SSE           →  无法被外部调用           →  任何客户端可调用 HTTP API  
LangChain 缓存          →  重复调用浪费             →  命中时 0ms 0 成本
三者整合                →  无法上生产的脚本         →  可面向用户的生产级 AI 服务

技术是工具,产品是目的。掌握这三项,你的 AI 就从"只有我能用"变成了"所有人都能用"。


下一步探索

完成本章后,你已掌握 AI 服务从开发到生产的完整技能链。进阶方向:下一步继续学习一些扩充的知识点,多模态AI实践


AI入门开发系列文章合集
作者:阿聪谈架构

公众号:阿聪谈架构 (分享后端架构 / AI / Java 技术文章)

相关代码关注公众号:【阿聪谈架构】 回复:AI专栏代码

相关推荐
Csvn1 小时前
SSH 远程管理与安全加固 — 运维的守门之道
后端
IT_陈寒1 小时前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
菜鸟谢3 小时前
Rust 智能指针完整详解
后端
大模型真好玩3 小时前
什么是Loop Engineering?最通俗易懂的Loop Engineering核心概念
人工智能·agent·deepseek
菜鸟谢3 小时前
Rust 函数完整知识点详解
后端
叁两3 小时前
前端转型AI Agent该如何学习?(前置篇)
前端·人工智能·node.js
爱勇宝3 小时前
淡泊名利之前,先承认我们都很焦虑
前端·后端·程序员
菜鸟谢3 小时前
Rust 闭包(Closure)完整详解
后端
ServBay3 小时前
如何利用本地技术栈构建 0 成本 AI SaaS 雏形
后端·aigc·ai编程
菜鸟谢3 小时前
Rust 集合 + 迭代器完整详解
后端