Skip to content

fix(chat): bound PDF context with retrieved chunks#1240

Open
lzhs1995 wants to merge 3 commits into
proma-ai:mainfrom
lzhs1995:fix/kiro-pdf-context-budget-20260721
Open

fix(chat): bound PDF context with retrieved chunks#1240
lzhs1995 wants to merge 3 commits into
proma-ai:mainfrom
lzhs1995:fix/kiro-pdf-context-budget-20260721

Conversation

@lzhs1995

Copy link
Copy Markdown

Summary

  • cache and chunk extracted document text
  • inject query-relevant chunks under a 60K token document budget
  • keep only attachment references in history and return an explicit preflight error above 180K estimated tokens

AI-assisted implementation; manually verified with deterministic tests.

Tests

  • bun test apps/electron/src/main/lib/document-context.test.ts
  • bun run typecheck

Copilot AI review requested due to automatic review settings July 21, 2026 16:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves Chat 模式下“文档附件(如 PDF)注入提示词”的策略:对抽取出的文档文本做分片缓存,并在发送前按 query 相关性检索片段,控制文档上下文预算,同时在整体提示词过大时给出明确的预检错误,避免模型侧静默截断或失败。

Changes:

  • 新增 document-context.ts:实现文档分片、相关片段检索、以及基于字符长度的 token 估算。
  • 更新 chat-service.ts:用户当前消息注入“检索到的文档片段”,历史消息仅保留附件引用,并增加 180K token 预检上限错误。
  • 新增 document-context.test.ts:覆盖分片与预算上限的基础测试。

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
apps/electron/src/main/lib/document-context.ts 新增文档分片缓存、query 相关片段检索与 token 粗估逻辑
apps/electron/src/main/lib/document-context.test.ts 新增对分片与 60K token 预算的单元测试
apps/electron/src/main/lib/chat-service.ts 使用检索片段注入文档上下文;历史仅保留引用;增加 180K token 预检报错

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9 to +26
const chunkCache = new Map<string, string[]>()

function documentKey(text: string): string {
return createHash('sha256').update(text).digest('hex')
}

export function chunkDocument(text: string): string[] {
const key = documentKey(text)
const cached = chunkCache.get(key)
if (cached) return cached
const normalized = text.replace(/\r\n/g, '\n').trim()
const chunks: string[] = []
for (let start = 0; start < normalized.length; start += CHUNK_CHARS - CHUNK_OVERLAP_CHARS) {
chunks.push(normalized.slice(start, start + CHUNK_CHARS))
}
chunkCache.set(key, chunks)
return chunks
}
Comment on lines +32 to +50
export function retrieveDocumentContext(text: string, query: string): string {
const terms = queryTerms(query)
const ranked = chunkDocument(text).map((chunk, index) => {
const lower = chunk.toLowerCase()
let score = 0
for (const term of terms) if (lower.includes(term)) score++
return { chunk, index, score }
}).sort((a, b) => b.score - a.score || a.index - b.index)

const selected: string[] = []
let chars = 0
for (const item of ranked) {
if (chars + item.chunk.length > DOCUMENT_CONTEXT_MAX_CHARS) continue
selected.push(`[片段 ${item.index + 1}]\n${item.chunk}`)
chars += item.chunk.length
if (chars >= DOCUMENT_CONTEXT_MAX_CHARS) break
}
return selected.join('\n\n')
}
Comment on lines 88 to 91
if (text.trim()) {
parts.push(`\n<file name="${att.filename}">\n${text}\n</file>`)
const context = retrieveDocumentContext(text, messageText)
parts.push(`\n<file name="${att.filename}" mode="retrieved-chunks">\n${context}\n</file>`)
} else {
if (estimatedPromptTokens > 180_000) {
webContents.send(CHAT_IPC_CHANNELS.STREAM_ERROR, {
conversationId,
error: `当前对话预计 ${estimatedPromptTokens.toLocaleString()} tokens,超过 Kiro 安全输入上限 180,000。请新建会话、缩小 PDF 范围或使用文档摘要。`,
Comment on lines +5 to +7
export const DOCUMENT_CONTEXT_MAX_TOKENS = 60_000
// 预留约 2% 给片段编号、XML 包装及 tokenizer 误差。
const DOCUMENT_CONTEXT_MAX_CHARS = Math.floor(DOCUMENT_CONTEXT_MAX_TOKENS * 4 * 0.98)
@lzhs1995

Copy link
Copy Markdown
Author

补充本轮可靠性验证:\n\n- PDF 原生 document + 超限 RAG 双路径已完成。\n- 多文档共享 60K RAG 预算,SHA256 去重,完整 prompt 180K 安全预算。\n- 原生 PDF:单份提取文本 ≤80K tokens、合计 ≤120K;单文件 ≤8 MiB、合计 ≤16 MiB。\n- 全量 Bun:414/414 tests passed。\n- 全项目 TypeScript typecheck passed。\n- 修复了 Electron 测试间不完整 mock 污染,原先 404 pass / 2 fail 已清零。\n\n该 PR 仍需与 A2/NewAPI 候选镜像一起完成真实 PDF、连续追问和 60 分钟 soak 后才进入生产灰度。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants