Skip to content

Commit 75fa673

Browse files
committed
Merge remote-tracking branch 'origin' into ms/use-inquirer
2 parents 7d8195c + 18c8d25 commit 75fa673

File tree

17 files changed

+888
-10
lines changed

17 files changed

+888
-10
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# create-llama
22

3+
## 0.3.27
4+
5+
### Patch Changes
6+
7+
- b4e41aa: Add deep research over own documents use case (Python)
8+
39
## 0.3.26
410

511
### Patch Changes

helpers/datasources.ts

+10
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ export const EXAMPLE_GDPR: TemplateDataSource = {
4242
},
4343
};
4444

45+
export const AI_REPORTS: TemplateDataSource = {
46+
type: "file",
47+
config: {
48+
url: new URL(
49+
"https://www.europarl.europa.eu/RegData/etudes/ATAG/2024/760392/EPRS_ATA(2024)760392_EN.pdf",
50+
),
51+
filename: "EPRS_ATA_2024_760392_EN.pdf",
52+
},
53+
};
54+
4555
export function getDataSources(
4656
files?: string,
4757
exampleFile?: boolean,

helpers/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export type TemplateObservability = "none" | "traceloop" | "llamatrace";
5252
export type TemplateUseCase =
5353
| "financial_report"
5454
| "blog"
55+
| "deep_research"
5556
| "form_filling"
5657
| "extractor"
5758
| "contract_review";

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "create-llama",
3-
"version": "0.3.26",
3+
"version": "0.3.27",
44
"description": "Create LlamaIndex-powered apps with one command",
55
"keywords": [
66
"rag",

questions/simple.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import inquirer from "inquirer";
22
import {
3+
AI_REPORTS,
34
EXAMPLE_10K_SEC_FILES,
45
EXAMPLE_FILE,
56
EXAMPLE_GDPR,
@@ -17,7 +18,8 @@ type AppType =
1718
| "form_filling"
1819
| "extractor"
1920
| "contract_review"
20-
| "data_scientist";
21+
| "data_scientist"
22+
| "deep_research";
2123

2224
type SimpleAnswers = {
2325
appType: AppType;
@@ -54,6 +56,10 @@ export const askSimpleQuestions = async (
5456
name: " Contract Review",
5557
value: "contract_review",
5658
},
59+
{
60+
name: " Deep Researcher",
61+
value: "deep_research",
62+
},
5763
],
5864
},
5965
]);
@@ -62,7 +68,11 @@ export const askSimpleQuestions = async (
6268
let llamaCloudKey = args.llamaCloudKey;
6369
let useLlamaCloud = false;
6470

65-
if (appType !== "extractor" && appType !== "contract_review") {
71+
if (
72+
appType !== "extractor" &&
73+
appType !== "contract_review" &&
74+
appType !== "deep_research"
75+
) {
6676
const { language: newLanguage } = await inquirer.prompt([
6777
{
6878
type: "list",
@@ -185,6 +195,13 @@ const convertAnswers = async (
185195
frontend: false,
186196
dataSources: [EXAMPLE_GDPR],
187197
},
198+
deep_research: {
199+
template: "multiagent",
200+
useCase: "deep_research",
201+
tools: [],
202+
frontend: true,
203+
dataSources: [AI_REPORTS],
204+
},
188205
};
189206
const results = lookup[answers.appType];
190207
return {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
This is a [LlamaIndex](https://www.llamaindex.ai/) multi-agents project using [Workflows](https://docs.llamaindex.ai/en/stable/understanding/workflows/).
2+
3+
## Getting Started
4+
5+
First, setup the environment with poetry:
6+
7+
> **_Note:_** This step is not needed if you are using the dev-container.
8+
9+
```shell
10+
poetry install
11+
```
12+
13+
Then check the parameters that have been pre-configured in the `.env` file in this directory. (E.g. you might need to configure an `OPENAI_API_KEY` if you're using OpenAI as model provider).
14+
Second, generate the embeddings of the documents in the `./data` directory:
15+
16+
```shell
17+
poetry run generate
18+
```
19+
20+
Third, run the development server:
21+
22+
```shell
23+
poetry run dev
24+
```
25+
26+
## Use Case: Deep Research over own documents
27+
28+
The workflow performs deep research by retrieving and analyzing documents from the [data](./data) directory from multiple perspectives. The project includes a sample PDF about AI investment in 2024 to help you get started. You can also add your own documents by placing them in the data directory and running the generate script again to index them.
29+
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: "AI investment in 2024"
32+
33+
To update the workflow, you can edit the [deep_research.py](./app/workflows/deep_research.py) file.
34+
35+
By default, the workflow retrieves 10 results from your documents. To customize the amount of information covered in the answer, you can adjust the `TOP_K` environment variable in the `.env` file. A higher value will retrieve more results from your documents, potentially providing more comprehensive answers.
36+
37+
## Deployments
38+
39+
For production deployments, check the [DEPLOY.md](DEPLOY.md) file.
40+
41+
## Learn More
42+
43+
To learn more about LlamaIndex, take a look at the following resources:
44+
45+
- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex.
46+
- [Workflows Introduction](https://docs.llamaindex.ai/en/stable/understanding/workflows/) - learn about LlamaIndex workflows.
47+
You can check out [the LlamaIndex GitHub repository](https://github.com/run-llama/llama_index) - your feedback and contributions are welcome!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .deep_research import create_workflow
2+
3+
__all__ = ["create_workflow"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
from typing import List, Literal, Optional
2+
3+
from llama_index.core.base.llms.types import (
4+
CompletionResponse,
5+
CompletionResponseAsyncGen,
6+
)
7+
from llama_index.core.memory.simple_composable_memory import SimpleComposableMemory
8+
from llama_index.core.prompts import PromptTemplate
9+
from llama_index.core.schema import MetadataMode, Node, NodeWithScore
10+
from llama_index.core.settings import Settings
11+
from pydantic import BaseModel, Field
12+
13+
14+
class AnalysisDecision(BaseModel):
15+
decision: Literal["research", "write", "cancel"] = Field(
16+
description="Whether to continue research, write a report, or cancel the research after several retries"
17+
)
18+
research_questions: Optional[List[str]] = Field(
19+
description="Questions to research if continuing research. Maximum 3 questions. Set to null or empty if writing a report.",
20+
default_factory=list,
21+
)
22+
cancel_reason: Optional[str] = Field(
23+
description="The reason for cancellation if the decision is to cancel research.",
24+
default=None,
25+
)
26+
27+
28+
async def plan_research(
29+
memory: SimpleComposableMemory,
30+
context_nodes: List[Node],
31+
user_request: str,
32+
) -> AnalysisDecision:
33+
analyze_prompt = PromptTemplate(
34+
"""
35+
You are a professor who is guiding a researcher to research a specific request/problem.
36+
Your task is to decide on a research plan for the researcher.
37+
The possible actions are:
38+
+ Provide a list of questions for the researcher to investigate, with the purpose of clarifying the request.
39+
+ Write a report if the researcher has already gathered enough research on the topic and can resolve the initial request.
40+
+ Cancel the research if most of the answers from researchers indicate there is insufficient information to research the request. Do not attempt more than 3 research iterations or too many questions.
41+
The workflow should be:
42+
+ Always begin by providing some initial questions for the researcher to investigate.
43+
+ Analyze the provided answers against the initial topic/request. If the answers are insufficient to resolve the initial request, provide additional questions for the researcher to investigate.
44+
+ If the answers are sufficient to resolve the initial request, instruct the researcher to write a report.
45+
<User request>
46+
{user_request}
47+
</User request>
48+
49+
<Collected information>
50+
{context_str}
51+
</Collected information>
52+
53+
<Conversation context>
54+
{conversation_context}
55+
</Conversation context>
56+
"""
57+
)
58+
conversation_context = "\n".join(
59+
[f"{message.role}: {message.content}" for message in memory.get_all()]
60+
)
61+
context_str = "\n".join(
62+
[node.get_content(metadata_mode=MetadataMode.LLM) for node in context_nodes]
63+
)
64+
res = await Settings.llm.astructured_predict(
65+
output_cls=AnalysisDecision,
66+
prompt=analyze_prompt,
67+
user_request=user_request,
68+
context_str=context_str,
69+
conversation_context=conversation_context,
70+
)
71+
return res
72+
73+
74+
async def research(
75+
question: str,
76+
context_nodes: List[NodeWithScore],
77+
) -> str:
78+
prompt = """
79+
You are a researcher who is in the process of answering the question.
80+
The purpose is to answer the question based on the collected information, without using prior knowledge or making up any new information.
81+
Always add citations to the sentence/point/paragraph using the id of the provided content.
82+
The citation should follow this format: [citation:id]() where id is the id of the content.
83+
84+
E.g:
85+
If we have a context like this:
86+
<Citation id='abc-xyz'>
87+
Baby llama is called cria
88+
</Citation id='abc-xyz'>
89+
90+
And your answer uses the content, then the citation should be:
91+
- Baby llama is called cria [citation:abc-xyz]()
92+
93+
Here is the provided context for the question:
94+
<Collected information>
95+
{context_str}
96+
</Collected information>`
97+
98+
No prior knowledge, just use the provided context to answer the question: {question}
99+
"""
100+
context_str = "\n".join(
101+
[_get_text_node_content_for_citation(node) for node in context_nodes]
102+
)
103+
res = await Settings.llm.acomplete(
104+
prompt=prompt.format(question=question, context_str=context_str),
105+
)
106+
return res.text
107+
108+
109+
async def write_report(
110+
memory: SimpleComposableMemory,
111+
user_request: str,
112+
stream: bool = False,
113+
) -> CompletionResponse | CompletionResponseAsyncGen:
114+
report_prompt = """
115+
You are a researcher writing a report based on a user request and the research context.
116+
You have researched various perspectives related to the user request.
117+
The report should provide a comprehensive outline covering all important points from the researched perspectives.
118+
Create a well-structured outline for the research report that covers all the answers.
119+
120+
# IMPORTANT when writing in markdown format:
121+
+ Use tables or figures where appropriate to enhance presentation.
122+
+ Preserve all citation syntax (the `[citation:id]()` parts in the provided context). Keep these citations in the final report - no separate reference section is needed.
123+
+ Do not add links, a table of contents, or a references section to the report.
124+
125+
<User request>
126+
{user_request}
127+
</User request>
128+
129+
<Research context>
130+
{research_context}
131+
</Research context>
132+
133+
Now, write a report addressing the user request based on the research provided following the format and guidelines above.
134+
"""
135+
research_context = "\n".join(
136+
[f"{message.role}: {message.content}" for message in memory.get_all()]
137+
)
138+
139+
llm_complete_func = (
140+
Settings.llm.astream_complete if stream else Settings.llm.acomplete
141+
)
142+
143+
res = await llm_complete_func(
144+
prompt=report_prompt.format(
145+
user_request=user_request,
146+
research_context=research_context,
147+
),
148+
)
149+
return res
150+
151+
152+
def _get_text_node_content_for_citation(node: NodeWithScore) -> str:
153+
"""
154+
Construct node content for LLM with citation flag.
155+
"""
156+
node_id = node.node.node_id
157+
content = f"<Citation id='{node_id}'>\n{node.get_content(metadata_mode=MetadataMode.LLM)}</Citation id='{node_id}'>"
158+
return content

0 commit comments

Comments
 (0)