Архитектура: ввести answer-inspection follow-up contract и закрыть phase15 replay
This commit is contained in:
@@ -255,4 +255,38 @@ describe("assistant living chat runtime adapter", () => {
|
||||
expect(output.debug?.living_chat_continuity_active_organization).toBe("ООО Альтернатива Плюс");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
it("builds deterministic answer inspection reply over grounded selected-object sale trace", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
userMessage: "у тебя написано кто контрагент: рабочая станция - это ошибка?",
|
||||
modeDecision: { mode: "chat", reason: "answer_inspection_followup_detected" },
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "inventory_sale_trace_for_item",
|
||||
extracted_filters: {
|
||||
item: "Рабочая станция универсального специалиста",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
as_of_date: "2016-03-31"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
executeLlmChat
|
||||
});
|
||||
|
||||
const output = await runAssistantLivingChatRuntime(input);
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText).toContain("не контрагент");
|
||||
expect(output.chatText).toContain("Рабочая станция универсального специалиста");
|
||||
expect(output.chatText).toContain("Покупатель");
|
||||
expect(output.debug?.living_chat_response_source).toBe("deterministic_answer_inspection_contract");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,6 +50,12 @@ describe("assistantLivingModePolicy", () => {
|
||||
expect(policy.hasConversationMemoryRecallFollowupSignal("а что мы уже выяснили по этой позиции?")).toBe(true);
|
||||
});
|
||||
|
||||
it("detects answer inspection wording for previous answer correction", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
expect(policy.hasAnswerInspectionFollowupSignal("у тебя написано кто контрагент: рабочая станция - это ошибка?")).toBe(true);
|
||||
});
|
||||
|
||||
it("routes casual small-talk to chat mode", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@ import { createAssistantMetaFollowupPolicy } from "../src/services/assistantMeta
|
||||
|
||||
const policy = createAssistantMetaFollowupPolicy({
|
||||
hasAssistantDataScopeMetaQuestionSignal: (text: unknown) =>
|
||||
/по какой компании|какая база/i.test(String(text ?? "")),
|
||||
/по какой компании|какая база/i.test(String(text ?? "")),
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: (text: unknown) =>
|
||||
/что ты можешь|что ты умеешь/i.test(String(text ?? "")),
|
||||
/что ты можешь|что ты умеешь/i.test(String(text ?? "")),
|
||||
hasMetaAnswerFollowupSignal: (text: unknown) =>
|
||||
/это норм|что думаешь/i.test(String(text ?? ""))
|
||||
/это норм|что думаешь/i.test(String(text ?? "")),
|
||||
hasAnswerInspectionFollowupSignal: (text: unknown) =>
|
||||
/это ошибка|у тебя написано кто контрагент/i.test(String(text ?? ""))
|
||||
});
|
||||
|
||||
describe("assistantMetaFollowupPolicy", () => {
|
||||
@@ -15,13 +17,14 @@ describe("assistantMetaFollowupPolicy", () => {
|
||||
const signals = policy.resolveMetaSignalSet({
|
||||
rawUserMessage: "",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "по какой компании мы можем работать?",
|
||||
effectiveAddressUserMessage: "по какой компании мы можем работать?",
|
||||
repairedEffectiveAddressUserMessage: ""
|
||||
});
|
||||
|
||||
expect(signals.dataScopeMetaQuery).toBe(true);
|
||||
expect(signals.capabilityMetaQuery).toBe(false);
|
||||
expect(signals.metaAnswerFollowupSignal).toBe(false);
|
||||
expect(signals.answerInspectionFollowupSignal).toBe(false);
|
||||
});
|
||||
|
||||
it("treats historical capability phrasing as capability meta follow-up", () => {
|
||||
@@ -72,4 +75,26 @@ describe("assistantMetaFollowupPolicy", () => {
|
||||
|
||||
expect(detected).toBe(true);
|
||||
});
|
||||
|
||||
it("detects answer inspection follow-up over grounded answer", () => {
|
||||
const signals = policy.resolveMetaSignalSet({
|
||||
rawUserMessage: "у тебя написано кто контрагент: рабочая станция - это ошибка?",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "",
|
||||
repairedEffectiveAddressUserMessage: ""
|
||||
});
|
||||
|
||||
expect(signals.answerInspectionFollowupSignal).toBe(true);
|
||||
|
||||
const detected = policy.isAnswerInspectionFollowupOverGroundedAnswer({
|
||||
followupContext: { previous_intent: "inventory_sale_trace_for_item", previous_anchor_type: "item" },
|
||||
hasPriorAddressAnswerContext: true,
|
||||
answerInspectionFollowupSignal: true,
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: false,
|
||||
aggregateBusinessAnalyticsSignal: false
|
||||
});
|
||||
|
||||
expect(detected).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,7 +47,8 @@ function buildPolicy(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
dataScopeMetaQuery: /по какой компании|какая база|по каким конторам/i.test(samples),
|
||||
capabilityMetaQuery: /что ты можешь|что ты умеешь/i.test(samples),
|
||||
metaAnswerFollowupSignal: /это норм|что думаешь/i.test(samples)
|
||||
metaAnswerFollowupSignal: /это норм|что думаешь/i.test(samples),
|
||||
answerInspectionFollowupSignal: /это ошибка|у тебя написано кто контрагент/i.test(samples)
|
||||
};
|
||||
},
|
||||
resolveHardMetaMode: (input: {
|
||||
@@ -61,6 +62,7 @@ function buildPolicy(overrides: Record<string, unknown> = {}) {
|
||||
? "capability"
|
||||
: null,
|
||||
isMetaFollowupOverGroundedAnswer: () => false,
|
||||
isAnswerInspectionFollowupOverGroundedAnswer: () => false,
|
||||
hasDataRetrievalRequestSignal: () => false,
|
||||
hasOrganizationFactLookupSignal: () => false,
|
||||
hasOrganizationFactFollowupSignal: () => false,
|
||||
@@ -236,6 +238,32 @@ describe("assistantRoutePolicy", () => {
|
||||
expect(decision.livingReason).toBe("memory_recap_followup_detected");
|
||||
});
|
||||
|
||||
it("routes answer inspection follow-up over grounded selected-object answer to chat", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastGroundedAddressAnswerDebug: () => ({ execution_lane: "address_query" }),
|
||||
resolveAddressToolGateDecision: () => ({
|
||||
runAddressLane: true,
|
||||
decision: "run_address_lane",
|
||||
reason: "address_mode_classifier_detected"
|
||||
}),
|
||||
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
|
||||
isAnswerInspectionFollowupOverGroundedAnswer: () => true
|
||||
});
|
||||
|
||||
const decision = policy.resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage: "у тебя написано кто контрагент: рабочая станция - это ошибка?",
|
||||
effectiveAddressUserMessage: "у тебя написано кто контрагент: рабочая станция - это ошибка?",
|
||||
followupContext: { previous_intent: "inventory_sale_trace_for_item", previous_anchor_type: "item" },
|
||||
llmPreDecomposeMeta: null,
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(false);
|
||||
expect(decision.toolGateReason).toBe("answer_inspection_followup_over_grounded_answer");
|
||||
expect(decision.livingMode).toBe("chat");
|
||||
expect(decision.livingReason).toBe("answer_inspection_followup_detected");
|
||||
});
|
||||
|
||||
it("routes organization fact lookup away from address lane even with follow-up context", () => {
|
||||
const policy = buildPolicy({
|
||||
hasDataRetrievalRequestSignal: () => true,
|
||||
|
||||
Reference in New Issue
Block a user