autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
342 lines (341 loc) • 15.4 kB
JavaScript
/**
* Presets — 命名的 Agent 配置组合
*
* 核心思想: Agent 不分"类型",只有"配置"。
* Preset 是 Capability + Strategy + Policy 的命名组合。
*
* 这是统一架构的最终体现:
*
* | 使用场景 | Capabilities | Strategy | Policies |
* |------------------|--------------------------|--------------|---------------------|
* | chat | Conv + Analysis | Single | StandardBudget |
* | insight | Analysis + Production | FanOut+Pipe | DeepBudget+Quality |
* | remote-exec | Conv + Analysis + System | Single | ShortBudget+Safety |
*
* 注意:
* - "飞书聊天" 用 chat preset,不需要单独的 Agent
* - "飞书远程执行" 用 remote-exec preset,Safety 由 Policy 提供
* - "冷启动" 和 "扫描" 统一使用 insight preset,仅编排层不同
*
* @module presets
*/
// v3.0: 导入 Insight 领域函数 (domain/ 模块)
import { ANALYST_BUDGET, ANALYST_SYSTEM_PROMPT, buildAnalystPrompt, } from './domain/insight-analyst.js';
import { buildEvolverPrompt, EVOLVER_BUDGET, EVOLVER_SYSTEM_PROMPT, } from './domain/insight-evolver.js';
import { buildRetryPrompt, evolutionGateEvaluator, insightGateEvaluator, } from './domain/insight-gate.js';
import { buildProducerPromptV2, PRODUCER_BUDGET, PRODUCER_SYSTEM_PROMPT, producerRejectionGateEvaluator, } from './domain/insight-producer.js';
import { PipelineStrategy } from './PipelineStrategy.js';
import { BudgetPolicy, QualityGatePolicy, SafetyPolicy } from './policies.js';
import { AdaptiveStrategy, FanOutStrategy, SingleStrategy } from './strategies.js';
// ─── Preset 定义 ──────────────────────────────
/** 所有内置 Preset */
export const PRESETS = Object.freeze({
// ─── chat: 通用对话 ──────────────────────
chat: {
name: '对话',
description: '多轮对话、知识检索、代码问答。适用于 Dashboard 和飞书的常规对话。',
capabilities: ['conversation', 'code_analysis'],
strategy: { type: 'single' },
policies: [
(config) => new BudgetPolicy({
maxIterations: config?.maxIterations ?? 8,
maxTokens: config?.maxTokens ?? 4096,
temperature: config?.temperature ?? 0.7,
timeoutMs: config?.timeoutMs ?? 120_000,
}),
],
persona: {
role: 'assistant',
description: 'AutoSnippet 知识管理助手',
},
memory: {
enabled: true,
mode: 'user',
tiers: ['working', 'episodic', 'semantic'],
},
},
// ─── insight: 深度代码分析 + 知识产出 ────────
//
// v3.0 重设计: PipelineStrategy 增强版
// - 每个 stage 有 systemPrompt + promptBuilder (替代通用 Capability prompt)
// - Quality Gate 使用自定义 evaluator (三态: pass/retry/degrade)
// - Rejection Gate 监控 Producer 拒绝率
// - promptBuilder 通过 strategyContext 获取运行时数据 (dimConfig/sessionStore/...)
//
// orchestrator 通过 createRuntime('insight', { strategy: {...} }) 按需覆盖
// onToolCall 由 orchestrator 按维度注入 (闭包引用 ActiveContext)
insight: {
name: '洞察',
description: '深度代码分析 + 知识提取。增强 PipelineStrategy: Analyze→QualityGate→Produce→RejectionGate。',
capabilities: ['code_analysis', 'knowledge_production'],
strategy: {
type: 'pipeline',
maxRetries: 1,
stages: [
// ── Phase 1: Analyst ──
{
name: 'analyze',
capabilities: ['code_analysis'],
budget: {
maxIterations: ANALYST_BUDGET.maxIterations,
temperature: 0.4,
timeoutMs: 300_000,
},
systemPrompt: ANALYST_SYSTEM_PROMPT,
promptBuilder: (ctx) => buildAnalystPrompt(ctx.dimConfig, ctx.projectInfo, ctx.dimContext, ctx.sessionStore, ctx.semanticMemory, ctx.codeEntityGraph, ctx.rescanContext, ctx.panorama, ctx.evidenceStarters, ctx.gateArtifact),
retryPromptBuilder: (retryCtx, _origPrompt, prev) => {
const prevAnalysis = prev.analyze?.reply || '';
const retryHint = buildRetryPrompt(retryCtx.reason ?? '');
return `${prevAnalysis}\n\n⚠️ 上述分析未通过质量检查: ${retryCtx.reason}\n\n${retryHint}`;
},
// onToolCall: 由 orchestrator 按维度注入
},
// ── Phase 2: Quality Gate ──
{
name: 'quality_gate',
gate: {
evaluator: insightGateEvaluator,
maxRetries: 1,
},
},
// ── Phase 3: Producer ──
{
name: 'produce',
capabilities: ['knowledge_production'],
// 透传完整 PRODUCER_BUDGET (searchBudget/maxSubmits/softSubmitLimit/idleRoundsToExit)
// 供 ExplorationTracker 精确控制 PRODUCE→SUMMARIZE 转换时机
budget: { ...PRODUCER_BUDGET, temperature: 0.3, timeoutMs: 180_000 },
systemPrompt: PRODUCER_SYSTEM_PROMPT,
promptBuilder: (ctx) => buildProducerPromptV2(ctx.gateArtifact, // 来自 quality_gate 的 AnalysisArtifact
ctx.dimConfig, ctx.projectInfo, ctx.rescanContext, ctx.panorama),
// 拒绝率过高时: 缩减预算 + 特定修复 prompt (对齐旧 ProducerAgent 的 rejection retry)
retryBudget: { maxIterations: 5, temperature: 0.3, timeoutMs: 120_000 },
retryPromptBuilder: (retryCtx, _origPrompt, prev) => {
const prevProduce = prev.produce;
const submitCalls = (prevProduce?.toolCalls || []).filter((tc) => ['submit_knowledge', 'submit_with_check'].includes((tc.tool || tc.name)));
const rejected = submitCalls.filter((tc) => {
const res = tc.result;
if (!res) {
return false;
}
if (typeof res === 'string') {
return res.includes('rejected') || res.includes('error');
}
return (res.status === 'rejected' ||
res.status === 'error' ||
res.reason === 'validation_failed');
}).length;
return `你的 ${rejected} 个提交被拒绝了。请根据拒绝原因改进后重新提交,确保:
1. content 必须是对象: { markdown: "...", rationale: "...", pattern: "..." }
2. content.markdown 字段 ≥ 200 字符,含代码块 (\`\`\`)
3. content.rationale 必填 — 设计原理说明(为什么这样设计)
4. 包含来源标注 (来源: FileName.m:行号)
5. 标题使用项目真实类名,不以项目名开头
6. 必填: trigger (@kebab-case)、kind (rule/pattern/fact)、doClause (英文祈使句)`;
},
skipOnDegrade: true,
},
// ── Phase 4: Rejection Gate ──
{
name: 'rejection_gate',
gate: {
evaluator: producerRejectionGateEvaluator,
maxRetries: 1,
},
skipOnDegrade: true,
},
],
},
policies: [
(config) => new BudgetPolicy({
maxIterations: config?.maxIterations ?? 24,
maxTokens: config?.maxTokens ?? 4096,
temperature: config?.temperature ?? 0.3,
timeoutMs: config?.timeoutMs ?? 3_600_000,
}),
(config) => new QualityGatePolicy({
minEvidenceLength: config?.minEvidenceLength ?? 500,
minFileRefs: config?.minFileRefs ?? 3,
minToolCalls: config?.minToolCalls ?? 3,
}),
],
persona: {
role: 'analyst',
description: '高级软件架构师 + 知识管理专家',
},
memory: {
enabled: false, // 无状态 worker
},
},
// ─── evolution: 衰退 Recipe 进化决策 ─────────
evolution: {
name: '进化',
description: '审查衰退 Recipe,决定进化(supersede)、废弃或跳过。Evolve→EvolutionGate。',
capabilities: ['evolution_analysis'],
strategy: {
type: 'pipeline',
maxRetries: 1,
stages: [
// ── Phase 1: Evolver ──
{
name: 'evolve',
capabilities: ['evolution_analysis'],
budget: {
...EVOLVER_BUDGET,
temperature: 0.3,
timeoutMs: 180_000,
},
systemPrompt: EVOLVER_SYSTEM_PROMPT,
promptBuilder: (ctx) => buildEvolverPrompt(null, null, ctx),
},
// ── Phase 2: Evolution Gate ──
{
name: 'evolution_gate',
gate: {
evaluator: evolutionGateEvaluator,
maxRetries: 1,
},
},
],
},
policies: [
(config) => new BudgetPolicy({
maxIterations: config?.maxIterations ?? 16,
maxTokens: config?.maxTokens ?? 4096,
temperature: config?.temperature ?? 0.3,
timeoutMs: config?.timeoutMs ?? 180_000,
}),
],
persona: {
role: 'analyst',
description: '知识进化专家',
},
memory: {
enabled: false,
},
},
// ─── lark: 飞书知识管理对话 ─────────────
lark: {
name: '飞书对话',
description: '通过飞书自然语言进行知识管理、代码分析、项目理解。服务端直接处理,不转发 IDE。',
capabilities: ['conversation', 'code_analysis'],
strategy: { type: 'single' },
policies: [
(config) => new BudgetPolicy({
maxIterations: config?.maxIterations ?? 12,
maxTokens: config?.maxTokens ?? 4096,
temperature: config?.temperature ?? 0.7,
timeoutMs: config?.timeoutMs ?? 180_000,
}),
() => new SafetyPolicy({
allowedSenders: process.env.ASD_LARK_ALLOWED_USERS?.split(',').filter(Boolean) || [],
}),
],
persona: {
role: 'assistant',
description: 'AutoSnippet 知识管理助手 (飞书)。用中文回复,简洁专业。',
},
memory: {
enabled: true,
mode: 'user',
tiers: ['working', 'episodic', 'semantic'],
},
},
// ─── remote-exec: 远程执行 ──────────────
'remote-exec': {
name: '远程执行',
description: '通过飞书/远程终端执行本地操作。搭配 SafetyPolicy 保障安全。',
capabilities: ['conversation', 'code_analysis', 'system_interaction'],
strategy: { type: 'single' },
policies: [
(config) => new BudgetPolicy({
maxIterations: config?.maxIterations ?? 6,
maxTokens: config?.maxTokens ?? 2048,
temperature: config?.temperature ?? 0.5,
timeoutMs: config?.timeoutMs ?? 60_000,
}),
() => new SafetyPolicy({
allowedSenders: process.env.ASD_LARK_ALLOWED_USERS?.split(',').filter(Boolean) || [],
fileScope: process.env.ASD_PROJECT_ROOT,
}),
],
persona: {
role: 'assistant',
description: 'AutoSnippet 远程编程助手',
},
memory: {
enabled: true,
mode: 'user',
tiers: ['working', 'episodic'],
},
},
});
// ─── Preset 解析器 ────────────────────────────
/**
* 将 Preset 配置中的 strategy 声明式配置转换为实际 Strategy 实例
*
* @param strategyConfig { type: 'single'|'pipeline'|'fan_out'|'adaptive', ...opts }
*/
export function resolveStrategy(strategyConfig) {
if (!strategyConfig) {
return new SingleStrategy();
}
switch (strategyConfig.type) {
case 'single':
return new SingleStrategy();
case 'pipeline':
return new PipelineStrategy({
stages: strategyConfig.stages || [],
maxRetries: strategyConfig.maxRetries,
});
case 'fan_out': {
const itemStrategy = strategyConfig.itemStrategy
? resolveStrategy(strategyConfig.itemStrategy)
: new SingleStrategy();
return new FanOutStrategy({
itemStrategy,
tiers: strategyConfig.tiers,
merge: strategyConfig.merge,
});
}
case 'adaptive':
return new AdaptiveStrategy({
single: strategyConfig.single ? resolveStrategy(strategyConfig.single) : undefined,
pipeline: strategyConfig.pipeline ? resolveStrategy(strategyConfig.pipeline) : undefined,
fanOut: strategyConfig.fanOut ? resolveStrategy(strategyConfig.fanOut) : undefined,
});
default:
throw new Error(`Unknown strategy type: ${strategyConfig.type}`);
}
}
/**
* 获取 Preset 并展开为可用配置
*
* @param [overrides] 覆盖 preset 中的特定字段
* @returns }
*/
export function getPreset(presetName, overrides = {}) {
const preset = PRESETS[presetName];
if (!preset) {
throw new Error(`Unknown preset: "${presetName}". Available: ${Object.keys(PRESETS).join(', ')}`);
}
const merged = {
...preset,
...overrides,
capabilities: overrides.capabilities || preset.capabilities,
policies: overrides.policies || preset.policies,
persona: {
...preset.persona,
...overrides.persona,
},
memory: {
...preset.memory,
...overrides.memory,
},
};
// 解析 strategy
const strategyConfig = (overrides.strategy || preset.strategy);
merged.strategyInstance = resolveStrategy(strategyConfig);
return merged;
}
export default { PRESETS, resolveStrategy, getPreset };