Skip to the content.

教育图像场景

此类库属于 samples/AI,依赖 AI.Core、AI.Qwen 和通用 Core。命名空间由 MS.Microservice.AI.Core.Images 改为 MS.Microservice.Samples.EducationalImages,无兼容转发。 通用 AI 组件不依赖此场景。QuestionGeneration Harness 仍是共享可选包。

引用本项目后,先注册 AddMicroserviceAI(…).AddQwen(),再调用 services.AddImagePromptPipeline();需要参考图编辑时额外调用 services.AddQwenEducationalImages()。 场景测试位于 test/MS.Microservice.Samples.EducationalImages.Tests。 所有密钥由宿主配置注入;仓库快速启动不需要 AI 账号。

MS.Microservice.AI — Image Generation Pipeline

英语教学卡片图片 Prompt 生成类库。将原始文本输入(单词、短语、句子)转换为高质量的生图 prompt,支持两路输出:

该模块已集成到 MS.Microservice.AI.Core 框架中,与现有的 IAIImageGenerationClient / IAIImageGenerationProvider 体系无缝协作。


与框架的集成架构

                             ┌──────────────────────────────┐
                             │        appsettings.json      │
                             │  AI:Models:Chat:             │
                             │    ImagePromptPlanning       │
                             │  AI:Models:ImageGeneration:  │
                             │    Default                   │
                             └──────────────┬───────────────┘
                                            │
              ┌─────────────────────────────┼─────────────────────────┐
              │                             ▼                         │
              │  ┌──────────────────────────────────────────────┐    │
              │  │         ImageGenerationOrchestrator           │    │
              │  │  GenerateFromTextAsync(wordText) → result     │    │
              │  └────────┬──────────────────┬──────────────────┘    │
              │           │                  │                       │
              │           ▼                  ▼                       │
              │  ┌────────────────┐  ┌──────────────────────┐       │
              │  │ WordImagePrompt │  │ IAIImageGeneration   │       │
              │  │    Pipeline     │  │      Client           │       │
              │  │  (prompt 生成)  │  │  (Routing Client)     │       │
              │  └───────┬────────┘  └──────────┬───────────┘       │
              │          │                       │                   │
              │          ▼                       ▼                   │
              │  ┌────────────────┐  ┌──────────────────────┐       │
              │  │ PlanGenerator   │  │ IAIModelResolver     │       │
              │  │    Client       │  │ (scenario → model)   │       │
              │  │ (IAIChatClient) │  └──────────┬───────────┘       │
              │  └───────┬────────┘             │                   │
              │          │                      ▼                   │
              │          │         ┌──────────────────────┐         │
              │          │         │ IAIProviderFactory    │         │
              │          │         │ .GetRequiredImage-    │         │
              │          │         │  GenerationProvider() │         │
              │          │         └──────────┬───────────┘         │
              │          │                    │                     │
              │          ▼                    ▼                     │
              │  ┌────────────────┐  ┌──────────────────────┐       │
              │  │ RoutingAIChat  │  │ OpenAICompatible-     │       │
              │  │    Client      │  │ ImageGeneration-      │       │
              │  │ (scenario:     │  │ ProviderBase          │       │
              │  │  ImagePrompt   │  │ (images/generations)  │       │
              │  │  Planning)     │  └──────────────────────┘       │
              │  └────────────────┘                                 │
              │                                                     │
              │  MS.Microservice.AI.Core                            │
              └─────────────────────────────────────────────────────┘

关键集成点:

层级 组件 作用
编排层 ImageGenerationOrchestrator 一站式:文本 → prompt → 图片(教育图片应用层)
Prompt 层 WordImagePromptPipeline LLM 视觉规划 + prompt 构建
Chat 路由 IAIChatClient → RoutingAIChatClient 通过 scenario ImagePromptPlanning 解析 LLM 模型
Image 路由 IAIImageGenerationClient → RoutingAIImageGenerationClient 通过 scenario Default 解析生图模型
参考图编辑 IReferenceImageEditClient → provider adapter 参考图编辑需 provider adapter(当前由 Qwen 提供)
Provider 层 OpenAICompatibleImageGenerationProviderBase 调用 /v1/images/generations API

配置

在 appsettings.json 中配置 prompt 规划模型(Chat capability)和图片生成模型(ImageGeneration capability):

{
  "AI": {
    "DefaultProvider": "OpenAI",
    "Providers": {
      "OpenAI": {
        "ApiKeySecretName": "AI_OPENAI_API_KEY",
        "BaseAddress": "https://api.openai.com/v1/",
        "TimeoutSeconds": 120
      },
      "Qwen": {
        "ApiKeySecretName": "AI_QWEN_API_KEY",
        "BaseAddress": "https://dashscope.aliyuncs.com/compatible-mode/v1/",
        "TimeoutSeconds": 120
      }
    },
    "Models": {
      "Chat": {
        "ImagePromptPlanning": {
          "Provider": "OpenAI",
          "Model": "gpt-4.1-mini",
          "TimeoutSeconds": 60
        }
      },
      "ImageGeneration": {
        "Default": {
          "Provider": "OpenAI",
          "Model": "gpt-image-1",
          "Size": "1024x1024",
          "Quality": "standard"
        }
      }
    }
  }
}

Qwen 参考图编辑配置

参考图编辑是 Qwen-specific 能力,通过 IReferenceImageEditClient adapter 提供(需 AddQwen()):

{
  "AI": {
    "Providers": {
      "Qwen": {
        "ApiKeySecretName": "AI_QWEN_API_KEY",
        "BaseAddress": "https://dashscope.aliyuncs.com/compatible-mode/v1/",
        "Endpoints": {
          "MultimodalGeneration": "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
        }
      }
    },
    "Models": {
      "ImageEdit": {
        "QwenReferenceEdit": {
          "Provider": "Qwen",
          "Model": "qwen-image-edit-plus",
          "Size": "1024*1024",
          "Count": 1
        }
      }
    }
  }
}

注意:AIImageEditRequest 是通用二进制图片编辑(inpainting / background removal),不承载参考图 URL。 Qwen 参考图编辑使用独立的 IQwenImageReferenceEditClient / IReferenceImageEditClient。 OpenAI-compatible 层是 provider HTTP 复用层(OpenAICompatibleImageGenerationProviderBase),不是参考图编辑通道。

Scenario 说明

Scenario Capability 用途
ImagePromptPlanning Chat LLM 视觉规划(可自定义,传入 AddImagePromptPipeline("MyScenario"))
Default ImageGeneration 实际图片生成(可通过 AIImageGenerationRequest.Scenario 覆盖)
QwenReferenceEdit ImageEdit Qwen 参考图编辑(需 AddQwen() adapter)

依赖说明

包 用途
Microsoft.Extensions.Logging.Abstractions 日志
MS.Microservice.AI.Abstractions AI 能力抽象
MS.Microservice.AI.Core 路由、模型解析
MS.Microservice.Core 核心工具(DefaultSerializeSetting 等),允许依赖
Images/
├── Models/                              # 纯数据模型
│   ├── WordImageCardType.cs             # 卡片类型常量
│   ├── WordImageInput.cs                # 解析后的输入
│   ├── WordImageRow.cs                  # 批量输入行(Excel/DB)
│   ├── WordImagePromptPlan.cs           # 最终合并计划
│   ├── WordImageVisualPlan.cs           # LLM 原始输出的结构化视觉计划
│   ├── VisualContextGroup.cs            # 视觉上下文组
│   ├── VisualContextMember.cs           # 组成员(行级视觉指引,含 EditDelta)
│   ├── CharacterProfile.cs              # 角色外观画像
│   ├── SentenceImageEditDelta.cs        # 结构化编辑 Delta
│   ├── SentenceImageEditOperation.cs    # 单个编辑操作
│   ├── ReferenceImageEditRequest.cs     # 参考图编辑请求 DTO
│   └── ImageGenerationResultTypes.cs    # 生成结果类型
│
├── IPlanGeneratorClient.cs
├── ISceneGroupingAgent.cs
├── IReferenceImageEditClient.cs         # 参考图编辑客户端接口(Core.Images 层)
├── PlanGeneratorClient.cs
├── SceneGroupingAgent.cs
├── WordImagePromptPipeline.cs           # 主编排器:Parse → Plan → Enrich → Build
├── ImageGenerationOrchestrator.cs       # 一站式编排器:文本 → prompt → 图片 / delta → 参考图编辑
├── SentenceEditDeltaAgent.cs            # 结构化编辑 Delta Agent
├── SentenceImageBatchOrchestrator.cs    # 批量句子生图编排器
│
├── Pipeline/
│   ├── VisualPlanEnricher.cs
│   ├── VisualPlanValidator.cs
│   ├── VisualPlanRepairer.cs
│   └── VisualPlanSceneSimplifier.cs
│
├── Building/
│   ├── EducationalFlashcardPromptBuilder.cs
│   ├── QwenSafePromptBuilder.cs
│   ├── SentenceImageContinuityPromptBuilder.cs  # 共享场景上下文构建
│   ├── SentenceImageEditPromptBuilder.cs       # 结构化 Delta → 编辑 Prompt
│   ├── SentenceImagePromptBranchComposer.cs
│   └── SentenceImageReferenceEditPolicy.cs
│
├── Analysis/
│   └── SentenceSemanticAnalyzer.cs
│
└── Helpers/
    ├── PromptSanitizer.cs
    └── PromptNormalizer.cs

数据流

输入: "Be careful! Don't run in the classroom."
  │
  ▼
Parse() → WordImageInput {
    RawInput: "Be careful! Don't run in the classroom."
    TargetText: "Be careful! Don't run in the classroom."
    MeaningHint: null
    ContentType: "sentence"
}
  │
  ▼
IPlanGeneratorClient.GenerateVisualPlanAsync() → WordImageVisualPlan {
    visualMeaning: "A child running between desks while a teacher gestures to stop..."
    sentenceIntent: "prohibition"
    primaryActor: "child"
    secondaryActor: "teacher"
    requiredAction: "..."
    prohibitedAction: "running"
    warningCue: "teacher raises palm in stopping gesture"
    safetyCue: "child near desks showing why running is unsafe"
    sceneSetting: "a bright classroom"
    settingCues: ["desks", "chairs", "windows", "blank board"]
    ...
}
  │
  ▼
VisualPlanEnricher.Enrich()          ← 确定性规则注入 mustShow/mustNotShow
  │
  ▼
VisualPlanValidator.Validate()       ← 检查禁止句/安全句/教室/跑步是否完备
  │ (如有缺失)
  ▼
VisualPlanRepairer.Repair()          ← 自动修复缺失项
  │
  ▼
VisualPlanSceneSimplifier.Simplify() ← 场景去冗余(剔除教室/杂乱关键词,仅保留一个核心环境锚点)
  │
  ▼
MergeVisualPlan() → WordImagePromptPlan  ← 合并 LLM 输出 + 规则补强 + 修复 + 去冗余结果
  │
  ├──▶ EducationalFlashcardPromptBuilder.Build()  → richPrompt (存 DB)
  │
  └──▶ QwenSafePromptBuilder.Build()              → safePrompt (发 Qwen)

双 Prompt 对比

以 Keep off the grass. 为例:

维度 Rich Prompt (存 DB) Safe Prompt (发 Qwen)
长度 ~500 words ~80 words
风格 约束式 (“No…”, “Avoid…”, “Strictly…”) 纯正向描述
敏感词 包含 (barefoot, injury, violent, sexual…) 零
用途 调试追溯、审计 实际生图

Rich Prompt 片段

…No flags, flag symbols, national emblems, political symbols, military elements, maps with borders, violent, sexual, hateful, disturbing, or adult content. All people must wear appropriate footwear — strictly no bare feet…

Safe Prompt 片段

A simple 4:3 horizontal illustration in bright cheerful children’s storybook style… A child walks beside a grassy lawn while a nearby adult gently signals to stay on the path… The scene takes place in a park path next to a lawn. Recognizable details include paved walkway, green grass area…


宿主项目集成

方式一:一站式编排器(推荐) — 普通生图

使用 ImageGenerationOrchestrator 一步完成”文本 → prompt → 图片”的完整流程:

// Program.cs — DI 注册
builder.Services.AddMicroserviceAI(builder.Configuration)
    .AddOpenAI()        // 或其他 Provider
    .AddQwen()          // 如需 Qwen 参考图编辑
    .Services
    .AddImagePromptPipeline();

// YourService.cs — 使用
public class FlashcardService
{
    private readonly ImageGenerationOrchestrator orchestrator;

    public FlashcardService(ImageGenerationOrchestrator orchestrator)
    {
        this.orchestrator = orchestrator;
    }

    public async Task<ImageGenerationResult> GenerateCardImageAsync(string wordText)
    {
        return await orchestrator.GenerateFromTextAsync(wordText);
    }
}

方式二:参考图编辑(基于结构化 EditDelta)

// SentenceEditDeltaAgent 写入 member.EditDelta
var agent = provider.GetRequiredService<SentenceEditDeltaAgent>();
await agent.EnrichAsync(group, rows);

// ImageGenerationOrchestrator 执行参考图编辑
var editResult = await orchestrator.GenerateFromReferenceEditDeltaAsync(
    member.EditDelta!, referenceImageUrl);

// editResult.ReusedSourceImage → true 表示复用源图
// editResult.ImageResponse.Images[0].Url → 编辑后的图片 URL

方式三:批量句子生图(完整流程)

// Step 1: 场景分组
var groupingAgent = provider.GetRequiredService<ISceneGroupingAgent>();
var groupingResult = await groupingAgent.GroupAsync(rows);

// Step 2: 结构化 EditDelta 注入
var deltaAgent = provider.GetRequiredService<SentenceEditDeltaAgent>();
foreach (var group in groupingResult.Groups)
{
    await deltaAgent.EnrichAsync(group, rows);
}

// Step 3: 批量生图(内部自动处理 reference edit 逻辑)
var batchOrchestrator = provider.GetRequiredService<SentenceImageBatchOrchestrator>();
var results = await batchOrchestrator.GenerateBatchAsync(rows);

注意:不要在宿主项目中直接注入 DashScopeService 做图生图。以下职责留在宿主项目:

AI 模块只返回 AIImageResponse、prompt、是否复用源图、是否使用 reference edit。

关键设计决策

1. 为什么使用 Scenario 而非硬编码 Model

PlanGeneratorClient 通过 AIChatRequest.Scenario 触发框架的 IAIModelResolver 模型解析,而非硬编码模型名。这意味着:

// 默认 scenario = "ImagePromptPlanning"
services.AddImagePromptPipeline();

// 或使用自定义 scenario
services.AddImagePromptPipeline("MyCustomImagePlanner");

2. 为什么 Safe Prompt 不能简单去掉否定词

Qwen/DashScope 的内容审核是基于 prompt 文本本身做关键词扫描的,不看语义意图。以下 prompt 仍然会被拦截:

Do not show any violence, blood, or weapons.

因为 violence, blood, weapons 三个词出现在了文本中,即使前面有 “Do not show”。

QwenSafePromptBuilder 使用 PromptSanitizer 完全剔除所有敏感词,并用 CleanNegativeLanguage 将否定约束转换为正向描述。

3. 为什么 LLM Planner + 确定性规则

LLM 规划的视觉计划可能遗漏关键语义元素(例如忘记画禁止动作、忘记画制止线索)。VisualPlanEnricher 用正则表达式检测句子类型,强制注入缺失的 mustShow/mustNotShow 约束。VisualPlanValidator 在 prompt 组装前做最终检查,VisualPlanRepairer 自动修复问题。

4. 为什么需要 IPlanGeneratorClient 抽象

将 LLM 调用抽象为接口的好处:

5. 为什么引入 ImageGenerationOrchestrator

ImageGenerationOrchestrator 将”prompt 规划”和”图片生成”两个步骤编排为一个原子操作:

无编排器 有编排器
手动调用 WordImagePromptPipeline → 得到 prompt orchestrator.GenerateFromTextAsync(text) → 得到图片
手动构建 AIImageGenerationRequest → 调用 IAIImageGenerationClient 一站式调用,统一的错误处理和 fallback
需自行管理 rich prompt 存储 ImageGenerationResult 同时返回 rich prompt + 图片

6. 反冗余设计(Anti-Clutter Design)

为避免生成图片中出现过多无关元素(如多个娱乐设施、不应出现的黑板等),系统引入了多层防护:

层级 机制 说明
LLM Prompt Rule 9, 12, 13, 16 黑板仅限教室场景、安全句仅选一个焦点、settingCues 1-3 个、cue 是环境描述不是主体
LLM Prompt Example 2 提供”Be careful! Play safely.”正确示例:仅滑梯一个游乐设施,无黑板
VisualPlanEnricher 归一化 NormalizeList(plan.SettingCues, 1) — 无论 LLM 返回几个 cue,最多保留 1 个
VisualPlanSceneSimplifier 关键词剔除 SimplifySettingCues() 剔除 ClassroomCueKeywords(board/blackboard/desk…)和 ClutterCueKeywords(basketball/swing/poster…),仅保留 1 个 PrimaryEnvironmentAnchor(slide/road/bench…)
VisualPlanSceneSimplifier MustShow 精简 剔除 clutter 关键词;安全句额外过滤 “multiple equipment” / “several” / “various”
VisualPlanSceneSimplifier SupportingVisual 精简 非教室句剔除教室关键词;无关 clutter 剔除
VisualPlanSceneSimplifier MustNotShow 注入 非教室句注入 blackboard/whiteboard/classroom board;所有句注入 extra playground equipment / unrelated sports balls / decorative props
QwenSafePromptBuilder .Take(2) Safe prompt 中 settingCues 最多保留 2 个
EducationalFlashcardPromptBuilder minimal props “every object must contribute to understanding” 约束

VisualPlanSceneSimplifier 是管线 Step 3(紧随 Enrich → Validate → Repair 之后),作为最后一道确定性防线,在 prompt 组装前确保场景简洁、聚焦、无多余元素。

7. 场景分组与批量生成(Scene Grouping & Batch Generation)

SceneGroupingAgent 提供 LLM 驱动的句子分组能力,用于批量图片生成时保持组内视觉一致性:

Excel/DB 行数据 → SceneGroupingAgent.GroupAsync()
    ├── 预分配组 (SceneGroupId 列) → 直接分组,confidence=1.0
    └── 未分配行 → LLM 语义分组
        ├── dialogue / greeting / object_drill → 共享角色+场景
        ├── instructional_sequence / safety_rules → 独立单句
        └── 低置信度行 → 标记为 uncertainRowIds

组内图片生成策略(SentenceImageReferenceEditPolicy):
    ├── object_drill / dialogue / greeting → 结构化 EditDelta + 参考图编辑
    ├── safety_rules / instructional_sequence → 各自独立生成
    └── 组内句子 → SentenceImageContinuityPromptBuilder 注入共享上下文

完整批量流程:
    ├── SceneGroupingAgent.GroupAsync(rows)
    ├── SentenceEditDeltaAgent.EnrichAsync(group, rows)
    ├── 保存 group/member/editDelta 到 DB
    └── SentenceImageBatchOrchestrator.GenerateBatchAsync(rows, ...)

配置方式:AddImagePromptPipeline() 自动注册 SceneGroupingAgent 为 Singleton。SentenceEditDeltaAgent 和 SentenceImageBatchOrchestrator 均为 Transient。

8. 组件接口概览

类型 生命周期 职责
IPlanGeneratorClient → PlanGeneratorClient Singleton LLM 视觉规划(alphabet / visual plan)
ISceneGroupingAgent → SceneGroupingAgent Singleton LLM 句子场景分组
WordImagePromptPipeline Transient 文本解析 → 视觉规划 → prompt 构建
SentenceEditDeltaAgent Transient 结构化编辑 Delta 注入(LLM → EditDelta)
ImageGenerationOrchestrator Transient 一站式:文本 → prompt → 图片 / delta → 参考图编辑
SentenceImageBatchOrchestrator Transient 批量句子生图编排(场景分组 + 参考图编辑)

9. 结构化参考图编辑数据流(SentenceEditDeltaAgent)

新的结构化方案替代了旧的 IMAGE EDIT DELTA 文本注入。数据流如下:

SceneGroupingAgent.GroupAsync(rows)
    │
    ▼
VisualContextGroup (含 anchor row + members)
    │
    ▼
SentenceEditDeltaAgent.EnrichAsync(group, rows)
    │   LLM 基于 group 上下文 + anchor row 产出 JSON deltas
    │   归一化后写入 member.EditDelta
    │
    ▼
SentenceImageEditPromptBuilder.CanUseReferenceEdit(member.EditDelta)
    │   confidence >= 0.6 && 恰好 1 个 concrete operation → 可用
    │
    ├── false → 复用源图 URL 或降级独立生图
    │
    └── true
        │
        ▼
SentenceImageEditPromptBuilder.BuildPrompt(delta)
    │   "Only edit: box -> apple."
    │
    ▼
IReferenceImageEditClient.EditReferenceAsync(request)
    │   (QwenReferenceImageEditAdapter → IQwenImageReferenceEditClient)
    │
    ▼
AIImageResponse (编辑后的图片)

关键特性:

依赖

包 用途
Microsoft.Extensions.Logging.Abstractions 日志
MS.Microservice.AI.Abstractions IAIChatClient, IAIImageGenerationClient, AIImageGenerationRequest, etc.
MS.Microservice.AI.Core RoutingAIChatClient, RoutingAIImageGenerationClient, IAIModelResolver

IPlanGeneratorClient 的默认实现 PlanGeneratorClient 已内置在 Core 项目中。宿主项目无需额外提供实现。