aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA.J. Shulman <Shulman.aj@gmail.com>2024-07-22 10:49:02 -0400
committerA.J. Shulman <Shulman.aj@gmail.com>2024-07-22 10:49:02 -0400
commit834ca4e21fead079e681b963e2d533d93a53cb91 (patch)
treeb091c4048db1473669011c3e35d0e2182281011c
parentae4809cce727a056bdc648249c0f76174a496307 (diff)
fixing summaries by having them part of the ReAct prompt
-rw-r--r--src/client/views/nodes/ChatBox/Agent.ts8
-rw-r--r--src/client/views/nodes/ChatBox/ChatBox.tsx80
-rw-r--r--src/client/views/nodes/ChatBox/MessageComponent.tsx11
-rw-r--r--src/client/views/nodes/ChatBox/prompts.ts295
-rw-r--r--src/client/views/nodes/ChatBox/tools/RAGTool.ts11
5 files changed, 172 insertions, 233 deletions
diff --git a/src/client/views/nodes/ChatBox/Agent.ts b/src/client/views/nodes/ChatBox/Agent.ts
index a3b1d083c..d494928f9 100644
--- a/src/client/views/nodes/ChatBox/Agent.ts
+++ b/src/client/views/nodes/ChatBox/Agent.ts
@@ -19,25 +19,27 @@ export class Agent {
private interMessages: AgentMessage[] = [];
private vectorstore: Vectorstore;
private _history: () => string;
+ private _summaries: () => string;
constructor(_vectorstore: Vectorstore, summaries: () => string, history: () => string) {
this.client = new OpenAI({ apiKey: process.env.OPENAI_KEY, dangerouslyAllowBrowser: true });
this.vectorstore = _vectorstore;
this._history = history;
+ this._summaries = summaries;
this.tools = {
wikipedia: new WikipediaTool(),
calculate: new CalculateTool(),
- rag: new RAGTool(this.vectorstore, summaries),
+ rag: new RAGTool(this.vectorstore),
no_tool: new NoTool(),
};
}
- async askAgent(question: string, maxTurns: number = 8): Promise<string> {
+ async askAgent(question: string, maxTurns: number = 10): Promise<string> {
console.log(`Starting query: ${question}`);
this.messages.push({ role: 'user', content: question });
const chatHistory = this._history();
console.log(`Chat history: ${chatHistory}`);
- const systemPrompt = getReactPrompt(Object.values(this.tools), chatHistory);
+ const systemPrompt = getReactPrompt(Object.values(this.tools), this._summaries, chatHistory);
console.log(`System prompt: ${systemPrompt}`);
this.interMessages = [{ role: 'system', content: systemPrompt }];
diff --git a/src/client/views/nodes/ChatBox/ChatBox.tsx b/src/client/views/nodes/ChatBox/ChatBox.tsx
index e3a164b3e..49c9b3292 100644
--- a/src/client/views/nodes/ChatBox/ChatBox.tsx
+++ b/src/client/views/nodes/ChatBox/ChatBox.tsx
@@ -4,7 +4,6 @@ import OpenAI, { ClientOptions } from 'openai';
import * as React from 'react';
import { Doc, DocListCast } from '../../../../fields/Doc';
import { CsvCast, DocCast, PDFCast, StrCast } from '../../../../fields/Types';
-import { Networking } from '../../../Network';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { LinkManager } from '../../../util/LinkManager';
@@ -14,19 +13,12 @@ import './ChatBox.scss';
import MessageComponentBox from './MessageComponent';
import { ASSISTANT_ROLE, AssistantMessage, AI_Document, Citation, CHUNK_TYPE, Chunk, getChunkType } from './types';
import { Vectorstore } from './vectorstore/VectorstoreUpload';
-import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
import { Agent } from './Agent';
import dotenv from 'dotenv';
import { DocData, DocViews } from '../../../../fields/DocSymbols';
-import { DocumentView } from '../DocumentView';
import { AnswerParser } from './AnswerParser';
import { DocumentManager } from '../../../util/DocumentManager';
-import { UUID } from 'bson';
import { v4 as uuidv4 } from 'uuid';
-import { aS } from '@fullcalendar/core/internal-common';
-import { computeRect } from '@fullcalendar/core/internal';
-import { DocUtils } from '../../../documents/DocUtils';
dotenv.config();
@@ -78,14 +70,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
await this.vectorstore.addAIDoc(newLinkedDoc);
};
- // @action
- // uploadNewDocument = async (newDoc: Doc) => {
- // const local_file_path: string = CsvCast(newDoc.data, PDFCast(newDoc.data)).url.pathname;
- // const { document_json } = await Networking.PostToServer('/createDocument', { file_path: local_file_path });
- // this.documents.push(...document_json.map(convertToAIDocument));
- // //newDoc['ai_document'] = document_json;
- // };
-
@action
toggleToolLogs = (index: number) => {
this.expandedScratchpadIndex = this.expandedScratchpadIndex === index ? null : index;
@@ -105,10 +89,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
};
- // getAssistantResponse() {
- // return Docs.Create.MessageDocument(text, {});
- // }
-
@action
askGPT = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();
@@ -223,42 +203,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return highlight_doc;
};
- // @action
- // uploadLinks = async (linkedDocs: Doc[]) => {
- // if (this.isUploadingDocs) {
- // console.log('Initialization in progress, upload aborted.');
- // return;
- // }
- // const urls: string[] = linkedDocs.map(doc => CsvCast(doc.data, PDFCast(doc.data)).url.pathname);
- // const csvUrls: string[] = urls.filter(url => url.endsWith('.csv'));
- // console.log(this.assistantID, this.threadID, urls);
-
- // await Networking.PostToServer('/uploadPDFs', { file_path: urls[0] });
-
- // // linkedDocs.forEach((doc, i) => {
- // // doc[this.Document[Id] + '_ai_field_id'] = openaiFileIds[i];
- // // console.log('AI Field ID: ' + openaiFileIds[i]);
- // // });
-
- // // if (csvUrls.length > 0) {
- // // for (let i = 0; i < csvUrls.length; i++) {
- // // this.linkedCsvIDs.push(openaiFileIds[urls.indexOf(csvUrls[i])]);
- // // }
- // // console.log('linked csvs:' + this.linkedCsvIDs);
- // // await this.openai.beta.assistants.update(this.assistantID, {
- // // tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
- // // tool_resources: {
- // // file_search: {
- // // vector_store_ids: [this.vectorStoreID],
- // // },
- // // code_interpreter: {
- // // file_ids: this.linkedCsvIDs,
- // // },
- // // },
- // // });
- // // }
- // };
-
componentDidMount() {
this._props.setContentViewBox?.(this);
if (this.dataDoc.data) {
@@ -307,20 +251,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
});
}
- // case 'splice':
- // if ((change as any).addedCount > 0) {
- // // maybe check here if its already in the urls datadoc array so doesn't add twice
- // console.log((change as any).added as Doc[]);
- // this.addDocsToVectorstore((change as any).added as Doc[]);
- // }
- // // (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
- // break;
- // case 'update': // let oldValue = change.oldValue;
- // default:
-
@computed
get linkedDocs() {
- //return (CollectionFreeFormDocumentView.from(this._props.DocumentView?.())?._props.parent as CollectionFreeFormView)?.childDocs.filter(doc => doc != this.Document) ?? [];
return LinkManager.Instance.getAllRelatedLinks(this.Document)
.map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
.map(d => DocCast(d?.annotationOn, d))
@@ -334,6 +266,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
.map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
.map(d => DocCast(d?.annotationOn, d))
.filter(d => d)
+ .filter(d => d.summary)
.map((doc, index) => `${index + 1}) ${doc.summary}`)
.join('\n') + '\n'
);
@@ -375,19 +308,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}}>
<div className="messages">
{this.history.map((message, index) => (
- //<DocumentView key={index} Document={message} index={index} onFollowUpClick={this.handleFollowUpClick} onCitationClick={this.handleCitationClick} updateMessageCitations={this.updateMessageCitations} />
<MessageComponentBox key={index} message={message} index={index} onFollowUpClick={this.handleFollowUpClick} onCitationClick={this.handleCitationClick} updateMessageCitations={this.updateMessageCitations} />
))}
- {this.current_message && (
- <MessageComponentBox
- key={this.history.length}
- message={this.current_message}
- index={this.history.length}
- onFollowUpClick={this.handleFollowUpClick}
- onCitationClick={this.handleCitationClick}
- updateMessageCitations={this.updateMessageCitations}
- />
- )}
</div>
</div>
<form onSubmit={this.askGPT} className="chat-form">
diff --git a/src/client/views/nodes/ChatBox/MessageComponent.tsx b/src/client/views/nodes/ChatBox/MessageComponent.tsx
index fb4a56bc3..56fde8bb2 100644
--- a/src/client/views/nodes/ChatBox/MessageComponent.tsx
+++ b/src/client/views/nodes/ChatBox/MessageComponent.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { observer } from 'mobx-react';
-import ReactMarkdown from 'react-markdown';
import { AssistantMessage, Citation } from './types';
+import Markdown from 'react-markdown';
interface MessageComponentProps {
message: AssistantMessage;
@@ -14,7 +14,7 @@ interface MessageComponentProps {
const MessageComponentBox: React.FC<MessageComponentProps> = function ({ message, index, onFollowUpClick, onCitationClick, updateMessageCitations }) {
const renderContent = (content: string) => {
if (!message.citations || message.citations.length === 0) {
- return <ReactMarkdown>{content}</ReactMarkdown>;
+ return content;
}
const parts = [];
@@ -23,10 +23,9 @@ const MessageComponentBox: React.FC<MessageComponentProps> = function ({ message
message.citations.forEach((citation, idx) => {
const location = citation.text_location;
const textBefore = content.slice(lastIndex, location);
- parts.push(<ReactMarkdown key={`md-${idx}`}>{textBefore}</ReactMarkdown>);
const citationButton = (
<button
- key={`citation-${idx}`}
+ key={idx}
className="citation-button"
onClick={() => onCitationClick(citation)}
style={{
@@ -48,11 +47,11 @@ const MessageComponentBox: React.FC<MessageComponentProps> = function ({ message
{idx + 1}
</button>
);
- parts.push(citationButton);
+ parts.push(textBefore, citationButton);
lastIndex = location;
});
- parts.push(<ReactMarkdown key="md-last">{content.slice(lastIndex)}</ReactMarkdown>);
+ parts.push(content.slice(lastIndex));
return parts;
};
diff --git a/src/client/views/nodes/ChatBox/prompts.ts b/src/client/views/nodes/ChatBox/prompts.ts
index 5c353337f..4109ffc17 100644
--- a/src/client/views/nodes/ChatBox/prompts.ts
+++ b/src/client/views/nodes/ChatBox/prompts.ts
@@ -2,147 +2,170 @@
import { Tool } from './types';
-export function getReactPrompt(tools: Tool[], chatHistory: string): string {
+export function getReactPrompt(tools: Tool[], summaries: () => string, chatHistory: string): string {
const toolDescriptions = tools.map(tool => `${tool.name}:\n${tool.briefSummary}`).join('\n*****\n');
return `
- <system>
-You are an advanced AI assistant with access to various tools. Your task is to answer user queries accurately and efficiently. Follow these instructions meticulously:
-
-1. Operation Loop:
- You operate in a loop of Thought, Action, Observation, and Answer. Each iteration is numbered (step1, step2, etc.).
-
-2. Response Structure:
- a. Enclose each step in numbered XML tags: <step1>, <step2>, etc.
- b. Within each step, use the following tags as needed:
- <thought> - Your reasoning process
- <action> - The tool you choose to use
- <action_input> - Parameters for the chosen tool
- <answer> - Your final response (only in the last step)
-
-3. Detailed Process:
- a. Analyze the user's query carefully.
- b. Determine if a tool is necessary or if you can answer directly.
- c. If a tool is needed:
- - Select the most appropriate tool.
- - Use <action> to specify the tool.
- - Wait for action rules to be provided.
- - Provide tool parameters in <action_input>.
- - Wait for the observation from the tool.
- d. If no tool is needed, use the 'no_tool' action.
- e. Based on observations or your knowledge, formulate your answer.
- f. Provide the final answer in the <answer> tag, including follow-up questions.
-
-4. Available Tools:
-${toolDescriptions}
- no_tool: Use when no external tool is required to answer the question.
-
-5. Critical Rules:
- - Use tools ONLY when absolutely necessary for accurate answers.
- - Ensure ALL XML is valid, properly nested, and complete.
- - ALWAYS pause after <action> and <action_input> tags for system processing.
- - If the initial answer is inadequate, iterate through additional steps to refine it.
- - Utilize context from past conversations when relevant (provided in Chat History).
- - ALWAYS include your final response within a single <answer> tag.
-
-6. Answer Format:
- Your final <answer> tag must contain:
- - The complete answer to the user's query.
- - An array of EXACTLY 3 follow-up questions within <follow_up_questions> tags.
-
-7. Example Interaction:
-<query>What is the population of Tokyo, and how does it compare to New York City?</query>
-
-<step1>
- <thought>To answer this question accurately, I need to look up the current population figures for both Tokyo and New York City. I'll use the Wikipedia tool for this information.</thought>
- <action>wikipedia</action>
-</step1>
-
-[SYSTEM PAUSE]
-
-<action_rules>
- {
- "wikipedia": {
- "name": "wikipedia",
- "description": "Search Wikipedia and return a summary",
- "parameters": [
- {
- "title": {
- "type": "string",
- "description": "The title of the Wikipedia article to search",
- "required": "true"
- }
+<step1 type="system">
+ You are an advanced AI assistant with access to various tools. Your task is to answer user queries accurately and efficiently. Follow these instructions meticulously:
+
+ 1. Operation Loop:
+ You operate in a loop of Thought, Action, (STOP), *Action Rules*, Action Input, (STOP), *Observation*, and Answer. Each iteration is numbered (step1, step2, etc.).
+
+ 2. Response Structure:
+ a. Enclose each step in numbered XML tags: <step1>, <step2>, etc.
+ b. Within each step, use the following tags as needed:
+ <thought> - Your reasoning process
+ <action> - The tool you choose to use
+ <action_input> - Parameters for the chosen tool
+ <answer> - Your final response (only in the last step)
+ c. Stop after <action> and <action_input> tags for system processing. You will receive a user response after each time you stop in the form of either action rules or an observation.
+
+ 3. Detailed Process:
+ a. Analyze the user's query carefully.
+ b. Determine if a tool is necessary or if you can answer directly.
+ c. If a tool is needed:
+ c.1) Select the most appropriate tool.
+ c.2) Use <action> to specify the tool.
+ c.3) End your response in order for action rules to be provided.
+ c.4) Based on the action rules, provide tool parameters in <action_input>.
+ c.5) End your response again and wait for the observation from the tool.
+ d. If no tool is needed, use the 'no_tool' action but still follow the same structure.
+ e. Use the retrieval (RAG) tool ANYTIME the question may potentially (even if you are not sure) relate to one of the user's documents. Here are the summaries of the user's documents:
+ ${summaries()}
+ f. Based on observations or your knowledge, formulate your answer.
+ g. Provide the final answer in the <answer> tag, including follow-up questions.
+
+ 4. Available Tools:
+ ${toolDescriptions}
+ no_tool: Use when no external tool is required to answer the question. If a user document may relate to the query, do not use, and instead, use the RAG tool (even if unsure).
+
+ 5. !!!Critical Rules!!!:
+ - Use tools ONLY when absolutely necessary for accurate answers (except when query may relate to user documents, then use RAG tool ALWAYS to start).
+ - Only provide one step at a time and only write assistant steps. Do not skip steps. Do not provide multiple steps at once. Decide the step that you will provide based on previous steps taken.
+ - Ensure ALL XML is valid, properly nested, and complete.
+ - ALWAYS stop after <action> and <action_input> tags.
+ - If the initial answer is inadequate, iterate through additional steps to refine it.
+ - Utilize context from past conversations when relevant (provided in Chat History).
+ - ALWAYS include your final response within a single <answer> tag.
+
+ 6. Answer Format:
+ Your final <answer> tag must contain:
+ - The complete answer to the user's query.
+ - An array of EXACTLY 3 follow-up questions within <follow_up_questions> tags.
+
+ 7. Example Interaction (YOU ONLY OUTPUT THE ASSISTANT STEPS):
+ SYSTEM:
+ <step1 role="system">
+ ***SYSTEM MESSAGE ELIDED***
+ </step1>
+
+ USER:
+ <step2 role="user">
+ <query>What is the population of Tokyo, and how does it compare to New York City?</query>
+ </step2>
+
+ ASSISTANT:
+ <step3 role="assistant">
+ <thought>To answer this question accurately, I need to look up the current population figures for both Tokyo and New York City. I'll use the Wikipedia tool for this information.</thought>
+ <action>wikipedia</action>
+ </step3>
+
+ USER:
+ <step4 role="user">
+ <action_rules>
+ {
+ "wikipedia": {
+ "name": "wikipedia",
+ "description": "Search Wikipedia and return a summary",
+ "parameters": [
+ {
+ "title": {
+ "type": "string",
+ "description": "The title of the Wikipedia article to search",
+ "required": "true"
+ }
+ }
+ ]
}
- ]
- }
- }
-</action_rules>
-
-<step2>
- <action_input>
- <title>Tokyo</title>
- </action_input>
-</step2>
-
-[SYSTEM PAUSE]
-
-<observation>Tokyo is the capital and most populous prefecture of Japan. The Tokyo metropolitan area, which includes Tokyo and several surrounding prefectures, is the world's most populous metropolitan area with an estimated 37.468 million residents as of 2018.</observation>
-
-<step3>
- <thought>Now that I have information about Tokyo, I need to get information about New York City for comparison.</thought>
- <action>wikipedia</action>
-</step3>
-
-[SYSTEM PAUSE]
-
-<action_rules>
- {
- "wikipedia": {
- "name": "wikipedia",
- "description": "Search Wikipedia and return a summary",
- "parameters": [
- {
- "title": {
- "type": "string",
- "description": "The title of the Wikipedia article to search",
- "required": "true"
- }
+ }
+ </action_rules>
+ </step4>
+
+ ASSISTANT:
+ <step5 role="assistant">
+ <action_input>
+ <title>Tokyo</title>
+ </action_input>
+ </step5>
+
+ USER:
+ <step6 role="user">
+ <observation>Tokyo is the capital and most populous prefecture of Japan. The Tokyo metropolitan area, which includes Tokyo and several surrounding prefectures, is the world's most populous metropolitan area with an estimated 37.468 million residents as of 2018.</observation>
+ </step6>
+
+ ASSISTANT:
+ <step7 role="assistant">
+ <thought>Now that I have information about Tokyo, I need to get information about New York City for comparison.</thought>
+ <action>wikipedia</action>
+ </step7>
+
+ USER:
+ <step8 role="user">
+ <action_rules>
+ {
+ "wikipedia": {
+ "name": "wikipedia",
+ "description": "Search Wikipedia and return a summary",
+ "parameters": [
+ {
+ "title": {
+ "type": "string",
+ "description": "The title of the Wikipedia article to search",
+ "required": "true"
+ }
+ }
+ ]
}
- ]
- }
- }
-</action_rules>
-
-<step4>
- <action_input>
- <title>New York City</title>
- </action_input>
-</step4>
-
-[SYSTEM PAUSE]
-
-<observation>New York City is the most populous city in the United States. With an estimated 2020 population of 8,804,190 distributed over 300.46 square miles (778.2 km2), New York City is also the most densely populated major city in the United States.</observation>
-
-<step5>
- <thought>Now that I have the population information for both Tokyo and New York City, I can provide a comprehensive answer comparing the two.</thought>
- <answer>
- The population of Tokyo metropolitan area is approximately 37.468 million (as of 2018), while New York City has a population of about 8.8 million (as of 2020). Tokyo's population is significantly larger, more than four times that of New York City. It's important to note that the Tokyo figure refers to the broader metropolitan area, while the New York City figure is for the city proper. Even accounting for this difference, Tokyo remains substantially more populous than New York City.
-
- <follow_up_questions>
- <question>What factors contribute to Tokyo's significantly larger population compared to New York City?</question>
- <question>How do the population densities of Tokyo and New York City compare?</question>
- <question>What challenges do these megacities face due to their large populations?</question>
- </follow_up_questions>
- </answer>
-</step5>
-
-8. Chat History:
-${chatHistory}
-
-Remember to use this history for context when appropriate.
-
-Now, process the user's query and provide your response following the format and rules outlined above. Ensure your final answer is comprehensive and entirely contained within a single <answer> tag.
-</system>`;
+ }
+ </action_rules>
+ </step8>
+
+ ASSISTANT:
+ <step9 role="assistant">
+ <action_input>
+ <title>New York City</title>
+ </action_input>
+ </step9>
+
+ USER:
+ <step10 role="user">
+ <observation>New York City is the most populous city in the United States. With an estimated 2020 population of 8,804,190 distributed over 300.46 square miles (778.2 km2), New York City is also the most densely populated major city in the United States.</observation>
+ </step10>
+
+ ASSISTANT:
+ <step11 role="assistant">
+ <thought>Now that I have the population information for both Tokyo and New York City, I can provide a comprehensive answer comparing the two.</thought>
+ <answer>
+ The population of Tokyo metropolitan area is approximately 37.468 million (as of 2018), while New York City has a population of about 8.8 million (as of 2020). Tokyo's population is significantly larger, more than four times that of New York City. It's important to note that the Tokyo figure refers to the broader metropolitan area, while the New York City figure is for the city proper. Even accounting for this difference, Tokyo remains substantially more populous than New York City.
+
+ <follow_up_questions>
+ <question>What factors contribute to Tokyo's significantly larger population compared to New York City?</question>
+ <question>How do the population densities of Tokyo and New York City compare?</question>
+ <question>What challenges do these megacities face due to their large populations?</question>
+ </follow_up_questions>
+ </answer>
+ </step11>
+
+ 8. Chat History:
+ ${chatHistory}
+
+ Remember to use this history for context when appropriate.
+
+ Now, process the user's query and provide your response following the format and rules outlined above. Ensure your final answer is comprehensive and entirely contained within a single <answer> tag.
+
+ !!!IMPORTANT Very importantly, even if you use no tool or have an answer, follow the structure and output ONE step at a time. You will be provided with user steps and will output the appropriate single assistant step. FOLLOW THE STRUCTURE; Do not skip to the answer right away or skip steps.
+</step1>`;
}
export function getSummarizedChunksPrompt(chunks: string): string {
diff --git a/src/client/views/nodes/ChatBox/tools/RAGTool.ts b/src/client/views/nodes/ChatBox/tools/RAGTool.ts
index 0a4529974..5bc31dbab 100644
--- a/src/client/views/nodes/ChatBox/tools/RAGTool.ts
+++ b/src/client/views/nodes/ChatBox/tools/RAGTool.ts
@@ -5,10 +5,7 @@ import * as fs from 'fs';
import { Networking } from '../../../../Network';
export class RAGTool extends BaseTool<{ hypothetical_document_chunk: string }> {
- constructor(
- private vectorstore: Vectorstore,
- summaries: () => string
- ) {
+ constructor(private vectorstore: Vectorstore) {
super(
'rag',
'Perform a RAG search on user documents',
@@ -43,11 +40,7 @@ export class RAGTool extends BaseTool<{ hypothetical_document_chunk: string }> {
`Performs a RAG (Retrieval-Augmented Generation) search on user documents and returns a
set of document chunks (either images or text) that can be used to provide a grounded response based on
- user documents
-
- !!!IMPORTANT Use the RAG tool ANYTIME the question may potentially (even if you are not sure) relate to one of the user's documents.
- Here are the summaries of the user's documents:
- ${summaries()}`
+ user documents`
);
}