Skip to content

Commit 397f96b

Browse files
committed
feat: add intent-aware tauri reply policy
1 parent 8b81c26 commit 397f96b

9 files changed

Lines changed: 149 additions & 3 deletions

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ The next real improvement beyond that baseline is now also in code:
7272
- `src/learning/KnowledgeLearningPlatform.ts` no longer treats `assistantBlocks` as a thin transport wrapper around the same old answer string,
7373
- the scoped conversation reply is now organized into explicit overview / explanation / evidence summary / memory notice / action guidance sections before citations and knowledge-action affordances are appended,
7474
- those sections now also consume real scoped data instead of only templated filler: explanation is anchored to the strongest scoped point, evidence summary reflects actual citations, and action guidance now carries memory follow-through hints,
75+
- the reply policy is now also intent-aware, so comparison-style and how-to-style prompts can shape the explanation and next-action sections differently,
7576
- which means the Tauri agent surface can now look materially different even when the underlying knowledge result set is unchanged.
7677

7778
The next gap is narrower now:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ Tauri-first reply rendering 基线已交付:
7272
- `src/learning/KnowledgeLearningPlatform.ts` 不再把 `assistantBlocks` 当成“同一段旧 answer 的运输包装层”,
7373
- scoped conversation reply 现在会先组织成明确的 overview / explanation / evidence summary / memory notice / action guidance,再追加 citations 与 knowledge-action affordance,
7474
- 这些 section 现在也会吃进真实 scoped 数据,而不再只是模板填充:explanation 会锚定最强 scoped point,evidence summary 反映真实 citation,action guidance 还会带上 memory follow-through hint,
75+
- reply policy 现在也具备 intent awareness,因此 comparison-style 与 how-to-style prompt 可以把 explanation 和 next-action section 引导成不同语气与用途,
7576
- 这意味着即便底层知识命中集合没变,Tauri 中的 agent 输出也已经可以在结构上明显不同。
7677

7778
当前剩余缺口已经收窄为:

docs/en/implementation_plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Align the active implementation plan with current code reality:
7070
- artifact-style handling for large HTML assistant outputs through sandboxed preview.
7171
- structured reply composition now splits the assistant output into overview / explanation / evidence summary / memory notice / action guidance blocks instead of emitting only one wrapped markdown answer.
7272
- those sections are now also semantically richer: the explanation is anchored to the strongest scoped knowledge point, the evidence summary reflects real scoped citations, and next-action guidance incorporates both scoped nodes and memory-action follow-through.
73+
- reply composition is now intent-aware as well: comparison-style and how-to-style prompts no longer reuse the exact same explanation/action phrasing as plain explanatory prompts.
7374

7475
#### Next execution order
7576

docs/en/task.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [x] The Tauri-first plan to evolve toward shared Reader-aligned rich reply rendering is now implemented as the current baseline while preserving knowledge-point/capability compatibility.
1212
- [x] The backend reply composer now uses `assistantBlocks` as a real organization layer in Tauri: overview, explanation, evidence summary, memory notice, and next-action guidance are emitted as distinct blocks instead of only wrapping the old answer text.
1313
- [x] The new Tauri reply sections are no longer template-only: explanation, evidence, and next-action guidance now derive from actual knowledge points, citations, and memory-action hints.
14+
- [x] The reply policy is now intent-aware for Tauri agent output: comparison-style and how-to-style prompts can yield different explanation/action phrasing instead of one generic section style.
1415
- [x] FR-010 is now governed by the current workflow reality instead of the removed transition-era assumption: repo-owned workflows pin `actions/setup-node@v4` to Node 24 and no longer rely on `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24`.
1516
- [~] Remote CI closure remains a live release bar rather than a static claim: `Fixrisk Operational Readiness` must stay green on `main`, while residual Node 20 deprecation annotations from marketplace actions remain documented as non-blocking external debt.
1617

docs/zh/implementation_plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
- 面向大型 HTML assistant output 的 artifact-style 隔离路径(sandboxed preview)。
7272
- assistant 输出现在按 overview / explanation / evidence summary / memory notice / action guidance 分块组织,而不再只是单个 markdown answer 的包装。
7373
- 这些 section 现在也具备更实在的语义内容:explanation 会锚定最强 scoped knowledge point,evidence summary 会反映真实 scoped citation,next-action guidance 也会吸收 scoped node 与 memory action 的 follow-through 建议。
74+
- reply composition 现在也具备 query intent awareness:comparison-style 与 how-to-style prompt 不再复用与普通 explanatory prompt 完全相同的 explanation / action phrasing。
7475

7576
#### 下一步执行顺序
7677

docs/zh/task.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [x] 这条 Tauri-first 路线已作为当前基线落地:在保持现有 knowledge-point / capability 兼容的前提下,引入了共享 Reader-aligned rich reply rendering。
88
- [x] 后端回复组织层现在已经把 `assistantBlocks` 用成真正的 Tauri 输出结构:overview、explanation、evidence summary、memory notice、next-action guidance 已分块输出,而不再只是旧 answer 文本的包装。
99
- [x] 新的 Tauri reply sections 已不再只是模板文本:explanation、evidence、next-action guidance 现在会由真实 knowledge point、citation 和 memory action hint 驱动。
10+
- [x] Tauri agent 输出的 reply policy 现在已具备 intent awareness:comparison-style 与 how-to-style prompt 会得到不同的 explanation / action phrasing,而不再只有一套通用 section 风格。
1011
- [x] FR-010 现已按当前工作流现实治理,而不是继续沿用已删除的过渡期假设:仓库自有工作流固定为 `actions/setup-node@v4` + Node 24,且不再依赖 `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24`
1112
- [~] remote CI 是否闭环仍以 `main` 上的实时 `Fixrisk Operational Readiness` 结果为准;其余 marketplace action 带来的 Node 20 弃用注解继续作为非阻塞外部债务记录。
1213

src/agent_workspace.frontend.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2784,6 +2784,12 @@ describe('agent workspace learning-path integration', () => {
27842784
);
27852785
}
27862786
if (markdown.includes('## Next Actions')) {
2787+
if (markdown.includes('side by side')) {
2788+
return (
2789+
'<h2>Next Actions</h2>'
2790+
+ '<p>inspect the strongest nodes side by side</p>'
2791+
);
2792+
}
27872793
return (
27882794
'<h2>Next Actions</h2>'
27892795
+ '<p>Persist the latest user focus to scoped conversation memory.</p>'

src/learning/KnowledgeLearningPlatform.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,81 @@ describe('KnowledgeLearningPlatform', () => {
14961496
expect(persistedMemory.results[0]?.tags).toContain('scope_corpus:optics');
14971497
});
14981498

1499+
test('agent conversation explanation and next actions adapt to comparison-style queries', async () => {
1500+
await platform.ingestKnowledge({
1501+
incremental: true,
1502+
documents: [
1503+
{
1504+
documentId: 'doc_compare_scope',
1505+
sourcePath: 'Knowledge_Base/optics/reflection.md',
1506+
language: 'en',
1507+
content: '# Reflection\nReflection and absorption differ in how optical energy is redirected versus dissipated.',
1508+
},
1509+
{
1510+
documentId: 'doc_compare_scope_2',
1511+
sourcePath: 'Knowledge_Base/optics/transmission.md',
1512+
language: 'en',
1513+
content: '# Transmission\nTransmission complements reflection when comparing optical interface behavior.',
1514+
},
1515+
],
1516+
});
1517+
1518+
const response = await platform.agentConversation({
1519+
userId: 'agent_compare_user',
1520+
sessionId: 'session_compare_scope',
1521+
message: 'compare reflection vs absorption',
1522+
scope: {
1523+
corpusId: 'optics',
1524+
languages: ['en'],
1525+
},
1526+
persistMemory: true,
1527+
});
1528+
1529+
const markdownBlocks = (response.assistantBlocks || []).filter((block) => block.type === 'main_markdown');
1530+
expect(
1531+
markdownBlocks.some((block) => String((block as { markdown?: string }).markdown || '').includes('comparison baseline'))
1532+
).toBe(true);
1533+
expect(
1534+
markdownBlocks.some((block) => String((block as { markdown?: string }).markdown || '').includes('Supporting comparison nodes'))
1535+
).toBe(true);
1536+
expect(
1537+
markdownBlocks.some((block) => String((block as { markdown?: string }).markdown || '').includes('inspect the strongest nodes side by side'))
1538+
).toBe(true);
1539+
});
1540+
1541+
test('agent conversation explanation and next actions adapt to how-to queries', async () => {
1542+
await platform.ingestKnowledge({
1543+
incremental: true,
1544+
documents: [
1545+
{
1546+
documentId: 'doc_howto_scope',
1547+
sourcePath: 'Knowledge_Base/optics/calibration.md',
1548+
language: 'en',
1549+
content: '# Calibration\nCalibration sequence requires establishing a baseline measurement, then validating response drift.',
1550+
},
1551+
],
1552+
});
1553+
1554+
const response = await platform.agentConversation({
1555+
userId: 'agent_howto_user',
1556+
sessionId: 'session_howto_scope',
1557+
message: 'how to calibrate optical response',
1558+
scope: {
1559+
corpusId: 'optics',
1560+
languages: ['en'],
1561+
},
1562+
persistMemory: true,
1563+
});
1564+
1565+
const markdownBlocks = (response.assistantBlocks || []).filter((block) => block.type === 'main_markdown');
1566+
expect(
1567+
markdownBlocks.some((block) => String((block as { markdown?: string }).markdown || '').includes('starting anchor for the next concrete steps'))
1568+
).toBe(true);
1569+
expect(
1570+
markdownBlocks.some((block) => String((block as { markdown?: string }).markdown || '').includes('move from explanation into concrete guided-learning or focus-mode steps'))
1571+
).toBe(true);
1572+
});
1573+
14991574
test('agent conversation exposes readiness and miss diagnostics when scoped retrieval is empty', async () => {
15001575
const response = await platform.agentConversation({
15011576
userId: 'agent_miss_user',

src/learning/KnowledgeLearningPlatform.ts

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7624,6 +7624,45 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
76247624
return `${memoryPrefix}The strongest scoped match is ${leadingPoint.title}. I found ${params.knowledgePoints.length} relevant knowledge point(s) and ${params.citations.length} citation(s).\n\nKey evidence:\n${evidenceLines.join('\n')}`;
76257625
}
76267626

7627+
private classifyScopedConversationIntent(message: string): 'explain' | 'compare' | 'how_to' | 'generic' {
7628+
const normalized = String(message || '').trim().toLowerCase();
7629+
if (!normalized) {
7630+
return 'generic';
7631+
}
7632+
if (
7633+
normalized.includes('compare')
7634+
|| normalized.includes('difference')
7635+
|| normalized.includes('vs')
7636+
|| normalized.includes('区别')
7637+
|| normalized.includes('对比')
7638+
) {
7639+
return 'compare';
7640+
}
7641+
if (
7642+
normalized.includes('how to')
7643+
|| normalized.includes('how do')
7644+
|| normalized.includes('steps')
7645+
|| normalized.includes('plan')
7646+
|| normalized.includes('如何')
7647+
|| normalized.includes('怎么')
7648+
|| normalized.includes('步骤')
7649+
|| normalized.includes('方案')
7650+
) {
7651+
return 'how_to';
7652+
}
7653+
if (
7654+
normalized.includes('what is')
7655+
|| normalized.includes('why')
7656+
|| normalized.includes('explain')
7657+
|| normalized.includes('解释')
7658+
|| normalized.includes('什么是')
7659+
|| normalized.includes('为什么')
7660+
) {
7661+
return 'explain';
7662+
}
7663+
return 'generic';
7664+
}
7665+
76277666
private buildScopedConversationOverviewMarkdown(params: {
76287667
knowledgePoints: AgentConversationKnowledgePoint[];
76297668
citations: KnowledgeCitation[];
@@ -7648,19 +7687,29 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
76487687
}
76497688

76507689
private buildScopedConversationExplanationMarkdown(params: {
7690+
message: string;
76517691
knowledgePoints: AgentConversationKnowledgePoint[];
76527692
citations: KnowledgeCitation[];
76537693
recalledMemories: AgentConversationMemoryRecord[];
76547694
}): string {
76557695
if (params.knowledgePoints.length <= 0) {
76567696
return '## Explanation\n\nThe current scope did not return a strong enough knowledge point to explain the request directly.';
76577697
}
7698+
const intent = this.classifyScopedConversationIntent(params.message);
76587699
const strongestPoint = params.knowledgePoints[0];
76597700
const explanationLines = [
76607701
'## Explanation',
76617702
'',
7662-
`**${strongestPoint.title}** is the current best scoped anchor.`,
76637703
];
7704+
if (intent === 'compare') {
7705+
explanationLines.push(`Use **${strongestPoint.title}** as the comparison baseline inside the current scope.`);
7706+
} else if (intent === 'how_to') {
7707+
explanationLines.push(`Use **${strongestPoint.title}** as the starting anchor for the next concrete steps.`);
7708+
} else if (intent === 'explain') {
7709+
explanationLines.push(`**${strongestPoint.title}** is the current best scoped anchor for the explanation.`);
7710+
} else {
7711+
explanationLines.push(`**${strongestPoint.title}** is the current best scoped anchor.`);
7712+
}
76647713
const summary = normalizeWhitespace(String(strongestPoint.summary || strongestPoint.evidenceSnippet || '').trim());
76657714
if (summary) {
76667715
explanationLines.push('', summary);
@@ -7672,7 +7721,9 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
76727721
if (supportingTitles.length > 0) {
76737722
explanationLines.push(
76747723
'',
7675-
`Supporting scoped nodes: ${supportingTitles.join(', ')}.`
7724+
intent === 'compare'
7725+
? `Supporting comparison nodes: ${supportingTitles.join(', ')}.`
7726+
: `Supporting scoped nodes: ${supportingTitles.join(', ')}.`
76767727
);
76777728
}
76787729
if (params.recalledMemories.length > 0) {
@@ -7719,12 +7770,14 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
77197770
}
77207771

77217772
private buildScopedConversationActionGuideMarkdown(params: {
7773+
message: string;
77227774
knowledgePoints: AgentConversationKnowledgePoint[];
77237775
memoryActions: AgentConversationMemoryAction[];
77247776
}): string {
77257777
if (params.knowledgePoints.length <= 0) {
77267778
return '## Next Actions\n\nNo actionable scoped knowledge card is available for this turn.';
77277779
}
7780+
const intent = this.classifyScopedConversationIntent(params.message);
77287781
const topTitles = params.knowledgePoints
77297782
.slice(0, 3)
77307783
.map((point) => `- ${point.title}`);
@@ -7736,7 +7789,11 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
77367789
return [
77377790
'## Next Actions',
77387791
'',
7739-
'Use the scoped knowledge cards below to continue with focus mode or guided learning for the highest-signal nodes:',
7792+
intent === 'compare'
7793+
? 'Use the scoped knowledge cards below to inspect the strongest nodes side by side before deciding which distinctions matter most:'
7794+
: intent === 'how_to'
7795+
? 'Use the scoped knowledge cards below to move from explanation into concrete guided-learning or focus-mode steps:'
7796+
: 'Use the scoped knowledge cards below to continue with focus mode or guided learning for the highest-signal nodes:',
77407797
...topTitles,
77417798
...(actionHints.length > 0
77427799
? ['', 'Suggested follow-through from the current turn:', ...actionHints]
@@ -7745,6 +7802,7 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
77457802
}
77467803

77477804
private buildScopedConversationAssistantBlocks(params: {
7805+
message: string;
77487806
citations: KnowledgeCitation[];
77497807
knowledgePoints: AgentConversationKnowledgePoint[];
77507808
recalledMemories: AgentConversationMemoryRecord[];
@@ -8212,6 +8270,7 @@ export class KnowledgeLearningPlatform implements KnowledgeLearningPlatformAPI {
82128270
usedScope: traceScope,
82138271
});
82148272
const assistantBlocks = this.buildScopedConversationAssistantBlocks({
8273+
message,
82158274
citations,
82168275
knowledgePoints,
82178276
recalledMemories,

0 commit comments

Comments
 (0)