Skip to content

Commit ade3e2b

Browse files
committed
revert blog changes, add deep research
1 parent 7061dc3 commit ade3e2b

File tree

18 files changed

+1261
-20
lines changed

18 files changed

+1261
-20
lines changed

helpers/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export type TemplateDataSourceType = "file" | "web" | "db";
5151
export type TemplateObservability = "none" | "traceloop" | "llamatrace";
5252
export type TemplateUseCase =
5353
| "financial_report"
54-
| "blog"
54+
| "deep_research"
5555
| "form_filling"
5656
| "extractor"
5757
| "contract_review";

questions/simple.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type AppType =
1919
| "extractor"
2020
| "contract_review"
2121
| "data_scientist"
22-
| "blog";
22+
| "deep_research";
2323

2424
type SimpleAnswers = {
2525
appType: AppType;
@@ -81,10 +81,10 @@ export const askSimpleQuestions = async (
8181
"Extract and review contracts to ensure compliance with regulations (GDPR)",
8282
},
8383
{
84-
title: "🔀 Blog Writer",
85-
value: "blog",
84+
title: "🔀 Deep Research over own documents",
85+
value: "deep_research",
8686
description:
87-
"Write a blog post by analyzing the provided data from different perspectives and crafting a coherent blog post with citations to the data.",
87+
"Research and analyze documents from multiple perspectives, generating a comprehensive report with citations to support key findings and insights.",
8888
},
8989
],
9090
},
@@ -98,7 +98,7 @@ export const askSimpleQuestions = async (
9898
if (
9999
appType !== "extractor" &&
100100
appType !== "contract_review" &&
101-
appType !== "blog"
101+
appType !== "deep_research"
102102
) {
103103
const { language: newLanguage } = await prompts(
104104
{
@@ -227,9 +227,9 @@ const convertAnswers = async (
227227
frontend: false,
228228
dataSources: [EXAMPLE_GDPR],
229229
},
230-
blog: {
230+
deep_research: {
231231
template: "multiagent",
232-
useCase: "blog",
232+
useCase: "deep_research",
233233
tools: [],
234234
frontend: true,
235235
dataSources: [AI_REPORTS],

templates/components/agents/python/blog/README-template.md

+29-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
This is a [LlamaIndex](https://www.llamaindex.ai/) multi-agents project using [Workflows](https://docs.llamaindex.ai/en/stable/understanding/workflows/).
1+
## Overview
2+
3+
This example is using three agents to generate a blog post:
4+
5+
- a researcher that retrieves content via a RAG pipeline,
6+
- a writer that specializes in writing blog posts and
7+
- a reviewer that is reviewing the blog post.
8+
9+
There are three different methods how the agents can interact to reach their goal:
10+
11+
1. [Choreography](./app/agents/choreography.py) - the agents decide themselves to delegate a task to another agent
12+
1. [Orchestrator](./app/agents/orchestrator.py) - a central orchestrator decides which agent should execute a task
13+
1. [Explicit Workflow](./app/agents/workflow.py) - a pre-defined workflow specific for the task is used to execute the tasks
214

315
## Getting Started
416

@@ -23,14 +35,25 @@ Third, run the development server:
2335
poetry run dev
2436
```
2537

26-
## Use Case: Blog writer
38+
Per default, the example is using the explicit workflow. You can change the example by setting the `EXAMPLE_TYPE` environment variable to `choreography` or `orchestrator`.
39+
The example provides one streaming API endpoint `/api/chat`.
40+
You can test the endpoint with the following curl request:
41+
42+
```
43+
curl --location 'localhost:8000/api/chat' \
44+
--header 'Content-Type: application/json' \
45+
--data '{ "messages": [{ "role": "user", "content": "Write a blog post about physical standards for letters" }] }'
46+
```
47+
48+
You can start editing the API by modifying `app/api/routers/chat.py` or `app/examples/workflow.py`. The API auto-updates as you save the files.
2749

28-
The workflow writes blog posts based on documents in the [data](./data) directory. You can start with the included PDF about AI investment in 2024, or add your own documents and run generate script again.
50+
Open [http://localhost:8000](http://localhost:8000) with your browser to start the app.
2951

30-
After starting the server, go to [http://localhost:8000](http://localhost:8000) and send a message to the agent to write a blog post.
31-
E.g: "Write a post about AI investment in 2024"
52+
To start the app optimized for **production**, run:
3253

33-
To update the workflow, you can edit the [writer.py](./app/workflows/writer.py) file.
54+
```
55+
poetry run prod
56+
```
3457

3558
## Deployments
3659

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from textwrap import dedent
2+
from typing import List, Optional
3+
4+
from app.agents.publisher import create_publisher
5+
from app.agents.researcher import create_researcher
6+
from app.workflows.multi import AgentCallingAgent
7+
from app.workflows.single import FunctionCallingAgent
8+
from llama_index.core.chat_engine.types import ChatMessage
9+
10+
11+
def create_choreography(chat_history: Optional[List[ChatMessage]] = None, **kwargs):
12+
researcher = create_researcher(chat_history, **kwargs)
13+
publisher = create_publisher(chat_history)
14+
reviewer = FunctionCallingAgent(
15+
name="reviewer",
16+
description="expert in reviewing blog posts, needs a written post to review",
17+
system_prompt="You are an expert in reviewing blog posts. You are given a task to review a blog post. Review the post for logical inconsistencies, ask critical questions, and provide suggestions for improvement. Furthermore, proofread the post for grammar and spelling errors. If the post is good, you can say 'The post is good.'",
18+
chat_history=chat_history,
19+
)
20+
return AgentCallingAgent(
21+
name="writer",
22+
agents=[researcher, reviewer, publisher],
23+
description="expert in writing blog posts, needs researched information and images to write a blog post",
24+
system_prompt=dedent(
25+
"""
26+
You are an expert in writing blog posts. You are given a task to write a blog post. Before starting to write the post, consult the researcher agent to get the information you need. Don't make up any information yourself.
27+
After creating a draft for the post, send it to the reviewer agent to receive feedback and make sure to incorporate the feedback from the reviewer.
28+
You can consult the reviewer and researcher a maximum of two times. Your output should contain only the blog post.
29+
Finally, always request the publisher to create a document (PDF, HTML) and publish the blog post.
30+
"""
31+
),
32+
# TODO: add chat_history support to AgentCallingAgent
33+
# chat_history=chat_history,
34+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from textwrap import dedent
2+
from typing import List, Optional
3+
4+
from app.agents.publisher import create_publisher
5+
from app.agents.researcher import create_researcher
6+
from app.workflows.multi import AgentOrchestrator
7+
from app.workflows.single import FunctionCallingAgent
8+
from llama_index.core.chat_engine.types import ChatMessage
9+
10+
11+
def create_orchestrator(chat_history: Optional[List[ChatMessage]] = None, **kwargs):
12+
researcher = create_researcher(chat_history, **kwargs)
13+
writer = FunctionCallingAgent(
14+
name="writer",
15+
description="expert in writing blog posts, need information and images to write a post",
16+
system_prompt=dedent(
17+
"""
18+
You are an expert in writing blog posts.
19+
You are given a task to write a blog post. Do not make up any information yourself.
20+
If you don't have the necessary information to write a blog post, reply "I need information about the topic to write the blog post".
21+
If you need to use images, reply "I need images about the topic to write the blog post". Do not use any dummy images made up by you.
22+
If you have all the information needed, write the blog post.
23+
"""
24+
),
25+
chat_history=chat_history,
26+
)
27+
reviewer = FunctionCallingAgent(
28+
name="reviewer",
29+
description="expert in reviewing blog posts, needs a written blog post to review",
30+
system_prompt=dedent(
31+
"""
32+
You are an expert in reviewing blog posts. You are given a task to review a blog post. Review the post and fix any issues found yourself. You must output a final blog post.
33+
A post must include at least one valid image. If not, reply "I need images about the topic to write the blog post". An image URL starting with "example" or "your website" is not valid.
34+
Especially check for logical inconsistencies and proofread the post for grammar and spelling errors.
35+
"""
36+
),
37+
chat_history=chat_history,
38+
)
39+
publisher = create_publisher(chat_history)
40+
return AgentOrchestrator(
41+
agents=[writer, reviewer, researcher, publisher],
42+
refine_plan=False,
43+
chat_history=chat_history,
44+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from textwrap import dedent
2+
from typing import List, Tuple
3+
4+
from app.engine.tools import ToolFactory
5+
from app.workflows.single import FunctionCallingAgent
6+
from llama_index.core.chat_engine.types import ChatMessage
7+
from llama_index.core.tools import FunctionTool
8+
9+
10+
def get_publisher_tools() -> Tuple[List[FunctionTool], str, str]:
11+
tools = []
12+
# Get configured tools from the tools.yaml file
13+
configured_tools = ToolFactory.from_env(map_result=True)
14+
if "generate_document" in configured_tools.keys():
15+
tools.append(configured_tools["generate_document"])
16+
prompt_instructions = dedent("""
17+
Normally, reply the blog post content to the user directly.
18+
But if user requested to generate a file, use the generate_document tool to generate the file and reply the link to the file.
19+
""")
20+
description = "Expert in publishing the blog post, able to publish the blog post in PDF or HTML format."
21+
else:
22+
prompt_instructions = "You don't have a tool to generate document. Please reply the content directly."
23+
description = "Expert in publishing the blog post"
24+
return tools, prompt_instructions, description
25+
26+
27+
def create_publisher(chat_history: List[ChatMessage]):
28+
tools, prompt_instructions, description = get_publisher_tools()
29+
return FunctionCallingAgent(
30+
name="publisher",
31+
tools=tools,
32+
description=description,
33+
system_prompt=prompt_instructions,
34+
chat_history=chat_history,
35+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from textwrap import dedent
2+
from typing import List
3+
4+
from app.engine.index import IndexConfig, get_index
5+
from app.engine.tools import ToolFactory
6+
from app.workflows.single import FunctionCallingAgent
7+
from llama_index.core.chat_engine.types import ChatMessage
8+
from app.engine.tools.query_engine import get_query_engine_tool
9+
10+
11+
def _get_research_tools(**kwargs):
12+
"""
13+
Researcher take responsibility for retrieving information.
14+
Try init wikipedia or duckduckgo tool if available.
15+
"""
16+
tools = []
17+
# Create query engine tool
18+
index_config = IndexConfig(**kwargs)
19+
index = get_index(index_config)
20+
if index is not None:
21+
query_engine_tool = get_query_engine_tool(index=index)
22+
if query_engine_tool is not None:
23+
tools.append(query_engine_tool)
24+
25+
# Create duckduckgo tool
26+
researcher_tool_names = [
27+
"duckduckgo_search",
28+
"duckduckgo_image_search",
29+
"wikipedia.WikipediaToolSpec",
30+
]
31+
configured_tools = ToolFactory.from_env(map_result=True)
32+
for tool_name, tool in configured_tools.items():
33+
if tool_name in researcher_tool_names:
34+
tools.append(tool)
35+
return tools
36+
37+
38+
def create_researcher(chat_history: List[ChatMessage], **kwargs):
39+
"""
40+
Researcher is an agent that take responsibility for using tools to complete a given task.
41+
"""
42+
tools = _get_research_tools(**kwargs)
43+
return FunctionCallingAgent(
44+
name="researcher",
45+
tools=tools,
46+
description="expert in retrieving any unknown content or searching for images from the internet",
47+
system_prompt=dedent(
48+
"""
49+
You are a researcher agent. You are given a research task.
50+
51+
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.
52+
Otherwise, you must use tools to retrieve information or images needed for the task.
53+
54+
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.
55+
Example:
56+
Request: "Create a blog post about the history of the internet, write in English and publish in PDF format."
57+
->Though: The main content is "history of the internet", while "write in English and publish in PDF format" is a requirement for other agents.
58+
Your task: Look for information in English about the history of the Internet.
59+
This is not your task: Create a blog post or look for how to create a PDF.
60+
61+
Next request: "Publish the blog post in HTML format."
62+
->Though: User just asking for a format change, the previous content is still valid.
63+
Your task: Return the previous content of the post to the writer. No need to do any research.
64+
This is not your task: Look for how to create an HTML file.
65+
66+
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.
67+
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."
68+
"""
69+
),
70+
chat_history=chat_history,
71+
)

0 commit comments

Comments
 (0)