本文档使用方法:将本文档内容复制粘贴至Google AIstudio Build中,然后执行。如果出现错误,点击auto-fix即可。
1. 引言
本文档基于项目的模块设计文档 (MDD) 的高级概念,运用公理设计(Axiomatic Design)原则,为 "Loop Forge"提供一个高层次的系统设计描述。公理设计旨在通过将客户需求 (CNs) 映射到功能需求 (FRs),再将功能需求映射到设计参数 (DPs),并力求满足独立性公理和信息公理,从而指导设计过程,创建出鲁棒且高效的系统。
本设计尝试遵循以下原则:
- 公理设计: 清晰映射 CNs -> FRs -> DPs,追求独立性公理和信息公理。
- 函数式编程理念: 尽可能将功能分解为具有明确输入和输出的无状态单元(DPs),强调数据流和转换。
- 奥卡姆剃刀: 避免不必要的复杂性,设计参数应简洁且直接实现功能需求。
- 语言无关性: 设计参数和流程描述应抽象,不依赖于特定的编程语言或框架。
本文档旨在成为后续工程实现的蓝图,指导开发者构建系统组件及其交互。
2. 客户需求 (Customer Needs - CNs)
从项目目标和用户场景出发,提炼出以下核心客户需求:
- CN1: 用户能够通过AI辅助,迭代地生成、审查和优化文档或代码。
- CN2: 用户能够灵活配置和控制迭代过程的关键参数与流程。
- CN3: 用户能够清晰地追踪和回顾整个迭代历史、AI的输出以及反馈。
- CN4: 系统应能处理用户提供的背景资料,并确保AI的输出具有结构化和可预测性。
- CN5: 系统运行应稳定可靠,并能在出现问题时提供明确的状态和错误反馈。
用户行为示例:
- 用户上传一份文档作为背景资料
- 用户向撰写者AI提出根据背景资料撰写目标文档的需求
- 用户向审查者AI提出目标文档需要达成的标准
- 用户设定撰写者AI每次应当提交2份草稿
- 用户设定最少迭代2轮,最多迭代5轮,评分阈值为90分
- 用户点击按钮,开始迭代
- 系统经过若干轮迭代得到了满意的结果文档。在达到最大迭代次数或者评分阈值之前,都无需用户干预,系统应自动进行迭代。
- 用户下载结果文档
- 迭代完成后,除了下载最终草稿,用户应该可以干预,是否需要再进行若干轮迭代。并且,此时应该展示最后一轮评审意见,并且可以让用户修改。
3. 功能需求 (Functional Requirements - FRs)
将客户需求分解为具体的、可操作的功能需求:
-
FR1: AI驱动的内容生成与修订 (对应CN1, CN4)
- FR1.1: 系统必须能接收并处理用户上传的多种格式背景资料文件。
- FR1.2: 系统必须能接收用户输入的初始撰写者指令。
- FR1.3: AI撰写者必须能根据初始指令和背景资料,生成用户指定数量(N)的独立文档草稿。
- FR1.4: AI撰写者必须能根据前一轮审查者选定的草稿和整合反馈,对内容进行修订,并生成N份新的独立文档草稿。
- FR1.5: AI撰写者的输出必须采用预定义的、结构化的JSON格式,包含对审查反馈的回应和N份草稿(每份草稿包含内容和修订总结)。
-
FR2: AI驱动的内容审查与反馈 (对应CN1, CN4)
- FR2.1: 系统必须能接收用户输入的审查者审查标准。
- FR2.2: AI审查者必须能接收并审查AI撰写者生成的所有N份草稿。
- FR2.3: AI审查者必须能为每一份草稿提供独立的、详细的审查意见和0-100分的评分。
- FR2.4: AI审查者必须能从N份草稿中选出其认为最优的一份草稿(标记其索引)。
- FR2.5: AI审查者必须能提供一份整合的审查反馈,包含选择原因和对下一轮撰写选定草稿的改进建议。
- FR2.6: AI审查者的输出必须采用预定义的、结构化的JSON格式,包含对N份草稿的审查数组、选定草稿索引和整合反馈。
-
FR3: 迭代过程的可配置性与控制 (对应CN2)
- FR3.1: 用户必须能配置迭代过程的最小迭代次数。
- FR3.2: 用户必须能配置迭代过程的最大迭代次数。
- FR3.3: 用户必须能配置迭代停止的目标评分。
- FR3.4: 用户必须能配置AI撰写者在每次迭代中生成的草稿数量(例如1-3份)。
- FR3.5: 用户必须能启动、重置整个迭代过程;系统能根据配置自动暂停迭代。
- FR3.6: 当迭代过程暂停时,用户必须能编辑AI审查者提供的整合反馈,并能手动触发继续执行指定轮次的迭代。
-
FR4: 迭代历史与结果的可视化与管理 (对应CN3)
- FR4.1: 系统必须能按迭代轮次顺序,清晰展示每一轮迭代的完整历史记录。
- FR4.2: 每一轮迭代记录必须详细展示该轮的撰写者指令、撰写者输出(包含其对上一轮反馈的回应、N份草稿的修订总结和内容)、审查者输出(包含对N份草稿的独立审查意见和评分、选定的草稿索引、以及整合反馈)。
- FR4.3: 系统必须能在界面上高亮或明确标识当前活动的迭代步骤以及被审查者选定的草稿。
- FR4.4: 用户必须能下载最终被选定草稿的文档内容。
- FR4.5: 系统必须能统计并展示与Gemini API交互所消耗的总输入和输出Token数量。
-
FR5: 系统稳定性与用户反馈 (对应CN5)
- FR5.1: 系统必须能捕获并处理与外部API交互时可能发生的错误(例如,无效API密钥、网络问题、配额超限)。
- FR5.2: 系统必须能捕获并处理AI响应解析错误(例如,返回的JSON格式不符合预定义的Schema,或关键字段缺失)。
- FR5.3: 系统必须能在界面上向用户提供清晰、易懂的当前处理状态信息和错误提示。
4. 设计参数 (Design Parameters - DPs)
为每个功能需求(FR)指定具体的设计参数(DP)。DP是实现FR的物理或逻辑手段,应抽象且语言无关。每个DP代表系统中的一个关键模块或功能单元,并明确其输入、输出和核心职责。
-
DP1: 用户输入与配置模块
- 输入: 原始背景资料文件、文本指令、数字配置、UI交互事件(如按钮点击、文本编辑)。
- 输出: 结构化背景资料数据、初始撰写指令、审查标准、迭代配置对象、手动继续指令。
- 职责: 接收、验证并结构化用户输入。提供配置参数的读写接口。
- 主要对应FRs: FR1.1, FR1.2, FR2.1, FR3.1-3.4, FR3.6, FR8.1 (输入部分)。
-
DP2: AI撰写指令生成器
- 输入: 结构化背景资料 (来自DP1), 初始撰写指令 (来自DP1), 上一轮审查结果对象 (来自DP6), 当前迭代轮次, 配置参数 (来自DP1)。
- 输出: AI撰写 Prompt (文本或结构化指令)。
- 职责: 根据当前上下文和配置,生成驱动AI撰写模块的详细指令。
- 主要对应FRs: FR1.3, FR1.4.
-
DP3: AI审查指令生成器
- 输入: 审查标准 (来自DP1), 当前轮次全部草稿内容数组 (来自DP5)。
- 输出: AI审查 Prompt (文本或结构化指令)。
- 职责: 根据审查标准和待审内容,生成驱动AI审查模块的详细指令。
- 主要对应FRs: FR2.2.
-
DP4: 外部AI模型接口
- 输入: AI模型 Prompt (来自DP2 或 DP3), 模型配置 (如模型名称)。
- 输出: 原始AI模型响应数据,或指示通信/API错误的通用错误对象。
- 职责: 封装与外部AI服务的交互。发送请求,接收原始响应,处理低级通信错误(网络、认证、配额)。跟踪并报告Token使用量。
- 主要对应FRs: FR1.3, FR1.4, FR2.2, FR4.5, FR5.1.
-
DP5: AI撰写响应解析与校验器
- 输入: 原始AI模型响应数据 (来自DP4), 撰写者 JSON Schema。
- 输出: 结构化撰写结果对象,或指示解析/Schema校验错误的通用错误对象。
- 职责: 接收原始响应,验证其格式和结构是否符合预期Schema,并解析出结构化结果。处理JSON解析和Schema不匹配错误。
- 主要对应FRs: FR1.5, FR5.2.
-
DP6: AI审查响应解析与校验器
- 输入: 原始AI模型响应数据 (来自DP4), 审查者 JSON Schema.
- 输出: 结构化审查结果对象,或指示解析/Schema校验错误的通用错误对象。
- 职责: 接收原始响应,验证其格式和结构是否符合预期Schema,并解析出结构化结果。处理JSON解析和Schema不匹配错误。
- 主要对应FRs: FR2.6, FR5.2.
-
DP7: 迭代流程管理器
- 输入: 迭代配置 (来自DP1), 手动继续指令 (来自DP1), 上一轮审查结果 (来自DP6), 当前迭代状态和历史数据 (来自DP9), 错误对象 (来自DP4, DP5, DP6)。
- 输出: 对其他DPs的调用信号,更新的系统状态(运行中、暂停、完成、错误)。
- 职责: 根据配置、输入和当前状态,协调各DP的执行顺序,控制迭代循环的进展、暂停和终止。接收并响应来自其他DP的错误信号。
- 主要对应FRs: FR3.5, FR3.6, FR5.3 (状态控制).
- 核心流程: (简化)
- 初始化 -> (DP7)
- 读取配置/输入 (DP1) -> (DP7)
- 循环 (未终止):
a. (DP7) -> 生成撰写Prompt (DP2) -> (DP7)
b. (DP7) -> 调用AI模型 (DP4) [Input: 撰写Prompt] -> (DP7) [Output: 原始响应 或 错误]
c. (DP7) -> 解析撰写响应 (DP5) [Input: 原始响应] -> (DP7) [Output: 撰写结果 或 错误]
d. (DP7) -> 生成审查Prompt (DP3) [Input: 撰写结果] -> (DP7)
e. (DP7) -> 调用AI模型 (DP4) [Input: 审查Prompt] -> (DP7) [Output: 原始响应 或 错误]
f. (DP7) -> 解析审查响应 (DP6) [Input: 原始响应] -> (DP7) [Output: 审查结果 或 错误]
g. (DP7) -> 记录历史 (DP9) [Input: 撰写结果, 审查结果]
h. (DP7) -> 检查终止条件 (配置, 审查结果); 若需暂停, 更新状态并等待手动继续 (来自DP1)
i. 错误处理: 若在 b, c, e, f 步骤收到错误,DP7 捕获错误,更新系统状态为错误,并将错误信息发送给 DP10。 - (DP7) -> 更新最终状态 (完成 或 错误) -> (DP10/DP8)
-
DP8: 用户界面展示与交互模块
- 输入: 结构化数据(来自DP1, DP9, DP5, DP6),系统状态和错误信息(来自DP7, DP10)。
- 输出: UI交互事件 (发送给 DP1 或 DP7)。
- 职责: 将内部数据和状态可视化呈现给用户。捕获用户交互并将其转化为系统输入。提供文件下载功能。
- 主要对应FRs: FR3.1-3.6 (部分,UI元素), FR4.1-4.5, FR5.3 (部分,错误/状态展示).
-
DP9: 迭代历史数据管理模块
- 输入: 每轮迭代的结构化撰写结果 (来自DP5) 和审查结果 (来自DP6)。
- 输出: 完整的迭代历史数据结构,按轮次查询的历史记录。
- 职责: 持久化存储和管理迭代过程中的所有中间和最终数据。提供历史数据的访问接口。
- 主要对应FRs: FR4.1-4.3, FR4.4 (通过提供最终内容访问).
-
DP10: 错误与状态报告模块
- 输入: 错误对象 (来自DP4, DP5, DP6, DP7), 系统状态更新 (来自DP7).
- 输出: 用户友好的错误消息,当前系统状态字符串或枚举。
- 职责: 接收并处理来自系统各处的错误信号,将其转化为可理解的错误信息。管理并报告系统的整体运行状态。
- 主要对应FRs: FR5.1-5.3.
5. FR-DP 映射矩阵 (Design Matrix)
设计矩阵用于分析功能需求(FRs)和设计参数(DPs)之间的关系。理想情况下,矩阵应为对角矩阵(解耦设计)或下三角矩阵(弱耦合设计)。
| DP1 输入/配置 | DP2 撰写Prompt | DP3 审查Prompt | DP4 AI接口 | DP5 撰写解析 | DP6 审查解析 | DP7 流程管理 | DP8 UI呈现 | DP9 历史管理 | DP10 错误/状态 | |
|---|---|---|---|---|---|---|---|---|---|---|
| FR1.1 | X | x | ||||||||
| FR1.2 | X | x | x | |||||||
| FR1.3 | x | X | X | x | x | x | x | |||
| FR1.4 | x | X | X | x | x | x | x | x | ||
| FR1.5 | X | x | x | x | x | |||||
| FR2.1 | X | x | x | |||||||
| FR2.2 | x | X | X | x | x | x | x | x | ||
| FR2.3 | X | x | x | x | x | |||||
| FR2.4 | X | x | x | x | x | |||||
| FR2.5 | X | x | x | x | x | |||||
| FR2.6 | X | x | x | x | x | |||||
| FR3.1-4 | X | x | x | |||||||
| FR3.5 | x | X | x | x | ||||||
| FR3.6 | X | x | x | X | x | x | ||||
| FR4.1-3 | x | x | x | X | X | x | ||||
| FR4.4 | x | x | x | X | x | |||||
| FR4.5 | X | x | x | x | ||||||
| FR5.1 | x | x | x | x | x | X | ||||
| FR5.2 | x | x | x | x | X | |||||
| FR5.3 | x | X | X |
- X: 主要DP,直接负责实现对应的FR。
- x: 次要影响或依赖。DP的改变可能影响此FR的实现,或FR的实现依赖此DP的输出/状态。
矩阵分析的强化: 该矩阵揭示了系统DPs之间存在多种形式的耦合,这在一个迭代、交互式系统中是不可避免的。关键在于识别耦合的类型并设计机制进行管理。
- 顺序耦合: 例如,DP2依赖DP1和DP6的输出,DP5/DP6依赖DP4的输出。这种依赖是串行执行流程的自然结果,由DP7(流程管理器)负责协调调用顺序。通过将每个DP设计为接收特定输入产生特定输出的纯函数(或类似概念),可以最大化这种顺序耦合的可控性。
- 广播耦合: 例如,DP7(流程管理)的状态更新需要通知DP8(UI呈现)和DP10(错误/状态)。DP10接收来自多个源(DP4, DP5, DP6, DP7, DP8)的错误。这种耦合可以通过观察者模式或集中的消息总线机制来管理,使得信息发布者不需要知道所有接收者。
- 数据耦合: DP之间通过传递数据结构进行交互(如DP1向DP2/DP3传递指令/标准,DP4向DP5/DP6传递响应)。这种耦合的强度取决于数据结构的复杂性和稳定性。通过定义清晰、稳定的数据契约(如JSON Schema),可以最小化数据结构变化对依赖方的影响。
- 控制耦合: DP7(流程管理)直接控制其他DPs的执行。这是其核心职责,将控制逻辑集中可以避免分布式控制带来的复杂性。其他DPs通常只向DP7报告执行结果或错误状态,而不直接控制流程。
本设计通过明确每个DP的输入、输出和职责,并突出DP7作为流程协调者的角色,旨在使得这些必要的耦合是显式的、可管理的,并符合公理设计的原则:
- 独立性: 尽管存在耦合,但通过定义清晰的模块边界和接口(DP的输入/输出),一个DP的内部实现可以在不影响其他DPs或FRs的情况下进行修改,前提是其接口契约不变。例如,DP4可以切换不同的AI供应商,只要它仍能按接口接收Prompt并返回原始响应。
- 信息内容: 每个DP只包含完成其特定职责所需的信息和逻辑。例如,Prompt生成的复杂性在DP2/DP3中,API通信细节在DP4中,解析逻辑在DP5/DP6中,流程编排在DP7中。这最大化了各模块的内聚性。
错误处理 (DP10) 被设计为一个横切关注点,与流程管理 (DP7) 紧密协作。当一个执行DP (如DP4, DP5, DP6) 遇到错误时,它向DP7报告一个标准化的错误对象。DP7根据流程逻辑决定如何响应(如停止迭代),并将错误信息传递给DP10进行格式化和报告,最终由DP8展示给用户。这种模式确保了错误处理的一致性和可管理性。
6. 结论
本公理设计文档基于MDD,将客户需求转化为明确的功能需求,并指定了抽象的、语言无关的设计参数来实现这些需求。通过FR-DP映射矩阵的分析,我们深入考察了系统各模块之间固有的耦合关系,并详细阐述了如何通过明确的DP输入/输出、职责定义、标准化的数据契约(Schema)以及中心化的流程管理(DP7)来管理这些耦合,从而在满足系统迭代和交互功能需求的同时,最大化设计的独立性和信息含量。同时,设计中考虑了错误处理作为跨模块的关注点,并指定了DP10进行集中管理。
所选择的设计参数及其定义的输入、输出和职责,结合DP7的流程示例,共同构成了后续工程实现的清晰蓝图,符合公理设计、函数式编程理念和奥卡姆剃刀的原则。未来的开发工作应严格遵循这些定义,确保实现与设计的契合。
7. 默认参数
- 背景资料默认为空,不含背景资料时应当亦可运行。
- 撰写者AI默认任务为:“用python写出斐波那契数列计算函数,至少4种算法”
- 审查者AI默认任务为:“代码审查。要求代码优雅,斐波那契数列算法至少4种,并且实质不同。”
- 最小迭代次数默认=2
- 最大迭代次数默认=5
- 评分阈值默认=90
- 界面语言默认为中文
8. 参考
推荐使用structured output。可以参考如下
// To run this code you need to install the following dependencies:
// npm install @google/genai mime
// npm install -D @types/node
import {
GoogleGenAI,
Type,
} from '@google/genai';
async function main() {
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
const config = {
thinkingConfig: {
thinkingBudget: -1,
},
responseMimeType: 'application/json',
responseSchema: {
type: Type.OBJECT,
properties: {
propertyA: {
type: Type.STRING,
},
propertyB: {
type: Type.NUMBER,
},
},
},
};
const model = 'gemini-2.5-pro-preview-06-05';
const contents = [
{
role: 'user',
parts: [
{
text: `INSERT_INPUT_HERE`,
},
],
},
];
const response = await ai.models.generateContentStream({
model,
config,
contents,
});
let fileIndex = 0;
for await (const chunk of response) {
console.log(chunk.text);
}
}
main();