06 API 层与完整集成
学习理念:前面 5 篇文章分别讲了各个模块的设计,这篇把它们串起来。DialogueService 是"胶水层",把 Repository(加载状态)→Engine(处理消息)→Repository(保存状态)串联成一次完整的对话处理。看完这篇你就可以回答:一条用户消息从 HTTP 请求进来到 HTTP 响应出去,每一层分别做了什么。
海外对标:FastAPI(API 层)、Rasa SDK(对话服务编排)
本节 AI 替代率:~75% | 人工干预率:~25%
| 角色 | 能力范围 |
|---|---|
| 🤖 AI 擅长 | 生成 FastAPI 路由代码、依赖注入链 |
| 👤 人类需理解 | DialogueEngine Builder 如何组装所有组件 |
📌 原文说明:以下内容来自
5.设计文档/FastAPI依赖注入和生命周期.md、DialogueEngine实现.md,代码来自atguigu/api/、atguigu/service/、atguigu/engine/builder.py。
📖 阅读优先级
| 等级 | 章节 | 说明 |
|---|---|---|
| 🔥 必须深入 | Service 层 + Engine Builder | 理解所有模块的组装方式 |
| 🟡 理解即可 | API 路由 | FastAPI 标准写法,第3次出现了 |
一、一次对话的完整生命周期
🔥【P0 必须理解】 这是从用户发消息到收到回复的完整链路,所有模块在此交汇:
用户 → POST /api/chat {"text": "我要退款"}
│
1. API Router → 解析请求 → 调用 DialogueService.chat()
│
2. DialogueService.chat()
├── DialogueStateRepository.load_state(sender_id) ← 加载对话状态
├── DialogueEngine.process(state, message) ← 核心处理
│ ├── 准备 Session
│ ├── 创建 Turn
│ ├── 判断消息类型(文本/对象)
│ ├── 文本 → TurnPlanner.predict() ← LLM 规划
│ ├── TurnPlanValidator.validate() ← 校验
│ ├── 分发到对应 Handler
│ │ ├── TaskHandler / KnowledgeHandler / ChitchatHandler
│ │ └── Handler 执行 → 返回 BotMessage
│ ├── 提交 Turn → 写入 Session
│ └── 返回 BotMessage
├── DialogueStateRepository.save_state(state) ← 保存对话状态
└── 返回 API 响应
│
3. API Router → 返回 JSON 给前端
│
用户 ← 收到回复二、DialogueService——胶水层
🔥【P0 必须理解】 Service 层只做三件事:加载状态 → 调用引擎 → 保存状态。
python
class DialogueService:
def __init__(self, engine: DialogueEngine, repository: DialogueStateRepository):
self._engine = engine
self._repository = repository
async def chat(self, sender_id: str, text: str) -> ChatResponse:
# 1. 加载对话状态
state = await self._repository.load_state(sender_id)
# 2. 调用引擎处理消息
bot_message = await self._engine.process(state, text)
# 3. 保存对话状态
await self._repository.save_state(state)
# 4. 返回响应
return ChatResponse(
reply=bot_message.text,
sender_id=sender_id,
)三、DialogueEngine Builder
🔥【P0 必须理解】 Builder 负责组装所有组件——这是理解整个项目如何"粘在一起"的关键。
python
class DialogueEngineBuilder:
"""对话引擎构造器——组装所有组件"""
def __init__(self):
self._llm_client = LLMClient(...) # LLM API
self._http_client = HTTPClient(...) # 电商后端 HTTP
self._turn_planner = TurnPlanner(self._llm_client) # 规划器
self._flow_loader = FlowLoader() # Flow 加载器
self._action_runner = ActionRunner() # Action 运行器
self._flow_executor = FlowExecutor(self._action_runner, ...)
self._command_processor = CommandProcessor(self._flow_loader, self._flow_executor)
self._knowledge_handler = KnowledgeHandler(self._llm_client, ...)
self._chitchat_handler = ChitChatHandler()
self._clarify_responder = ClarifyResponder()
def build(self) -> DialogueEngine:
return DialogueEngine(
turn_planner=self._turn_planner,
flow_loader=self._flow_loader,
command_processor=self._command_processor,
knowledge_handler=self._knowledge_handler,
chitchat_handler=self._chitchat_handler,
clarify_responder=self._clarify_responder,
)组件依赖关系:
DialogueEngine
├── TurnPlanner (依赖: LLMClient)
├── CommandProcessor (依赖: FlowLoader, FlowExecutor)
├── FlowExecutor (依赖: ActionRunner)
├── KnowledgeHandler (依赖: LLMClient, KnowledgeProvider[])
├── ChitChatHandler
└── ClarifyResponder四、API 路由
🟡【P1 看注释就行】 FastAPI 的标准写法,和 Ch20 类似。
4.1 主入口
python
# main.py
from atguigu.conf.config import AppConfig
from atguigu.api.app import create_app
config = AppConfig()
app = create_app(config)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host=config.app_host, port=config.app_port)4.2 路由定义
python
# api/routers/chat_router.py
from fastapi import APIRouter, Depends
router = APIRouter()
@router.post("/api/chat")
async def chat(
request: ChatRequest,
service: DialogueService = Depends(get_dialogue_service),
):
result = await service.chat(sender_id=request.sender_id, text=request.text)
return result
@router.get("/api/history/{sender_id}")
async def get_history(
sender_id: str,
service: DialogueService = Depends(get_dialogue_service),
):
state = await service._repository.load_state(sender_id)
return state.to_history_dict()4.3 请求/响应模型
python
# api/schemas.py
from pydantic import BaseModel
class ChatRequest(BaseModel):
sender_id: str
text: str
class ChatResponse(BaseModel):
reply: str
sender_id: str4.4 依赖注入
python
# api/dependencies.py
from fastapi import Depends
async def get_config() -> AppConfig:
return AppConfig()
async def get_llm_client(config: AppConfig = Depends(get_config)) -> LLMClient:
return LLMClient(api_key=config.llm_api_key, base_url=config.llm_base_url)
async def get_dialogue_service(
config: AppConfig = Depends(get_config),
llm_client: LLMClient = Depends(get_llm_client),
) -> DialogueService:
builder = DialogueEngineBuilder(config, llm_client)
engine = builder.build()
repository = DialogueStateRepository(config.database_url)
return DialogueService(engine, repository)五、完整启动流程
bash
# 1. 启动 MySQL
cd docker && docker compose up -d
# 2. 启动模拟电商后端
cd ecommerce-service-backend && uv run python main.py
# 3. 配置客服后端环境变量
# 编辑 customer-service-backend/.env:
# LLM_MODEL=...
# LLM_BASE_URL=...
# LLM_API_KEY=...
# 4. 启动客服后端
cd customer-service-backend && uv run python main.py
# 5. 启动前端(可选)
cd customer-service-frontend && npm run dev六、项目所有模块的依赖关系总图
七、本阶段文件索引
| 优先级 | 文件 | 路径 |
|---|---|---|
| 🔥 P0 | engine/builder.py | atguigu/engine/builder.py |
| 🔥 P0 | service/dialogue_service.py | atguigu/service/dialogue_service.py |
| 🟡 P1 | api/routers/chat_router.py | atguigu/api/routers/chat_router.py |
| 🟡 P1 | api/dependencies.py | atguigu/api/dependencies.py |
| 🟡 P1 | api/schemas.py | atguigu/api/schemas.py |
| 🟢 P2 | main.py | main.py |
| 🟢 P2 | infrastructure/llm.py | atguigu/infrastructure/llm.py |
| 🟢 P2 | infrastructure/database.py | atguigu/infrastructure/database.py |