吸收数字员工入口路由观测

This commit is contained in:
baiyanyun
2026-06-10 11:43:48 +08:00
parent d12db03216
commit 914ed81f19
11 changed files with 388 additions and 194 deletions

View File

@@ -592,6 +592,19 @@ describe("digital employee state service", () => {
contextRefs: { plan_id: "plan-1" },
recordedAt: "2026-06-07T08:02:00.000Z",
});
const attention = recordDigitalEmployeeRouteDecision({
idempotencyKey: "route-key-attention",
correlationId: "corr-attention",
routeKind: "PlanExecution",
intentKind: "execute",
reasonCodes: ["candidate_skills_disabled", "route_action_missing_context"],
candidateSkills: ["qimingclaw-computer-control"],
policyResult: "accepted",
entrypoint: "webhook",
actor: "management-api",
envelope: { text: "执行但缺少 task_id" },
recordedAt: "2026-06-07T08:03:00.000Z",
});
expect(first).toMatchObject({
id: "route-decision:route-key-1",
@@ -615,12 +628,23 @@ describe("digital employee state service", () => {
},
envelope: { text: "开始执行这个步骤" },
});
expect(JSON.parse(mockState.db?.events.get(attention.id)?.payload ?? "{}")).toMatchObject({
routeDecision: {
id: attention.id,
reason_codes: ["candidate_skills_disabled", "route_action_missing_context"],
candidate_skills: ["qimingclaw-computer-control"],
entrypoint: "webhook",
actor: "management-api",
},
envelope: { text: "执行但缺少 task_id" },
});
expect(mockState.db?.outbox.get(`event:insert:${first.id}`)).toMatchObject({
entity_type: "event",
operation: "insert",
status: "pending",
});
expect(listDigitalEmployeeRouteDecisions()).toEqual([
expect.objectContaining({ id: attention.id, reason_codes: ["candidate_skills_disabled", "route_action_missing_context"] }),
expect.objectContaining({ id: second.id, route_kind: "ProjectionQuery" }),
expect.objectContaining({ id: first.id, route_kind: "PlanExecution", created_command_id: "qimingclaw-digital-start_run-1" }),
]);

View File

@@ -1630,8 +1630,8 @@ function routeDecisionFromEventPayload(payload: unknown): DigitalEmployeeRouteDe
id,
route_kind: stringValue(decision.route_kind ?? decision.routeKind) || "ChatOnly",
intent_kind: stringValue(decision.intent_kind ?? decision.intentKind) || "chat",
reason_codes: parseStringArray(decision.reason_codes ?? decision.reasonCodes),
candidate_skills: parseStringArray(decision.candidate_skills ?? decision.candidateSkills),
reason_codes: parseRouteDecisionStringArray(decision.reason_codes ?? decision.reasonCodes),
candidate_skills: parseRouteDecisionStringArray(decision.candidate_skills ?? decision.candidateSkills),
context_refs: objectRecord(decision.context_refs ?? decision.contextRefs) ?? {},
created_command_id: stringValue(decision.created_command_id ?? decision.createdCommandId) || null,
correlation_id: stringValue(decision.correlation_id ?? decision.correlationId) || id,
@@ -1645,6 +1645,11 @@ function routeDecisionFromEventPayload(payload: unknown): DigitalEmployeeRouteDe
};
}
function parseRouteDecisionStringArray(value: unknown): string[] {
if (Array.isArray(value)) return normalizeSkillIds(value);
return parseStringArray(value);
}
function routeDecisionMessage(decision: DigitalEmployeeRouteDecisionRecord): string {
return `入口路由决策:${decision.route_kind} / ${decision.intent_kind}`;
}