Skip to content

Commit

Permalink
Implement forceFinalAnswer Logic and Update Test Cases (#22)
Browse files Browse the repository at this point in the history
### PR Description:
This PR introduces the `forceFinalAnswer` logic, which triggers a final
answer from the AI agent when approaching the maximum iteration limit.
This feature ensures that the agent utilizes all gathered information to
provide a decisive response before the session expires. Additionally,
the PR includes several updates and fixes:

- **Fix in Self-Question Logic**: Revised the handling of
`self_question` actions to ensure they are passed back correctly to the
LLM for a coherent follow-up.
- **Gemini Test Cases**: Added new test cases to the `sports_news` team
scenarios, enhancing our test coverage.
- **Snapshot Updates**: Updated snapshots to reflect the new
`forceFinalAnswer` logic, ensuring our tests remain accurate and
reliable.
  • Loading branch information
darielnoel authored Aug 9, 2024
2 parents 51d1db7 + 92321b3 commit 6f888ac
Show file tree
Hide file tree
Showing 13 changed files with 27,508 additions and 4 deletions.
8 changes: 8 additions & 0 deletions playground/react/src/stories/TripPlanningTeam.stories.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AgentsBoardDebugger from '../AgentsBoardDebugger';
import teamOpenAI from '../teams/trip_planning/openai';
import teamGemini from '../teams/trip_planning/gemini';


// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
Expand All @@ -15,3 +16,10 @@ export const withOpenAI = {
title: 'With OpenAI Model'
},
};

export const withGemini = {
args: {
team: teamGemini,
title: 'With Gemini Model'
},
};
3 changes: 2 additions & 1 deletion playground/react/src/teams/sport_news/gemini.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ const team = new Team({
ANTHROPIC_API_KEY: import.meta.env.VITE_ANTHROPIC_API_KEY,
MISTRAL_API_KEY: import.meta.env.VITE_MISTRAL_API_KEY,
GOOGLE_API_KEY: import.meta.env.VITE_GOOGLE_API_KEY
}
},
logLevel: 'debug'
// Results of the latest UEFA Champions League match.
});

Expand Down
106 changes: 106 additions & 0 deletions playground/react/src/teams/trip_planning/gemini.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Agent, Task, Team } from 'agenticjs';

import { TavilySearchResults } from '@langchain/community/tools/tavily_search';

// Define tools
const searchInternet = new TavilySearchResults({
maxResults: 3,
// apiKey: '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',
apiKey: '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',
});

// Define agents with exact roles, goals, and backgrounds from Python example
const citySelectorAgent = new Agent({
name: 'Peter Atlas',
role: 'City Selection Expert',
goal: 'Select the best city based on weather, season, and prices',
background: 'An expert in analyzing travel data to pick ideal destinations',
type: 'ReactChampionAgent',
tools: [searchInternet],
maxIterations: 20,
llmConfig: {
provider: "google",
model: "gemini-1.5-pro",
}
});

const localExpertAgent = new Agent({
name: 'Sophia Lore',
role: 'Local Expert at this city',
goal: 'Provide the BEST insights about the selected city',
background: `A knowledgeable local guide with extensive information about the city, it's attractions and customs`,
type: 'ReactChampionAgent',
tools: [searchInternet],
maxIterations: 5,
llmConfig: {
provider: "google",
model: "gemini-1.5-pro",
}
});

const travelConciergeAgent = new Agent({
name: 'Maxwell Journey',
role: 'Amazing Travel Concierge',
goal: `Create the most amazing travel itineraries with budget and packing suggestions for the city`,
background: `Specialist in travel planning and logistics with decades of experience`,
type: 'ReactChampionAgent',
tools: [searchInternet],
maxIterations: 5,
llmConfig: {
provider: "google",
model: "gemini-1.5-pro",
}
});

// Define tasks with dynamic input placeholders
const identifyTask = new Task({
description: `Analyze and select the best city for the trip based on
specific criteria such as weather patterns, seasonal events,
and travel costs. ...
Origin: {origin}, City Options: {cities},
Trip Date: {range},
Traveler Interests: {interests}`,
expectedOutput: `Detailed report on the chosen city,
including flight costs,
weather forecast and attractions`,
agent: citySelectorAgent
});

const gatherTask = new Task({
description: `Compile an in-depth guide for the selected city,
considering key attractions, local customs, and special events.
... Trip Date: {range}, Origin: {origin}, Interests: {interests}`,
expectedOutput: `A comprehensive city guide,
rich in cultural insights and practical tips`,
agent: localExpertAgent
});

const planTask = new Task({
description: `Develop a full 7-day travel itinerary
with detailed daily plans, including places to eat,
packing suggestions, and a budget breakdown. ...
Trip Date: {range}, Origin: {origin}, Interests: {interests}`,
expectedOutput: 'A complete expanded travel plan formatted as markdown',
agent: travelConciergeAgent
});

// Team to coordinate the agents, with dynamic inputs
const tripPlanningTeam = new Team({
name: 'Trip Planning Team',
agents: [citySelectorAgent, localExpertAgent, travelConciergeAgent],
tasks: [identifyTask, gatherTask, planTask],
logLevel: 'info',
inputs: {
origin: 'New York',
cities: ['Tokyo', 'Paris', 'Berlin'],
interests: 'Art and Culture',
range: '2024-12-01 to 2024-12-15'
}, // Actual dynamic inputs
env: {
OPENAI_API_KEY: import.meta.env.VITE_OPENAI_API_KEY,
ANTHROPIC_API_KEY: import.meta.env.VITE_ANTHROPIC_API_KEY,
GOOGLE_API_KEY: import.meta.env.VITE_GOOGLE_API_KEY
}
});

export default tripPlanningTeam;
3 changes: 2 additions & 1 deletion src/agents/baseAgent.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AGENT_STATUS_enum } from '../utils/enums';


class BaseAgent {
constructor({ name, role, goal, background, tools, llmConfig = {}, maxIterations = 10 }) {
constructor({ name, role, goal, background, tools, llmConfig = {}, maxIterations = 10, forceFinalAnswer = true }) {
this.id = uuidv4();
this.name = name;
this.role = role;
Expand All @@ -21,6 +21,7 @@ class BaseAgent {
...llmConfig
};
this.llmSystemMessage = null;
this.forceFinalAnswer = forceFinalAnswer;
}

setStore(store) {
Expand Down
13 changes: 11 additions & 2 deletions src/agents/reactChampionAgent.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class ReactChampionAgent extends BaseAgent {
try {
_self.handleIterationStart({agent: _self, task, iterations, maxAgentIterations});

// Check if we need to force the final answer
if (_self.forceFinalAnswer && iterations === maxAgentIterations - 2) {
feedbackMessage = "We don't have more time to keep looking for the answer. Please use all the information you have gathered until now and give the finalAnswer right away.";
}

// pure function that returns the result of the agent thinking
const thinkingResult = await this.executeThinking(_self, task, ExecutableAgent, feedbackMessage);
// sometimes the LLM does not returns a valid JSON object so we try to sanitize the output here
Expand Down Expand Up @@ -323,13 +328,17 @@ class ReactChampionAgent extends BaseAgent {

handleThought({agent, task, parsedLLMOutput}) {
agent.store.getState().handleAgentThought({agent, task, output: parsedLLMOutput});
const feedbackMessage = "Your toughts are great, let's keep going.";
let feedbackMessage = "Your toughts are great, let's keep going.";
if(parsedLLMOutput.action === 'self_question' && parsedLLMOutput.actionInput) {
const actionAsString = typeof parsedLLMOutput.actionInput == 'object' ? JSON.stringify(parsedLLMOutput.actionInput) : parsedLLMOutput.actionInput;
feedbackMessage = "Awesome, please answer yourself the question: " + actionAsString;
}
return feedbackMessage;
}

handleSelfQuestion({agent, task, parsedLLMOutput}) {
agent.store.getState().handleAgentSelfQuestion({agent, task, output: parsedLLMOutput});
const feedbackMessage = "Awesome please answer yourself the question";
const feedbackMessage = "Awesome, please answer yourself the question";
return feedbackMessage;
}

Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ class Agent {
get llmSystemMessage() {
return this.agentInstance.llmSystemMessage;
}
get forceFinalAnswer() {
return this.agentInstance.forceFinalAnswer;
}
}
class Task {
constructor({ title = '', description, expectedOutput, agent, isDeliverable = false }) {
Expand Down
Loading

0 comments on commit 6f888ac

Please sign in to comment.