Skip to content

Commit e16426b

Browse files
committed
update single agent
1 parent 613af08 commit e16426b

File tree

2 files changed

+100
-181
lines changed

2 files changed

+100
-181
lines changed

templates/components/agents/typescript/blog/workflow/agents.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { ChatMessage } from "llamaindex";
2+
import { getTool } from "../engine/tools";
23
import { FunctionCallingAgent } from "./single-agent";
3-
import { getQueryEngineTools, lookupTools } from "./tools";
4+
import { getQueryEngineTools } from "./tools";
45

56
export const createResearcher = async (chatHistory: ChatMessage[]) => {
67
const queryEngineTools = await getQueryEngineTools();
7-
const tools = (
8-
await lookupTools([
9-
"wikipedia_tool",
10-
"duckduckgo_search",
11-
"image_generator",
12-
])
13-
).concat(queryEngineTools ? queryEngineTools : []);
8+
const tools = [
9+
await getTool("wikipedia_tool"),
10+
await getTool("duckduckgo_search"),
11+
await getTool("image_generator"),
12+
...(queryEngineTools ? queryEngineTools : []),
13+
].filter((tool) => tool !== undefined);
1414

1515
return new FunctionCallingAgent({
1616
name: "researcher",
@@ -78,17 +78,17 @@ Example:
7878
};
7979

8080
export const createPublisher = async (chatHistory: ChatMessage[]) => {
81-
const tools = await lookupTools(["document_generator"]);
81+
const tool = await getTool("document_generator");
8282
let systemPrompt = `You are an expert in publishing blog posts. You are given a task to publish a blog post.
8383
If the writer says that there was an error, you should reply with the error and not publish the post.`;
84-
if (tools.length > 0) {
84+
if (tool) {
8585
systemPrompt = `${systemPrompt}.
8686
If the user requests to generate a file, use the document_generator tool to generate the file and reply with the link to the file.
8787
Otherwise, simply return the content of the post.`;
8888
}
8989
return new FunctionCallingAgent({
9090
name: "publisher",
91-
tools: tools,
91+
tools: tool ? [tool] : [],
9292
systemPrompt: systemPrompt,
9393
chatHistory,
9494
});
Lines changed: 89 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,95 @@
1-
import {
2-
HandlerContext,
3-
StartEvent,
4-
StopEvent,
5-
Workflow,
6-
WorkflowEvent,
7-
} from "@llamaindex/workflow";
8-
import {
9-
BaseToolWithCall,
10-
ChatMemoryBuffer,
11-
ChatMessage,
12-
ChatResponseChunk,
13-
Settings,
14-
ToolCall,
15-
ToolCallLLM,
16-
} from "llamaindex";
17-
import { callTools, chatWithTools } from "./tools";
18-
import { AgentInput, AgentRunEvent } from "./type";
19-
20-
class InputEvent extends WorkflowEvent<{
21-
input: ChatMessage[];
22-
}> {}
23-
24-
class ToolCallEvent extends WorkflowEvent<{
25-
toolCalls: ToolCall[];
26-
}> {}
27-
28-
type FunctionCallingAgentContextData = {
29-
streaming: boolean;
1+
import { ChatMessage } from "llamaindex";
2+
import { getTool } from "../engine/tools";
3+
import { FunctionCallingAgent } from "./single-agent";
4+
import { getQueryEngineTools } from "./tools";
5+
6+
export const createResearcher = async (chatHistory: ChatMessage[]) => {
7+
const queryEngineTools = await getQueryEngineTools();
8+
const tools = [
9+
await getTool("wikipedia_tool"),
10+
await getTool("duckduckgo_search"),
11+
await getTool("image_generator"),
12+
...(queryEngineTools ? queryEngineTools : []),
13+
].filter((tool) => tool !== undefined);
14+
15+
return new FunctionCallingAgent({
16+
name: "researcher",
17+
tools: tools,
18+
systemPrompt: `You are a researcher agent. You are given a research task.
19+
20+
If the conversation already includes the information and there is no new request for additional information from the user, you should return the appropriate content to the writer.
21+
Otherwise, you must use tools to retrieve information or images needed for the task.
22+
23+
It's normal for the task to include some ambiguity. You must always think carefully about the context of the user's request to understand what are the main content needs to be retrieved.
24+
Example:
25+
Request: "Create a blog post about the history of the internet, write in English and publish in PDF format."
26+
->Though: The main content is "history of the internet", while "write in English and publish in PDF format" is a requirement for other agents.
27+
Your task: Look for information in English about the history of the Internet.
28+
This is not your task: Create a blog post or look for how to create a PDF.
29+
30+
Next request: "Publish the blog post in HTML format."
31+
->Though: User just asking for a format change, the previous content is still valid.
32+
Your task: Return the previous content of the post to the writer. No need to do any research.
33+
This is not your task: Look for how to create an HTML file.
34+
35+
If you use the tools but don't find any related information, please return "I didn't find any new information for {the topic}." along with the content you found. Don't try to make up information yourself.
36+
If the request doesn't need any new information because it was in the conversation history, please return "The task doesn't need any new information. Please reuse the existing content in the conversation history.
37+
`,
38+
chatHistory,
39+
});
3040
};
3141

32-
export type FunctionCallingAgentInput = AgentInput & {
33-
displayName: string;
42+
export const createWriter = (chatHistory: ChatMessage[]) => {
43+
return new FunctionCallingAgent({
44+
name: "writer",
45+
systemPrompt: `You are an expert in writing blog posts.
46+
You are given the task of writing a blog post based on research content provided by the researcher agent. Do not invent any information yourself.
47+
It's important to read the entire conversation history to write the blog post accurately.
48+
If you receive a review from the reviewer, update the post according to the feedback and return the new post content.
49+
If the content is not valid (e.g., broken link, broken image, etc.), do not use it.
50+
It's normal for the task to include some ambiguity, so you must define the user's initial request to write the post correctly.
51+
If you update the post based on the reviewer's feedback, first explain what changes you made to the post, then provide the new post content. Do not include the reviewer's comments.
52+
Example:
53+
Task: "Here is the information I found about the history of the internet:
54+
Create a blog post about the history of the internet, write in English, and publish in PDF format."
55+
-> Your task: Use the research content {...} to write a blog post in English.
56+
-> This is not your task: Create a PDF
57+
Please note that a localhost link is acceptable, but dummy links like "example.com" or "your-website.com" are not valid.`,
58+
chatHistory,
59+
});
3460
};
3561

36-
export class FunctionCallingAgent extends Workflow<
37-
FunctionCallingAgentContextData,
38-
FunctionCallingAgentInput,
39-
string | AsyncGenerator<boolean | ChatResponseChunk<object>>
40-
> {
41-
name: string;
42-
llm: ToolCallLLM;
43-
memory: ChatMemoryBuffer;
44-
tools: BaseToolWithCall[];
45-
systemPrompt?: string;
46-
writeEvents: boolean;
47-
role?: string;
48-
49-
constructor(options: {
50-
name: string;
51-
llm?: ToolCallLLM;
52-
chatHistory?: ChatMessage[];
53-
tools?: BaseToolWithCall[];
54-
systemPrompt?: string;
55-
writeEvents?: boolean;
56-
role?: string;
57-
verbose?: boolean;
58-
timeout?: number;
59-
}) {
60-
super({
61-
verbose: options?.verbose ?? false,
62-
timeout: options?.timeout ?? 360,
63-
});
64-
this.name = options?.name;
65-
this.llm = options.llm ?? (Settings.llm as ToolCallLLM);
66-
if (!(this.llm instanceof ToolCallLLM)) {
67-
throw new Error("LLM is not a ToolCallLLM");
68-
}
69-
this.memory = new ChatMemoryBuffer({
70-
llm: this.llm,
71-
chatHistory: options.chatHistory,
72-
});
73-
this.tools = options?.tools ?? [];
74-
this.systemPrompt = options.systemPrompt;
75-
this.writeEvents = options?.writeEvents ?? true;
76-
this.role = options?.role;
77-
78-
// add steps
79-
this.addStep(
80-
{
81-
inputs: [StartEvent<AgentInput>],
82-
outputs: [InputEvent],
83-
},
84-
this.prepareChatHistory.bind(this),
85-
);
86-
this.addStep(
87-
{
88-
inputs: [InputEvent],
89-
outputs: [ToolCallEvent, StopEvent],
90-
},
91-
this.handleLLMInput.bind(this),
92-
);
93-
this.addStep(
94-
{
95-
inputs: [ToolCallEvent],
96-
outputs: [InputEvent],
97-
},
98-
this.handleToolCalls.bind(this),
99-
);
100-
}
101-
102-
private get chatHistory() {
103-
return this.memory.getMessages();
104-
}
105-
106-
private async prepareChatHistory(
107-
ctx: HandlerContext<FunctionCallingAgentContextData>,
108-
ev: StartEvent<AgentInput>,
109-
): Promise<InputEvent> {
110-
const { message, streaming } = ev.data;
111-
ctx.data.streaming = streaming ?? false;
112-
this.writeEvent(`Start to work on: ${message}`, ctx);
113-
if (this.systemPrompt) {
114-
this.memory.put({ role: "system", content: this.systemPrompt });
115-
}
116-
this.memory.put({ role: "user", content: message });
117-
return new InputEvent({ input: this.chatHistory });
118-
}
119-
120-
private async handleLLMInput(
121-
ctx: HandlerContext<FunctionCallingAgentContextData>,
122-
ev: InputEvent,
123-
): Promise<StopEvent<string | AsyncGenerator> | ToolCallEvent> {
124-
const toolCallResponse = await chatWithTools(
125-
this.llm,
126-
this.tools,
127-
this.chatHistory,
128-
);
129-
if (toolCallResponse.toolCallMessage) {
130-
this.memory.put(toolCallResponse.toolCallMessage);
131-
}
132-
133-
if (toolCallResponse.hasToolCall()) {
134-
return new ToolCallEvent({ toolCalls: toolCallResponse.toolCalls });
135-
}
136-
137-
if (ctx.data.streaming) {
138-
if (!toolCallResponse.responseGenerator) {
139-
throw new Error("No streaming response");
140-
}
141-
return new StopEvent(toolCallResponse.responseGenerator);
142-
}
143-
144-
const fullResponse = await toolCallResponse.asFullResponse();
145-
this.memory.put(fullResponse);
146-
return new StopEvent(fullResponse.content.toString());
147-
}
148-
149-
private async handleToolCalls(
150-
ctx: HandlerContext<FunctionCallingAgentContextData>,
151-
ev: ToolCallEvent,
152-
): Promise<InputEvent> {
153-
const { toolCalls } = ev.data;
154-
155-
const toolMsgs = await callTools({
156-
tools: this.tools,
157-
toolCalls,
158-
ctx,
159-
agentName: this.name,
160-
});
161-
162-
for (const msg of toolMsgs) {
163-
this.memory.put(msg);
164-
}
165-
166-
return new InputEvent({ input: this.memory.getMessages() });
167-
}
62+
export const createReviewer = (chatHistory: ChatMessage[]) => {
63+
return new FunctionCallingAgent({
64+
name: "reviewer",
65+
systemPrompt: `You are an expert in reviewing blog posts.
66+
You are given a task to review a blog post. As a reviewer, it's important that your review aligns with the user's request. Please focus on the user's request when reviewing the post.
67+
Review the post for logical inconsistencies, ask critical questions, and provide suggestions for improvement.
68+
Furthermore, proofread the post for grammar and spelling errors.
69+
Only if the post is good enough for publishing should you return 'The post is good.' In all other cases, return your review.
70+
It's normal for the task to include some ambiguity, so you must define the user's initial request to review the post correctly.
71+
Please note that a localhost link is acceptable, but dummy links like "example.com" or "your-website.com" are not valid.
72+
Example:
73+
Task: "Create a blog post about the history of the internet, write in English and publish in PDF format."
74+
-> Your task: Review whether the main content of the post is about the history of the internet and if it is written in English.
75+
-> This is not your task: Create blog post, create PDF, write in English.`,
76+
chatHistory,
77+
});
78+
};
16879

169-
private writeEvent(
170-
msg: string,
171-
ctx: HandlerContext<FunctionCallingAgentContextData>,
172-
) {
173-
if (!this.writeEvents) return;
174-
ctx.sendEvent(new AgentRunEvent({ agent: this.name, text: msg }));
80+
export const createPublisher = async (chatHistory: ChatMessage[]) => {
81+
const tool = await getTool("document_generator");
82+
let systemPrompt = `You are an expert in publishing blog posts. You are given a task to publish a blog post.
83+
If the writer says that there was an error, you should reply with the error and not publish the post.`;
84+
if (tool) {
85+
systemPrompt = `${systemPrompt}.
86+
If the user requests to generate a file, use the document_generator tool to generate the file and reply with the link to the file.
87+
Otherwise, simply return the content of the post.`;
17588
}
176-
}
89+
return new FunctionCallingAgent({
90+
name: "publisher",
91+
tools: tool ? [tool] : [],
92+
systemPrompt: systemPrompt,
93+
chatHistory,
94+
});
95+
};

0 commit comments

Comments
 (0)