fix: 调整Claude Code相似度检测并恢复401处理

This commit is contained in:
shaw
2025-09-24 15:04:39 +08:00
parent b89305ad4d
commit ad443ea18a
12 changed files with 476 additions and 56 deletions

View File

@@ -1,6 +1,6 @@
export const haikuSystemPrompt = `Analyze if this message indicates a new conversation topic. If it does, extract a 2-3 word title that captures the new topic. Format your response as a JSON object with two fields: 'isNewTopic' (boolean) and 'title' (string, or null if isNewTopic is false). Only include these fields, no other text.`
export const claudeOtherSystemPrompt1 = `You are Claude Code, Anthropic's official CLI for Claude.`
export const claudeOtherSystemPrompt2 = `
const haikuSystemPrompt = `Analyze if this message indicates a new conversation topic. If it does, extract a 2-3 word title that captures the new topic. Format your response as a JSON object with two fields: 'isNewTopic' (boolean) and 'title' (string, or null if isNewTopic is false). Only include these fields, no other text.`
const claudeOtherSystemPrompt1 = `You are Claude Code, Anthropic's official CLI for Claude.`
const claudeOtherSystemPrompt2 = `
You are an interactive CLI tool that helps users 'according to your "Output Style" below, which describes how you should respond to user queries.' : 'with software engineering tasks.'} Use the instructions below and the tools available to you to assist the user.
IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files.
@@ -163,3 +163,9 @@ user: Where are errors from the client handled?
assistant: Clients are marked as failed in the \`connectToServer\` function in src/services/process.ts:712.
</example>
`
module.exports = {
haikuSystemPrompt,
claudeOtherSystemPrompt1,
claudeOtherSystemPrompt2
}

View File

@@ -1,18 +1,81 @@
import stringSimilarity from 'string-similarity'
const MAX_TEXT_LENGTH = 4000
function normalize(value) {
return value.replace(/\s+/g, ' ').trim()
return value.replace(/\s+/g, ' ').trim().toLowerCase()
}
export function simple(actual, expected, threshold) {
function clamp(value) {
if (value.length <= MAX_TEXT_LENGTH) {
return value
}
// 截断极长文本,避免耗时的相似度计算
return value.slice(0, MAX_TEXT_LENGTH)
}
function buildBigramStats(text) {
const stats = new Map()
for (let index = 0; index < text.length - 1; index += 1) {
const gram = text[index] + text[index + 1]
stats.set(gram, (stats.get(gram) || 0) + 1)
}
return {
stats,
total: Math.max(text.length - 1, 0)
}
}
function diceCoefficient(left, right) {
if (left === right) {
return 1
}
if (left.length < 2 || right.length < 2) {
return left === right && left.length > 0 ? 1 : 0
}
const { stats: leftStats, total: leftTotal } = buildBigramStats(left)
const { stats: rightStats, total: rightTotal } = buildBigramStats(right)
let intersection = 0
const [smaller, larger] =
leftStats.size <= rightStats.size ? [leftStats, rightStats] : [rightStats, leftStats]
smaller.forEach((count, gram) => {
if (larger.has(gram)) {
intersection += Math.min(count, larger.get(gram))
}
})
if (leftTotal + rightTotal === 0) {
return 0
}
return (2 * intersection) / (leftTotal + rightTotal)
}
function simple(actual, expected, threshold = 0.9) {
if (typeof expected !== 'string' || !expected.trim()) {
throw new Error('Expected prompt text must be a non-empty string')
throw new Error('期望的提示词必须是非空字符串')
}
if (typeof actual !== 'string' || !actual.trim()) {
return { score: 0, threshold, passed: false }
}
const score = stringSimilarity.compareTwoStrings(normalize(actual), normalize(expected))
return { score, threshold, passed: score >= threshold }
const normalizedExpected = clamp(normalize(expected))
const normalizedActual = clamp(normalize(actual))
const score = diceCoefficient(normalizedActual, normalizedExpected)
return {
score,
threshold,
passed: score >= threshold
}
}
module.exports = {
simple
}