Step 19: 核心服务拆分 —— 从单体到微服务¶
目标:将 Step 17 的 CustomerServiceAgent 拆分为三个独立服务 难度:⭐⭐⭐⭐ 代码量:约 1500 行(较 Step 17 新增/拆分 300 行)
问题引入¶
Step 17 的局限¶
Step 17 的 CustomerServiceAgent 功能完整,但所有逻辑都在一个类里:
// Step 17: 单体设计
class CustomerServiceAgent : public StatefulAgent {
// 这一个人类负责:
// - WebSocket 连接管理
// - 会话状态维护
// - LLM 调用和回复生成
// - 知识库检索
// - 情感/记忆/关系管理
};
问题: 1. 无法独立扩展 - 如果 WebSocket 连接数增加,必须整体扩容 2. 技术栈耦合 - 向量数据库升级会影响整个 Agent 3. 团队协作困难 - 所有人修改同一个文件,容易冲突 4. 单点故障 - 一个 Bug 可能导致整个服务崩溃
本章目标¶
服务拆分:将单体 Agent 拆分为三个独立服务,每个服务职责单一。
解决方案¶
服务拆分设计¶
Step 17(单体):
┌─────────────────────────────────────────────────┐
│ CustomerServiceAgent │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 连接管理 │ │ LLM 调用 │ │ 知识检索 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────┘
Step 19(微服务):
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────────┐ │
│ │ ChatService │ ← 负责:WebSocket、会话管理 │
│ │ │ │
│ │ 连接管理、消息路由 │ │
│ └────────┬─────────┘ │
│ │ 调用 │
│ ▼ │
│ ┌──────────────────┐ │
│ │ AIService │ ← 负责:LLM、智能回复、情感记忆 │
│ │ │ │
│ │ 复用 CustomerServiceAgent │
│ └────────┬─────────┘ │
│ │ 调用 │
│ ▼ │
│ ┌──────────────────┐ │
│ │ KnowledgeService │ ← 负责:向量检索、知识库管理 │
│ │ │ │
│ │ 复用 Step 10 VectorStore │
│ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
代码对比¶
Step 17 的代码(单体)¶
// Step 17: 单体 Agent 处理所有逻辑
CustomerServiceAgent agent(tenant);
std::string response = agent.handle_customer_message(user_id, user_name, message);
// 内部:连接管理 + LLM 调用 + 知识检索 + 情感计算
Step 19 的修改(服务拆分)¶
// Step 20: 分层调用
// 1. ChatService 管理连接
std::string conn_id = chat_service->connect_customer(tenant_id, user_id, user_name);
// 2. 处理消息(ChatService 内部委托给 AIService)
std::string response = chat_service->handle_message(conn_id, message);
// 3. AIService 内部调用 KnowledgeService 做 RAG
// 4. 最终生成回复
服务职责划分¶
| 服务 | 职责 | 复用来源 |
|---|---|---|
| ChatService | WebSocket 连接管理、会话生命周期、消息路由 | 新增 |
| AIService | LLM 调用、智能回复生成、情感/记忆/关系管理 | Step 17 CustomerServiceAgent |
| KnowledgeService | 向量数据库、文档索引、RAG 检索 | Step 10 VectorStore |
新增代码详解¶
1. ChatService - 会话管理服务¶
// include/nuclaw/services/chat_service.hpp
class ChatService {
public:
// 客户连接(模拟 WebSocket 连接建立)
std::string connect_customer(const std::string& tenant_id,
const std::string& user_id,
const std::string& user_name);
// 处理消息(核心:委托给 AIService)
std::string handle_message(const std::string& connection_id,
const std::string& message);
private:
std::shared_ptr<AIService> ai_service_; // 依赖注入
std::map<std::string, std::unique_ptr<WebSocketConnection>> connections_;
};
2. AIService - AI 智能回复服务¶
// include/nuclaw/services/ai_service.hpp
class AIService {
public:
// 生成智能回复(复用 CustomerServiceAgent)
std::string generate_response(const std::string& tenant_id,
const std::string& user_id,
const std::string& message,
const std::vector<... >& history);
// 注册租户 Agent(每个租户一个 CustomerServiceAgent 实例)
void register_tenant_agent(const TenantContext& tenant);
private:
// 复用 Step 17:每个租户一个 CustomerServiceAgent
std::map<std::string, std::unique_ptr<CustomerServiceAgent>> tenant_agents_;
std::shared_ptr<KnowledgeService> knowledge_service_;
};
3. KnowledgeService - 知识库服务¶
// include/nuclaw/services/knowledge_service.hpp
class KnowledgeService {
public:
// 初始化租户知识库(数据隔离)
void initialize_tenant_kb(const std::string& tenant_id);
// 添加文档
void add_document(const std::string& tenant_id,
const std::string& doc_id,
const std::string& content);
// RAG 检索
std::vector<RetrievalResult> retrieve(const std::string& tenant_id,
const std::string& query,
int top_k = 5);
private:
// 复用 Step 10:每个租户一个 VectorStore
std::map<std::string, std::unique_ptr<VectorStore>> tenant_stores_;
};
文件变更清单¶
| 文件 | 变更类型 | 说明 |
|---|---|---|
include/nuclaw/customer_service_agent.hpp | 复用 | 从 Step 17 复用,无修改 |
include/nuclaw/tenant.hpp | 复用 | 从 Step 17 复用,无修改 |
include/nuclaw/vector_store.hpp | 复用 | 从 Step 10 复用,无修改 |
include/nuclaw/services/chat_service.hpp | 新增 | 会话管理服务 |
include/nuclaw/services/ai_service.hpp | 新增 | AI 智能回复服务 |
include/nuclaw/services/knowledge_service.hpp | 新增 | 知识库服务 |
src/main.cpp | 修改 | 演示服务拆分效果 |
完整源码¶
目录结构¶
src/step18/
├── CMakeLists.txt
├── include/
│ └── nuclaw/
│ ├── customer_service_agent.hpp # 复用 Step 17
│ ├── tenant.hpp # 复用 Step 17
│ ├── vector_store.hpp # 复用 Step 10
│ └── services/
│ ├── chat_service.hpp # 新增
│ ├── ai_service.hpp # 新增
│ └── knowledge_service.hpp # 新增
└── src/
└── main.cpp # 演示
服务初始化流程¶
// main.cpp 核心逻辑
// 1. 初始化 KnowledgeService(最底层,无依赖)
auto knowledge_service = std::make_shared<KnowledgeService>(kb_config);
// 2. 初始化 AIService(依赖 KnowledgeService)
auto ai_service = std::make_shared<AIService>(io, llm_config, knowledge_service);
// 3. 初始化 ChatService(依赖 AIService)
auto chat_service = std::make_shared<ChatService>(io, ai_service);
// 4. 注册租户
tenant_a.agent_persona = {{"name", "小快"}};
knowledge_service->initialize_tenant_kb(tenant_a.tenant_id);
ai_service->register_tenant_agent(tenant_a);
// 5. 添加知识库文档
knowledge_service->add_document(tenant_a.tenant_id, "faq_shipping", "...");
// 6. 处理客户连接
std::string conn_id = chat_service->connect_customer(tenant_id, user_id, user_name);
std::string response = chat_service->handle_message(conn_id, message);
编译运行¶
# 进入 Step 19 目录
cd src/step18
# 创建构建目录
mkdir build && cd build
# 配置
cmake ..
# 编译
make -j
# 运行
./step18_demo
预期输出:
========================================
Step 20: 核心服务拆分
========================================
演进:Step 17 单体 Agent → 3 个微服务
【初始化】KnowledgeService
【初始化】AIService
【初始化】ChatService
服务初始化完成!
【演示 1】初始化租户数据
----------------------------------------
[KnowledgeService] 初始化知识库: tenant_ecommerce
[初始化] 租户: 快购商城 (tenant_ecommerce)
[AIService] 注册租户 Agent: 快购商城
...
【演示 2】客户连接和对话流程
----------------------------------------
[ChatService] 客户连接: conn_1 (租户: tenant_ecommerce, 用户: 小明)
小明: 你好,请问发货要多久?
小快: [模拟回复] 收到消息: 你好,请问发货要多久?
【演示 4】服务统计
----------------------------------------
[ChatService 统计]
活跃连接: 2
总会话: 2
[KnowledgeService 统计]
租户 tenant_ecommerce: 2 文档
租户 tenant_edu: 1 文档
本章总结¶
- ✅ 解决了 Step 17 的问题:单体架构无法独立扩展
- ✅ 实现了服务拆分:ChatService + AIService + KnowledgeService
- ✅ 保持了演进连续性:复用 CustomerServiceAgent 和 VectorStore
- ✅ 实现了职责分离:每个服务可以独立开发、部署、扩展
- ✅ 保持了多租户隔离:每个服务都支持租户隔离
课后思考¶
当前实现还有什么问题?