吸收数字员工管理端服务运营预览
This commit is contained in:
@@ -43,6 +43,8 @@ public class DigitalEmployeeAdminWorkbenchRowDto {
|
|||||||
private String reportId;
|
private String reportId;
|
||||||
@JsonProperty("route_decision_id")
|
@JsonProperty("route_decision_id")
|
||||||
private String routeDecisionId;
|
private String routeDecisionId;
|
||||||
|
@JsonProperty("service_id")
|
||||||
|
private String serviceId;
|
||||||
@JsonProperty("occurred_at")
|
@JsonProperty("occurred_at")
|
||||||
private String occurredAt;
|
private String occurredAt;
|
||||||
@JsonProperty("synced_at")
|
@JsonProperty("synced_at")
|
||||||
|
|||||||
@@ -979,6 +979,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
|||||||
.notificationId(firstString(businessView.get("notification_id"), payload.get("notification_id")))
|
.notificationId(firstString(businessView.get("notification_id"), payload.get("notification_id")))
|
||||||
.reportId(firstString(businessView.get("report_id"), payload.get("report_id")))
|
.reportId(firstString(businessView.get("report_id"), payload.get("report_id")))
|
||||||
.routeDecisionId(firstString(businessView.get("route_decision_id"), payload.get("route_decision_id")))
|
.routeDecisionId(firstString(businessView.get("route_decision_id"), payload.get("route_decision_id")))
|
||||||
|
.serviceId(firstString(businessView.get("service_id"), businessView.get("serviceId"), payload.get("service_id"), payload.get("serviceId")))
|
||||||
.occurredAt(businessRow.getOccurredAt())
|
.occurredAt(businessRow.getOccurredAt())
|
||||||
.syncedAt(record.getSyncedAt())
|
.syncedAt(record.getSyncedAt())
|
||||||
.clientKeyMasked(maskClientKey(record.getClientKey()))
|
.clientKeyMasked(maskClientKey(record.getClientKey()))
|
||||||
@@ -997,6 +998,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
|||||||
return switch (view) {
|
return switch (view) {
|
||||||
case "interventions" -> isInterventionRow(row);
|
case "interventions" -> isInterventionRow(row);
|
||||||
case "approval_governance" -> isApprovalGovernanceRow(row);
|
case "approval_governance" -> isApprovalGovernanceRow(row);
|
||||||
|
case "service_operations" -> isServiceOperationsRow(row);
|
||||||
case "artifact_lifecycle" -> isArtifactLifecycleRow(row);
|
case "artifact_lifecycle" -> isArtifactLifecycleRow(row);
|
||||||
case "notifications" -> isNotificationRow(row);
|
case "notifications" -> isNotificationRow(row);
|
||||||
case "audits" -> isAuditRow(row);
|
case "audits" -> isAuditRow(row);
|
||||||
@@ -1037,6 +1039,20 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
|||||||
|| StringUtils.equals(row.getCategory(), "artifact_lifecycle");
|
|| StringUtils.equals(row.getCategory(), "artifact_lifecycle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isServiceOperationsRow(DigitalEmployeeAdminWorkbenchRowDto row) {
|
||||||
|
String kind = firstString(row.getKind());
|
||||||
|
String category = firstString(row.getCategory());
|
||||||
|
return StringUtils.equals(category, "managed_service")
|
||||||
|
|| StringUtils.equals(category, "service_operation")
|
||||||
|
|| StringUtils.equals(category, "service_health")
|
||||||
|
|| contains(kind, "managed_service")
|
||||||
|
|| contains(kind, "service_snapshot")
|
||||||
|
|| contains(kind, "service_restart")
|
||||||
|
|| contains(kind, "doctor_service")
|
||||||
|
|| contains(kind, "doctor_issue")
|
||||||
|
|| StringUtils.isNotBlank(row.getServiceId());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isNotificationRow(DigitalEmployeeAdminWorkbenchRowDto row) {
|
private boolean isNotificationRow(DigitalEmployeeAdminWorkbenchRowDto row) {
|
||||||
return StringUtils.equals(row.getCategory(), "notification")
|
return StringUtils.equals(row.getCategory(), "notification")
|
||||||
|| StringUtils.equals(row.getCategory(), "action_notification")
|
|| StringUtils.equals(row.getCategory(), "action_notification")
|
||||||
|
|||||||
@@ -349,6 +349,60 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
|||||||
assertThat(result.getRecords().get(3).getApprovalId()).isEqualTo("approval-1");
|
assertThat(result.getRecords().get(3).getApprovalId()).isEqualTo("approval-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryAdminWorkbenchServiceOperationsReturnsServiceHealthAndRestartRowsOnly() {
|
||||||
|
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
|
||||||
|
.thenReturn(page(List.of(
|
||||||
|
record("outbox-service-snapshot", "event", "event-service-1", 101L, new Date(10_000),
|
||||||
|
Map.of(
|
||||||
|
"entity_type", "event",
|
||||||
|
"category", "managed_service",
|
||||||
|
"kind", "service_snapshot",
|
||||||
|
"status", "degraded",
|
||||||
|
"title", "File Server 异常",
|
||||||
|
"service_id", "file-server",
|
||||||
|
"level", "warn",
|
||||||
|
"summary", "端口不可用"
|
||||||
|
),
|
||||||
|
Map.of("id", "event-service-1", "kind", "service_snapshot")),
|
||||||
|
record("outbox-service-restart", "event", "event-service-2", 101L, new Date(11_000),
|
||||||
|
Map.of(
|
||||||
|
"entity_type", "event",
|
||||||
|
"category", "service_operation",
|
||||||
|
"kind", "managed_service_restart",
|
||||||
|
"status", "recorded",
|
||||||
|
"title", "GUI Server 安全重启",
|
||||||
|
"service_id", "gui-server"
|
||||||
|
),
|
||||||
|
Map.of("id", "event-service-2", "kind", "managed_service_restart")),
|
||||||
|
record("outbox-doctor-service", "event", "event-service-3", 101L, new Date(12_000),
|
||||||
|
Map.of(
|
||||||
|
"entity_type", "event",
|
||||||
|
"kind", "doctor_service_issue",
|
||||||
|
"status", "failed",
|
||||||
|
"title", "服务诊断失败",
|
||||||
|
"service_id", "agent-server",
|
||||||
|
"reason", "health_check_failed"
|
||||||
|
),
|
||||||
|
Map.of("id", "event-service-3", "kind", "doctor_service_issue")),
|
||||||
|
record("outbox-approval", "approval", "approval-1", 101L, new Date(13_000),
|
||||||
|
Map.of("entity_type", "approval", "title", "审批", "status", "pending"),
|
||||||
|
Map.of("id", "approval-1", "status", "pending"))
|
||||||
|
)));
|
||||||
|
|
||||||
|
DigitalEmployeeAdminWorkbenchPageDto result = service.queryAdminWorkbench(
|
||||||
|
"service_operations", null, null, null, null, null, null, null, null, null, null, null, 1L, 20L
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(result.getTotal()).isEqualTo(3);
|
||||||
|
assertThat(result.getRecords()).extracting(DigitalEmployeeAdminWorkbenchRowDto::getEntityId)
|
||||||
|
.containsExactly("event-service-1", "event-service-2", "event-service-3");
|
||||||
|
assertThat(result.getRecords().get(0).getView()).isEqualTo("service_operations");
|
||||||
|
assertThat(result.getRecords().get(0).getServiceId()).isEqualTo("file-server");
|
||||||
|
assertThat(result.getRecords().get(1).getCategory()).isEqualTo("service_operation");
|
||||||
|
assertThat(result.getRecords().get(2).getServiceId()).isEqualTo("agent-server");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void queryAdminWorkbenchAuditsRedactsSensitivePayloadFields() {
|
void queryAdminWorkbenchAuditsRedactsSensitivePayloadFields() {
|
||||||
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
|
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const WORKBENCH_TABS: Array<{
|
|||||||
{ key: 'business', label: '业务视图' },
|
{ key: 'business', label: '业务视图' },
|
||||||
{ key: 'interventions', label: '介入台' },
|
{ key: 'interventions', label: '介入台' },
|
||||||
{ key: 'approval_governance', label: '审批治理' },
|
{ key: 'approval_governance', label: '审批治理' },
|
||||||
|
{ key: 'service_operations', label: '服务运营' },
|
||||||
{ key: 'artifact_lifecycle', label: '产物治理' },
|
{ key: 'artifact_lifecycle', label: '产物治理' },
|
||||||
{ key: 'notifications', label: '通知运营' },
|
{ key: 'notifications', label: '通知运营' },
|
||||||
{ key: 'audits', label: '审计诊断' },
|
{ key: 'audits', label: '审计诊断' },
|
||||||
@@ -101,6 +102,7 @@ function renderSummary(record: DigitalEmployeeAdminWorkbenchRow) {
|
|||||||
record.task_id && `Task ${record.task_id}`,
|
record.task_id && `Task ${record.task_id}`,
|
||||||
record.run_id && `Run ${record.run_id}`,
|
record.run_id && `Run ${record.run_id}`,
|
||||||
record.approval_id && `Approval ${record.approval_id}`,
|
record.approval_id && `Approval ${record.approval_id}`,
|
||||||
|
record.service_id && `Service ${record.service_id}`,
|
||||||
record.artifact_id && `Artifact ${record.artifact_id}`,
|
record.artifact_id && `Artifact ${record.artifact_id}`,
|
||||||
record.route_decision_id && `Route ${record.route_decision_id}`,
|
record.route_decision_id && `Route ${record.route_decision_id}`,
|
||||||
record.policy_kind && `Policy ${record.policy_kind}`,
|
record.policy_kind && `Policy ${record.policy_kind}`,
|
||||||
@@ -154,6 +156,9 @@ function actionTarget(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalEmp
|
|||||||
if (view === 'notifications' && record.notification_id) {
|
if (view === 'notifications' && record.notification_id) {
|
||||||
return { entityType: 'notification', entityId: record.notification_id };
|
return { entityType: 'notification', entityId: record.notification_id };
|
||||||
}
|
}
|
||||||
|
if (view === 'service_operations') {
|
||||||
|
return { entityType: 'event', entityId: record.entity_id };
|
||||||
|
}
|
||||||
if (view === 'artifact_lifecycle' && record.artifact_id) {
|
if (view === 'artifact_lifecycle' && record.artifact_id) {
|
||||||
return { entityType: 'artifact', entityId: record.artifact_id };
|
return { entityType: 'artifact', entityId: record.artifact_id };
|
||||||
}
|
}
|
||||||
@@ -211,6 +216,12 @@ function allowedActions(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalE
|
|||||||
{ action: 'dismiss', label: '忽略' },
|
{ action: 'dismiss', label: '忽略' },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
if (view === 'service_operations') {
|
||||||
|
return [
|
||||||
|
{ action: 'mark_reviewed', label: '标记已看' },
|
||||||
|
{ action: 'dismiss', label: '忽略' },
|
||||||
|
];
|
||||||
|
}
|
||||||
if (view === 'artifact_lifecycle' && record.artifact_id) {
|
if (view === 'artifact_lifecycle' && record.artifact_id) {
|
||||||
return [
|
return [
|
||||||
{ action: 'mark_keep', label: '标记保留' },
|
{ action: 'mark_keep', label: '标记保留' },
|
||||||
@@ -686,6 +697,7 @@ const DigitalEmployeeOps: React.FC = () => {
|
|||||||
detailRecord.task_id && `Task ${detailRecord.task_id}`,
|
detailRecord.task_id && `Task ${detailRecord.task_id}`,
|
||||||
detailRecord.run_id && `Run ${detailRecord.run_id}`,
|
detailRecord.run_id && `Run ${detailRecord.run_id}`,
|
||||||
detailRecord.approval_id && `Approval ${detailRecord.approval_id}`,
|
detailRecord.approval_id && `Approval ${detailRecord.approval_id}`,
|
||||||
|
detailRecord.service_id && `Service ${detailRecord.service_id}`,
|
||||||
detailRecord.artifact_id && `Artifact ${detailRecord.artifact_id}`,
|
detailRecord.artifact_id && `Artifact ${detailRecord.artifact_id}`,
|
||||||
detailRecord.notification_id &&
|
detailRecord.notification_id &&
|
||||||
`Notification ${detailRecord.notification_id}`,
|
`Notification ${detailRecord.notification_id}`,
|
||||||
@@ -725,6 +737,18 @@ const DigitalEmployeeOps: React.FC = () => {
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
)}
|
)}
|
||||||
|
{activeView === 'service_operations' && (
|
||||||
|
<Typography.Paragraph>
|
||||||
|
<Typography.Text strong>服务运营:</Typography.Text>
|
||||||
|
{detailRecord.service_id || detailRecord.entity_id || '--'}
|
||||||
|
{detailRecord.category && ` · ${detailRecord.category}`}
|
||||||
|
{detailRecord.level && ` · ${detailRecord.level}`}
|
||||||
|
<Typography.Text type="secondary">
|
||||||
|
{' '}
|
||||||
|
· 只记录复核/忽略意图,不开放 start/stop。
|
||||||
|
</Typography.Text>
|
||||||
|
</Typography.Paragraph>
|
||||||
|
)}
|
||||||
{allowedActions(detailRecord, activeView).length > 0 && (
|
{allowedActions(detailRecord, activeView).length > 0 && (
|
||||||
<div style={{ marginBottom: 16 }}>
|
<div style={{ marginBottom: 16 }}>
|
||||||
<Typography.Title level={5}>低风险动作</Typography.Title>
|
<Typography.Title level={5}>低风险动作</Typography.Title>
|
||||||
|
|||||||
@@ -560,6 +560,7 @@ export type DigitalEmployeeAdminWorkbenchView =
|
|||||||
| 'business'
|
| 'business'
|
||||||
| 'interventions'
|
| 'interventions'
|
||||||
| 'approval_governance'
|
| 'approval_governance'
|
||||||
|
| 'service_operations'
|
||||||
| 'artifact_lifecycle'
|
| 'artifact_lifecycle'
|
||||||
| 'notifications'
|
| 'notifications'
|
||||||
| 'audits'
|
| 'audits'
|
||||||
@@ -589,6 +590,7 @@ export interface DigitalEmployeeAdminWorkbenchRow {
|
|||||||
notification_id?: string;
|
notification_id?: string;
|
||||||
report_id?: string;
|
report_id?: string;
|
||||||
route_decision_id?: string;
|
route_decision_id?: string;
|
||||||
|
service_id?: string;
|
||||||
policy_kind?: string;
|
policy_kind?: string;
|
||||||
policy_key?: string;
|
policy_key?: string;
|
||||||
conflict_keys?: string[];
|
conflict_keys?: string[];
|
||||||
|
|||||||
@@ -1449,6 +1449,91 @@ function checkDigitalAdminApprovalGovernanceWorkbench() {
|
|||||||
console.log("[DigitalEmployeeCheck] admin approval governance workbench hooks OK");
|
console.log("[DigitalEmployeeCheck] admin approval governance workbench hooks OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkDigitalAdminServiceOperationsWorkbench() {
|
||||||
|
console.log("\n[DigitalEmployeeCheck] Verify admin service operations workbench hooks");
|
||||||
|
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
|
||||||
|
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
|
||||||
|
const servicePath = path.join(
|
||||||
|
backendRoot,
|
||||||
|
"app-platform-modules",
|
||||||
|
"app-platform-agent",
|
||||||
|
"app-platform-agent-core-application",
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"java",
|
||||||
|
"com",
|
||||||
|
"xspaceagi",
|
||||||
|
"agent",
|
||||||
|
"core",
|
||||||
|
"application",
|
||||||
|
"service",
|
||||||
|
"DigitalEmployeeSyncApplicationServiceImpl.java",
|
||||||
|
);
|
||||||
|
const dtoPath = path.join(
|
||||||
|
backendRoot,
|
||||||
|
"app-platform-modules",
|
||||||
|
"app-platform-agent",
|
||||||
|
"app-platform-agent-core-adapter",
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"java",
|
||||||
|
"com",
|
||||||
|
"xspaceagi",
|
||||||
|
"agent",
|
||||||
|
"core",
|
||||||
|
"adapter",
|
||||||
|
"dto",
|
||||||
|
"digital",
|
||||||
|
"DigitalEmployeeAdminWorkbenchRowDto.java",
|
||||||
|
);
|
||||||
|
const testPath = path.join(
|
||||||
|
backendRoot,
|
||||||
|
"app-platform-modules",
|
||||||
|
"app-platform-agent",
|
||||||
|
"app-platform-agent-core-application",
|
||||||
|
"src",
|
||||||
|
"test",
|
||||||
|
"java",
|
||||||
|
"com",
|
||||||
|
"xspaceagi",
|
||||||
|
"agent",
|
||||||
|
"core",
|
||||||
|
"application",
|
||||||
|
"service",
|
||||||
|
"DigitalEmployeeSyncApplicationServiceImplTest.java",
|
||||||
|
);
|
||||||
|
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
|
||||||
|
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
|
||||||
|
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
|
||||||
|
const service = fs.readFileSync(servicePath, "utf8");
|
||||||
|
const dto = fs.readFileSync(dtoPath, "utf8");
|
||||||
|
const test = fs.readFileSync(testPath, "utf8");
|
||||||
|
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
|
||||||
|
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
|
||||||
|
const docs = fs.readFileSync(docsPath, "utf8");
|
||||||
|
const requiredSnippets = [
|
||||||
|
[service, "service_operations", "backend service operations view"],
|
||||||
|
[service, "isServiceOperationsRow", "backend service operations matcher"],
|
||||||
|
[service, "managed_service", "backend managed service classifier"],
|
||||||
|
[service, "service_restart", "backend service restart classifier"],
|
||||||
|
[dto, "service_id", "backend service id row field"],
|
||||||
|
[test, "queryAdminWorkbenchServiceOperationsReturnsServiceHealthAndRestartRowsOnly", "backend service operations test"],
|
||||||
|
[qimingTypes, "service_operations", "qiming service operations view type"],
|
||||||
|
[qimingTypes, "service_id", "qiming service id field"],
|
||||||
|
[qimingOps, "服务运营", "qiming service operations tab"],
|
||||||
|
[qimingOps, "只记录复核/忽略意图", "qiming service operations safety copy"],
|
||||||
|
[qimingOps, "不开放 start/stop", "qiming service start stop guard copy"],
|
||||||
|
[docs, "正式外部管理端服务运营台预览", "docs service operations absorption"],
|
||||||
|
];
|
||||||
|
const missing = requiredSnippets
|
||||||
|
.filter(([content, snippet]) => !content.includes(snippet))
|
||||||
|
.map(([, , label]) => label);
|
||||||
|
if (missing.length > 0) {
|
||||||
|
throw new Error(`Missing admin service operations workbench hooks: ${missing.join(", ")}`);
|
||||||
|
}
|
||||||
|
console.log("[DigitalEmployeeCheck] admin service operations workbench hooks OK");
|
||||||
|
}
|
||||||
|
|
||||||
function checkLocalDatabaseSchema() {
|
function checkLocalDatabaseSchema() {
|
||||||
console.log("\n[DigitalEmployeeCheck] Verify local SQLite digital schema");
|
console.log("\n[DigitalEmployeeCheck] Verify local SQLite digital schema");
|
||||||
const dbPath = path.join(os.homedir(), ".qimingclaw", "qimingclaw.db");
|
const dbPath = path.join(os.homedir(), ".qimingclaw", "qimingclaw.db");
|
||||||
@@ -1512,6 +1597,7 @@ function main() {
|
|||||||
checkDigitalBackendMaterializedFacts();
|
checkDigitalBackendMaterializedFacts();
|
||||||
checkDigitalAdminRecordBillingArchiveShell();
|
checkDigitalAdminRecordBillingArchiveShell();
|
||||||
checkDigitalAdminApprovalGovernanceWorkbench();
|
checkDigitalAdminApprovalGovernanceWorkbench();
|
||||||
|
checkDigitalAdminServiceOperationsWorkbench();
|
||||||
checkLocalDatabaseSchema();
|
checkLocalDatabaseSchema();
|
||||||
console.log("\n[DigitalEmployeeCheck] All checks passed");
|
console.log("\n[DigitalEmployeeCheck] All checks passed");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -573,8 +573,8 @@ GET /api/digital-employee/approvals
|
|||||||
- 建议:下一轮围绕正式管理端治理工作台和 ACP 硬中断,把本地治理审计推进到完整跨端裁决与执行控制。
|
- 建议:下一轮围绕正式管理端治理工作台和 ACP 硬中断,把本地治理审计推进到完整跨端裁决与执行控制。
|
||||||
|
|
||||||
5. **ManagedService 控制面**
|
5. **ManagedService 控制面**
|
||||||
- 已吸收:Agent、MCP、Computer Server、Lanproxy、File Server、GUI Server 状态已进入 snapshot / projection;File Server 与 GUI Server 已开放白名单式 restart bridge,并会写入 `managed_service_restart` / `managed_service_restart_rejected` 事件,安全服务 restart bridge 已开始闭环。完整人机介入入口已开始吸收,embedded 新增“处置台”聚合服务异常、诊断风险、审计风险和策略拉取错误,支持批量标记已看、忽略、策略重拉和白名单服务安全重启。
|
- 已吸收:Agent、MCP、Computer Server、Lanproxy、File Server、GUI Server 状态已进入 snapshot / projection;File Server 与 GUI Server 已开放白名单式 restart bridge,并会写入 `managed_service_restart` / `managed_service_restart_rejected` 事件,安全服务 restart bridge 已开始闭环。完整人机介入入口已开始吸收,embedded 新增“处置台”聚合服务异常、诊断风险、审计风险和策略拉取错误,支持批量标记已看、忽略、策略重拉和白名单服务安全重启。正式外部管理端服务运营台预览已开始吸收,qiming-backend 新增 `service_operations` 运营台视图聚合 managed service 快照、服务诊断和重启审计,qiming“数字员工运营台”新增“服务运营” tab,并只开放复核/忽略等低风险动作意图,不开放 start/stop。
|
||||||
- 缺口:仍缺少 start / stop、lease 持久化、重启历史、自动恢复策略和正式外部管理端服务运营台;Agent、MCP、Computer Server、Lanproxy 等高风险服务不暴露数字员工重启动作。
|
- 缺口:仍缺少 start / stop、lease 持久化、重启历史、自动恢复策略和完整外部管理端服务运营台;Agent、MCP、Computer Server、Lanproxy 等高风险服务不暴露数字员工重启动作。
|
||||||
- 建议:先沉淀安全重启审计和人工确认体验,再评估更细粒度的服务恢复策略。
|
- 建议:先沉淀安全重启审计和人工确认体验,再评估更细粒度的服务恢复策略。
|
||||||
|
|
||||||
6. **技能生命周期与策略**
|
6. **技能生命周期与策略**
|
||||||
|
|||||||
Reference in New Issue
Block a user