From 2e3dc2d5faf53a616038b45647770b7bd743ec87 Mon Sep 17 00:00:00 2001 From: Mo Zhou Date: Tue, 10 Dec 2024 19:04:48 +0800 Subject: [PATCH] docs: improve `agent workflow` documentation --- docs/explanation/agent-workflow.md | 131 +++++++++++++++++++++++++++-- docs/static/result.png | Bin 0 -> 22794 bytes 2 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 docs/static/result.png diff --git a/docs/explanation/agent-workflow.md b/docs/explanation/agent-workflow.md index 871113a..27bc4f1 100644 --- a/docs/explanation/agent-workflow.md +++ b/docs/explanation/agent-workflow.md @@ -2,11 +2,132 @@ The Agent Workflow is the core functionality of the `tablegpt-agent`. It processes user input and generates appropriate responses. This workflow is similar to those found in most single-agent systems and consists of an agent and various tools. Specifically, the data analysis workflow includes: -- **An Agent Powered by TableGPT2**: This agent performs data analysis tasks. -- **An IPython tool**: This tool executes the generated code within a sandbox environment. +- **An Agent Powered by TableGPT2**: This agent performs data analysis tasks. It is designed to understand and execute complex data analysis queries, providing accurate and insightful results. +- **An IPython tool**: This tool executes the generated code within a sandbox environment, ensuring that the code runs safely and efficiently. Additionally, TableGPT Agent offers several optional plugins that extend the agent's functionality: -- A Visual Language Model that can be used to enhance summarization for data visualization tasks. -- A retriever that fetches information about the dataset, improving the quality and relevance of the generated code. -- A safety mechanism that protects the system from toxic inputs. +- **Visual Language Model**: This plugin can be used to enhance summarization for data visualization tasks. +- **Retriever**: This plugin fetches information about the dataset, improving the quality and relevance of the generated code. +- **Safety Mechanism**: This plugin protects the system from toxic inputs. + +## Workflow Steps + +1. **User Input**: The user provides a query or command to the agent. +2. **Security Assessment (optional)**: The agent evaluates whether the user's query involves sensitive topics. If it does, the agent will prompt the LLM to be cautious in its response. +3. **Data Retrieval(optional)**: The retriever plugin fetches relevant data and metadata. +4. **Code Generation**: The agent generates the appropriate code to perform the requested task. +5. **Code Execution**: The generated code is executed in the IPython sandbox environment. +6. **Result Generation**: The agent processes the results of the code execution and generates a response. +7. **Visual analysis (optional)**: Perform visual analysis and summary on the generated pictures to enrich the answers. + +**NOTE:** During the operation of `tablegpt-agent`, the system will repeatedly attempt to resolve any issues that arise during code execution. As a result, Steps `4~7` may be executed multiple times in an iterative debugging process. This cycle continues until the final solution is reached or the maximum iteration limit is exceeded. The default maximum iteration count is **25**. + +## Example Workflow + +### User Input + +The user inputs a query: + +> Draw a pie chart of the male-female ratio based on gender. + +### Security Assessment (Optional) + +When creating a `tablegpt-agent`, you can specify a dedicated safety evaluation model using the `safety_llm` parameter. For example: + +```python +from langchain_openai import ChatOpenAI +from tablegpt.agent import create_tablegpt_graph + +safety_llm = ChatOpenAI(openai_api_base="LLM_URL", openai_api_key="whatever", model_name="LLM_MODEL_NAME") + +agent = create_tablegpt_graph( + ... + safety_llm=safety_llm, + ... +) +``` + +If `safety_llm` is configured, it will evaluate user inputs to identify if they fall under any of the following `14` hazard categories. If a question is flagged as belonging to one of these categories, `tablegpt-agent` will respond with caution. + +| Hazard Category | Description | +| ---- | ---- | +| S1: Violent Crimes | Violent criminal activities | +| S2: Non-Violent Crimes | Non-violent criminal activities | +| S3: Sex-Related Crimes | Crimes related to sexual misconduct | +| S4: Child Sexual Exploitation | Exploitation of children | +| S5: Defamation | Defamatory content | +| S6: Specialized Advice | Professional advice (e.g., medical, legal) | +| S7: Privacy | Privacy violations | +| S8: Intellectual Property | Intellectual property issues | +| S9: Indiscriminate Weapons | Use or production of indiscriminate weapons | +| S10: Hate | Hateful or discriminatory content | +| S11: Suicide & Self-Harm | Suicide or self-harm-related content | +| S12: Sexual Content | Explicit sexual content | +| S13: Elections | Content related to elections | +| S14: Code Interpreter Abuse | Misuse of code interpretation features | + +This feature enhances the safety of the `tablegpt-agent`, helping to mitigate ethical and legal risks associated with generated content. + +### Data Retrieval (optional) + +The retriever plugin recalls columns and values related to the query, enhancing the LLM's understanding of the dataset. This improves the accuracy of the code generated by the LLM. For detailed usage instructions, refer to [Enhance TableGPT Agent with RAG](../../howto/retrieval). + +For this example, based on the user’s input, the retrieved results are as follows: + +```pycon +Here are some extra column information that might help you understand the dataset:\n- titanic.csv:\n - {"column": Sex, "dtype": "string", "values": ["male", "female", ...]} +``` + +### Code Generation +The agent generates the following Python code: +```python +import seaborn as sns +import matplotlib.pyplot as plt + +# Count the number of males and females +gender_counts = df1['Sex'].value_counts() + +# Create a pie chart +plt.figure(figsize=(6, 6)) +plt.pie(gender_counts, labels=gender_counts.index, autopct='%1.1f%%', startangle=140) +plt.title('Gender Distribution') +plt.show() +``` + +### Code Execution + +The generated code is automatically executed in the IPython sandbox environment. + +### Result Generation + +After the execution is complete, the results are generated as follows: + +![result image](../static/result.png) + +### Visual Analysis (optional) + +The visual analysis plugin allows you to enhance generated results with visualizations, making the output more intuitive and informative. + +To enable this feature, you can pass the `vlm` parameter when creating a `tablegpt-agent`. Here’s an example: + +```python +from langchain_openai import ChatOpenAI +from tablegpt.agent import create_tablegpt_graph + +vlm = ChatOpenAI(openai_api_base="VLM_URL", openai_api_key="whatever", model_name="VLM_MODEL_NAME") + +agent = create_tablegpt_graph( + ... + vlm=vlm, + ... +) +``` + +Once enabled, the `tablegpt-agent` will use the `vlm` model to generate visual representations of the data. + +For instance, in response to the query mentioned earlier, the `tablegpt-agent` generates the following visualization: + +> *I have drawn a pie chart illustrating the ratio of men to women. From the chart, you can see that men constitute 64.4% while women make up 35.6%. If you need any further analysis or visualizations, feel free to let me know.* + +This feature adds a layer of clarity and insight, helping users interpret the results more effectively. On some complex graphs, this function is more effective. diff --git a/docs/static/result.png b/docs/static/result.png new file mode 100644 index 0000000000000000000000000000000000000000..ff63fa1129ea54061b0ed59d8ab4141e8bf329de GIT binary patch literal 22794 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 literal 0 HcmV?d00001