Skip to content

Commit

Permalink
Merge branch 'main' into searxng
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-desmond authored Jan 22, 2025
2 parents 1109fa9 + 72bfab6 commit b2b7f3f
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 14 deletions.
6 changes: 6 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
yarn lint-staged

MD_FILES=$(git diff --cached --name-only | grep -E '^docs/.+\.md$' | tr '\n' ' ')
if [ -n "$MD_FILES" ]; then
yarn docs:links -- $MD_FILES
fi

CI=true yarn copyright:check && yarn docs:check && yarn lint && yarn format && yarn ts:check && yarn run test:unit
198 changes: 191 additions & 7 deletions docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ console.info(`Counter:`, response.result);

_Source: [examples/workflows/nesting.ts](/examples/workflows/nesting.ts)_

### Content Creator Agent
### Agent Delegation

<!-- embedme examples/workflows/agent.ts -->

Expand Down Expand Up @@ -180,7 +180,183 @@ for await (const { prompt } of reader) {

_Source: [examples/workflows/agent.ts](/examples/workflows/agent.ts)_

### Multi Agents
### Multi-agent Content Creator

<!-- embedme examples/workflows/contentCreator.ts -->

```ts
import "dotenv/config";
import { z } from "zod";
import { Workflow } from "bee-agent-framework/experimental/workflows/workflow";
import { BeeAgent } from "bee-agent-framework/agents/bee/agent";
import { UnconstrainedMemory } from "bee-agent-framework/memory/unconstrainedMemory";
import { createConsoleReader } from "examples/helpers/io.js";
import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
import { JsonDriver } from "bee-agent-framework/llms/drivers/json";
import { isEmpty, pick } from "remeda";
import { LLMTool } from "bee-agent-framework/tools/llm";
import { GoogleSearchTool } from "bee-agent-framework/tools/search/googleSearch";
import { GroqChatLLM } from "bee-agent-framework/adapters/groq/chat";

const schema = z.object({
input: z.string(),
output: z.string().optional(),

topic: z.string().optional(),
notes: z.array(z.string()).default([]),
plan: z.string().optional(),
draft: z.string().optional(),
});

const workflow = new Workflow({
schema,
outputSchema: schema.required({ output: true }),
})
.addStep("preprocess", async (state) => {
const llm = new GroqChatLLM();
const driver = new JsonDriver(llm);

const { parsed } = await driver.generate(
schema.pick({ topic: true, notes: true }).or(
z.object({
error: z
.string()
.describe("Use when the input query does not make sense or you need clarification."),
}),
),
[
BaseMessage.of({
role: `user`,
text: [
"Your task is to rewrite the user query so that it guides the content planner and editor to craft a blog post that perfectly aligns with the user's needs. Notes should be used only if the user complains about something.",
"If the user query does ",
"",
...[state.topic && ["# Previous Topic", state.topic, ""]],
...[!isEmpty(state.notes) && ["# Previous Notes", ...state.notes, ""]],
"# User Query",
state.input || "empty",
]
.filter(Boolean)
.join("\n"),
}),
],
);

return "error" in parsed
? { update: { output: parsed.error }, next: Workflow.END }
: { update: pick(parsed, ["notes", "topic"]) };
})
.addStrictStep("planner", schema.required({ topic: true }), async (state) => {
const llm = new GroqChatLLM();
const agent = new BeeAgent({
llm,
memory: new UnconstrainedMemory(),
tools: [new GoogleSearchTool(), new LLMTool({ llm })],
});

agent.emitter.on("update", (data) => {
console.info(data.update);
});

const { result } = await agent.run({
prompt: [
`You are a Content Planner. Your task is to write a content plan for "${state.topic}" topic in Markdown format.`,
``,
`# Objectives`,
`1. Prioritize the latest trends, key players, and noteworthy news.`,
`2. Identify the target audience, considering their interests and pain points.`,
`3. Develop a detailed content outline including introduction, key points, and a call to action.`,
`4. Include SEO keywords and relevant sources.`,
``,
...[!isEmpty(state.notes) && ["# Notes", ...state.notes, ""]],
`Provide a structured output that covers the mentioned sections.`,
].join("\n"),
});

return {
update: {
plan: result.text,
},
};
})
.addStrictStep("writer", schema.required({ plan: true }), async (state) => {
const llm = new GroqChatLLM();
const output = await llm.generate([
BaseMessage.of({
role: `system`,
text: [
`You are a Content Writer. Your task is to write a compelling blog post based on the provided context.`,
``,
`# Context`,
`${state.plan}`,
``,
`# Objectives`,
`- An engaging introduction`,
`- Insightful body paragraphs (2-3 per section)`,
`- Properly named sections/subtitles`,
`- A summarizing conclusion`,
`- Format: Markdown`,
``,
...[!isEmpty(state.notes) && ["# Notes", ...state.notes, ""]],
`Ensure the content flows naturally, incorporates SEO keywords, and is well-structured.`,
].join("\n"),
}),
]);

return {
update: { draft: output.getTextContent() },
};
})
.addStrictStep("editor", schema.required({ draft: true }), async (state) => {
const llm = new GroqChatLLM();
const output = await llm.generate([
BaseMessage.of({
role: `system`,
text: [
`You are an Editor. Your task is to transform the following draft blog post to a final version.`,
``,
`# Draft`,
`${state.draft}`,
``,
`# Objectives`,
`- Fix Grammatical errors`,
`- Journalistic best practices`,
``,
...[!isEmpty(state.notes) && ["# Notes", ...state.notes, ""]],
``,
`IMPORTANT: The final version must not contain any editor's comments.`,
].join("\n"),
}),
]);

return {
update: { output: output.getTextContent() },
};
});

let lastResult = {} as Workflow.output<typeof workflow>;
const reader = createConsoleReader();
for await (const { prompt } of reader) {
const { result } = await workflow
.run({
input: prompt,
notes: lastResult?.notes,
topic: lastResult?.topic,
})
.observe((emitter) => {
emitter.on("start", ({ step, run }) => {
reader.write(`-> ▶️ ${step}`, JSON.stringify(run.state).substring(0, 200).concat("..."));
});
});

lastResult = result;
reader.write("🤖 Answer", lastResult.output);
}
```

_Source: [examples/workflows/contentCreator.ts](/examples/workflows/contentCreator.ts)_

### Multi Agents Workflows

<!-- embedme examples/workflows/multiAgents.ts -->

Expand All @@ -192,27 +368,32 @@ import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo";
import { WikipediaTool } from "bee-agent-framework/tools/search/wikipedia";
import { AgentWorkflow } from "bee-agent-framework/experimental/workflows/agent";
import { BaseMessage, Role } from "bee-agent-framework/llms/primitives/message";
import { GroqChatLLM } from "bee-agent-framework/adapters/groq/chat";
import { WatsonXChatLLM } from "bee-agent-framework/adapters/watsonx/chat";

const workflow = new AgentWorkflow();
const llm = WatsonXChatLLM.fromPreset("meta-llama/llama-3-3-70b-instruct", {
apiKey: process.env.WATSONX_API_KEY,
projectId: process.env.WATSONX_PROJECT_ID,
});

workflow.addAgent({
name: "WeatherForecaster",
instructions: "You are a weather assistant. Respond only if you can provide a useful answer.",
tools: [new OpenMeteoTool()],
llm: new GroqChatLLM(),
llm,
execution: { maxIterations: 3 },
});
workflow.addAgent({
name: "Researcher",
instructions: "You are a researcher assistant. Respond only if you can provide a useful answer.",
tools: [new WikipediaTool()],
llm: new GroqChatLLM(),
llm,
});
workflow.addAgent({
name: "Solver",
instructions:
"Your task is to provide the most useful final answer based on the assistants' responses which all are relevant. Ignore those where assistant do not know.",
llm: new GroqChatLLM(),
llm,
});

const reader = createConsoleReader();
Expand All @@ -232,7 +413,10 @@ for await (const { prompt } of reader) {
reader.write(`-> ${data.step}`, data.response?.update?.finalAnswer ?? "-");
});
});
await memory.addMany(result.newMessages);

// await memory.addMany(result.newMessages); // save intermediate steps + final answer
await memory.addMany(result.newMessages.slice(-1)); // save only the final answer

reader.write(`Agent 🤖`, result.finalAnswer);
}
```
Expand Down
18 changes: 13 additions & 5 deletions examples/workflows/multiAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,32 @@ import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo";
import { WikipediaTool } from "bee-agent-framework/tools/search/wikipedia";
import { AgentWorkflow } from "bee-agent-framework/experimental/workflows/agent";
import { BaseMessage, Role } from "bee-agent-framework/llms/primitives/message";
import { GroqChatLLM } from "bee-agent-framework/adapters/groq/chat";
import { WatsonXChatLLM } from "bee-agent-framework/adapters/watsonx/chat";

const workflow = new AgentWorkflow();
const llm = WatsonXChatLLM.fromPreset("meta-llama/llama-3-3-70b-instruct", {
apiKey: process.env.WATSONX_API_KEY,
projectId: process.env.WATSONX_PROJECT_ID,
});

workflow.addAgent({
name: "WeatherForecaster",
instructions: "You are a weather assistant. Respond only if you can provide a useful answer.",
tools: [new OpenMeteoTool()],
llm: new GroqChatLLM(),
llm,
execution: { maxIterations: 3 },
});
workflow.addAgent({
name: "Researcher",
instructions: "You are a researcher assistant. Respond only if you can provide a useful answer.",
tools: [new WikipediaTool()],
llm: new GroqChatLLM(),
llm,
});
workflow.addAgent({
name: "Solver",
instructions:
"Your task is to provide the most useful final answer based on the assistants' responses which all are relevant. Ignore those where assistant do not know.",
llm: new GroqChatLLM(),
llm,
});

const reader = createConsoleReader();
Expand All @@ -45,6 +50,9 @@ for await (const { prompt } of reader) {
reader.write(`-> ${data.step}`, data.response?.update?.finalAnswer ?? "-");
});
});
await memory.addMany(result.newMessages);

// await memory.addMany(result.newMessages); // save intermediate steps + final answer
await memory.addMany(result.newMessages.slice(-1)); // save only the final answer

reader.write(`Agent 🤖`, result.finalAnswer);
}
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@
"lint-staged": {
"{src,tests,scripts}/**/*": "yarn copyright",
"*": "prettier --ignore-unknown --write",
"*.{ts,js}": "eslint --fix",
"docs/**/*.md": "yarn docs:links"
"*.{ts,js}": "eslint --fix"
},
"prettier": {
"printWidth": 100,
Expand Down

0 comments on commit b2b7f3f

Please sign in to comment.