ARCH: добавить answer adapter MCP discovery
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION = void 0;
|
||||
exports.buildAssistantMcpDiscoveryAnswerDraft = buildAssistantMcpDiscoveryAnswerDraft;
|
||||
exports.ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION = "assistant_mcp_discovery_answer_draft_v1";
|
||||
function normalizeReasonCode(value) {
|
||||
const normalized = value
|
||||
.trim()
|
||||
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
|
||||
.replace(/^_+|_+$/g, "")
|
||||
.toLowerCase();
|
||||
return normalized.length > 0 ? normalized.slice(0, 120) : null;
|
||||
}
|
||||
function pushReason(target, value) {
|
||||
const normalized = normalizeReasonCode(value);
|
||||
if (normalized && !target.includes(normalized)) {
|
||||
target.push(normalized);
|
||||
}
|
||||
}
|
||||
function uniqueStrings(values) {
|
||||
const result = [];
|
||||
for (const value of values) {
|
||||
const text = String(value ?? "").trim();
|
||||
if (text && !result.includes(text)) {
|
||||
result.push(text);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function isInternalMechanicsLine(value) {
|
||||
const text = value.toLowerCase();
|
||||
return (text.includes("primitive") ||
|
||||
text.includes("query_documents") ||
|
||||
text.includes("query_movements") ||
|
||||
text.includes("resolve_entity_reference") ||
|
||||
text.includes("probe_coverage") ||
|
||||
text.includes("explain_evidence_basis") ||
|
||||
text.includes("pilot_only_executes") ||
|
||||
text.includes("runtime_") ||
|
||||
text.includes("planner_") ||
|
||||
text.includes("catalog_"));
|
||||
}
|
||||
function userFacingLimitations(values) {
|
||||
return uniqueStrings(values).filter((value) => !isInternalMechanicsLine(value));
|
||||
}
|
||||
function modeFor(pilot) {
|
||||
if (pilot.pilot_status === "blocked") {
|
||||
return "blocked";
|
||||
}
|
||||
if (pilot.pilot_status === "skipped_needs_clarification") {
|
||||
return "needs_clarification";
|
||||
}
|
||||
if (pilot.evidence.answer_permission === "confirmed_answer") {
|
||||
return "confirmed_with_bounded_inference";
|
||||
}
|
||||
if (pilot.evidence.answer_permission === "bounded_inference") {
|
||||
return "bounded_inference_only";
|
||||
}
|
||||
return "checked_sources_only";
|
||||
}
|
||||
function headlineFor(mode) {
|
||||
if (mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С есть подтвержденная активность; длительность можно оценивать только как вывод из этих строк.";
|
||||
}
|
||||
if (mode === "bounded_inference_only") {
|
||||
return "Точный факт не подтвержден, но есть ограниченная оценка по найденной активности в 1С.";
|
||||
}
|
||||
if (mode === "needs_clarification") {
|
||||
return "Нужно уточнить контекст перед поиском в 1С.";
|
||||
}
|
||||
if (mode === "blocked") {
|
||||
return "Поиск в 1С заблокирован runtime-политикой до выполнения.";
|
||||
}
|
||||
return "Я проверил доступный контур, но подтвержденного факта для ответа не получил.";
|
||||
}
|
||||
function nextStepFor(mode, pilot) {
|
||||
if (mode === "needs_clarification") {
|
||||
return "Уточните контрагента, период или организацию, и я смогу выполнить проверку по 1С.";
|
||||
}
|
||||
if (mode === "checked_sources_only" && pilot.query_limitations.length > 0) {
|
||||
return "Можно повторить проверку после восстановления MCP-доступа или сузить вопрос до конкретного контрагента/периода.";
|
||||
}
|
||||
if (mode === "blocked") {
|
||||
return "Нужно сначала снять policy/blocking причину, иначе данные 1С использовать нельзя.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function buildMustNotClaim(pilot) {
|
||||
const claims = [
|
||||
"Do not claim legal registration age unless a legal registration source is confirmed.",
|
||||
"Do not present inferred activity duration as a formally confirmed legal fact.",
|
||||
"Do not expose MCP primitive names, query text, debug ids, or internal execution mechanics in the user answer.",
|
||||
"Do not claim rows were checked when mcp_execution_performed=false."
|
||||
];
|
||||
if (pilot.evidence.confirmed_facts.length === 0) {
|
||||
claims.push("Do not claim a confirmed business fact when confirmed_facts is empty.");
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
|
||||
const mode = modeFor(pilot);
|
||||
const reasonCodes = [...pilot.reason_codes, ...pilot.evidence.reason_codes];
|
||||
pushReason(reasonCodes, `answer_mode_${mode}`);
|
||||
if (pilot.evidence.unknown_facts.length > 0) {
|
||||
pushReason(reasonCodes, "answer_contains_unknown_fact_boundary");
|
||||
}
|
||||
if (pilot.evidence.inferred_facts.length > 0) {
|
||||
pushReason(reasonCodes, "answer_contains_bounded_inference");
|
||||
}
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryAnswerAdapter",
|
||||
answer_mode: mode,
|
||||
headline: headlineFor(mode),
|
||||
confirmed_lines: uniqueStrings(pilot.evidence.confirmed_facts),
|
||||
inference_lines: uniqueStrings(pilot.evidence.inferred_facts),
|
||||
unknown_lines: uniqueStrings(pilot.evidence.unknown_facts),
|
||||
limitation_lines: userFacingLimitations([...pilot.query_limitations, ...pilot.evidence.query_limitations]),
|
||||
next_step_line: nextStepFor(mode, pilot),
|
||||
internal_mechanics_allowed: false,
|
||||
must_not_claim: buildMustNotClaim(pilot),
|
||||
reason_codes: uniqueStrings(reasonCodes)
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user