Доказать полезность margin-agent через честный boundary replay
This commit is contained in:
@@ -2167,7 +2167,11 @@ function hasNomenclatureMarginRankingSignal(text: string): boolean {
|
||||
/(?:высок|низк|топ|сам(?:ая|ый|ое|ые|ой|ого|ому|ым|ых|ую)|больш|меньш|ранж|рейтинг|max|min|high|low|top|rank|best|worst)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasNomenclatureCue && hasMarginCue && hasRankingCue;
|
||||
const hasCalculationCue =
|
||||
/(?:посчита\p{L}*|рассчита\p{L}*|расч[её]т\p{L}*|расчита\p{L}*|понять|calculate|compute)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasNomenclatureCue && hasMarginCue && (hasRankingCue || hasCalculationCue);
|
||||
}
|
||||
|
||||
function hasVatPeriodInspectionBridgeSignal(text: string): boolean {
|
||||
|
||||
@@ -53,7 +53,11 @@ function hasInventoryMarginRankingSignal(text: string): boolean {
|
||||
/(?:высок|низк|топ|сам(?:ая|ый|ое|ые|ой|ого|ому|ым|ых|ую)|больш|меньш|ранж|рейтинг|max|min|high|low|top|rank|best|worst)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasNomenclatureCue && hasMarginCue && hasRankingCue;
|
||||
const hasCalculationCue =
|
||||
/(?:посчита\p{L}*|рассчита\p{L}*|расч[её]т\p{L}*|расчита\p{L}*|понять|calculate|compute)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasNomenclatureCue && hasMarginCue && (hasRankingCue || hasCalculationCue);
|
||||
}
|
||||
|
||||
function hasInventoryOnHandSignal(text: string): boolean {
|
||||
|
||||
@@ -22,6 +22,11 @@ const ADDRESS_ACTION_TOKENS = [
|
||||
"показ",
|
||||
"проверь",
|
||||
"провер",
|
||||
"посчитай",
|
||||
"посчитать",
|
||||
"рассчитай",
|
||||
"рассчитать",
|
||||
"понять",
|
||||
"чекни",
|
||||
"чекн",
|
||||
"глянь",
|
||||
@@ -108,6 +113,10 @@ const ADDRESS_ENTITY_TOKENS = [
|
||||
"чек",
|
||||
"доход",
|
||||
"выруч",
|
||||
"прибыл",
|
||||
"марж",
|
||||
"рентаб",
|
||||
"себестоим",
|
||||
"сделк",
|
||||
"бюджет",
|
||||
"топ",
|
||||
@@ -427,6 +436,15 @@ export function detectAddressQuestionMode(userMessage: string): AddressModeDetec
|
||||
const hasFollowupSignal = hasAddressFollowupSignal(text);
|
||||
const hasSelectedObjectInventoryFollowup = hasSelectedObjectInventoryFollowupSignal(text);
|
||||
const hasAccountCode = hasAccountCodeAnchor(text);
|
||||
const hasInventoryProfitabilitySignal = hasInventoryProfitabilityCue(text);
|
||||
|
||||
if (hasInventoryProfitabilitySignal && hasAddressEntity && !hasDeepReasoning) {
|
||||
return {
|
||||
mode: "address_query",
|
||||
confidence: "high",
|
||||
reasons: ["inventory_profitability_signal_detected", "address_entity_detected"]
|
||||
};
|
||||
}
|
||||
|
||||
if (hasAddressAction && (hasAddressEntity || hasAccountCode) && !hasDeepReasoning) {
|
||||
return {
|
||||
|
||||
@@ -179,6 +179,15 @@ function asksForInventoryMarginBasis(userMessage: string | null | undefined): bo
|
||||
);
|
||||
}
|
||||
|
||||
function asksInventoryMarginFromPaymentOrBank(userMessage: string | null | undefined): boolean {
|
||||
const text = String(userMessage ?? "").toLowerCase();
|
||||
return (
|
||||
/(?:марж|прибыл|рентаб|profit|margin)/iu.test(text) &&
|
||||
/(?:товар|номенклатур|inventory|item|sku)/iu.test(text) &&
|
||||
/(?:банк|банковск|выписк|плат[её]ж|оплат|payment|bank|statement)/iu.test(text)
|
||||
);
|
||||
}
|
||||
|
||||
interface InventoryMarginRankingEntry {
|
||||
item: string;
|
||||
revenue: number;
|
||||
@@ -649,6 +658,26 @@ export function composeInventoryReply(
|
||||
const totalSpread = totalRevenue - totalCostProxy;
|
||||
const topMarginEntry = highMargin[0] ?? null;
|
||||
const marginBasisRequested = asksForInventoryMarginBasis(options.userMessage);
|
||||
const paymentOrBankFalseSourceRequested = asksInventoryMarginFromPaymentOrBank(options.userMessage);
|
||||
if (paymentOrBankFalseSourceRequested) {
|
||||
const lines = [
|
||||
"По оплатам и банку такой показатель нельзя честно подтвердить: платежи показывают денежный поток и факт оплаты, а не связь реализации с себестоимостью по номенклатуре."
|
||||
];
|
||||
appendInventoryBulletSection(lines, "Корректная база для маржинальности:", [
|
||||
"выручка реализации по номенклатуре;",
|
||||
"себестоимостная база реализации по той же номенклатуре;",
|
||||
"валовая разница и процент валовой маржи."
|
||||
]);
|
||||
appendInventoryBulletSection(lines, "Что можно сделать дальше:", [
|
||||
`посчитать управленческий рейтинг по выручке и себестоимостной базе за период ${periodLabel};`,
|
||||
"отдельно сверить оплаты и банк как денежный поток, но не использовать их как расчетную базу."
|
||||
]);
|
||||
appendInventoryBulletSection(lines, "Граница ответа:", [
|
||||
"оплаты могут помочь сверить поступление денег, но сами по себе не подтверждают валовую прибыль по товарам;",
|
||||
"строгий бухгалтерский расчет требует проводок реализации и себестоимости, а не только банковских движений."
|
||||
]);
|
||||
return buildFactualSummaryReply(lines, buildConfirmedBalanceSemantics("medium", false));
|
||||
}
|
||||
if (confirmedEntries.length === 0) {
|
||||
const costBaseRowsRequested = asksForInventoryCostBaseRows(options.userMessage);
|
||||
const lines: string[] = [
|
||||
|
||||
Reference in New Issue
Block a user