From 797ffeee82da16b0f0ad74cc486cbeb44215d818 Mon Sep 17 00:00:00 2001 From: baiyanyun Date: Sat, 13 Jun 2026 16:36:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=B8=E6=94=B6=E6=95=B0=E5=AD=97=E5=91=98?= =?UTF-8?q?=E5=B7=A5=E7=AE=A1=E7=90=86=E7=AB=AF=E8=AE=B0=E5=BF=86=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E5=80=99=E9=80=89=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DigitalEmployeeAdminWorkbenchRowDto.java | 13 +++++++++ ...talEmployeeSyncApplicationServiceImpl.java | 27 ++++++++++++++++++- ...mployeeSyncApplicationServiceImplTest.java | 5 +++- .../Content/DigitalEmployeeOps/index.tsx | 15 ++++++++++- qiming/src/types/interfaces/systemManage.ts | 6 +++++ .../scripts/check-digital-employee.js | 8 ++++++ ...ital-employee-frontend-integration-plan.md | 2 +- 7 files changed, 72 insertions(+), 4 deletions(-) diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java index f277ad83..91970abd 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Data; import java.util.Date; +import java.util.List; import java.util.Map; @Data @@ -100,6 +101,18 @@ public class DigitalEmployeeAdminWorkbenchRowDto { private String memoryId; @JsonProperty("memory_key") private String memoryKey; + @JsonProperty("governance_status") + private String governanceStatus; + @JsonProperty("merge_candidate_count") + private Long mergeCandidateCount; + @JsonProperty("duplicate_memory_ids") + private List duplicateMemoryIds; + @JsonProperty("latest_memory_id") + private String latestMemoryId; + @JsonProperty("last_governance_action") + private String lastGovernanceAction; + @JsonProperty("last_reviewed_at") + private String lastReviewedAt; @JsonProperty("skill_id") private String skillId; @JsonProperty("skill_version") diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java index 82e8e93f..1cf6cc3e 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java @@ -46,6 +46,7 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.LinkedHashMap; @@ -1092,6 +1093,12 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye .autoRecoveryStatus(firstString(businessView.get("auto_recovery_status"), businessView.get("autoRecoveryStatus"), payload.get("auto_recovery_status"), payload.get("autoRecoveryStatus"))) .memoryId(firstString(businessView.get("memory_id"), businessView.get("memoryId"), payload.get("memory_id"), payload.get("memoryId"), "memory".equals(record.getEntityType()) ? record.getEntityId() : null)) .memoryKey(firstString(businessView.get("memory_key"), businessView.get("memoryKey"), businessView.get("key"), payload.get("memory_key"), payload.get("memoryKey"), payload.get("key"))) + .governanceStatus(firstString(businessView.get("governance_status"), businessView.get("governanceStatus"), payload.get("governance_status"), payload.get("governanceStatus"))) + .mergeCandidateCount(numberValue(firstPresent(businessView.get("merge_candidate_count"), businessView.get("mergeCandidateCount"), payload.get("merge_candidate_count"), payload.get("mergeCandidateCount")))) + .duplicateMemoryIds(stringListValue(firstPresent(businessView.get("duplicate_memory_ids"), businessView.get("duplicateMemoryIds"), payload.get("duplicate_memory_ids"), payload.get("duplicateMemoryIds")))) + .latestMemoryId(firstString(businessView.get("latest_memory_id"), businessView.get("latestMemoryId"), payload.get("latest_memory_id"), payload.get("latestMemoryId"))) + .lastGovernanceAction(firstString(businessView.get("last_governance_action"), businessView.get("lastGovernanceAction"), payload.get("last_governance_action"), payload.get("lastGovernanceAction"))) + .lastReviewedAt(firstString(businessView.get("last_reviewed_at"), businessView.get("lastReviewedAt"), payload.get("last_reviewed_at"), payload.get("lastReviewedAt"))) .skillId(firstString(businessView.get("skill_id"), businessView.get("skillId"), payload.get("skill_id"), payload.get("skillId"))) .skillVersion(firstString(businessView.get("skill_version"), businessView.get("skillVersion"), businessView.get("version"), payload.get("skill_version"), payload.get("skillVersion"), payload.get("version"))) .installStatus(firstString(businessView.get("install_status"), businessView.get("installStatus"), payload.get("install_status"), payload.get("installStatus"))) @@ -1233,7 +1240,10 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye || StringUtils.equals(category, "memory_governance") || startsWithAny(kind, "memory_") || StringUtils.isNotBlank(row.getMemoryId()) - || StringUtils.isNotBlank(row.getMemoryKey()); + || StringUtils.isNotBlank(row.getMemoryKey()) + || StringUtils.isNotBlank(row.getLatestMemoryId()) + || StringUtils.isNotBlank(row.getGovernanceStatus()) + || row.getMergeCandidateCount() != null; } private boolean isSkillAuditRow(DigitalEmployeeAdminWorkbenchRowDto row) { @@ -1607,6 +1617,21 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye return null; } + private List stringListValue(Object value) { + if (value instanceof List list) { + return list.stream() + .map(item -> stringValue(item, null)) + .filter(StringUtils::isNotBlank) + .toList(); + } + String text = stringValue(value, null); + if (StringUtils.isBlank(text)) return List.of(); + return Arrays.stream(text.split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .toList(); + } + private String stringValue(Object value, String fallback) { if (value == null) return fallback; String text = Objects.toString(value, "").trim(); diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java index 397ad7ba..fbe21c43 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java @@ -395,8 +395,11 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(0).getView()).isEqualTo("memory_governance"); assertThat(result.getRecords().get(0).getMemoryId()).isEqualTo("memory-1"); assertThat(result.getRecords().get(0).getMemoryKey()).isEqualTo("customer.preference"); - assertThat(result.getRecords().get(0).getBusinessView()).containsEntry("merge_candidate_count", 2); + assertThat(result.getRecords().get(0).getGovernanceStatus()).isEqualTo("active"); + assertThat(result.getRecords().get(0).getMergeCandidateCount()).isEqualTo(2L); + assertThat(result.getRecords().get(0).getDuplicateMemoryIds()).containsExactly("memory-2"); assertThat(result.getRecords().get(1).getCategory()).isEqualTo("memory_governance"); + assertThat(result.getRecords().get(1).getLastGovernanceAction()).isEqualTo("archive"); } @Test diff --git a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx index b5445fc8..3fb3e4b4 100644 --- a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx +++ b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx @@ -127,6 +127,8 @@ function renderSummary(record: DigitalEmployeeAdminWorkbenchRow) { record.subscription_policy_id && `Subscription ${record.subscription_policy_id}`, record.daily_report_fact_id && `ReportFact ${record.daily_report_fact_id}`, record.memory_id && `Memory ${record.memory_id}`, + record.merge_candidate_count != null && `Merge ${record.merge_candidate_count}`, + record.governance_status && `Governance ${record.governance_status}`, record.skill_id && `Skill ${record.skill_id}`, record.skill_version && `Version ${record.skill_version}`, record.install_status && `Install ${record.install_status}`, @@ -838,6 +840,8 @@ const DigitalEmployeeOps: React.FC = () => { detailRecord.policy_kind && `Policy ${detailRecord.policy_kind}`, detailRecord.memory_id && `Memory ${detailRecord.memory_id}`, detailRecord.memory_key && `MemoryKey ${detailRecord.memory_key}`, + detailRecord.latest_memory_id && + `LatestMemory ${detailRecord.latest_memory_id}`, detailRecord.skill_id && `Skill ${detailRecord.skill_id}`, detailRecord.skill_version && `Version ${detailRecord.skill_version}`, detailRecord.policy_source && `PolicySource ${detailRecord.policy_source}`, @@ -972,9 +976,18 @@ const DigitalEmployeeOps: React.FC = () => { 记忆治理: {detailRecord.memory_key || detailRecord.memory_id || detailRecord.entity_id || '--'} {detailRecord.status && ` · ${detailRecord.status}`} + {detailRecord.governance_status && ` · 治理状态 ${detailRecord.governance_status}`} + {detailRecord.merge_candidate_count != null && + ` · 合并候选 ${detailRecord.merge_candidate_count}`} + {detailRecord.duplicate_memory_ids?.length + ? ` · 重复记忆 ${detailRecord.duplicate_memory_ids.join('/')}` + : ''} + {detailRecord.last_governance_action && + ` · 最近治理 ${detailRecord.last_governance_action}`} + {detailRecord.last_reviewed_at && ` · 复核 ${detailRecord.last_reviewed_at}`} {' '} - · 展示重复候选与治理状态,批量归档/批量恢复只记录低风险意图。 + · 展示重复候选、重复记忆和治理状态,批量归档/批量恢复只记录低风险意图,不自动合并记忆。 )} diff --git a/qiming/src/types/interfaces/systemManage.ts b/qiming/src/types/interfaces/systemManage.ts index 9a1eca6f..98850fb6 100644 --- a/qiming/src/types/interfaces/systemManage.ts +++ b/qiming/src/types/interfaces/systemManage.ts @@ -627,6 +627,12 @@ export interface DigitalEmployeeAdminWorkbenchRow { auto_recovery_status?: string; memory_id?: string; memory_key?: string; + governance_status?: string; + merge_candidate_count?: number; + duplicate_memory_ids?: string[]; + latest_memory_id?: string; + last_governance_action?: string; + last_reviewed_at?: string; skill_id?: string; skill_version?: string; install_status?: string; diff --git a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js index 57ffbbab..447a29f5 100644 --- a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js +++ b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js @@ -1584,6 +1584,9 @@ function checkDigitalAdminMemorySkillWorkbench() { [service, "token_cost", "backend token cost classifier"], [dto, "memory_id", "backend memory id row field"], [dto, "memory_key", "backend memory key row field"], + [dto, "governance_status", "backend memory governance status row field"], + [dto, "merge_candidate_count", "backend memory merge candidate count row field"], + [dto, "duplicate_memory_ids", "backend duplicate memory ids row field"], [dto, "skill_id", "backend skill id row field"], [dto, "skill_version", "backend skill version row field"], [dto, "install_status", "backend skill install status row field"], @@ -1592,9 +1595,12 @@ function checkDigitalAdminMemorySkillWorkbench() { [dto, "tool_name", "backend tool name row field"], [dto, "server_id", "backend server id row field"], [test, "queryAdminWorkbenchMemoryGovernanceReturnsMemoryRowsOnly", "backend memory governance test"], + [test, "getMergeCandidateCount", "backend memory merge candidate assertion"], [test, "queryAdminWorkbenchSkillAuditsReturnsSkillAndToolAuditRowsOnly", "backend skill audits test"], [test, "queryAdminWorkbenchSkillAuditsReturnsVersionPermissionAndPolicyRowsOnly", "backend skill version permission test"], [qimingTypes, "memory_governance", "qiming memory governance view type"], + [qimingTypes, "merge_candidate_count", "qiming memory merge candidate count field"], + [qimingTypes, "duplicate_memory_ids", "qiming duplicate memory ids field"], [qimingTypes, "skill_audits", "qiming skill audits view type"], [qimingTypes, "skill_version", "qiming skill version field"], [qimingTypes, "permission_scope", "qiming skill permission scope field"], @@ -1605,7 +1611,9 @@ function checkDigitalAdminMemorySkillWorkbench() { [qimingOps, "批量归档", "qiming memory archive action"], [qimingOps, "批量恢复", "qiming memory restore action"], [qimingOps, "重复候选", "qiming memory duplicate copy"], + [qimingOps, "不自动合并记忆", "qiming memory merge safety copy"], [docs, "正式管理端记忆治理预览", "docs memory governance absorption"], + [docs, "正式管理端记忆合并候选预览", "docs memory merge candidate absorption"], [docs, "正式管理端技能审计预览", "docs skill audit absorption"], [docs, "正式管理端技能版本权限预览", "docs skill version permission absorption"], ]; diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index 2c27ae47..f52fa70f 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -562,7 +562,7 @@ GET /api/digital-employee/approvals 3. **MemoryEntry / 长期记忆(已开始)** - 已吸收:qimingclaw `memoryService` 已投射为数字员工本地 `digital_memories`,支持从主客户端记忆列表同步、桥接新增和软删除;本地兜底记忆仍可在 memory 服务未初始化时工作。 - - 当前能力:embedded `/api/memory` 已由 qimingclaw adapter 接管,GET / POST / DELETE 会通过 preload bridge 读写 `digital_memories`;记忆新增、更新和删除会进入 `digital_sync_outbox`,entity type 为 `memory`,同步业务视图会提炼 key、category、source、status 和 content preview。记忆归档/恢复 UI、管理端长期记忆业务视图和记忆治理审计链已开始吸收,embedded 新增“记忆治理”入口,可从 runtime memories 派生 key/category/source/status/content preview、重复候选和同步状态,并通过本地 overlay/audit 批量归档、恢复或标记复核。正式管理端记忆治理预览已开始吸收,qiming-backend 新增 `memory_governance` 运营台视图聚合 memory fact、归档/恢复/复核事件和重复候选摘要,qiming“数字员工运营台”的“记忆治理”tab 可记录批量归档、批量恢复和标记复核等低风险动作意图。 + - 当前能力:embedded `/api/memory` 已由 qimingclaw adapter 接管,GET / POST / DELETE 会通过 preload bridge 读写 `digital_memories`;记忆新增、更新和删除会进入 `digital_sync_outbox`,entity type 为 `memory`,同步业务视图会提炼 key、category、source、status 和 content preview。记忆归档/恢复 UI、管理端长期记忆业务视图和记忆治理审计链已开始吸收,embedded 新增“记忆治理”入口,可从 runtime memories 派生 key/category/source/status/content preview、重复候选和同步状态,并通过本地 overlay/audit 批量归档、恢复或标记复核。正式管理端记忆治理预览已开始吸收,qiming-backend 新增 `memory_governance` 运营台视图聚合 memory fact、归档/恢复/复核事件和重复候选摘要,qiming“数字员工运营台”的“记忆治理”tab 可记录批量归档、批量恢复和标记复核等低风险动作意图。正式管理端记忆合并候选预览已开始吸收,运营台详情可展示治理状态、合并候选数、重复记忆 ID、最近治理动作和复核时间,但不自动合并记忆。 - 后续缺口:真实合并执行、远端记忆策略下发和更完整的外部管理端长期记忆策略工作台仍待吸收。 - 建议:下一轮围绕管理端视图和记忆治理,把长期记忆从本地事实继续扩展到人工可审计、可归档、可合并的运营闭环。