Skip to content

feat: Use selected LlamaCloud index in multi-agent template #350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/witty-hotels-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-llama": patch
---

Use selected LlamaCloud index in multi-agent template
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ async def chat(
# but agent workflow does not support them yet
# ignore chat params and use all documents for now
# TODO: generate filters based on doc_ids
# TODO: use chat params
engine = get_chat_engine(chat_history=messages)
params = data.data or {}
engine = get_chat_engine(chat_history=messages, params=params)

event_handler = engine.run(input=last_message_content, streaming=True)
return VercelStreamResponse(
Expand Down
6 changes: 3 additions & 3 deletions templates/components/multiagent/python/app/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ def get_chat_engine(
agent_type = os.getenv("EXAMPLE_TYPE", "").lower()
match agent_type:
case "choreography":
agent = create_choreography(chat_history)
agent = create_choreography(chat_history, **kwargs)
case "orchestrator":
agent = create_orchestrator(chat_history)
agent = create_orchestrator(chat_history, **kwargs)
case _:
agent = create_workflow(chat_history)
agent = create_workflow(chat_history, **kwargs)

logger.info(f"Using agent pattern: {agent_type}")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from llama_index.core.chat_engine.types import ChatMessage


def create_choreography(chat_history: Optional[List[ChatMessage]] = None):
researcher = create_researcher(chat_history)
def create_choreography(chat_history: Optional[List[ChatMessage]] = None, **kwargs):
researcher = create_researcher(chat_history, **kwargs)
publisher = create_publisher(chat_history)
reviewer = FunctionCallingAgent(
name="reviewer",
Expand All @@ -21,12 +21,14 @@ def create_choreography(chat_history: Optional[List[ChatMessage]] = None):
name="writer",
agents=[researcher, reviewer, publisher],
description="expert in writing blog posts, needs researched information and images to write a blog post",
system_prompt=dedent("""
system_prompt=dedent(
"""
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.
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.
You can consult the reviewer and researcher a maximum of two times. Your output should contain only the blog post.
Finally, always request the publisher to create a document (PDF, HTML) and publish the blog post.
"""),
"""
),
# TODO: add chat_history support to AgentCallingAgent
# chat_history=chat_history,
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,32 @@
from llama_index.core.chat_engine.types import ChatMessage


def create_orchestrator(chat_history: Optional[List[ChatMessage]] = None):
researcher = create_researcher(chat_history)
def create_orchestrator(chat_history: Optional[List[ChatMessage]] = None, **kwargs):
researcher = create_researcher(chat_history, **kwargs)
writer = FunctionCallingAgent(
name="writer",
description="expert in writing blog posts, need information and images to write a post",
system_prompt=dedent("""
system_prompt=dedent(
"""
You are an expert in writing blog posts.
You are given a task to write a blog post. Do not make up any information yourself.
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".
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.
If you have all the information needed, write the blog post.
"""),
"""
),
chat_history=chat_history,
)
reviewer = FunctionCallingAgent(
name="reviewer",
description="expert in reviewing blog posts, needs a written blog post to review",
system_prompt=dedent("""
system_prompt=dedent(
"""
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.
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.
Especially check for logical inconsistencies and proofread the post for grammar and spelling errors.
"""),
"""
),
chat_history=chat_history,
)
publisher = create_publisher(chat_history)
Expand Down
22 changes: 13 additions & 9 deletions templates/components/multiagent/python/app/examples/researcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
from typing import List

from app.agents.single import FunctionCallingAgent
from app.engine.index import get_index
from app.engine.index import IndexConfig, get_index
from app.engine.tools import ToolFactory
from llama_index.core.chat_engine.types import ChatMessage
from llama_index.core.tools import QueryEngineTool, ToolMetadata


def _create_query_engine_tool() -> QueryEngineTool:
def _create_query_engine_tool(params=None) -> QueryEngineTool:
"""
Provide an agent worker that can be used to query the index.
"""
index = get_index()
# Add query tool if index exists
index_config = IndexConfig(**(params or {}))
index = get_index(index_config)
Comment on lines +12 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Specify type annotations for params

To improve code clarity and type safety, consider adding a type annotation to params. Since it's expected to be a dictionary of parameters, you could define it as Optional[Dict].

Apply this diff to add the type annotation:

-def _create_query_engine_tool(params=None) -> QueryEngineTool:
+def _create_query_engine_tool(params: Optional[Dict] = None) -> QueryEngineTool:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _create_query_engine_tool(params=None) -> QueryEngineTool:
"""
Provide an agent worker that can be used to query the index.
"""
index = get_index()
# Add query tool if index exists
index_config = IndexConfig(**(params or {}))
index = get_index(index_config)
def _create_query_engine_tool(params: Optional[Dict] = None) -> QueryEngineTool:
"""
Provide an agent worker that can be used to query the index.
"""
# Add query tool if index exists
index_config = IndexConfig(**(params or {}))
index = get_index(index_config)

if index is None:
return None
top_k = int(os.getenv("TOP_K", 0))
Expand All @@ -31,13 +33,13 @@ def _create_query_engine_tool() -> QueryEngineTool:
)


def _get_research_tools() -> QueryEngineTool:
def _get_research_tools(**kwargs) -> QueryEngineTool:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct return type annotation in _get_research_tools

The function returns a list of QueryEngineTools, but the return type annotation is -> QueryEngineTool. Update the return type to List[QueryEngineTool] for accuracy.

Apply this diff to fix the return type annotation:

-from typing import List
+from typing import List, Dict, Optional

-def _get_research_tools(**kwargs) -> QueryEngineTool:
+def _get_research_tools(**kwargs) -> List[QueryEngineTool]:

Committable suggestion was skipped due to low confidence.

"""
Researcher take responsibility for retrieving information.
Try init wikipedia or duckduckgo tool if available.
"""
tools = []
query_engine_tool = _create_query_engine_tool()
query_engine_tool = _create_query_engine_tool(**kwargs)
if query_engine_tool is not None:
tools.append(query_engine_tool)
researcher_tool_names = ["duckduckgo", "wikipedia.WikipediaToolSpec"]
Expand All @@ -48,16 +50,17 @@ def _get_research_tools() -> QueryEngineTool:
return tools


def create_researcher(chat_history: List[ChatMessage]):
def create_researcher(chat_history: List[ChatMessage], **kwargs):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type annotations for **kwargs in create_researcher

Consider specifying the expected types within **kwargs for better code clarity and to assist developers in understanding what parameters can be passed.

"""
Researcher is an agent that take responsibility for using tools to complete a given task.
"""
tools = _get_research_tools()
tools = _get_research_tools(**kwargs)
return FunctionCallingAgent(
name="researcher",
tools=tools,
description="expert in retrieving any unknown content or searching for images from the internet",
system_prompt=dedent("""
system_prompt=dedent(
"""
You are a researcher agent. You are given a research task.

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.
Expand All @@ -77,6 +80,7 @@ def create_researcher(chat_history: List[ChatMessage]):

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.
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."
"""),
"""
),
chat_history=chat_history,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
)


def create_workflow(chat_history: Optional[List[ChatMessage]] = None):
def create_workflow(chat_history: Optional[List[ChatMessage]] = None, **kwargs):
researcher = create_researcher(
chat_history=chat_history,
**kwargs,
)
publisher = create_publisher(
chat_history=chat_history,
Expand Down Expand Up @@ -127,7 +128,8 @@ async def _decide_workflow(
self, input: str, chat_history: List[ChatMessage]
) -> str:
prompt_template = PromptTemplate(
dedent("""
dedent(
"""
You are an expert in decision-making, helping people write and publish blog posts.
If the user is asking for a file or to publish content, respond with 'publish'.
If the user requests to write or update a blog post, respond with 'not_publish'.
Expand All @@ -140,7 +142,8 @@ async def _decide_workflow(

Given the chat history and the new user request, decide whether to publish based on existing information.
Decision (respond with either 'not_publish' or 'publish'):
""")
"""
)
)

chat_history_str = "\n".join(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async def chat_config() -> ChatConfig:
try:
from app.engine.service import LLamaCloudFileService

logger.info("LlamaCloud is configured. Adding /config/llamacloud route.")
print("LlamaCloud is configured. Adding /config/llamacloud route.")

@r.get("/llamacloud")
async def chat_llama_cloud_config():
Expand All @@ -42,7 +42,5 @@ async def chat_llama_cloud_config():
}

except ImportError:
logger.debug(
"LlamaCloud is not configured. Skipping adding /config/llamacloud route."
)
print("LlamaCloud is not configured. Skipping adding /config/llamacloud route.")
pass
Loading