Skip to content

Commit 8fc25b3

Browse files
committed
feat(agent-workspace): add deep rag answer profile budget
1 parent c463129 commit 8fc25b3

8 files changed

Lines changed: 201 additions & 5 deletions

docs/diataxis/en/explanation/development-progress-dashboard.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Fresh implementation evidence in this slice:
5353
- The runtime probe `contextoverflow_no_provider_budget_drop_en` now verifies that `what is overflow budget probe?` stays deterministic without an LLM judge and records `fragment_dropped` in the final bounded `RagContextPack`; recovery source-decision counts are preserved in `ragRecovery` when recovery actually occurs.
5454
- The runtime probe `contextoverflow_malformed_provider_judge_fallback_en` now verifies that a local malformed OpenAI-compatible judge response does not block the answer path: the fixture is called once, the first review records `llm_judge_failed` in `ragRecovery.beforeReasons`, and the recovered answer remains deterministic and bounded.
5555
- The runtime probe `contextoverflow_timeout_provider_judge_fallback_en` now verifies that a delayed local OpenAI-compatible judge response times out through the bounded provider-judge path: the fixture is called once, `ragRecovery.beforeReasons` records `llm_judge_failed:RAG sufficiency judge timed out.`, and the final recovered answer remains deterministic and bounded.
56+
- The runtime probe `contextoverflow_deep_profile_budget_en` now verifies explicit deep/explain requests use a wider first-pass RAG answer profile (`24` fragments, `1600` chars per fragment, `9000` total chars) while preserving the bounded `RagContextPack` contract and avoiding recovery-only budget expansion.
5657
- The runtime probe `graphintent_compare_neighbor_selection_en` now verifies that `compare brittle glass vessel with polymer cup material behavior` selects analogy graph successors from `Knowledge_Base/graphintent`, excludes the high-overlap procedural successor from `successorWindow`, keeps `graph_neighbor_support`, includes at least one `full_document` graph-neighbor support fragment, and does not leak the procedural path into the public answer.
5758
- The runtime probe `graphintent_missing_neighbor_source_window_en` now verifies that when selected graph-neighbor source paths are unavailable, direct graph-neighbor spans remain observable but do not satisfy graph evidence by themselves: source decisions include `source_resolver_returned_no_content:direct_support,graph_neighbor_support`, sufficiency stays `borderline/partial_coverage`, and failure classification includes both `parsing_source` and `graph_evidence`.
5859
- The runtime probe `graphintent_multi_neighbor_source_loss_en` now requires two selected graph-neighbor source documents to be unavailable at once and asserts at least two `source_window_unavailable` decisions. The matching assembler test records both missing neighbor documents independently, so multi-document graph evidence loss remains replayable per neighbor.

docs/diataxis/zh/explanation/development-progress-dashboard.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
| 查询与预览链路必须鲁棒且向前兼容 | 读查询和只读 path preview 都不再触发 snapshot 持久化;新增字段、action-strip selector、缓存诊断和方法都保持 additive,耐久写路径保持原有行为。 | 已显著改善 |
122122
| 架构压力应继续下降 | `KnowledgeLearningPlatform.ts` 仍承载 hydrate、retrieval、store snapshot、path preview composition 和 query context assembly 等多个 owner;本次先收紧读写边界,后续应继续拆小持久化、retrieval context 与 preview composition owner。 | 改善但仍需推进 |
123123

124+
- `contextoverflow_deep_profile_budget_en` 现在验证显式 deep / explain 请求会使用更宽的一次 RAG answer profile(`24` 个 fragment、每 fragment `1600` 字符、总计 `9000` 字符),同时继续遵守有界 `RagContextPack` contract,避免只能通过 recovery 预算或无界上下文来获得更充分回答。
124125
- `conflicting_adjacent_evidence_probe_en` 现在由 `Knowledge_Base/ragconflict/calibration tolerance conflict probe.md` 支撑,验证相邻的 `+/-0.10 mm``+/-0.50 mm` calibration-tolerance 证据会进入 `conflict` RAG role,把 sufficiency 降级为 `borderline/conflict`,并归因到 `context_assembly`,而不是发布单一稳定值。
125126
- `conflicting_nonadjacent_section_evidence_probe_en` 现在由 `Knowledge_Base/ragconflict/remote calibration tolerance conflict probe.md` 支撑,验证同一 scoped section 内非相邻的 `+/-0.10 mm``+/-0.50 mm` calibration-tolerance 证据也会进入 `conflict` RAG role,证明完整文档增强不会把远距离同节矛盾压平成单一值。
126127
- `conflicting_release_date_evidence_probe_en` 现在由 `Knowledge_Base/ragdateconflict/release date conflict probe.md` 支撑,验证同一 scoped section 内的 `2026-07-01``2026-08-15` release-date 证据也会进入 `conflict` RAG role,并保持 `borderline/conflict` 降级,而不是发布单一稳定 schedule。

docs/plans/2026-07-05-001-feat-rse-document-augmented-rag-plan.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,11 +441,14 @@ flowchart TB
441441

442442
**Condition-qualified increment (2026-07-06):** `temporal_scoped_state_status_probe_en`, `temporal_scoped_release_date_probe_en`, and `temporal_scoped_planned_release_date_probe_en`, backed by `Knowledge_Base/ragtemporalqualifier`, plus `temporal_cross_document_planned_release_date_probe_en`, backed by `Knowledge_Base/ragtemporalcrossscope`, now verify that explicitly current, historical, and planned/future-qualified state/status or release-date facts remain separate comparable scopes. The public answer can use the current fact, the historical or planned fact stays available as scoped evidence, and the RAG pack must not emit a false `conflict` fragment, including when the current and planned dates are read from separate scoped documents through the full-document cross-document conflict scanner. The release-review path now also keeps generic graph-profile narration such as predecessor/successor window labels out of RAG-grounded public answers unless that content is present as citation-backed RAG evidence. This closes four controlled false-positive classes while leaving broader condition-qualified facts and unconstrained semantic contradiction detection as follow-up work.
443443

444+
**Answer-profile budget increment (2026-07-06):** `KnowledgeLearningPlatform.agentConversation()` now resolves a bounded first-pass RAG answer profile from the user query. Ordinary turns keep the existing base context budget (`14` fragments, `1400` chars per fragment, `5600` total chars), while explicit deep/explain/analyze requests use a wider but still capped profile (`24` fragments, `1600` chars per fragment, `9000` total chars) plus the larger paragraph window and graph-neighbor limit before any recovery pass. The runtime verifier and conversation regression contract can now assert `expectedRagBudget`; `contextoverflow_deep_profile_budget_en`, backed by `Knowledge_Base/contextoverflow`, verifies that deep answer requests expand the evidence budget without switching to an unbounded pack or exposing orchestration details in the public answer.
445+
444446
**Requirements:** R8.
445447

446448
**Dependencies:** Unit 7.
447449

448450
**Files:**
451+
- Modify: `src/learning/KnowledgeLearningPlatform.ts`
449452
- Modify: `src/learning/KnowledgeWorkspaceConversationRegression.ts`
450453
- Modify: `scripts/verify-knowledge-workspace-runtime.js`
451454
- Test: `src/learning/KnowledgeWorkspaceConversationRegression.test.ts`
@@ -906,7 +909,10 @@ flowchart TB
906909

907910
**条件限定增量(2026-07-06):** 新增由 `Knowledge_Base/ragtemporalqualifier` 支撑的 `temporal_scoped_state_status_probe_en`、`temporal_scoped_release_date_probe_en` 与 `temporal_scoped_planned_release_date_probe_en`,以及由 `Knowledge_Base/ragtemporalcrossscope` 支撑的 `temporal_cross_document_planned_release_date_probe_en`,验证显式 current、historical 以及 planned/future-qualified 的 state/status 或 release-date 事实会进入不同 comparable scope。公开回答可以使用 current 事实,historical 或 planned 事实仍保留为 scoped evidence,且 RAG pack 不应误产出 `conflict` fragment;即使 current 与 planned 日期来自两个不同 scoped 文档,并经过完整文档 cross-document conflict scanner,也不应误判为冲突。release-review 路径现在也会把 predecessor / successor window label 这类通用 graph-profile 叙述留在 trace/结构面,除非它们已经作为带引用支撑的 RAG evidence 进入 context pack,否则不进入 RAG-grounded 公开回答。这个切片关闭了四类受控误报,但更广的条件限定事实和无约束语义矛盾检测仍是后续工作。
908911

912+
**回答 profile 预算增量(2026-07-06):** `KnowledgeLearningPlatform.agentConversation()` 现在会根据用户 query 解析有界的一次 RAG answer profile。普通 turn 保持既有 base context budget(`14` 个 fragment、每 fragment `1400` 字符、总计 `5600` 字符);显式 deep / explain / analyze 请求会在 recovery 之前使用更宽但仍有硬上限的 profile(`24` 个 fragment、每 fragment `1600` 字符、总计 `9000` 字符),并同步扩大 paragraph window 与 graph-neighbor limit。runtime verifier 与 conversation regression contract 现在可以断言 `expectedRagBudget`;由 `Knowledge_Base/contextoverflow` 支撑的 `contextoverflow_deep_profile_budget_en` 验证深度回答请求会扩大证据预算,但不会切换成无界 context pack,也不会把编排细节暴露到公开回答。
913+
909914
**文件:**
915+
- 修改:`src/learning/KnowledgeLearningPlatform.ts`
910916
- 修改:`src/learning/KnowledgeWorkspaceConversationRegression.ts`
911917
- 修改:`scripts/verify-knowledge-workspace-runtime.js`
912918
- 测试:`src/learning/KnowledgeWorkspaceConversationRegression.test.ts`

scripts/verify-knowledge-workspace-runtime.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ function validatePositiveConversationResult(summary, options) {
423423
answerMustContain,
424424
answerMustNotContain,
425425
expectedRagSourceBoundary,
426+
expectedRagBudget,
426427
requiredRagRoles,
427428
minimumRagFullDocumentFragmentCounts,
428429
acceptedRagSufficiencyStatuses,
@@ -545,6 +546,21 @@ function validatePositiveConversationResult(summary, options) {
545546
throw new Error(`RAG replay id missing or invalid for query=${query}: actual=${JSON.stringify(summary.ragContextPack)}`);
546547
}
547548
}
549+
if (expectedRagBudget && typeof expectedRagBudget === 'object') {
550+
const observedBudget = summary.ragContextPack && summary.ragContextPack.budget
551+
? summary.ragContextPack.budget
552+
: null;
553+
['maxFragments', 'maxCharsPerFragment', 'maxTotalChars'].forEach((field) => {
554+
if (typeof expectedRagBudget[field] !== 'number') {
555+
return;
556+
}
557+
if (!observedBudget || Number(observedBudget[field]) !== Number(expectedRagBudget[field])) {
558+
throw new Error(
559+
`RAG budget mismatch for query=${query}: field=${field} expected=${expectedRagBudget[field]} actual=${JSON.stringify(observedBudget)}`
560+
);
561+
}
562+
});
563+
}
548564
if (Array.isArray(requiredRagRoles) && requiredRagRoles.length > 0) {
549565
const observedRoles = Array.isArray(summary.ragContextPack && summary.ragContextPack.fragments)
550566
? summary.ragContextPack.fragments.map((fragment) => String(fragment && fragment.role || '')).filter(Boolean)
@@ -1054,6 +1070,7 @@ async function main() {
10541070
answerMustContain: regressionCase.expected.answerMustContain,
10551071
answerMustNotContain: regressionCase.expected.answerMustNotContain,
10561072
expectedRagSourceBoundary: regressionCase.expected.ragSourceBoundary,
1073+
expectedRagBudget: regressionCase.expected.expectedRagBudget,
10571074
requiredRagRoles: regressionCase.expected.requiredRagRoles,
10581075
acceptedRagSufficiencyStatuses: regressionCase.expected.runtimeAcceptedRagSufficiencyStatuses
10591076
|| regressionCase.expected.acceptedRagSufficiencyStatuses,

src/learning/KnowledgeLearningPlatform.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2358,6 +2358,67 @@ describe('KnowledgeLearningPlatform', () => {
23582358
expect(response.answer).toContain('transparent drinking vessel');
23592359
});
23602360

2361+
test('agent conversation widens the first-pass RAG budget only for explicit depth requests', async () => {
2362+
await platform.ingestKnowledge({
2363+
incremental: true,
2364+
documents: [
2365+
{
2366+
documentId: 'doc_answer_profile_budget_probe',
2367+
sourcePath: 'Knowledge_Base/answerprofile/answer profile budget probe.md',
2368+
language: 'en',
2369+
workspaceId: 'answerprofile',
2370+
corpusId: 'answerprofile',
2371+
content: [
2372+
'# Answer Profile Budget Probe',
2373+
'',
2374+
'Answer profile budget probe defines a bounded RAG answer profile for ordinary and explicit deep requests.',
2375+
'',
2376+
'## Detail Layer',
2377+
'',
2378+
'The detail layer keeps full-document source reading available while the model-visible RAG pack remains capped.',
2379+
].join('\n'),
2380+
},
2381+
],
2382+
});
2383+
2384+
const baseResponse = await platform.agentConversation({
2385+
userId: 'agent_rag_profile_user',
2386+
sessionId: 'session_rag_profile_base',
2387+
message: 'what is answer profile budget probe?',
2388+
topK: 3,
2389+
scope: {
2390+
workspaceId: 'answerprofile',
2391+
corpusId: 'answerprofile',
2392+
sourcePathPrefixes: ['Knowledge_Base/answerprofile'],
2393+
},
2394+
persistMemory: false,
2395+
});
2396+
const deepResponse = await platform.agentConversation({
2397+
userId: 'agent_rag_profile_user',
2398+
sessionId: 'session_rag_profile_deep',
2399+
message: 'explain in detail answer profile budget probe',
2400+
topK: 3,
2401+
scope: {
2402+
workspaceId: 'answerprofile',
2403+
corpusId: 'answerprofile',
2404+
sourcePathPrefixes: ['Knowledge_Base/answerprofile'],
2405+
},
2406+
persistMemory: false,
2407+
});
2408+
2409+
expect(baseResponse.trace.ragContextPack?.budget).toEqual({
2410+
maxFragments: 14,
2411+
maxCharsPerFragment: 1400,
2412+
maxTotalChars: 5600,
2413+
});
2414+
expect(deepResponse.trace.ragContextPack?.budget).toEqual({
2415+
maxFragments: 24,
2416+
maxCharsPerFragment: 1600,
2417+
maxTotalChars: 9000,
2418+
});
2419+
expect(deepResponse.trace.ragContextPack?.sourceBoundary).toBe('full_document');
2420+
});
2421+
23612422
test('agent conversation uses optional LLM sufficiency judge for borderline legacy source windows', async () => {
23622423
const savedAt = '2026-07-05T00:00:00.000Z';
23632424
const sourcePath = 'tmp/missing-rag-legacy-source/water-glass.md';

src/learning/KnowledgeLearningPlatform.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,10 +416,24 @@ const AGENT_RAG_RECOVERY_CONTEXT_BUDGET: RagContextBudget = {
416416
maxTotalChars: 12000,
417417
};
418418

419+
const AGENT_RAG_DEEP_CONTEXT_BUDGET: RagContextBudget = {
420+
maxFragments: 24,
421+
maxCharsPerFragment: 1600,
422+
maxTotalChars: 9000,
423+
};
424+
419425
const AGENT_RAG_BASE_GRAPH_NEIGHBOR_LIMIT = 6;
426+
const AGENT_RAG_DEEP_GRAPH_NEIGHBOR_LIMIT = 8;
420427
const AGENT_RAG_RECOVERY_GRAPH_NEIGHBOR_LIMIT = 8;
428+
const AGENT_RAG_DEEP_PARAGRAPH_WINDOW = 8;
421429
const AGENT_RAG_RECOVERY_PARAGRAPH_WINDOW = 8;
422430

431+
type AgentRagEvidenceProfile = {
432+
budget: RagContextBudget;
433+
graphNeighborLimit: number;
434+
paragraphWindow?: number;
435+
};
436+
423437
const MEMORY_LAYER_CAPACITY: Record<MemoryLayer, number> = {
424438
session: 80,
425439
unit: 320,
@@ -533,6 +547,25 @@ function tokenize(text: string): string[] {
533547
return Array.from(new Set(normalized));
534548
}
535549

550+
function resolveAgentRagEvidenceProfile(message: string): AgentRagEvidenceProfile {
551+
const normalizedMessage = normalizeWhitespace(String(message || '')).toLowerCase();
552+
const explicitDeepSignal = Boolean(
553+
/\b(?:deep|detailed|comprehensive|thorough|in-depth|in depth|fully|complete|explain|analy[sz]e)\b/i.test(normalizedMessage)
554+
|| /深度|详细|詳盡|详尽|完整|充分|展开|展開|分析|解释|解釋/u.test(normalizedMessage)
555+
);
556+
if (!explicitDeepSignal) {
557+
return {
558+
budget: { ...AGENT_RAG_BASE_CONTEXT_BUDGET },
559+
graphNeighborLimit: AGENT_RAG_BASE_GRAPH_NEIGHBOR_LIMIT,
560+
};
561+
}
562+
return {
563+
budget: { ...AGENT_RAG_DEEP_CONTEXT_BUDGET },
564+
graphNeighborLimit: AGENT_RAG_DEEP_GRAPH_NEIGHBOR_LIMIT,
565+
paragraphWindow: AGENT_RAG_DEEP_PARAGRAPH_WINDOW,
566+
};
567+
}
568+
536569
function computeJaccard(left: string[], right: string[]): number {
537570
if (!left.length || !right.length) {
538571
return 0;
@@ -4028,8 +4061,8 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
40284061
baseCandidates.add(candidate);
40294062
});
40304063
const stripped = normalized
4031-
.replace(/^(what is|what are|define|explain|tell me about|what's)\s+/i, '')
4032-
.replace(/^(什么是|解释一下|介绍一下|请解释|请介绍)\s*/i, '')
4064+
.replace(/^(what is|what are|define|explain(?:\s+(?:in\s+detail|deeply|thoroughly|fully|comprehensively))?|tell me about|what's)\s+/i, '')
4065+
.replace(/^(什么是|解释一下|详细解释|詳盡解釋|深入解释|深度解释|介绍一下|请解释|请详细解释|请介绍|深度分析|深入分析)\s*/i, '')
40334066
.trim();
40344067
if (stripped) {
40354068
baseCandidates.add(stripped);
@@ -9851,20 +9884,22 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
98519884
});
98529885
const conversationKnowledgePoints = assembledConversation.knowledgePoints;
98539886
const graphContext = assembledConversation.graphContext;
9887+
const ragEvidenceProfile = resolveAgentRagEvidenceProfile(message);
98549888
const graphNeighborItems = this.buildRagGraphNeighborQueryItems(
98559889
graphContext,
98569890
conversationKnowledgePoints,
98579891
generatedAt,
98589892
message,
9859-
AGENT_RAG_BASE_GRAPH_NEIGHBOR_LIMIT
9893+
ragEvidenceProfile.graphNeighborLimit
98609894
);
98619895
const firstReviewedRag = await this.assembleReviewedRagEvidenceContext({
98629896
query: message || 'local knowledge',
98639897
items: queryResult.items,
98649898
graphNeighborItems,
98659899
graphContext,
98669900
generatedAt,
9867-
budget: AGENT_RAG_BASE_CONTEXT_BUDGET,
9901+
budget: ragEvidenceProfile.budget,
9902+
paragraphWindow: ragEvidenceProfile.paragraphWindow,
98689903
});
98699904
let ragContextPack = firstReviewedRag.ragContextPack;
98709905
let ragSufficiencyReview = firstReviewedRag.ragSufficiencyReview;

0 commit comments

Comments
 (0)