BossAgents SCSAI 回写优化 — 实现方案文档
版本:v1.0 | 日期:2026-06-25 | 基于 spec-SCSAI回写优化.md 需求规格 v2.0
1. 实现模型
1.1 上下文视图
1.1.1 系统上下文
SCSAI 回写优化在 BossAgents 整体架构中的定位——修复前端创建闭环、后端降级路径、关系格式转换、后端重试回滚、create_post 可靠性、前后端同步六大关键路径缺陷:
┌─────────────────────────────────────────────────────────────────────────┐
│ 外部系统 │
│ ┌───────────────┐ ┌───────────────┐ ┌────────────────────────────┐ │
│ │ 业务用户 │ │ SCSAI Agent│ │ SmartLLMRouter │ │
│ │ (Web/小程序) │ │ (AML操作) │ │ (5级降级链路) │ │
│ └───────┬───────┘ └───────┬───────┘ └─────────────┬──────────────┘ │
└──────────┼──────────────────┼─────────────────────────┼─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ BossAgents 核心层 │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 前端创建路径(权威实现) │ │
│ │ useCapabilityCreate.js → AmlBuilder.js → /SCSAI-api/ApplyItem │ │
│ │ ★ 5.1: generate输出对接前端创建流程 │ │
│ │ ★ 5.5: create_post规则执行结果可感知 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 后端创建路径(镜像实现) │ │
│ │ CapabilityRuntime.create() → rule-engine.js createItem() │ │
│ │ → AMLGenerator / aml-builder.js → SCSAIClient.sendAML() │ │
│ │ ★ 5.2: AMLGenerator item_properties补齐 │ │
│ │ ★ 5.3: 关系发现格式转换 │ │
│ │ ★ 5.4: 后端创建路径重试与回滚 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 同步维护层 │ │
│ │ ★ 5.6: 前后端创建逻辑同步维护 │ │
│ │ aml-builder.js @mirror-of AmlBuilder.js │ │
│ │ unified-create.js @mirror-of useCapabilityCreate.js │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
1.1.2 需求与模块映射
| 需求ID | 优先级 | 需求名称 | 主要修改模块 | 修改类型 |
|--------|--------|---------|-------------|---------|
| 5.1 | P0 | generate能力输出与前端创建流程对接 | capability-runtime.js, rule-engine.js, useCapabilityCreate.js | 修改 |
| 5.2 | P0 | AMLGenerator降级路径item_properties补齐 | aml-generator.js | 修改 |
| 5.3 | P0 | 关系发现格式转换 | capability-runtime.js, relationship-resolver.js | 修改+新增 |
| 5.4 | P1 | 后端创建路径重试与回滚 | SCSAI-client.js, rule-engine.js, unified-create.js | 修改 |
| 5.5 | P1 | 前端create_post规则执行可靠性 | rule-engine.js, server.js, useCapabilityCreate.js | 修改+新增 |
| 5.6 | P2 | 前后端创建逻辑同步维护 | aml-builder.js, unified-create.js, 测试文件 | 修改+新增 |
1.2 服务/组件总体架构
1.2.1 优化后的回写数据流
┌─────────────────────────┐
│ 用户触发 generate 能力 │
└───────────┬─────────────┘
│
┌───────────▼─────────────┐
│ CapabilityRuntime.generate() │
│ → executeGenerate() │
│ 返回 generated_content │
│ ★ 标准化输出格式: │
│ { item_type, properties, │
│ item_properties, │
│ relationships } │
└───────────┬─────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌───────────▼──────────┐ ┌─────▼──────────┐ ┌──────▼──────────┐
│ 前端权威创建路径 │ │ 后端镜像创建路径 │ │ 后端降级路径 │
│ useCapabilityCreate │ │ rule-engine.js │ │ AMLGenerator │
│ → AmlBuilder.js │ │ → aml-builder.js│ │ ★ item_props │
│ → /SCSAI-api/ApplyItem │ │ → SCSAIClient │ │ 补齐 │
│ ★ 自动消费 generate │ │ ★ 重试与回滚 │ │ │
│ 输出 │ │ ★ 关系格式转换 │ │ │
└───────────┬──────────┘ └─────┬──────────┘ └──────┬──────────┘
│ │ │
│ ┌────────▼────────┐ │
│ │ SCSAIClient │ │
│ │ ★ SOAP Fault解析 │ │
│ │ ★ 超时结构化错误 │ │
│ │ ★ 重试策略 │ │
│ └────────┬────────┘ │
│ │ │
┌───────────▼────────────────────▼─────────────────────▼──────────┐
│ SCSAI Agent │
└───────────────────────────────┬──────────────────────────────────┘
│
┌───────────────────▼───────────────────────┐
│ create_post 规则执行 │
│ ★ 前端可感知执行结果(非 fire-and-forget) │
│ ★ 重试机制(最多2次,指数退避) │
│ ★ 超时处理(30秒) │
│ ★ 结果持久化(前端断线可补发) │
└───────────────────────────────────────────┘
1.2.2 关系发现格式转换流
RelationshipResolver.discoverRelations()
│
▼ 返回 source_index/target_index 格式
[{ parent_index: 0, child_index: 1, relation_type: 'Part BOM', properties: {...} }]
│
▼ ★ 格式转换器(新增 convertDiscoveryRelations)
[{ relationship_type: 'Part BOM', related_item_type: 'Part',
related_items: [{ properties: {...} }] }]
│
▼ 传入 AmlBuilder / AMLGenerator
buildAML({ relationships: 转换后数据 })
1.3 实现设计文档
1.3.1 P0-5.1: generate能力输出与前端创建流程对接
#### 需求概述
executeGenerate() 返回的 generated_content 需要输出为前端 useCapabilityCreate 可直接消费的结构化 JSON,且前端能自动启动创建流程。
#### 当前问题分析
- executeGenerate() 输出格式不标准:当前
generated_content来自规则 action_script 的自由格式输出,不保证包含item_type/properties/item_properties/relationships四层结构 - 前端无法自动消费:useCapabilityCreate.js 期望接收
{ item_type, properties, item_properties, relationships }格式,但 generate 返回的是自由文本或非标准 JSON - LLM 降级路径输出不标准:LLM Router 返回的内容可能是
{"content": "..."}而非创建所需的结构化数据
#### 修改文件
| 文件路径 | 修改类型 | 说明 |
|---------|---------|------|
| server/core/rule-engine.js | 修改 | executeGenerate() 返回标准化 generated_content |
| server/core/capability-runtime.js | 修改 | generate() 方法确保输出格式标准化 |
| src/composables/useCapabilityCreate.js | 修改 | 新增 createFromGenerate() 方法消费 generate 输出 |
#### 核心类/函数设计
1. rule-engine.js — executeGenerate() 输出标准化
在 executeGenerate() 方法(第1683行)中,对 generated_content 进行格式标准化处理:
// executeGenerate() 返回前,标准化 generated_content
_standardizeGeneratedContent(rawContent, item_type) {
if (!rawContent) return null;
// 如果已是标准格式(含 item_type + properties),直接返回
if (typeof rawContent === 'object' && rawContent.item_type && rawContent.properties) {
return rawContent;
}
// 如果是字符串,尝试解析 JSON
let parsed = rawContent;
if (typeof rawContent === 'string') {
try {
const jsonMatch = rawContent.match(/\{[\s\S]*\}/);
if (jsonMatch) parsed = JSON.parse(jsonMatch[0]);
} catch (e) {
// 无法解析,返回原始文本包装
return { item_type, properties: { description: rawContent }, _unparseable: true };
}
}
// 如果解析后的对象有 properties 字段,提取标准格式
if (parsed && typeof parsed === 'object') {
// 情况1:已经是标准格式
if (parsed.item_type && parsed.properties) return parsed;
// 情况2:LLM 返回 { properties: {...}, relationships: [...] } 但无 item_type
if (parsed.properties && typeof parsed.properties === 'object') {
return {
item_type: item_type || parsed.item_type || 'Part',
properties: parsed.properties,
item_properties: parsed.item_properties || {},
relationships: parsed.relationships || []
};
}
// 情况3:LLM 返回扁平属性对象(最常见)
// 将非标准字段(relationships, item_properties)提取出来
const { relationships, item_properties, ...properties } = parsed;
return {
item_type: item_type || parsed.item_type || 'Part',
properties,
item_properties: item_properties || {},
relationships: relationships || []
};
}
// 无法标准化
return { item_type, properties: { description: String(rawContent) }, _unparseable: true };
}
2. capability-runtime.js — generate() 方法增强
在 generate() 方法(第774行)中,确保返回的 generated_content 是标准格式:
async generate(context, options = {}) {
const { item_type, data, template_id, autoCreate } = context;
const engine = await this._initRuleEngine();
const startTime = Date.now();
let result = null;
if (engine) {
const engineResult = await engine.executeGenerate(
{ item_type, data, template_id },
this._buildEngineOptions()
);
if (engineResult && engineResult.executed) {
this._recordRuleEngineHit();
// ★ 标准化 generated_content
engineResult.generated_content = this._standardizeGeneratedContent(
engineResult.generated_content, item_type
);
result = this._formatResult('generate', engineResult, {
source: engineResult.source || 'rule_engine',
durationMs: Date.now() - startTime
});
}
}
if (!result) {
// LLM 降级:使用创建专用 prompt
const systemPrompt = this._buildCreatePrompt(item_type, null, data);
const llmResult = await this._callLLMViaRouter({
prompt: JSON.stringify(data),
systemPrompt,
taskType: 'generate',
capability: 'generate',
context: { item_type, data },
});
if (llmResult) {
// ★ 标准化 LLM 输出
const standardized = this._standardizeGeneratedContent(
llmResult.data || llmResult.raw, item_type
);
result = this._formatResult('generate', {
executed: true,
generated_content: standardized,
scope: 'generate',
item_type
}, { source: 'llm_router', durationMs: Date.now() - startTime });
}
}
if (!result) {
return { capability: 'generate', success: false, error: '规则引擎和 LLM 均不可用' };
}
// ★ autoCreate 标记:指示前端是否自动启动创建流程
result.autoCreate = autoCreate !== false;
return result;
}
/**
* ★ 标准化 generate 输出为前端可消费格式
*/
_standardizeGeneratedContent(rawContent, item_type) {
if (!rawContent) return null;
if (typeof rawContent === 'object' && rawContent.item_type && rawContent.properties) {
return rawContent;
}
let parsed = rawContent;
if (typeof rawContent === 'string') {
try {
const jsonMatch = rawContent.match(/\{[\s\S]*\}/);
if (jsonMatch) parsed = JSON.parse(jsonMatch[0]);
} catch (e) {
return { item_type, properties: { description: rawContent }, _unparseable: true };
}
}
if (parsed && typeof parsed === 'object') {
if (parsed.item_type && parsed.properties) return parsed;
if (parsed.properties && typeof parsed.properties === 'object') {
return {
item_type: item_type || parsed.item_type || 'Part',
properties: parsed.properties,
item_properties: parsed.item_properties || {},
relationships: parsed.relationships || []
};
}
const { relationships, item_properties, ...properties } = parsed;
return {
item_type: item_type || parsed.item_type || 'Part',
properties,
item_properties: item_properties || {},
relationships: relationships || []
};
}
return { item_type, properties: { description: String(rawContent) }, _unparseable: true };
}
3. useCapabilityCreate.js — 新增 createFromGenerate() 方法
在前端 useCapabilityCreate.js 中新增方法,消费 generate 输出并自动启动创建流程:
/**
* ★ 从 generate 输出创建对象
* @param {Object} generatedContent - generate 返回的结构化数据
* { item_type, properties, item_properties, relationships }
* @param {Object} options - { autoCreate: true/false }
* @returns {Object} 创建结果
*/
async function createFromGenerate(generatedContent, options = {}) {
const { autoCreate = true } = options;
// 1. 格式校验
if (!generatedContent || !generatedContent.item_type) {
error.value = '生成内容无法自动创建,请手动填写'
return { success: false, reason: 'unparseable' }
}
// 2. 保留生成内容(失败不丢失)
result.value = { generated_content: generatedContent }
if (!autoCreate) {
// 仅展示,不自动创建
return { success: true, autoCreate: false, generatedContent }
}
// 3. 启动创建流程(复用 useCapabilityCreate 的 10 步流程)
try {
const createResult = await createObject({
item_type: generatedContent.item_type,
properties: generatedContent.properties || {},
item_properties: generatedContent.item_properties || {},
relationships: generatedContent.relationships || []
})
return createResult
} catch (e) {
// 创建失败,保留 generated_content 供用户手动修正
error.value = `创建失败: ${e.message},生成内容已保留`
return {
success: false,
reason: 'create_failed',
generatedContent,
error: e.message
}
}
}
#### 数据模型
generate 输出标准化格式:
interface GeneratedContent {
item_type: string // 必填,SCSAI ItemType 名称
properties: Record<string, any> // 必填,普通属性键值对
item_properties?: Record<string, { // 可选,Item 类型属性
action: 'add' | 'get'
item_type: string
properties?: Record<string, any>
id?: string
keyed_name?: string
}>
relationships?: Array<{ // 可选,关系定义
relationship_type: string
related_item_type: string
related_items: Array<{
properties?: Record<string, any>
related_id?: string | object
relItemProps?: Record<string, any>
}>
}>
_unparseable?: boolean // 内部标记:原始内容无法解析
}
#### 依赖关系
- 依赖
useCapabilityCreate.js的createObject()方法(已存在) - 依赖
AmlBuilder.js的buildAML()方法(已存在) - 依赖
rule-engine.js的executeGenerate()方法(已存在)
1.3.2 P0-5.2: AMLGenerator降级路径item_properties补齐
#### 需求概述
AMLGenerator._buildAML() 当前不支持 item_properties 字段的嵌套输出,item/foreign 类型字段的对象值被直接输出为字符串(裸 GUID),需与前端 AmlBuilder.buildItemElement() 输出一致。
#### 当前问题分析
- _buildAML() 缺少 item_properties 处理:当前代码(第347-383行)仅处理 properties 和 relationships,无 item_properties 嵌套输出
- item/foreign 类型字段对象值被裸输出:第359行
return \<${key}>${String(value)}${key}>\;将对象值直接String()化,输出[object Object] - 与 AmlBuilder 输出不一致:AmlBuilder.buildItemElement() 支持 item_properties 嵌套创建和 action='get' 引用
#### 修改文件
| 文件路径 | 修改类型 | 说明 |
|---------|---------|------|
| server/core/aml-generator.js | 修改 | _buildAML() 方法添加 item_properties 嵌套输出 |
#### 核心类/函数设计
1. aml-generator.js — 修改 _buildAML() 方法
在 _buildAML() 方法中,将 item/foreign 类型字段的对象值按 item_properties 格式输出,并在 properties XML 后、relationships XML 前插入 item_properties 嵌套输出:
_buildAML(data, schema, options) {
const { itemType } = schema;
const action = options.action || 'add';
const { properties } = schema;
const skipKeys = ['relationships', '_relationships', 'id'];
// ★ 收集 item_properties(从 data 中提取或从 schema 推断)
const itemProps = data.item_properties || data._item_properties || {};
// 构建属性 XML(★ 修改:item/foreign 类型对象值不直接输出,转入 itemProps)
const propertiesXml = Object.entries(data)
.filter(([key, value]) => value !== undefined && value !== null
&& !skipKeys.includes(key)
&& key !== 'item_properties' && key !== '_item_properties')
.map(([key, value]) => {
const prop = properties[key];
const dataType = prop?.type || 'string';
// ★ item/foreign 类型且值为对象 → 转入 item_properties 处理
if ((dataType === 'item' || dataType === 'foreign') && typeof value === 'object' && value !== null) {
itemProps[key] = value;
return null; // 不在 properties XML 中输出
}
// item/foreign 类型且值为字符串(GUID)→ 直接输出
if (dataType === 'item' || dataType === 'foreign') {
return ` <${key}>${String(value)}</${key}>`;
}
if (['list', 'color list', 'date', 'integer', 'float', 'decimal', 'number', 'boolean'].includes(dataType)) {
return ` <${key}>${this._escapeXml(String(value))}</${key}>`;
}
const escaped = this._escapeXml(String(value));
return ` <${key}><![CDATA[${escaped}]]></${key}>`;
})
.filter(Boolean) // ★ 过滤掉 null(被转入 item_properties 的字段)
.join('\n');
// ★ 构建 item_properties 嵌套 XML(与 AmlBuilder.buildItemElement 一致)
const itemPropsXml = this._buildItemPropertiesXml(itemProps);
// 构建 Relationships XML
const relationships = data.relationships || data._relationships || [];
const relationshipsXml = this._buildRelationshipsXml(relationships);
const idAttr = (action !== 'add' && data.id) ? ` id="${data.id}"` : '';
const aml = `<AML>
<Item type="${itemType}" action="${action}"${idAttr}>
${propertiesXml}${itemPropsXml}${relationshipsXml}
</Item>
</AML>`;
return aml;
}
/**
* ★ 新增:构建 item_properties 嵌套 XML
* 与 AmlBuilder.buildItemElement() 的 item_properties 处理逻辑一致
*
* 支持两种格式:
* 1. 嵌套创建:{ action: 'add', item_type: 'WBS Element', properties: { name: 'xxx' } }
* 2. 引用已有:{ action: 'get', item_type: 'Method', id: 'xxx', keyed_name: 'yyy' }
*/
_buildItemPropertiesXml(itemProperties) {
if (!itemProperties || typeof itemProperties !== 'object') return '';
let xml = '';
for (const [propName, propDef] of Object.entries(itemProperties)) {
if (!propDef || typeof propDef !== 'object') continue;
if (propDef.action === 'add' && propDef.item_type) {
// 嵌套创建:递归构建子 Item
const subProps = propDef.properties || {};
const subPropsXml = Object.entries(subProps)
.filter(([, v]) => v !== undefined && v !== null)
.map(([k, v]) => {
if (typeof v === 'object') return null; // 不支持更深层嵌套
return ` <${k}>${this._escapeXml(String(v))}</${k}>`;
})
.filter(Boolean)
.join('\n');
xml += `\n <${propName}>`;
xml += `\n <Item type="${this._escapeXml(propDef.item_type)}" action="add">`;
xml += subPropsXml ? '\n' + subPropsXml : '';
xml += `\n </Item>`;
xml += `\n </${propName}>`;
} else if (propDef.action === 'get' && propDef.id && propDef.item_type) {
// 引用已有对象
const keyedName = propDef.keyed_name || '';
xml += `\n <${propName}>`;
xml += `<Item type="${this._escapeXml(propDef.item_type)}" id="${this._escapeXml(propDef.id)}" keyed_name="${this._escapeXml(keyedName)}" action="get"/>`;
xml += `</${propName}>`;
}
}
return xml;
}
#### 依赖关系
- 依赖
server/utils/aml-builder.js的buildItemElement()作为格式参考(唯一真相源) - 不引入新依赖
1.3.3 P0-5.3: 关系发现格式转换
#### 需求概述
RelationshipResolver.discoverRelations() 返回 source_index/target_index 格式的关系数据,需转换为 AmlBuilder 标准的 relationship_type/related_item_type/related_items 格式后传入 AML 构建。
#### 当前问题分析
- capability-runtime.js create() 直接推送 source_index 格式:第880-888行,discoveredRelations 被直接 push 到 relationships 数组,保留了
source_index/target_index字段 - AmlBuilder 无法处理 source_index 格式:AmlBuilder.buildRelationshipElement() 期望
relationship_type/related_item_type/related_items格式 - 缺少格式转换器:系统中无 source_index → AmlBuilder 标准格式的转换逻辑
#### 修改文件
| 文件路径 | 修改类型 | 说明 |
|---------|---------|------|
| server/core/capability-runtime.js | 修改 | create() 方法中关系发现结果格式转换 |
| server/core/relationship-resolver.js | 新增 | convertDiscoveryRelations() 静态方法 |
#### 核心类/函数设计
1. relationship-resolver.js — 新增格式转换方法
在 RelationshipResolver 类中新增静态方法:
/**
* ★ 将 discoverRelations() 返回的 source_index/target_index 格式
* 转换为 AmlBuilder 标准的 relationship_type/related_item_type/related_items 格式
*
* @param {Array} discoveredRelations - discoverRelations() 返回的关系数组
* [{ parent_index, child_index, relation_type, properties, field_hint }]
* @param {Array} batchItems - 原始对象数组
* @param {string} mainItemType - 主对象类型
* @returns {Array} AmlBuilder 标准格式的关系数组
*/
static convertDiscoveryRelations(discoveredRelations, batchItems, mainItemType) {
if (!Array.isArray(discoveredRelations) || discoveredRelations.length === 0) return [];
if (!Array.isArray(batchItems) || batchItems.length === 0) return [];
// 按 relation_type 分组
const grouped = {};
for (const rel of discoveredRelations) {
const relType = rel.relation_type || rel.relationship_type;
if (!relType) {
console.warn('[RelationshipResolver] 关系类型为空,跳过');
continue;
}
const parentIdx = rel.parent_index ?? rel.source_index;
const childIdx = rel.child_index ?? rel.target_index;
// 索引越界检查
if (parentIdx === undefined || childIdx === undefined) continue;
if (parentIdx < 0 || parentIdx >= batchItems.length || childIdx < 0 || childIdx >= batchItems.length) {
console.warn(`[RelationshipResolver] 关系索引越界: parent=${parentIdx}, child=${childIdx}, batch_size=${batchItems.length}`);
continue;
}
if (!grouped[relType]) {
grouped[relType] = { relationship_type: relType, related_items: [] };
}
const childItem = batchItems[childIdx];
const relatedItemType = childItem.item_type || mainItemType;
// 设置 related_item_type(同一关系类型下取第一个子对象的类型)
if (!grouped[relType].related_item_type) {
grouped[relType].related_item_type = relatedItemType;
}
// 构建相关项
const relatedItem = {
properties: { ...childItem }
};
// 关系级别属性(如 quantity, sort_order)
if (rel.properties && Object.keys(rel.properties).length > 0) {
const relItemProps = {};
for (const [k, v] of Object.entries(rel.properties)) {
if (v !== null && v !== undefined && k !== 'SCSAI_relation_id' && k !== 'direction') {
relatedItem[k] = v; // 放在 related_item 层级
}
}
}
grouped[relType].related_items.push(relatedItem);
}
// 校验转换结果
const result = [];
for (const group of Object.values(grouped)) {
if (!group.relationship_type || !group.related_item_type || !group.related_items || group.related_items.length === 0) {
console.warn('[RelationshipResolver] 转换结果校验失败,跳过:', JSON.stringify(group).substring(0, 100));
continue;
}
result.push(group);
}
return result;
}
2. capability-runtime.js — 修改 create() 方法
在 create() 方法(第871-893行)中,将关系发现结果转换为 AmlBuilder 标准格式:
// ===== 关系感知:批量关系发现 =====
let discoveredRelations = [];
if (resolver && Array.isArray(context.batch_items) && context.batch_items.length > 1) {
try {
const discovery = await resolver.discoverRelations(item_type, context.batch_items);
discoveredRelations = discovery.relations || [];
if (discoveredRelations.length > 0) {
this._log('create', item_type, '自动发现关系', `${discoveredRelations.length} 条`);
// ★ 格式转换:source_index/target_index → relationship_type/related_item_type/related_items
const { RelationshipResolver } = require('./relationship-resolver');
const convertedRelations = RelationshipResolver.convertDiscoveryRelations(
discoveredRelations, context.batch_items, item_type
);
// 合并到 relationships(转换后的标准格式)
for (const rel of convertedRelations) {
relationships.push(rel);
}
}
} catch (e) {
this._log('create', item_type, '关系发现失败(非阻塞)', e.message);
}
}
#### 依赖关系
- 依赖
relationship-resolver.js的discoverRelations()方法(已存在) - 依赖
AmlBuilder.buildRelationshipElement()的输入格式要求(已存在)
1.3.4 P1-5.4: 后端创建路径重试与回滚
#### 需求概述
后端 createItem()(unified-create.js / rule-engine.js)中 applyAML 失败后需实施重试,SCSAIClient.sendAML() 需解析 SOAP Fault 返回结构化错误,多关系对象部分失败需回滚。
#### 当前问题分析
- SCSAI-client.js sendAML() 超时抛异常:第121-123行,超时时
reject(new Error(...)),调用方需 try/catch - rule-engine.js createItem() 无重试:第2538-2550行,applyAML 失败后直接返回
{ success: false } - unified-create.js 无回滚:创建步骤失败后不回滚已创建的对象
- SOAP Fault 解析已有但不完善:
_parseFault()方法已存在(第137-153行),但 sendAML() 的错误路径未统一使用
#### 修改文件
| 文件路径 | 修改类型 | 说明 |
|---------|---------|------|
| server/utils/SCSAI-client.js | 修改 | sendAML() 超时返回结构化错误而非抛异常 |
| server/core/rule-engine.js | 修改 | createItem() 添加重试与回滚逻辑 |
| server/routes/unified-create.js | 修改 | 7步创建流程添加回滚逻辑 |
#### 核心类/函数设计
1. SCSAI-client.js — sendAML() 超时返回结构化错误
修改 _sendRequest() 方法,超时时 resolve 而非 reject:
_sendRequest(url, options, postData) {
return new Promise((resolve, reject) => {
// ... 现有代码 ...
// 超时处理(★ 修改:resolve 而非 reject,返回结构化错误)
req.setTimeout(this.timeout, () => {
req.destroy();
// ★ 不再 reject,改为 resolve 结构化错误
resolve({
statusCode: 0,
headers: {},
data: '',
timeoutError: true,
timeoutMs: this.timeout
});
});
// ... 现有代码 ...
});
}
修改 sendAML() 方法,处理超时响应:
async sendAML(aml, opts = {}) {
// ... 现有代码 ...
const response = await this._sendRequest(url, options, soapEnvelope);
// ★ 超时处理:返回结构化错误
if (response.timeoutError) {
return {
success: false,
items: [],
fault: {
code: 'TIMEOUT',
string: `SCSAI请求超时 (${response.timeoutMs}ms)`,
actor: '',
detail: `timeout after ${response.timeoutMs}ms`
},
rawXml: '',
count: 0
};
}
// 检查 HTTP 状态码
if (response.statusCode >= 400) {
return {
success: false,
items: [],
fault: {
code: 'HTTP_ERROR',
string: `HTTP ${response.statusCode}`,
actor: '',
detail: response.data
},
rawXml: response.data,
count: 0
};
}
return this._parseResponse(response.data);
}
2. rule-engine.js — createItem() 添加重试与回滚
在 createItem() 方法中,对 applyAML 调用添加重试逻辑:
/**
* ★ 带重试的 AML 提交
* @param {Function} applyAML - AML 提交函数
* @param {string} aml - AML 字符串
* @param {Object} context - 上下文(用于重试时修改 AML)
* @returns {Object} { result, retryCount, retryReasons }
*/
async _applyAMLWithRetry(applyAML, aml, context) {
const maxRetries = 3;
let currentAml = aml;
const retryReasons = [];
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const result = await applyAML(currentAml);
// 成功
if (result && (result.items?.length > 0 || result.Item)) {
return { result, retryCount: attempt, retryReasons };
}
// 解析错误
const fault = result?.fault || {};
const faultCode = fault.code || '';
const faultString = fault.string || '';
// 权限错误 → 重试1次(重新获取 token 后重试)
if (/no default permission|permission_id|access denied/i.test(faultString) && attempt === 0) {
retryReasons.push({ attempt, reason: 'permission_error', faultCode, faultString });
// 权限重试:注入 World 权限
const worldAml = `<AML><Item type="Permission" action="get"><name>World</name></Item></AML>`;
try {
const worldRes = await applyAML(worldAml);
const worldId = worldRes?.items?.[0]?.id;
if (worldId) {
currentAml = currentAml.replace(
/<Item type="([^"]+)" action="add">/,
`<Item type="$1" action="add" permission_id="${worldId}">`
);
}
} catch (e) { /* 忽略 */ }
continue;
}
// 唯一性冲突 → 追加后缀重试(最多3次)
if (/not unique|PropertiesAreNotUnique/i.test(faultString)) {
retryReasons.push({ attempt, reason: 'uniqueness_conflict', faultCode, faultString });
const suffix = `-${attempt + 1}`;
// 在 item_number 或 name 后追加后缀
currentAml = currentAml.replace(
/<item_number>([^<]+)<\/item_number>/,
(match, val) => `<item_number>${val}${suffix}</item_number>`
);
if (attempt < 2) continue; // 最多追加3次后缀
}
// 超时 → 重试1次
if (faultCode === 'TIMEOUT' && attempt === 0) {
retryReasons.push({ attempt, reason: 'timeout', faultCode, faultString });
continue;
}
// 其他错误 → 指数退避重试
if (attempt < maxRetries) {
retryReasons.push({ attempt, reason: 'other_error', faultCode, faultString });
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
continue;
}
// 全部重试失败
return {
result,
retryCount: attempt,
retryReasons,
finalFault: fault
};
}
}
/**
* ★ 回滚已创建的对象
* @param {Function} applyAML - AML 提交函数
* @param {Array} createdIds - 已创建对象的 ID 列表 [{ item_type, item_id }]
* @returns {Object} { rolled_back, rolled_back_ids, orphan_ids }
*/
async _rollbackCreatedItems(applyAML, createdIds) {
const rolledBackIds = [];
const orphanIds = [];
for (const { item_type, item_id } of createdIds) {
try {
const deleteAml = `<AML><Item type="${item_type}" action="delete" id="${item_id}" /></AML>`;
const result = await applyAML(deleteAml);
if (result?.items?.length > 0 || result?.Item) {
rolledBackIds.push(item_id);
// ★ 审计日志
console.log('[AUDIT] rollback_delete', JSON.stringify({
action: 'rollback_delete',
item_type,
item_id,
reason: '后续步骤失败,回滚已创建对象',
timestamp: new Date().toISOString()
}));
} else {
orphanIds.push(item_id);
console.error('[CRITICAL] 回滚失败,孤儿对象:', item_id);
}
} catch (e) {
orphanIds.push(item_id);
console.error('[CRITICAL] 回滚异常,孤儿对象:', item_id, e.message);
}
}
return {
rolled_back: orphanIds.length === 0,
rolled_back_ids: rolledBackIds,
orphan_ids: orphanIds
};
}
3. rule-engine.js — createItem() 集成重试与回滚
修改 createItem() 方法中 applyAML 调用部分(第2538行附近):
// 3. 构建AML并提交(★ 使用带重试的提交)
let itemId = null, amlError = null;
try {
const cleanProps = {};
for (const [k, v] of Object.entries(context.properties || {})) {
if (v === null || v === undefined || v === '') continue;
if (typeof v === 'object') continue;
cleanProps[k] = v;
}
const { buildAML } = require('../utils/aml-builder');
const aml = buildAML({ item_type, action: 'add', properties: cleanProps, item_properties: context.item_properties, relationships: context.relationships });
if (applyAML) {
// ★ 带重试的 AML 提交
const { result: applyResult, retryCount, finalFault } = await this._applyAMLWithRetry(applyAML, aml, context);
// 提取 itemId
if (applyResult?.Item?.id) itemId = applyResult.Item.id;
if (!itemId && applyResult?.items?.[0]?.id) itemId = applyResult.items[0].id;
if (!itemId && applyResult?.id) itemId = applyResult.id;
if (!itemId) {
// ★ 嵌套 AML 失败 → 分步降级
const hasNested = Object.keys(context.item_properties || {}).length > 0 ||
(context.relationships || []).length > 0;
if (hasNested && retryCount > 0) {
// 分步降级:先创建主对象(仅 properties),再逐个创建子对象和关系
const stepDownResult = await this._stepDownCreate(applyAML, item_type, cleanProps, context.item_properties, context.relationships);
if (stepDownResult.success) {
itemId = stepDownResult.item_id;
} else {
return { success: false, errors: ['SCSAI提交失败(含分步降级)'], warnings, rule_results: allResults, aml, retry_count: retryCount, final_fault: finalFault };
}
} else {
return { success: false, errors: ['SCSAI提交失败'], warnings, rule_results: allResults, aml, retry_count: retryCount, final_fault: finalFault };
}
}
} else {
return { success: true, aml, rule_results: allResults, warnings, item_id: null };
}
} catch (e) { amlError = e.message; errors.push('AML创建失败: ' + e.message); }
#### 依赖关系
- 依赖
SCSAI-client.js的sendAML()方法(已存在,需修改超时处理) - 依赖
aml-builder.js的buildAML()方法(已存在)
1.3.5 P1-5.5: 前端create_post规则执行可靠性
#### 需求概述
前端创建成功后触发的 create_post 规则执行结果需能被前端感知,不允许 fire-and-forget 导致规则执行丢失。
#### 当前问题分析
- create_post 是 fire-and-forget:rule-engine.js createItem() 第2566-2570行,create_post 执行结果仅记录在
allResults.create_post中,但前端通过/SCSAI-api/ApplyItem直接提交时,不经过 createItem() - 前端直接提交不触发 create_post:前端通过
/SCSAI-api/ApplyItem代理提交 AML,绕过了后端规则引擎的 create_post 执行 - 无 create_post 结果通知机制:即使后端执行了 create_post,前端也无法获取结果
#### 修改文件
| 文件路径 | 修改类型 | 说明 |
|---------|---------|------|
| server.js | 修改 | 新增 /api/rule-engine/execute-create-post 端点 |
| server/core/rule-engine.js | 修改 | 新增 executeCreatePost() 方法(带重试和超时) |
| src/composables/useCapabilityCreate.js | 修改 | 创建成功后调用 create_post 端点 |
#### 核心类/函数设计
1. rule-engine.js — 新增 executeCreatePost() 方法
/**
* ★ 执行 create_post 规则(带重试和超时)
* 供前端创建成功后调用,替代 fire-and-forget 模式
*
* @param {Object} context - { item_type, item_id, properties, relationships }
* @param {Object} options - { maxRetries: 2, timeout: 30000 }
* @returns {Object} { success, rule_results, retry_count, error }
*/
async executeCreatePost(context, options = {}) {
const { item_type, item_id } = context;
const maxRetries = options.maxRetries ?? 2;
const timeoutMs = options.timeout ?? 30000;
if (!item_type || !item_id) {
return { success: false, error: '缺少 item_type 或 item_id', rule_results: [] };
}
const postContext = { ...context, item_id };
let lastError = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
// 超时控制
const result = await Promise.race([
this.execute('create_post', postContext, { conflict_strategy: 'merge' }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('create_post 执行超时')), timeoutMs)
)
]);
// 审计日志
console.log('[AUDIT] create_post', JSON.stringify({
action: 'create_post_execute',
item_type,
item_id,
attempt,
success: true,
rule_count: result.results?.length || 0,
timestamp: new Date().toISOString()
}));
return {
success: true,
rule_results: result.results || [],
retry_count: attempt,
elapsed: result.elapsed || 0
};
} catch (e) {
lastError = e.message;
// 审计日志(失败)
console.log('[AUDIT] create_post_failed', JSON.stringify({
action: 'create_post_execute',
item_type,
item_id,
attempt,
success: false,
error: lastError,
timestamp: new Date().toISOString()
}));
// 指数退避重试
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
}
}
}
return {
success: false,
error: lastError,
rule_results: [],
retry_count: maxRetries
};
}
2. server.js — 新增 create_post 端点
在 server.js 的路由处理中新增端点:
// POST /api/rule-engine/execute-create-post
// 前端创建成功后调用,执行 create_post 规则
if (pathname === '/api/rule-engine/execute-create-post') {
const { item_type, item_id, properties, relationships } = body || {};
if (!item_type || !item_id) {
return { success: false, message: '请提供 item_type 和 item_id' };
}
try {
const result = await engine.executeCreatePost(
{ item_type, item_id, properties: properties || {}, relationships: relationships || [] },
{ maxRetries: 2, timeout: 30000 }
);
return { success: result.success, data: result };
} catch (e) {
return { success: false, message: e.message };
}
}
3. useCapabilityCreate.js — 创建成功后调用 create_post
在前端创建成功后,调用 create_post 端点获取规则执行结果:
// ★ 创建成功后执行 create_post 规则(替代 fire-and-forget)
async function executeCreatePost(itemType, itemId, properties, relationships) {
try {
const response = await fetch('/api/rule-engine/execute-create-post', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
item_type: itemType,
item_id: itemId,
properties,
relationships
})
})
if (!response.ok) {
console.warn('[useCapabilityCreate] create_post 请求失败:', response.status)
return { success: false, error: `HTTP ${response.status}` }
}
const result = await response.json()
if (result.success) {
console.log('[useCapabilityCreate] create_post 执行成功:', result.data?.rule_results?.length || 0, '条规则')
} else {
console.warn('[useCapabilityCreate] create_post 执行失败:', result.data?.error)
// ★ 不回滚主对象,仅通知用户
error.value = `对象创建成功,但后续处理失败:${result.data?.error || '未知原因'}`
}
return result
} catch (e) {
console.warn('[useCapabilityCreate] create_post 请求异常:', e.message)
return { success: false, error: e.message }
}
}
#### 数据模型
create_post 结果持久化(用于前端断线后补发):
CREATE TABLE IF NOT EXISTS create_post_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_type TEXT NOT NULL,
item_id TEXT NOT NULL,
success INTEGER NOT NULL DEFAULT 0,
rule_results TEXT, -- JSON
error TEXT,
retry_count INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now','localtime')),
delivered INTEGER DEFAULT 0, -- 是否已推送到前端
delivered_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_post_results_item ON create_post_results(item_type, item_id);
CREATE INDEX IF NOT EXISTS idx_post_results_undelivered ON create_post_results(delivered) WHERE delivered = 0;
1.3.6 P2-5.6: 前后端创建逻辑同步维护
#### 需求概述
后端 aml-builder.js 与前端 AmlBuilder.js、后端 unified-create.js 与前端 useCapabilityCreate.js 的逻辑差异需通过自动化测试检测,后端镜像代码需标注前端对应文件和同步状态。
#### 当前问题分析
- aml-builder.js 与 AmlBuilder.js 已有差异:后端 aml-builder.js 无
_formatValue()方法(前端有 list 值匹配、日期格式化等),esc()方法实现略有不同 - unified-create.js 与 useCapabilityCreate.js 差异更大:unified-create.js 是简化版7步流程,useCapabilityCreate.js 是完整10步流程(含 AI 纠错闭环)
- 无自动化一致性测试:CI 中无前后端输出对比测试
#### 修改文件
| 文件路径 | 修改类型 | 说明 |
|---------|---------|------|
| server/utils/aml-builder.js | 修改 | 添加文件头 @mirror-of 标注 |
| server/routes/unified-create.js | 修改 | 添加文件头 @mirror-of 标注 |
| tests/aml-builder-consistency.test.js | 新增 | 前后端 AmlBuilder 输出一致性测试 |
| tests/create-flow-consistency.test.js | 新增 | 前后端创建流程逻辑一致性测试 |
#### 核心类/函数设计
1. 文件头 @mirror-of 标注
在 server/utils/aml-builder.js 文件头部添加:
/**
* 服务端 AML 构建器 - 将标准 JSON 格式转换为 SCSAI AML
*
* @mirror-of src/utils/AmlBuilder.js
* @last-sync 2026-06-25
* @sync-status drifted
*
* ⚠️ 此文件逻辑必须与前端 src/utils/AmlBuilder.js 保持完全一致
* 前端逻辑经过反复测试(十几种复杂业务对象),后端不得自创逻辑
*
* 已知差异(drifted 项):
* 1. 前端有 _formatValue() 方法(list值匹配、日期格式化),后端无
* 2. 前端 esc() 不转义单引号,后端转义
* 3. 前端支持 default_permission 注入,后端支持
* 4. 前端 buildRelationshipElement 支持 isSameType 分支,后端无
*/
在 server/routes/unified-create.js 文件头部添加:
/**
* unified-create.js — 服务端统一对象创建 API
*
* @mirror-of src/composables/useCapabilityCreate.js
* @last-sync 2026-06-25
* @sync-status drifted
*
* 镜像 src/composables/useCapabilityCreate.js 的创建流程
*
* 已知差异(drifted 项):
* 1. 前端10步完整流程(含AI纠错3次重试),后端7步简化流程
* 2. 前端有 _dualDbDedupCheck 双库查重,后端无
* 3. 前端有 precreateNestedItem 预创建,后端有简化版
* 4. 前端有权限重试(World权限),后端有
* 5. 前端有去重重试(追加后缀),后端有
*/
2. 自动化一致性测试
// tests/aml-builder-consistency.test.js
/**
* 前后端 AmlBuilder 输出一致性测试
*
* 目的:检测后端 aml-builder.js 与前端 AmlBuilder.js 的输出差异
* 在 CI 中运行,差异时构建失败
*/
const { buildAML: serverBuildAML } = require('../server/utils/aml-builder')
// 测试用例:标准三层数据结构
const testCases = [
{
name: '普通属性',
data: { item_type: 'Part', action: 'add', properties: { name: '测试零件', item_number: 'P-001' } }
},
{
name: '含 item_properties(嵌套创建)',
data: {
item_type: 'Project', action: 'add',
properties: { name: '测试项目' },
item_properties: {
wbs_id: { action: 'add', item_type: 'WBS Element', properties: { name: '测试 WBS' } }
}
}
},
{
name: '含 item_properties(引用已有)',
data: {
item_type: 'Project', action: 'add',
properties: { name: '测试项目' },
item_properties: {
scheduling_method: { action: 'get', item_type: 'Method', id: 'ABC123', keyed_name: 'Standard' }
}
}
},
{
name: '含 relationships',
data: {
item_type: 'Part', action: 'add',
properties: { name: '父零件' },
relationships: [{
relationship_type: 'Part BOM',
related_item_type: 'Part',
related_items: [{ properties: { name: '子零件', item_number: 'P-002' } }]
}]
}
},
{
name: '完整三层结构',
data: {
item_type: 'Project', action: 'add',
properties: { name: '完整项目' },
item_properties: {
wbs_id: { action: 'add', item_type: 'WBS Element', properties: { name: '项目 WBS' } }
},
relationships: [{
relationship_type: 'Project Tree',
related_item_type: 'Project',
related_items: [{ properties: { name: '子项目' } }]
}]
}
}
]
describe('AmlBuilder 前后端一致性', () => {
testCases.forEach(({ name, data }) => {
test(name, () => {
const serverAml = serverBuildAML(data)
// 基本校验:输出非空
expect(serverAml).toBeTruthy()
// 结构校验:包含必要的 XML 元素
expect(serverAml).toContain('<AML>')
expect(serverAml).toContain('</AML>')
expect(serverAml).toContain(`type="${data.item_type}"`)
expect(serverAml).toContain(`action="${data.action || 'add'}"`)
// item_properties 校验
if (data.item_properties) {
for (const [propName, propDef] of Object.entries(data.item_properties)) {
expect(serverAml).toContain(`<${propName}>`)
expect(serverAml).toContain(`</${propName}>`)
if (propDef.action === 'add') {
expect(serverAml).toContain(`type="${propDef.item_type}"`)
} else if (propDef.action === 'get') {
expect(serverAml).toContain(`id="${propDef.id}"`)
}
}
}
// relationships 校验
if (data.relationships?.length > 0) {
expect(serverAml).toContain('<Relationships>')
expect(serverAml).toContain('</Relationships>')
for (const rel of data.relationships) {
expect(serverAml).toContain(`type="${rel.relationship_type}"`)
}
}
})
})
})
#### 依赖关系
- 依赖 CI/CD 流水线(需配置测试步骤)
- 依赖
aml-builder.js和AmlBuilder.js的输出格式(已存在)
2. 接口设计
2.1 总体设计
SCSAI 回写优化涉及的接口变更遵循以下原则:
- 前端直接提交模式不变:
/SCSAI-api/ApplyItem代理保持不变 - 新增 create_post 端点:前端创建成功后主动调用
- 后端内部接口增强:重试、回滚、格式转换为内部逻辑,不新增外部 API
2.2 接口清单
2.2.1 新增接口
| 接口 | 方法 | 路径 | 说明 |
|------|------|------|------|
| create_post 执行 | POST | /api/rule-engine/execute-create-post | 前端创建成功后调用,执行 create_post 规则并返回结果 |
POST /api/rule-engine/execute-create-post
请求体:
{
"item_type": "Project",
"item_id": "ABC123DEF456",
"properties": { "name": "测试项目" },
"relationships": []
}
响应体(成功):
{
"success": true,
"data": {
"success": true,
"rule_results": [
{ "rule_id": "builtin-project-create-post-001", "rule_name": "Project: 子任务编号补全", "result": { "modified": true } }
],
"retry_count": 0,
"elapsed": 150
}
}
响应体(失败):
{
"success": false,
"data": {
"success": false,
"error": "create_post 执行超时",
"rule_results": [],
"retry_count": 2
}
}
2.2.2 变更接口
| 接口 | 变更类型 | 说明 |
|------|---------|------|
| CapabilityRuntime.generate() | 返回值变更 | generated_content 标准化为 { item_type, properties, item_properties, relationships } 格式 |
| SCSAIClient.sendAML() | 行为变更 | 超时不再抛异常,返回 { success: false, fault: { code: 'TIMEOUT' } } |
| rule-engine.js createItem() | 行为变更 | applyAML 失败后实施重试,支持分步降级和回滚 |
4. 数据模型
4.1 设计目标
- generate 输出标准化:定义
GeneratedContent接口,确保 generate 输出可被前端消费 - 关系格式统一:定义
AmlBuilderRelation接口,统一关系数据格式 - create_post 结果持久化:新增
create_post_results表,支持前端断线后补发 - 重试策略配置化:重试策略参数可配置,不硬编码
4.2 模型实现
4.2.1 GeneratedContent 接口
// generate 能力输出标准化格式
interface GeneratedContent {
// 必填
item_type: string; // SCSAI ItemType 名称
properties: Record<string, any>; // 普通属性键值对
// 可选
item_properties?: Record<string, { // Item 类型属性
action: 'add' | 'get';
item_type: string;
properties?: Record<string, any>;
id?: string; // action='get' 时必填
keyed_name?: string; // action='get' 时推荐
}>;
relationships?: AmlBuilderRelation[]; // 关系定义
// 内部标记
_unparseable?: boolean; // 原始内容无法解析
_source?: 'rule_engine' | 'llm_router'; // 生成来源
}
4.2.2 AmlBuilderRelation 接口
// AmlBuilder 标准关系格式(转换后)
interface AmlBuilderRelation {
relationship_type: string; // SCSAI RelationshipType 名称
related_item_type: string; // 关联对象 ItemType
related_items: AmlBuilderRelatedItem[]; // 关联对象列表
}
interface AmlBuilderRelatedItem {
properties?: Record<string, any>; // 子对象属性
item_properties?: Record<string, any>; // 子对象 Item 类型属性
related_id?: string | object; // 引用 ID
relItemProps?: Record<string, any>; // 关系级别附加属性
id?: string; // 已有对象 ID
item_number?: string; // 已有对象编号
}
4.2.3 DiscoveryRelation 接口(转换前)
// discoverRelations() 返回格式(转换前)
interface DiscoveryRelation {
parent_index: number; // 源对象在 batch_items 中的索引
child_index: number; // 目标对象在 batch_items 中的索引
relation_type: string; // SCSAI RelationshipType 名称
properties?: Record<string, any>; // 关系级别属性
field_hint?: string; // 关系发现来源提示
parent_ref?: string; // 父对象引用
}
4.2.4 create_post_results 表
CREATE TABLE IF NOT EXISTS create_post_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_type TEXT NOT NULL,
item_id TEXT NOT NULL,
success INTEGER NOT NULL DEFAULT 0,
rule_results TEXT, -- JSON 格式
error TEXT,
retry_count INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now','localtime')),
delivered INTEGER DEFAULT 0, -- 是否已推送到前端
delivered_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_post_results_item ON create_post_results(item_type, item_id);
CREATE INDEX IF NOT EXISTS idx_post_results_undelivered ON create_post_results(delivered) WHERE delivered = 0;
4.2.5 重试策略配置
# config.yaml 新增配置段
SCSAI_retry:
max_retries: 3
permission_retry: 1
uniqueness_retry: 3
timeout_retry: 1
backoff_base_ms: 1000
create_post:
max_retries: 2
timeout_ms: 30000
backoff_base_ms: 1000
4.2.6 回滚审计日志格式
// 审计日志记录格式
interface RollbackAuditLog {
action: 'rollback_delete';
item_type: string;
item_id: string;
reason: string;
timestamp: string; // ISO 8601
operator?: string; // 操作人(可选)
}
interface CreatePostAuditLog {
action: 'create_post_execute' | 'create_post_failed';
item_type: string;
item_id: string;
attempt: number;
success: boolean;
error?: string;
rule_count?: number;
timestamp: string;
}
BossAgents