Skip to content

06 API 层与完整集成

学习理念:前面 5 篇文章分别讲了各个模块的设计,这篇把它们串起来。DialogueService 是"胶水层",把 Repository(加载状态)→Engine(处理消息)→Repository(保存状态)串联成一次完整的对话处理。看完这篇你就可以回答:一条用户消息从 HTTP 请求进来到 HTTP 响应出去,每一层分别做了什么。

海外对标:FastAPI(API 层)、Rasa SDK(对话服务编排)

本节 AI 替代率:~75% | 人工干预率:~25%

角色能力范围
🤖 AI 擅长生成 FastAPI 路由代码、依赖注入链
👤 人类需理解DialogueEngine Builder 如何组装所有组件

📌 原文说明:以下内容来自 5.设计文档/FastAPI依赖注入和生命周期.mdDialogueEngine实现.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: str

4.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

六、项目所有模块的依赖关系总图


七、本阶段文件索引

优先级文件路径
🔥 P0engine/builder.pyatguigu/engine/builder.py
🔥 P0service/dialogue_service.pyatguigu/service/dialogue_service.py
🟡 P1api/routers/chat_router.pyatguigu/api/routers/chat_router.py
🟡 P1api/dependencies.pyatguigu/api/dependencies.py
🟡 P1api/schemas.pyatguigu/api/schemas.py
🟢 P2main.pymain.py
🟢 P2infrastructure/llm.pyatguigu/infrastructure/llm.py
🟢 P2infrastructure/database.pyatguigu/infrastructure/database.py

OPC 超级个体实战指南