Skip to content

Commit 100fb02

Browse files
committed
refactor: architecture deepening — 6 candidates implemented
1. IntentPostProcessor: extract intent post-processing into strategy registry (architecture dedup, symbol_lookup diversity) 2. FusionStrategy: extract fusion logic from HybridRecallEngine into SmartFusionStrategy + rrfFusion module. symbol_lookup short-circuit now lives in the strategy, not the engine. 3. executeAssembleContext split: 861-line god function → assembleContextStrategy.ts (data collection) + assembleContextFormatter.ts (JSON/text formatting) + thin 56-line orchestrator 4. memoryMatcher: extract pure ranking functions from resultCard.ts (rankFeatureMemoryMatches, rankDecisionMatches, rankFeedbackMatches, rankLongTermMemoryMatches) into independently testable module 5. MemoryStoreProvider: optional instance cache for MCP/CLI sessions 6. CONTEXT.md: domain vocabulary for future architecture reviews All 551 existing tests pass. Zero regressions.
1 parent 256578e commit 100fb02

12 files changed

Lines changed: 1863 additions & 962 deletions

CONTEXT.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# ContextAtlas Domain Vocabulary
2+
3+
## Core Pipeline
4+
5+
- **Recall** — 混合召回:向量 + 词法 + skeleton + graph 四路并发召回原始候选
6+
- **Rerank** — 精排:对召回候选做 cross-encoder 重排
7+
- **Expand** — 上下文扩展:从 seed chunks 补充邻居、面包屑、跨文件引用
8+
- **Pack** — 上下文打包:合并 chunks → 按文件聚合段落 → Token 预算裁剪
9+
10+
## Memory
11+
12+
- **FeatureMemory** — 模块记忆:记录模块职责、API、数据流
13+
- **DecisionRecord** — 决策记录:记录架构决策
14+
- **LongTermMemory** — 长期记忆:无法从代码推导的事实(用户偏好、纠正、项目状态)
15+
- **MemoryCatalog** — 路由索引:轻量索引,用于按需加载模块记忆
16+
- **MemoryRouter** — 渐进式记忆路由器:catalog → global → feature 三层加载
17+
- **MemoryStore** — 项目记忆 facade:协调 feature/project-meta/decision/long-term 四类子存储
18+
- **MemorySession** — 已完成初始化的记忆会话(候选概念,待实现)
19+
20+
## Retrieval
21+
22+
- **ContextPack** — 检索输出包:seeds + expanded + files + debug info
23+
- **ResultCard** — 检索结果卡片:排名 + 上下文块 + 记忆匹配 + 格式化
24+
- **QueryIntent** — 查询意图:balanced / architecture / symbol_lookup
25+
- **LexicalStrategy** — 词法策略:chunks_fts / files_fts / none
26+
27+
## Assembly
28+
29+
- **AssemblyProfile** — 装配配置:overview / debug / implementation / verification / handoff
30+
- **ContextBlock** — 上下文块:类型化、优先级、来源追踪的结构化上下文单元
31+
- **WakeupLayers** — 唤醒层:分层展示上下文块,从关键到辅助
32+
- **Checkpoint** — 任务检查点:保存/恢复 Agent 工作状态
33+
34+
## Infrastructure
35+
36+
- **MemoryStoreProvider** — 记忆存储提供者:统一创建/缓存 MemoryStore 实例(候选概念)
37+
- **IntentPostProcessor** — 意图后处理器:按 QueryIntent 应用不同的后处理策略(候选概念)
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
/**
2+
* AssembleContext 格式化层
3+
*
4+
* 将 AssembledData 格式化为 JSON payload 或文本输出。
5+
* 不包含任何业务逻辑或数据源调用。
6+
*/
7+
8+
import type {
9+
ContextBlock,
10+
ContextBlockReference,
11+
FeatureMemory,
12+
TaskCheckpoint,
13+
} from '../../memory/types.js';
14+
import type { AssemblyProfileName } from '../../memory/MemoryRouter.js';
15+
import { formatWakeupLayersText, type WakeupLayersBundle } from './wakeupLayers.js';
16+
import type {
17+
AssembleContextPhase,
18+
AssembleContextSource,
19+
AssembledData,
20+
CheckpointPayload,
21+
CodebaseRetrievalPayload,
22+
ModuleMemoryPayload,
23+
} from './assembleContextStrategy.js';
24+
25+
// ===========================================
26+
// JSON Payload 类型
27+
// ===========================================
28+
29+
export interface AssembledContextJsonPayload {
30+
tool: 'assemble_context';
31+
repo_path: string;
32+
input: {
33+
phase?: AssembleContextPhase;
34+
profile?: AssemblyProfileName;
35+
query?: string;
36+
moduleName?: string;
37+
filePaths?: string[];
38+
checkpoint_id?: string;
39+
includeDiary?: boolean;
40+
agentName?: string;
41+
diaryTopic?: string;
42+
diaryLimit?: number;
43+
format: 'json';
44+
};
45+
assemblyProfile: {
46+
requestedPhase?: AssembleContextPhase;
47+
resolvedProfile: AssemblyProfileName;
48+
source: AssembleContextSource;
49+
};
50+
routing: {
51+
checkpoint: {
52+
checkpointId?: string;
53+
phase?: AssembleContextPhase;
54+
loaded: boolean;
55+
};
56+
moduleMemory: ModuleMemoryPayload['routing'] | null;
57+
codebaseRetrieval: {
58+
informationRequest: string;
59+
technicalTerms: string[];
60+
responseMode: 'expanded';
61+
summary: CodebaseRetrievalPayload['summary'] | null;
62+
architecturePrimaryFiles: string[];
63+
nextInspectionSuggestions: string[];
64+
} | null;
65+
};
66+
budget: {
67+
moduleMemory: {
68+
candidateCount: number;
69+
selectedCount: number;
70+
selectedModules: string[];
71+
maxResults: number;
72+
selectionStrategy: 'mmr' | 'ranked';
73+
routeStrategy: string;
74+
} | null;
75+
codebaseRetrieval: {
76+
codeBlocks: number;
77+
files: number;
78+
totalSegments: number;
79+
} | null;
80+
selectedContextBlocks: number;
81+
};
82+
selectedContext: {
83+
checkpoint: CheckpointPayload | null;
84+
moduleMemories: FeatureMemory[];
85+
codebaseRetrieval: CodebaseRetrievalPayload | null;
86+
contextBlocks: ContextBlock[];
87+
summary: {
88+
checkpointBlocks: number;
89+
diaryBlocks: number;
90+
moduleMemoryBlocks: number;
91+
codeBlocks: number;
92+
totalBlocks: number;
93+
references: number;
94+
};
95+
};
96+
references: ContextBlockReference[];
97+
wakeupLayers: WakeupLayersBundle;
98+
source: {
99+
checkpoint: null | {
100+
tool: 'load_checkpoint';
101+
checkpointId: string;
102+
phase: AssembleContextPhase;
103+
};
104+
moduleMemory: null | {
105+
tool: 'load_module_memory';
106+
assembly: ModuleMemoryPayload['assembly'];
107+
resultCount: number;
108+
};
109+
codebaseRetrieval: null | {
110+
tool: 'codebase-retrieval';
111+
responseMode: 'expanded';
112+
summary: CodebaseRetrievalPayload['summary'];
113+
architecturePrimaryFiles: string[];
114+
};
115+
diary: null | {
116+
tool: 'record_agent_diary';
117+
resultCount: number;
118+
agentName?: string;
119+
topic?: string;
120+
};
121+
};
122+
}
123+
124+
// ===========================================
125+
// 格式化函数
126+
// ===========================================
127+
128+
export function buildAssembledPayload(
129+
args: {
130+
repo_path: string;
131+
phase?: AssembleContextPhase;
132+
profile?: AssemblyProfileName;
133+
query?: string;
134+
moduleName?: string;
135+
filePaths?: string[];
136+
checkpoint_id?: string;
137+
includeDiary?: boolean;
138+
agentName?: string;
139+
diaryTopic?: string;
140+
diaryLimit?: number;
141+
},
142+
data: AssembledData,
143+
): AssembledContextJsonPayload {
144+
const checkpointPayload = data.checkpoint;
145+
const modulePayload = data.moduleMemory;
146+
const codePayload = data.codebaseRetrieval;
147+
148+
return {
149+
tool: 'assemble_context',
150+
repo_path: args.repo_path,
151+
input: {
152+
phase: args.phase,
153+
profile: args.profile,
154+
query: args.query,
155+
moduleName: args.moduleName,
156+
filePaths: args.filePaths,
157+
checkpoint_id: args.checkpoint_id,
158+
includeDiary: args.includeDiary,
159+
agentName: args.agentName,
160+
diaryTopic: args.diaryTopic,
161+
diaryLimit: args.diaryLimit,
162+
format: 'json',
163+
},
164+
assemblyProfile: {
165+
requestedPhase: data.profile.requestedPhase ?? args.phase,
166+
resolvedProfile: data.profile.name,
167+
source: data.profile.source,
168+
},
169+
routing: {
170+
checkpoint: {
171+
checkpointId: checkpointPayload?.checkpoint.id,
172+
phase: checkpointPayload?.checkpoint.phase,
173+
loaded: Boolean(checkpointPayload),
174+
},
175+
moduleMemory: modulePayload?.routing ?? null,
176+
codebaseRetrieval: data.codebaseRetrieval
177+
? {
178+
informationRequest: data.codebaseRequest.information_request,
179+
technicalTerms: data.codebaseRequest.technical_terms,
180+
responseMode: 'expanded',
181+
summary: data.codebaseRetrieval.summary,
182+
architecturePrimaryFiles: data.codebaseRetrieval.architecturePrimaryFiles,
183+
nextInspectionSuggestions: data.codebaseRetrieval.nextInspectionSuggestions,
184+
}
185+
: null,
186+
},
187+
budget: {
188+
moduleMemory: modulePayload?.routing
189+
? {
190+
candidateCount: modulePayload.routing.candidateCount,
191+
selectedCount: modulePayload.routing.selectedCount,
192+
selectedModules: modulePayload.routing.selectedModules,
193+
maxResults: modulePayload.assembly.maxResults,
194+
selectionStrategy: modulePayload.routing.selectionStrategy,
195+
routeStrategy: modulePayload.routing.routeStrategy,
196+
}
197+
: null,
198+
codebaseRetrieval: data.codebaseRetrieval?.summary
199+
? {
200+
codeBlocks: data.codebaseRetrieval.summary.codeBlocks,
201+
files: data.codebaseRetrieval.summary.files,
202+
totalSegments: data.codebaseRetrieval.summary.totalSegments,
203+
}
204+
: null,
205+
selectedContextBlocks: data.selectedBlocks.length,
206+
},
207+
selectedContext: {
208+
checkpoint: checkpointPayload,
209+
moduleMemories: modulePayload?.memories ?? [],
210+
codebaseRetrieval: codePayload,
211+
contextBlocks: data.selectedBlocks,
212+
summary: {
213+
checkpointBlocks: checkpointPayload?.contextBlocks.length ?? 0,
214+
diaryBlocks: data.diaryBlocks.length,
215+
moduleMemoryBlocks: (modulePayload?.memories ?? []).length,
216+
codeBlocks: codePayload?.contextBlocks.length ?? 0,
217+
totalBlocks: data.selectedBlocks.length,
218+
references: data.references.length,
219+
},
220+
},
221+
references: data.references,
222+
wakeupLayers: data.wakeupLayers,
223+
source: {
224+
checkpoint: checkpointPayload
225+
? {
226+
tool: 'load_checkpoint',
227+
checkpointId: checkpointPayload.checkpoint.id,
228+
phase: checkpointPayload.checkpoint.phase,
229+
}
230+
: null,
231+
moduleMemory: modulePayload
232+
? {
233+
tool: 'load_module_memory',
234+
assembly: modulePayload.assembly,
235+
resultCount: modulePayload.result_count,
236+
}
237+
: null,
238+
codebaseRetrieval: codePayload?.summary
239+
? {
240+
tool: 'codebase-retrieval',
241+
responseMode: 'expanded',
242+
summary: codePayload.summary,
243+
architecturePrimaryFiles: codePayload.architecturePrimaryFiles,
244+
}
245+
: null,
246+
diary: data.diaryBlocks.length > 0
247+
? {
248+
tool: 'record_agent_diary',
249+
resultCount: data.diaryBlocks.length,
250+
agentName: args.agentName,
251+
topic: args.diaryTopic,
252+
}
253+
: null,
254+
},
255+
};
256+
}
257+
258+
export function formatAssembleContextText(payload: AssembledContextJsonPayload): string {
259+
const stage = payload.input.phase ?? payload.assemblyProfile.resolvedProfile;
260+
const moduleSummary =
261+
payload.selectedContext.moduleMemories.length > 0
262+
? payload.selectedContext.moduleMemories.map((m) => `- ${m.name}`).join('\n')
263+
: '- None';
264+
const blockSummary =
265+
payload.selectedContext.contextBlocks.length > 0
266+
? payload.selectedContext.contextBlocks.slice(0, 12).map(formatContextBlockText).join('\n\n---\n\n')
267+
: '- None';
268+
const wakeupLayerSummary = formatWakeupLayersText(payload.wakeupLayers);
269+
const referenceSummary =
270+
payload.references.length > 0
271+
? payload.references
272+
.slice(0, 12)
273+
.map((ref) => `- ${ref.source}:${ref.ref} (${ref.blockId})`)
274+
.join('\n')
275+
: '- None';
276+
277+
return [
278+
'## Context Assembly',
279+
`- **Stage**: ${stage}`,
280+
`- **Assembly Profile**: ${payload.assemblyProfile.resolvedProfile}`,
281+
`- **Assembly Source**: ${payload.assemblyProfile.source}`,
282+
'',
283+
'### Routing / Budget',
284+
`- **Module Memory**: ${
285+
payload.budget.moduleMemory
286+
? `${payload.budget.moduleMemory.selectedCount}/${payload.budget.moduleMemory.maxResults}`
287+
: 'None'
288+
}`,
289+
`- **Code Retrieval**: ${payload.routing.codebaseRetrieval ? `${payload.routing.codebaseRetrieval.summary?.codeBlocks ?? 0} blocks` : 'None'}`,
290+
`- **Selected Context Blocks**: ${payload.budget.selectedContextBlocks}`,
291+
'',
292+
'### Selected Context',
293+
`- **Checkpoint**: ${payload.selectedContext.checkpoint ? payload.selectedContext.checkpoint.checkpoint.id : 'None'}`,
294+
`- **Diary Blocks**: ${payload.selectedContext.summary.diaryBlocks}`,
295+
`- **Module Memories**: ${payload.selectedContext.moduleMemories.length}`,
296+
`- **Code Evidence Blocks**: ${payload.selectedContext.codebaseRetrieval?.contextBlocks.length ?? 0}`,
297+
`- **Architecture Primary Files**: ${payload.routing.codebaseRetrieval?.architecturePrimaryFiles.length ?? 0}`,
298+
'',
299+
wakeupLayerSummary,
300+
'',
301+
'### Loaded Module Memories',
302+
moduleSummary,
303+
'',
304+
'### Selected Context Blocks',
305+
blockSummary,
306+
'',
307+
'### References',
308+
referenceSummary,
309+
].join('\n');
310+
}
311+
312+
function formatContextBlockText(block: ContextBlock): string {
313+
const provenance = block.provenance.map((item) => `${item.source}:${item.ref}`).join(', ') || 'None';
314+
const contentLines = block.content
315+
.split('\n')
316+
.map((line) => line.trimEnd())
317+
.filter((line) => line.length > 0);
318+
319+
return [
320+
`### ${block.title || block.id}`,
321+
`- **ID**: ${block.id}`,
322+
`- **Type**: ${block.type}`,
323+
`- **Purpose**: ${block.purpose}`,
324+
'- **Content**:',
325+
...(contentLines.length > 0 ? contentLines.map((line) => ` - ${line}`) : [' - None']),
326+
`- **Provenance**: ${provenance}`,
327+
].join('\n');
328+
}

0 commit comments

Comments
 (0)