feat: 添加 skill learning 技能学习闭环系统

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
unraid
2026-04-22 22:38:09 +08:00
parent 04c7ed4250
commit 1837df5f88
64 changed files with 11009 additions and 36 deletions

View File

@@ -0,0 +1,26 @@
import { afterEach, describe, expect, test } from 'bun:test'
import { isSkillImprovementEnabled } from '../skillImprovement.js'
const originalEnv = { ...process.env }
afterEach(() => {
process.env = { ...originalEnv }
})
describe('skillImprovement', () => {
test('is enabled when skill learning is enabled', () => {
process.env = { ...originalEnv }
process.env.SKILL_LEARNING_ENABLED = '1'
delete process.env.SKILL_IMPROVEMENT_ENABLED
expect(isSkillImprovementEnabled()).toBe(true)
})
test('explicit skill improvement opt-out wins', () => {
process.env = { ...originalEnv }
process.env.SKILL_LEARNING_ENABLED = '1'
process.env.SKILL_IMPROVEMENT_ENABLED = '0'
expect(isSkillImprovementEnabled()).toBe(false)
})
})

View File

@@ -7,7 +7,11 @@ import {
logEvent,
} from '../../services/analytics/index.js'
import { queryModelWithoutStreaming } from '../../services/api/claude.js'
import { createTrace, endTrace, isLangfuseEnabled } from '../../services/langfuse/index.js'
import {
createTrace,
endTrace,
isLangfuseEnabled,
} from '../../services/langfuse/index.js'
import { getSessionId } from '../../bootstrap/state.js'
import { getAPIProvider } from '../model/providers.js'
import { getEmptyToolPermissionContext } from '../../Tool.js'
@@ -31,6 +35,16 @@ import {
} from './apiQueryHookHelper.js'
import { registerPostSamplingHook } from './postSamplingHooks.js'
export function isSkillImprovementEnabled(): boolean {
const explicit = process.env.SKILL_IMPROVEMENT_ENABLED
if (explicit === '0' || explicit === 'false') return false
if (explicit === '1' || explicit === 'true') return true
return (
process.env.SKILL_LEARNING_ENABLED === '1' ||
process.env.SKILL_LEARNING_ENABLED === 'true'
)
}
const TURN_BATCH_SIZE = 5
export type SkillUpdate = {
@@ -265,7 +279,9 @@ Rules:
endTrace(langfuseTrace)
const responseText = extractTextContent(Array.isArray(response.message.content) ? response.message.content : []).trim()
const responseText = extractTextContent(
Array.isArray(response.message.content) ? response.message.content : [],
).trim()
const updatedContent = extractTag(responseText, 'updated_file')
if (!updatedContent) {