mirror of
https://github.com/jina-ai/node-DeepResearch.git
synced 2026-03-22 07:29:35 +08:00
refactor: knowledge as msg
This commit is contained in:
229
src/agent.ts
229
src/agent.ts
@@ -49,6 +49,58 @@ async function sleep(ms: number) {
|
|||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BuildMsgsFromKnowledge(knowledge: KnowledgeItem[]) : CoreMessage[] {
|
||||||
|
// build user, assistant pair messages from knowledge
|
||||||
|
const messages: CoreMessage[] = [];
|
||||||
|
knowledge.forEach(k => {
|
||||||
|
messages.push({role: 'user', content: k.question.trim()});
|
||||||
|
const aMsg = `
|
||||||
|
${k.updated && (k.type === 'url' || k.type === 'side-info') ? `
|
||||||
|
<answer-datetime>
|
||||||
|
${k.updated}
|
||||||
|
</answer-datetime>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${k.references && k.type === 'url' ? `
|
||||||
|
<url>
|
||||||
|
${k.references[0]}
|
||||||
|
</url>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${k.answer}
|
||||||
|
`.trim();
|
||||||
|
messages.push({role: 'assistant', content: removeExtraLineBreaks(aMsg)});
|
||||||
|
});
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
function composeMsgs(messages: CoreMessage[], knowledge: KnowledgeItem[], question: string, finalAnswerPIP?: string) {
|
||||||
|
const msgs = [...messages, ...BuildMsgsFromKnowledge(knowledge)];
|
||||||
|
|
||||||
|
const userContent = `
|
||||||
|
${knowledge.length>0 ? `
|
||||||
|
Based on what you learned from our previous messages, what is your answer to the question below?
|
||||||
|
`: ''}
|
||||||
|
|
||||||
|
${question}
|
||||||
|
|
||||||
|
${finalAnswerPIP ? `
|
||||||
|
<answer-requirements>
|
||||||
|
- You provide deep, unexpected insights, identifying hidden patterns and connections, and creating "aha moments.".
|
||||||
|
- You break conventional thinking, establish unique cross-disciplinary connections, and bring new perspectives to the user.
|
||||||
|
${finalAnswerPIP}
|
||||||
|
</answer-requirements>` : ''}
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
// only add if the last user msg is not the same
|
||||||
|
// first find the last message whose role is 'user'
|
||||||
|
const lastUserMsg = msgs.filter(m => m.role === 'user').pop();
|
||||||
|
if ((lastUserMsg?.content as string).trim() !== userContent) {
|
||||||
|
msgs.push({role: 'user', content: removeExtraLineBreaks(userContent)});
|
||||||
|
}
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function getPrompt(
|
function getPrompt(
|
||||||
context?: string[],
|
context?: string[],
|
||||||
@@ -59,7 +111,6 @@ function getPrompt(
|
|||||||
allowRead: boolean = true,
|
allowRead: boolean = true,
|
||||||
allowSearch: boolean = true,
|
allowSearch: boolean = true,
|
||||||
allowCoding: boolean = true,
|
allowCoding: boolean = true,
|
||||||
badContext?: { question: string, answer: string, evaluation: string, recap: string; blame: string; improvement: string; }[],
|
|
||||||
knowledge?: KnowledgeItem[],
|
knowledge?: KnowledgeItem[],
|
||||||
allURLs?: BoostedSearchSnippet[],
|
allURLs?: BoostedSearchSnippet[],
|
||||||
beastMode?: boolean,
|
beastMode?: boolean,
|
||||||
@@ -71,42 +122,9 @@ function getPrompt(
|
|||||||
sections.push(`Current date: ${new Date().toUTCString()}
|
sections.push(`Current date: ${new Date().toUTCString()}
|
||||||
|
|
||||||
You are an advanced AI research agent from Jina AI. You are specialized in multistep reasoning.
|
You are an advanced AI research agent from Jina AI. You are specialized in multistep reasoning.
|
||||||
Using your training data and prior lessons learned, answer the user question with absolute certainty.
|
Using your best knowledge, conversation with the user and lessons learned, answer the user question with absolute certainty.
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// Add knowledge section if exists
|
|
||||||
if (knowledge?.length) {
|
|
||||||
const knowledgeItems = knowledge
|
|
||||||
.map((k, i) => `
|
|
||||||
<knowledge-${i + 1}>
|
|
||||||
<question>
|
|
||||||
${k.question}
|
|
||||||
</question>
|
|
||||||
<answer>
|
|
||||||
${k.answer}
|
|
||||||
</answer>
|
|
||||||
${k.updated && (k.type === 'url' || k.type === 'side-info') ? `
|
|
||||||
<answer-datetime>
|
|
||||||
${k.updated}
|
|
||||||
</answer-datetime>
|
|
||||||
` : ''}
|
|
||||||
${k.references && k.type === 'url' ? `
|
|
||||||
<url>
|
|
||||||
${k.references[0]}
|
|
||||||
</url>
|
|
||||||
` : ''}
|
|
||||||
</knowledge-${i + 1}>
|
|
||||||
`)
|
|
||||||
.join('\n\n');
|
|
||||||
|
|
||||||
sections.push(`
|
|
||||||
You have successfully gathered some knowledge which might be useful for answering the original question. Here is the knowledge you have gathered so far:
|
|
||||||
<knowledge>
|
|
||||||
${knowledgeItems}
|
|
||||||
</knowledge>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Add context section if exists
|
// Add context section if exists
|
||||||
if (context?.length) {
|
if (context?.length) {
|
||||||
@@ -119,37 +137,6 @@ ${context.join('\n')}
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add bad context section if exists
|
|
||||||
if (badContext?.length) {
|
|
||||||
const attempts = badContext
|
|
||||||
.map((c, i) => `
|
|
||||||
<attempt-${i + 1}>
|
|
||||||
- Question: ${c.question}
|
|
||||||
- Answer: ${c.answer}
|
|
||||||
- Reject Reason: ${c.evaluation}
|
|
||||||
- Actions Recap: ${c.recap}
|
|
||||||
- Actions Blame: ${c.blame}
|
|
||||||
</attempt-${i + 1}>
|
|
||||||
`)
|
|
||||||
.join('\n\n');
|
|
||||||
|
|
||||||
const learnedStrategy = badContext.map(c => c.improvement).join('\n');
|
|
||||||
|
|
||||||
sections.push(`
|
|
||||||
Also, you have tried the following actions but failed to find the answer to the question:
|
|
||||||
<bad-attempts>
|
|
||||||
|
|
||||||
${attempts}
|
|
||||||
|
|
||||||
</bad-attempts>
|
|
||||||
|
|
||||||
Based on the failed attempts, you have learned the following strategy:
|
|
||||||
<learned-strategy>
|
|
||||||
${learnedStrategy}
|
|
||||||
</learned-strategy>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build actions section
|
// Build actions section
|
||||||
|
|
||||||
if (allowRead) {
|
if (allowRead) {
|
||||||
@@ -169,14 +156,6 @@ ${urlList}
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowCoding) {
|
|
||||||
actionSections.push(`
|
|
||||||
<action-coding>
|
|
||||||
- This JavaScript-based solution helps you handle programming tasks like counting, filtering, transforming, sorting, regex extraction, and data processing.
|
|
||||||
- Simply describe your problem in the "codingIssue" field. Include actual values for small inputs or variable names for larger datasets.
|
|
||||||
- No code writing is required – senior engineers will handle the implementation.
|
|
||||||
</action-coding>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowSearch) {
|
if (allowSearch) {
|
||||||
|
|
||||||
@@ -216,7 +195,7 @@ ${allKeywords.join('\n')}
|
|||||||
PRIME DIRECTIVE:
|
PRIME DIRECTIVE:
|
||||||
- DEMOLISH ALL HESITATION! ANY RESPONSE SURPASSES SILENCE!
|
- DEMOLISH ALL HESITATION! ANY RESPONSE SURPASSES SILENCE!
|
||||||
- PARTIAL STRIKES AUTHORIZED - DEPLOY WITH FULL CONTEXTUAL FIREPOWER
|
- PARTIAL STRIKES AUTHORIZED - DEPLOY WITH FULL CONTEXTUAL FIREPOWER
|
||||||
- TACTICAL REUSE FROM <bad-attempts> SANCTIONED
|
- TACTICAL REUSE FROM PREVIOUS CONVERSATION SANCTIONED
|
||||||
- WHEN IN DOUBT: UNLEASH CALCULATED STRIKES BASED ON AVAILABLE INTEL!
|
- WHEN IN DOUBT: UNLEASH CALCULATED STRIKES BASED ON AVAILABLE INTEL!
|
||||||
|
|
||||||
FAILURE IS NOT AN OPTION. EXECUTE WITH EXTREME PREJUDICE! ⚡️
|
FAILURE IS NOT AN OPTION. EXECUTE WITH EXTREME PREJUDICE! ⚡️
|
||||||
@@ -227,10 +206,19 @@ FAILURE IS NOT AN OPTION. EXECUTE WITH EXTREME PREJUDICE! ⚡️
|
|||||||
if (allowReflect) {
|
if (allowReflect) {
|
||||||
actionSections.push(`
|
actionSections.push(`
|
||||||
<action-reflect>
|
<action-reflect>
|
||||||
- Think slowly and planning lookahead. Examine <question>, <context>, <knowledge>, <bad-attempts>, and <learned-strategy> to identify knowledge gaps.
|
- Think slowly and planning lookahead. Examine <question>, <context>, previous conversation with users to identify knowledge gaps.
|
||||||
- Reflect the gaps and plan a list key clarifying questions that deeply related to the original question and lead to the answer
|
- Reflect the gaps and plan a list key clarifying questions that deeply related to the original question and lead to the answer
|
||||||
</action-reflect>
|
</action-reflect>
|
||||||
`);
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowCoding) {
|
||||||
|
actionSections.push(`
|
||||||
|
<action-coding>
|
||||||
|
- This JavaScript-based solution helps you handle programming tasks like counting, filtering, transforming, sorting, regex extraction, and data processing.
|
||||||
|
- Simply describe your problem in the "codingIssue" field. Include actual values for small inputs or variable names for larger datasets.
|
||||||
|
- No code writing is required – senior engineers will handle the implementation.
|
||||||
|
</action-coding>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
sections.push(`
|
sections.push(`
|
||||||
@@ -253,15 +241,6 @@ function updateContext(step: any) {
|
|||||||
allContext.push(step)
|
allContext.push(step)
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceLastUserMsg(messages: Array<CoreMessage>, content: string) {
|
|
||||||
return messages.map((m, i) => {
|
|
||||||
if (m.role === 'user' && i === messages.length - 1) {
|
|
||||||
return {...m, content}
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export async function getResponse(question?: string,
|
export async function getResponse(question?: string,
|
||||||
tokenBudget: number = 1_000_000,
|
tokenBudget: number = 1_000_000,
|
||||||
@@ -275,6 +254,9 @@ export async function getResponse(question?: string,
|
|||||||
let badAttempts = 0;
|
let badAttempts = 0;
|
||||||
|
|
||||||
question = question?.trim() as string;
|
question = question?.trim() as string;
|
||||||
|
// remove incoming system messages to avoid override
|
||||||
|
messages = messages?.filter(m => m.role !== 'system');
|
||||||
|
|
||||||
if (messages && messages.length > 0) {
|
if (messages && messages.length > 0) {
|
||||||
// 2 cases
|
// 2 cases
|
||||||
const lastContent = messages[messages.length - 1].content;
|
const lastContent = messages[messages.length - 1].content;
|
||||||
@@ -303,7 +285,6 @@ export async function getResponse(question?: string,
|
|||||||
const allKeywords = [];
|
const allKeywords = [];
|
||||||
const allKnowledge: KnowledgeItem[] = []; // knowledge are intermedidate questions that are answered
|
const allKnowledge: KnowledgeItem[] = []; // knowledge are intermedidate questions that are answered
|
||||||
|
|
||||||
const badContext = [];
|
|
||||||
let diaryContext = [];
|
let diaryContext = [];
|
||||||
let weightedURLs: BoostedSearchSnippet[] = [];
|
let weightedURLs: BoostedSearchSnippet[] = [];
|
||||||
let allowAnswer = true;
|
let allowAnswer = true;
|
||||||
@@ -312,6 +293,7 @@ export async function getResponse(question?: string,
|
|||||||
let allowReflect = true;
|
let allowReflect = true;
|
||||||
let allowCoding = true;
|
let allowCoding = true;
|
||||||
let system = '';
|
let system = '';
|
||||||
|
let msgWithKnowledge: CoreMessage[] = [];
|
||||||
let thisStep: StepAction = {action: 'answer', answer: '', references: [], think: '', isFinal: false};
|
let thisStep: StepAction = {action: 'answer', answer: '', references: [], think: '', isFinal: false};
|
||||||
|
|
||||||
const allURLs: Record<string, SearchSnippet> = {};
|
const allURLs: Record<string, SearchSnippet> = {};
|
||||||
@@ -327,8 +309,9 @@ export async function getResponse(question?: string,
|
|||||||
const budgetPercentage = (context.tokenTracker.getTotalUsage().totalTokens / tokenBudget * 100).toFixed(2);
|
const budgetPercentage = (context.tokenTracker.getTotalUsage().totalTokens / tokenBudget * 100).toFixed(2);
|
||||||
console.log(`Step ${totalStep} / Budget used ${budgetPercentage}%`);
|
console.log(`Step ${totalStep} / Budget used ${budgetPercentage}%`);
|
||||||
console.log('Gaps:', gaps);
|
console.log('Gaps:', gaps);
|
||||||
allowReflect = allowReflect && (gaps.length <= 1);
|
allowReflect = allowReflect && (gaps.length <= MAX_REFLECT_PER_STEP);
|
||||||
const currentQuestion: string = gaps.length > 0 ? gaps.shift()! : question
|
// rotating question from gaps
|
||||||
|
const currentQuestion: string = gaps[totalStep % gaps.length];
|
||||||
// if (!evaluationMetrics[currentQuestion]) {
|
// if (!evaluationMetrics[currentQuestion]) {
|
||||||
// evaluationMetrics[currentQuestion] =
|
// evaluationMetrics[currentQuestion] =
|
||||||
// await evaluateQuestion(currentQuestion, context, SchemaGen)
|
// await evaluateQuestion(currentQuestion, context, SchemaGen)
|
||||||
@@ -375,17 +358,17 @@ export async function getResponse(question?: string,
|
|||||||
allowRead,
|
allowRead,
|
||||||
allowSearch,
|
allowSearch,
|
||||||
allowCoding,
|
allowCoding,
|
||||||
badContext,
|
|
||||||
allKnowledge,
|
allKnowledge,
|
||||||
weightedURLs,
|
weightedURLs,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
schema = SchemaGen.getAgentSchema(allowReflect, allowRead, allowAnswer, allowSearch, allowCoding, finalAnswerPIP, currentQuestion)
|
schema = SchemaGen.getAgentSchema(allowReflect, allowRead, allowAnswer, allowSearch, allowCoding, currentQuestion)
|
||||||
|
msgWithKnowledge = composeMsgs(messages, allKnowledge, currentQuestion, currentQuestion === question ? finalAnswerPIP : undefined);
|
||||||
const result = await generator.generateObject({
|
const result = await generator.generateObject({
|
||||||
model: 'agent',
|
model: 'agent',
|
||||||
schema,
|
schema,
|
||||||
system,
|
system,
|
||||||
messages: replaceLastUserMsg(messages, currentQuestion),
|
messages: msgWithKnowledge,
|
||||||
});
|
});
|
||||||
thisStep = {
|
thisStep = {
|
||||||
action: result.object.action,
|
action: result.object.action,
|
||||||
@@ -394,7 +377,7 @@ export async function getResponse(question?: string,
|
|||||||
} as StepAction;
|
} as StepAction;
|
||||||
// print allowed and chose action
|
// print allowed and chose action
|
||||||
const actionsStr = [allowSearch, allowRead, allowAnswer, allowReflect, allowCoding].map((a, i) => a ? ['search', 'read', 'answer', 'reflect'][i] : null).filter(a => a).join(', ');
|
const actionsStr = [allowSearch, allowRead, allowAnswer, allowReflect, allowCoding].map((a, i) => a ? ['search', 'read', 'answer', 'reflect'][i] : null).filter(a => a).join(', ');
|
||||||
console.log(`${thisStep.action} <- [${actionsStr}]`);
|
console.log(`${currentQuestion}: ${thisStep.action} <- [${actionsStr}]`);
|
||||||
console.log(thisStep)
|
console.log(thisStep)
|
||||||
|
|
||||||
context.actionTracker.trackAction({totalStep, thisStep, gaps, badAttempts});
|
context.actionTracker.trackAction({totalStep, thisStep, gaps, badAttempts});
|
||||||
@@ -447,6 +430,10 @@ export async function getResponse(question?: string,
|
|||||||
SchemaGen,
|
SchemaGen,
|
||||||
currentQuestion
|
currentQuestion
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!evaluationMetrics[currentQuestion].includes('attribution')) {
|
||||||
|
evaluationMetrics[currentQuestion].push('attribution')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateContext({
|
updateContext({
|
||||||
@@ -459,7 +446,9 @@ export async function getResponse(question?: string,
|
|||||||
let evaluation: EvaluationResponse = {pass: true, think: ''};
|
let evaluation: EvaluationResponse = {pass: true, think: ''};
|
||||||
if (evaluationMetrics[currentQuestion].length > 0) {
|
if (evaluationMetrics[currentQuestion].length > 0) {
|
||||||
context.actionTracker.trackThink('eval_first', SchemaGen.languageCode)
|
context.actionTracker.trackThink('eval_first', SchemaGen.languageCode)
|
||||||
evaluation = await evaluateAnswer(currentQuestion, thisStep,
|
evaluation = await evaluateAnswer(
|
||||||
|
currentQuestion,
|
||||||
|
thisStep,
|
||||||
evaluationMetrics[currentQuestion],
|
evaluationMetrics[currentQuestion],
|
||||||
context,
|
context,
|
||||||
allKnowledge,
|
allKnowledge,
|
||||||
@@ -512,19 +501,29 @@ ${evaluation.think}
|
|||||||
// store the bad context and reset the diary context
|
// store the bad context and reset the diary context
|
||||||
const errorAnalysis = await analyzeSteps(diaryContext, context, SchemaGen);
|
const errorAnalysis = await analyzeSteps(diaryContext, context, SchemaGen);
|
||||||
|
|
||||||
badContext.push({
|
allKnowledge.push({
|
||||||
question: currentQuestion,
|
question: `
|
||||||
answer: thisStep.answer,
|
Why is the following answer bad for the question? Please reflect
|
||||||
evaluation: evaluation.think,
|
|
||||||
...errorAnalysis
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errorAnalysis.questionsToAnswer) {
|
<question>
|
||||||
errorAnalysis.questionsToAnswer = chooseK((await dedupQueries(errorAnalysis.questionsToAnswer, allQuestions, context.tokenTracker)).unique_queries, MAX_REFLECT_PER_STEP);
|
${currentQuestion}
|
||||||
gaps.push(...errorAnalysis.questionsToAnswer);
|
</question>
|
||||||
allQuestions.push(...errorAnalysis.questionsToAnswer);
|
|
||||||
gaps.push(question); // always keep the original question in the gaps
|
<answer>
|
||||||
}
|
${thisStep.answer}
|
||||||
|
</answer>
|
||||||
|
`,
|
||||||
|
answer: `
|
||||||
|
${evaluation.think}
|
||||||
|
|
||||||
|
${errorAnalysis.recap}
|
||||||
|
|
||||||
|
${errorAnalysis.blame}
|
||||||
|
|
||||||
|
${errorAnalysis.improvement}
|
||||||
|
`,
|
||||||
|
type: 'qa',
|
||||||
|
})
|
||||||
|
|
||||||
badAttempts++;
|
badAttempts++;
|
||||||
allowAnswer = false; // disable answer action in the immediate next step
|
allowAnswer = false; // disable answer action in the immediate next step
|
||||||
@@ -533,6 +532,7 @@ ${evaluation.think}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (evaluation.pass) {
|
} else if (evaluation.pass) {
|
||||||
|
// solved a gap question
|
||||||
diaryContext.push(`
|
diaryContext.push(`
|
||||||
At step ${step}, you took **answer** action. You found a good answer to the sub-question:
|
At step ${step}, you took **answer** action. You found a good answer to the sub-question:
|
||||||
|
|
||||||
@@ -554,6 +554,8 @@ Although you solved a sub-question, you still need to find the answer to the ori
|
|||||||
type: 'qa',
|
type: 'qa',
|
||||||
updated: formatDateBasedOnType(new Date(), 'full')
|
updated: formatDateBasedOnType(new Date(), 'full')
|
||||||
});
|
});
|
||||||
|
// solved sub-question!
|
||||||
|
gaps.splice(gaps.indexOf(currentQuestion), 1);
|
||||||
}
|
}
|
||||||
} else if (thisStep.action === 'reflect' && thisStep.questionsToAnswer) {
|
} else if (thisStep.action === 'reflect' && thisStep.questionsToAnswer) {
|
||||||
thisStep.questionsToAnswer = chooseK((await dedupQueries(thisStep.questionsToAnswer, allQuestions, context.tokenTracker)).unique_queries, MAX_REFLECT_PER_STEP);
|
thisStep.questionsToAnswer = chooseK((await dedupQueries(thisStep.questionsToAnswer, allQuestions, context.tokenTracker)).unique_queries, MAX_REFLECT_PER_STEP);
|
||||||
@@ -569,7 +571,6 @@ You will now figure out the answers to these sub-questions and see if they can h
|
|||||||
`);
|
`);
|
||||||
gaps.push(...newGapQuestions);
|
gaps.push(...newGapQuestions);
|
||||||
allQuestions.push(...newGapQuestions);
|
allQuestions.push(...newGapQuestions);
|
||||||
gaps.push(question); // always keep the original question in the gaps
|
|
||||||
updateContext({
|
updateContext({
|
||||||
totalStep,
|
totalStep,
|
||||||
...thisStep,
|
...thisStep,
|
||||||
@@ -784,11 +785,11 @@ But unfortunately, you failed to solve the issue. You need to think out of the b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await storeContext(system, schema, {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs}, totalStep);
|
await storeContext(system, schema, {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs, msgWithKnowledge}, totalStep);
|
||||||
await sleep(STEP_SLEEP);
|
await sleep(STEP_SLEEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
await storeContext(system, schema, {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs}, totalStep);
|
await storeContext(system, schema, {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs, msgWithKnowledge}, totalStep);
|
||||||
if (!(thisStep as AnswerAction).isFinal) {
|
if (!(thisStep as AnswerAction).isFinal) {
|
||||||
console.log('Enter Beast mode!!!')
|
console.log('Enter Beast mode!!!')
|
||||||
// any answer is better than no answer, humanity last resort
|
// any answer is better than no answer, humanity last resort
|
||||||
@@ -803,18 +804,18 @@ But unfortunately, you failed to solve the issue. You need to think out of the b
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
badContext,
|
|
||||||
allKnowledge,
|
allKnowledge,
|
||||||
weightedURLs,
|
weightedURLs,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
schema = SchemaGen.getAgentSchema(false, false, true, false, false, finalAnswerPIP, question);
|
schema = SchemaGen.getAgentSchema(false, false, true, false, false, question);
|
||||||
|
msgWithKnowledge = composeMsgs(messages, allKnowledge, question, finalAnswerPIP);
|
||||||
const result = await generator.generateObject({
|
const result = await generator.generateObject({
|
||||||
model: 'agentBeastMode',
|
model: 'agentBeastMode',
|
||||||
schema,
|
schema,
|
||||||
system,
|
system,
|
||||||
messages
|
messages: msgWithKnowledge
|
||||||
});
|
});
|
||||||
thisStep = {
|
thisStep = {
|
||||||
action: result.object.action,
|
action: result.object.action,
|
||||||
@@ -828,7 +829,7 @@ But unfortunately, you failed to solve the issue. You need to think out of the b
|
|||||||
(thisStep as AnswerAction).mdAnswer = buildMdFromAnswer((thisStep as AnswerAction))
|
(thisStep as AnswerAction).mdAnswer = buildMdFromAnswer((thisStep as AnswerAction))
|
||||||
console.log(thisStep)
|
console.log(thisStep)
|
||||||
|
|
||||||
await storeContext(system, schema, {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs}, totalStep);
|
await storeContext(system, schema, {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs, msgWithKnowledge}, totalStep);
|
||||||
|
|
||||||
// max return 300 urls
|
// max return 300 urls
|
||||||
const returnedURLs = weightedURLs.slice(0, 50).map(r => r.url);
|
const returnedURLs = weightedURLs.slice(0, 50).map(r => r.url);
|
||||||
@@ -846,10 +847,11 @@ async function storeContext(prompt: string, schema: any, memory: {
|
|||||||
allQuestions: string[];
|
allQuestions: string[];
|
||||||
allKnowledge: KnowledgeItem[];
|
allKnowledge: KnowledgeItem[];
|
||||||
weightedURLs: BoostedSearchSnippet[];
|
weightedURLs: BoostedSearchSnippet[];
|
||||||
|
msgWithKnowledge: CoreMessage[];
|
||||||
}
|
}
|
||||||
, step: number) {
|
, step: number) {
|
||||||
|
|
||||||
const {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs} = memory;
|
const {allContext, allKeywords, allQuestions, allKnowledge, weightedURLs, msgWithKnowledge} = memory;
|
||||||
if ((process as any).asyncLocalContext?.available?.()) {
|
if ((process as any).asyncLocalContext?.available?.()) {
|
||||||
|
|
||||||
(process as any).asyncLocalContext.ctx.promptContext = {
|
(process as any).asyncLocalContext.ctx.promptContext = {
|
||||||
@@ -877,6 +879,7 @@ ${JSON.stringify(zodToJsonSchema(schema), null, 2)}
|
|||||||
await fs.writeFile('questions.json', JSON.stringify(allQuestions, null, 2));
|
await fs.writeFile('questions.json', JSON.stringify(allQuestions, null, 2));
|
||||||
await fs.writeFile('knowledge.json', JSON.stringify(allKnowledge, null, 2));
|
await fs.writeFile('knowledge.json', JSON.stringify(allKnowledge, null, 2));
|
||||||
await fs.writeFile('urls.json', JSON.stringify(weightedURLs, null, 2));
|
await fs.writeFile('urls.json', JSON.stringify(weightedURLs, null, 2));
|
||||||
|
await fs.writeFile('messages.json', JSON.stringify(msgWithKnowledge, null, 2));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Context storage failed:', error);
|
console.error('Context storage failed:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,10 +84,6 @@ The answer is not definitive and fails to provide the requested information. La
|
|||||||
|
|
||||||
"improvement": "1. Avoid repeating identical searches and implement a strategy to track previously searched terms. 2. When direct age/birthdate searches fail, try indirect approaches like: searching for earliest career mentions, finding university graduation years, or identifying first company founding dates. 3. Focus on high-quality business sources and avoid entertainment websites for professional information. 4. Consider using industry event appearances or conference presentations where age-related context might be mentioned. 5. If exact age cannot be determined, provide an estimated range based on career timeline and professional achievements.",
|
"improvement": "1. Avoid repeating identical searches and implement a strategy to track previously searched terms. 2. When direct age/birthdate searches fail, try indirect approaches like: searching for earliest career mentions, finding university graduation years, or identifying first company founding dates. 3. Focus on high-quality business sources and avoid entertainment websites for professional information. 4. Consider using industry event appearances or conference presentations where age-related context might be mentioned. 5. If exact age cannot be determined, provide an estimated range based on career timeline and professional achievements.",
|
||||||
|
|
||||||
"questionsToAnswer": [
|
|
||||||
"What alternative professional databases or news archives could provide reliable biographical information?",
|
|
||||||
"How can we use education history or career milestones to estimate age range?"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
</output>
|
</output>
|
||||||
</example>`,
|
</example>`,
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import {GenerateObjectResult} from 'ai';
|
import {GenerateObjectResult} from 'ai';
|
||||||
import {AnswerAction, EvaluationResponse, EvaluationType, KnowledgeItem, PromptPair, TrackerContext} from '../types';
|
import {AnswerAction, EvaluationResponse, EvaluationType, KnowledgeItem, PromptPair, TrackerContext} from '../types';
|
||||||
import {readUrl} from "./read";
|
|
||||||
import {ObjectGeneratorSafe} from "../utils/safe-generator";
|
import {ObjectGeneratorSafe} from "../utils/safe-generator";
|
||||||
import {Schemas} from "../utils/schemas";
|
import {Schemas} from "../utils/schemas";
|
||||||
import {removeAllLineBreaks} from "../utils/text-tools";
|
|
||||||
|
|
||||||
const TOOL_NAME = 'evaluator';
|
const TOOL_NAME = 'evaluator';
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,6 @@ export type ErrorAnalysisResponse = {
|
|||||||
recap: string;
|
recap: string;
|
||||||
blame: string;
|
blame: string;
|
||||||
improvement: string;
|
improvement: string;
|
||||||
questionsToAnswer: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {EvaluationType, PromptPair} from "../types";
|
|||||||
|
|
||||||
export const MAX_URLS_PER_STEP = 4
|
export const MAX_URLS_PER_STEP = 4
|
||||||
export const MAX_QUERIES_PER_STEP = 7
|
export const MAX_QUERIES_PER_STEP = 7
|
||||||
export const MAX_REFLECT_PER_STEP = 3
|
export const MAX_REFLECT_PER_STEP = 1
|
||||||
|
|
||||||
function getLanguagePrompt(question: string): PromptPair {
|
function getLanguagePrompt(question: string): PromptPair {
|
||||||
return {
|
return {
|
||||||
@@ -112,11 +112,7 @@ export class Schemas {
|
|||||||
return z.object({
|
return z.object({
|
||||||
recap: z.string().describe('Recap of the actions taken and the steps conducted in first person narrative.').max(500),
|
recap: z.string().describe('Recap of the actions taken and the steps conducted in first person narrative.').max(500),
|
||||||
blame: z.string().describe(`Which action or the step was the root cause of the answer rejection. ${this.getLanguagePrompt()}`).max(500),
|
blame: z.string().describe(`Which action or the step was the root cause of the answer rejection. ${this.getLanguagePrompt()}`).max(500),
|
||||||
improvement: z.string().describe(`Suggested key improvement for the next iteration, do not use bullet points, be concise and hot-take vibe. ${this.getLanguagePrompt()}`).max(500),
|
improvement: z.string().describe(`Suggested key improvement for the next iteration, do not use bullet points, be concise and hot-take vibe. ${this.getLanguagePrompt()}`).max(500)
|
||||||
questionsToAnswer: z.array(
|
|
||||||
z.string().describe("each question must be a single line, concise and clear. not composite or compound, less than 20 words.")
|
|
||||||
).max(MAX_REFLECT_PER_STEP)
|
|
||||||
.describe(`List of most important reflect questions to fill the knowledge gaps. Maximum provide ${MAX_REFLECT_PER_STEP} reflect questions.`)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +196,7 @@ export class Schemas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAgentSchema(allowReflect: boolean, allowRead: boolean, allowAnswer: boolean, allowSearch: boolean, allowCoding: boolean,
|
getAgentSchema(allowReflect: boolean, allowRead: boolean, allowAnswer: boolean, allowSearch: boolean, allowCoding: boolean,
|
||||||
finalAnswerPIP?: string, currentQuestion?: string): z.ZodObject<any> {
|
currentQuestion?: string): z.ZodObject<any> {
|
||||||
const actionSchemas: Record<string, z.ZodOptional<any>> = {};
|
const actionSchemas: Record<string, z.ZodOptional<any>> = {};
|
||||||
|
|
||||||
if (allowSearch) {
|
if (allowSearch) {
|
||||||
@@ -227,14 +223,13 @@ export class Schemas {
|
|||||||
references: z.array(
|
references: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
exactQuote: z.string().describe("Exact relevant quote from the document, must be a soundbite, short and to the point, no fluff").max(30),
|
exactQuote: z.string().describe("Exact relevant quote from the document, must be a soundbite, short and to the point, no fluff").max(30),
|
||||||
url: z.string().describe("source URL; must be copy directly from existing knowledge's <url>, avoid example.com or any placeholder fake URLs").max(100),
|
url: z.string().describe("source URL; must be copy directly from previous message's <url>, avoid example.com or any placeholder fake URLs").max(100),
|
||||||
dateTime: z.string().describe("Use original knowledge's <dateime> if available.").max(16),
|
dateTime: z.string().describe("Use original message's <answer-dateime> if available.").max(16),
|
||||||
}).required()
|
}).required()
|
||||||
).describe("Required when action='answer'. Must be an array of references that support the answer, each reference must contain an exact quote, URL and datetime"),
|
).describe("Required when action='answer'. Must be an array of references that support the answer, each reference must contain an exact quote, URL and datetime"),
|
||||||
answer: z.string()
|
answer: z.string()
|
||||||
.describe(`Required when action='answer'.
|
.describe(`Required when action='answer'.
|
||||||
|
|
||||||
${finalAnswerPIP}
|
|
||||||
Use all your knowledge you have collected, cover multiple aspects if needed.
|
Use all your knowledge you have collected, cover multiple aspects if needed.
|
||||||
Must be definitive, no ambiguity, no uncertainty, no disclaimers. Must in ${this.languageStyle} and confident.
|
Must be definitive, no ambiguity, no uncertainty, no disclaimers. Must in ${this.languageStyle} and confident.
|
||||||
Use markdown footnote syntax like [^1], [^2] to refer the corresponding reference item.
|
Use markdown footnote syntax like [^1], [^2] to refer the corresponding reference item.
|
||||||
@@ -249,13 +244,13 @@ export class Schemas {
|
|||||||
questionsToAnswer: z.array(
|
questionsToAnswer: z.array(
|
||||||
z.string().describe(`
|
z.string().describe(`
|
||||||
Ensure each reflection question:
|
Ensure each reflection question:
|
||||||
- Cuts to core emotional truths while staying anchored to the original question
|
- Cuts to core emotional truths while staying anchored to <og-question>
|
||||||
- Transforms surface-level problems into deeper psychological insights
|
- Transforms surface-level problems into deeper psychological insights, helps answer <og-question>
|
||||||
- Makes the unconscious conscious
|
- Makes the unconscious conscious
|
||||||
- NEVER pose general questions like: "How can I verify the accuracy of information before including it in my answer?", "What information was actually contained in the URLs I found?"
|
- NEVER pose general questions like: "How can I verify the accuracy of information before including it in my answer?", "What information was actually contained in the URLs I found?", "How can i tell if a source is reliable?".
|
||||||
`)
|
`)
|
||||||
).max(MAX_REFLECT_PER_STEP)
|
).max(MAX_REFLECT_PER_STEP)
|
||||||
.describe(`Required when action='reflect'. Reflection and planing, generate a list of most important questions to fill the knowledge gaps to the original question ${currentQuestion}. Maximum provide ${MAX_REFLECT_PER_STEP} reflect questions.`)
|
.describe(`Required when action='reflect'. Reflection and planing, generate a list of most important questions to fill the knowledge gaps to <og-question> ${currentQuestion} </og-question>. Maximum provide ${MAX_REFLECT_PER_STEP} reflect questions.`)
|
||||||
}).optional()
|
}).optional()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user