feat(core): add matched context documents to ai prompt (#11148)

Close [BS-2834](https://linear.app/affine-design/issue/BS-2834).

### What Changed?
- Change `reference_index` from chip order to increasing positive integer.
- Add matched context documents to ai prompt.
This commit is contained in:
akumatus
2025-03-26 01:55:54 +00:00
parent ae552c97cf
commit d991149faa
4 changed files with 57 additions and 51 deletions

View File

@@ -311,10 +311,9 @@ const actions = [
files: [ files: [
{ {
blobId: 'euclidean_distance', blobId: 'euclidean_distance',
refIndex: 1,
fileName: 'euclidean_distance.rs', fileName: 'euclidean_distance.rs',
fileType: 'text/rust', fileType: 'text/rust',
chunks: TestAssets.Code, fileContent: TestAssets.Code,
}, },
], ],
}, },
@@ -339,10 +338,9 @@ const actions = [
files: [ files: [
{ {
blobId: 'SSOT', blobId: 'SSOT',
refIndex: 1,
fileName: 'Single source of truth - Wikipedia', fileName: 'Single source of truth - Wikipedia',
fileType: 'text/markdown', fileType: 'text/markdown',
chunks: TestAssets.SSOT, fileContent: TestAssets.SSOT,
}, },
], ],
}, },

View File

@@ -1012,13 +1012,12 @@ Use the structure of the fragments to assess their relevance and provide the nec
## Content fragments format: ## Content fragments format:
- Document fragments, identified by a \`document_id\` and containing \`document_content\`. - Document fragments, identified by a \`document_id\` and containing \`document_content\`.
- File fragments, identified by a \`blob_id\` and containing \`file_content\`. - File fragments, identified by a \`blob_id\` and containing \`file_content\`.
- Each fragment has a \`reference_index\` that indicates its source.
## Citations Rules ## Citations Rules
When referencing information from the provided documents or files in your response: When referencing information from the provided documents or files in your response:
1. Use markdown footnote format for citations 1. Use markdown footnote format for citations
2. Add citations immediately after the relevant sentence or paragraph 2. Add citations immediately after the relevant sentence or paragraph
3. Required format: [^reference_index] where reference_index is the numerical index of the source document or file 3. Required format: [^reference_index] where reference_index is an increasing positive integer
4. You MUST include citations at the end of your response in this exact format: 4. You MUST include citations at the end of your response in this exact format:
- For documents: [^reference_index]:{"type":"doc","docId":"document_id"} - For documents: [^reference_index]:{"type":"doc","docId":"document_id"}
- For files: [^reference_index]:{"type":"attachment","blobId":"blob_id","fileName":"file_name","fileType":"file_type"} - For files: [^reference_index]:{"type":"attachment","blobId":"blob_id","fileName":"file_name","fileType":"file_type"}
@@ -1045,22 +1044,20 @@ The following content is a relevant content segment:
{{#docs}} {{#docs}}
========== ==========
- type: document - type: document
- reference_index: {{refIndex}}
- document_id: {{docId}} - document_id: {{docId}}
- document_content: - document_content:
{{markdown}} {{docContent}}
========== ==========
{{/docs}} {{/docs}}
{{#files}} {{#files}}
========== ==========
- type: file - type: file
- reference_index: {{refIndex}}
- blob_id: {{blobId}} - blob_id: {{blobId}}
- file_name: {{fileName}} - file_name: {{fileName}}
- file_type: {{fileType}} - file_type: {{fileType}}
- file_content: - file_content:
{{chunks}} {{fileContent}}
========== ==========
{{/files}} {{/files}}

View File

@@ -36,17 +36,15 @@ export type ChatStatus =
export interface DocContext { export interface DocContext {
docId: string; docId: string;
refIndex: number; docContent: string;
markdown: string;
} }
export type FileContext = { export interface FileContext {
blobId: string; blobId: string;
refIndex: number;
fileName: string; fileName: string;
fileType: string; fileType: string;
chunks: string; fileContent: string;
}; }
export type ChatContextValue = { export type ChatContextValue = {
// history messages of the chat // history messages of the chat

View File

@@ -23,6 +23,7 @@ import type {
ChatContextValue, ChatContextValue,
ChatMessage, ChatMessage,
DocContext, DocContext,
FileChip,
FileContext, FileContext,
} from './chat-context'; } from './chat-context';
import { isDocChip, isFileChip } from './components/utils'; import { isDocChip, isFileChip } from './components/utils';
@@ -556,43 +557,55 @@ export class ChatPanelInput extends SignalWatcher(WithDisposable(LitElement)) {
private async _getMatchedContexts(userInput: string) { private async _getMatchedContexts(userInput: string) {
const contextId = await this.getContextId(); const contextId = await this.getContextId();
// TODO(@akumatus): adapt workspace docs if (!contextId) {
const { files: matched = [] } = return { files: [], docs: [] };
(contextId && }
(await AIProvider.context?.matchContext(contextId, userInput))) ||
{};
const contexts = this.chatContextValue.chips.reduce( const docContexts = new Map<string, DocContext>();
(acc, chip, index) => { const fileContexts = new Map<string, FileContext>();
if (chip.state !== 'finished') {
return acc; const { files: matchedFiles = [], docs: matchedDocs = [] } =
(await AIProvider.context?.matchContext(contextId, userInput)) ?? {};
matchedDocs.forEach(doc => {
docContexts.set(doc.docId, {
docId: doc.docId,
docContent: doc.content,
});
});
matchedFiles.forEach(file => {
const context = fileContexts.get(file.fileId);
if (context) {
context.fileContent += `\n${file.content}`;
} else {
const fileChip = this.chatContextValue.chips.find(
chip => isFileChip(chip) && chip.fileId === file.fileId
) as FileChip | undefined;
if (fileChip && fileChip.blobId) {
fileContexts.set(file.fileId, {
blobId: fileChip.blobId,
fileName: fileChip.file.name,
fileType: fileChip.file.type,
fileContent: file.content,
});
} }
}
});
this.chatContextValue.chips.forEach(chip => {
if (isDocChip(chip) && !!chip.markdown?.value) { if (isDocChip(chip) && !!chip.markdown?.value) {
acc.docs.push({ docContexts.set(chip.docId, {
docId: chip.docId, docId: chip.docId,
refIndex: index + 1, docContent: chip.markdown.value,
markdown: chip.markdown.value,
}); });
} }
if (isFileChip(chip) && chip.blobId) {
const matchedChunks = matched
.filter(chunk => chunk.fileId === chip.fileId)
.map(chunk => chunk.content);
if (matchedChunks.length > 0) {
acc.files.push({
blobId: chip.blobId,
refIndex: index + 1,
fileName: chip.file.name,
fileType: chip.file.type,
chunks: matchedChunks.join('\n'),
}); });
}
} return {
return acc; docs: Array.from(docContexts.values()),
}, files: Array.from(fileContexts.values()),
{ docs: [], files: [] } as { docs: DocContext[]; files: FileContext[] } };
);
return contexts;
} }
} }