Уточнить честность широкого бизнес-обзора ассистента

This commit is contained in:
dctouch 2026-05-26 00:05:28 +03:00
parent baa8aa31e7
commit 92473c894f
4 changed files with 118 additions and 20 deletions

View File

@ -0,0 +1,96 @@
{
"schema_version": "domain_truth_harness_spec_v1",
"scenario_id": "hm_business_profile_evaluation_limits_20260525",
"domain": "assistant_stage1_business_overview",
"title": "Hm business profile evaluation coverage honesty",
"description": "Focused live replay for broad company evaluation: the assistant must give a useful business reading while clearly presenting cashflow probes as a limited checked 1C slice, not a full audit, clean profit, or complete accounting turnover.",
"bindings": {},
"steps": [
{
"step_id": "s01_top_year_establish_company",
"title": "Establish company context through top-year question",
"question": "какой у ООО \"Альтернатива Плюс\" самый доходный год",
"allowed_reply_types": [
"factual",
"factual_with_explanation",
"partial_coverage"
],
"required_direct_answer_patterns_any": [
"2015",
"входящ"
],
"forbidden_answer_patterns": [
"Коротко:",
"capability_id",
"mcp_discovery"
],
"criticality": "critical",
"semantic_tags": [
"business_overview",
"company_context",
"yearly_ranking"
]
},
{
"step_id": "s02_broad_company_evaluation",
"title": "Broad company evaluation must lead with limited-evidence framing",
"question": "Как ты оценишь деятельность компании?",
"allowed_reply_types": [
"factual_with_explanation",
"partial_coverage"
],
"required_answer_patterns_all": [
"ограниченн",
"денежн",
"не аудиторское",
"не подтвержден",
"чист",
"прибыл",
"Комитет государственных услуг",
"зависим"
],
"forbidden_answer_patterns": [
"Коротко:",
"capability_id",
"mcp_discovery",
"query_movements",
"business_overview_route_template_v1"
],
"criticality": "critical",
"semantic_tags": [
"business_overview",
"broad_business_evaluation",
"coverage_honesty",
"profit_boundary"
]
},
{
"step_id": "s03_profitability_boundary",
"title": "Profitability follow-up must not convert net cashflow into profit",
"question": "то есть бизнес прибыльный или это только обороты?",
"allowed_reply_types": [
"factual_with_explanation",
"partial_coverage"
],
"required_answer_patterns_all": [
"не подтверждаю",
"прибыл",
"денежн",
"нетто",
"не полн"
],
"forbidden_answer_patterns": [
"Коротко:",
"capability_id",
"mcp_discovery",
"это прибыль"
],
"criticality": "critical",
"semantic_tags": [
"business_overview",
"profitability_boundary",
"cashflow_not_profit"
]
}
]
}

View File

@ -959,11 +959,7 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
: "Это учетный финрезультат по найденным строкам закрытия периода в 1С, а не внешний аудит и не юридически подтвержденная отчетность.");
return joinBusinessReplyLines(lines);
}
const headline = toNonEmptyString(draft.headline);
const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim();
lines.push(cleanHeadline
? `Коротко: ${localizeLine(cleanHeadline)}`
: "Коротко: нельзя точно подтвердить чистую прибыль и маржу по текущему срезу 1С; есть только ограниченный операционный денежный/товарный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат.");
lines.push("Коротко: чистую прибыль и маржу не подтверждаю по текущему срезу 1С; видны только ограниченные денежные обороты и расчетное нетто/операционный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат.");
const boundaryLines = userFacingLines([
...toStringList(draft.confirmed_lines),
...toStringList(draft.inference_lines),
@ -1113,8 +1109,8 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
(actionFamily === "broad_evaluation" || unsupportedFamily === "broad_business_evaluation")) {
const subject = organizationScope ?? "компания";
const periodWithoutPrefix = period.replace(/^за\s+/iu, "");
lines.push(`Коротко: по доступным данным ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж.`);
lines.push("Что видно:");
lines.push(`Коротко: по ограниченному проверенному 1С-срезу ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж; это не аудиторское заключение и не подтвержденная чистая прибыль.`);
lines.push("Что видно в ограниченном денежном срезе:");
if (incomingAmount) {
lines.push(`- входящие деньги за ${periodWithoutPrefix}: ${incomingAmount};`);
}
@ -1146,7 +1142,9 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
if (debtLine) {
lines.push(`- ${localizeLine(debtLine)}`);
}
lines.push("Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль.");
if (!limitLine) {
lines.push("Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль.");
}
const missingOverviewFamilies = [];
if (!taxLine) {
missingOverviewFamilies.push("НДС/налоговая позиция без отдельного точного расчета");

View File

@ -1167,12 +1167,8 @@ function buildCompactBusinessOverviewReply(
);
return joinBusinessReplyLines(lines);
}
const headline = toNonEmptyString(draft.headline);
const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim();
lines.push(
cleanHeadline
? `Коротко: ${localizeLine(cleanHeadline)}`
: "Коротко: нельзя точно подтвердить чистую прибыль и маржу по текущему срезу 1С; есть только ограниченный операционный денежный/товарный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат."
"Коротко: чистую прибыль и маржу не подтверждаю по текущему срезу 1С; видны только ограниченные денежные обороты и расчетное нетто/операционный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат."
);
const boundaryLines = userFacingLines([
...toStringList(draft.confirmed_lines),
@ -1366,9 +1362,9 @@ function buildCompactBusinessOverviewReply(
const subject = organizationScope ?? "компания";
const periodWithoutPrefix = period.replace(/^за\s+/iu, "");
lines.push(
`Коротко: по доступным данным ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж.`
`Коротко: по ограниченному проверенному 1С-срезу ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж; это не аудиторское заключение и не подтвержденная чистая прибыль.`
);
lines.push("Что видно:");
lines.push("Что видно в ограниченном денежном срезе:");
if (incomingAmount) {
lines.push(`- входящие деньги за ${periodWithoutPrefix}: ${incomingAmount};`);
}
@ -1404,9 +1400,11 @@ function buildCompactBusinessOverviewReply(
if (debtLine) {
lines.push(`- ${localizeLine(debtLine)}`);
}
lines.push(
"Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль."
);
if (!limitLine) {
lines.push(
"Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль."
);
}
const missingOverviewFamilies: string[] = [];
if (!taxLine) {
missingOverviewFamilies.push("НДС/налоговая позиция без отдельного точного расчета");

View File

@ -202,7 +202,8 @@ describe("assistant MCP discovery response candidate", () => {
})
);
expect(candidate.reply_text).toContain("нельзя точно подтвердить чистую прибыль");
expect(candidate.reply_text).toContain("чистую прибыль и маржу не подтверждаю");
expect(candidate.reply_text).toContain("расчетное нетто");
expect(candidate.reply_text).toContain("полный отчет о прибылях и убытках");
expect(candidate.reply_text).toContain("себестоимости");
expect(candidate.reply_text).not.toContain("47 628 853");
@ -1003,7 +1004,7 @@ describe("assistant MCP discovery response candidate", () => {
})
);
expect(candidate.reply_text).toContain("Что видно:");
expect(candidate.reply_text).toContain("Что видно в ограниченном денежном срезе:");
expect(candidate.reply_text).toContain("НДС:");
expect(candidate.reply_text).toContain("Долги:");
expect(candidate.reply_text).toContain("Склад:");
@ -1425,6 +1426,11 @@ describe("assistant MCP discovery response candidate", () => {
})
);
const firstLine = candidate.reply_text?.split("\n")[0] ?? "";
expect(firstLine).toContain("по ограниченному проверенному 1С-срезу");
expect(firstLine).toContain("не аудиторское заключение");
expect(firstLine).toContain("не подтвержденная чистая прибыль");
expect(candidate.reply_text).toContain("Что видно в ограниченном денежном срезе");
expect(candidate.reply_text).toContain("Что не подтверждено в этом срезе");
expect(candidate.reply_text).toContain("НДС");
expect(candidate.reply_text).toContain("долги");