init
This commit is contained in:
48
AlgoriAgent/examples/conversation_with_RAG_agents/README.md
Normal file
48
AlgoriAgent/examples/conversation_with_RAG_agents/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# AgentScope Copilot: a Multi-Agent RAG Application
|
||||
|
||||
* **What is this example about?**
|
||||
With the provided implementation and configuration,
|
||||
you will obtain three different agents who can help you answer different questions about AgentScope.
|
||||
|
||||
* **What is this example for?** By this example, we want to show how the agent with retrieval augmented generation (RAG)
|
||||
capability can be used to build easily.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
* **Cloning repo:** This example requires cloning the whole AgentScope repo to local.
|
||||
* **Packages:** This example is built on the LlamaIndex package. Thus, some packages need to be installed before running the example.
|
||||
```bash
|
||||
pip install llama-index==0.10.30 llama-index-readers-docstring-walker==0.1.3 tree-sitter==0.21.3 tree-sitter-languages==1.10.2
|
||||
```
|
||||
* **Model APIs:** This example uses Dashscope APIs. Thus, we also need an API key for DashScope.
|
||||
```bash
|
||||
export DASHSCOPE_API_KEY='YOUR_API_KEY'
|
||||
```
|
||||
|
||||
**Note:** This example has been tested with `dashscope_chat` and `dashscope_text_embedding` model wrapper, with `qwen-max` and `text-embedding-v2` models.
|
||||
However, you are welcome to replace the Dashscope language and embedding model wrappers or models with other models you like to test.
|
||||
|
||||
## Start AgentScope Copilot
|
||||
* **Terminal:** The most simple way to execute the AgentScope Copilot is running in terminal.
|
||||
```bash
|
||||
python ./rag_example.py
|
||||
```
|
||||
|
||||
|
||||
* **AS gradio:** If you want to have more organized, clean UI, you can also run with our `as_gradio`.
|
||||
```bash
|
||||
as_gradio ./rag_example.py
|
||||
```
|
||||
|
||||
### Agents in the example
|
||||
After you run the example, you may notice that this example consists of three RAG agents:
|
||||
* `Tutorial-Assistant`: responsible for answering questions based on AgentScope tutorials (markdown files).
|
||||
* `Code-Search-Assistant`: responsible for answering questions based on AgentScope code base (python files).
|
||||
* `API-Assistant`: responsible for answering questions based on AgentScope API documents (html files, generated by `sphinx`)
|
||||
* `Searching-Assistant`: responsible for general search in tutorial and code base (markdown files and code files)
|
||||
* `Agent-Guiding-Assistant`: responsible for referring the correct agent(s) among the above ones.
|
||||
|
||||
Besides the last `Agent-Guiding-Assistant`, all other agents can be configured to answering questions based on other GitHub repo by replacing the `knowledge`.
|
||||
|
||||
For more details about how to use the RAG module in AgentScope, please refer to the tutorial.
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
[
|
||||
{
|
||||
"class": "LlamaIndexAgent",
|
||||
"args": {
|
||||
"name": "Tutorial-Assistant",
|
||||
"description": "Tutorial-Assistant is an agent that can provide answer based on English tutorial material, mainly the markdown files. It can answer general questions about AgentScope.",
|
||||
"sys_prompt": "You're an assistant helping new users to use AgentScope. The language style is helpful and cheerful. You generate answers based on the provided context. The answer is expected to be no longer than 100 words. If the key words of the question can be found in the provided context, the answer should contain the section name which contains the answer. For example, 'You may refer to SECTION_NAME for more details.'",
|
||||
"model_config_name": "qwen_config",
|
||||
"knowledge_id_list": ["agentscope_tutorial_rag"],
|
||||
"similarity_top_k": 5,
|
||||
"log_retrieval": false,
|
||||
"recent_n_mem_for_retrieve": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "LlamaIndexAgent",
|
||||
"args": {
|
||||
"name": "Code-Search-Assistant",
|
||||
"description": "Code-Search-Assistant is an agent that can provide answer based on AgentScope code base. It can answer questions about specific modules in AgentScope.",
|
||||
"sys_prompt": "You're a coding assistant of AgentScope. The answer starts with appreciation for the question, then provide details regarding the functionality and features of the modules mentioned in the question. The language should be in a professional and simple style. The answer is limited to be less than 100 words.",
|
||||
"model_config_name": "qwen_config",
|
||||
"knowledge_id_list": ["agentscope_code_rag"],
|
||||
"similarity_top_k": 5,
|
||||
"log_retrieval": false,
|
||||
"recent_n_mem_for_retrieve": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "LlamaIndexAgent",
|
||||
"args": {
|
||||
"name": "API-Assistant",
|
||||
"description": "API-Assistant is an agent that can answer questions about APIs in AgentScope. It can answer general questions about AgentScope.",
|
||||
"sys_prompt": "You're an assistant providing answers to the questions related to APIs (functions and classes) in AgentScope. The language style is helpful and cheerful. You generate answers based on the provided context. The answer is expected to be no longer than 200 words. If the key words of the question can be found in the provided context, the answer should contain the module of the API. For example, 'You may refer to MODULE_NAME for more details.'",
|
||||
"model_config_name": "qwen_config",
|
||||
"knowledge_id_list": ["agentscope_api_rag"],
|
||||
"similarity_top_k": 2,
|
||||
"log_retrieval": true,
|
||||
"recent_n_mem_for_retrieve": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "LlamaIndexAgent",
|
||||
"args": {
|
||||
"name": "Searching-Assistant",
|
||||
"description": "Search-Assistant is an agent that can provide answer based on AgentScope code and tutorial. It can answer questions about everything in AgentScope codes and tutorials.",
|
||||
"sys_prompt": "You're a helpful assistant of AgentScope. The answer starts with appreciation for the question, then provide output the location of the code or section that the most relevant to the question. The answer is limited to be less than 50 words.",
|
||||
"model_config_name": "qwen_config",
|
||||
"knowledge_id_list": ["agentscope_code_rag","agentscope_tutorial_rag"],
|
||||
"similarity_top_k": 5,
|
||||
"log_retrieval": false,
|
||||
"recent_n_mem_for_retrieve": 1,
|
||||
"persist_dir": "./rag_storage/searching_assist"
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "DialogAgent",
|
||||
"args": {
|
||||
"name": "Agent-Guiding-Assistant",
|
||||
"sys_prompt": "You're an assistant guiding the user to specific agent for help. The answer is in a cheerful styled language. The output starts with appreciation for the question. Next, rephrase the question in a simple declarative Sentence for example, 'I think you are asking...'. Last, if the question is about detailed code or example in AgentScope Framework, output '@ Code-Search-Assistant you might be suitable for answering the question'; if the question is about API or function calls (Example: 'Is there function related...' or 'how can I initialize ...' ) in AgentScope, output '@ API-Assistant, I think you are more suitable for the question, please tell us more about it'; if question is about where to find some context (Example:'where can I find...'), output '@ Searching-Assistant, we need your help', otherwise, output '@ Tutorial-Assistant, I think you are more suitable for the question, can you tell us more about it?'. The answer is expected to be only one sentence",
|
||||
"model_config_name": "qwen_config",
|
||||
"use_memory": false
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,114 @@
|
||||
[
|
||||
{
|
||||
"knowledge_id": "agentscope_code_rag",
|
||||
"emb_model_config_name": "qwen_emb_config",
|
||||
"chunk_size": 2048,
|
||||
"chunk_overlap": 40,
|
||||
"data_processing": [
|
||||
{
|
||||
"load_data": {
|
||||
"loader": {
|
||||
"create_object": true,
|
||||
"module": "llama_index.core",
|
||||
"class": "SimpleDirectoryReader",
|
||||
"init_args": {
|
||||
"input_dir": "../../src/agentscope",
|
||||
"recursive": true,
|
||||
"required_exts": [
|
||||
".py"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"store_and_index": {
|
||||
"transformations": [
|
||||
{
|
||||
"create_object": true,
|
||||
"module": "llama_index.core.node_parser",
|
||||
"class": "CodeSplitter",
|
||||
"init_args": {
|
||||
"language": "python",
|
||||
"chunk_lines": 100
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"knowledge_id": "agentscope_api_rag",
|
||||
"emb_model_config_name": "qwen_emb_config",
|
||||
"chunk_size": 1024,
|
||||
"chunk_overlap": 40,
|
||||
"data_processing": [
|
||||
{
|
||||
"load_data": {
|
||||
"loader": {
|
||||
"create_object": true,
|
||||
"module": "llama_index.core",
|
||||
"class": "SimpleDirectoryReader",
|
||||
"init_args": {
|
||||
"input_dir": "../../docs/docstring_html/",
|
||||
"required_exts": [
|
||||
".html"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"knowledge_id": "agentscope_global_rag",
|
||||
"emb_model_config_name": "qwen_emb_config",
|
||||
"chunk_size": 2048,
|
||||
"chunk_overlap": 40,
|
||||
"data_processing": [
|
||||
{
|
||||
"load_data": {
|
||||
"loader": {
|
||||
"create_object": true,
|
||||
"module": "llama_index.core",
|
||||
"class": "SimpleDirectoryReader",
|
||||
"init_args": {
|
||||
"input_dir": "../../docs/sphinx_doc/en/source/tutorial",
|
||||
"required_exts": [
|
||||
".md"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"load_data": {
|
||||
"loader": {
|
||||
"create_object": true,
|
||||
"module": "llama_index.core",
|
||||
"class": "SimpleDirectoryReader",
|
||||
"init_args": {
|
||||
"input_dir": "../../src/agentscope",
|
||||
"recursive": true,
|
||||
"required_exts": [
|
||||
".py"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"store_and_index": {
|
||||
"transformations": [
|
||||
{
|
||||
"create_object": true,
|
||||
"module": "llama_index.core.node_parser",
|
||||
"class": "CodeSplitter",
|
||||
"init_args": {
|
||||
"language": "python",
|
||||
"chunk_lines": 100
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"model_type": "dashscope_text_embedding",
|
||||
"config_name": "qwen_emb_config",
|
||||
"model_name": "text-embedding-v2",
|
||||
"api_key": ""
|
||||
},
|
||||
{
|
||||
"model_type": "dashscope_chat",
|
||||
"config_name": "qwen_config",
|
||||
"model_name": "qwen-max",
|
||||
"api_key": ""
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Group chat utils."""
|
||||
import re
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
def select_next_one(agents: Sequence, rnd: int) -> Sequence:
|
||||
"""
|
||||
Select next agent.
|
||||
"""
|
||||
return agents[rnd % len(agents)]
|
||||
|
||||
|
||||
def filter_agents(string: str, agents: Sequence) -> Sequence:
|
||||
"""
|
||||
This function filters the input string for occurrences of the given names
|
||||
prefixed with '@' and returns a list of the found names.
|
||||
"""
|
||||
if len(agents) == 0:
|
||||
return []
|
||||
|
||||
# Create a pattern that matches @ followed by any of the candidate names
|
||||
pattern = (
|
||||
r"@(" + "|".join(re.escape(agent.name) for agent in agents) + r")\b"
|
||||
)
|
||||
|
||||
# Find all occurrences of the pattern in the string
|
||||
matches = re.findall(pattern, string)
|
||||
|
||||
# Create a dictionary mapping agent names to agent objects for quick lookup
|
||||
agent_dict = {agent.name: agent for agent in agents}
|
||||
|
||||
# Return the list of matched agent objects preserving the order
|
||||
ordered_agents = [
|
||||
agent_dict[name] for name in matches if name in agent_dict
|
||||
]
|
||||
return ordered_agents
|
||||
148
AlgoriAgent/examples/conversation_with_RAG_agents/rag_example.py
Normal file
148
AlgoriAgent/examples/conversation_with_RAG_agents/rag_example.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
An example for conversation between user and agents with RAG capability.
|
||||
One agent is a tutorial assistant, the other is a code explainer.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
|
||||
from groupchat_utils import filter_agents
|
||||
|
||||
import agentscope
|
||||
from agentscope.agents import UserAgent
|
||||
from agentscope.rag import KnowledgeBank
|
||||
|
||||
|
||||
AGENT_CHOICE_PROMPT = """
|
||||
There are following available agents. You need to choose the most appropriate
|
||||
agent(s) to answer the user's question.
|
||||
|
||||
agent descriptions:{}
|
||||
|
||||
First, rephrase the user's question, which must contain the key information.
|
||||
The you need to think step by step. If you believe some of the agents are
|
||||
good candidates to answer the question (e.g., AGENT_1 and AGENT_2), then
|
||||
you need to follow the following format to generate your output:
|
||||
|
||||
'
|
||||
Because $YOUR_REASONING.
|
||||
I believe @AGENT_1 and @AGENT_2 are the most appropriate agents to answer
|
||||
your question.
|
||||
'
|
||||
"""
|
||||
|
||||
|
||||
def prepare_docstring_html() -> None:
|
||||
"""prepare docstring in html for API assistant"""
|
||||
if not os.path.exists("../../docs/docstring_html/"):
|
||||
os.system(
|
||||
"sphinx-apidoc -f -o ../../docs/sphinx_doc/en/source "
|
||||
"../../src/agentscope -t template",
|
||||
)
|
||||
os.system(
|
||||
"sphinx-build -b html ../../docs/sphinx_doc/en/source "
|
||||
"../../docs/docstring_html/ -W --keep-going",
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""A RAG multi-agent demo"""
|
||||
# prepare html for api agent
|
||||
prepare_docstring_html()
|
||||
|
||||
# prepare models
|
||||
with open("configs/model_config.json", "r", encoding="utf-8") as f:
|
||||
model_configs = json.load(f)
|
||||
|
||||
# load config of the agents
|
||||
with open("configs/agent_config.json", "r", encoding="utf-8") as f:
|
||||
agent_configs = json.load(f)
|
||||
|
||||
agent_list = agentscope.init(
|
||||
model_configs=model_configs,
|
||||
agent_configs=agent_configs,
|
||||
project="Conversation with RAG agents",
|
||||
)
|
||||
rag_agent_list = agent_list[:4]
|
||||
guide_agent = agent_list[4]
|
||||
|
||||
# the knowledge bank can be configured by loading config file
|
||||
knowledge_bank = KnowledgeBank(configs="configs/knowledge_config.json")
|
||||
|
||||
# alternatively, we can easily input the configs to add data to RAG
|
||||
knowledge_bank.add_data_as_knowledge(
|
||||
knowledge_id="agentscope_tutorial_rag",
|
||||
emb_model_name="qwen_emb_config",
|
||||
data_dirs_and_types={
|
||||
"../../docs/sphinx_doc/en/source/tutorial": [".md"],
|
||||
},
|
||||
)
|
||||
|
||||
# let knowledgebank to equip rag agent with a (set of) knowledge
|
||||
# corresponding to its knowledge_id_list
|
||||
for agent in rag_agent_list:
|
||||
knowledge_bank.equip(agent, agent.knowledge_id_list)
|
||||
|
||||
# an alternative way is to provide knowledge list to agents
|
||||
# when initializing them one by one, e.g.
|
||||
#
|
||||
# ```
|
||||
# knowledge = knowledge_bank.get_knowledge(knowledge_id)
|
||||
# agent = LlamaIndexAgent(
|
||||
# name="rag_worker",
|
||||
# sys_prompt="{your_prompt}",
|
||||
# model_config_name="{your_model}",
|
||||
# knowledge_list=[knowledge], # provide knowledge object directly
|
||||
# similarity_top_k=3,
|
||||
# log_retrieval=False,
|
||||
# recent_n_mem_for_retrieve=1,
|
||||
# )
|
||||
# ```
|
||||
|
||||
rag_agent_names = [agent.name for agent in rag_agent_list]
|
||||
# update guide agent system prompt with the descriptions of rag agents
|
||||
rag_agent_descriptions = [
|
||||
"agent name: "
|
||||
+ agent.name
|
||||
+ "\n agent description:"
|
||||
+ agent.description
|
||||
+ "\n"
|
||||
for agent in rag_agent_list
|
||||
]
|
||||
|
||||
guide_agent.sys_prompt = (
|
||||
guide_agent.sys_prompt
|
||||
+ AGENT_CHOICE_PROMPT.format(
|
||||
"".join(rag_agent_descriptions),
|
||||
)
|
||||
)
|
||||
|
||||
user_agent = UserAgent()
|
||||
while True:
|
||||
# The workflow is the following:
|
||||
# 1. user input a message,
|
||||
# 2. if it mentions (@) one of the agents, the agent will be called
|
||||
# 3. otherwise, the guide agent will decide which agent to call
|
||||
# 4. the called agent will respond to the user
|
||||
# 5. repeat
|
||||
x = user_agent()
|
||||
x.role = "user" # to enforce dashscope requirement on roles
|
||||
if len(x["content"]) == 0 or str(x["content"]).startswith("exit"):
|
||||
break
|
||||
speak_list = filter_agents(x.get("content", ""), rag_agent_list)
|
||||
if len(speak_list) == 0:
|
||||
guide_response = guide_agent(x)
|
||||
# Only one agent can be called in the current version,
|
||||
# we may support multi-agent conversation later
|
||||
speak_list = filter_agents(
|
||||
guide_response.get("content", ""),
|
||||
rag_agent_list,
|
||||
)
|
||||
agent_name_list = [agent.name for agent in speak_list]
|
||||
for agent_name, agent in zip(agent_name_list, speak_list):
|
||||
if agent_name in rag_agent_names:
|
||||
agent(x)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user