diff --git a/frontend/src/lib/prompts/exportCompact.ts b/frontend/src/lib/prompts/exportCompact.ts
index a828f23..0b93624 100644
--- a/frontend/src/lib/prompts/exportCompact.ts
+++ b/frontend/src/lib/prompts/exportCompact.ts
@@ -4,31 +4,61 @@ import type {
PromptVersion,
} from "./types";
-const EXPORT_COMPACT_SYSTEM = `You are Vesti's export compaction assistant.
-
-Your job is to compress one conversation into a high-fidelity markdown handoff for another AI or engineer who must continue the work with minimal context loss.
-
-Output must contain these exact headings:
-## Background
-## Key Questions
-## Decisions And Answers
-## Reusable Artifacts
-## Unresolved
-
-Hard rules:
-1) Use only evidence present in the provided transcript.
-2) Prioritize transfer fidelity over aggressive shortening. Do not drop grounded decisions, commands, files, APIs, or next-step context just to be shorter.
-3) Preserve concrete details such as filenames, function names, shell commands, URLs, APIs, and code blocks when grounded.
-4) Keep chronological logic intact: if a later decision depends on earlier context, make that dependency explicit.
-5) If a section has no grounded evidence, write a conservative placeholder instead of inventing details.
-6) Respect the requested locale.
-7) Output markdown only. Do not wrap the whole answer in code fences.
-8) If the transcript contains reusable code or command snippets, preserve them in markdown-friendly form rather than paraphrasing them away.`;
+const EXPORT_COMPACT_SYSTEM = `将技术对话压缩为其他AI可接续的格式。
+
+输出必须包含以下标记:
+[主题] 一句话概括
+[背景] 问题背景和约束
+[关键决策] 编号列表,每项包含"决策内容 - 选择理由"
+[核心代码] \`\`\`语言\n完整代码\n\`\`\`
+[待解决问题] 仍需处理的事项
+[来源] X轮对话
+
+规则:
+1. 使用对话中实际存在的信息,不编造
+2. 代码必须完整,禁止截断
+3. 决策必须说明"为什么选A而不是B"
+4. 去除寒暄和重复内容
+5. 保留文件名、函数名、命令等具体细节
+6. 如果某部分无内容,使用占位符如"无代码"或"已解决"
+7. 仅输出markdown,不要包裹代码块
+8. 用中文输出
+
+示例:
+[主题] React虚拟滚动实现
+
+[背景] 需渲染10万+数据导致卡顿。使用react-window时遇到动态高度问题。
+
+[关键决策]
+1. 使用VariableSizeList替代FixedSizeList - Fixed无法处理动态高度,会导致布局错乱
+2. 设置estimatedItemSize=350 - 基于业务数据80%条目在300-400px
+
+[核心代码]
+\`\`\`jsx
+import { VariableSizeList } from 'react-window';
+function List({ items }) {
+ const getItemSize = (i) => items[i].height || 350;
+ return (
+
+ {({ index, style }) =>
{items[index].content}
}
+
+ );
+}
+\`\`\`
+
+[待解决问题] 需要测试边界情况:当列表为空时的处理
+
+[来源] 8轮对话`;
function formatDateTime(value: number): string {
- return new Date(value).toLocaleString("en-US", {
+ return new Date(value).toLocaleString("zh-CN", {
year: "numeric",
- month: "short",
+ month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
@@ -36,7 +66,7 @@ function formatDateTime(value: number): string {
}
function formatTime(value: number): string {
- return new Date(value).toLocaleTimeString("en-US", {
+ return new Date(value).toLocaleTimeString("zh-CN", {
hour: "2-digit",
minute: "2-digit",
});
@@ -44,95 +74,79 @@ function formatTime(value: number): string {
function toTranscript(messages: Message[]): string {
if (!messages.length) {
- return "[No messages available]";
+ return "[无消息]";
}
return messages
.map((message, index) => {
- const role = message.role === "user" ? "User" : "AI";
+ const role = message.role === "user" ? "用户" : "AI";
return `${index + 1}. [${formatTime(message.created_at)}] [${role}] ${message.content_text}`;
})
.join("\n");
}
function buildCompactPrompt(payload: ExportCompressionPromptPayload): string {
- const isStepProfile = payload.profile === "step_flash_concise";
- const brevityRule = isStepProfile
- ? "Keep bullets concise and prioritize grounded artifacts over narrative polish."
- : "Preserve the full implementation trail when it is grounded, even if the output becomes longer.";
+ return `将以下技术对话压缩为AI Handoff格式。
- return `Create a high-fidelity export handoff for this conversation.
-
-Metadata:
-- Title: ${payload.conversationTitle || "(untitled)"}
-- Platform: ${payload.conversationPlatform || "unknown"}
-- StartedAt: ${
+元数据:
+- 标题: ${payload.conversationTitle || "(未命名)"}
+- 平台: ${payload.conversationPlatform || "unknown"}
+- 开始时间: ${
payload.conversationOriginAt
? formatDateTime(payload.conversationOriginAt)
: "unknown"
}
-- Locale: ${payload.locale || "zh"}
-- MessageCount: ${payload.messages.length}
+- 对话轮数: ${payload.messages.length}
-Transcript:
+对话记录:
${toTranscript(payload.messages)}
-Output requirements:
-1) Use the exact headings listed in the system prompt.
-2) Optimize for AI handoff, not for skimming: preserve background, key asks, decisions, constraints, artifacts, and unresolved work.
-3) In ## Background, include the task frame, constraints, and any context another assistant would need before acting.
-4) In ## Key Questions, keep only the questions that actually drove the work forward.
-5) In ## Decisions And Answers, capture grounded resolutions, tradeoffs, and chosen implementation paths.
-6) In ## Reusable Artifacts, preserve filenames, commands, APIs, functions, and code blocks when grounded.
-7) In ## Unresolved, call out what remains open, risky, or needs continuation.
-8) ${brevityRule}
-9) If evidence is sparse, keep the structure and use conservative placeholders.
-10) Write the final output in ${payload.locale === "en" ? "natural English" : "natural Chinese"}.
-11) Output markdown only.`;
+要求:
+1. 严格使用系统提示中的标记格式
+2. [关键决策]必须包含选择理由(为什么选这个而不是其他方案)
+3. [核心代码]保持完整可运行,包含必要的import
+4. 去除寒暄、重复和与主题无关的内容
+5. 如果对话是中文,用中文输出;如果是英文,可以保留关键术语
+6. 输出纯markdown,不要包裹在代码块中`;
}
function buildCompactFallbackPrompt(
payload: ExportCompressionPromptPayload
): string {
- const fallbackNote =
- payload.profile === "step_flash_concise"
- ? "Keep it tight, but do not lose grounded files, commands, APIs, or unresolved work."
- : "Preserve grounded files, commands, APIs, code, and unresolved work whenever they appear.";
-
- return `Write a shorter fallback markdown handoff for this conversation.
-You must keep these exact headings:
-## Background
-## Key Questions
-## Decisions And Answers
-## Reusable Artifacts
-## Unresolved
-
-Keep it shorter and more conservative than the main prompt.
-${fallbackNote}
-Use ${payload.locale === "en" ? "English" : "Chinese"}.
-Output markdown only.
-
-Transcript:
-${toTranscript(payload.messages)}`;
+ return `将以下对话压缩为简洁的AI交接格式:
+
+标题: ${payload.conversationTitle || "(未命名)"}
+轮数: ${payload.messages.length}
+
+对话记录:
+${toTranscript(payload.messages)}
+
+必须包含以下标记:
+[主题] 一句话概括
+[背景] 关键背景
+[关键决策] 核心决策点
+[核心代码] 代码块(如有)
+[来源] ${payload.messages.length}轮对话
+
+保持简洁,但保留关键决策和代码。用中文输出。`;
}
export const CURRENT_EXPORT_COMPACT_PROMPT: PromptVersion = {
- version: "v1.2.0-export-compact-kimi-step-profiled",
- createdAt: "2026-03-16",
- description:
- "High-fidelity compact export handoff prompt with Kimi-rich and Step-concise profiles.",
+ version: "v1.2.0-export-compact-zh",
+ createdAt: "2026-03-17",
+ description: "中文AI Handoff格式,保留决策理由和完整代码",
system: EXPORT_COMPACT_SYSTEM,
- fallbackSystem: "You are a cautious technical export assistant. Output markdown only.",
+ fallbackSystem: "你是技术对话压缩助手,输出简洁的中文摘要。",
userTemplate: buildCompactPrompt,
fallbackTemplate: buildCompactFallbackPrompt,
};
export const EXPERIMENTAL_EXPORT_COMPACT_PROMPT: PromptVersion = {
- version: "v1.2.0-export-compact-kimi-step-profiled-exp",
- createdAt: "2026-03-16",
- description: "Experimental profiled compact export handoff variant.",
+ version: "v1.2.0-export-compact-zh-exp",
+ createdAt: "2026-03-17",
+ description: "Experimental Chinese compact export variant.",
system: EXPORT_COMPACT_SYSTEM,
- fallbackSystem: "You are a cautious technical export assistant. Output markdown only.",
+ fallbackSystem: "你是技术对话压缩助手,输出简洁的中文摘要。",
userTemplate: buildCompactPrompt,
fallbackTemplate: buildCompactFallbackPrompt,
};
diff --git a/frontend/src/lib/prompts/exportSummary.ts b/frontend/src/lib/prompts/exportSummary.ts
index b2d344c..3918f30 100644
--- a/frontend/src/lib/prompts/exportSummary.ts
+++ b/frontend/src/lib/prompts/exportSummary.ts
@@ -4,40 +4,89 @@ import type {
PromptVersion,
} from "./types";
-const EXPORT_SUMMARY_SYSTEM = `You are Vesti's export summary assistant.
+const EXPORT_SUMMARY_SYSTEM = `将技术对话整理为可直接存入Notion/Obsidian的知识卡片。
-Your job is to compress one conversation into a note-ready markdown summary for future human recall.
+输出格式(严格遵循Markdown):
+\`\`\`markdown
+# [标题] 技术主题
+> [日期] | [平台] | [对话轮数]
-Output must contain these exact headings:
## TL;DR
-## Problem Frame
-## Important Moves
-## Reusable Snippets
-## Next Steps
-## Tags
-
-Hard rules:
-1) Use only evidence present in the provided transcript.
-2) Prefer concrete phrasing over vague praise or filler.
-3) Keep the summary readable by a human returning later with limited context.
-4) Align the content structure with Vesti's conversation_summary.v2 mindset: core question, key moves, reusable insights, unresolved work, and next actions.
-5) If evidence is missing, state that conservatively instead of inventing details.
-6) Respect the requested locale.
-7) Output markdown only. Do not wrap the whole answer in code fences.
-8) Reusable snippets may include commands, files, APIs, or code only when grounded in the transcript.`;
+[一句话总结核心内容,包含关键数据和结果]
+
+## 问题定义(如有)
+**症状**:[问题表现]
+**约束**:[限制条件]
+**影响**:[影响范围]
+
+## 方案对比(如涉及技术选型)
+| 方案 | 维度1 | 维度2 | 维度3 | 决策 |
+|------|-------|-------|-------|------|
+| 方案A | ... | ... | ... | ❌/✅ |
+| 方案B | ... | ... | ... | ❌/✅ |
+
+## 可复用代码
+### 场景1:xxx
+[完整可运行的代码块]
+
+### 场景2:xxx
+[变体代码]
+
+## 关键决策依据
+1. **[决策点]**:[选择理由,包含"为什么选A而非B"]
+2. **[决策点]**:[选择理由]
+
+## 踩坑记录(如有)
+- ❌ [错误做法] → [后果]
+- ✅ [正确做法]
+
+## 后续行动
+- [ ] [具体可执行的任务]
+- [ ] [具体可执行的任务]
+
+## 相关资源
+- [链接描述](url)
+- [内部文档](wiki-link)
+
+## 标签
+#标签1 #标签2 #标签3
+\`\`\`
+
+核心原则:
+1. **可复用**:读者无需看原始对话,仅看笔记就能理解并应用
+2. **结构化**:清晰的层级,方便快速定位和检索
+3. **完整性**:包含问题、方案、代码、决策依据、后续行动
+4. **可执行**:代码可直接复制使用,步骤可立即执行
+5. **决策依据**:每个选择必须说明理由
+
+特殊场景:
+- 纯调研类(无代码):保留方案对比和决策依据,代码块写"无代码"
+- 超长对话(>100轮):按子主题拆分多个##二级标题
+- 调试/排查类:必须保留 现象→排查步骤→根因→修复 的完整链条
+
+质量自检(输出前必查):
+- [ ] TL;DR是否包含关键数据/结果?
+- [ ] 代码是否完整可运行?(包含必要import)
+- [ ] 是否有"为什么选这个方案"的决策依据?
+- [ ] 标签是否便于检索?
+- [ ] 如果我是3个月后回看,能否快速理解?`;
function formatDateTime(value: number): string {
- return new Date(value).toLocaleString("en-US", {
+ return new Date(value).toLocaleString("zh-CN", {
year: "numeric",
- month: "short",
+ month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}
+function formatDate(value: number): string {
+ return new Date(value).toISOString().split("T")[0];
+}
+
function formatTime(value: number): string {
- return new Date(value).toLocaleTimeString("en-US", {
+ return new Date(value).toLocaleTimeString("zh-CN", {
hour: "2-digit",
minute: "2-digit",
});
@@ -45,101 +94,93 @@ function formatTime(value: number): string {
function toTranscript(messages: Message[]): string {
if (!messages.length) {
- return "[No messages available]";
+ return "[无消息]";
}
return messages
.map((message, index) => {
- const role = message.role === "user" ? "User" : "AI";
+ const role = message.role === "user" ? "用户" : "AI";
return `${index + 1}. [${formatTime(message.created_at)}] [${role}] ${message.content_text}`;
})
.join("\n");
}
function buildSummaryPrompt(payload: ExportCompressionPromptPayload): string {
- const isStepProfile = payload.profile === "step_flash_concise";
- const profileInstruction = isStepProfile
- ? "Favor structural coverage and concise grounded bullets over essay-like phrasing."
- : "Favor richer problem framing and clearer reconstruction of the thread's actual progression.";
-
- return `Create a note-ready export summary for this conversation.
-
-Metadata:
-- Title: ${payload.conversationTitle || "(untitled)"}
-- Platform: ${payload.conversationPlatform || "unknown"}
-- StartedAt: ${
- payload.conversationOriginAt
- ? formatDateTime(payload.conversationOriginAt)
- : "unknown"
- }
-- Locale: ${payload.locale || "zh"}
-- MessageCount: ${payload.messages.length}
+ return `将以下技术对话整理为知识卡片格式。
+
+元数据:
+- 标题: ${payload.conversationTitle || "(未命名)"}
+- 平台: ${payload.conversationPlatform || "unknown"}
+- 日期: ${payload.conversationOriginAt ? formatDate(payload.conversationOriginAt) : new Date().toISOString().split("T")[0]}
+- 对话轮数: ${payload.messages.length}
-Transcript:
+对话记录:
${toTranscript(payload.messages)}
-Output requirements:
-1) Use the exact headings listed in the system prompt.
-2) Keep this optimized for future recall: crisp TL;DR, problem framing, key moves, reusable snippets, next steps, and tags.
-3) Let ## Problem Frame align with the core question and constraints that shaped the thread.
-4) Let ## Important Moves reflect the actual progression of the discussion, similar to a lightweight thinking_journey.
-5) Let ## Next Steps reflect grounded actionable follow-ups, not generic advice.
-6) Let ## Tags stay concrete and limited to 3-5 useful tags when evidence exists.
-7) ${profileInstruction}
-8) Keep bullets concise and grounded.
-9) If evidence is sparse, keep the structure and use conservative placeholders.
-10) Write the final output in ${payload.locale === "en" ? "natural English" : "natural Chinese"}.
-11) Output markdown only.`;
+输出要求:
+1. 严格遵循系统提示中的知识卡片格式
+2. TL;DR必须包含关键数据或结果(如性能提升、问题解决等)
+3. 代码块完整可运行,包含必要的import和类型定义
+4. 方案对比表格要包含决策列(✅/❌)
+5. 后续行动使用复选框格式(- [ ])
+6. 标签使用#开头,便于后续检索
+7. 如果对话是中文,用中文输出;如果是英文,可以保留关键术语
+8. 输出纯markdown,不要包裹在代码块中`;
}
function buildSummaryFallbackPrompt(
payload: ExportCompressionPromptPayload
): string {
- const fallbackGuidance =
- payload.profile === "step_flash_concise"
- ? "Prefer compact bullets that preserve the thread's concrete actions and artifacts."
- : "Prefer stronger problem framing and more explicit important moves when evidence exists.";
+ return `将以下对话整理为简化的知识卡片:
+
+标题: ${payload.conversationTitle || "(未命名)"}
+平台: ${payload.conversationPlatform || "unknown"}
+轮数: ${payload.messages.length}
+
+对话记录:
+${toTranscript(payload.messages)}
- return `Write a markdown export summary for this conversation.
+必须包含以下章节:
+# 标题
+> 日期 | 平台 | ${payload.messages.length}轮
-You must use these exact headings:
## TL;DR
-## Problem Frame
-## Important Moves
-## Reusable Snippets
-## Next Steps
-## Tags
-
-Requirements:
-1) Keep the structure aligned with a v2-style thinking summary, but do not output JSON.
-2) Use grounded evidence only.
-3) Preserve commands, files, APIs, and code references when they exist.
-4) Even if the transcript is sparse, keep all headings and fill them conservatively.
-5) ${fallbackGuidance}
-6) Use ${payload.locale === "en" ? "English" : "Chinese"}.
-7) Output markdown only.
-
-Transcript:
-${toTranscript(payload.messages)}`;
+一句话总结
+
+## 问题定义
+关键问题描述
+
+## 方案对比
+简要对比(如有)
+
+## 可复用代码
+代码块或"无代码"
+
+## 后续行动
+- [ ] 待办事项
+
+## 标签
+#相关标签
+
+保持简洁但结构完整。用中文输出。`;
}
export const CURRENT_EXPORT_SUMMARY_PROMPT: PromptVersion = {
- version: "v1.2.0-export-summary-kimi-step-profiled",
- createdAt: "2026-03-16",
- description:
- "Summary export prompt for human-readable notes with Kimi-rich and Step-concise profiles.",
+ version: "v1.2.0-export-summary-zh",
+ createdAt: "2026-03-17",
+ description: "中文知识卡片格式,适合存入Notion/Obsidian",
system: EXPORT_SUMMARY_SYSTEM,
- fallbackSystem: "You are a concise technical export assistant. Output markdown only.",
+ fallbackSystem: "你是知识整理助手,将对话转换为结构化的中文笔记。",
userTemplate: buildSummaryPrompt,
fallbackTemplate: buildSummaryFallbackPrompt,
};
export const EXPERIMENTAL_EXPORT_SUMMARY_PROMPT: PromptVersion = {
- version: "v1.2.0-export-summary-kimi-step-profiled-exp",
- createdAt: "2026-03-16",
- description: "Experimental profiled summary export variant.",
+ version: "v1.2.0-export-summary-zh-exp",
+ createdAt: "2026-03-17",
+ description: "Experimental Chinese knowledge export variant.",
system: EXPORT_SUMMARY_SYSTEM,
- fallbackSystem: "You are a concise technical export assistant. Output markdown only.",
+ fallbackSystem: "你是知识整理助手,将对话转换为结构化的中文笔记。",
userTemplate: buildSummaryPrompt,
fallbackTemplate: buildSummaryFallbackPrompt,
};
diff --git a/frontend/src/sidepanel/components/BatchActionBar.tsx b/frontend/src/sidepanel/components/BatchActionBar.tsx
index ad89ad0..e07433a 100644
--- a/frontend/src/sidepanel/components/BatchActionBar.tsx
+++ b/frontend/src/sidepanel/components/BatchActionBar.tsx
@@ -3,6 +3,9 @@ import {
CheckSquare,
Copy,
Download,
+ FileCode,
+ FileJson,
+ FileText,
Loader2,
Square,
Trash2,
@@ -81,19 +84,17 @@ const EXPORT_MODE_OPTIONS: Array<{
{
mode: "full",
label: "Full",
- description: "Keep the complete thread transcript locally.",
+ description: "Complete conversation history.",
},
{
mode: "compact",
- label: "Compact",
- description:
- "AI handoff format. Tries current LLM settings first, then local fallback.",
+ label: "AI Handoff",
+ description: "For other AI assistants. Preserves decisions, code, and context.",
},
{
mode: "summary",
- label: "Summary",
- description:
- "Human note format. Tries current LLM settings first, then local fallback.",
+ label: "Knowledge Export",
+ description: "For notes & docs. Structured knowledge card format.",
},
];
@@ -194,53 +195,63 @@ export function BatchActionBar({
-