DDD + AI Agent 怎么设计一套智能教育题库系统?从知识库、搜题到智能组卷完整拆解

很多人第一次设计“AI 教育平台”,很容易把重点放在大模型上:接一个模型 API,给它一段 Prompt,让它生成教案、解析题目、自动组卷,看起来很快就能做出一套“智能系统”。

但真做过复杂业务系统的人都知道,问题往往不在“AI 能不能生成”,而在于:生成出来的东西是不是受业务规则约束,题目数据能不能长期维护,知识点体系会不会越做越乱,组卷能不能稳定可控,AI 出错之后谁来兜底。

如果系统只是做 Demo,Prompt 驱动当然够用。但如果目标是建设一套真正能落地、能持续演进、能服务教师和学生的教育平台,那么架构核心就不能是“调用大模型”,而应该是:

先用 DDD 把教育业务领域拆清楚,再把 AI Agent 作为领域能力增强层接进去。

本文就围绕一套典型的智能教育平台,完整拆一下应该怎么设计。

平台主要包含知识库建设、题目维护、格式模板管理、学生搜题、教师备课、智能出卷等能力,同时还要考虑 AI 预标注、同类题、变式题、分步解析、智能组卷等 AI 场景。

这类系统非常适合用 DDD。


一、先说结论:AI 不是核心领域,教育业务才是

设计这类系统时,我最先确定的一条原则是:

AI 不是领域本身,AI 是能力提供者。

这句话很重要。

很多系统会直接把业务写成:

String result = llm.chat("帮我生成一套高二数学试卷");

如果只是内部工具,这样做问题不大。

但一旦进入正式业务,这种设计很危险。

因为一套试卷到底应该有多少题、选择题几道、难度比例是多少、哪些知识点必须覆盖、总分是多少、是否允许重复题,这些都属于确定性的业务规则。

大模型适合做:

  • 理解自然语言要求;
  • 推荐候选题;
  • 生成变式题;
  • 分析题目;
  • 辅助补充内容。

但最终的业务决策,不应该交给模型自由发挥。

比如智能组卷,更合理的流程应该是:

教师输入组卷要求
        ↓
AI 解析自然语言
        ↓
转换为结构化组卷约束
        ↓
领域规则校验
        ↓
题目候选池检索
        ↓
组卷算法求解
        ↓
AI 辅助补题 / 调整
        ↓
试卷领域再次校验
        ↓
生成最终试卷

换句话说:

AI 负责理解和生成,领域模型负责约束和裁决。

这也是整个系统最重要的架构边界。


二、先用 DDD 划清业务边界

从功能上看,这个平台表面上只有几个模块:

知识库、题库、模板、搜题、备课、组卷。

但如果真的做 DDD,不能只按页面菜单拆模块,而要按照业务能力和变化边界拆限界上下文。

我会至少划分下面几个 Bounded Context:

限界上下文主要职责
Knowledge Context知识点、课程标准、教材、章节体系
Question Context题目创建、审核、发布、版本、标签
Search Context关键词搜索、语义检索、相似题
Paper Context组卷、试卷、细目表、组卷规则
Lesson Context教案、课堂练习、分层作业
Template Context教案模板、试卷模板、答题卡模板
AI ContextAgent、Prompt、RAG、模型调用
IAM Context用户、教师、学生、权限

这里需要特别注意一点:

DDD 不是简单地把目录从:

controller
service
mapper
entity

换成:

question
paper
lesson

真正的 DDD 是先思考:

业务能力
    ↓
业务边界
    ↓
领域模型
    ↓
聚合与行为

也就是说,一个模块存在的原因,不是因为页面上有个菜单,而是因为它拥有一套独立的业务规则和生命周期。


三、知识库建设:不是建几张“知识点表”那么简单

知识库是这个系统的基础。

从需求上看,知识库要维护:

  • 知识点体系;
  • 考纲;
  • 教材;
  • 文档库;
  • 题库;
  • 卷库。

很多系统一开始会设计一张 knowledge_point 表,然后配个 parent_id 做树。

这只能解决“展示树”的问题,却解决不了真正的知识体系问题。

例如高中数学中:

函数
 ├── 函数概念
 ├── 定义域
 ├── 值域
 ├── 单调性
 └── 奇偶性

这里不仅仅存在父子关系。

还可能存在:

函数定义
    ↓ prerequisite
函数单调性

也就是“前置知识”。

还可能存在:

函数图像
    ↓ related
函数单调性

或者:

函数最值
    ↓ examinedBy
选择题 / 填空题 / 解答题

因此,真正的知识点领域不能只有一棵树。

我会把它建模为:

Subject
  └── KnowledgeTree
        └── KnowledgePoint
               ├── Parent
               ├── Children
               ├── Prerequisite
               ├── Related
               └── CurriculumMapping

Java 领域模型可能类似:

public class KnowledgePoint {

    private KnowledgePointId id;

    private SubjectId subjectId;

    private String name;

    private KnowledgePointId parentId;

    private Integer level;

    private Difficulty difficulty;

    private List<KnowledgeRelation> relations;

    public void addPrerequisite(KnowledgePoint point) {
        // 领域规则校验
    }

    public void bindCurriculum(CurriculumNode node) {
        // 关联课程标准
    }
}

知识点和教材之间也不应该写死。

因为同一个知识点,在不同教材版本中的章节位置可能不同。

所以需要有:

Textbook
    ↓
TextbookVersion
    ↓
Chapter
    ↓
KnowledgePoint Mapping

课程标准同理:

Curriculum
    ↓
CurriculumVersion
    ↓
CurriculumNode
    ↓
KnowledgePoint Mapping

这样后面才能支持不同省份、不同版本教材、不同年级,以及知识点体系的版本演进。


四、题目领域才是整个系统真正的核心

从业务价值来看,题库往往是整个平台最重要的数据资产。

因此 Question Context 应该作为核心域来设计。

题目不能只是数据库中的一行数据。

至少应该拥有这些属性:

public class Question {

    private QuestionId id;

    private QuestionContent content;

    private QuestionType type;

    private Subject subject;

    private Difficulty difficulty;

    private List<KnowledgePointRef> knowledgePoints;

    private Answer answer;

    private Analysis analysis;

    private QuestionStatus status;

    private QuestionVersion version;
}

更关键的是,它应该有自己的生命周期。

比如:

DRAFT
  ↓
AI_TAGGED
  ↓
WAITING_REVIEW
  ↓
APPROVED
  ↓
PUBLISHED
  ↓
ARCHIVED

很多 CRUD 系统会写成:

status = 0
status = 1
status = 2

然后所有业务逻辑堆在 Service 里。

DDD 更推荐让领域对象自己表达业务行为:

question.aiTag();

question.submitReview();

question.approve(reviewer);

question.publish();

question.archive();

例如发布题目时,可以这样:

public void publish() {

    if (status != QuestionStatus.APPROVED) {
        throw new DomainException("题目未经审核不能发布");
    }

    if (knowledgePoints.isEmpty()) {
        throw new DomainException("题目必须关联知识点");
    }

    this.status = QuestionStatus.PUBLISHED;
}

这时候“题目能不能发布”这个规则,就真正属于 Question,而不是散落在某个 Service 的 if else 中。

这就是贫血模型和领域模型之间的差别。


五、AI 预标注不能直接污染正式题库

题目批量导入之后,通常会希望 AI 自动完成这些事情:

  • 判断学科;
  • 判断年级;
  • 判断题型;
  • 识别知识点;
  • 判断难度;
  • 补充标签。

比如一道题:

已知函数 f(x)=x²-2x+3,求函数最小值。

AI 可能返回:

{
  "subject": "数学",
  "grade": "高中",
  "knowledgePoints": [
    "二次函数",
    "函数最值"
  ],
  "difficulty": 2,
  "questionType": "解答题"
}

这个结果看起来很好,但有一个架构问题:

AI 返回值不能直接覆盖正式题目。

因为 AI 本质上具有不确定性。

今天模型判断为“函数最值”,明天模型升级后可能又多判断一个“配方法”。

因此中间应该增加一个 AI 建议对象:

AITagSuggestion

流程变成:

导入题目
    ↓
AI 分析
    ↓
AITagSuggestion
    ↓
教师审核
    ↓
写入正式 Question

领域对象可以类似:

public class AITagSuggestion {

    private List<TagSuggestion> knowledgePoints;

    private DifficultySuggestion difficulty;

    private double confidence;
}

这样有几个明显的好处。

第一,AI 和业务数据彻底解耦。

第二,可以保留 AI 的原始判断,后续做模型评估。

第三,可以统计不同模型的准确率。

第四,未来换 DeepSeek、豆包、OpenAI 或其他模型,都不会影响 Question 领域本身。


六、题目查重不能只靠 MD5

题库系统里,查重一定是高频需求。

但如果只是:

MD5(questionText)

实际上只能发现完全一样的文本。

例如:

题目 A:
若 x²-5x+6=0,求 x。

另外一道:

题目 B:
已知方程 x²−5x+6=0,请求出方程的根。

两道题表达方式不同,但本质上一样。

因此我会把查重分三层。

第一层是标准化文本 Hash。

先把:

  • 空格;
  • 标点;
  • LaTeX 格式;
  • 特殊字符;
  • 全角半角;

做标准化。

这一层成本最低,可以快速挡掉完全重复题。

第二层使用 Elasticsearch BM25。

通过文本相关度判断高相似题。

第三层再使用 Embedding 向量。

把题目内容向量化,例如:

Question
    ↓
Embedding Model
    ↓
Vector

然后做 cosine similarity。

例如:

similarity = 0.96

就可以进入人工确认。

一个比较实用的技术组合是:

PostgreSQL
+
Elasticsearch
+
pgvector

早期规模不是特别大时,pgvector 完全够用。

等题库达到千万级甚至更大时,再考虑 Milvus、Qdrant 等独立向量数据库。


七、学生搜题应该独立成 Search Context

学生搜题看起来只是“查询题目”,实际上它和题库后台管理完全不是一回事。

后台题目查询强调:

  • 条件准确;
  • 权限;
  • 状态;
  • 管理操作。

学生搜索强调:

  • 召回率;
  • 语义理解;
  • 相似题;
  • 搜索体验;
  • 排序效果。

所以我不会让学生搜题直接调用:

QuestionRepository

而是独立出 Search Context。

典型搜索链路:

用户输入
   ↓
Query Understanding
   ↓
BM25 Search
   +
Vector Search
   ↓
融合排序
   ↓
Reranker
   ↓
权限过滤
   ↓
返回结果

为什么需要混合搜索?

例如用户输入:

函数最大值怎么求

真正相关的内容可能是:

  • 函数最值;
  • 二次函数;
  • 配方法;
  • 单调性;
  • 函数图像。

如果只用数据库 LIKE:

WHERE content LIKE '%函数最大值%'

很多真正相关的题目根本搜不到。

而 BM25 + 向量搜索可以兼顾关键词准确性和语义召回。

再加一层 Reranker,则可以进一步优化排序。


八、分步解析要做成结构化 Agent,而不是长文本生成

学生搜题之后,平台可能要返回:

  • 答案;
  • 分步解析;
  • 对应知识点;
  • 解题思路;
  • 易错点。

这里最容易出现的设计就是:

Prompt
    ↓
LLM
    ↓
一大段 Markdown

能用,但很难工程化。

更合理的是强制 Agent 返回结构化 JSON:

{
  "steps": [
    {
      "index": 1,
      "description": "整理函数表达式",
      "formula": "...",
      "knowledgePoint": "二次函数"
    },
    {
      "index": 2,
      "description": "使用配方法求最值",
      "formula": "...",
      "knowledgePoint": "函数最值"
    }
  ],
  "answer": "...",
  "knowledgePoints": [
    "二次函数",
    "函数最值"
  ]
}

这样前端才能真正按照业务需求展示。

同时解析过程最好不要只依赖 LLM。

可以走:

Question
    ↓
标准答案
    ↓
知识点体系
    ↓
RAG
    ↓
Solution Agent
    ↓
Verifier Agent
    ↓
最终结果

尤其数学、物理这类题目,建议增加校验 Agent,降低模型生成错误答案的概率。


九、“同类题”和“变式题”必须分开设计

这两个功能看起来差不多,实际上完全是两种能力。

同类题属于搜索。

它的意思是:

从现有题库中找出与当前题目类似的题。

因此属于 Search Context。

链路大概是:

当前题目
    ↓
提取知识点
    ↓
文本检索
+
向量召回
    ↓
相似题排序

而变式题属于生成。

它的意思是:

在保持核心知识点的前提下,生成一道新的题。

所以应该由 AI Context 与 Question Context 协作。

流程:

Original Question
        ↓
Knowledge Constraint
        ↓
Variation Strategy
        ↓
LLM Generate
        ↓
Answer Agent
        ↓
Consistency Check
        ↓
Question Candidate

比如原题:

x²-5x+6=0

可以改变:

  • 参数;
  • 问法;
  • 难度;
  • 题型;
  • 情景。

但必须保证:

核心知识点 = 一元二次方程

最终生成的也不能直接进入正式题库,而应该先进入候选状态。


十、智能组卷是另一个核心域

如果说 Question 是第一个核心域,那么 Paper 基本就是第二个。

试卷也不应该只是一张表。

可以设计为:

Paper
 ├── PaperSection
 │      └── PaperQuestion
 │
 ├── PaperConstraint
 │
 └── Blueprint

组卷本质上是一个多约束问题。

例如老师可能要求:

满分:150
考试时间:120 分钟

选择题:12
填空题:4
解答题:6

难度比例:
简单 30%
中等 50%
困难 20%

知识点比例:
函数 30%
数列 20%
概率 20%
其他 30%

这时候可以抽象出:

public class PaperConstraint {

    private int totalScore;

    private Duration duration;

    private Map<QuestionType, Integer> typeDistribution;

    private Map<Difficulty, Double> difficultyDistribution;

    private Map<KnowledgePointId, Double> knowledgeDistribution;
}

有了这个对象,后面的组卷逻辑就不再依赖某个页面参数,而是真正形成领域语言。


十一、智能组卷绝不能全部交给大模型

这是整个 AI 教育系统里最容易踩坑的地方之一。

假设老师输入:

帮我生成一套高二数学月考试卷,重点考函数和数列,中等偏难。

LLM 很适合把这句话解析成:

{
  "grade": "高二",
  "difficulty": {
    "easy": 0.2,
    "medium": 0.5,
    "hard": 0.3
  },
  "knowledge": {
    "函数": 0.55,
    "数列": 0.45
  }
}

但是接下来真正选哪些题,不应该再让 LLM 随机决定。

合理的方案是:

自然语言
    ↓
Constraint Agent
    ↓
PaperConstraint
    ↓
Candidate Search
    ↓
Constraint Solver
    ↓
Paper
    ↓
Paper Evaluation

早期可以使用:

贪心
+
回溯

后续复杂后可以考虑:

  • 遗传算法;
  • 整数规划;
  • Constraint Programming;
  • Timefold;
  • OptaPlanner。

这样生成出来的试卷才能真正满足:

  • 总分;
  • 题量;
  • 知识点覆盖;
  • 难度;
  • 题型;
  • 时间;
  • 重复率。

AI 在这里是辅助者,而不是裁判。


十二、双向细目表应该是试卷领域自身的能力

双向细目表其实就是对试卷结构的一个二维统计。

比如:

知识点选择题填空题解答题
函数312
数列211
概率211

也可以从难度维度看:

知识点简单中等困难
函数132
数列121

这个能力最好放在 Paper Context 中。

比如:

Blueprint blueprint = paper.generateBlueprint();

而不是让前端临时统计。

因为双向细目表后续可能还会参与组卷反向校验,是重要的领域对象。


十三、教师备课是最适合发挥 Agent 能力的场景

相对于题库和组卷,备课是最适合大模型发挥的地方。

教师可能只输入一句:

帮我准备一节高一函数单调性的课。

如果只是普通 LLM 调用,流程就是:

Prompt
    ↓
LLM
    ↓
教案

但真正的 AI Agent 应该能够自动完成多个步骤。

例如:

                   Lesson Planning Agent
                            │
            ┌───────────────┼───────────────┐
            ↓               ↓               ↓
     Curriculum Agent   Knowledge Agent   Search Agent
            │               │               │
            └───────────────┼───────────────┘
                            ↓
                       Lesson Planner
                            ↓
          ┌─────────────────┼─────────────────┐
          ↓                 ↓                 ↓
      教案 Agent          课件 Agent         练习 Agent
          │                 │                 │
          └─────────────────┼─────────────────┘
                            ↓
                       Review Agent

整个过程可能包括:

读取课程标准
    ↓
定位教材章节
    ↓
识别知识点
    ↓
寻找对应例题
    ↓
生成教学目标
    ↓
生成教学流程
    ↓
生成课件大纲
    ↓
生成课堂练习
    ↓
生成分层作业

这里 Agent 的价值就体现出来了。

因为它不是单次生成,而是:

拆任务、调用工具、读取知识、形成结果、再次校验。


十四、模板管理一定要从 AI 中独立出来

不同学校对教案、试卷、答题卡的格式要求往往完全不一样。

如果把这些格式全部写进 Prompt:

你必须按照 XX 学校的格式……

后面一定会越来越难维护。

因此应该独立设计 Template Context。

例如:

Template
 ├── LessonTemplate
 ├── QuestionTemplate
 ├── PaperTemplate
 └── AnswerSheetTemplate

教案模板可以结构化:

{
  "sections": [
    {
      "name": "教学目标",
      "required": true
    },
    {
      "name": "教学重点",
      "required": true
    },
    {
      "name": "教学难点",
      "required": true
    },
    {
      "name": "教学过程",
      "required": true
    }
  ]
}

这样以后学校 A、学校 B、学校 C 都可以维护自己的模板。

AI Agent 只负责:

读取模板
    ↓
按模板生成内容

而不是让模型自己决定格式。


十五、Java 工程目录不要再按 Controller、Service、Mapper 平铺

如果用了 DDD,我不太推荐这种传统项目结构:

controller
service
mapper
entity

因为项目一大,所有领域会混在一起。

更推荐:

education-platform

├── interfaces
│   ├── rest
│   ├── dto
│   └── assembler
│
├── application
│   ├── command
│   ├── query
│   ├── service
│   └── event
│
├── domain
│   ├── question
│   │   ├── model
│   │   ├── repository
│   │   ├── service
│   │   └── event
│   │
│   ├── knowledge
│   ├── paper
│   ├── lesson
│   └── template
│
└── infrastructure
    ├── persistence
    ├── redis
    ├── elasticsearch
    ├── mq
    ├── ai
    └── oss

依赖关系保持:

Interface
    ↓
Application
    ↓
Domain

Infrastructure
    ↑
implements
    ↑
Domain Repository

例如 Domain 只定义:

public interface QuestionRepository {

    Optional<Question> findById(QuestionId id);

    void save(Question question);
}

MyBatis、JPA、数据库表这些实现细节全部放在 Infrastructure。

这样 Domain 就不会知道:

  • MyBatis;
  • Elasticsearch;
  • Redis;
  • PostgreSQL。

这也是六边形架构、整洁架构和 DDD 很容易结合的地方。


十六、这个系统非常适合 CQRS

题库平台有一个很明显的特点:

写操作业务规则重,读操作查询复杂。

写侧包括:

创建题目
修改题目
提交审核
审核通过
发布题目
组卷

这些操作应该经过领域模型。

而读侧包括:

题库检索
试卷预览
知识点统计
学生搜题
后台报表

这些查询没必要每次还原领域对象。

所以非常适合 CQRS。

例如:

Command
    ↓
Application
    ↓
Domain
    ↓
PostgreSQL

查询则可以直接:

Query
    ↓
Elasticsearch
Redis
Read Model

这会比所有请求都走 Repository 更合理。

尤其学生搜题,更不应该写成:

SELECT *
FROM question
WHERE content LIKE '%函数%'

十七、领域事件和 MQ 是非常重要的一环

这个系统存在大量“主流程完成后,异步处理”的任务。

比如一道题发布之后,后面可能需要:

  • 写入 Elasticsearch;
  • 生成 Embedding;
  • 更新知识点统计;
  • 更新推荐索引;
  • 写审计日志。

所以可以定义:

QuestionPublishedEvent

消费者分别处理:

SearchIndexConsumer
    → 更新 ES

EmbeddingConsumer
    → 生成向量

KnowledgeConsumer
    → 更新知识体系统计

AuditConsumer
    → 写审计日志

整体:

老师发布题目
     ↓
Question 聚合
     ↓
数据库事务提交
     ↓
Domain Event
     ↓
MQ
     ↓
ES / Vector / Statistics

这里建议再配合 Outbox Pattern。

因为经典问题是:

数据库提交成功
MQ 发送失败

如果没有 Outbox,很容易出现数据库已经有题目,但搜索索引永远没有更新的情况。


十八、AI 层一定要做自己的 LLM Gateway

业务代码不要到处出现:

openAiClient.chat(...)

或者:

deepSeekClient.chat(...)

更合理的方式是定义统一接口:

public interface LlmClient {

    LlmResponse chat(LlmRequest request);

}

具体实现:

OpenAiLlmClient
DoubaoLlmClient
DeepSeekLlmClient
QwenLlmClient

上面再包一层 LLM Gateway。

负责:

  • 模型路由;
  • 限流;
  • 重试;
  • 熔断;
  • 超时;
  • Fallback;
  • Token 统计;
  • 成本统计;
  • Prompt 版本;
  • 结构化输出。

例如:

题目分类
→ 低成本模型

复杂数学解析
→ 推理模型

教案生成
→ 长文本模型

Embedding
→ Embedding Model

否则系统越做越大,模型成本一定会失控。


十九、Agent 平台不要只理解成“LangChain”

如果后续 AI 场景越来越多,建议把 AI Context 做成一个相对独立的平台层。

可以抽象:

AI Gateway
    │
    ├── Model Router
    ├── Prompt Manager
    ├── Guardrail
    └── LLM Client

再往上:

Agent Runtime
 ├── Tool Registry
 ├── Context Manager
 ├── Workflow Engine
 ├── RAG
 ├── Memory
 └── MCP Client

最后才是具体业务 Agent:

QuestionTagAgent

QuestionExplainAgent

QuestionVariationAgent

LessonPlanAgent

PaperGenerationAgent

PaperEvaluationAgent

这样做最大的好处,就是业务 Agent 不再关心:

到底调用哪个模型
Prompt 放在哪里
Token 怎么统计
超时怎么处理
失败怎么降级

这些全部交给 AI 基础设施层。


二十、第一版到底要不要上微服务?

我的建议反而是:

不要一上来就拆微服务。

很多团队一看到:

Knowledge Context
Question Context
Paper Context
Lesson Context

马上就想做:

knowledge-service

question-service

paper-service

lesson-service

但 DDD 和微服务不是一回事。

如果团队只有五六个人,却拆十几个服务,结果往往是:

Feign 调用满天飞

本地启动困难

链路排查困难

事务更复杂

发布更复杂

业务还没复杂,基础设施先复杂了。

所以第一阶段我更推荐:

Spring Boot
+
Spring Modulith
+
DDD

做模块化单体。

模块之间通过明确接口和领域事件交互。

当后面真正出现这些条件时,再拆:

  • Search QPS 很高;
  • AI 需要独立 GPU 或弹性扩容;
  • 组卷计算越来越重;
  • 团队组织独立;
  • 模块发布频率明显不同。

这时候再把 Search、AI、Paper 等逐渐拆出来。

这比第一天就搞二三十个微服务务实得多。


二十一、我会采用的第一版技术栈

如果现在真让我落地,我会优先选成熟和可维护的技术,而不是追求“全新”。

后端:

Java 21
Spring Boot 3
Spring Modulith

数据库:

PostgreSQL

缓存:

Redis

全文检索:

Elasticsearch

向量检索:

pgvector

消息队列:

RocketMQ
或
Kafka

对象存储:

MinIO / OSS

AI:

Spring AI
或
LangChain4j
+
自研 Agent Runtime

任务调度:

XXL-JOB

复杂流程如果真的需要再考虑:

Flowable

Temporal

可观测:

Prometheus
Grafana
SkyWalking

部署阶段则:

Docker
+
Kubernetes

但还是那句话:

第一天不一定就需要 Kubernetes。

架构的目标永远不是复杂,而是让复杂业务变得可控制。


二十二、最终架构可以浓缩成这一张逻辑图

                         智能教育平台
                              │
          ┌───────────────────┼───────────────────┐
          │                   │                   │
        教师端               学生端              教务端
          │                   │                   │
          └───────────────────┼───────────────────┘
                              ↓
                         API Gateway
                              ↓
                       Application / BFF
                              ↓
      ┌──────────────── Core Domain ────────────────┐
      │                                             │
      │ Knowledge       Question       Paper         │
      │ 知识体系         题库           智能组卷       │
      │                                             │
      │ Lesson          Search         Template      │
      │ 智能备课         搜题           模板           │
      │                                             │
      └─────────────────────────────────────────────┘
             │                          │
             ↓                          ↓
       Domain Event                  AI Agent
             │                          │
             ↓                ┌─────────┼──────────┐
          Kafka / MQ          RAG      Tools      LLM
             │                 │                   │
      ┌──────┼──────┐         │                   │
      ↓      ↓      ↓         ↓                   ↓
     ES    Redis  Vector   Knowledge DB       Model Gateway

整个架构真正稳定的部分,是中间的领域模型。

AI 模型未来一定会变化。

今天可能用一个模型,半年以后可能全部切换。

Embedding 模型也会换,Agent 框架也会换。

但是:

什么是一道题

什么叫题目审核

什么叫试卷

什么叫知识点

什么叫组卷规则

什么叫教材章节

这些核心业务概念不会轻易改变。

这就是为什么要先做 DDD。


写在最后

做 AI 系统这两年,我越来越觉得一个误区特别明显:

很多人觉得 AI 来了之后,传统软件架构就不重要了。

实际恰恰相反。

因为 AI 是不确定的。

传统业务系统追求:

输入 A
一定得到 B

而 AI 可能是:

输入 A
这次得到 B
下次可能得到 C

正因为存在这种不确定性,核心业务才更加需要稳定的边界、确定的规则和可追踪的数据模型。

所以这类教育系统真正合理的架构,不应该是:

页面
 ↓
Spring Boot
 ↓
Prompt
 ↓
LLM

而应该是:

业务需求
    ↓
DDD 领域模型
    ↓
确定性业务规则
    ↓
AI Agent 能力增强
    ↓
规则验证
    ↓
最终业务结果

简单来说就是:

确定性的事情交给代码,不确定性的事情交给 AI。

题目是否已经审核通过,由领域模型决定。

一套试卷总分是不是 150 分,由规则引擎决定。

题型比例是否符合要求,由组卷算法决定。

AI 可以帮助理解老师想要什么,可以推荐更合适的题,也可以生成一道变式题,但它不应该成为整个业务系统最终的裁判。

如果把这个原则守住,再结合 DDD 做好 Knowledge、Question、Paper、Lesson、Search、Template 等业务边界,这套系统后面不管是扩展到小学、初高中,还是职业教育、在线教育,甚至继续加入 AI 学情分析、个性化推荐、自动批改,都还有比较充足的演进空间。

真正好的 AI 架构,从来不是“模型调用得有多花”,而是当模型发生变化、业务规模扩大、数据越来越多之后,系统依然能保持清晰。

而这,才是 DDD 在 AI Agent 时代最大的价值。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐