1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-18 19:40:33 +08:00
parent 54b3d3c490
commit 8c3fbd9bcf
15 changed files with 904 additions and 65 deletions

View File

@@ -47,6 +47,75 @@ function readNumber(value: unknown, fallback = 0) {
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
}
function countKeywordMatches(text: string, keywords: string[]) {
return keywords.reduce(
(count, keyword) => (text.includes(keyword) ? count + 1 : count),
0,
);
}
function clampAffinityDelta(value: number) {
return Math.max(-3, Math.min(3, value));
}
function computeNpcChatAffinityDelta(params: {
playerMessage: string;
npcReply: string;
chattedCount: number;
}) {
const playerMessage = params.playerMessage.trim();
const npcReply = params.npcReply.trim();
const positiveKeywords = [
'谢谢',
'辛苦',
'抱歉',
'理解',
'相信',
'放心',
'一起',
'帮你',
'在意',
'关心',
];
const negativeKeywords = [
'闭嘴',
'滚',
'少废话',
'威胁',
'骗',
'不信',
'别装',
'快说',
'审问',
'怀疑',
];
const warmReplyKeywords = ['可以', '愿意', '放心', '谢谢', '明白', '好'];
const coldReplyKeywords = ['没必要', '不想', '别问', '与你无关', '算了', '住口'];
const positiveScore =
countKeywordMatches(playerMessage, positiveKeywords) +
countKeywordMatches(npcReply, warmReplyKeywords);
const negativeScore =
countKeywordMatches(playerMessage, negativeKeywords) +
countKeywordMatches(npcReply, coldReplyKeywords);
if (positiveScore === 0 && negativeScore === 0) {
return params.chattedCount === 0 ? 1 : 0;
}
if (positiveScore > negativeScore) {
const baseDelta =
positiveScore - negativeScore + (params.chattedCount <= 1 ? 1 : 0);
return clampAffinityDelta(baseDelta);
}
if (negativeScore > positiveScore) {
return clampAffinityDelta(positiveScore - negativeScore);
}
return 0;
}
function describeAffinityShift(affinityDelta: number) {
if (affinityDelta >= 8) return '态度明显软化了下来。';
if (affinityDelta >= 5) return '态度比刚才亲近了一些。';
@@ -153,7 +222,11 @@ export async function streamNpcChatTurnFromOrchestrator(
const suggestions = parseLineListContent(suggestionText, 3);
const npcState = readRecord(params.payload.npcState);
const chattedCount = readNumber(npcState?.chattedCount, 0);
const affinityDelta = Math.max(2, 6 - chattedCount);
const affinityDelta = computeNpcChatAffinityDelta({
playerMessage: params.payload.playerMessage,
npcReply: npcReply || streamedReply,
chattedCount,
});
writeSseEvent(params.response, 'complete', {
npcReply: npcReply || streamedReply,

View File

@@ -353,7 +353,7 @@ export function buildCharacterPanelChatSummaryPrompt(
}
function buildNpcDialoguePromptBase(
payload: NpcChatDialogueRequest | NpcRecruitDialogueRequest,
payload: NpcChatDialogueRequest | NpcChatTurnRequest | NpcRecruitDialogueRequest,
) {
const encounter = describeEncounter(payload.encounter);