Skip to content

Commit

Permalink
Merge pull request #12 from BCG-X-Official/feat/tutorial_data
Browse files Browse the repository at this point in the history
Feat: Tutorial updates
  • Loading branch information
drivian committed Feb 6, 2024
2 parents 68282a5 + 09ff445 commit f913735
Show file tree
Hide file tree
Showing 33 changed files with 978 additions and 130 deletions.
7 changes: 3 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,10 @@ AGENT_CONFIG_PATH='app/config/agent.yml'
#############################################
# PDF Tool
#############################################
PDF_PATH='app/tool_constants/pdf_data'
PDF_TOOL_ENABLED="true"
PDF_TOOL_DATA_PATH='app/tool_constants/pdf_data' # backend/app/app/tool_constants/pdf_data for local dev
PDF_TOOL_ENABLED="true" # Set to "true" to enable the PDF tool.
PDF_TOOL_DATA_PATH='app/tool_constants/pdf_data'
PDF_TOOL_DATABASE='pdf_indexing_1'
PDF_TOOL_EXTRACTION_CONFIG_PATH='app/config/extraction.yml' # backend/app/app/config/extraction.yml for local dev
PDF_TOOL_EXTRACTION_CONFIG_PATH='app/config/extraction.yml'

#############################################
# Langsmith variables
Expand Down
41 changes: 36 additions & 5 deletions backend/app/app/db/vector_db_pdf_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from app.core.config import settings
from app.schemas.ingestion_schema import LOADER_DICT, IndexingConfig
from app.schemas.tool_schemas.pdf_tool_schema import MarkdownMetadata
from app.services.chat_agent.helpers.embedding_models import get_embedding_model
from app.utils.config_loader import get_ingestion_configs

Expand Down Expand Up @@ -60,21 +61,22 @@ def run(
return self._load_documents(folder_path=folder_path, collection_name=collection_name)
raise ValueError("folder_path must be provided if load_index is False")

def _pdf_to_docs(
def _load_docs(
self,
pdf_dir_path: str,
dir_path: str,
) -> List[Document]:
"""
Using specified PDF miner to convert PDF documents to raw text chunks.
Fallback: PyPDF
"""
documents = []
for file_name in os.listdir(pdf_dir_path):
for file_name in os.listdir(dir_path):
file_extension = os.path.splitext(file_name)[1].lower()
# Load PDF files
if file_extension == ".pdf":
logger.info(f"Loading {file_name} into vectorstore")
file_path = f"{pdf_dir_path}/{file_name}"
file_path = f"{dir_path}/{file_name}"
try:
loader: Any = self.pdf_loader(file_path) # type: ignore
file_docs = loader.load()
Expand All @@ -84,6 +86,35 @@ def _pdf_to_docs(
logger.error(
f"Could not extract text from PDF {file_name} with {self.pipeline_config.pdf_parser}: {repr(e)}"
)
# Load Markdown files
elif file_extension == ".md":
logger.info(f"Loading data from {file_name} as Document...")
file_path = f"{dir_path}/{file_name}"
try:
# Load md files as single document
with open(file_path, "r", encoding="utf-8") as f:
md_file = f.read()

md_doc = Document(
page_content=md_file,
metadata=MarkdownMetadata.parse_obj({"source": file_name, "type": "text"}).dict(),
)

# Further split at token-level, when splits are above chunk_size configuration (rare)
text_splitter = TokenTextSplitter(
chunk_size=self.pipeline_config.tokenizer_chunk_size,
chunk_overlap=self.pipeline_config.tokenizer_chunk_overlap,
)
file_docs = text_splitter.split_documents([md_doc])

documents.extend(file_docs)
if len(file_docs) > 1:
logger.info(
f"Split {file_name} to {len(file_docs)} documents due to "
f"chunk_size: ({self.pipeline_config.tokenizer_chunk_size})"
)
except Exception as e:
logger.error(f"Could not load MD file {file_name}: {repr(e)}")

return documents

Expand All @@ -93,7 +124,7 @@ def _load_documents(
collection_name: str,
) -> PGVector:
"""Load documents into vectorstore."""
text_documents = self._pdf_to_docs(folder_path)
text_documents = self._load_docs(folder_path)
text_splitter = TokenTextSplitter(
chunk_size=self.pipeline_config.tokenizer_chunk_size,
chunk_overlap=self.pipeline_config.tokenizer_chunk_overlap,
Expand Down
10 changes: 8 additions & 2 deletions backend/app/app/schemas/tool_schemas/pdf_tool_schema.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# -*- coding: utf-8 -*-
from typing import List
from typing import List, Optional

from pydantic import BaseModel
from pydantic import BaseModel, Field


class PdfAppendix(BaseModel):
doc_id: str
page_numbers: List[int]
reference_text: str


class MarkdownMetadata(BaseModel):
type: str
source: str
header1: Optional[str] = Field(None, alias="Header 1")
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,5 @@ async def _aqa_pdf_chunks(
)
),
]
response = await self._agenerate_response(question_messages)
logger.info(response)
response = await self._agenerate_response(question_messages, discard_fast_llm=True, run_manager=run_manager)
return response
29 changes: 29 additions & 0 deletions backend/app/app/tool_constants/tutorial_data/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Contributing to AgentKit

We want to make contributing to this project as easy and transparent as possible, whether it's:
- Reporting a bug and ideally submitting a fix
- Proposing new features
- Adding a tool to the library

## Guidelines
We use Github to host code, to track issues and feature requests, as well as accept pull requests.

We actively welcome your pull requests:
1. Fork the repo and create your branch from `develop`.
2. If you've changed key features (tools, APIs etc), update the documentation.
3. Ensure the code is clean: passes the set of tests, lints etc.
5. Issue a pull request to the main repo

## Report bugs and add feature requests using Github issues
Please not that *currently* there is no central team working on this repository that is actively resolving bugs and responding to feature requests.

### Bugs:
Provide
1) Summary of the issue
2) Steps to reproduce
3) What you expected vs what actually happened
4) Any notes on why you think this might be happening or things you tried that didn't work


### Features:
Provide a description of the feature you would like. You're encouraged to build it yourself and open a PR :-)
77 changes: 77 additions & 0 deletions backend/app/app/tool_constants/tutorial_data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<img src="static/AgentKit_logo_color.png" alt="AgentKit logo" style="width:500px;"/>

# AgentKit: rapidly build Agent apps
AgentKit is a LangChain-based toolkit developed by BCG X to build Agent apps. Key advantages of the AgentKit framework include:
- 🚀 **Quickly build high quality Agent apps**: Build a strong demo in a few hours using a modular, easy to configure tech stack based on FastAPI/Nextjs and a library of useful GenAI tools
- 💻 **Flexible, reactive UI/UX designed for Agents**: React/Nextjs chat-based UI that is easy to configure, with features such as streaming, rendering of tables/visualizations/code, status of Agent actions and more
- 🛡️ **Focus on reliability**: Easy to configure routing architecture gives control of possible paths Agent can take, increasing reliability and making it suited for real-life use cases

[Placeholder for demo video]

## Quickstart
For a quick setup of AgentKit, use the steps below, where both the backend app and frontend app are run inside a Docker container. More elaborate setup instructions can be found at [setup.md](docs/setup.md).

### Prerequisites
- Docker: https://www.docker.com/get-started

### Installation steps
1. Clone the repository containing the source code for the backend and frontend apps.

2. Copy the `frontend/.env.example` file in the frontend directory and change the name to `.env`. Also, copy the `.env.example` file in the root directory of the repository and change the name to `.env`.
- Change the OPENAI_API_KEY and OPENAI_ORGANIZATION to your own (n.b. OPENAI_ORGANIZATION should be your OpenAI 'Organization ID')

3. In the terminal, navigate to the root directory of the cloned repository. Build and start the Docker containers with the following command:
```
docker-compose -f docker-compose.yml up -d
```
Wait for the containers to build and start, which may take a few minutes depending on your system. Once the containers are up and running, you can access the apps in your browser at [http://localhost](http://localhost/).

## Chinook music database demo
- Ensure you are on the `develop` branch
- Follow the installation instructions above and run the app
- Try the prompt "When was AC/DC founded?" to see AgentKit in action!

## Set up your own app
- Configure your Agent and Tools [link](docs/configure_agent_and_tools.md)
- (Optional) Adjust the UI to your use case [link](docs/configure_ui.md)
- (Optional) Set up evaluation with LangSmith [link](docs/evaluation.md)

## Documentation
- [Installation instructions for running frontend or entire app outside Docker](docs/setup_development.md)
- [Key concepts](docs/key_concepts.md)
- [Agent configuration](docs/configure_agent.md)
- [UI configuration](docs/configure_ui.md)
- [Optional features](docs/optional_features.md)
- [Tool library](docs/tool_library.md)

## How it works

### Reliability
AgentKit attempts to solve the reliability issue of agents such as ReAct agents by constraining the potential routes the agent can take to a pre-configured sets of routes, or **Action Plans**. Since for many use cases the potential routes the agent can take are known, we can use our human domain expertise to steer the agent in the right direction, and reduce it going into unexpected directions or rabbit holes. This is achieved by combining a **Meta Agent** with **Action Plans**: A set of tools which are executed linearily and in parallel, similar to a Chain. The Meta Agent takes in the user prompt and outputs the most suited Action Plan to generate an answer. Note: implementing multiple Meta Agents is possible, generating a tree of possible routes.

### User experience
To optimize user experience, the intermediary output of every step in the Action Plan can be shown to the user. For example, consider an Action Plan consisting of 2 toolsets: `[[sql_tool, pdf_tool], [generate_summary_tool, visualize_tool]]`. In the first action step, information from a SQL database and a vector database with embedded PDFs are retrieved in parallel. The retrieved data and most relevant PDF are streamed to the UI as soon as the first action step finishes. In the second action step, the output from step 1 is passed to a tool that generates a text summary and a tool that creates a JSX visualization from the data, which is streamed to the UI to create the final answer.

For a high level overview of the routing flow and connection the UI, please see below diagram:
<img src="static/AgentKit_flow_diagram.png" alt="AgentKit framework" style="width:500px;"/>

## Additional optional features

- **Feedback integration**: allow users to give feedback on generated answers by showing a pop up after each message with quantitative (thumbs up/down) and qualitative (comment) feedback

[placeholder for picture of feedback feature]

- **User settings**: Allow users to specify default settings in the app that can be used to customize prompts for the user

[placeholder for picture of settings feature]

- **User authentication**: Enable NextAuth on your app to authenticate users with Github or with email/password

[placeholder for picture of authentication feature]

See [optional feature documentation](docs/optional_features.md) for more detailed info.

## Contributors

[Placeholder for contributor list]
Please read `CONTRIBUTING.md` for more details on how to contribute.
45 changes: 45 additions & 0 deletions backend/app/app/tool_constants/tutorial_data/backend_flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# AgentKit Backend Flow

The general backend flow is mainly constructed through the following four Python files, each serving a specific purpose.

## chat.py

This is the main backend entry point of AgentKit. It exposes a FastAPI endpoint at `/agent` which accepts POST requests.
The request body should contain a chat query, which is processed by the `agent_chat` function. This function creates a
conversation with an agent and returns an `StreamingJsonListResponse` object.

## meta_agent.py

This file contains functions for creating and managing a meta agent. A meta agent is an instance of the `AgentExecutor`
class, which is responsible for executing AgentKit's logic.
- The `create_meta_agent` function creates a meta agent from a given configuration.
- The `get_conv_token_buffer_memory` function retrieves the chat history and stores it in a
`ConversationTokenBufferMemory` object.

## SimpleRouterAgent.py

This file contains the `SimpleRouterAgent` class. This class is
responsible for managing AgentKit's actions based on the input it receives.
- The `aplan` function decides what actions the agent should take based on the input and the intermediate steps taken so far.
- The `create_prompt` function creates a prompt for the agent.
- The `from_llm_and_tools` function constructs an agent from a language model and a set of tools.

## get_tools.py

This file contains the `get_tools` function, which retrieves a list of tools from a list of tool names. Each tool class
is responsible for a specific functionality of AgentKit.

## Flow

1. A POST request is sent to the `/agent` endpoint with a chat query in the request body.
2. The `agent_chat` function in `chat.py` is called. This function retrieves the meta agent associated with the API key
specified in the chat query.
3. The `agent_chat` function creates a conversation with the agent, handles exceptions, and returns a streaming response.
4. The meta agent, which is an instance of the `AgentExecutor` class, executes AgentKit's logic. This logic is
determined by the `SimpleRouterAgent` class in `SimpleRouterAgent.py`.
5. The `SimpleRouterAgent` class decides what actions the agent should take based on the input it receives and the
intermediate steps taken so far.
6. The `get_tools` function in `get_tools.py` is called to retrieve the tools needed by the agent. These tools are used
to perform various tasks, such as generating images, summarizing text, executing SQL queries, etc.
7. The conversation continues until the agent decides to stop, at which point the `agent_chat` function returns a
`StreamingJsonListResponse` object containing the conversation history.
Loading

0 comments on commit f913735

Please sign in to comment.