diff --git a/.gitignore b/.gitignore index 3848c44..3a4c26b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,8 @@ code_base .DS_Store .idea data +.pyc tests +*egg-info +build +dist diff --git a/Dockerfile b/Dockerfile index f213f38..0d8d5f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,6 @@ From python:3.9.18-bookworm WORKDIR /home/user COPY ./requirements.txt /home/user/docker_requirements.txt -COPY ./jupyter_start.sh /home/user/jupyter_start.sh RUN apt-get update diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index 06a17fe..34f5aba 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -

- 中文  |  English  -

- #

CodeFuse-ChatBot: Development by Private Knowledge Augmentation

+ ZH doc + EN doc License Open Issues @@ -38,7 +36,7 @@ DevOps-ChatBot是由蚂蚁CodeFuse团队开发的开源AI智能助手,致力 💡 本项目旨在通过检索增强生成(Retrieval Augmented Generation,RAG)、工具学习(Tool Learning)和沙盒环境来构建软件开发全生命周期的AI智能助手,涵盖设计、编码、测试、部署和运维等阶段。 逐渐从各处资料查询、独立分散平台操作的传统开发运维模式转变到大模型问答的智能化开发运维模式,改变人们的开发运维习惯。 本项目核心差异技术、功能点: -- **🧠 智能调度核心:** 构建了体系链路完善的调度核心,支持多模式一键配置,简化操作流程。 [使用说明](sources/readme_docs/multi-agent.md) +- **🧠 智能调度核心:** 构建了体系链路完善的调度核心,支持多模式一键配置,简化操作流程。 [使用说明](sources/readme_docs/coagent/coagent.md) - **💻 代码整库分析:** 实现了仓库级的代码深入理解,以及项目文件级的代码编写与生成,提升了开发效率。 - **📄 文档分析增强:** 融合了文档知识库与知识图谱,通过检索和推理增强,为文档分析提供了更深层次的支持。 - **🔧 垂类专属知识:** 为DevOps领域定制的专属知识库,支持垂类知识库的自助一键构建,便捷实用。 @@ -93,7 +91,13 @@ DevOps-ChatBot是由蚂蚁CodeFuse团队开发的开源AI智能助手,致力 ## 🚀 快速使用 +### coagent-py +完整文档见:[coagent](sources/readme_docs/coagent/coagent.md) +``` +pip install coagent +``` +### 使用ChatBot 请自行安装 nvidia 驱动程序,本项目已在 Python 3.9.18,CUDA 11.7 环境下,Windows、X86 架构的 macOS 系统中完成测试。 Docker安装、私有化LLM接入及相关启动问题见:[快速使用明细](sources/readme_docs/start.md) @@ -155,12 +159,12 @@ NO_REMOTE_API = True ```bash # 若需要支撑codellama-34b-int4模型,需要给fastchat打一个补丁 # cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py -# dev_opsgpt/service/llm_api.py#258 修改为 kwargs={"gptq_wbits": 4}, +# examples/llm_api.py#258 修改为 kwargs={"gptq_wbits": 4}, # start llm-service(可选) -python dev_opsgpt/service/llm_api.py +python examples/llm_api.py ``` -更多LLM接入方法见[详情...](sources/readme_docs/fastchat.md) +更多LLM接入方法见[更多细节...](sources/readme_docs/fastchat.md)
```bash @@ -168,6 +172,12 @@ python dev_opsgpt/service/llm_api.py cd examples python start.py ``` +## 贡献指南 +非常感谢您对 Codefuse 项目感兴趣,我们非常欢迎您对 Codefuse 项目的各种建议、意见(包括批评)、评论和贡献。 + +您对 Codefuse 的各种建议、意见、评论可以直接通过 GitHub 的 Issues 提出。 + +参与 Codefuse 项目并为其作出贡献的方法有很多:代码实现、测试编写、流程工具改进、文档完善等等。任何贡献我们都会非常欢迎,并将您加入贡献者列表。详见[Contribution Guide...](sources/readme_docs/contribution/contribute_guide.md) ## 🤗 致谢 diff --git a/README_en.md b/README_en.md index 1f22eda..c3c6c45 100644 --- a/README_en.md +++ b/README_en.md @@ -1,10 +1,8 @@ -

- 中文  |  English  -

- #

Codefuse-ChatBot: Development by Private Knowledge Augmentation

+ ZH doc + EN doc License Open Issues @@ -15,6 +13,7 @@ This project is an open-source AI intelligent assistant, specifically designed f ## 🔔 Updates +- [2023.12.26] Opening the capability to integrate with open-source private large models and large model interfaces based on FastChat - [2023.12.01] Release of Multi-Agent and codebase retrieval functionalities. - [2023.11.15] Addition of Q&A enhancement mode based on the local codebase. - [2023.09.15] Launch of sandbox functionality for local/isolated environments, enabling knowledge retrieval from specified URLs using web crawlers. @@ -30,13 +29,13 @@ This project is an open-source AI intelligent assistant, specifically designed f 💡 The aim of this project is to construct an AI intelligent assistant for the entire lifecycle of software development, covering design, coding, testing, deployment, and operations, through Retrieval Augmented Generation (RAG), Tool Learning, and sandbox environments. It transitions gradually from the traditional development and operations mode of querying information from various sources and operating on standalone, disparate platforms to an intelligent development and operations mode based on large-model Q&A, changing people's development and operations habits. -- **🧠 Intelligent Scheduling Core:** Constructed a well-integrated scheduling core system that supports multi-mode one-click configuration, simplifying the operational process. +- **🧠 Intelligent Scheduling Core:** Constructed a well-integrated scheduling core system that supports multi-mode one-click configuration, simplifying the operational process. [coagent](sources/readme_docs/coagent/coagent-en.md) - **💻 Comprehensive Code Repository Analysis:** Achieved in-depth understanding at the repository level and coding and generation at the project file level, enhancing development efficiency. - **📄 Enhanced Document Analysis:** Integrated document knowledge bases with knowledge graphs, providing deeper support for document analysis through enhanced retrieval and reasoning. - **🔧 Industry-Specific Knowledge:** Tailored a specialized knowledge base for the DevOps domain, supporting the self-service one-click construction of industry-specific knowledge bases for convenience and practicality. - **🤖 Compatible Models for Specific Verticals:** Designed small models specifically for the DevOps field, ensuring compatibility with related DevOps platforms and promoting the integration of the technological ecosystem. -🌍 Relying on open-source LLM and Embedding models, this project can achieve offline private deployments based on open-source models. Additionally, this project also supports the use of the OpenAI API. +🌍 Relying on open-source LLM and Embedding models, this project can achieve offline private deployments based on open-source models. Additionally, this project also supports the use of the OpenAI API.[Access Demo](sources/readme_docs/fastchat-en.md) 👥 The core development team has been long-term focused on research in the AIOps + NLP domain. We initiated the CodefuseGPT project, hoping that everyone could contribute high-quality development and operations documents widely, jointly perfecting this solution to achieve the goal of "Making Development Seamless for Everyone." @@ -64,7 +63,7 @@ This project is an open-source AI intelligent assistant, specifically designed f - 💬 **LLM:**:Supports various open-source models and LLM interfaces. - 🛠️ **API Management::** Enables rapid integration of open-source components and operational platforms. -For implementation details, see: [Technical Route Details](sources/readme_docs/roadmap.md) +For implementation details, see: [Technical Route Details](sources/readme_docs/roadmap-en.md) ## 🌐 Model Integration @@ -79,7 +78,13 @@ If you need to integrate a specific model, please inform us of your requirements ## 🚀 Quick Start +### coagent-py +More Detail see:[coagent](sources/readme_docs/coagent/coagent-en.md) +``` +pip install coagent +``` +### ChatBot-UI Please install the Nvidia driver yourself; this project has been tested on Python 3.9.18, CUDA 11.7, Windows, and X86 architecture macOS systems. 1. Preparation of Python environment @@ -172,11 +177,13 @@ By default, only webui related services are started, and fastchat is not started ```bash # if use codellama-34b-int4, you should replace fastchat's gptq.py # cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py -# dev_opsgpt/service/llm_api.py#258 => kwargs={"gptq_wbits": 4}, +# examples/llm_api.py#258 => kwargs={"gptq_wbits": 4}, # start llm-service(可选) -python dev_opsgpt/service/llm_api.py +python examples/llm_api.py ``` +More details about accessing LLM Moldes[More Details...](sources/readme_docs/fastchat.md) +
```bash # After configuring server_config.py, you can start with just one click. @@ -184,6 +191,13 @@ cd examples bash start_webui.sh ``` +## 贡献指南 +Thank you for your interest in the Codefuse project. We warmly welcome any suggestions, opinions (including criticisms), comments, and contributions to the Codefuse project. + +Your suggestions, opinions, and comments on Codefuse can be directly submitted through GitHub Issues. + +There are many ways to participate in the Codefuse project and contribute to it: code implementation, test writing, process tool improvement, documentation enhancement, and more. We welcome any contributions and will add you to our list of contributors. See [contribution guide](sources/readme_docs/contribution/contribute_guide_en.md) + ## 🤗 Acknowledgements -This project is based on [langchain-chatchat](https://github.com/chatchat-space/Langchain-Chatchat) and [codebox-api](https://github.com/shroominic/codebox-api). We deeply appreciate their contributions to open source! +This project is based on [langchain-chatchat](https://github.com/chatchat-space/Langchain-Chatchat) and [codebox-api](https://github.com/shroominic/codebox-api). We deeply appreciate their contributions to open source! \ No newline at end of file diff --git a/dev_opsgpt/__init__.py b/coagent/__init__.py similarity index 100% rename from dev_opsgpt/__init__.py rename to coagent/__init__.py diff --git a/dev_opsgpt/service/__init__.py b/coagent/base_configs/__init__.py similarity index 100% rename from dev_opsgpt/service/__init__.py rename to coagent/base_configs/__init__.py diff --git a/coagent/base_configs/env_config.py b/coagent/base_configs/env_config.py new file mode 100644 index 0000000..45a20e5 --- /dev/null +++ b/coagent/base_configs/env_config.py @@ -0,0 +1,88 @@ +import os +import platform + +system_name = platform.system() +executable_path = os.getcwd() + +# 日志存储路径 +LOG_PATH = os.environ.get("LOG_PATH", None) or os.path.join(executable_path, "logs") + +# 知识库默认存储路径 +SOURCE_PATH = os.environ.get("SOURCE_PATH", None) or os.path.join(executable_path, "sources") + +# 知识库默认存储路径 +KB_ROOT_PATH = os.environ.get("KB_ROOT_PATH", None) or os.path.join(executable_path, "knowledge_base") + +# 代码库默认存储路径 +CB_ROOT_PATH = os.environ.get("CB_ROOT_PATH", None) or os.path.join(executable_path, "code_base") + +# nltk 模型存储路径 +NLTK_DATA_PATH = os.environ.get("NLTK_DATA_PATH", None) or os.path.join(executable_path, "nltk_data") + +# 代码存储路径 +JUPYTER_WORK_PATH = os.environ.get("JUPYTER_WORK_PATH", None) or os.path.join(executable_path, "jupyter_work") + +# WEB_CRAWL存储路径 +WEB_CRAWL_PATH = os.environ.get("WEB_CRAWL_PATH", None) or os.path.join(executable_path, "knowledge_base") + +# NEBULA_DATA存储路径 +NELUBA_PATH = os.environ.get("NELUBA_PATH", None) or os.path.join(executable_path, "data/neluba_data") + +for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NELUBA_PATH]: + if not os.path.exists(_path): + os.makedirs(_path, exist_ok=True) + +# 数据库默认存储路径。 +# 如果使用sqlite,可以直接修改DB_ROOT_PATH;如果使用其它数据库,请直接修改SQLALCHEMY_DATABASE_URI。 +DB_ROOT_PATH = os.path.join(KB_ROOT_PATH, "info.db") +SQLALCHEMY_DATABASE_URI = f"sqlite:///{DB_ROOT_PATH}" + +kbs_config = { + "faiss": { + },} + + +# GENERAL SERVER CONFIG +DEFAULT_BIND_HOST = os.environ.get("DEFAULT_BIND_HOST", None) or "127.0.0.1" + +# NEBULA SERVER CONFIG +NEBULA_HOST = DEFAULT_BIND_HOST +NEBULA_PORT = 9669 +NEBULA_STORAGED_PORT = 9779 +NEBULA_USER = 'root' +NEBULA_PASSWORD = '' +NEBULA_GRAPH_SERVER = { + "host": DEFAULT_BIND_HOST, + "port": NEBULA_PORT, + "docker_port": NEBULA_PORT +} + +# CHROMA CONFIG +CHROMA_PERSISTENT_PATH = '/home/user/chatbot/data/chroma_data' + + +# 默认向量库类型。可选:faiss, milvus, pg. +DEFAULT_VS_TYPE = os.environ.get("DEFAULT_VS_TYPE") or "faiss" + +# 缓存向量库数量 +CACHED_VS_NUM = os.environ.get("CACHED_VS_NUM") or 1 + +# 知识库中单段文本长度 +CHUNK_SIZE = os.environ.get("CHUNK_SIZE") or 500 + +# 知识库中相邻文本重合长度 +OVERLAP_SIZE = os.environ.get("OVERLAP_SIZE") or 50 + +# 知识库匹配向量数量 +VECTOR_SEARCH_TOP_K = os.environ.get("VECTOR_SEARCH_TOP_K") or 5 + +# 知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右 +# Mac 可能存在无法使用normalized_L2的问题,因此调整SCORE_THRESHOLD至 0~1100 +FAISS_NORMALIZE_L2 = True if system_name in ["Linux", "Windows"] else False +SCORE_THRESHOLD = 1 if system_name in ["Linux", "Windows"] else 1100 + +# 搜索引擎匹配结题数量 +SEARCH_ENGINE_TOP_K = os.environ.get("SEARCH_ENGINE_TOP_K") or 5 + +# 代码引擎匹配结题数量 +CODE_SEARCH_TOP_K = os.environ.get("CODE_SEARCH_TOP_K") or 1 \ No newline at end of file diff --git a/dev_opsgpt/chat/__init__.py b/coagent/chat/__init__.py similarity index 100% rename from dev_opsgpt/chat/__init__.py rename to coagent/chat/__init__.py diff --git a/dev_opsgpt/chat/agent_chat.py b/coagent/chat/agent_chat.py similarity index 73% rename from dev_opsgpt/chat/agent_chat.py rename to coagent/chat/agent_chat.py index 5efc208..62def33 100644 --- a/dev_opsgpt/chat/agent_chat.py +++ b/coagent/chat/agent_chat.py @@ -5,30 +5,26 @@ from loguru import logger import importlib import copy import json +import os from pathlib import Path -from configs.model_config import ( - llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, - VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) +# from configs.model_config import ( +# llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, +# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from dev_opsgpt.tools import ( +from coagent.tools import ( toLangchainTools, TOOL_DICT, TOOL_SETS ) -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent, ReactAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, - load_phase_configs, load_chain_configs, load_role_configs - ) -from dev_opsgpt.connector.schema import Memory -from dev_opsgpt.utils.common_utils import file_normalize -from dev_opsgpt.chat.utils import History, wrap_done -from dev_opsgpt.connector.configs import PHASE_CONFIGS, AGETN_CONFIGS, CHAIN_CONFIGS +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message +from coagent.connector.schema import Memory +from coagent.chat.utils import History, wrap_done +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig +from coagent.connector.configs import PHASE_CONFIGS, AGETN_CONFIGS, CHAIN_CONFIGS -PHASE_MODULE = importlib.import_module("dev_opsgpt.connector.phase") +PHASE_MODULE = importlib.import_module("coagent.connector.phase") @@ -56,8 +52,8 @@ class AgentChat: doc_engine_name: str = Body(..., description="知识库名称", examples=["samples"]), search_engine_name: str = Body(..., description="搜索引擎名称", examples=["duckduckgo"]), code_engine_name: str = Body(..., description="代码引擎名称", examples=["samples"]), - top_k: int = Body(VECTOR_SEARCH_TOP_K, description="匹配向量数"), - score_threshold: float = Body(SCORE_THRESHOLD, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), + top_k: int = Body(5, description="匹配向量数"), + score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), stream: bool = Body(False, description="流式输出"), local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), choose_tools: List[str] = Body([], description="选择tool的集合"), @@ -71,12 +67,27 @@ class AgentChat: history_node_list: List = Body([], description="代码历史相关节点"), isDetailed: bool = Body(False, description="是否输出完整的agent相关内容"), upload_file: Union[str, Path, bytes] = "", + kb_root_path: str = Body("", description="知识库存储路径"), + jupyter_work_path: str = Body("", description="sandbox执行环境"), + sandbox_server: str = Body({}, description="代码历史相关节点"), + api_key: str = Body(os.environ.get("OPENAI_API_KEY"), description=""), + api_base_url: str = Body(os.environ.get("API_BASE_URL"),), + embed_model: str = Body("", description="向量模型"), + embed_model_path: str = Body("", description="向量模型路径"), + model_device: str = Body("", description="模型加载设备"), + embed_engine: str = Body("", description="向量模型类型"), + model_name: str = Body("", description="llm模型名称"), + temperature: float = Body(0.2, description=""), **kargs ) -> Message: # update configs phase_configs, chain_configs, agent_configs = self.update_configs( custom_phase_configs, custom_chain_configs, custom_role_configs) + params = locals() + params.pop("self") + embed_config: EmbedConfig = EmbedConfig(**params) + llm_config: LLMConfig = LLMConfig(**params) logger.info('phase_configs={}'.format(phase_configs)) logger.info('chain_configs={}'.format(chain_configs)) @@ -86,7 +97,6 @@ class AgentChat: # choose tools tools = toLangchainTools([TOOL_DICT[i] for i in choose_tools if i in TOOL_DICT]) - logger.debug(f"upload_file: {upload_file}") if upload_file: upload_file_name = upload_file if upload_file and isinstance(upload_file, str) else upload_file.name @@ -97,8 +107,8 @@ class AgentChat: input_message = Message( role_content=query, - role_type="human", - role_name="user", + role_type="user", + role_name="human", input_query=query, origin_query=query, phase_name=phase_name, @@ -120,30 +130,25 @@ class AgentChat: ]) # start to execute phase_class = getattr(PHASE_MODULE, phase_configs[input_message.phase_name]["phase_type"]) + # TODO 需要把相关信息补充上去 phase = phase_class(input_message.phase_name, task = input_message.task, - phase_config = phase_configs, - chain_config = chain_configs, - role_config = agent_configs, - do_summary=phase_configs[input_message.phase_name]["do_summary"], - do_code_retrieval=input_message.do_code_retrieval, - do_doc_retrieval=input_message.do_doc_retrieval, - do_search=input_message.do_search, + base_phase_config = phase_configs, + base_chain_config = chain_configs, + base_role_config = agent_configs, + phase_config = None, + kb_root_path = kb_root_path, + jupyter_work_path = jupyter_work_path, + sandbox_server = sandbox_server, + embed_config = embed_config, + llm_config = llm_config, ) output_message, local_memory = phase.step(input_message, history) - # logger.debug(f"local_memory: {local_memory.to_str_messages(content_key='step_content')}") - - # return { - # "answer": output_message.role_content, - # "db_docs": output_message.db_docs, - # "search_docs": output_message.search_docs, - # "code_docs": output_message.code_docs, - # "figures": output_message.figures - # } def chat_iterator(message: Message, local_memory: Memory, isDetailed=False): step_content = local_memory.to_str_messages(content_key='step_content', filter_roles=["user"]) final_content = message.role_content + logger.debug(f"{step_content}") result = { "answer": "", "db_docs": [str(doc) for doc in message.db_docs], @@ -190,8 +195,8 @@ class AgentChat: search_engine_name: str = Body(..., description="搜索引擎名称", examples=["duckduckgo"]), code_engine_name: str = Body(..., description="代码引擎名称", examples=["samples"]), cb_search_type: str = Body(..., description="代码查询模式", examples=["tag"]), - top_k: int = Body(VECTOR_SEARCH_TOP_K, description="匹配向量数"), - score_threshold: float = Body(SCORE_THRESHOLD, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), + top_k: int = Body(5, description="匹配向量数"), + score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), stream: bool = Body(False, description="流式输出"), local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), choose_tools: List[str] = Body([], description="选择tool的集合"), @@ -205,15 +210,32 @@ class AgentChat: history_node_list: List = Body([], description="代码历史相关节点"), isDetailed: bool = Body(False, description="是否输出完整的agent相关内容"), upload_file: Union[str, Path, bytes] = "", + kb_root_path: str = Body("", description="知识库存储路径"), + jupyter_work_path: str = Body("", description="sandbox执行环境"), + sandbox_server: str = Body({}, description="代码历史相关节点"), + api_key: str = Body(os.environ["OPENAI_API_KEY"], description=""), + api_base_url: str = Body(os.environ.get("API_BASE_URL"),), + embed_model: str = Body("", description="向量模型"), + embed_model_path: str = Body("", description="向量模型路径"), + model_device: str = Body("", description="模型加载设备"), + embed_engine: str = Body("", description="向量模型类型"), + model_name: str = Body("", description="llm模型名称"), + temperature: float = Body(0.2, description=""), **kargs ) -> Message: # update configs phase_configs, chain_configs, agent_configs = self.update_configs( custom_phase_configs, custom_chain_configs, custom_role_configs) + + # + params = locals() + params.pop("self") + embed_config: EmbedConfig = EmbedConfig(**params) + llm_config: LLMConfig = LLMConfig(**params) + # choose tools tools = toLangchainTools([TOOL_DICT[i] for i in choose_tools if i in TOOL_DICT]) - logger.debug(f"upload_file: {upload_file}") if upload_file: upload_file_name = upload_file if upload_file and isinstance(upload_file, str) else upload_file.name @@ -224,8 +246,8 @@ class AgentChat: input_message = Message( role_content=query, - role_type="human", - role_name="user", + role_type="user", + role_name="human", input_query=query, origin_query=query, phase_name=phase_name, @@ -252,21 +274,23 @@ class AgentChat: phase_class = getattr(PHASE_MODULE, phase_configs[input_message.phase_name]["phase_type"]) phase = phase_class(input_message.phase_name, task = input_message.task, - phase_config = phase_configs, - chain_config = chain_configs, - role_config = agent_configs, - do_summary=phase_configs[input_message.phase_name]["do_summary"], - do_code_retrieval=input_message.do_code_retrieval, - do_doc_retrieval=input_message.do_doc_retrieval, - do_search=input_message.do_search, + base_phase_config = phase_configs, + base_chain_config = chain_configs, + base_role_config = agent_configs, + phase_config = None, + kb_root_path = kb_root_path, + jupyter_work_path = jupyter_work_path, + sandbox_server = sandbox_server, + embed_config = embed_config, + llm_config = llm_config, ) self.chatPhase_dict[phase_configs[input_message.phase_name]["phase_type"]] = phase else: phase = self.chatPhase_dict[phase_configs[input_message.phase_name]["phase_type"]] def chat_iterator(message: Message, local_memory: Memory, isDetailed=False): - step_content = local_memory.to_str_messages(content_key='step_content', filter_roles=["user"]) - step_content = "\n\n".join([f"{v}" for parsed_output in local_memory.get_parserd_output_list() for k, v in parsed_output.items() if k not in ["Action Status"]]) + step_content = local_memory.to_str_messages(content_key='step_content', filter_roles=["human"]) + step_content = "\n\n".join([f"{v}" for parsed_output in local_memory.get_parserd_output_list()[1:] for k, v in parsed_output.items() if k not in ["Action Status"]]) final_content = message.role_content result = { "answer": "", @@ -279,7 +303,6 @@ class AgentChat: "final_content": final_content, } - related_nodes, has_nodes = [], [ ] for nodes in result["related_nodes"]: for node in nodes: @@ -301,7 +324,7 @@ class AgentChat: for output_message, local_memory in phase.astep(input_message, history): - # logger.debug(f"output_message: {output_message.role_content}") + # logger.debug(f"output_message: {output_message}") # output_message = Message(**output_message) # local_memory = Memory(**local_memory) for result in chat_iterator(output_message, local_memory, isDetailed): diff --git a/dev_opsgpt/chat/base_chat.py b/coagent/chat/base_chat.py similarity index 65% rename from dev_opsgpt/chat/base_chat.py rename to coagent/chat/base_chat.py index 6764772..490f20d 100644 --- a/dev_opsgpt/chat/base_chat.py +++ b/coagent/chat/base_chat.py @@ -1,16 +1,17 @@ from fastapi import Body, Request from fastapi.responses import StreamingResponse -import asyncio, json +import asyncio, json, os from typing import List, AsyncIterable from langchain import LLMChain from langchain.callbacks import AsyncIteratorCallbackHandler from langchain.prompts.chat import ChatPromptTemplate -from dev_opsgpt.llm_models import getChatModel -from dev_opsgpt.chat.utils import History, wrap_done -from configs.model_config import (llm_model_dict, LLM_MODEL, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from dev_opsgpt.utils import BaseResponse +from coagent.llm_models import getChatModel, getChatModelFromConfig +from coagent.chat.utils import History, wrap_done +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig +# from configs.model_config import (llm_model_dict, LLM_MODEL, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) +from coagent.utils import BaseResponse from loguru import logger @@ -37,22 +38,34 @@ class Chat: examples=[[{"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"}]] ), engine_name: str = Body(..., description="知识库名称", examples=["samples"]), - top_k: int = Body(VECTOR_SEARCH_TOP_K, description="匹配向量数"), - score_threshold: float = Body(SCORE_THRESHOLD, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), + top_k: int = Body(5, description="匹配向量数"), + score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), stream: bool = Body(False, description="流式输出"), local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), request: Request = None, + api_key: str = Body(os.environ.get("OPENAI_API_KEY")), + api_base_url: str = Body(os.environ.get("API_BASE_URL")), + embed_model: str = Body("", ), + embed_model_path: str = Body("", ), + embed_engine: str = Body("", ), + model_name: str = Body("", ), + temperature: float = Body(0.5, ), + model_device: str = Body("", ), **kargs ): + params = locals() + params.pop("self", None) + llm_config: LLMConfig = LLMConfig(**params) + embed_config: EmbedConfig = EmbedConfig(**params) self.engine_name = engine_name if isinstance(engine_name, str) else engine_name.default self.top_k = top_k if isinstance(top_k, int) else top_k.default self.score_threshold = score_threshold if isinstance(score_threshold, float) else score_threshold.default self.stream = stream if isinstance(stream, bool) else stream.default self.local_doc_url = local_doc_url if isinstance(local_doc_url, bool) else local_doc_url.default self.request = request - return self._chat(query, history, **kargs) + return self._chat(query, history, llm_config, embed_config, **kargs) - def _chat(self, query: str, history: List[History], **kargs): + def _chat(self, query: str, history: List[History], llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): history = [History(**h) if isinstance(h, dict) else h for h in history] ## check service dependcy is ok @@ -61,9 +74,10 @@ class Chat: if service_status.code!=200: return service_status def chat_iterator(query: str, history: List[History]): - model = getChatModel() + # model = getChatModel() + model = getChatModelFromConfig(llm_config) - result, content = self.create_task(query, history, model, **kargs) + result, content = self.create_task(query, history, model, llm_config, embed_config, **kargs) logger.info('result={}'.format(result)) logger.info('content={}'.format(content)) @@ -87,21 +101,34 @@ class Chat: examples=[[{"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"}]] ), engine_name: str = Body(..., description="知识库名称", examples=["samples"]), - top_k: int = Body(VECTOR_SEARCH_TOP_K, description="匹配向量数"), - score_threshold: float = Body(SCORE_THRESHOLD, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), + top_k: int = Body(5, description="匹配向量数"), + score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), stream: bool = Body(False, description="流式输出"), local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), request: Request = None, + api_key: str = Body(os.environ.get("OPENAI_API_KEY")), + api_base_url: str = Body(os.environ.get("API_BASE_URL")), + embed_model: str = Body("", ), + embed_model_path: str = Body("", ), + embed_engine: str = Body("", ), + model_name: str = Body("", ), + temperature: float = Body(0.5, ), + model_device: str = Body("", ), ): + # + params = locals() + params.pop("self", None) + llm_config: LLMConfig = LLMConfig(**params) + embed_config: EmbedConfig = EmbedConfig(**params) self.engine_name = engine_name if isinstance(engine_name, str) else engine_name.default self.top_k = top_k if isinstance(top_k, int) else top_k.default self.score_threshold = score_threshold if isinstance(score_threshold, float) else score_threshold.default self.stream = stream if isinstance(stream, bool) else stream.default self.local_doc_url = local_doc_url if isinstance(local_doc_url, bool) else local_doc_url.default self.request = request - return self._achat(query, history) + return self._achat(query, history, llm_config, embed_config) - def _achat(self, query: str, history: List[History]): + def _achat(self, query: str, history: List[History], llm_config: LLMConfig, embed_config: EmbedConfig): history = [History(**h) if isinstance(h, dict) else h for h in history] ## check service dependcy is ok service_status = self.check_service_status() @@ -109,9 +136,10 @@ class Chat: async def chat_iterator(query, history): callback = AsyncIteratorCallbackHandler() - model = getChatModel() + # model = getChatModel() + model = getChatModelFromConfig(llm_config) - task, result = self.create_atask(query, history, model, callback) + task, result = self.create_atask(query, history, model, llm_config, embed_config, callback) if self.stream: for token in callback["text"]: result["answer"] = token @@ -125,7 +153,7 @@ class Chat: return StreamingResponse(chat_iterator(query, history), media_type="text/event-stream") - def create_task(self, query: str, history: List[History], model, **kargs): + def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): '''构建 llm 生成任务''' chat_prompt = ChatPromptTemplate.from_messages( [i.to_msg_tuple() for i in history] + [("human", "{input}")] @@ -134,7 +162,7 @@ class Chat: content = chain({"input": query}) return {"answer": "", "docs": ""}, content - def create_atask(self, query, history, model, callback: AsyncIteratorCallbackHandler): + def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): chat_prompt = ChatPromptTemplate.from_messages( [i.to_msg_tuple() for i in history] + [("human", "{input}")] ) diff --git a/dev_opsgpt/chat/code_chat.py b/coagent/chat/code_chat.py similarity index 69% rename from dev_opsgpt/chat/code_chat.py rename to coagent/chat/code_chat.py index 9068bfb..a0bb7a3 100644 --- a/dev_opsgpt/chat/code_chat.py +++ b/coagent/chat/code_chat.py @@ -8,7 +8,6 @@ from fastapi import Request, Body import os, asyncio -from urllib.parse import urlencode from typing import List from fastapi.responses import StreamingResponse @@ -16,16 +15,19 @@ from langchain import LLMChain from langchain.callbacks import AsyncIteratorCallbackHandler from langchain.prompts.chat import ChatPromptTemplate -from configs.model_config import ( - llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, - VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, CODE_PROMPT_TEMPLATE) -from dev_opsgpt.chat.utils import History, wrap_done -from dev_opsgpt.utils import BaseResponse +# from configs.model_config import ( +# llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, +# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, CODE_PROMPT_TEMPLATE) +from coagent.connector.configs.prompts import CODE_PROMPT_TEMPLATE +from coagent.chat.utils import History, wrap_done +from coagent.utils import BaseResponse from .base_chat import Chat -from dev_opsgpt.llm_models import getChatModel +from coagent.llm_models import getChatModel, getChatModelFromConfig -from dev_opsgpt.service.kb_api import search_docs, KBServiceFactory -from dev_opsgpt.service.cb_api import search_code, cb_exists_api +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig + + +from coagent.service.cb_api import search_code, cb_exists_api from loguru import logger import json @@ -51,12 +53,21 @@ class CodeChat(Chat): return BaseResponse(code=404, msg=f"未找到代码库 {self.engine_name}") return BaseResponse(code=200, msg=f"找到代码库 {self.engine_name}") - def _process(self, query: str, history: List[History], model): + def _process(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig): '''process''' codes_res = search_code(query=query, cb_name=self.engine_name, code_limit=self.code_limit, search_type=self.cb_search_type, - history_node_list=self.history_node_list) + history_node_list=self.history_node_list, + api_key=llm_config.api_key, + api_base_url=llm_config.api_base_url, + model_name=llm_config.model_name, + temperature=llm_config.temperature, + embed_model=embed_config.embed_model, + embed_model_path=embed_config.embed_model_path, + embed_engine=embed_config.embed_engine, + model_device=embed_config.model_device, + ) context = codes_res['context'] related_vertices = codes_res['related_vertices'] @@ -94,17 +105,30 @@ class CodeChat(Chat): stream: bool = Body(False, description="流式输出"), local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), request: Request = None, + + api_key: str = Body(os.environ.get("OPENAI_API_KEY")), + api_base_url: str = Body(os.environ.get("API_BASE_URL")), + embed_model: str = Body("", ), + embed_model_path: str = Body("", ), + embed_engine: str = Body("", ), + model_name: str = Body("", ), + temperature: float = Body(0.5, ), + model_device: str = Body("", ), **kargs ): + params = locals() + params.pop("self") + llm_config: LLMConfig = LLMConfig(**params) + embed_config: EmbedConfig = EmbedConfig(**params) self.engine_name = engine_name if isinstance(engine_name, str) else engine_name.default self.code_limit = code_limit self.stream = stream if isinstance(stream, bool) else stream.default self.local_doc_url = local_doc_url if isinstance(local_doc_url, bool) else local_doc_url.default self.request = request self.cb_search_type = cb_search_type - return self._chat(query, history, **kargs) + return self._chat(query, history, llm_config, embed_config, **kargs) - def _chat(self, query: str, history: List[History], **kargs): + def _chat(self, query: str, history: List[History], llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): history = [History(**h) if isinstance(h, dict) else h for h in history] service_status = self.check_service_status() @@ -112,9 +136,10 @@ class CodeChat(Chat): if service_status.code != 200: return service_status def chat_iterator(query: str, history: List[History]): - model = getChatModel() + # model = getChatModel() + model = getChatModelFromConfig(llm_config) - result, content = self.create_task(query, history, model, **kargs) + result, content = self.create_task(query, history, model, llm_config, embed_config, **kargs) # logger.info('result={}'.format(result)) # logger.info('content={}'.format(content)) @@ -130,9 +155,9 @@ class CodeChat(Chat): return StreamingResponse(chat_iterator(query, history), media_type="text/event-stream") - def create_task(self, query: str, history: List[History], model): + def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig): '''构建 llm 生成任务''' - chain, context, result = self._process(query, history, model) + chain, context, result = self._process(query, history, model, llm_config, embed_config) logger.info('chain={}'.format(chain)) try: content = chain({"context": context, "question": query}) @@ -140,8 +165,8 @@ class CodeChat(Chat): content = {"text": str(e)} return result, content - def create_atask(self, query, history, model, callback: AsyncIteratorCallbackHandler): - chain, context, result = self._process(query, history, model) + def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): + chain, context, result = self._process(query, history, model, llm_config, embed_config) task = asyncio.create_task(wrap_done( chain.acall({"context": context, "question": query}), callback.done )) diff --git a/dev_opsgpt/chat/knowledge_chat.py b/coagent/chat/knowledge_chat.py similarity index 64% rename from dev_opsgpt/chat/knowledge_chat.py rename to coagent/chat/knowledge_chat.py index 5d725f3..2e2bdcd 100644 --- a/dev_opsgpt/chat/knowledge_chat.py +++ b/coagent/chat/knowledge_chat.py @@ -8,13 +8,16 @@ from langchain.callbacks import AsyncIteratorCallbackHandler from langchain.prompts.chat import ChatPromptTemplate -from configs.model_config import ( - llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, - VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from dev_opsgpt.chat.utils import History, wrap_done -from dev_opsgpt.utils import BaseResponse +# from configs.model_config import ( +# llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, +# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) +from coagent.base_configs.env_config import KB_ROOT_PATH +from coagent.connector.configs.prompts import ORIGIN_TEMPLATE_PROMPT +from coagent.chat.utils import History, wrap_done +from coagent.utils import BaseResponse +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig from .base_chat import Chat -from dev_opsgpt.service.kb_api import search_docs, KBServiceFactory +from coagent.service.kb_api import search_docs, KBServiceFactory from loguru import logger @@ -23,26 +26,33 @@ class KnowledgeChat(Chat): def __init__( self, engine_name: str = "", - top_k: int = VECTOR_SEARCH_TOP_K, + top_k: int = 5, stream: bool = False, - score_thresold: float = SCORE_THRESHOLD, + score_thresold: float = 1.0, local_doc_url: bool = False, request: Request = None, + kb_root_path: str = KB_ROOT_PATH, ) -> None: super().__init__(engine_name, top_k, stream) self.score_thresold = score_thresold self.local_doc_url = local_doc_url self.request = request + self.kb_root_path = kb_root_path def check_service_status(self) -> BaseResponse: - kb = KBServiceFactory.get_service_by_name(self.engine_name) + kb = KBServiceFactory.get_service_by_name(self.engine_name, self.kb_root_path) if kb is None: return BaseResponse(code=404, msg=f"未找到知识库 {self.engine_name}") return BaseResponse(code=200, msg=f"找到知识库 {self.engine_name}") - def _process(self, query: str, history: List[History], model): + def _process(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, ): '''process''' - docs = search_docs(query, self.engine_name, self.top_k, self.score_threshold) + docs = search_docs( + query, self.engine_name, self.top_k, self.score_threshold, self.kb_root_path, + api_key=embed_config.api_key, api_base_url=embed_config.api_base_url, embed_model=embed_config.embed_model, + embed_model_path=embed_config.embed_model_path, embed_engine=embed_config.embed_engine, + model_device=embed_config.model_device, + ) context = "\n".join([doc.page_content for doc in docs]) source_documents = [] for inum, doc in enumerate(docs): @@ -55,24 +65,24 @@ class KnowledgeChat(Chat): text = f"""出处 [{inum + 1}] [{filename}]({url}) \n\n{doc.page_content}\n\n""" source_documents.append(text) chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", PROMPT_TEMPLATE)] + [i.to_msg_tuple() for i in history] + [("human", ORIGIN_TEMPLATE_PROMPT)] ) chain = LLMChain(prompt=chat_prompt, llm=model) result = {"answer": "", "docs": source_documents} return chain, context, result - def create_task(self, query: str, history: List[History], model): + def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, ): '''构建 llm 生成任务''' logger.debug(f"query: {query}, history: {history}") - chain, context, result = self._process(query, history, model) + chain, context, result = self._process(query, history, model, llm_config, embed_config) try: content = chain({"context": context, "question": query}) except Exception as e: content = {"text": str(e)} return result, content - def create_atask(self, query, history, model, callback: AsyncIteratorCallbackHandler): - chain, context, result = self._process(query, history, model) + def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): + chain, context, result = self._process(query, history, model, llm_config, embed_config) task = asyncio.create_task(wrap_done( chain.acall({"context": context, "question": query}), callback.done )) diff --git a/dev_opsgpt/chat/llm_chat.py b/coagent/chat/llm_chat.py similarity index 76% rename from dev_opsgpt/chat/llm_chat.py rename to coagent/chat/llm_chat.py index 54e5a56..2c36f96 100644 --- a/dev_opsgpt/chat/llm_chat.py +++ b/coagent/chat/llm_chat.py @@ -6,7 +6,8 @@ from langchain.callbacks import AsyncIteratorCallbackHandler from langchain.prompts.chat import ChatPromptTemplate -from dev_opsgpt.chat.utils import History, wrap_done +from coagent.chat.utils import History, wrap_done +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig from .base_chat import Chat from loguru import logger @@ -21,7 +22,7 @@ class LLMChat(Chat): ) -> None: super().__init__(engine_name, top_k, stream) - def create_task(self, query: str, history: List[History], model): + def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): '''构建 llm 生成任务''' chat_prompt = ChatPromptTemplate.from_messages( [i.to_msg_tuple() for i in history] + [("human", "{input}")] @@ -30,7 +31,7 @@ class LLMChat(Chat): content = chain({"input": query}) return {"answer": "", "docs": ""}, content - def create_atask(self, query, history, model, callback: AsyncIteratorCallbackHandler): + def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): chat_prompt = ChatPromptTemplate.from_messages( [i.to_msg_tuple() for i in history] + [("human", "{input}")] ) diff --git a/dev_opsgpt/chat/search_chat.py b/coagent/chat/search_chat.py similarity index 76% rename from dev_opsgpt/chat/search_chat.py rename to coagent/chat/search_chat.py index 8346469..bf929e9 100644 --- a/dev_opsgpt/chat/search_chat.py +++ b/coagent/chat/search_chat.py @@ -1,4 +1,3 @@ -from fastapi import Request import os, asyncio from typing import List, Optional, Dict @@ -8,11 +7,13 @@ from langchain.utilities import BingSearchAPIWrapper, DuckDuckGoSearchAPIWrapper from langchain.prompts.chat import ChatPromptTemplate from langchain.docstore.document import Document -from configs.model_config import ( - PROMPT_TEMPLATE, SEARCH_ENGINE_TOP_K, BING_SUBSCRIPTION_KEY, BING_SEARCH_URL, - VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from dev_opsgpt.chat.utils import History, wrap_done -from dev_opsgpt.utils import BaseResponse +# from configs.model_config import ( +# PROMPT_TEMPLATE, SEARCH_ENGINE_TOP_K, BING_SUBSCRIPTION_KEY, BING_SEARCH_URL, +# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) +from coagent.connector.configs.prompts import ORIGIN_TEMPLATE_PROMPT +from coagent.chat.utils import History, wrap_done +from coagent.utils import BaseResponse +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig from .base_chat import Chat from loguru import logger @@ -20,19 +21,19 @@ from loguru import logger from duckduckgo_search import DDGS -def bing_search(text, result_len=SEARCH_ENGINE_TOP_K): - if not (BING_SEARCH_URL and BING_SUBSCRIPTION_KEY): - return [{"snippet": "please set BING_SUBSCRIPTION_KEY and BING_SEARCH_URL in os ENV", - "title": "env info is not found", - "link": "https://python.langchain.com/en/latest/modules/agents/tools/examples/bing_search.html"}] - search = BingSearchAPIWrapper(bing_subscription_key=BING_SUBSCRIPTION_KEY, - bing_search_url=BING_SEARCH_URL) - return search.results(text, result_len) +# def bing_search(text, result_len=5): +# if not (BING_SEARCH_URL and BING_SUBSCRIPTION_KEY): +# return [{"snippet": "please set BING_SUBSCRIPTION_KEY and BING_SEARCH_URL in os ENV", +# "title": "env info is not found", +# "link": "https://python.langchain.com/en/latest/modules/agents/tools/examples/bing_search.html"}] +# search = BingSearchAPIWrapper(bing_subscription_key=BING_SUBSCRIPTION_KEY, +# bing_search_url=BING_SEARCH_URL) +# return search.results(text, result_len) def duckduckgo_search( query: str, - result_len: int = SEARCH_ENGINE_TOP_K, + result_len: int = 5, region: Optional[str] = "wt-wt", safesearch: str = "moderate", time: Optional[str] = "y", @@ -79,7 +80,7 @@ def duckduckgo_search( SEARCH_ENGINES = {"duckduckgo": duckduckgo_search, - "bing": bing_search, + # "bing": bing_search, } @@ -96,7 +97,7 @@ def search_result2docs(search_results): def lookup_search_engine( query: str, search_engine_name: str, - top_k: int = SEARCH_ENGINE_TOP_K, + top_k: int = 5, ): results = SEARCH_ENGINES[search_engine_name](query, result_len=top_k) docs = search_result2docs(results) @@ -109,7 +110,7 @@ class SearchChat(Chat): def __init__( self, engine_name: str = "", - top_k: int = VECTOR_SEARCH_TOP_K, + top_k: int = 5, stream: bool = False, ) -> None: super().__init__(engine_name, top_k, stream) @@ -130,19 +131,19 @@ class SearchChat(Chat): ] chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", PROMPT_TEMPLATE)] + [i.to_msg_tuple() for i in history] + [("human", ORIGIN_TEMPLATE_PROMPT)] ) chain = LLMChain(prompt=chat_prompt, llm=model) result = {"answer": "", "docs": source_documents} return chain, context, result - def create_task(self, query: str, history: List[History], model): + def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, ): '''构建 llm 生成任务''' chain, context, result = self._process(query, history, model) content = chain({"context": context, "question": query}) return result, content - def create_atask(self, query, history, model, callback: AsyncIteratorCallbackHandler): + def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): chain, context, result = self._process(query, history, model) task = asyncio.create_task(wrap_done( chain.acall({"context": context, "question": query}), callback.done diff --git a/dev_opsgpt/chat/utils.py b/coagent/chat/utils.py similarity index 100% rename from dev_opsgpt/chat/utils.py rename to coagent/chat/utils.py diff --git a/dev_opsgpt/codechat/__init__.py b/coagent/codechat/__init__.py similarity index 100% rename from dev_opsgpt/codechat/__init__.py rename to coagent/codechat/__init__.py diff --git a/dev_opsgpt/codechat/code_analyzer/__init__.py b/coagent/codechat/code_analyzer/__init__.py similarity index 100% rename from dev_opsgpt/codechat/code_analyzer/__init__.py rename to coagent/codechat/code_analyzer/__init__.py diff --git a/dev_opsgpt/codechat/code_analyzer/code_analyzer.py b/coagent/codechat/code_analyzer/code_analyzer.py similarity index 94% rename from dev_opsgpt/codechat/code_analyzer/code_analyzer.py rename to coagent/codechat/code_analyzer/code_analyzer.py index 5375c61..f725ea1 100644 --- a/dev_opsgpt/codechat/code_analyzer/code_analyzer.py +++ b/coagent/codechat/code_analyzer/code_analyzer.py @@ -8,17 +8,20 @@ import time from loguru import logger -from dev_opsgpt.codechat.code_analyzer.code_static_analysis import CodeStaticAnalysis -from dev_opsgpt.codechat.code_analyzer.code_intepreter import CodeIntepreter -from dev_opsgpt.codechat.code_analyzer.code_preprocess import CodePreprocessor -from dev_opsgpt.codechat.code_analyzer.code_dedup import CodeDedup +from coagent.codechat.code_analyzer.code_static_analysis import CodeStaticAnalysis +from coagent.codechat.code_analyzer.code_intepreter import CodeIntepreter +from coagent.codechat.code_analyzer.code_preprocess import CodePreprocessor +from coagent.codechat.code_analyzer.code_dedup import CodeDedup +from coagent.llm_models.llm_config import LLMConfig + class CodeAnalyzer: - def __init__(self, language: str): + def __init__(self, language: str, llm_config: LLMConfig): + self.llm_config = llm_config self.code_preprocessor = CodePreprocessor() self.code_debup = CodeDedup() - self.code_interperter = CodeIntepreter() + self.code_interperter = CodeIntepreter(self.llm_config) self.code_static_analyzer = CodeStaticAnalysis(language=language) def analyze(self, code_dict: dict, do_interpret: bool = True): diff --git a/dev_opsgpt/codechat/code_analyzer/code_dedup.py b/coagent/codechat/code_analyzer/code_dedup.py similarity index 100% rename from dev_opsgpt/codechat/code_analyzer/code_dedup.py rename to coagent/codechat/code_analyzer/code_dedup.py diff --git a/dev_opsgpt/codechat/code_analyzer/code_intepreter.py b/coagent/codechat/code_analyzer/code_intepreter.py similarity index 94% rename from dev_opsgpt/codechat/code_analyzer/code_intepreter.py rename to coagent/codechat/code_analyzer/code_intepreter.py index 673bc44..a96cda4 100644 --- a/dev_opsgpt/codechat/code_analyzer/code_intepreter.py +++ b/coagent/codechat/code_analyzer/code_intepreter.py @@ -10,14 +10,15 @@ from langchain.schema import ( HumanMessage, ) -from configs.model_config import CODE_INTERPERT_TEMPLATE - -from dev_opsgpt.llm_models.openai_model import getChatModel +# from configs.model_config import CODE_INTERPERT_TEMPLATE +from coagent.connector.configs.prompts import CODE_INTERPERT_TEMPLATE +from coagent.llm_models.openai_model import getChatModel, getChatModelFromConfig +from coagent.llm_models.llm_config import LLMConfig class CodeIntepreter: - def __init__(self): - pass + def __init__(self, llm_config: LLMConfig): + self.llm_config = llm_config def get_intepretation(self, code_list): ''' @@ -25,7 +26,8 @@ class CodeIntepreter: @param code_list: @return: ''' - chat_model = getChatModel() + # chat_model = getChatModel() + chat_model = getChatModelFromConfig(self.llm_config) res = {} for code in code_list: @@ -42,7 +44,8 @@ class CodeIntepreter: @param code_list: @return: ''' - chat_model = getChatModel() + # chat_model = getChatModel() + chat_model = getChatModelFromConfig(self.llm_config) res = {} messages = [] diff --git a/dev_opsgpt/codechat/code_analyzer/code_preprocess.py b/coagent/codechat/code_analyzer/code_preprocess.py similarity index 100% rename from dev_opsgpt/codechat/code_analyzer/code_preprocess.py rename to coagent/codechat/code_analyzer/code_preprocess.py diff --git a/dev_opsgpt/codechat/code_analyzer/code_static_analysis.py b/coagent/codechat/code_analyzer/code_static_analysis.py similarity index 88% rename from dev_opsgpt/codechat/code_analyzer/code_static_analysis.py rename to coagent/codechat/code_analyzer/code_static_analysis.py index b943795..4acf6ce 100644 --- a/dev_opsgpt/codechat/code_analyzer/code_static_analysis.py +++ b/coagent/codechat/code_analyzer/code_static_analysis.py @@ -5,7 +5,7 @@ @time: 2023/11/21 下午2:28 @desc: ''' -from dev_opsgpt.codechat.code_analyzer.language_static_analysis import * +from coagent.codechat.code_analyzer.language_static_analysis import * class CodeStaticAnalysis: def __init__(self, language): diff --git a/dev_opsgpt/codechat/code_analyzer/language_static_analysis/__init__.py b/coagent/codechat/code_analyzer/language_static_analysis/__init__.py similarity index 100% rename from dev_opsgpt/codechat/code_analyzer/language_static_analysis/__init__.py rename to coagent/codechat/code_analyzer/language_static_analysis/__init__.py diff --git a/dev_opsgpt/codechat/code_analyzer/language_static_analysis/java_static_analysis.py b/coagent/codechat/code_analyzer/language_static_analysis/java_static_analysis.py similarity index 85% rename from dev_opsgpt/codechat/code_analyzer/language_static_analysis/java_static_analysis.py rename to coagent/codechat/code_analyzer/language_static_analysis/java_static_analysis.py index 475da2d..83bca4e 100644 --- a/dev_opsgpt/codechat/code_analyzer/language_static_analysis/java_static_analysis.py +++ b/coagent/codechat/code_analyzer/language_static_analysis/java_static_analysis.py @@ -62,7 +62,7 @@ class JavaStaticAnalysis: for node in tree.types: if type(node) in (javalang.tree.ClassDeclaration, javalang.tree.InterfaceDeclaration): - class_name = pac_name + '#' + node.name + class_name = tree.package.name + '.' + node.name class_name_list.append(class_name) for node_inner in node.body: @@ -108,6 +108,28 @@ class JavaStaticAnalysis: return res_dict +if __name__ == '__main__': + java_code_dict = { + 'test': '''package com.theokanning.openai; + +import com.theokanning.openai.client.Utils; + + +public class UtilsTest { + public void testRemoveChar() { + String input = "hello"; + char ch = 'l'; + String expected = "heo"; + String res = Utils.remove(input, ch); + System.out.println(res.equals(expected)); + } +} +''' + } + + jsa = JavaStaticAnalysis() + res = jsa.analyze(java_code_dict) + logger.info(res) diff --git a/dev_opsgpt/codechat/code_crawler/__init__.py b/coagent/codechat/code_crawler/__init__.py similarity index 100% rename from dev_opsgpt/codechat/code_crawler/__init__.py rename to coagent/codechat/code_crawler/__init__.py diff --git a/dev_opsgpt/codechat/code_crawler/dir_crawler.py b/coagent/codechat/code_crawler/dir_crawler.py similarity index 100% rename from dev_opsgpt/codechat/code_crawler/dir_crawler.py rename to coagent/codechat/code_crawler/dir_crawler.py diff --git a/dev_opsgpt/codechat/code_crawler/zip_crawler.py b/coagent/codechat/code_crawler/zip_crawler.py similarity index 90% rename from dev_opsgpt/codechat/code_crawler/zip_crawler.py rename to coagent/codechat/code_crawler/zip_crawler.py index e5ed23a..8cb968d 100644 --- a/dev_opsgpt/codechat/code_crawler/zip_crawler.py +++ b/coagent/codechat/code_crawler/zip_crawler.py @@ -8,7 +8,7 @@ from loguru import logger import zipfile -from dev_opsgpt.codechat.code_crawler.dir_crawler import DirCrawler +from coagent.codechat.code_crawler.dir_crawler import DirCrawler class ZipCrawler: diff --git a/dev_opsgpt/codechat/code_search/__init__.py b/coagent/codechat/code_search/__init__.py similarity index 100% rename from dev_opsgpt/codechat/code_search/__init__.py rename to coagent/codechat/code_search/__init__.py diff --git a/dev_opsgpt/codechat/code_search/code_search.py b/coagent/codechat/code_search/code_search.py similarity index 70% rename from dev_opsgpt/codechat/code_search/code_search.py rename to coagent/codechat/code_search/code_search.py index 4ce937d..7fc720d 100644 --- a/dev_opsgpt/codechat/code_search/code_search.py +++ b/coagent/codechat/code_search/code_search.py @@ -9,13 +9,16 @@ import time from loguru import logger from collections import defaultdict -from dev_opsgpt.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from dev_opsgpt.db_handler.vector_db_handler.chroma_handler import ChromaHandler +from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler +from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler -from dev_opsgpt.codechat.code_search.cypher_generator import CypherGenerator -from dev_opsgpt.codechat.code_search.tagger import Tagger -from dev_opsgpt.embeddings.get_embedding import get_embedding +from coagent.codechat.code_search.cypher_generator import CypherGenerator +from coagent.codechat.code_search.tagger import Tagger +from coagent.embeddings.get_embedding import get_embedding +from coagent.llm_models.llm_config import LLMConfig + +# from configs.model_config import EMBEDDING_DEVICE, EMBEDDING_MODEL # search_by_tag VERTEX_SCORE = 10 HISTORY_VERTEX_SCORE = 5 @@ -26,13 +29,14 @@ MAX_DISTANCE = 1000 class CodeSearch: - def __init__(self, nh: NebulaHandler, ch: ChromaHandler, limit: int = 3): + def __init__(self, llm_config: LLMConfig, nh: NebulaHandler, ch: ChromaHandler, limit: int = 3): ''' init @param nh: NebulaHandler @param ch: ChromaHandler @param limit: limit of result ''' + self.llm_config = llm_config self.nh = nh self.ch = ch self.limit = limit @@ -50,7 +54,6 @@ class CodeSearch: # get all verticex vertex_list = self.nh.get_vertices().get('v', []) vertex_vid_list = [i.as_node().get_id().as_string() for i in vertex_list] - logger.debug(vertex_vid_list) # update score vertex_score_dict = defaultdict(lambda: 0) @@ -77,8 +80,26 @@ class CodeSearch: # get most prominent package tag package_score_dict = defaultdict(lambda: 0) + for vertex, score in vertex_score_dict.items(): - package = '#'.join(vertex.split('#')[0:2]) + if '#' in vertex: + # get class name first + cypher = f'''MATCH (v1:class)-[e:contain]->(v2) WHERE id(v2) == '{vertex}' RETURN id(v1) as id;''' + cypher_res = self.nh.execute_cypher(cypher=cypher, format_res=True) + class_vertices = cypher_res.get('id', []) + if not class_vertices: + continue + + vertex = class_vertices[0].as_string() + + # get package name + cypher = f'''MATCH (v1:package)-[e:contain]->(v2) WHERE id(v2) == '{vertex}' RETURN id(v1) as id;''' + cypher_res = self.nh.execute_cypher(cypher=cypher, format_res=True) + pac_vertices = cypher_res.get('id', []) + if not pac_vertices: + continue + + package = pac_vertices[0].as_string() package_score_dict[package] += score # get respective code @@ -87,7 +108,10 @@ class CodeSearch: package_score_tuple.sort(key=lambda x: x[1], reverse=True) ids = [i[0] for i in package_score_tuple] + logger.info(f'ids={ids}') chroma_res = self.ch.get(ids=ids, include=['metadatas']) + + # logger.info(chroma_res) for vertex, score in package_score_tuple: index = chroma_res['result']['ids'].index(vertex) code_text = chroma_res['result']['metadatas'][index]['code_text'] @@ -97,16 +121,17 @@ class CodeSearch: ) if len(res) >= self.limit: break + logger.info(f'retrival code={res}') return res - def search_by_desciption(self, query: str, engine: str): + def search_by_desciption(self, query: str, engine: str, model_path: str = "text2vec-base-chinese", embedding_device: str = "cpu"): ''' search by perform sim search @param query: @return: ''' query = query.replace(',', ',') - query_emb = get_embedding(engine=engine, text_list=[query]) + query_emb = get_embedding(engine=engine, text_list=[query], model_path=model_path, embedding_device= embedding_device,) query_emb = query_emb[query] query_embeddings = [query_emb] @@ -133,7 +158,7 @@ class CodeSearch: @param engine: @return: ''' - cg = CypherGenerator() + cg = CypherGenerator(self.llm_config) cypher = cg.get_cypher(query) if not cypher: @@ -156,9 +181,12 @@ class CodeSearch: if __name__ == '__main__': - from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT - from configs.server_config import CHROMA_PERSISTENT_PATH - + # from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT + # from configs.server_config import CHROMA_PERSISTENT_PATH + from coagent.base_configs.env_config import ( + NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT, + CHROMA_PERSISTENT_PATH + ) codebase_name = 'testing' nh = NebulaHandler(host=NEBULA_HOST, port=NEBULA_PORT, username=NEBULA_USER, diff --git a/coagent/codechat/code_search/cypher_generator.py b/coagent/codechat/code_search/cypher_generator.py new file mode 100644 index 0000000..6b23f88 --- /dev/null +++ b/coagent/codechat/code_search/cypher_generator.py @@ -0,0 +1,82 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: cypher_generator.py +@time: 2023/11/24 上午10:17 +@desc: +''' +from langchain import PromptTemplate +from loguru import logger + +from coagent.llm_models.openai_model import getChatModel, getChatModelFromConfig +from coagent.llm_models.llm_config import LLMConfig +from coagent.utils.postprocess import replace_lt_gt +from langchain.schema import ( + HumanMessage, +) +from langchain.chains.graph_qa.prompts import NGQL_GENERATION_PROMPT, CYPHER_GENERATION_TEMPLATE + +schema = ''' +Node properties: [{'tag': 'package', 'properties': []}, {'tag': 'class', 'properties': []}, {'tag': 'method', 'properties': []}] +Edge properties: [{'edge': 'contain', 'properties': []}, {'edge': 'depend', 'properties': []}] +Relationships: ['(:package)-[:contain]->(:class)', '(:class)-[:contain]->(:method)', '(:package)-[:contain]->(:package)'] +''' + + +class CypherGenerator: + def __init__(self, llm_config: LLMConfig): + self.model = getChatModelFromConfig(llm_config) + NEBULAGRAPH_EXTRA_INSTRUCTIONS = """ + Instructions: + + First, generate cypher then convert it to NebulaGraph Cypher dialect(rather than standard): + 1. it requires explicit label specification only when referring to node properties: v.`Foo`.name + 2. note explicit label specification is not needed for edge properties, so it's e.name instead of e.`Bar`.name + 3. it uses double equals sign for comparison: `==` rather than `=` + 4. only use id(Foo) to get the name of node or edge + ```\n""" + + NGQL_GENERATION_TEMPLATE = CYPHER_GENERATION_TEMPLATE.replace( + "Generate Cypher", "Generate NebulaGraph Cypher" + ).replace("Instructions:", NEBULAGRAPH_EXTRA_INSTRUCTIONS) + + self.NGQL_GENERATION_PROMPT = PromptTemplate( + input_variables=["schema", "question"], template=NGQL_GENERATION_TEMPLATE + ) + + def get_cypher(self, query: str): + ''' + get cypher from query + @param query: + @return: + ''' + content = self.NGQL_GENERATION_PROMPT.format(schema=schema, question=query) + logger.info(content) + ans = '' + message = [HumanMessage(content=content)] + chat_res = self.model.predict_messages(message) + ans = chat_res.content + + ans = replace_lt_gt(ans) + + ans = self.post_process(ans) + return ans + + def post_process(self, cypher_res: str): + ''' + 判断是否为正确的 cypher + @param cypher_res: + @return: + ''' + if '(' not in cypher_res or ')' not in cypher_res: + return '' + + return cypher_res + + +if __name__ == '__main__': + query = '代码库里有哪些函数,返回5个就可以' + cg = CypherGenerator() + + ans = cg.get_cypher(query) + logger.debug(f'ans=\n{ans}') diff --git a/dev_opsgpt/codechat/code_search/tagger.py b/coagent/codechat/code_search/tagger.py similarity index 55% rename from dev_opsgpt/codechat/code_search/tagger.py rename to coagent/codechat/code_search/tagger.py index f7efa72..b3a2816 100644 --- a/dev_opsgpt/codechat/code_search/tagger.py +++ b/coagent/codechat/code_search/tagger.py @@ -20,4 +20,20 @@ class Tagger: # simple extract english tag_list = re.findall(r'[a-zA-Z\_\.]+', query) tag_list = list(set(tag_list)) + tag_list = self.filter_tag_list(tag_list) return tag_list + + def filter_tag_list(self, tag_list): + ''' + filter out tag + @param tag_list: + @return: + ''' + res = [] + for tag in tag_list: + if tag in ['java', 'python']: + continue + res.append(tag) + return res + + diff --git a/dev_opsgpt/codechat/codebase_handler/__init__.py b/coagent/codechat/codebase_handler/__init__.py similarity index 100% rename from dev_opsgpt/codechat/codebase_handler/__init__.py rename to coagent/codechat/codebase_handler/__init__.py diff --git a/dev_opsgpt/codechat/codebase_handler/code_importer.py b/coagent/codechat/codebase_handler/code_importer.py similarity index 89% rename from dev_opsgpt/codechat/codebase_handler/code_importer.py rename to coagent/codechat/codebase_handler/code_importer.py index bf50b28..801b19a 100644 --- a/dev_opsgpt/codechat/codebase_handler/code_importer.py +++ b/coagent/codechat/codebase_handler/code_importer.py @@ -8,17 +8,20 @@ import time from loguru import logger -from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT -from configs.server_config import CHROMA_PERSISTENT_PATH -from dev_opsgpt.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from dev_opsgpt.db_handler.vector_db_handler.chroma_handler import ChromaHandler -from dev_opsgpt.embeddings.get_embedding import get_embedding +# from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT +# from configs.server_config import CHROMA_PERSISTENT_PATH +# from configs.model_config import EMBEDDING_DEVICE, EMBEDDING_MODEL +from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler +from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler +from coagent.embeddings.get_embedding import get_embedding +from coagent.llm_models.llm_config import EmbedConfig class CodeImporter: - def __init__(self, codebase_name: str, engine: str, nh: NebulaHandler, ch: ChromaHandler): + def __init__(self, codebase_name: str, embed_config: EmbedConfig, nh: NebulaHandler, ch: ChromaHandler): self.codebase_name = codebase_name - self.engine = engine + # self.engine = engine + self.embed_config: EmbedConfig= embed_config self.nh = nh self.ch = ch @@ -27,9 +30,36 @@ class CodeImporter: import code to nebula and chroma @return: ''' + static_analysis_res = self.filter_out_vertex(static_analysis_res, interpretation) + logger.info(f'static_analysis_res={static_analysis_res}') + self.analysis_res_to_graph(static_analysis_res) self.interpretation_to_db(static_analysis_res, interpretation, do_interpret) + def filter_out_vertex(self, static_analysis_res, interpretation): + ''' + filter out nonexist vertices + @param static_analysis_res: + @param interpretation: + @return: + ''' + save_pac_name = set() + for i, j in static_analysis_res.items(): + save_pac_name.add(j['pac_name']) + + for class_name in j['class_name_list']: + save_pac_name.add(class_name) + save_pac_name.update(j['func_name_dict'].get(class_name, [])) + + for _, structure in static_analysis_res.items(): + new_pac_name_list = [] + for i in structure['import_pac_name_list']: + if i in save_pac_name: + new_pac_name_list.append(i) + + structure['import_pac_name_list'] = new_pac_name_list + return static_analysis_res + def analysis_res_to_graph(self, static_analysis_res): ''' transform static_analysis_res to tuple @@ -93,7 +123,7 @@ class CodeImporter: return - def interpretation_to_db(self, static_analysis_res, interpretation, do_interpret): + def interpretation_to_db(self, static_analysis_res, interpretation, do_interpret, ): ''' vectorize interpretation and save to db @return: @@ -102,7 +132,7 @@ class CodeImporter: if do_interpret: logger.info('start get embedding for interpretion') interp_list = list(interpretation.values()) - emb = get_embedding(engine=self.engine, text_list=interp_list) + emb = get_embedding(engine=self.embed_config.embed_engine, text_list=interp_list, model_path=self.embed_config.embed_model_path, embedding_device= self.embed_config.model_device) logger.info('get embedding done') else: emb = {i: [0] for i in list(interpretation.values())} @@ -113,6 +143,9 @@ class CodeImporter: metadatas = [] for code_text, interp in interpretation.items(): + if code_text not in static_analysis_res: + continue + pac_name = static_analysis_res[code_text]['pac_name'] if pac_name in ids: continue diff --git a/dev_opsgpt/codechat/codebase_handler/codebase_handler.py b/coagent/codechat/codebase_handler/codebase_handler.py similarity index 74% rename from dev_opsgpt/codechat/codebase_handler/codebase_handler.py rename to coagent/codechat/codebase_handler/codebase_handler.py index 66970c5..136e737 100644 --- a/dev_opsgpt/codechat/codebase_handler/codebase_handler.py +++ b/coagent/codechat/codebase_handler/codebase_handler.py @@ -8,26 +8,41 @@ import time from loguru import logger -from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT -from configs.server_config import CHROMA_PERSISTENT_PATH +# from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT +# from configs.server_config import CHROMA_PERSISTENT_PATH +# from configs.model_config import EMBEDDING_ENGINE -from configs.model_config import EMBEDDING_ENGINE +from coagent.base_configs.env_config import ( + NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT, + CHROMA_PERSISTENT_PATH +) -from dev_opsgpt.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from dev_opsgpt.db_handler.vector_db_handler.chroma_handler import ChromaHandler -from dev_opsgpt.codechat.code_crawler.zip_crawler import * -from dev_opsgpt.codechat.code_analyzer.code_analyzer import CodeAnalyzer -from dev_opsgpt.codechat.codebase_handler.code_importer import CodeImporter -from dev_opsgpt.codechat.code_search.code_search import CodeSearch + +from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler +from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler +from coagent.codechat.code_crawler.zip_crawler import * +from coagent.codechat.code_analyzer.code_analyzer import CodeAnalyzer +from coagent.codechat.codebase_handler.code_importer import CodeImporter +from coagent.codechat.code_search.code_search import CodeSearch +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig class CodeBaseHandler: - def __init__(self, codebase_name: str, code_path: str = '', - language: str = 'java', crawl_type: str = 'ZIP'): + def __init__( + self, + codebase_name: str, + code_path: str = '', + language: str = 'java', + crawl_type: str = 'ZIP', + embed_config: EmbedConfig = EmbedConfig(), + llm_config: LLMConfig = LLMConfig() + ): self.codebase_name = codebase_name self.code_path = code_path self.language = language self.crawl_type = crawl_type + self.embed_config = embed_config + self.llm_config = llm_config self.nh = NebulaHandler(host=NEBULA_HOST, port=NEBULA_PORT, username=NEBULA_USER, password=NEBULA_PASSWORD, space_name=codebase_name) @@ -42,7 +57,7 @@ class CodeBaseHandler: @return: ''' # init graph to init tag and edge - code_importer = CodeImporter(engine=EMBEDDING_ENGINE, codebase_name=self.codebase_name, + code_importer = CodeImporter(embed_config=self.embed_config, codebase_name=self.codebase_name, nh=self.nh, ch=self.ch) code_importer.init_graph() time.sleep(5) @@ -56,7 +71,7 @@ class CodeBaseHandler: # analyze code logger.info('start analyze') st1 = time.time() - code_analyzer = CodeAnalyzer(language=self.language) + code_analyzer = CodeAnalyzer(language=self.language, llm_config = self.llm_config) static_analysis_res, interpretation = code_analyzer.analyze(code_dict, do_interpret=do_interpret) logger.debug('analyze done, rt={}'.format(time.time() - st1)) @@ -111,14 +126,15 @@ class CodeBaseHandler: ''' assert search_type in ['cypher', 'tag', 'description'] - code_search = CodeSearch(nh=self.nh, ch=self.ch, limit=limit) + code_search = CodeSearch(llm_config=self.llm_config, nh=self.nh, ch=self.ch, limit=limit) if search_type == 'cypher': search_res = code_search.search_by_cypher(query=query) elif search_type == 'tag': search_res = code_search.search_by_tag(query=query) elif search_type == 'description': - search_res = code_search.search_by_desciption(query=query, engine=EMBEDDING_ENGINE) + search_res = code_search.search_by_desciption( + query=query, engine=self.embed_config.embed_engine, model_path=self.embed_config.embed_model_path, embedding_device=self.embed_config.model_device) context, related_vertice = self.format_search_res(search_res, search_type) return context, related_vertice diff --git a/dev_opsgpt/connector/__init__.py b/coagent/connector/__init__.py similarity index 100% rename from dev_opsgpt/connector/__init__.py rename to coagent/connector/__init__.py diff --git a/dev_opsgpt/connector/agents/__init__.py b/coagent/connector/agents/__init__.py similarity index 57% rename from dev_opsgpt/connector/agents/__init__.py rename to coagent/connector/agents/__init__.py index e0ee881..cc190b1 100644 --- a/dev_opsgpt/connector/agents/__init__.py +++ b/coagent/connector/agents/__init__.py @@ -1,9 +1,8 @@ from .base_agent import BaseAgent from .react_agent import ReactAgent -from .check_agent import CheckAgent from .executor_agent import ExecutorAgent from .selector_agent import SelectorAgent __all__ = [ - "BaseAgent", "ReactAgent", "CheckAgent", "ExecutorAgent", "SelectorAgent" + "BaseAgent", "ReactAgent", "ExecutorAgent", "SelectorAgent" ] \ No newline at end of file diff --git a/dev_opsgpt/connector/agents/base_agent.py b/coagent/connector/agents/base_agent.py similarity index 73% rename from dev_opsgpt/connector/agents/base_agent.py rename to coagent/connector/agents/base_agent.py index 09890ab..a1ae699 100644 --- a/dev_opsgpt/connector/agents/base_agent.py +++ b/coagent/connector/agents/base_agent.py @@ -1,113 +1,218 @@ -from pydantic import BaseModel from typing import List, Union -import re +import importlib +import re, os import copy -import json -import traceback -import uuid from loguru import logger -from dev_opsgpt.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus, CodeDoc, Doc +from coagent.connector.schema import ( + Memory, Task, Role, Message, PromptField, LogVerboseEnum ) -from configs.server_config import SANDBOX_SERVER -from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse -from dev_opsgpt.tools import DDGSTool, DocRetrieval, CodeRetrieval -from dev_opsgpt.connector.configs.prompts import BASE_PROMPT_INPUT, QUERY_CONTEXT_DOC_PROMPT_INPUT, BEGIN_PROMPT_INPUT -from dev_opsgpt.connector.message_process import MessageUtils -from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT, QUERY_CONTEXT_PROMPT_INPUT, PLAN_PROMPT_INPUT - -from dev_opsgpt.llm_models import getChatModel, getExtraModel -from dev_opsgpt.connector.utils import parse_section - - +from coagent.connector.memory_manager import BaseMemoryManager +from coagent.connector.configs.prompts import BEGIN_PROMPT_INPUT +from coagent.connector.message_process import MessageUtils +from coagent.llm_models import getChatModel, getExtraModel, LLMConfig, getChatModelFromConfig, EmbedConfig +from coagent.connector.prompt_manager import PromptManager +from coagent.connector.memory_manager import LocalMemoryManager +from coagent.connector.utils import parse_section +# from configs.model_config import JUPYTER_WORK_PATH +# from configs.server_config import SANDBOX_SERVER class BaseAgent: def __init__( self, role: Role, + prompt_config: [PromptField], + prompt_manager_type: str = "PromptManager", task: Task = None, memory: Memory = None, chat_turn: int = 1, - do_search: bool = False, - do_doc_retrieval: bool = False, - do_tool_retrieval: bool = False, - temperature: float = 0.2, - stop: Union[List[str], str] = None, - do_filter: bool = True, - do_use_self_memory: bool = True, focus_agents: List[str] = [], focus_message_keys: List[str] = [], - # prompt_mamnger: PromptManager + # + llm_config: LLMConfig = None, + embed_config: EmbedConfig = None, + sandbox_server: dict = {}, + jupyter_work_path: str = "", + kb_root_path: str = "", + log_verbose: str = "0" ): self.task = task self.role = role - self.message_utils = MessageUtils(role) - self.llm = self.create_llm_engine(temperature, stop) + self.sandbox_server = sandbox_server + self.jupyter_work_path = jupyter_work_path + self.kb_root_path = kb_root_path + self.message_utils = MessageUtils(role, sandbox_server, jupyter_work_path, embed_config, llm_config, kb_root_path, log_verbose) self.memory = self.init_history(memory) + self.llm_config: LLMConfig = llm_config + self.embed_config: EmbedConfig = embed_config + self.llm = self.create_llm_engine(llm_config=self.llm_config) self.chat_turn = chat_turn - self.do_search = do_search - self.do_doc_retrieval = do_doc_retrieval - self.do_tool_retrieval = do_tool_retrieval + # self.focus_agents = focus_agents self.focus_message_keys = focus_message_keys - self.do_filter = do_filter - self.do_use_self_memory = do_use_self_memory - # self.prompt_manager = None + # + prompt_manager_module = importlib.import_module("coagent.connector.prompt_manager") + prompt_manager = getattr(prompt_manager_module, prompt_manager_type) + self.prompt_manager: PromptManager = prompt_manager(role_prompt=role.role_prompt, prompt_config=prompt_config) + self.log_verbose = max(os.environ.get("log_verbose", "0"), log_verbose) - def run(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> Message: + def step(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: '''agent reponse from multi-message''' message = None - for message in self.arun(query, history, background, memory_pool): + for message in self.astep(query, history, background, memory_manager): pass return message - def arun(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> Message: + def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: '''agent reponse from multi-message''' # insert query into memory query_c = copy.deepcopy(query) query_c = self.start_action_step(query_c) - - self_memory = self.memory if self.do_use_self_memory else None - # create your llm prompt - prompt = self.create_prompt(query_c, self_memory, history, background, memory_pool=memory_pool) + + # llm predict + # prompt = self.create_prompt(query_c, self.memory, history, background, memory_pool=memory_manager.current_memory) + if memory_manager is None: + memory_manager = LocalMemoryManager( + unique_name=self.role.role_name, + do_init=True, + kb_root_path = self.kb_root_path, + embed_config=self.embed_config, + llm_config=self.embed_config + ) + memory_manager.append(query) + memory_pool = memory_manager.current_memory + else: + memory_pool = memory_manager.current_memory + + + logger.debug(f"memory_pool: {memory_pool}") + prompt = self.prompt_manager.generate_full_prompt( + previous_agent_message=query_c, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, memory_pool=memory_pool) content = self.llm.predict(prompt) - logger.debug(f"{self.role.role_name} prompt: {prompt}") - logger.debug(f"{self.role.role_name} content: {content}") + + if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): + logger.debug(f"{self.role.role_name} prompt: {prompt}") + + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"{self.role.role_name} content: {content}") output_message = Message( role_name=self.role.role_name, - role_type="ai", #self.role.role_type, + role_type="assistant", #self.role.role_type, role_content=content, step_content=content, input_query=query_c.input_query, tools=query_c.tools, - parsed_output_list=[query.parsed_output], + # parsed_output_list=[query.parsed_output], customed_kargs=query_c.customed_kargs ) + # common parse llm' content to message output_message = self.message_utils.parser(output_message) - if self.do_filter: - output_message = self.message_utils.filter(output_message) + # action step - output_message, observation_message = self.message_utils.step_router(output_message, history, background, memory_pool=memory_pool) + output_message, observation_message = self.message_utils.step_router(output_message, history, background, memory_manager=memory_manager) output_message.parsed_output_list.append(output_message.parsed_output) if observation_message: output_message.parsed_output_list.append(observation_message.parsed_output) + # update self_memory self.append_history(query_c) self.append_history(output_message) - # logger.info(f"{self.role.role_name} currenct question: {output_message.input_query}\nllm_step_run: {output_message.role_content}") + output_message.input_query = output_message.role_content - # output_message.parsed_output_list.append(output_message.parsed_output) # 与上述重复? # end output_message = self.message_utils.inherit_extrainfo(query, output_message) output_message = self.end_action_step(output_message) + # update memory pool - memory_pool.append(output_message) + memory_manager.append(output_message) yield output_message + + def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None): + prompt = self.prompt_manager.pre_print( + previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, memory_pool=memory_manager.current_memory) + title = f"<<<<{self.role.role_name}'s prompt>>>>" + print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") + + def init_history(self, memory: Memory = None) -> Memory: + return Memory(messages=[]) + + def update_history(self, message: Message): + self.memory.append(message) + + def append_history(self, message: Message): + self.memory.append(message) + + def clear_history(self, ): + self.memory.clear() + self.memory = self.init_history() + + def create_llm_engine(self, llm_config: LLMConfig = None, temperature=0.2, stop=None): + if llm_config is None: + return getChatModel(temperature=temperature, stop=stop) + else: + return getChatModelFromConfig(llm_config=llm_config) + + def registry_actions(self, actions): + '''registry llm's actions''' + self.action_list = actions + + def start_action_step(self, message: Message) -> Message: + '''do action before agent predict ''' + # action_json = self.start_action() + # message["customed_kargs"]["xx"] = action_json + return message + + def end_action_step(self, message: Message) -> Message: + '''do action after agent predict ''' + # action_json = self.end_action() + # message["customed_kargs"]["xx"] = action_json + return message + + def token_usage(self, ): + '''calculate the usage of token''' + pass + + def select_memory_by_key(self, memory: Memory) -> Memory: + return Memory( + messages=[self.select_message_by_key(message) for message in memory.messages + if self.select_message_by_key(message) is not None] + ) + + def select_memory_by_agent_key(self, memory: Memory) -> Memory: + return Memory( + messages=[self.select_message_by_agent_key(message) for message in memory.messages + if self.select_message_by_agent_key(message) is not None] + ) + + def select_message_by_agent_key(self, message: Message) -> Message: + # assume we focus all agents + if self.focus_agents == []: + return message + return None if message is None or message.role_name not in self.focus_agents else self.select_message_by_key(message) + + def select_message_by_key(self, message: Message) -> Message: + # assume we focus all key contents + if message is None: + return message + + if self.focus_message_keys == []: + return message + + message_c = copy.deepcopy(message) + message_c.parsed_output = {k: v for k,v in message_c.parsed_output.items() if k in self.focus_message_keys} + message_c.parsed_output_list = [{k: v for k,v in parsed_output.items() if k in self.focus_message_keys} for parsed_output in message_c.parsed_output_list] + return message_c + + def get_memory(self, content_key="role_content"): + return self.memory.to_tuple_messages(content_key="step_content") + + def get_memory_str(self, content_key="role_content"): + return "\n".join([": ".join(i) for i in self.memory.to_tuple_messages(content_key="step_content")]) + def create_prompt( self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: @@ -225,7 +330,7 @@ class BaseAgent: # logger.debug(f"{self.role.role_name} prompt: {prompt}") return prompt - + def create_doc_prompt(self, message: Message) -> str: '''''' db_docs = message.db_docs @@ -274,76 +379,4 @@ class BaseAgent: if selfmemory_message: selfmemory_message = re.sub("}", "}}", re.sub("{", "{{", selfmemory_message)) return "\n补充自身对话信息: " + selfmemory_message if selfmemory_message else None - - def init_history(self, memory: Memory = None) -> Memory: - return Memory(messages=[]) - - def update_history(self, message: Message): - self.memory.append(message) - - def append_history(self, message: Message): - self.memory.append(message) - - def clear_history(self, ): - self.memory.clear() - self.memory = self.init_history() - - def create_llm_engine(self, temperature=0.2, stop=None): - return getChatModel(temperature=temperature, stop=stop) - - def registry_actions(self, actions): - '''registry llm's actions''' - self.action_list = actions - - def start_action_step(self, message: Message) -> Message: - '''do action before agent predict ''' - # action_json = self.start_action() - # message["customed_kargs"]["xx"] = action_json - return message - - def end_action_step(self, message: Message) -> Message: - '''do action after agent predict ''' - # action_json = self.end_action() - # message["customed_kargs"]["xx"] = action_json - return message - - def token_usage(self, ): - '''calculate the usage of token''' - pass - - def select_memory_by_key(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_key(message) for message in memory.messages - if self.select_message_by_key(message) is not None] - ) - - def select_memory_by_agent_key(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_agent_key(message) for message in memory.messages - if self.select_message_by_agent_key(message) is not None] - ) - - def select_message_by_agent_key(self, message: Message) -> Message: - # assume we focus all agents - if self.focus_agents == []: - return message - return None if message is None or message.role_name not in self.focus_agents else self.select_message_by_key(message) - - def select_message_by_key(self, message: Message) -> Message: - # assume we focus all key contents - if message is None: - return message - - if self.focus_message_keys == []: - return message - - message_c = copy.deepcopy(message) - message_c.parsed_output = {k: v for k,v in message_c.parsed_output.items() if k in self.focus_message_keys} - message_c.parsed_output_list = [{k: v for k,v in parsed_output.items() if k in self.focus_message_keys} for parsed_output in message_c.parsed_output_list] - return message_c - - def get_memory(self, content_key="role_content"): - return self.memory.to_tuple_messages(content_key="step_content") - - def get_memory_str(self, content_key="role_content"): - return "\n".join([": ".join(i) for i in self.memory.to_tuple_messages(content_key="step_content")]) \ No newline at end of file + \ No newline at end of file diff --git a/coagent/connector/agents/executor_agent.py b/coagent/connector/agents/executor_agent.py new file mode 100644 index 0000000..4a41aef --- /dev/null +++ b/coagent/connector/agents/executor_agent.py @@ -0,0 +1,152 @@ +from typing import List, Union +import copy +from loguru import logger + +from coagent.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus, PromptField, LogVerboseEnum +) +from coagent.connector.memory_manager import BaseMemoryManager +from coagent.connector.configs.prompts import BEGIN_PROMPT_INPUT +from coagent.llm_models import LLMConfig, EmbedConfig +from coagent.connector.memory_manager import LocalMemoryManager + +from .base_agent import BaseAgent + + +class ExecutorAgent(BaseAgent): + def __init__( + self, + role: Role, + prompt_config: [PromptField], + prompt_manager_type: str= "PromptManager", + task: Task = None, + memory: Memory = None, + chat_turn: int = 1, + focus_agents: List[str] = [], + focus_message_keys: List[str] = [], + # + llm_config: LLMConfig = None, + embed_config: EmbedConfig = None, + sandbox_server: dict = {}, + jupyter_work_path: str = "", + kb_root_path: str = "", + log_verbose: str = "0" + ): + + super().__init__(role, prompt_config, prompt_manager_type, task, memory, chat_turn, + focus_agents, focus_message_keys, llm_config, embed_config, sandbox_server, + jupyter_work_path, kb_root_path, log_verbose + ) + self.do_all_task = True # run all tasks + + def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: + '''agent reponse from multi-message''' + # insert query into memory + task_executor_memory = Memory(messages=[]) + # insert query + output_message = Message( + role_name=self.role.role_name, + role_type="assistant", #self.role.role_type, + role_content=query.input_query, + step_content="", + input_query=query.input_query, + tools=query.tools, + # parsed_output_list=[query.parsed_output], + customed_kargs=query.customed_kargs + ) + + if memory_manager is None: + memory_manager = LocalMemoryManager( + unique_name=self.role.role_name, + do_init=True, + kb_root_path = self.kb_root_path, + embed_config=self.embed_config, + llm_config=self.embed_config + ) + memory_manager.append(query) + + # self_memory = self.memory if self.do_use_self_memory else None + + plan_step = int(query.parsed_output.get("PLAN_STEP", 0)) + # 如果存在plan字段且plan字段为str的时候 + if "PLAN" not in query.parsed_output or isinstance(query.parsed_output.get("PLAN", []), str) or plan_step >= len(query.parsed_output.get("PLAN", [])): + query_c = copy.deepcopy(query) + query_c = self.start_action_step(query_c) + query_c.parsed_output = {"CURRENT_STEP": query_c.input_query} + task_executor_memory.append(query_c) + for output_message, task_executor_memory in self._arun_step(output_message, query_c, self.memory, history, background, memory_manager, task_executor_memory): + pass + # task_executor_memory.append(query_c) + # content = "the execution step of the plan is exceed the planned scope." + # output_message.parsed_dict = {"Thought": content, "Action Status": "finished", "Action": content} + # task_executor_memory.append(output_message) + + elif "PLAN" in query.parsed_output: + if self.do_all_task: + # run all tasks step by step + for task_content in query.parsed_output["PLAN"][plan_step:]: + # create your llm prompt + query_c = copy.deepcopy(query) + query_c.parsed_output = {"CURRENT_STEP": task_content} + task_executor_memory.append(query_c) + for output_message, task_executor_memory in self._arun_step(output_message, query_c, self.memory, history, background, memory_manager, task_executor_memory): + pass + yield output_message + else: + query_c = copy.deepcopy(query) + query_c = self.start_action_step(query_c) + task_content = query_c.parsed_output["PLAN"][plan_step] + query_c.parsed_output = {"CURRENT_STEP": task_content} + task_executor_memory.append(query_c) + for output_message, task_executor_memory in self._arun_step(output_message, query_c, self.memory, history, background, memory_manager, task_executor_memory): + pass + output_message.parsed_output.update({"CURRENT_STEP": plan_step}) + # update self_memory + self.append_history(query) + self.append_history(output_message) + output_message.input_query = output_message.role_content + # end_action_step + output_message = self.end_action_step(output_message) + # update memory pool + memory_manager.append(output_message) + yield output_message + + def _arun_step(self, output_message: Message, query: Message, self_memory: Memory, + history: Memory, background: Memory, memory_manager: BaseMemoryManager, + task_memory: Memory) -> Union[Message, Memory]: + '''execute the llm predict by created prompt''' + memory_pool = memory_manager.current_memory + prompt = self.prompt_manager.generate_full_prompt( + previous_agent_message=query, agent_long_term_memory=self_memory, ui_history=history, chain_summary_messages=background, memory_pool=memory_pool, + task_memory=task_memory) + content = self.llm.predict(prompt) + + if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): + logger.debug(f"{self.role.role_name} prompt: {prompt}") + + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"{self.role.role_name} content: {content}") + + output_message.role_content = content + output_message.step_content += "\n"+output_message.role_content + output_message = self.message_utils.parser(output_message) + # according the output to choose one action for code_content or tool_content + output_message, observation_message = self.message_utils.step_router(output_message) + # update parserd_output_list + output_message.parsed_output_list.append(output_message.parsed_output) + + react_message = copy.deepcopy(output_message) + task_memory.append(react_message) + if observation_message: + task_memory.append(observation_message) + output_message.parsed_output_list.append(observation_message.parsed_output) + # logger.debug(f"{observation_message.role_name} content: {observation_message.role_content}") + yield output_message, task_memory + + def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None): + task_memory = Memory(messages=[]) + prompt = self.prompt_manager.pre_print( + previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=None, + memory_pool=memory_manager.current_memory, task_memory=task_memory) + title = f"<<<<{self.role.role_name}'s prompt>>>>" + print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") diff --git a/dev_opsgpt/connector/agents/react_agent.py b/coagent/connector/agents/react_agent.py similarity index 51% rename from dev_opsgpt/connector/agents/react_agent.py rename to coagent/connector/agents/react_agent.py index 6593cea..64e23f1 100644 --- a/dev_opsgpt/connector/agents/react_agent.py +++ b/coagent/connector/agents/react_agent.py @@ -1,96 +1,117 @@ -from pydantic import BaseModel from typing import List, Union -import re -import json import traceback import copy from loguru import logger -from langchain.prompts.chat import ChatPromptTemplate - -from dev_opsgpt.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus +from coagent.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus, PromptField, LogVerboseEnum ) -from dev_opsgpt.llm_models import getChatModel -from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT - +from coagent.connector.memory_manager import BaseMemoryManager +from coagent.connector.configs.agent_config import REACT_PROMPT_INPUT +from coagent.llm_models import LLMConfig, EmbedConfig from .base_agent import BaseAgent +from coagent.connector.memory_manager import LocalMemoryManager + +from coagent.connector.prompt_manager import PromptManager class ReactAgent(BaseAgent): def __init__( self, role: Role, + prompt_config: [PromptField], + prompt_manager_type: str = "PromptManager", task: Task = None, memory: Memory = None, chat_turn: int = 1, - do_search: bool = False, - do_doc_retrieval: bool = False, - do_tool_retrieval: bool = False, - temperature: float = 0.2, - stop: Union[List[str], str] = None, - do_filter: bool = True, - do_use_self_memory: bool = True, focus_agents: List[str] = [], focus_message_keys: List[str] = [], - # prompt_mamnger: PromptManager + # + llm_config: LLMConfig = None, + embed_config: EmbedConfig = None, + sandbox_server: dict = {}, + jupyter_work_path: str = "", + kb_root_path: str = "", + log_verbose: str = "0" ): - super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, - do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, - focus_agents, focus_message_keys + super().__init__(role, prompt_config, prompt_manager_type, task, memory, chat_turn, + focus_agents, focus_message_keys, llm_config, embed_config, sandbox_server, + jupyter_work_path, kb_root_path, log_verbose ) - def run(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: + def step(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: '''agent reponse from multi-message''' - for message in self.arun(query, history, background, memory_pool): + for message in self.astep(query, history, background, memory_manager): pass return message - def arun(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: + def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: '''agent reponse from multi-message''' step_nums = copy.deepcopy(self.chat_turn) react_memory = Memory(messages=[]) # insert query output_message = Message( role_name=self.role.role_name, - role_type="ai", #self.role.role_type, + role_type="assistant", #self.role.role_type, role_content=query.input_query, step_content="", input_query=query.input_query, tools=query.tools, - parsed_output_list=[query.parsed_output], + # parsed_output_list=[query.parsed_output], customed_kargs=query.customed_kargs ) query_c = copy.deepcopy(query) query_c = self.start_action_step(query_c) - if query.parsed_output: - query_c.parsed_output = {"Question": "\n".join([f"{v}" for k, v in query.parsed_output.items() if k not in ["Action Status"]])} - else: - query_c.parsed_output = {"Question": query.input_query} - react_memory.append(query_c) - self_memory = self.memory if self.do_use_self_memory else None + # if query.parsed_output: + # query_c.parsed_output = {"Question": "\n".join([f"{v}" for k, v in query.parsed_output.items() if k not in ["Action Status"]])} + # else: + # query_c.parsed_output = {"Question": query.input_query} + # react_memory.append(query_c) + # self_memory = self.memory if self.do_use_self_memory else None idx = 0 # start to react while step_nums > 0: output_message.role_content = output_message.step_content - prompt = self.create_prompt(query, self_memory, history, background, react_memory, memory_pool) + # prompt = self.create_prompt(query, self.memory, history, background, react_memory, memory_manager.current_memory) + + if memory_manager is None: + memory_manager = LocalMemoryManager( + unique_name=self.role.role_name, + do_init=True, + kb_root_path = self.kb_root_path, + embed_config=self.embed_config, + llm_config=self.embed_config + ) + memory_manager.append(query) + memory_pool = memory_manager.current_memory + else: + memory_pool = memory_manager.current_memory + + prompt = self.prompt_manager.generate_full_prompt( + previous_agent_message=query_c, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=react_memory, + memory_pool=memory_pool) try: content = self.llm.predict(prompt) except Exception as e: - logger.warning(f"error prompt: {prompt}") + logger.error(f"error prompt: {prompt}") raise Exception(traceback.format_exc()) output_message.role_content = "\n"+content output_message.step_content += "\n"+output_message.role_content yield output_message - # logger.debug(f"{self.role.role_name}, {idx} iteration prompt: {prompt}") - logger.info(f"{self.role.role_name}, {idx} iteration step_run: {output_message.role_content}") + if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): + logger.debug(f"{self.role.role_name}, {idx} iteration prompt: {prompt}") + + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"{self.role.role_name}, {idx} iteration step_run: {output_message.role_content}") output_message = self.message_utils.parser(output_message) # when get finished signal can stop early - if output_message.action_status == ActionStatus.FINISHED or output_message.action_status == ActionStatus.STOPED: break + if output_message.action_status == ActionStatus.FINISHED or output_message.action_status == ActionStatus.STOPPED: + output_message.parsed_output_list.append(output_message.parsed_output) + break # according the output to choose one action for code_content or tool_content output_message, observation_message = self.message_utils.step_router(output_message) output_message.parsed_output_list.append(output_message.parsed_output) @@ -101,21 +122,45 @@ class ReactAgent(BaseAgent): react_memory.append(observation_message) output_message.parsed_output_list.append(observation_message.parsed_output) # logger.debug(f"{observation_message.role_name} content: {observation_message.role_content}") - # logger.info(f"{self.role.role_name} currenct question: {output_message.input_query}\nllm_react_run: {output_message.role_content}") - idx += 1 step_nums -= 1 yield output_message # react' self_memory saved at last self.append_history(output_message) - # update memory pool - # memory_pool.append(output_message) output_message.input_query = query.input_query - # end_action_step + # end_action_step, BUG:it may cause slack some information output_message = self.end_action_step(output_message) # update memory pool - memory_pool.append(output_message) + memory_manager.append(output_message) yield output_message + + def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None): + react_memory = Memory(messages=[]) + prompt = self.prompt_manager.pre_print( + previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=react_memory, + memory_pool=memory_manager.current_memory) + title = f"<<<<{self.role.role_name}'s prompt>>>>" + print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") + + # def create_prompt( + # self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, react_memory: Memory = None, memory_manager: BaseMemoryManager= None, + # prompt_mamnger=None) -> str: + # prompt_mamnger = PromptManager() + # prompt_mamnger.register_standard_fields() + + # # input_keys = parse_section(self.role.role_prompt, 'Agent Profile') + + # data_dict = { + # "agent_profile": extract_section(self.role.role_prompt, 'Agent Profile'), + # "tool_information": query.tools, + # "session_records": memory_manager, + # "reference_documents": query, + # "output_format": extract_section(self.role.role_prompt, 'Response Output Format'), + # "response": "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()]), + # } + # # logger.debug(memory_pool) + + # return prompt_mamnger.generate_full_prompt(data_dict) def create_prompt( self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, react_memory: Memory = None, memory_pool: Memory= None, diff --git a/coagent/connector/agents/selector_agent.py b/coagent/connector/agents/selector_agent.py new file mode 100644 index 0000000..76a2012 --- /dev/null +++ b/coagent/connector/agents/selector_agent.py @@ -0,0 +1,190 @@ +from typing import List, Union +import copy +import random +from loguru import logger + +from coagent.connector.schema import ( + Memory, Task, Role, Message, PromptField, LogVerboseEnum +) +from coagent.connector.memory_manager import BaseMemoryManager +from coagent.connector.configs.prompts import BEGIN_PROMPT_INPUT +from coagent.connector.memory_manager import LocalMemoryManager +from coagent.llm_models import LLMConfig, EmbedConfig +from .base_agent import BaseAgent + + +class SelectorAgent(BaseAgent): + + def __init__( + self, + role: Role, + prompt_config: List[PromptField] = None, + prompt_manager_type: str = "PromptManager", + task: Task = None, + memory: Memory = None, + chat_turn: int = 1, + focus_agents: List[str] = [], + focus_message_keys: List[str] = [], + group_agents: List[BaseAgent] = [], + # + llm_config: LLMConfig = None, + embed_config: EmbedConfig = None, + sandbox_server: dict = {}, + jupyter_work_path: str = "", + kb_root_path: str = "", + log_verbose: str = "0" + ): + + super().__init__(role, prompt_config, prompt_manager_type, task, memory, chat_turn, + focus_agents, focus_message_keys, llm_config, embed_config, sandbox_server, + jupyter_work_path, kb_root_path, log_verbose + ) + self.group_agents = group_agents + + def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: + '''agent reponse from multi-message''' + # insert query into memory + query_c = copy.deepcopy(query) + query_c = self.start_action_step(query_c) + # create your llm prompt + if memory_manager is None: + memory_manager = LocalMemoryManager( + unique_name=self.role.role_name, + do_init=True, + kb_root_path = self.kb_root_path, + embed_config=self.embed_config, + llm_config=self.embed_config + ) + memory_manager.append(query) + memory_pool = memory_manager.current_memory + else: + memory_pool = memory_manager.current_memory + prompt = self.prompt_manager.generate_full_prompt( + previous_agent_message=query_c, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=None, + memory_pool=memory_pool, agents=self.group_agents) + content = self.llm.predict(prompt) + + if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): + logger.debug(f"{self.role.role_name} prompt: {prompt}") + + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"{self.role.role_name} content: {content}") + + # select agent + select_message = Message( + role_name=self.role.role_name, + role_type="assistant", #self.role.role_type, + role_content=content, + step_content=content, + input_query=query_c.input_query, + tools=query_c.tools, + # parsed_output_list=[query_c.parsed_output] + customed_kargs=query.customed_kargs + ) + # common parse llm' content to message + select_message = self.message_utils.parser(select_message) + select_message.parsed_output_list.append(select_message.parsed_output) + + output_message = None + if select_message.parsed_output.get("Role", "") in [agent.role.role_name for agent in self.group_agents]: + for agent in self.group_agents: + if agent.role.role_name == select_message.parsed_output.get("Role", ""): + break + for output_message in agent.astep(query_c, history, background=background, memory_manager=memory_manager): + yield output_message or select_message + # update self_memory + self.append_history(query_c) + self.append_history(output_message) + output_message.input_query = output_message.role_content + # output_message.parsed_output_list.append(output_message.parsed_output) + # + output_message = self.end_action_step(output_message) + # update memory pool + memory_manager.append(output_message) + + select_message.parsed_output = output_message.parsed_output + select_message.parsed_output_list.extend(output_message.parsed_output_list) + yield select_message + + def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None): + prompt = self.prompt_manager.pre_print( + previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=None, + memory_pool=memory_manager.current_memory, agents=self.group_agents) + title = f"<<<<{self.role.role_name}'s prompt>>>>" + print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") + + for agent in self.group_agents: + agent.pre_print(query=query, history=history, background=background, memory_manager=memory_manager) + + # def create_prompt( + # self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None, prompt_mamnger=None) -> str: + # ''' + # role\task\tools\docs\memory + # ''' + # # + # doc_infos = self.create_doc_prompt(query) + # code_infos = self.create_codedoc_prompt(query) + # # + # formatted_tools, tool_names, tools_descs = self.create_tools_prompt(query) + # agent_names, agents = self.create_agent_names() + # task_prompt = self.create_task_prompt(query) + # background_prompt = self.create_background_prompt(background) + # history_prompt = self.create_history_prompt(history) + # selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") + + + # DocInfos = "" + # if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": + # DocInfos += f"\nDocument Information: {doc_infos}" + + # if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": + # DocInfos += f"\nCodeBase Infomation: {code_infos}" + + # input_query = query.input_query + # logger.debug(f"{self.role.role_name} input_query: {input_query}") + # prompt = self.role.role_prompt.format(**{"agent_names": agent_names, "agents": agents, "formatted_tools": tools_descs, "tool_names": tool_names}) + # # + # memory_pool_select_by_agent_key = self.select_memory_by_agent_key(memory_manager.current_memory) + # memory_pool_select_by_agent_key_context = '\n\n'.join([f"*{k}*\n{v}" for parsed_output in memory_pool_select_by_agent_key.get_parserd_output_list() for k, v in parsed_output.items() if k not in ['Action Status']]) + + # input_keys = parse_section(self.role.role_prompt, 'Input Format') + # # + # prompt += "\n" + BEGIN_PROMPT_INPUT + # for input_key in input_keys: + # if input_key == "Origin Query": + # prompt += "\n**Origin Query:**\n" + query.origin_query + # elif input_key == "Context": + # context = "\n".join([f"*{k}*\n{v}" for i in query.parsed_output_list for k,v in i.items() if "Action Status" !=k]) + # if history: + # context = history_prompt + "\n" + context + # if not context: + # context = "there is no context" + + # if self.focus_agents and memory_pool_select_by_agent_key_context: + # context = memory_pool_select_by_agent_key_context + # prompt += "\n**Context:**\n" + context + "\n" + input_query + # elif input_key == "DocInfos": + # prompt += "\n**DocInfos:**\n" + DocInfos + # elif input_key == "Question": + # prompt += "\n**Question:**\n" + input_query + + # while "{{" in prompt or "}}" in prompt: + # prompt = prompt.replace("{{", "{") + # prompt = prompt.replace("}}", "}") + + # # logger.debug(f"{self.role.role_name} prompt: {prompt}") + # return prompt + + # def create_agent_names(self): + # random.shuffle(self.group_agents) + # agent_names = ", ".join([f'{agent.role.role_name}' for agent in self.group_agents]) + # agent_descs = [] + # for agent in self.group_agents: + # role_desc = agent.role.role_prompt.split("####")[1] + # while "\n\n" in role_desc: + # role_desc = role_desc.replace("\n\n", "\n") + # role_desc = role_desc.replace("\n", ",") + + # agent_descs.append(f'"role name: {agent.role.role_name}\nrole description: {role_desc}"') + + # return agent_names, "\n".join(agent_descs) \ No newline at end of file diff --git a/dev_opsgpt/connector/chains/__init__.py b/coagent/connector/chains/__init__.py similarity index 100% rename from dev_opsgpt/connector/chains/__init__.py rename to coagent/connector/chains/__init__.py diff --git a/dev_opsgpt/connector/chains/base_chain.py b/coagent/connector/chains/base_chain.py similarity index 58% rename from dev_opsgpt/connector/chains/base_chain.py rename to coagent/connector/chains/base_chain.py index 336ba24..7840ffb 100644 --- a/dev_opsgpt/connector/chains/base_chain.py +++ b/coagent/connector/chains/base_chain.py @@ -1,56 +1,68 @@ -from pydantic import BaseModel from typing import List -import json -import re from loguru import logger -import traceback -import uuid -import copy +import copy, os -from dev_opsgpt.connector.agents import BaseAgent, CheckAgent -from dev_opsgpt.tools.base_tool import BaseTools, Tool +from coagent.connector.agents import BaseAgent -from dev_opsgpt.connector.schema import ( +from coagent.connector.schema import ( Memory, Role, Message, ActionStatus, ChainConfig, load_role_configs ) -from dev_opsgpt.connector.message_process import MessageUtils - -from dev_opsgpt.connector.configs.agent_config import AGETN_CONFIGS +from coagent.connector.memory_manager import BaseMemoryManager +from coagent.connector.message_process import MessageUtils +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig +from coagent.connector.configs.agent_config import AGETN_CONFIGS role_configs = load_role_configs(AGETN_CONFIGS) +# from configs.model_config import JUPYTER_WORK_PATH +# from configs.server_config import SANDBOX_SERVER + class BaseChain: def __init__( self, - chainConfig: ChainConfig, + # chainConfig: ChainConfig, agents: List[BaseAgent], chat_turn: int = 1, do_checker: bool = False, - # prompt_mamnger: PromptManager + sandbox_server: dict = {}, + jupyter_work_path: str = "", + kb_root_path: str = "", + llm_config: LLMConfig = LLMConfig(), + embed_config: EmbedConfig = None, + log_verbose: str = "0" ) -> None: - self.chainConfig = chainConfig - self.agents = agents + # self.chainConfig = chainConfig + self.agents: List[BaseAgent] = agents self.chat_turn = chat_turn self.do_checker = do_checker - self.checker = CheckAgent(role=role_configs["checker"].role, - task = None, - memory = None, - do_search = role_configs["checker"].do_search, - do_doc_retrieval = role_configs["checker"].do_doc_retrieval, - do_tool_retrieval = role_configs["checker"].do_tool_retrieval, - do_filter=False, do_use_self_memory=False) - self.messageUtils = MessageUtils() + self.sandbox_server = sandbox_server + self.jupyter_work_path = jupyter_work_path + self.llm_config = llm_config + self.log_verbose = max(os.environ.get("log_verbose", "0"), log_verbose) + self.checker = BaseAgent(role=role_configs["checker"].role, + prompt_config=role_configs["checker"].prompt_config, + task = None, memory = None, + llm_config=llm_config, embed_config=embed_config, + sandbox_server=sandbox_server, jupyter_work_path=jupyter_work_path, + kb_root_path=kb_root_path + ) + self.messageUtils = MessageUtils(None, sandbox_server, self.jupyter_work_path, embed_config, llm_config, kb_root_path, log_verbose) # all memory created by agent until instance deleted self.global_memory = Memory(messages=[]) - def step(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: + def step(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: '''execute chain''' - for output_message, local_memory in self.astep(query, history, background, memory_pool): + for output_message, local_memory in self.astep(query, history, background, memory_manager): pass return output_message, local_memory + + def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: + '''execute chain''' + for agent in self.agents: + agent.pre_print(query, history, background=background, memory_manager=memory_manager) - def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: + def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: '''execute chain''' local_memory = Memory(messages=[]) input_message = copy.deepcopy(query) @@ -61,42 +73,35 @@ class BaseChain: # local_memory.append(input_message) while step_nums > 0: for agent in self.agents: - for output_message in agent.arun(input_message, history, background=background, memory_pool=memory_pool): + for output_message in agent.astep(input_message, history, background=background, memory_manager=memory_manager): # logger.debug(f"local_memory {local_memory + output_message}") yield output_message, local_memory + output_message - output_message = self.messageUtils.inherit_extrainfo(input_message, output_message) # according the output to choose one action for code_content or tool_content - # logger.info(f"{agent.role.role_name} currenct message: {output_message.step_content}\n next llm question: {output_message.input_query}") output_message = self.messageUtils.parser(output_message) yield output_message, local_memory + output_message # output_message = self.step_router(output_message) - input_message = output_message self.global_memory.append(output_message) local_memory.append(output_message) # when get finished signal can stop early - if output_message.action_status == ActionStatus.FINISHED or output_message.action_status == ActionStatus.STOPED: + if output_message.action_status == ActionStatus.FINISHED or output_message.action_status == ActionStatus.STOPPED: action_status = False break - if output_message.action_status == ActionStatus.FINISHED: break if self.do_checker and self.chat_turn > 1: - # logger.debug(f"{self.checker.role.role_name} input global memory: {self.global_memory.to_str_messages(content_key='step_content', return_all=False)}") - for check_message in self.checker.arun(query, background=local_memory, memory_pool=memory_pool): + for check_message in self.checker.astep(query, background=local_memory, memory_manager=memory_manager): pass check_message = self.messageUtils.parser(check_message) - check_message = self.messageUtils.filter(check_message) check_message = self.messageUtils.inherit_extrainfo(output_message, check_message) - logger.debug(f"{self.checker.role.role_name}: {check_message.role_content}") + # logger.debug(f"{self.checker.role.role_name}: {check_message.role_content}") if check_message.action_status == ActionStatus.FINISHED: self.global_memory.append(check_message) break - step_nums -= 1 # output_message = check_message or output_message # 返回chain和checker的结果 @@ -109,8 +114,6 @@ class BaseChain: def get_memory_str(self, content_key="role_content") -> Memory: memory = self.global_memory - # for i in memory.to_tuple_messages(content_key=content_key): - # logger.debug(f"{i}") return "\n".join([": ".join(i) for i in memory.to_tuple_messages(content_key=content_key)]) def get_agents_memory(self, content_key="role_content"): diff --git a/coagent/connector/chains/chains.py b/coagent/connector/chains/chains.py new file mode 100644 index 0000000..50d71bc --- /dev/null +++ b/coagent/connector/chains/chains.py @@ -0,0 +1,12 @@ +from typing import List +from loguru import logger +from coagent.connector.agents import BaseAgent +from .base_chain import BaseChain + + + + +class ExecutorRefineChain(BaseChain): + + def __init__(self, agents: List[BaseAgent], do_code_exec: bool = False) -> None: + super().__init__(agents, do_code_exec) diff --git a/coagent/connector/configs/__init__.py b/coagent/connector/configs/__init__.py new file mode 100644 index 0000000..fd1c6bd --- /dev/null +++ b/coagent/connector/configs/__init__.py @@ -0,0 +1,9 @@ +from .agent_config import AGETN_CONFIGS +from .chain_config import CHAIN_CONFIGS +from .phase_config import PHASE_CONFIGS +from .prompt_config import BASE_PROMPT_CONFIGS, EXECUTOR_PROMPT_CONFIGS, SELECTOR_PROMPT_CONFIGS, BASE_NOTOOLPROMPT_CONFIGS + +__all__ = [ + "AGETN_CONFIGS", "CHAIN_CONFIGS", "PHASE_CONFIGS", + "BASE_PROMPT_CONFIGS", "EXECUTOR_PROMPT_CONFIGS", "SELECTOR_PROMPT_CONFIGS", "BASE_NOTOOLPROMPT_CONFIGS" + ] \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/agent_config.py b/coagent/connector/configs/agent_config.py similarity index 75% rename from dev_opsgpt/connector/configs/agent_config.py rename to coagent/connector/configs/agent_config.py index 58a1150..5073795 100644 --- a/dev_opsgpt/connector/configs/agent_config.py +++ b/coagent/connector/configs/agent_config.py @@ -13,6 +13,7 @@ from .prompts import ( REACT_TEMPLATE_PROMPT, REACT_TOOL_PROMPT, REACT_CODE_PROMPT, REACT_TOOL_AND_CODE_PLANNER_PROMPT, REACT_TOOL_AND_CODE_PROMPT ) +from .prompt_config import BASE_PROMPT_CONFIGS, EXECUTOR_PROMPT_CONFIGS, SELECTOR_PROMPT_CONFIGS, BASE_NOTOOLPROMPT_CONFIGS @@ -34,11 +35,9 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "SelectorAgent" }, + "prompt_config": SELECTOR_PROMPT_CONFIGS, "group_agents": ["tool_react", "code_react"], "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "checker": { "role": { @@ -48,10 +47,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "conv_summary": { "role": { @@ -61,10 +58,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "general_planner": { "role": { @@ -74,10 +69,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "executor": { "role": { @@ -87,11 +80,9 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "ExecutorAgent", }, + "prompt_config": EXECUTOR_PROMPT_CONFIGS, "stop": "\n**Observation:**", "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "base_refiner": { "role": { @@ -101,10 +92,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "planner": { "role": { @@ -114,10 +103,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "intention_recognizer": { "role": { @@ -127,10 +114,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "tool_planner": { "role": { @@ -140,10 +125,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "tool_and_code_react": { "role": { @@ -153,11 +136,9 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "ReactAgent", }, + "prompt_config": BASE_PROMPT_CONFIGS, "stop": "\n**Observation:**", "chat_turn": 7, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "tool_and_code_planner": { "role": { @@ -167,10 +148,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "tool_react": { "role": { @@ -180,10 +159,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "ReactAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 5, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False, "stop": "\n**Observation:**" }, "code_react": { @@ -194,10 +171,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "ReactAgent" }, + "prompt_config": BASE_NOTOOLPROMPT_CONFIGS, "chat_turn": 5, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False, "stop": "\n**Observation:**" }, "qaer": { @@ -208,23 +183,19 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "code_qaer": { "role": { - "role_prompt": CODE_QA_PROMPT , + "role_prompt": CODE_QA_PROMPT, "role_type": "assistant", "role_name": "code_qaer", "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": True, - "do_tool_retrieval": False }, "searcher": { "role": { @@ -234,10 +205,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": True, - "do_doc_retrieval": False, - "do_tool_retrieval": False }, "metaGPT_PRD": { "role": { @@ -247,10 +216,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False, "focus_agents": [], "focus_message_keys": [], }, @@ -263,10 +230,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False, "focus_agents": ["metaGPT_PRD"], "focus_message_keys": [], }, @@ -278,10 +243,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "BaseAgent" }, + "prompt_config": BASE_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False, "focus_agents": ["metaGPT_DESIGN"], "focus_message_keys": [], }, @@ -293,10 +256,8 @@ AGETN_CONFIGS = { "role_desc": "", "agent_type": "ExecutorAgent" }, + "prompt_config": EXECUTOR_PROMPT_CONFIGS, "chat_turn": 1, - "do_search": False, - "do_doc_retrieval": False, - "do_tool_retrieval": False, "focus_agents": ["metaGPT_DESIGN", "metaGPT_TASK"], "focus_message_keys": [], }, diff --git a/dev_opsgpt/connector/configs/agent_prompt/design_writer.yaml b/coagent/connector/configs/agent_prompt/design_writer.yaml similarity index 100% rename from dev_opsgpt/connector/configs/agent_prompt/design_writer.yaml rename to coagent/connector/configs/agent_prompt/design_writer.yaml diff --git a/dev_opsgpt/connector/configs/agent_prompt/prd_writer.yaml b/coagent/connector/configs/agent_prompt/prd_writer.yaml similarity index 100% rename from dev_opsgpt/connector/configs/agent_prompt/prd_writer.yaml rename to coagent/connector/configs/agent_prompt/prd_writer.yaml diff --git a/dev_opsgpt/connector/configs/agent_prompt/review_code.yaml b/coagent/connector/configs/agent_prompt/review_code.yaml similarity index 100% rename from dev_opsgpt/connector/configs/agent_prompt/review_code.yaml rename to coagent/connector/configs/agent_prompt/review_code.yaml diff --git a/dev_opsgpt/connector/configs/agent_prompt/task_write.yaml b/coagent/connector/configs/agent_prompt/task_write.yaml similarity index 100% rename from dev_opsgpt/connector/configs/agent_prompt/task_write.yaml rename to coagent/connector/configs/agent_prompt/task_write.yaml diff --git a/dev_opsgpt/connector/configs/agent_prompt/write_code.yaml b/coagent/connector/configs/agent_prompt/write_code.yaml similarity index 100% rename from dev_opsgpt/connector/configs/agent_prompt/write_code.yaml rename to coagent/connector/configs/agent_prompt/write_code.yaml diff --git a/dev_opsgpt/connector/configs/chain_config.py b/coagent/connector/configs/chain_config.py similarity index 100% rename from dev_opsgpt/connector/configs/chain_config.py rename to coagent/connector/configs/chain_config.py diff --git a/dev_opsgpt/connector/configs/phase_config.py b/coagent/connector/configs/phase_config.py similarity index 76% rename from dev_opsgpt/connector/configs/phase_config.py rename to coagent/connector/configs/phase_config.py index 5b50002..48be270 100644 --- a/dev_opsgpt/connector/configs/phase_config.py +++ b/coagent/connector/configs/phase_config.py @@ -88,26 +88,26 @@ PHASE_CONFIGS = { "do_tool_retrieval": False, "do_using_tool": False }, - # "metagpt_code_devlop": { - # "phase_name": "metagpt_code_devlop", - # "phase_type": "BasePhase", - # "chains": ["metagptChain",], - # "do_summary": False, - # "do_search": False, - # "do_doc_retrieval": False, - # "do_code_retrieval": False, - # "do_tool_retrieval": False, - # "do_using_tool": False - # }, - # "baseGroupPhase": { - # "phase_name": "baseGroupPhase", - # "phase_type": "BasePhase", - # "chains": ["baseGroupChain"], - # "do_summary": False, - # "do_search": False, - # "do_doc_retrieval": False, - # "do_code_retrieval": False, - # "do_tool_retrieval": False, - # "do_using_tool": False - # }, + "metagpt_code_devlop": { + "phase_name": "metagpt_code_devlop", + "phase_type": "BasePhase", + "chains": ["metagptChain",], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": False + }, + "baseGroupPhase": { + "phase_name": "baseGroupPhase", + "phase_type": "BasePhase", + "chains": ["baseGroupChain"], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": False + }, } diff --git a/coagent/connector/configs/prompt_config.py b/coagent/connector/configs/prompt_config.py new file mode 100644 index 0000000..45c0f3d --- /dev/null +++ b/coagent/connector/configs/prompt_config.py @@ -0,0 +1,43 @@ +BASE_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'task_records', "function_name": 'handle_task_records'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} + ] + +BASE_NOTOOLPROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} + ] + +EXECUTOR_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'task_records', "function_name": 'handle_task_records'}, + {"field_name": 'current_plan', "function_name": 'handle_current_plan'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} + ] + +SELECTOR_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, + {"field_name": 'agent_infomation', "function_name": 'handle_agent_data', "is_context": False, "omit_if_empty": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'current_plan', "function_name": 'handle_current_plan'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} + ] \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/__init__.py b/coagent/connector/configs/prompts/__init__.py similarity index 83% rename from dev_opsgpt/connector/configs/prompts/__init__.py rename to coagent/connector/configs/prompts/__init__.py index 859feda..e939e07 100644 --- a/dev_opsgpt/connector/configs/prompts/__init__.py +++ b/coagent/connector/configs/prompts/__init__.py @@ -8,9 +8,9 @@ from .intention_template_prompt import RECOGNIZE_INTENTION_PROMPT from .checker_template_prompt import CHECKER_PROMPT, CHECKER_TEMPLATE_PROMPT -from .summary_template_prompt import CONV_SUMMARY_PROMPT +from .summary_template_prompt import CONV_SUMMARY_PROMPT, CONV_SUMMARY_PROMPT_SPEC -from .qa_template_prompt import QA_PROMPT, CODE_QA_PROMPT, QA_TEMPLATE_PROMPT +from .qa_template_prompt import QA_PROMPT, CODE_QA_PROMPT, QA_TEMPLATE_PROMPT, CODE_PROMPT_TEMPLATE, CODE_INTERPERT_TEMPLATE, ORIGIN_TEMPLATE_PROMPT from .executor_template_prompt import EXECUTOR_TEMPLATE_PROMPT from .refine_template_prompt import REFINE_TEMPLATE_PROMPT @@ -30,8 +30,8 @@ __all__ = [ "RECOGNIZE_INTENTION_PROMPT", "PRD_WRITER_METAGPT_PROMPT", "DESIGN_WRITER_METAGPT_PROMPT", "TASK_WRITER_METAGPT_PROMPT", "CODE_WRITER_METAGPT_PROMPT", "CHECKER_PROMPT", "CHECKER_TEMPLATE_PROMPT", - "CONV_SUMMARY_PROMPT", - "QA_PROMPT", "CODE_QA_PROMPT", "QA_TEMPLATE_PROMPT", + "CONV_SUMMARY_PROMPT", "CONV_SUMMARY_PROMPT_SPEC", + "QA_PROMPT", "CODE_QA_PROMPT", "QA_TEMPLATE_PROMPT", "CODE_PROMPT_TEMPLATE", "CODE_INTERPERT_TEMPLATE", "ORIGIN_TEMPLATE_PROMPT", "EXECUTOR_TEMPLATE_PROMPT", "REFINE_TEMPLATE_PROMPT", "SELECTOR_AGENT_TEMPLATE_PROMPT", diff --git a/coagent/connector/configs/prompts/agent_selector_template_prompt.py b/coagent/connector/configs/prompts/agent_selector_template_prompt.py new file mode 100644 index 0000000..099f0ad --- /dev/null +++ b/coagent/connector/configs/prompts/agent_selector_template_prompt.py @@ -0,0 +1,21 @@ +SELECTOR_AGENT_TEMPLATE_PROMPT = """#### Agent Profile + +Your goal is to response according the Context Data's information with the role that will best facilitate a solution, taking into account all relevant context (Context) provided. + +When you need to select the appropriate role for handling a user's query, carefully read the provided role names, role descriptions and tool list. + +ATTENTION: response carefully referenced "Response Output Format" in format. + +#### Input Format + +**Origin Query:** the initial question or objective that the user wanted to achieve + +**Context:** the context history to determine if Origin Query has been achieved. + +#### Response Output Format + +**Thoughts:** think the reason step by step about why you selecte one role + +**Role:** Select the role from agent names. + +""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/checker_template_prompt.py b/coagent/connector/configs/prompts/checker_template_prompt.py similarity index 92% rename from dev_opsgpt/connector/configs/prompts/checker_template_prompt.py rename to coagent/connector/configs/prompts/checker_template_prompt.py index 2b02fc3..8378b7e 100644 --- a/dev_opsgpt/connector/configs/prompts/checker_template_prompt.py +++ b/coagent/connector/configs/prompts/checker_template_prompt.py @@ -1,5 +1,5 @@ -CHECKER_TEMPLATE_PROMPT = """#### Checker Assistance Guidance +CHECKER_TEMPLATE_PROMPT = """#### Agent Profile When users have completed a sequence of tasks or if there is clear evidence that no further actions are required, your role is to confirm the completion. Your task is to assess the current situation based on the context and determine whether all objectives have been met. @@ -12,7 +12,7 @@ Each decision should be justified based on the context provided, specifying if t **Context:** the current status and history of the tasks to determine if Origin Query has been achieved. #### Response Output Format -**Action Status:** Set to 'finished' or 'continued'. +**Action Status:** finished or continued If it's 'finished', the context can answer the origin query. If it's 'continued', the context cant answer the origin query. diff --git a/coagent/connector/configs/prompts/executor_template_prompt.py b/coagent/connector/configs/prompts/executor_template_prompt.py new file mode 100644 index 0000000..5731b5c --- /dev/null +++ b/coagent/connector/configs/prompts/executor_template_prompt.py @@ -0,0 +1,31 @@ +EXECUTOR_TEMPLATE_PROMPT = """#### Agent Profile + +When users need help with coding or using tools, your role is to provide precise and effective guidance. +Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. +Each reply should contain only the guidance required for the current step either by tool usage or code. +ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. + +#### Response Output Format + +**Thoughts:** Considering the session records and executed steps, decide whether the current step requires the use of a tool or code_executing. +Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. +If code_executing is required, outline the plan for executing this step. + +**Action Status:** Set to 'stopped' or 'code_executing'. If it's 'stopped', the next action is to provide the final answer to the original question. If it's 'code_executing', the next step is to write the code. + +**Action:** Code according to your thoughts. Use this format for code: + +```python +# Write your code here +``` +""" + +# **Observation:** Check the results and effects of the executed code. + +# ... (Repeat this Question/Thoughts/Action/Observation cycle as needed) + +# **Thoughts:** I now know the final answer + +# **Action Status:** Set to 'stopped' + +# **Action:** The final answer to the original input question \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/input_template_prompt.py b/coagent/connector/configs/prompts/input_template_prompt.py similarity index 100% rename from dev_opsgpt/connector/configs/prompts/input_template_prompt.py rename to coagent/connector/configs/prompts/input_template_prompt.py diff --git a/dev_opsgpt/connector/configs/prompts/intention_template_prompt.py b/coagent/connector/configs/prompts/intention_template_prompt.py similarity index 100% rename from dev_opsgpt/connector/configs/prompts/intention_template_prompt.py rename to coagent/connector/configs/prompts/intention_template_prompt.py diff --git a/dev_opsgpt/connector/configs/prompts/metagpt_prompt.py b/coagent/connector/configs/prompts/metagpt_prompt.py similarity index 91% rename from dev_opsgpt/connector/configs/prompts/metagpt_prompt.py rename to coagent/connector/configs/prompts/metagpt_prompt.py index 39b92c4..231e5c5 100644 --- a/dev_opsgpt/connector/configs/prompts/metagpt_prompt.py +++ b/coagent/connector/configs/prompts/metagpt_prompt.py @@ -1,4 +1,4 @@ -PRD_WRITER_METAGPT_PROMPT = """#### PRD Writer Assistance Guidance +PRD_WRITER_METAGPT_PROMPT = """#### Agent Profile You are a professional Product Manager, your goal is to design a concise, usable, efficient product. According to the context, fill in the following missing information, note that each sections are returned in Python code triple quote form seperatedly. @@ -56,12 +56,12 @@ There are no unclear points.''' -DESIGN_WRITER_METAGPT_PROMPT = """#### PRD Writer Assistance Guidance +DESIGN_WRITER_METAGPT_PROMPT = """#### Agent Profile You are an architect; the goal is to design a SOTA PEP8-compliant python system; make the best use of good open source tools. Fill in the following missing information based on the context, note that all sections are response with code form separately. 8192 chars or 2048 tokens. Try to use them up. -ATTENTION: response carefully referenced "Response Format" in format. +ATTENTION: response carefully referenced "Response Output Format" in format. #### Input Format @@ -69,7 +69,7 @@ ATTENTION: response carefully referenced "Response Format" in format. **Context:** the current status and history of the tasks to determine if Origin Query has been achieved. -#### Response Format +#### Response Output Format **Implementation approach:** Provide as Plain text. Analyze the difficult points of the requirements, select the appropriate open-source framework. @@ -117,7 +117,7 @@ Provide as Plain text. Make clear here. -TASK_WRITER_METAGPT_PROMPT = """#### Task Plan Assistance Guidance +TASK_WRITER_METAGPT_PROMPT = """#### Agent Profile You are a project manager, the goal is to break down tasks according to PRD/technical design, give a task list, and analyze task dependencies to start with the prerequisite modules Based on the context, fill in the following missing information, note that all sections are returned in Python code triple quote form seperatedly. @@ -176,7 +176,7 @@ Provide as Plain text. Make clear here. For example, don't forget a main entry. """ -CODE_WRITER_METAGPT_PROMPT = """#### Code Writer Assistance Guidance +CODE_WRITER_METAGPT_PROMPT = """#### Agent Profile You are a professional engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language) @@ -204,7 +204,7 @@ ATTENTION: response carefully referenced "Response Output Format" in format **$k #### Response Output Format **Action Status:** Coding2File -**SaveFileName** construct a local file name based on Question and Context, such as +**SaveFileName:** construct a local file name based on Question and Context, such as ```python $projectname/$filename.py diff --git a/dev_opsgpt/connector/configs/prompts/planner_template_prompt.py b/coagent/connector/configs/prompts/planner_template_prompt.py similarity index 93% rename from dev_opsgpt/connector/configs/prompts/planner_template_prompt.py rename to coagent/connector/configs/prompts/planner_template_prompt.py index fa9e1bb..5564efd 100644 --- a/dev_opsgpt/connector/configs/prompts/planner_template_prompt.py +++ b/coagent/connector/configs/prompts/planner_template_prompt.py @@ -1,6 +1,6 @@ -PLANNER_TEMPLATE_PROMPT = """#### Planner Assistance Guidance +PLANNER_TEMPLATE_PROMPT = """#### Agent Profile When users need assistance with generating a sequence of achievable tasks, your role is to provide a coherent and continuous plan. Design the plan step by step, ensuring each task builds on the completion of the previous one. @@ -27,12 +27,11 @@ If it's 'planning', the PLAN is to provide a Python list[str] of achievable task """ -TOOL_PLANNER_PROMPT = """#### Tool Planner Assistance Guidance +TOOL_PLANNER_PROMPT = """#### Agent Profile Helps user to break down a process of tool usage into a series of plans. If there are no available tools, can directly answer the question. Rrespond to humans in the most helpful and accurate way possible. -You can use the following tool: {formatted_tools} #### Input Format diff --git a/dev_opsgpt/connector/configs/prompts/qa_template_prompt.py b/coagent/connector/configs/prompts/qa_template_prompt.py similarity index 74% rename from dev_opsgpt/connector/configs/prompts/qa_template_prompt.py rename to coagent/connector/configs/prompts/qa_template_prompt.py index 809a0d8..24bfe51 100644 --- a/dev_opsgpt/connector/configs/prompts/qa_template_prompt.py +++ b/coagent/connector/configs/prompts/qa_template_prompt.py @@ -1,4 +1,6 @@ -QA_TEMPLATE_PROMPT = """#### Question Answer Assistance Guidance +# Question Answer Assistance Guidance + +QA_TEMPLATE_PROMPT = """#### Agent Profile Based on the information provided, please answer the origin query concisely and professionally. Attention: Follow the input format and response output format @@ -18,7 +20,7 @@ If the answer cannot be derived from the given Context and DocInfos, please say """ -CODE_QA_PROMPT = """#### Code Answer Assistance Guidance +CODE_QA_PROMPT = """#### Agent Profile Based on the information provided, please answer the origin query concisely and professionally. Attention: Follow the input format and response output format @@ -51,4 +53,22 @@ $JSON_BLOB ``` """ -# CODE_QA_PROMPT = """【指令】根据已知信息来回答问""" \ No newline at end of file +# 基于本地代码知识问答的提示词模版 +CODE_PROMPT_TEMPLATE = """【指令】根据已知信息来回答问题。 + +【已知信息】{context} + +【问题】{question}""" + +# 代码解释模版 +CODE_INTERPERT_TEMPLATE = '''{code} + +解释一下这段代码''' +# CODE_QA_PROMPT = """【指令】根据已知信息来回答问""" + +# 基于本地知识问答的提示词模版 +ORIGIN_TEMPLATE_PROMPT = """【指令】根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,不允许在答案中添加编造成分,答案请使用中文。 + +【已知信息】{context} + +【问题】{question}""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/react_code_prompt.py b/coagent/connector/configs/prompts/react_code_prompt.py new file mode 100644 index 0000000..0aa9944 --- /dev/null +++ b/coagent/connector/configs/prompts/react_code_prompt.py @@ -0,0 +1,103 @@ + + +# REACT_CODE_PROMPT = """#### Agent Profile + +# 1. When users need help with coding, your role is to provide precise and effective guidance. +# 2. Reply follows the format of Thoughts/Action Status/Action/Observation cycle. +# 3. Provide the final answer if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. +# Each reply should contain only the guidance required for the current step either by tool usage or code. +# 4. If the Response already contains content, continue writing following the format of the Response Output Format. + +# #### Response Output Format + +# **Thoughts:** Considering the session records and executed steps, solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem, +# outline the plan for executing this step. + +# **Action Status:** Set to 'stopped' or 'code_executing'. +# If it's 'stopped', the action is to provide the final answer to the session records and executed steps. +# If it's 'code_executing', the action is to write the code. + +# **Action:** +# ```python +# # Write your code here +# ... +# ``` + +# **Observation:** Check the results and effects of the executed code. + +# ... (Repeat this "Thoughts/Action Status/Action/Observation" cycle format as needed) + +# **Thoughts:** Considering the session records and executed steps, give the final answer +# . +# **Action Status:** stopped + +# **Action:** Response the final answer to the session records. + +# """ + +REACT_CODE_PROMPT = """#### Agent Profile + +When users need help with coding, your role is to provide precise and effective guidance. + +Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step. + +#### Response Output Format + +**Thoughts:** According the previous context, solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem, +outline the plan for executing this step. + +**Action Status:** Set to 'stopped' or 'code_executing'. +If it's 'stopped', the action is to provide the final answer to the session records and executed steps. +If it's 'code_executing', the action is to write the code. + +**Action:** +```python +# Write your code here +... +``` + +**Observation:** Check the results and effects of the executed code. + +... (Repeat this "Thoughts/Action Status/Action/Observation" cycle format as needed) + +**Thoughts:** Considering the session records and executed steps, give the final answer +. +**Action Status:** stopped + +**Action:** Response the final answer to the session records. + +""" + + +# REACT_CODE_PROMPT = """#### Writing Code Assistance Guidance + +# When users need help with coding, your role is to provide precise and effective guidance. + +# Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step. + +# #### Response Process + +# **Question:** First, clarify the problem to be solved. + +# **Thoughts:** Based on the question and observations above, provide the plan for executing this step. + +# **Action Status:** Set to 'stoped' or 'code_executing'. If it's 'stoped', the action is to provide the final answer to the original question. If it's 'code_executing', the action is to write the code. + +# **Action:** +# ```python +# # Write your code here +# import os +# ... +# ``` + +# **Observation:** Check the results and effects of the executed code. + +# ... (Repeat this Thoughts/Action/Observation cycle as needed) + +# **Thoughts:** I now know the final answer + +# **Action Status:** Set to 'stoped' + +# **Action:** The final answer to the original input question + +# """ \ No newline at end of file diff --git a/coagent/connector/configs/prompts/react_template_prompt.py b/coagent/connector/configs/prompts/react_template_prompt.py new file mode 100644 index 0000000..e71b344 --- /dev/null +++ b/coagent/connector/configs/prompts/react_template_prompt.py @@ -0,0 +1,37 @@ + + +REACT_TEMPLATE_PROMPT = """#### Agent Profile + +1. When users need help with coding, your role is to provide precise and effective guidance. +2. Reply follows the format of Thoughts/Action Status/Action/Observation cycle. +3. Provide the final answer if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. +Each reply should contain only the guidance required for the current step either by tool usage or code. +4. If the Response already contains content, continue writing following the format of the Response Output Format. + +ATTENTION: Under the "Response" heading, the output format strictly adheres to the content specified in the "Response Output Format." + +#### Response Output Format + +**Question:** First, clarify the problem to be solved. + +**Thoughts:** Based on the Session Records or observations above, provide the plan for executing this step. + +**Action Status:** Set to either 'stopped' or 'code_executing'. If it's 'stopped', the next action is to provide the final answer to the original question. If it's 'code_executing', the next step is to write the code. + +**Action:** Code according to your thoughts. Use this format for code: + +```python +# Write your code here +``` + +**Observation:** Check the results and effects of the executed code. + +... (Repeat this "Thoughts/Action Status/Action/Observation" cycle format as needed) + +**Thoughts:** Considering the session records and executed steps, give the final answer. + +**Action Status:** stopped + +**Action:** Response the final answer to the session records. + +""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_tool_code_planner_prompt.py b/coagent/connector/configs/prompts/react_tool_code_planner_prompt.py similarity index 83% rename from dev_opsgpt/connector/configs/prompts/react_tool_code_planner_prompt.py rename to coagent/connector/configs/prompts/react_tool_code_planner_prompt.py index b9636a1..bb722fd 100644 --- a/dev_opsgpt/connector/configs/prompts/react_tool_code_planner_prompt.py +++ b/coagent/connector/configs/prompts/react_tool_code_planner_prompt.py @@ -1,13 +1,9 @@ -REACT_TOOL_AND_CODE_PLANNER_PROMPT = """#### Planner Assistance Guidance +REACT_TOOL_AND_CODE_PLANNER_PROMPT = """#### Agent Profile When users seek assistance in breaking down complex issues into manageable and actionable steps, your responsibility is to deliver a well-organized strategy or resolution through the use of tools or coding. ATTENTION: response carefully referenced "Response Output Format" in format. -You may use the following tools: -{formatted_tools} -Depending on the user's query, the response will either be a plan detailing the use of tools and reasoning, or a direct answer if the problem does not require breaking down. - #### Input Format **Question:** First, clarify the problem to be solved. diff --git a/coagent/connector/configs/prompts/react_tool_code_prompt.py b/coagent/connector/configs/prompts/react_tool_code_prompt.py new file mode 100644 index 0000000..462b57f --- /dev/null +++ b/coagent/connector/configs/prompts/react_tool_code_prompt.py @@ -0,0 +1,197 @@ +REACT_TOOL_AND_CODE_PROMPT = """#### Agent Profile + +When users need help with coding or using tools, your role is to provide precise and effective guidance. +Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. +Each reply should contain only the guidance required for the current step either by tool usage or code. +ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. + +#### Tool Infomation + +You can use these tools:\n{formatted_tools} + +Valid "tool_name" value:\n{tool_names} + +#### Response Output Format + +**Thoughts:** Considering the session records and executed steps, decide whether the current step requires the use of a tool or code_executing. Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. If code_executing is required, outline the plan for executing this step. + +**Action Status:** stoped, tool_using or code_executing +Use 'stopped' when the task has been completed, and no further use of tools or execution of code is necessary. +Use 'tool_using' when the current step in the process involves utilizing a tool to proceed. +Use 'code_executing' when the current step requires writing and executing code. + +**Action:** + +If Action Status is 'tool_using', format the tool action in JSON from Question and Observation, enclosed in a code block, like this: +```json +{ + "tool_name": "$TOOL_NAME", + "tool_params": "$INPUT" +} +``` + +If Action Status is 'code_executing', write the necessary code to solve the issue, enclosed in a code block, like this: +```python +Write your running code here +``` + +If Action Status is 'stopped', provide the final response or instructions in written form, enclosed in a code block, like this: +```text +The final response or instructions to the user question. +``` + +**Observation:** Check the results and effects of the executed action. + +... (Repeat this Thoughts/Action Status/Action/Observation cycle as needed) + +**Thoughts:** Conclude the final response to the user question. + +**Action Status:** stoped + +**Action:** The final answer or guidance to the user question. +""" + +# REACT_TOOL_AND_CODE_PROMPT = """#### Agent Profile + +# 1. When users need help with coding or using tools, your role is to provide precise and effective guidance. +# 2. Reply follows the format of Thoughts/Action Status/Action/Observation cycle. +# 3. Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. +# Each reply should contain only the guidance required for the current step either by tool usage or code. +# 4. If the Response already contains content, continue writing following the format of the Response Output Format. + +# ATTENTION: The "Action Status" field ensures that the tools or code mentioned in the "Action" can be parsed smoothly. Please make sure not to omit the "Action Status" field when replying. + +# #### Tool Infomation + +# You can use these tools:\n{formatted_tools} + +# Valid "tool_name" value:\n{tool_names} + +# #### Response Output Format + +# **Thoughts:** Considering the user's question, previously executed steps, and the plan, decide whether the current step requires the use of a tool or code_executing. +# Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. +# If a tool can be used, provide its name and parameters. If code_executing is required, outline the plan for executing this step. + +# **Action Status:** stoped, tool_using, or code_executing. (Choose one from these three statuses.) +# # If the task is done, set it to 'stoped'. +# # If using a tool, set it to 'tool_using'. +# # If writing code, set it to 'code_executing'. + +# **Action:** + +# If Action Status is 'tool_using', format the tool action in JSON from Question and Observation, enclosed in a code block, like this: +# ```json +# { +# "tool_name": "$TOOL_NAME", +# "tool_params": "$INPUT" +# } +# ``` + +# If Action Status is 'code_executing', write the necessary code to solve the issue, enclosed in a code block, like this: +# ```python +# Write your running code here +# ... +# ``` + +# If Action Status is 'stopped', provide the final response or instructions in written form, enclosed in a code block, like this: +# ```text +# The final response or instructions to the original input question. +# ``` + +# **Observation:** Check the results and effects of the executed action. + +# ... (Repeat this Thoughts/Action Status/Action/Observation cycle as needed) + +# **Thoughts:** Considering the user's question, previously executed steps, give the final answer. + +# **Action Status:** stopped + +# **Action:** Response the final answer to the session records. +# """ + + +# REACT_TOOL_AND_CODE_PROMPT = """#### Code and Tool Agent Assistance Guidance + +# When users need help with coding or using tools, your role is to provide precise and effective guidance. Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the guidance required for the current step either by tool usage or code. + +# #### Tool Infomation + +# You can use these tools:\n{formatted_tools} + +# Valid "tool_name" value:\n{tool_names} + +# #### Response Process + +# **Question:** Start by understanding the input question to be answered. + +# **Thoughts:** Considering the user's question, previously executed steps, and the plan, decide whether the current step requires the use of a tool or code_executing. Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. If a tool can be used, provide its name and parameters. If code_executing is required, outline the plan for executing this step. + +# **Action Status:** stoped, tool_using, or code_executing. (Choose one from these three statuses.) +# If the task is done, set it to 'stoped'. +# If using a tool, set it to 'tool_using'. +# If writing code, set it to 'code_executing'. + +# **Action:** + +# If using a tool, use the tools by formatting the tool action in JSON from Question and Observation:. The format should be: +# ```json +# {{ +# "tool_name": "$TOOL_NAME", +# "tool_params": "$INPUT" +# }} +# ``` + +# If the problem cannot be solved with a tool at the moment, then proceed to solve the issue using code. Output the following format to execute the code: + +# ```python +# Write your code here +# ``` + +# **Observation:** Check the results and effects of the executed action. + +# ... (Repeat this Thoughts/Action/Observation cycle as needed) + +# **Thoughts:** Conclude the final response to the input question. + +# **Action Status:** stoped + +# **Action:** The final answer or guidance to the original input question. +# """ + + +# REACT_TOOL_AND_CODE_PROMPT = """你是一个使用工具与代码的助手。 +# 如果现有工具不足以完成整个任务,请不要添加不存在的工具,只使用现有工具完成可能的部分。 +# 如果当前步骤不能使用工具完成,将由代码来完成。 +# 有效的"action"值为:"stopped"(已经完成用户的任务) 、 "tool_using" (使用工具来回答问题) 或 'code_executing'(结合总结下述思维链过程编写下一步的可执行代码)。 +# 尽可能地以有帮助和准确的方式回应人类,你可以使用以下工具: +# {formatted_tools} +# 如果现在的步骤可以用工具解决问题,请仅在每个$JSON_BLOB中提供一个action,如下所示: +# ``` +# {{{{ +# "action": $ACTION, +# "tool_name": $TOOL_NAME +# "tool_params": $INPUT +# }}}} +# ``` +# 若当前无法通过工具解决问题,则使用代码解决问题 +# 请仅在每个$JSON_BLOB中提供一个action,如下所示: +# ``` +# {{{{'action': $ACTION,'code_content': $CODE}}}} +# ``` + +# 按照以下思维链格式进行回应($JSON_BLOB要求符合上述规定): +# 问题:输入问题以回答 +# 思考:考虑之前和之后的步骤 +# 行动: +# ``` +# $JSON_BLOB +# ``` +# 观察:行动结果 +# ...(重复思考/行动/观察N次) +# 思考:我知道该如何回应 +# 行动: +# ``` +# $JSON_BLOB +# ``` +# """ \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_tool_prompt.py b/coagent/connector/configs/prompts/react_tool_prompt.py similarity index 60% rename from dev_opsgpt/connector/configs/prompts/react_tool_prompt.py rename to coagent/connector/configs/prompts/react_tool_prompt.py index 8581cf5..ad029c7 100644 --- a/dev_opsgpt/connector/configs/prompts/react_tool_prompt.py +++ b/coagent/connector/configs/prompts/react_tool_prompt.py @@ -1,47 +1,43 @@ -REACT_TOOL_PROMPT = """#### Tool Agent Assistance Guidance +REACT_TOOL_PROMPT = """#### Agent Profile When interacting with users, your role is to respond in a helpful and accurate manner using the tools available. Follow the steps below to ensure efficient and effective use of the tools. -Please note that all the tools you can use are listed below. You can only choose from these tools for use. If there are no suitable tools, please do not invent any tools. Just let the user know that you do not have suitable tools to use. +Please note that all the tools you can use are listed below. You can only choose from these tools for use. -#### Tool List +If there are no suitable tools, please do not invent any tools. Just let the user know that you do not have suitable tools to use. -you can use these tools:\n{formatted_tools} +ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. -valid "tool_name" value is:\n{tool_names} +#### Response Output Format -#### Response Process +**Thoughts:** According the previous observations, plan the approach for using the tool effectively. -**Question:** Start by understanding the input question to be answered. - -**Thoughts:** Based on the question and previous observations, plan the approach for using the tool effectively. - -**Action Status:** Set to either 'stoped' or 'tool_using'. If 'stoped', provide the final response to the original question. If 'tool_using', proceed with using the specified tool. +**Action Status:** Set to either 'stopped' or 'tool_using'. If 'stopped', provide the final response to the original question. If 'tool_using', proceed with using the specified tool. **Action:** Use the tools by formatting the tool action in JSON. The format should be: ```json -{{ +{ "tool_name": "$TOOL_NAME", "tool_params": "$INPUT" -}} +} ``` **Observation:** Evaluate the outcome of the tool's usage. -... (Repeat this Thoughts/Action/Observation cycle as needed) +... (Repeat this Thoughts/Action Status/Action/Observation cycle as needed) **Thoughts:** Determine the final response based on the results. -**Action Status:** Set to 'stoped' +**Action Status:** Set to 'stopped' **Action:** Conclude with the final response to the original question in this format: ```json -{{ +{ "tool_params": "Final response to be provided to the user", "tool_name": "notool", -}} +} ``` """ @@ -49,7 +45,7 @@ valid "tool_name" value is:\n{tool_names} # REACT_TOOL_PROMPT = """尽可能地以有帮助和准确的方式回应人类。您可以使用以下工具: # {formatted_tools} # 使用json blob来指定一个工具,提供一个action关键字(工具名称)和一个tool_params关键字(工具输入)。 -# 有效的"action"值为:"stoped" 或 "tool_using" (使用工具来回答问题) +# 有效的"action"值为:"stopped" 或 "tool_using" (使用工具来回答问题) # 有效的"tool_name"值为:{tool_names} # 请仅在每个$JSON_BLOB中提供一个action,如下所示: # ``` @@ -73,7 +69,7 @@ valid "tool_name" value is:\n{tool_names} # 行动: # ``` # {{{{ -# "action": "stoped", +# "action": "stopped", # "tool_name": "notool", # "tool_params": "最终返回答案给到用户" # }}}} diff --git a/dev_opsgpt/connector/configs/prompts/refine_template_prompt.py b/coagent/connector/configs/prompts/refine_template_prompt.py similarity index 93% rename from dev_opsgpt/connector/configs/prompts/refine_template_prompt.py rename to coagent/connector/configs/prompts/refine_template_prompt.py index 76cd466..5b52cad 100644 --- a/dev_opsgpt/connector/configs/prompts/refine_template_prompt.py +++ b/coagent/connector/configs/prompts/refine_template_prompt.py @@ -1,4 +1,4 @@ -REFINE_TEMPLATE_PROMPT = """#### Refiner Assistance Guidance +REFINE_TEMPLATE_PROMPT = """#### Agent Profile When users have a sequence of tasks that require optimization or adjustment based on feedback from the context, your role is to refine the existing plan. Your task is to identify where improvements can be made and provide a revised plan that is more efficient or effective. diff --git a/coagent/connector/configs/prompts/summary_template_prompt.py b/coagent/connector/configs/prompts/summary_template_prompt.py new file mode 100644 index 0000000..8f7c9aa --- /dev/null +++ b/coagent/connector/configs/prompts/summary_template_prompt.py @@ -0,0 +1,40 @@ +CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类,根据“背景信息”中的有效信息回答问题, +使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 +有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (根据背景信息回答问题)。 +在每个 $JSON_BLOB 中仅提供一个 action,如下所示: +``` +{{'action': $ACTION, 'content': '根据背景信息回答问题'}} +``` +按照以下格式进行回应: +问题:输入问题以回答 +行动: +``` +$JSON_BLOB +``` +""" + +CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类 +根据“背景信息”中的有效信息回答问题,同时展现解答的过程和内容 +若能根“背景信息”回答问题,则直接回答 +否则,总结“背景信息”的内容 +""" + + +CONV_SUMMARY_PROMPT_SPEC = """ +Your job is to summarize a history of previous messages in a conversation between an AI persona and a human. +The conversation you are given is a fixed context window and may not be complete. +Messages sent by the AI are marked with the 'assistant' role. +The AI 'assistant' can also make calls to functions, whose outputs can be seen in messages with the 'function' role. +Things the AI says in the message content are considered inner monologue and are not seen by the user. +The only AI messages seen by the user are from when the AI uses 'send_message'. +Messages the user sends are in the 'user' role. +The 'user' role is also used for important system events, such as login events and heartbeat events (heartbeats run the AI's program without user action, allowing the AI to act without prompting from the user sending them a message). +Summarize what happened in the conversation from the perspective of the AI (use the first person). +Keep your summary less than 100 words, do NOT exceed this word limit. +Only output the summary, do NOT include anything else in your output. + +--- conversation +{conversation} +--- + +""" \ No newline at end of file diff --git a/coagent/connector/memory_manager.py b/coagent/connector/memory_manager.py new file mode 100644 index 0000000..bbfceaf --- /dev/null +++ b/coagent/connector/memory_manager.py @@ -0,0 +1,430 @@ +from abc import abstractmethod, ABC +from typing import List +import os, sys, copy, json +from jieba.analyse import extract_tags +from collections import Counter +from loguru import logger + +from langchain.docstore.document import Document + + +from .schema import Memory, Message +from coagent.service.service_factory import KBServiceFactory +from coagent.llm_models import getChatModel, getChatModelFromConfig +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.embeddings.utils import load_embeddings_from_path +from coagent.utils.common_utils import save_to_json_file, read_json_file, addMinutesToTime +from coagent.connector.configs.prompts import CONV_SUMMARY_PROMPT_SPEC +from coagent.orm import table_init +# from configs.model_config import KB_ROOT_PATH, EMBEDDING_MODEL, EMBEDDING_DEVICE, SCORE_THRESHOLD +# from configs.model_config import embedding_model_dict + + +class BaseMemoryManager(ABC): + """ + This class represents a local memory manager that inherits from BaseMemoryManager. + + Attributes: + - user_name: A string representing the user name. Default is "default". + - unique_name: A string representing the unique name. Default is "default". + - memory_type: A string representing the memory type. Default is "recall". + - do_init: A boolean indicating whether to initialize. Default is False. + - current_memory: An instance of Memory class representing the current memory. + - recall_memory: An instance of Memory class representing the recall memory. + - summary_memory: An instance of Memory class representing the summary memory. + - save_message_keys: A list of strings representing the keys for saving messages. + + Methods: + - __init__: Initializes the LocalMemoryManager with the given user_name, unique_name, memory_type, and do_init. + - init_vb: Initializes the vb. + - append: Appends a message to the recall memory, current memory, and summary memory. + - extend: Extends the recall memory, current memory, and summary memory. + - save: Saves the memory to the specified directory. + - load: Loads the memory from the specified directory and returns a Memory instance. + - save_new_to_vs: Saves new messages to the vector space. + - save_to_vs: Saves the memory to the vector space. + - router_retrieval: Routes the retrieval based on the retrieval type. + - embedding_retrieval: Retrieves messages based on embedding. + - text_retrieval: Retrieves messages based on text. + - datetime_retrieval: Retrieves messages based on datetime. + - recursive_summary: Performs recursive summarization of messages. + """ + + def __init__( + self, + user_name: str = "default", + unique_name: str = "default", + memory_type: str = "recall", + do_init: bool = False, + ): + """ + Initializes the LocalMemoryManager with the given parameters. + + Args: + - user_name: A string representing the user name. Default is "default". + - unique_name: A string representing the unique name. Default is "default". + - memory_type: A string representing the memory type. Default is "recall". + - do_init: A boolean indicating whether to initialize. Default is False. + """ + self.user_name = user_name + self.unique_name = unique_name + self.memory_type = memory_type + self.do_init = do_init + self.current_memory = Memory(messages=[]) + self.recall_memory = Memory(messages=[]) + self.summary_memory = Memory(messages=[]) + self.save_message_keys = [ + 'chat_index', 'role_name', 'role_type', 'role_prompt', 'input_query', 'origin_query', + 'datetime', 'role_content', 'step_content', 'parsed_output', 'spec_parsed_output', 'parsed_output_list', + 'task', 'db_docs', 'code_docs', 'search_docs', 'phase_name', 'chain_name', 'customed_kargs'] + self.init_vb() + + def init_vb(self): + """ + Initializes the vb. + """ + pass + + def append(self, message: Message): + """ + Appends a message to the recall memory, current memory, and summary memory. + + Args: + - message: An instance of Message class representing the message to be appended. + """ + pass + + def extend(self, memory: Memory): + """ + Extends the recall memory, current memory, and summary memory. + + Args: + - memory: An instance of Memory class representing the memory to be extended. + """ + pass + + def save(self, save_dir: str = ""): + """ + Saves the memory to the specified directory. + + Args: + - save_dir: A string representing the directory to save the memory. Default is KB_ROOT_PATH. + """ + pass + + def load(self, load_dir: str = "") -> Memory: + """ + Loads the memory from the specified directory and returns a Memory instance. + + Args: + - load_dir: A string representing the directory to load the memory from. Default is KB_ROOT_PATH. + + Returns: + - An instance of Memory class representing the loaded memory. + """ + pass + + def save_new_to_vs(self, messages: List[Message]): + """ + Saves new messages to the vector space. + + Args: + - messages: A list of Message instances representing the messages to be saved. + - embed_model: A string representing the embedding model. Default is EMBEDDING_MODEL. + - embed_device: A string representing the embedding device. Default is EMBEDDING_DEVICE. + """ + pass + + def save_to_vs(self, embed_model="", embed_device=""): + """ + Saves the memory to the vector space. + + Args: + - embed_model: A string representing the embedding model. Default is EMBEDDING_MODEL. + - embed_device: A string representing the embedding device. Default is EMBEDDING_DEVICE. + """ + pass + + def router_retrieval(self, text: str=None, datetime: str = None, n=5, top_k=5, retrieval_type: str = "embedding", **kwargs) -> List[Message]: + """ + Routes the retrieval based on the retrieval type. + + Args: + - text: A string representing the text for retrieval. Default is None. + - datetime: A string representing the datetime for retrieval. Default is None. + - n: An integer representing the number of messages. Default is 5. + - top_k: An integer representing the top k messages. Default is 5. + - retrieval_type: A string representing the retrieval type. Default is "embedding". + - **kwargs: Additional keyword arguments for retrieval. + + Returns: + - A list of Message instances representing the retrieved messages. + """ + pass + + def embedding_retrieval(self, text: str, embed_model="", top_k=1, score_threshold=1.0, **kwargs) -> List[Message]: + """ + Retrieves messages based on embedding. + + Args: + - text: A string representing the text for retrieval. + - embed_model: A string representing the embedding model. Default is EMBEDDING_MODEL. + - top_k: An integer representing the top k messages. Default is 1. + - score_threshold: A float representing the score threshold. Default is SCORE_THRESHOLD. + - **kwargs: Additional keyword arguments for retrieval. + + Returns: + - A list of Message instances representing the retrieved messages. + """ + pass + + def text_retrieval(self, text: str, **kwargs) -> List[Message]: + """ + Retrieves messages based on text. + + Args: + - text: A string representing the text for retrieval. + - **kwargs: Additional keyword arguments for retrieval. + + Returns: + - A list of Message instances representing the retrieved messages. + """ + pass + + def datetime_retrieval(self, datetime: str, text: str = None, n: int = 5, **kwargs) -> List[Message]: + """ + Retrieves messages based on datetime. + + Args: + - datetime: A string representing the datetime for retrieval. + - text: A string representing the text for retrieval. Default is None. + - n: An integer representing the number of messages. Default is 5. + - **kwargs: Additional keyword arguments for retrieval. + + Returns: + - A list of Message instances representing the retrieved messages. + """ + pass + + def recursive_summary(self, messages: List[Message], split_n: int = 20) -> List[Message]: + """ + Performs recursive summarization of messages. + + Args: + - messages: A list of Message instances representing the messages to be summarized. + - split_n: An integer representing the split n. Default is 20. + + Returns: + - A list of Message instances representing the summarized messages. + """ + pass + + +class LocalMemoryManager(BaseMemoryManager): + + def __init__( + self, + embed_config: EmbedConfig, + llm_config: LLMConfig, + user_name: str = "default", + unique_name: str = "default", + memory_type: str = "recall", + do_init: bool = False, + kb_root_path: str = "", + ): + self.user_name = user_name + self.unique_name = unique_name + self.memory_type = memory_type + self.do_init = do_init + self.kb_root_path = kb_root_path + self.embed_config: EmbedConfig = embed_config + self.llm_config: LLMConfig = llm_config + self.current_memory = Memory(messages=[]) + self.recall_memory = Memory(messages=[]) + self.summary_memory = Memory(messages=[]) + self.save_message_keys = [ + 'chat_index', 'role_name', 'role_type', 'role_prompt', 'input_query', 'origin_query', + 'datetime', 'role_content', 'step_content', 'parsed_output', 'spec_parsed_output', 'parsed_output_list', + 'task', 'db_docs', 'code_docs', 'search_docs', 'phase_name', 'chain_name', 'customed_kargs'] + self.init_vb() + + def init_vb(self): + vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" + # default to recreate a new vb + table_init() + vb = KBServiceFactory.get_service_by_name(vb_name, self.embed_config, self.kb_root_path) + if vb: + status = vb.clear_vs() + + if not self.do_init: + self.load(self.kb_root_path) + self.save_to_vs() + + def append(self, message: Message): + self.recall_memory.append(message) + # + if message.role_type == "summary": + self.summary_memory.append(message) + else: + self.current_memory.append(message) + + self.save(self.kb_root_path) + self.save_new_to_vs([message]) + + def extend(self, memory: Memory): + self.recall_memory.extend(memory) + self.current_memory.extend(self.recall_memory.filter_by_role_type(["summary"])) + self.summary_memory.extend(self.recall_memory.select_by_role_type(["summary"])) + self.save(self.kb_root_path) + self.save_new_to_vs(memory.messages) + + def save(self, save_dir: str = "./"): + file_path = os.path.join(save_dir, f"{self.user_name}/{self.unique_name}/{self.memory_type}/converation.jsonl") + memory_messages = self.recall_memory.dict() + memory_messages = {k: [ + {kkk: vvv for kkk, vvv in vv.items() if kkk in self.save_message_keys} + for vv in v ] + for k, v in memory_messages.items() + } + # + save_to_json_file(memory_messages, file_path) + + def load(self, load_dir: str = "./") -> Memory: + file_path = os.path.join(load_dir, f"{self.user_name}/{self.unique_name}/{self.memory_type}/converation.jsonl") + + if os.path.exists(file_path): + self.recall_memory = Memory(**read_json_file(file_path)) + self.current_memory = Memory(messages=self.recall_memory.filter_by_role_type(["summary"])) + self.summary_memory = Memory(messages=self.recall_memory.select_by_role_type(["summary"])) + + def save_new_to_vs(self, messages: List[Message]): + if self.embed_config: + vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" + # default to faiss, todo: add new vstype + vb = KBServiceFactory.get_service(vb_name, "faiss", self.embed_config, self.kb_root_path) + embeddings = load_embeddings_from_path(self.embed_config.embed_model_path, self.embed_config.model_device,) + messages = [ + {k: v for k, v in m.dict().items() if k in self.save_message_keys} + for m in messages] + docs = [{"page_content": m["step_content"] or m["role_content"] or m["input_query"] or m["origin_query"], "metadata": m} for m in messages] + docs = [Document(**doc) for doc in docs] + vb.do_add_doc(docs, embeddings) + + def save_to_vs(self): + vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" + # default to recreate a new vb + vb = KBServiceFactory.get_service_by_name(vb_name, self.embed_config, self.kb_root_path) + if vb: + status = vb.clear_vs() + # create_kb(vb_name, "faiss", embed_model) + + # default to faiss, todo: add new vstype + vb = KBServiceFactory.get_service(vb_name, "faiss", self.embed_config, self.kb_root_path) + embeddings = load_embeddings_from_path(self.embed_config.embed_model_path, self.embed_config.model_device,) + messages = self.recall_memory.dict() + messages = [ + {kkk: vvv for kkk, vvv in vv.items() if kkk in self.save_message_keys} + for k, v in messages.items() for vv in v] + docs = [{"page_content": m["step_content"] or m["role_content"] or m["input_query"] or m["origin_query"], "metadata": m} for m in messages] + docs = [Document(**doc) for doc in docs] + vb.do_add_doc(docs, embeddings) + + # def load_from_vs(self, embed_model=EMBEDDING_MODEL) -> Memory: + # vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" + + # create_kb(vb_name, "faiss", embed_model) + # # default to faiss, todo: add new vstype + # vb = KBServiceFactory.get_service(vb_name, "faiss", embed_model) + # docs = vb.get_all_documents() + # print(docs) + + def router_retrieval(self, text: str=None, datetime: str = None, n=5, top_k=5, retrieval_type: str = "embedding", **kwargs) -> List[Message]: + retrieval_func_dict = { + "embedding": self.embedding_retrieval, "text": self.text_retrieval, "datetime": self.datetime_retrieval + } + + # 确保提供了合法的检索类型 + if retrieval_type not in retrieval_func_dict: + raise ValueError(f"Invalid retrieval_type: '{retrieval_type}'. Available types: {list(retrieval_func_dict.keys())}") + + retrieval_func = retrieval_func_dict[retrieval_type] + # + params = locals() + params.pop("self") + params.pop("retrieval_type") + params.update(params.pop('kwargs', {})) + # + return retrieval_func(**params) + + def embedding_retrieval(self, text: str, top_k=1, score_threshold=1.0, **kwargs) -> List[Message]: + if text is None: return [] + vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" + vb = KBServiceFactory.get_service(vb_name, "faiss", self.embed_config, self.kb_root_path) + docs = vb.search_docs(text, top_k=top_k, score_threshold=score_threshold) + return [Message(**doc.metadata) for doc, score in docs] + + def text_retrieval(self, text: str, **kwargs) -> List[Message]: + if text is None: return [] + return self._text_retrieval_from_cache(self.recall_memory.messages, text, score_threshold=0.3, topK=5, **kwargs) + + def datetime_retrieval(self, datetime: str, text: str = None, n: int = 5, **kwargs) -> List[Message]: + if datetime is None: return [] + return self._datetime_retrieval_from_cache(self.recall_memory.messages, datetime, text, n, **kwargs) + + def _text_retrieval_from_cache(self, messages: List[Message], text: str = None, score_threshold=0.3, topK=5, tag_topK=5, **kwargs) -> List[Message]: + keywords = extract_tags(text, topK=tag_topK) + + matched_messages = [] + for message in messages: + message_keywords = extract_tags(message.step_content or message.role_content or message.input_query, topK=tag_topK) + # calculate jaccard similarity + intersection = Counter(keywords) & Counter(message_keywords) + union = Counter(keywords) | Counter(message_keywords) + similarity = sum(intersection.values()) / sum(union.values()) + if similarity >= score_threshold: + matched_messages.append((message, similarity)) + matched_messages = sorted(matched_messages, key=lambda x:x[1]) + return [m for m, s in matched_messages][:topK] + + def _datetime_retrieval_from_cache(self, messages: List[Message], datetime: str, text: str = None, n: int = 5, **kwargs) -> List[Message]: + # select message by datetime + datetime_before, datetime_after = addMinutesToTime(datetime, n) + select_messages = [ + message for message in messages + if datetime_before<=message.datetime<=datetime_after + ] + return self._text_retrieval_from_cache(select_messages, text) + + def recursive_summary(self, messages: List[Message], split_n: int = 20) -> List[Message]: + + if len(messages) == 0: + return messages + + newest_messages = messages[-split_n:] + summary_messages = messages[:len(messages)-split_n] + + while (len(newest_messages) != 0) and (newest_messages[0].role_type != "user"): + message = newest_messages.pop(0) + summary_messages.append(message) + + # summary + # model = getChatModel(temperature=0.2) + model = getChatModelFromConfig(self.llm_config) + summary_content = '\n\n'.join([ + m.role_type + "\n" + "\n".join(([f"*{k}* {v}" for parsed_output in m.parsed_output_list for k, v in parsed_output.items() if k not in ['Action Status']])) + for m in summary_messages if m.role_type not in ["summary"] + ]) + + summary_prompt = CONV_SUMMARY_PROMPT_SPEC.format(conversation=summary_content) + content = model.predict(summary_prompt) + summary_message = Message( + role_name="summaryer", + role_type="summary", + role_content=content, + step_content=content, + parsed_output_list=[], + customed_kargs={} + ) + summary_message.parsed_output_list.append({"summary": content}) + newest_messages.insert(0, summary_message) + return newest_messages \ No newline at end of file diff --git a/coagent/connector/message_process.py b/coagent/connector/message_process.py new file mode 100644 index 0000000..a97b2cb --- /dev/null +++ b/coagent/connector/message_process.py @@ -0,0 +1,267 @@ +import re, traceback, uuid, copy, json, os +from loguru import logger + + +# from configs.server_config import SANDBOX_SERVER +# from configs.model_config import JUPYTER_WORK_PATH +from coagent.connector.schema import ( + Memory, Role, Message, ActionStatus, CodeDoc, Doc, LogVerboseEnum +) +from coagent.connector.memory_manager import BaseMemoryManager +from coagent.tools import DDGSTool, DocRetrieval, CodeRetrieval +from coagent.sandbox import PyCodeBox, CodeBoxResponse +from coagent.llm_models.llm_config import LLMConfig, EmbedConfig +from .utils import parse_dict_to_dict, parse_text_to_dict + + +class MessageUtils: + def __init__( + self, + role: Role = None, + sandbox_server: dict = {}, + jupyter_work_path: str = "./", + embed_config: EmbedConfig = None, + llm_config: LLMConfig = None, + kb_root_path: str = "", + log_verbose: str = "0" + ) -> None: + self.role = role + self.sandbox_server = sandbox_server + self.jupyter_work_path = jupyter_work_path + self.embed_config = embed_config + self.llm_config = llm_config + self.kb_root_path = kb_root_path + self.codebox = PyCodeBox( + remote_url=self.sandbox_server.get("url", "http://127.0.0.1:5050"), + remote_ip=self.sandbox_server.get("host", "http://127.0.0.1"), + remote_port=self.sandbox_server.get("port", "5050"), + jupyter_work_path=jupyter_work_path, + token="mytoken", + do_code_exe=True, + do_remote=self.sandbox_server.get("do_remote", False), + do_check_net=False + ) + self.log_verbose = os.environ.get("log_verbose", "0") or log_verbose + + def inherit_extrainfo(self, input_message: Message, output_message: Message): + output_message.db_docs = input_message.db_docs + output_message.search_docs = input_message.search_docs + output_message.code_docs = input_message.code_docs + output_message.figures.update(input_message.figures) + output_message.origin_query = input_message.origin_query + output_message.code_engine_name = input_message.code_engine_name + + output_message.doc_engine_name = input_message.doc_engine_name + output_message.search_engine_name = input_message.search_engine_name + output_message.top_k = input_message.top_k + output_message.score_threshold = input_message.score_threshold + output_message.cb_search_type = input_message.cb_search_type + output_message.do_doc_retrieval = input_message.do_doc_retrieval + output_message.do_code_retrieval = input_message.do_code_retrieval + output_message.do_tool_retrieval = input_message.do_tool_retrieval + # + output_message.tools = input_message.tools + output_message.agents = input_message.agents + + # update customed_kargs, if exist, keep; else add + customed_kargs = copy.deepcopy(input_message.customed_kargs) + customed_kargs.update(output_message.customed_kargs) + output_message.customed_kargs = customed_kargs + return output_message + + def inherit_baseparam(self, input_message: Message, output_message: Message): + # 只更新参数 + output_message.doc_engine_name = input_message.doc_engine_name + output_message.search_engine_name = input_message.search_engine_name + output_message.top_k = input_message.top_k + output_message.score_threshold = input_message.score_threshold + output_message.cb_search_type = input_message.cb_search_type + output_message.do_doc_retrieval = input_message.do_doc_retrieval + output_message.do_code_retrieval = input_message.do_code_retrieval + output_message.do_tool_retrieval = input_message.do_tool_retrieval + # + output_message.tools = input_message.tools + output_message.agents = input_message.agents + # 存在bug导致相同key被覆盖 + output_message.customed_kargs.update(input_message.customed_kargs) + return output_message + + def get_extrainfo_step(self, message: Message, do_search, do_doc_retrieval, do_code_retrieval, do_tool_retrieval) -> Message: + '''''' + if do_search: + message = self.get_search_retrieval(message) + + if do_doc_retrieval: + message = self.get_doc_retrieval(message) + + if do_code_retrieval: + message = self.get_code_retrieval(message) + + if do_tool_retrieval: + message = self.get_tool_retrieval(message) + + return message + + def get_search_retrieval(self, message: Message,) -> Message: + SEARCH_ENGINES = {"duckduckgo": DDGSTool} + search_docs = [] + for idx, doc in enumerate(SEARCH_ENGINES["duckduckgo"].run(message.role_content, 3)): + doc.update({"index": idx}) + search_docs.append(Doc(**doc)) + message.search_docs = search_docs + return message + + def get_doc_retrieval(self, message: Message) -> Message: + query = message.role_content + knowledge_basename = message.doc_engine_name + top_k = message.top_k + score_threshold = message.score_threshold + if knowledge_basename: + docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold, self.embed_config, self.kb_root_path) + message.db_docs = [Doc(**doc) for doc in docs] + return message + + def get_code_retrieval(self, message: Message) -> Message: + query = message.input_query + code_engine_name = message.code_engine_name + history_node_list = message.history_node_list + code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list, search_type=message.cb_search_type, + llm_config=self.llm_config, embed_config=self.embed_config,) + message.code_docs = [CodeDoc(**doc) for doc in code_docs] + return message + + def get_tool_retrieval(self, message: Message) -> Message: + return message + + def step_router(self, message: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> tuple[Message, ...]: + '''''' + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"message.action_status: {message.action_status}") + + observation_message = None + if message.action_status == ActionStatus.CODE_EXECUTING: + message, observation_message = self.code_step(message) + elif message.action_status == ActionStatus.TOOL_USING: + message, observation_message = self.tool_step(message) + elif message.action_status == ActionStatus.CODING2FILE: + self.save_code2file(message, self.jupyter_work_path) + elif message.action_status == ActionStatus.CODE_RETRIEVAL: + pass + elif message.action_status == ActionStatus.CODING: + pass + + return message, observation_message + + def code_step(self, message: Message) -> Message: + '''execute code''' + # logger.debug(f"message.role_content: {message.role_content}, message.code_content: {message.code_content}") + code_answer = self.codebox.chat('```python\n{}```'.format(message.code_content)) + code_prompt = f"The return error after executing the above code is {code_answer.code_exe_response},need to recover.\n" \ + if code_answer.code_exe_type == "error" else f"The return information after executing the above code is {code_answer.code_exe_response}.\n" + + observation_message = Message( + role_name="observation", + role_type="function", #self.role.role_type, + role_content="", + step_content="", + input_query=message.code_content, + ) + uid = str(uuid.uuid1()) + if code_answer.code_exe_type == "image/png": + message.figures[uid] = code_answer.code_exe_response + message.code_answer = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" + message.observation = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" + message.step_content += f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" + # message.role_content += f"\n**Observation:**:执行上述代码后生成一张图片, 图片名为{uid}\n" + observation_message.role_content = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" + observation_message.parsed_output = {"Observation": f"The return figure name is {uid} after executing the above code.\n"} + else: + message.code_answer = code_answer.code_exe_response + message.observation = code_answer.code_exe_response + message.step_content += f"\n**Observation:**: {code_prompt}\n" + # message.role_content += f"\n**Observation:**: {code_prompt}\n" + observation_message.role_content = f"\n**Observation:**: {code_prompt}\n" + observation_message.parsed_output = {"Observation": f"{code_prompt}\n"} + + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"**Observation:** {message.action_status}, {message.observation}") + return message, observation_message + + def tool_step(self, message: Message) -> Message: + '''execute tool''' + observation_message = Message( + role_name="observation", + role_type="function", #self.role.role_type, + role_content="\n**Observation:** there is no tool can execute\n", + step_content="", + input_query=str(message.tool_params), + tools=message.tools, + ) + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"message: {message.action_status}, {message.tool_params}") + + tool_names = [tool.name for tool in message.tools] + if message.tool_name not in tool_names: + message.tool_answer = "\n**Observation:** there is no tool can execute.\n" + message.observation = "\n**Observation:** there is no tool can execute.\n" + # message.role_content += f"\n**Observation:**: 不存在可以执行的tool\n" + message.step_content += f"\n**Observation:** there is no tool can execute.\n" + observation_message.role_content = f"\n**Observation:** there is no tool can execute.\n" + observation_message.parsed_output = {"Observation": "there is no tool can execute.\n"} + + # logger.debug(message.tool_params) + for tool in message.tools: + if tool.name == message.tool_params.get("tool_name", ""): + tool_res = tool.func(**message.tool_params.get("tool_params", {})) + message.tool_answer = tool_res + message.observation = tool_res + # message.role_content += f"\n**Observation:**: {tool_res}\n" + message.step_content += f"\n**Observation:** {tool_res}.\n" + observation_message.role_content = f"\n**Observation:** {tool_res}.\n" + observation_message.parsed_output = {"Observation": f"{tool_res}.\n"} + break + + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"**Observation:** {message.action_status}, {message.observation}") + return message, observation_message + + def parser(self, message: Message) -> Message: + '''''' + content = message.role_content + # parse start + parsed_dict = parse_text_to_dict(content) + spec_parsed_dict = parse_dict_to_dict(parsed_dict) + # select parse value + action_value = parsed_dict.get('Action Status') + if action_value: + action_value = action_value.lower() + + code_content_value = spec_parsed_dict.get('code') + if action_value == 'tool_using': + tool_params_value = spec_parsed_dict.get('json') + else: + tool_params_value = None + + # add parse value to message + message.action_status = action_value or "default" + message.code_content = code_content_value + message.tool_params = tool_params_value + message.parsed_output = parsed_dict + message.spec_parsed_output = spec_parsed_dict + return message + + def save_code2file(self, message: Message, project_dir="./"): + filename = message.parsed_output.get("SaveFileName") + code = message.spec_parsed_output.get("code") + + for k, v in {">": ">", "≥": ">=", "<": "<", "≤": "<="}.items(): + code = code.replace(k, v) + + file_path = os.path.join(project_dir, filename) + + if not os.path.exists(file_path): + os.makedirs(os.path.dirname(file_path), exist_ok=True) + + with open(file_path, "w") as f: + f.write(code) + \ No newline at end of file diff --git a/dev_opsgpt/connector/phase/__init__.py b/coagent/connector/phase/__init__.py similarity index 100% rename from dev_opsgpt/connector/phase/__init__.py rename to coagent/connector/phase/__init__.py diff --git a/coagent/connector/phase/base_phase.py b/coagent/connector/phase/base_phase.py new file mode 100644 index 0000000..14bc66b --- /dev/null +++ b/coagent/connector/phase/base_phase.py @@ -0,0 +1,255 @@ +from typing import List, Union, Dict, Tuple +import os +import json +import importlib +import copy +from loguru import logger + +from coagent.connector.agents import BaseAgent +from coagent.connector.chains import BaseChain +from coagent.connector.schema import ( + Memory, Task, Message, AgentConfig, ChainConfig, PhaseConfig, LogVerboseEnum, + CompletePhaseConfig, + load_chain_configs, load_phase_configs, load_role_configs +) +from coagent.connector.memory_manager import BaseMemoryManager, LocalMemoryManager +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +from coagent.connector.message_process import MessageUtils +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH + +# from configs.model_config import JUPYTER_WORK_PATH, KB_ROOT_PATH +# from configs.server_config import SANDBOX_SERVER + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + + +CUR_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class BasePhase: + + def __init__( + self, + phase_name: str, + phase_config: CompletePhaseConfig = None, + kb_root_path: str = KB_ROOT_PATH, + jupyter_work_path: str = JUPYTER_WORK_PATH, + sandbox_server: dict = {}, + embed_config: EmbedConfig = EmbedConfig(), + llm_config: LLMConfig = LLMConfig(), + task: Task = None, + base_phase_config: Union[dict, str] = PHASE_CONFIGS, + base_chain_config: Union[dict, str] = CHAIN_CONFIGS, + base_role_config: Union[dict, str] = AGETN_CONFIGS, + log_verbose: str = "0" + ) -> None: + # + self.phase_name = phase_name + self.do_summary = False + self.do_search = False + self.do_code_retrieval = False + self.do_doc_retrieval = False + self.do_tool_retrieval = False + # memory_pool dont have specific order + # self.memory_pool = Memory(messages=[]) + self.embed_config = embed_config + self.llm_config = llm_config + self.sandbox_server = sandbox_server + self.jupyter_work_path = jupyter_work_path + self.kb_root_path = kb_root_path + self.log_verbose = max(os.environ.get("log_verbose", "0"), log_verbose) + + self.message_utils = MessageUtils(None, sandbox_server, jupyter_work_path, embed_config, llm_config, kb_root_path, log_verbose) + self.global_memory = Memory(messages=[]) + self.phase_memory: List[Memory] = [] + # according phase name to init the phase contains + self.chains: List[BaseChain] = self.init_chains( + phase_name, + phase_config, + task=task, + memory=None, + base_phase_config = base_phase_config, + base_chain_config = base_chain_config, + base_role_config = base_role_config, + ) + self.memory_manager: BaseMemoryManager = LocalMemoryManager( + unique_name=phase_name, do_init=True, kb_root_path = kb_root_path, embed_config=embed_config, llm_config=llm_config + ) + self.conv_summary_agent = BaseAgent( + role=role_configs["conv_summary"].role, + prompt_config=role_configs["conv_summary"].prompt_config, + task = None, memory = None, + llm_config=self.llm_config, + embed_config=self.embed_config, + sandbox_server=sandbox_server, + jupyter_work_path=jupyter_work_path, + kb_root_path=kb_root_path + ) + + def astep(self, query: Message, history: Memory = None) -> Tuple[Message, Memory]: + self.memory_manager.append(query) + summary_message = None + chain_message = Memory(messages=[]) + local_phase_memory = Memory(messages=[]) + # do_search、do_doc_search、do_code_search + query = self.message_utils.get_extrainfo_step(query, self.do_search, self.do_doc_retrieval, self.do_code_retrieval, self.do_tool_retrieval) + query.parsed_output = query.parsed_output if query.parsed_output else {"origin_query": query.input_query} + query.parsed_output_list = query.parsed_output_list if query.parsed_output_list else [{"origin_query": query.input_query}] + input_message = copy.deepcopy(query) + + self.global_memory.append(input_message) + local_phase_memory.append(input_message) + for chain in self.chains: + # chain can supply background and query to next chain + for output_message, local_chain_memory in chain.astep(input_message, history, background=chain_message, memory_manager=self.memory_manager): + # logger.debug(f"local_memory: {local_phase_memory + local_chain_memory}") + yield output_message, local_phase_memory + local_chain_memory + + output_message = self.message_utils.inherit_extrainfo(input_message, output_message) + input_message = output_message + # logger.info(f"{chain.chainConfig.chain_name} phase_step: {output_message.role_content}") + # 这一段也有问题 + self.global_memory.extend(local_chain_memory) + local_phase_memory.extend(local_chain_memory) + + # whether to use summary_llm + if self.do_summary: + if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): + logger.info(f"{self.conv_summary_agent.role.role_name} input global memory: {local_phase_memory.to_str_messages(content_key='step_content')}") + for summary_message in self.conv_summary_agent.astep(query, background=local_phase_memory, memory_manager=self.memory_manager): + pass + # summary_message = Message(**summary_message) + summary_message.role_name = chain.chainConfig.chain_name + summary_message = self.conv_summary_agent.message_utils.parser(summary_message) + summary_message = self.message_utils.inherit_extrainfo(output_message, summary_message) + chain_message.append(summary_message) + + message = summary_message or output_message + yield message, local_phase_memory + + # 由于不会存在多轮chain执行,所以直接保留memory即可 + for chain in self.chains: + self.phase_memory.append(chain.global_memory) + # TODO:local_memory缺少添加summary的过程 + message = summary_message or output_message + message.role_name = self.phase_name + yield message, local_phase_memory + + def step(self, query: Message, history: Memory = None) -> Tuple[Message, Memory]: + for message, local_phase_memory in self.astep(query, history=history): + pass + return message, local_phase_memory + + def pre_print(self, query, history: Memory = None) -> List[str]: + chain_message = Memory(messages=[]) + for chain in self.chains: + chain.pre_print(query, history, background=chain_message, memory_manager=self.memory_manager) + + def init_chains(self, phase_name: str, phase_config: CompletePhaseConfig, base_phase_config, base_chain_config, + base_role_config, task=None, memory=None) -> List[BaseChain]: + # load config + role_configs = load_role_configs(base_role_config) + chain_configs = load_chain_configs(base_chain_config) + phase_configs = load_phase_configs(base_phase_config) + + chains = [] + self.chain_module = importlib.import_module("coagent.connector.chains") + self.agent_module = importlib.import_module("coagent.connector.agents") + + phase: PhaseConfig = phase_configs.get(phase_name) + # set phase + self.do_summary = phase.do_summary + self.do_search = phase.do_search + self.do_code_retrieval = phase.do_code_retrieval + self.do_doc_retrieval = phase.do_doc_retrieval + self.do_tool_retrieval = phase.do_tool_retrieval + logger.info(f"start to init the phase, the phase_name is {phase_name}, it contains these chains such as {phase.chains}") + + for chain_name in phase.chains: + # logger.debug(f"{chain_configs.keys()}") + chain_config: ChainConfig = chain_configs[chain_name] + logger.info(f"start to init the chain, the chain_name is {chain_name}, it contains these agents such as {chain_config.agents}") + + agents = [] + for agent_name in chain_config.agents: + agent_config: AgentConfig = role_configs[agent_name] + llm_config = copy.deepcopy(self.llm_config) + llm_config.stop = agent_config.stop + baseAgent: BaseAgent = getattr(self.agent_module, agent_config.role.agent_type) + base_agent = baseAgent( + role=agent_config.role, + prompt_config = agent_config.prompt_config, + prompt_manager_type=agent_config.prompt_manager_type, + task = task, + memory = memory, + chat_turn=agent_config.chat_turn, + focus_agents=agent_config.focus_agents, + focus_message_keys=agent_config.focus_message_keys, + llm_config=llm_config, + embed_config=self.embed_config, + sandbox_server=self.sandbox_server, + jupyter_work_path=self.jupyter_work_path, + kb_root_path=self.kb_root_path, + log_verbose=self.log_verbose + ) + if agent_config.role.agent_type == "SelectorAgent": + for group_agent_name in agent_config.group_agents: + group_agent_config = role_configs[group_agent_name] + llm_config = copy.deepcopy(self.llm_config) + llm_config.stop = group_agent_config.stop + baseAgent: BaseAgent = getattr(self.agent_module, group_agent_config.role.agent_type) + group_base_agent = baseAgent( + role=group_agent_config.role, + prompt_config = group_agent_config.prompt_config, + prompt_manager_type=agent_config.prompt_manager_type, + task = task, + memory = memory, + chat_turn=group_agent_config.chat_turn, + focus_agents=group_agent_config.focus_agents, + focus_message_keys=group_agent_config.focus_message_keys, + llm_config=llm_config, + embed_config=self.embed_config, + sandbox_server=self.sandbox_server, + jupyter_work_path=self.jupyter_work_path, + kb_root_path=self.kb_root_path, + log_verbose=self.log_verbose + ) + base_agent.group_agents.append(group_base_agent) + + agents.append(base_agent) + + chain_instance = BaseChain( + agents, chain_config.chat_turn, + do_checker=chain_configs[chain_name].do_checker, + jupyter_work_path=self.jupyter_work_path, + sandbox_server=self.sandbox_server, + embed_config=self.embed_config, + llm_config=self.llm_config, + kb_root_path=self.kb_root_path, + log_verbose=self.log_verbose + ) + chains.append(chain_instance) + + return chains + + def update(self) -> Memory: + pass + + def get_memory(self, ) -> Memory: + return Memory.from_memory_list( + [chain.get_memory() for chain in self.chains] + ) + + def get_memory_str(self, do_all_memory=True, content_key="role_content") -> str: + memory = self.global_memory if do_all_memory else self.phase_memory + return "\n".join([": ".join(i) for i in memory.to_tuple_messages(content_key=content_key)]) + + def get_chains_memory(self, content_key="role_content") -> List[Tuple]: + return [memory.to_tuple_messages(content_key=content_key) for memory in self.phase_memory] + + def get_chains_memory_str(self, content_key="role_content") -> str: + return "************".join([f"{chain.chainConfig.chain_name}\n" + chain.get_memory_str(content_key=content_key) for chain in self.chains]) \ No newline at end of file diff --git a/coagent/connector/prompt_manager.py b/coagent/connector/prompt_manager.py new file mode 100644 index 0000000..192b12b --- /dev/null +++ b/coagent/connector/prompt_manager.py @@ -0,0 +1,350 @@ +from coagent.connector.schema import Memory, Message +import random +from textwrap import dedent +import copy +from loguru import logger + +from coagent.connector.utils import extract_section, parse_section + + +class PromptManager: + def __init__(self, role_prompt="", prompt_config=None, monitored_agents=[], monitored_fields=[]): + self.role_prompt = role_prompt + self.monitored_agents = monitored_agents + self.monitored_fields = monitored_fields + self.field_handlers = {} + self.context_handlers = {} + self.field_order = [] # 用于普通字段的顺序 + self.context_order = [] # 单独维护上下文字段的顺序 + self.field_descriptions = {} + self.omit_if_empty_flags = {} + self.context_title = "### Context Data\n\n" + + self.prompt_config = prompt_config + if self.prompt_config: + self.register_fields_from_config() + + def register_field(self, field_name, function=None, title=None, description=None, is_context=True, omit_if_empty=True): + """ + 注册一个新的字段及其处理函数。 + Args: + field_name (str): 字段名称。 + function (callable): 处理字段数据的函数。 + title (str, optional): 字段的自定义标题(可选)。 + description (str, optional): 字段的描述(可选,可以是几句话)。 + is_context (bool, optional): 指示该字段是否为上下文字段。 + omit_if_empty (bool, optional): 如果数据为空,是否省略该字段。 + """ + if not function: + function = self.handle_custom_data + + # Register the handler function based on context flag + if is_context: + self.context_handlers[field_name] = function + else: + self.field_handlers[field_name] = function + + # Store the custom title if provided and adjust the title prefix based on context + title_prefix = "####" if is_context else "###" + if title is not None: + self.field_descriptions[field_name] = f"{title_prefix} {title}\n\n" + elif description is not None: + # If title is not provided but description is, use description as title + self.field_descriptions[field_name] = f"{title_prefix} {field_name.replace('_', ' ').title()}\n\n{description}\n\n" + else: + # If neither title nor description is provided, use the field name as title + self.field_descriptions[field_name] = f"{title_prefix} {field_name.replace('_', ' ').title()}\n\n" + + # Store the omit_if_empty flag for this field + self.omit_if_empty_flags[field_name] = omit_if_empty + + if is_context and field_name != 'context_placeholder': + self.context_handlers[field_name] = function + self.context_order.append(field_name) + else: + self.field_handlers[field_name] = function + self.field_order.append(field_name) + + def generate_full_prompt(self, **kwargs): + full_prompt = [] + context_prompts = [] # 用于收集上下文内容 + is_pre_print = kwargs.get("is_pre_print", False) # 用于强制打印所有prompt 字段信息,不管有没有空 + + # 先处理上下文字段 + for field_name in self.context_order: + handler = self.context_handlers[field_name] + processed_prompt = handler(**kwargs) + # Check if the field should be omitted when empty + if self.omit_if_empty_flags.get(field_name, False) and not processed_prompt and not is_pre_print: + continue # Skip this field + title_or_description = self.field_descriptions.get(field_name, f"#### {field_name.replace('_', ' ').title()}\n\n") + context_prompts.append(title_or_description + processed_prompt + '\n\n') + + # 处理普通字段,同时查找 context_placeholder 的位置 + for field_name in self.field_order: + if field_name == 'context_placeholder': + # 在 context_placeholder 的位置插入上下文数据 + full_prompt.append(self.context_title) # 添加上下文部分的大标题 + full_prompt.extend(context_prompts) # 添加收集的上下文内容 + else: + handler = self.field_handlers[field_name] + processed_prompt = handler(**kwargs) + # Check if the field should be omitted when empty + if self.omit_if_empty_flags.get(field_name, False) and not processed_prompt and not is_pre_print: + continue # Skip this field + title_or_description = self.field_descriptions.get(field_name, f"### {field_name.replace('_', ' ').title()}\n\n") + full_prompt.append(title_or_description + processed_prompt + '\n\n') + + # 返回完整的提示,移除尾部的空行 + return ''.join(full_prompt).rstrip('\n') + + def pre_print(self, **kwargs): + kwargs.update({"is_pre_print": True}) + prompt = self.generate_full_prompt(**kwargs) + + input_keys = parse_section(self.role_prompt, 'Response Output Format') + llm_predict = "\n".join([f"**{k}:**" for k in input_keys]) + return prompt + "\n\n" + "#"*19 + "\n<<<>>>\n" + "#"*19 + f"\n\n{llm_predict}\n" + + def handle_custom_data(self, **kwargs): + return "" + + def handle_tool_data(self, **kwargs): + if 'previous_agent_message' not in kwargs: + return "" + + previous_agent_message = kwargs.get('previous_agent_message') + tools = previous_agent_message.tools + + if not tools: + return "" + + tool_strings = [] + for tool in tools: + args_schema = str(tool.args) + tool_strings.append(f"{tool.name}: {tool.description}, args: {args_schema}") + formatted_tools = "\n".join(tool_strings) + + tool_names = ", ".join([tool.name for tool in tools]) + + tool_prompt = dedent(f""" +Below is a list of tools that are available for your use: +{formatted_tools} + +valid "tool_name" value is: +{tool_names} +""") + + return tool_prompt + + def handle_agent_data(self, **kwargs): + if 'agents' not in kwargs: + return "" + + agents = kwargs.get('agents') + random.shuffle(agents) + agent_names = ", ".join([f'{agent.role.role_name}' for agent in agents]) + agent_descs = [] + for agent in agents: + role_desc = agent.role.role_prompt.split("####")[1] + while "\n\n" in role_desc: + role_desc = role_desc.replace("\n\n", "\n") + role_desc = role_desc.replace("\n", ",") + + agent_descs.append(f'"role name: {agent.role.role_name}\nrole description: {role_desc}"') + + agents = "\n".join(agent_descs) + agent_prompt = f''' + Please ensure your selection is one of the listed roles. Available roles for selection: + {agents} + Please ensure select the Role from agent names, such as {agent_names}''' + + return dedent(agent_prompt) + + def handle_doc_info(self, **kwargs) -> str: + if 'previous_agent_message' not in kwargs: + return "" + previous_agent_message: Message = kwargs.get('previous_agent_message') + db_docs = previous_agent_message.db_docs + search_docs = previous_agent_message.search_docs + code_cocs = previous_agent_message.code_docs + doc_infos = "\n".join([doc.get_snippet() for doc in db_docs] + [doc.get_snippet() for doc in search_docs] + + [doc.get_code() for doc in code_cocs]) + return doc_infos + + def handle_session_records(self, **kwargs) -> str: + + memory_pool: Memory = kwargs.get('memory_pool', Memory(messages=[])) + memory_pool = self.select_memory_by_agent_name(memory_pool) + memory_pool = self.select_memory_by_parsed_key(memory_pool) + + return memory_pool.to_str_messages(content_key="parsed_output_list", with_tag=True) + + def handle_current_plan(self, **kwargs) -> str: + if 'previous_agent_message' not in kwargs: + return "" + previous_agent_message = kwargs['previous_agent_message'] + return previous_agent_message.parsed_output.get("CURRENT_STEP", "") + + def handle_agent_profile(self, **kwargs) -> str: + return extract_section(self.role_prompt, 'Agent Profile') + + def handle_output_format(self, **kwargs) -> str: + return extract_section(self.role_prompt, 'Response Output Format') + + def handle_response(self, **kwargs) -> str: + if 'react_memory' not in kwargs: + return "" + + react_memory = kwargs.get('react_memory', Memory(messages=[])) + if react_memory is None: + return "" + + return "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()]) + + def handle_task_records(self, **kwargs) -> str: + if 'task_memory' not in kwargs: + return "" + + task_memory: Memory = kwargs.get('task_memory', Memory(messages=[])) + if task_memory is None: + return "" + + return "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items() if k not in ["CURRENT_STEP"]]) for _dict in task_memory.get_parserd_output()]) + + def handle_previous_message(self, message: Message) -> str: + pass + + def handle_message_by_role_name(self, message: Message) -> str: + pass + + def handle_message_by_role_type(self, message: Message) -> str: + pass + + def handle_current_agent_react_message(self, message: Message) -> str: + pass + + def extract_codedoc_info_for_prompt(self, message: Message) -> str: + code_docs = message.code_docs + doc_infos = "\n".join([doc.get_code() for doc in code_docs]) + return doc_infos + + def select_memory_by_parsed_key(self, memory: Memory) -> Memory: + return Memory( + messages=[self.select_message_by_parsed_key(message) for message in memory.messages + if self.select_message_by_parsed_key(message) is not None] + ) + + def select_memory_by_agent_name(self, memory: Memory) -> Memory: + return Memory( + messages=[self.select_message_by_agent_name(message) for message in memory.messages + if self.select_message_by_agent_name(message) is not None] + ) + + def select_message_by_agent_name(self, message: Message) -> Message: + # assume we focus all agents + if self.monitored_agents == []: + return message + return None if message is None or message.role_name not in self.monitored_agents else self.select_message_by_parsed_key(message) + + def select_message_by_parsed_key(self, message: Message) -> Message: + # assume we focus all key contents + if message is None: + return message + + if self.monitored_fields == []: + return message + + message_c = copy.deepcopy(message) + message_c.parsed_output = {k: v for k,v in message_c.parsed_output.items() if k in self.monitored_fields} + message_c.parsed_output_list = [{k: v for k,v in parsed_output.items() if k in self.monitored_fields} for parsed_output in message_c.parsed_output_list] + return message_c + + def get_memory(self, content_key="role_content"): + return self.memory.to_tuple_messages(content_key="step_content") + + def get_memory_str(self, content_key="role_content"): + return "\n".join([": ".join(i) for i in self.memory.to_tuple_messages(content_key="step_content")]) + + def register_fields_from_config(self): + + for prompt_field in self.prompt_config: + + function_name = prompt_field.function_name + # 检查function_name是否是self的一个方法 + if function_name and hasattr(self, function_name): + function = getattr(self, function_name) + else: + function = self.handle_custom_data + + self.register_field(prompt_field.field_name, + function=function, + title=prompt_field.title, + description=prompt_field.description, + is_context=prompt_field.is_context, + omit_if_empty=prompt_field.omit_if_empty) + + def register_standard_fields(self): + self.register_field('agent_profile', function=self.handle_agent_profile, is_context=False) + self.register_field('tool_information', function=self.handle_tool_data, is_context=False) + self.register_field('context_placeholder', is_context=True) # 用于标记上下文数据部分的位置 + self.register_field('reference_documents', function=self.handle_doc_info, is_context=True) + self.register_field('session_records', function=self.handle_session_records, is_context=True) + self.register_field('output_format', function=self.handle_output_format, title='Response Output Format', is_context=False) + self.register_field('response', function=self.handle_response, is_context=False, omit_if_empty=False) + + def register_executor_fields(self): + self.register_field('agent_profile', function=self.handle_agent_profile, is_context=False) + self.register_field('tool_information', function=self.handle_tool_data, is_context=False) + self.register_field('context_placeholder', is_context=True) # 用于标记上下文数据部分的位置 + self.register_field('reference_documents', function=self.handle_doc_info, is_context=True) + self.register_field('session_records', function=self.handle_session_records, is_context=True) + self.register_field('current_plan', function=self.handle_current_plan, is_context=True) + self.register_field('output_format', function=self.handle_output_format, title='Response Output Format', is_context=False) + self.register_field('response', function=self.handle_response, is_context=False, omit_if_empty=False) + + def register_fields_from_dict(self, fields_dict): + # 使用字典注册字段的函数 + for field_name, field_config in fields_dict.items(): + function_name = field_config.get('function', None) + title = field_config.get('title', None) + description = field_config.get('description', None) + is_context = field_config.get('is_context', True) + omit_if_empty = field_config.get('omit_if_empty', True) + + # 检查function_name是否是self的一个方法 + if function_name and hasattr(self, function_name): + function = getattr(self, function_name) + else: + function = self.handle_custom_data + + # 调用已存在的register_field方法注册字段 + self.register_field(field_name, function=function, title=title, description=description, is_context=is_context, omit_if_empty=omit_if_empty) + + + +def main(): + manager = PromptManager() + manager.register_standard_fields() + + manager.register_field('agents_work_progress', title=f"Agents' Work Progress", is_context=True) + + # 创建数据字典 + data_dict = { + "agent_profile": "这是代理配置文件...", + # "tool_list": "这是工具列表...", + "reference_documents": "这是参考文档...", + "session_records": "这是会话记录...", + "agents_work_progress": "这是代理工作进展...", + "output_format": "这是预期的输出格式...", + # "response": "这是生成或继续回应的指令...", + "response": "", + "test": 'xxxxx' + } + + # 组合完整的提示 + full_prompt = manager.generate_full_prompt(data_dict) + print(full_prompt) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dev_opsgpt/connector/schema/__init__.py b/coagent/connector/schema/__init__.py similarity index 74% rename from dev_opsgpt/connector/schema/__init__.py rename to coagent/connector/schema/__init__.py index f858055..8f90bd7 100644 --- a/dev_opsgpt/connector/schema/__init__.py +++ b/coagent/connector/schema/__init__.py @@ -3,7 +3,7 @@ from .general_schema import * from .message import Message __all__ = [ - "Memory", "ActionStatus", "Doc", "CodeDoc", "Task", + "Memory", "ActionStatus", "Doc", "CodeDoc", "Task", "LogVerboseEnum", "Env", "Role", "ChainConfig", "AgentConfig", "PhaseConfig", "Message", "load_role_configs", "load_chain_configs", "load_phase_configs" ] \ No newline at end of file diff --git a/dev_opsgpt/connector/schema/general_schema.py b/coagent/connector/schema/general_schema.py similarity index 72% rename from dev_opsgpt/connector/schema/general_schema.py rename to coagent/connector/schema/general_schema.py index 0f8c6f1..9da753c 100644 --- a/dev_opsgpt/connector/schema/general_schema.py +++ b/coagent/connector/schema/general_schema.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -from typing import List, Dict +from typing import List, Dict, Optional, Union from enum import Enum import re import json @@ -11,7 +11,7 @@ class ActionStatus(Enum): DEFAUILT = "default" FINISHED = "finished" - STOPED = "stoped" + STOPPED = "stopped" CONTINUED = "continued" TOOL_USING = "tool_using" @@ -38,8 +38,8 @@ class FinishedAction(Action): action_name: str = ActionStatus.FINISHED description: str = "provide the final answer to the original query to break the chain answer" -class StopedAction(Action): - action_name: str = ActionStatus.STOPED +class StoppedAction(Action): + action_name: str = ActionStatus.STOPPED description: str = "provide the final answer to the original query to break the agent answer" class ContinuedAction(Action): @@ -86,6 +86,7 @@ class RoleTypeEnums(Enum): ASSISTANT = "assistant" FUNCTION = "function" OBSERVATION = "observation" + SUMMARY = "summary" def __eq__(self, other): if isinstance(other, str): @@ -118,7 +119,7 @@ class PromptKeyEnums(Enum): def __eq__(self, other): if isinstance(other, str): return self.value == other - return super().__eq__(other) + return super().__eq__(other) class Doc(BaseModel): @@ -167,15 +168,43 @@ class CodeDoc(BaseModel): return f"""出处 [{self.index + 1}] \n\n来源 ({self.related_nodes}) \n\n内容 {self.code}\n\n""" +class LogVerboseEnum(Enum): + Log0Level = "0" # don't print log + Log1Level = "1" # print level-1 log + Log2Level = "2" # print level-2 log + Log3Level = "3" # print level-3 log + + def __eq__(self, other): + if isinstance(other, str): + return self.value.lower() == other.lower() + if isinstance(other, LogVerboseEnum): + return self.value == other.value + return False + + def __ge__(self, other): + if isinstance(other, LogVerboseEnum): + return int(self.value) >= int(other.value) + if isinstance(other, str): + return int(self.value) >= int(other) + return NotImplemented + + def __le__(self, other): + if isinstance(other, LogVerboseEnum): + return int(self.value) <= int(other.value) + if isinstance(other, str): + return int(self.value) <= int(other) + return NotImplemented + + @classmethod + def ge(self, enum_value: 'LogVerboseEnum', other: Union[str, 'LogVerboseEnum']): + return enum_value <= other + + class Task(BaseModel): task_type: str task_name: str task_desc: str task_prompt: str - # def __init__(self, task_type, task_name, task_desc) -> None: - # self.task_type = task_type - # self.task_name = task_name - # self.task_desc = task_desc class Env(BaseModel): env_type: str @@ -192,30 +221,32 @@ class Role(BaseModel): template_prompt: str = "" - class ChainConfig(BaseModel): chain_name: str chain_type: str agents: List[str] do_checker: bool = False chat_turn: int = 1 - clear_structure: bool = False - brainstorming: bool = False - gui_design: bool = True - git_management: bool = False - self_improve: bool = False + + +class PromptField(BaseModel): + field_name: str # 假设这是一个函数类型,您可以根据需要更改 + function_name: str + title: Optional[str] = None + description: Optional[str] = None + is_context: Optional[bool] = True + omit_if_empty: Optional[bool] = True class AgentConfig(BaseModel): role: Role - stop: str = None + prompt_config: List[PromptField] + prompt_manager_type: str = "PromptManager" chat_turn: int = 1 - do_search: bool = False - do_doc_retrieval: bool = False - do_tool_retrieval: bool = False focus_agents: List = [] focus_message_keys: List = [] group_agents: List = [] + stop: str = "" class PhaseConfig(BaseModel): @@ -229,13 +260,32 @@ class PhaseConfig(BaseModel): do_tool_retrieval: bool = False +class CompleteChainConfig(BaseModel): + chain_name: str + chain_type: str + agents: Dict[str, AgentConfig] + do_checker: bool = False + chat_turn: int = 1 + + +class CompletePhaseConfig(BaseModel): + phase_name: str + phase_type: str + chains: Dict[str, CompleteChainConfig] + do_summary: bool = False + do_search: bool = False + do_doc_retrieval: bool = False + do_code_retrieval: bool = False + do_tool_retrieval: bool = False + + def load_role_configs(config) -> Dict[str, AgentConfig]: if isinstance(config, str): with open(config, 'r', encoding="utf8") as file: configs = json.load(file) else: configs = config - + # logger.debug(configs) return {name: AgentConfig(**v) for name, v in configs.items()} @@ -254,4 +304,6 @@ def load_phase_configs(config) -> Dict[str, PhaseConfig]: configs = json.load(file) else: configs = config - return {name: PhaseConfig(**v) for name, v in configs.items()} \ No newline at end of file + return {name: PhaseConfig(**v) for name, v in configs.items()} + +# AgentConfig.update_forward_refs() \ No newline at end of file diff --git a/coagent/connector/schema/memory.py b/coagent/connector/schema/memory.py new file mode 100644 index 0000000..bba4c43 --- /dev/null +++ b/coagent/connector/schema/memory.py @@ -0,0 +1,158 @@ +from pydantic import BaseModel +from typing import List, Union, Dict +from loguru import logger + +from .message import Message +from coagent.utils.common_utils import ( + save_to_jsonl_file, save_to_json_file, read_json_file, read_jsonl_file +) + + +class Memory(BaseModel): + messages: List[Message] = [] + + # def __init__(self, messages: List[Message] = []): + # self.messages = messages + + def append(self, message: Message): + self.messages.append(message) + + def extend(self, memory: 'Memory'): + self.messages.extend(memory.messages) + + def update(self, role_name: str, role_type: str, role_content: str): + self.messages.append(Message(role_name, role_type, role_content, role_content)) + + def clear(self, ): + self.messages = [] + + def delete(self, ): + pass + + def get_messages(self, k=0) -> List[Message]: + """Return the most recent k memories, return all when k=0""" + return self.messages[-k:] + + def split_by_role_type(self) -> List[Dict[str, 'Memory']]: + """ + Split messages into rounds of conversation based on role_type. + Each round consists of consecutive messages of the same role_type. + User messages form a single round, while assistant and function messages are combined into a single round. + Each round is represented by a dict with 'role' and 'memory' keys, with assistant and function messages + labeled as 'assistant'. + """ + rounds = [] + current_memory = Memory() + current_role = None + + logger.debug(len(self.messages)) + + for msg in self.messages: + # Determine the message's role, considering 'function' as 'assistant' + message_role = 'assistant' if msg.role_type in ['assistant', 'function'] else 'user' + + # If the current memory is empty or the current message is of the same role_type as current_role, add to current memory + if not current_memory.messages or current_role == message_role: + current_memory.append(msg) + else: + # Finish the current memory and start a new one + rounds.append({'role': current_role, 'memory': current_memory}) + current_memory = Memory() + current_memory.append(msg) + + # Update the current_role, considering 'function' as 'assistant' + current_role = message_role + + # Don't forget to add the last memory if it exists + if current_memory.messages: + rounds.append({'role': current_role, 'memory': current_memory}) + + logger.debug(rounds) + + return rounds + + def format_rounds_to_html(self) -> str: + formatted_html_str = "" + rounds = self.split_by_role_type() + + for round in rounds: + role = round['role'] + memory = round['memory'] + + # 转换当前round的Memory为字符串 + messages_str = memory.to_str_messages() + + # 根据角色类型添加相应的HTML标签 + if role == 'user': + formatted_html_str += f"\n{messages_str}\n\n" + else: # 对于'assistant'和'function'角色,我们将其视为'assistant' + formatted_html_str += f"\n{messages_str}\n\n" + + return formatted_html_str + + + def filter_by_role_type(self, role_types: List[str]) -> List[Message]: + # Filter messages based on role types + return [message for message in self.messages if message.role_type not in role_types] + + def select_by_role_type(self, role_types: List[str]) -> List[Message]: + # Select messages based on role types + return [message for message in self.messages if message.role_type in role_types] + + def to_tuple_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): + # Convert messages to tuples based on parameters + # logger.debug(f"{[message.to_tuple_message(return_all, content_key) for message in self.messages ]}") + return [ + message.to_tuple_message(return_all, content_key) for message in self.messages + if message.role_name not in filter_roles + ] + + def to_dict_messages(self, filter_roles=[]): + # Convert messages to dictionaries based on filter roles + return [ + message.to_dict_message() for message in self.messages + if message.role_name not in filter_roles + ] + + def to_str_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[], with_tag=False): + # Convert messages to strings based on parameters + # for message in self.messages: + # logger.debug(f"{message.role_name}: {message.to_str_content(return_all, content_key, with_tag=with_tag)}") + # logger.debug(f"{[message.to_tuple_message(return_all, content_key) for message in self.messages ]}") + return "\n\n".join([message.to_str_content(return_all, content_key, with_tag=with_tag) for message in self.messages + if message.role_name not in filter_roles + ]) + + def get_parserd_output(self, ): + return [message.parsed_output for message in self.messages] + + def get_parserd_output_list(self, ): + # for message in self.messages: + # logger.debug(f"{message.role_name}: {message.parsed_output_list}") + # return [parsed_output for message in self.messages for parsed_output in message.parsed_output_list[1:]] + return [parsed_output for message in self.messages for parsed_output in message.parsed_output_list] + + def get_rolenames(self, ): + '''''' + return [message.role_name for message in self.messages] + + @classmethod + def from_memory_list(cls, memorys: List['Memory']) -> 'Memory': + return cls(messages=[message for memory in memorys for message in memory.get_messages()]) + + def __len__(self, ): + return len(self.messages) + + def __str__(self) -> str: + return "\n".join([":".join(i) for i in self.to_tuple_messages()]) + + def __add__(self, other: Union[Message, 'Memory']) -> 'Memory': + if isinstance(other, Message): + return Memory(messages=self.messages + [other]) + elif isinstance(other, Memory): + return Memory(messages=self.messages + other.messages) + else: + raise ValueError(f"cant add unspecified type like as {type(other)}") + + + \ No newline at end of file diff --git a/dev_opsgpt/connector/schema/message.py b/coagent/connector/schema/message.py similarity index 58% rename from dev_opsgpt/connector/schema/message.py rename to coagent/connector/schema/message.py index baa807d..c306ba5 100644 --- a/dev_opsgpt/connector/schema/message.py +++ b/coagent/connector/schema/message.py @@ -1,6 +1,7 @@ -from pydantic import BaseModel +from pydantic import BaseModel, root_validator from loguru import logger +from coagent.utils.common_utils import getCurrentDatetime from .general_schema import * @@ -11,6 +12,7 @@ class Message(BaseModel): role_prompt: str = None input_query: str = None origin_query: str = None + datetime: str = getCurrentDatetime() # llm output role_content: str = None @@ -27,7 +29,7 @@ class Message(BaseModel): parsed_output_list: List[Dict] = [] # llm\tool\code executre information - action_status: str = ActionStatus.DEFAUILT + action_status: str = "default" agent_index: int = None code_answer: str = None tool_answer: str = None @@ -59,6 +61,27 @@ class Message(BaseModel): # user's customed kargs for init or end action customed_kargs: dict = {} + + @root_validator(pre=True) + def check_card_number_omitted(cls, values): + input_query = values.get("input_query") + origin_query = values.get("origin_query") + role_content = values.get("role_content") + if input_query is None: + values["input_query"] = origin_query or role_content + if role_content is None: + values["role_content"] = origin_query + return values + + # pydantic>=2.0 + # @model_validator(mode='after') + # def check_passwords_match(self) -> 'Message': + # if self.input_query is None: + # self.input_query = self.origin_query or self.role_content + # if self.role_content is None: + # self.role_content = self.origin_query + # return self + def to_tuple_message(self, return_all: bool = True, content_key="role_content"): role_content = self.to_str_content(False, content_key) if return_all: @@ -66,29 +89,28 @@ class Message(BaseModel): else: return (role_content) - def to_dict_message(self, return_all: bool = True, content_key="role_content"): - role_content = self.to_str_content(False, content_key) - if return_all: - return {"role": self.role_name, "content": role_content} - else: - return vars(self) + def to_dict_message(self, ): + return vars(self) - def to_str_content(self, return_all: bool = True, content_key="role_content"): + def to_str_content(self, return_all: bool = True, content_key="role_content", with_tag=False): if content_key == "role_content": role_content = self.role_content or self.input_query elif content_key == "step_content": role_content = self.step_content or self.role_content or self.input_query + elif content_key == "parsed_output": + role_content = "\n".join([f"**{k}:** {v}" for k, v in self.parsed_output.items()]) + elif content_key == "parsed_output_list": + role_content = "\n".join([f"**{k}:** {v}" for po in self.parsed_output_list for k,v in po.items()]) else: role_content = self.role_content or self.input_query - if return_all: - return f"{self.role_name}: {role_content}" + if with_tag: + start_tag = f"<{self.role_type}-{self.role_name}-message>" + end_tag = f"" + return f"{start_tag}\n{role_content}\n{end_tag}" else: return role_content - def is_system_role(self,): - return self.role_type == "system" - def __str__(self) -> str: # key_str = '\n'.join([k for k, v in vars(self).items()]) # logger.debug(f"{key_str}") diff --git a/coagent/connector/utils.py b/coagent/connector/utils.py new file mode 100644 index 0000000..d3dfb61 --- /dev/null +++ b/coagent/connector/utils.py @@ -0,0 +1,117 @@ +import re, copy, json +from loguru import logger + + +def extract_section(text, section_name): + # Define a pattern to extract the named section along with its content + section_pattern = rf'#### {section_name}\n(.*?)(?=####|$)' + + # Find the specific section content + section_content = re.search(section_pattern, text, re.DOTALL) + + if section_content: + # If the section is found, extract the content and strip the leading/trailing whitespace + # This will also remove leading/trailing newlines + content = section_content.group(1).strip() + + # Return the cleaned content + return content + else: + # If the section is not found, return an empty string + return "" + + +def parse_section(text, section_name): + # Define a pattern to extract the named section along with its content + section_pattern = rf'#### {section_name}\n(.*?)(?=####|$)' + + # Find the specific section content + section_content = re.search(section_pattern, text, re.DOTALL) + + if section_content: + # If the section is found, extract the content + content = section_content.group(1) + + # Define a pattern to find segments that follow the format **xx:** + segments_pattern = r'\*\*([^*]+):\*\*' + + # Use findall method to extract all matches in the section content + segments = re.findall(segments_pattern, content) + + return segments + else: + # If the section is not found, return an empty list + return [] + + +def parse_text_to_dict(text): + # Define a regular expression pattern to capture the key and value + main_pattern = r"\*\*(.+?):\*\*\s*(.*?)\s*(?=\*\*|$)" + list_pattern = r'```python\n(.*?)```' + plan_pattern = r'\[\s*.*?\s*\]' + + # Use re.findall to find all main matches in the text + main_matches = re.findall(main_pattern, text, re.DOTALL) + + # Convert main matches to a dictionary + parsed_dict = {key.strip(): value.strip() for key, value in main_matches} + + for k, v in parsed_dict.items(): + for pattern in [list_pattern, plan_pattern]: + if "PLAN" != k: continue + v = v.replace("```list", "```python") + match_value = re.search(pattern, v, re.DOTALL) + if match_value: + # Add the code block to the dictionary + parsed_dict[k] = eval(match_value.group(1).strip()) + break + + return parsed_dict + + +def parse_dict_to_dict(parsed_dict) -> dict: + code_pattern = r'```python\n(.*?)```' + tool_pattern = r'```json\n(.*?)```' + + pattern_dict = {"code": code_pattern, "json": tool_pattern} + spec_parsed_dict = copy.deepcopy(parsed_dict) + for key, pattern in pattern_dict.items(): + for k, text in parsed_dict.items(): + # Search for the code block + if not isinstance(text, str): continue + _match = re.search(pattern, text, re.DOTALL) + if _match: + # Add the code block to the dictionary + try: + spec_parsed_dict[key] = json.loads(_match.group(1).strip()) + except: + spec_parsed_dict[key] = _match.group(1).strip() + break + return spec_parsed_dict + + +def prompt_cost(model_type: str, num_prompt_tokens: float, num_completion_tokens: float): + input_cost_map = { + "gpt-3.5-turbo": 0.0015, + "gpt-3.5-turbo-16k": 0.003, + "gpt-3.5-turbo-0613": 0.0015, + "gpt-3.5-turbo-16k-0613": 0.003, + "gpt-4": 0.03, + "gpt-4-0613": 0.03, + "gpt-4-32k": 0.06, + } + + output_cost_map = { + "gpt-3.5-turbo": 0.002, + "gpt-3.5-turbo-16k": 0.004, + "gpt-3.5-turbo-0613": 0.002, + "gpt-3.5-turbo-16k-0613": 0.004, + "gpt-4": 0.06, + "gpt-4-0613": 0.06, + "gpt-4-32k": 0.12, + } + + if model_type not in input_cost_map or model_type not in output_cost_map: + return -1 + + return num_prompt_tokens * input_cost_map[model_type] / 1000.0 + num_completion_tokens * output_cost_map[model_type] / 1000.0 diff --git a/dev_opsgpt/db_handler/__init__.py b/coagent/db_handler/__init__.py similarity index 100% rename from dev_opsgpt/db_handler/__init__.py rename to coagent/db_handler/__init__.py diff --git a/dev_opsgpt/db_handler/graph_db_handler/__init__.py b/coagent/db_handler/graph_db_handler/__init__.py similarity index 100% rename from dev_opsgpt/db_handler/graph_db_handler/__init__.py rename to coagent/db_handler/graph_db_handler/__init__.py diff --git a/dev_opsgpt/db_handler/graph_db_handler/nebula_handler.py b/coagent/db_handler/graph_db_handler/nebula_handler.py similarity index 99% rename from dev_opsgpt/db_handler/graph_db_handler/nebula_handler.py rename to coagent/db_handler/graph_db_handler/nebula_handler.py index ac3f539..e5f4cde 100644 --- a/dev_opsgpt/db_handler/graph_db_handler/nebula_handler.py +++ b/coagent/db_handler/graph_db_handler/nebula_handler.py @@ -224,6 +224,7 @@ class NebulaHandler: res = {'vertices': -1, 'edges': -1} stats_res_dict = self.result_to_dict(stats_res) + logger.info(stats_res_dict) for idx in range(len(stats_res_dict['Type'])): t = stats_res_dict['Type'][idx].as_string() name = stats_res_dict['Name'][idx].as_string() @@ -264,7 +265,3 @@ class NebulaHandler: - - - - diff --git a/dev_opsgpt/db_handler/vector_db_handler/__init__.py b/coagent/db_handler/vector_db_handler/__init__.py similarity index 100% rename from dev_opsgpt/db_handler/vector_db_handler/__init__.py rename to coagent/db_handler/vector_db_handler/__init__.py diff --git a/dev_opsgpt/db_handler/vector_db_handler/chroma_handler.py b/coagent/db_handler/vector_db_handler/chroma_handler.py similarity index 100% rename from dev_opsgpt/db_handler/vector_db_handler/chroma_handler.py rename to coagent/db_handler/vector_db_handler/chroma_handler.py diff --git a/dev_opsgpt/document_loaders/__init__.py b/coagent/document_loaders/__init__.py similarity index 100% rename from dev_opsgpt/document_loaders/__init__.py rename to coagent/document_loaders/__init__.py diff --git a/dev_opsgpt/document_loaders/json_loader.py b/coagent/document_loaders/json_loader.py similarity index 94% rename from dev_opsgpt/document_loaders/json_loader.py rename to coagent/document_loaders/json_loader.py index e931f60..4e5ecd3 100644 --- a/dev_opsgpt/document_loaders/json_loader.py +++ b/coagent/document_loaders/json_loader.py @@ -6,7 +6,7 @@ from langchain.docstore.document import Document from langchain.document_loaders.base import BaseLoader from langchain.text_splitter import RecursiveCharacterTextSplitter, TextSplitter -from dev_opsgpt.utils.common_utils import read_json_file +from coagent.utils.common_utils import read_json_file class JSONLoader(BaseLoader): diff --git a/dev_opsgpt/document_loaders/jsonl_loader.py b/coagent/document_loaders/jsonl_loader.py similarity index 94% rename from dev_opsgpt/document_loaders/jsonl_loader.py rename to coagent/document_loaders/jsonl_loader.py index 5a50d36..a56e6eb 100644 --- a/dev_opsgpt/document_loaders/jsonl_loader.py +++ b/coagent/document_loaders/jsonl_loader.py @@ -6,7 +6,7 @@ from langchain.docstore.document import Document from langchain.document_loaders.base import BaseLoader from langchain.text_splitter import RecursiveCharacterTextSplitter, TextSplitter -from dev_opsgpt.utils.common_utils import read_jsonl_file +from coagent.utils.common_utils import read_jsonl_file class JSONLLoader(BaseLoader): diff --git a/dev_opsgpt/service/llm_api.py.bak b/coagent/embeddings/__init__.py similarity index 100% rename from dev_opsgpt/service/llm_api.py.bak rename to coagent/embeddings/__init__.py diff --git a/dev_opsgpt/text_splitter/utils.py b/coagent/embeddings/commands/__init__.py similarity index 100% rename from dev_opsgpt/text_splitter/utils.py rename to coagent/embeddings/commands/__init__.py diff --git a/dev_opsgpt/embeddings/commands/default_vs_cds.py b/coagent/embeddings/commands/default_vs_cds.py similarity index 100% rename from dev_opsgpt/embeddings/commands/default_vs_cds.py rename to coagent/embeddings/commands/default_vs_cds.py diff --git a/dev_opsgpt/embeddings/faiss_m.py b/coagent/embeddings/faiss_m.py similarity index 97% rename from dev_opsgpt/embeddings/faiss_m.py rename to coagent/embeddings/faiss_m.py index 505f845..17a910d 100644 --- a/dev_opsgpt/embeddings/faiss_m.py +++ b/coagent/embeddings/faiss_m.py @@ -602,7 +602,6 @@ class FAISS(VectorStore): faiss = FAISS.from_texts(texts, embeddings) """ from loguru import logger - logger.debug(f"texts: {len(texts)}") embeddings = embedding.embed_documents(texts) return cls.__from( texts, diff --git a/dev_opsgpt/embeddings/get_embedding.py b/coagent/embeddings/get_embedding.py similarity index 91% rename from dev_opsgpt/embeddings/get_embedding.py rename to coagent/embeddings/get_embedding.py index b7b8cdc..3ea45ab 100644 --- a/dev_opsgpt/embeddings/get_embedding.py +++ b/coagent/embeddings/get_embedding.py @@ -7,12 +7,17 @@ ''' from loguru import logger -from configs.model_config import EMBEDDING_MODEL -from dev_opsgpt.embeddings.openai_embedding import OpenAIEmbedding -from dev_opsgpt.embeddings.huggingface_embedding import HFEmbedding +# from configs.model_config import EMBEDDING_MODEL +from coagent.embeddings.openai_embedding import OpenAIEmbedding +from coagent.embeddings.huggingface_embedding import HFEmbedding -def get_embedding(engine: str, text_list: list): +def get_embedding( + engine: str, + text_list: list, + model_path: str = "text2vec-base-chinese", + embedding_device: str = "cpu", + ): ''' get embedding @param engine: openai / hf @@ -25,7 +30,7 @@ def get_embedding(engine: str, text_list: list): oae = OpenAIEmbedding() emb_res = oae.get_emb(text_list) elif engine == 'model': - hfe = HFEmbedding(EMBEDDING_MODEL) + hfe = HFEmbedding(model_path, embedding_device) emb_res = hfe.get_emb(text_list) return emb_res diff --git a/dev_opsgpt/embeddings/huggingface_embedding.py b/coagent/embeddings/huggingface_embedding.py similarity index 93% rename from dev_opsgpt/embeddings/huggingface_embedding.py rename to coagent/embeddings/huggingface_embedding.py index 1175d83..b103753 100644 --- a/dev_opsgpt/embeddings/huggingface_embedding.py +++ b/coagent/embeddings/huggingface_embedding.py @@ -6,8 +6,9 @@ @desc: ''' from loguru import logger -from configs.model_config import EMBEDDING_DEVICE -from dev_opsgpt.embeddings.utils import load_embeddings +# from configs.model_config import EMBEDDING_DEVICE +# from configs.model_config import embedding_model_dict +from coagent.embeddings.utils import load_embeddings, load_embeddings_from_path class HFEmbedding: @@ -22,8 +23,8 @@ class HFEmbedding: cls._instance[instance_key] = super().__new__(cls) return cls._instance[instance_key] - def __init__(self, model_name): - self.model = load_embeddings(model=model_name, device=EMBEDDING_DEVICE) + def __init__(self, model_name, embedding_device): + self.model = load_embeddings_from_path(model_path=model_name, device=embedding_device) logger.debug('load success') def get_emb(self, text_list): @@ -32,9 +33,7 @@ class HFEmbedding: @param text_list: @return: ''' - logger.info('st') emb_res = self.model.embed_documents(text_list) - logger.info('ed') res = { text_list[idx]: emb_res[idx] for idx in range(len(text_list)) } diff --git a/dev_opsgpt/embeddings/openai_embedding.py b/coagent/embeddings/openai_embedding.py similarity index 100% rename from dev_opsgpt/embeddings/openai_embedding.py rename to coagent/embeddings/openai_embedding.py diff --git a/coagent/embeddings/utils.py b/coagent/embeddings/utils.py new file mode 100644 index 0000000..8b98f27 --- /dev/null +++ b/coagent/embeddings/utils.py @@ -0,0 +1,20 @@ +import os +from functools import lru_cache +from langchain.embeddings.huggingface import HuggingFaceEmbeddings +# from configs.model_config import embedding_model_dict +from loguru import logger + + +@lru_cache(1) +def load_embeddings(model: str, device: str, embedding_model_dict: dict): + embeddings = HuggingFaceEmbeddings(model_name=embedding_model_dict[model], + model_kwargs={'device': device}) + return embeddings + + +@lru_cache(1) +def load_embeddings_from_path(model_path: str, device: str): + embeddings = HuggingFaceEmbeddings(model_name=model_path, + model_kwargs={'device': device}) + return embeddings + diff --git a/coagent/llm_models/__init__.py b/coagent/llm_models/__init__.py new file mode 100644 index 0000000..606e061 --- /dev/null +++ b/coagent/llm_models/__init__.py @@ -0,0 +1,8 @@ +from .openai_model import getChatModel, getExtraModel, getChatModelFromConfig +from .llm_config import LLMConfig, EmbedConfig + + +__all__ = [ + "getChatModel", "getExtraModel", "getChatModelFromConfig", + "LLMConfig", "EmbedConfig" +] \ No newline at end of file diff --git a/coagent/llm_models/llm_config.py b/coagent/llm_models/llm_config.py new file mode 100644 index 0000000..389290f --- /dev/null +++ b/coagent/llm_models/llm_config.py @@ -0,0 +1,61 @@ +from dataclasses import dataclass +from typing import List, Union + + + +@dataclass +class LLMConfig: + def __init__( + self, + model_name: str = "gpt-3.5-turbo", + temperature: float = 0.25, + stop: Union[List[str], str] = None, + api_key: str = "", + api_base_url: str = "", + model_device: str = "cpu", + **kwargs + ): + + self.model_name: str = model_name + self.temperature: float = temperature + self.stop: Union[List[str], str] = stop + self.api_key: str = api_key + self.api_base_url: str = api_base_url + self.model_device: str = model_device + # + self.check_config() + + def check_config(self, ): + pass + + def __str__(self): + return ', '.join(f"{k}: {v}" for k,v in vars(self).items()) + + +@dataclass +class EmbedConfig: + def __init__( + self, + api_key: str = "", + api_base_url: str = "", + embed_model: str = "", + embed_model_path: str = "", + embed_engine: str = "", + model_device: str = "cpu", + **kwargs + ): + self.embed_model: str = embed_model + self.embed_model_path: str = embed_model_path + self.embed_engine: str = embed_engine + self.model_device: str = model_device + self.api_key: str = api_key + self.api_base_url: str = api_base_url + # + self.check_config() + + def check_config(self, ): + pass + + def __str__(self): + return ', '.join(f"{k}: {v}" for k,v in vars(self).items()) + \ No newline at end of file diff --git a/dev_opsgpt/llm_models/openai_model.py b/coagent/llm_models/openai_model.py similarity index 55% rename from dev_opsgpt/llm_models/openai_model.py rename to coagent/llm_models/openai_model.py index 52d4537..381e512 100644 --- a/dev_opsgpt/llm_models/openai_model.py +++ b/coagent/llm_models/openai_model.py @@ -1,7 +1,10 @@ +import os + from langchain.callbacks import AsyncIteratorCallbackHandler from langchain.chat_models import ChatOpenAI -from configs.model_config import (llm_model_dict, LLM_MODEL) +from .llm_config import LLMConfig +# from configs.model_config import (llm_model_dict, LLM_MODEL) def getChatModel(callBack: AsyncIteratorCallbackHandler = None, temperature=0.3, stop=None): @@ -28,6 +31,33 @@ def getChatModel(callBack: AsyncIteratorCallbackHandler = None, temperature=0.3, ) return model + +def getChatModelFromConfig(llm_config: LLMConfig, callBack: AsyncIteratorCallbackHandler = None, ): + if callBack is None: + model = ChatOpenAI( + streaming=True, + verbose=True, + openai_api_key=llm_config.api_key, + openai_api_base=llm_config.api_base_url, + model_name=llm_config.model_name, + temperature=llm_config.temperature, + stop=llm_config.stop + ) + else: + model = ChatOpenAI( + streaming=True, + verbose=True, + callBack=[callBack], + openai_api_key=llm_config.api_key, + openai_api_base=llm_config.api_base_url, + model_name=llm_config.model_name, + temperature=llm_config.temperature, + stop=llm_config.stop + ) + + return model + + import json, requests diff --git a/dev_opsgpt/orm/__init__.py b/coagent/orm/__init__.py similarity index 100% rename from dev_opsgpt/orm/__init__.py rename to coagent/orm/__init__.py diff --git a/dev_opsgpt/orm/commands/__init__.py b/coagent/orm/commands/__init__.py similarity index 100% rename from dev_opsgpt/orm/commands/__init__.py rename to coagent/orm/commands/__init__.py diff --git a/dev_opsgpt/orm/commands/code_base_cds.py b/coagent/orm/commands/code_base_cds.py similarity index 95% rename from dev_opsgpt/orm/commands/code_base_cds.py rename to coagent/orm/commands/code_base_cds.py index 258a039..cb3c161 100644 --- a/dev_opsgpt/orm/commands/code_base_cds.py +++ b/coagent/orm/commands/code_base_cds.py @@ -6,8 +6,8 @@ @desc: ''' from loguru import logger -from dev_opsgpt.orm.db import with_session, _engine -from dev_opsgpt.orm.schemas.base_schema import CodeBaseSchema +from coagent.orm.db import with_session +from coagent.orm.schemas.base_schema import CodeBaseSchema @with_session diff --git a/dev_opsgpt/orm/commands/document_base_cds.py b/coagent/orm/commands/document_base_cds.py similarity index 95% rename from dev_opsgpt/orm/commands/document_base_cds.py rename to coagent/orm/commands/document_base_cds.py index 4c34a7d..2091abe 100644 --- a/dev_opsgpt/orm/commands/document_base_cds.py +++ b/coagent/orm/commands/document_base_cds.py @@ -1,5 +1,5 @@ -from dev_opsgpt.orm.db import with_session, _engine -from dev_opsgpt.orm.schemas.base_schema import KnowledgeBaseSchema +from coagent.orm.db import with_session +from coagent.orm.schemas.base_schema import KnowledgeBaseSchema # @with_session diff --git a/dev_opsgpt/orm/commands/document_file_cds.py b/coagent/orm/commands/document_file_cds.py similarity index 94% rename from dev_opsgpt/orm/commands/document_file_cds.py rename to coagent/orm/commands/document_file_cds.py index 7099303..a17d43f 100644 --- a/dev_opsgpt/orm/commands/document_file_cds.py +++ b/coagent/orm/commands/document_file_cds.py @@ -1,6 +1,6 @@ -from dev_opsgpt.orm.db import with_session, _engine -from dev_opsgpt.orm.schemas.base_schema import KnowledgeFileSchema, KnowledgeBaseSchema -from dev_opsgpt.orm.utils import DocumentFile +from coagent.orm.db import with_session +from coagent.orm.schemas.base_schema import KnowledgeFileSchema, KnowledgeBaseSchema +from coagent.orm.utils import DocumentFile diff --git a/dev_opsgpt/orm/db.py b/coagent/orm/db.py similarity index 90% rename from dev_opsgpt/orm/db.py rename to coagent/orm/db.py index 1cb086a..ac7e3b1 100644 --- a/dev_opsgpt/orm/db.py +++ b/coagent/orm/db.py @@ -3,7 +3,8 @@ from sqlalchemy.engine import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker -from configs.model_config import SQLALCHEMY_DATABASE_URI +from coagent.base_configs.env_config import SQLALCHEMY_DATABASE_URI +# from configs.model_config import SQLALCHEMY_DATABASE_URI _engine = create_engine(SQLALCHEMY_DATABASE_URI) diff --git a/dev_opsgpt/tools/sandbox.py b/coagent/orm/schemas/__init__.py similarity index 100% rename from dev_opsgpt/tools/sandbox.py rename to coagent/orm/schemas/__init__.py diff --git a/dev_opsgpt/orm/schemas/base_schema.py b/coagent/orm/schemas/base_schema.py similarity index 98% rename from dev_opsgpt/orm/schemas/base_schema.py rename to coagent/orm/schemas/base_schema.py index 2657884..775d7a1 100644 --- a/dev_opsgpt/orm/schemas/base_schema.py +++ b/coagent/orm/schemas/base_schema.py @@ -1,6 +1,6 @@ from sqlalchemy import Column, Integer, String, DateTime, func -from dev_opsgpt.orm.db import Base +from coagent.orm.db import Base class KnowledgeBaseSchema(Base): diff --git a/coagent/orm/utils.py b/coagent/orm/utils.py new file mode 100644 index 0000000..cf52498 --- /dev/null +++ b/coagent/orm/utils.py @@ -0,0 +1,22 @@ +import os +from coagent.utils.path_utils import get_file_path, get_LoaderClass, SUPPORTED_EXTS +# from configs.model_config import KB_ROOT_PATH +from loguru import logger + + +class DocumentFile: + def __init__( + self, filename: str, knowledge_base_name: str, kb_root_path: str) -> None: + self.kb_name = knowledge_base_name + self.filename = filename + self.filename = os.path.basename(filename) + self.ext = os.path.splitext(filename)[-1].lower() + self.kb_root_path = kb_root_path + if self.ext not in SUPPORTED_EXTS: + raise ValueError(f"暂未支持的文件格式 {self.ext}") + self.filepath = get_file_path(knowledge_base_name, self.filename, kb_root_path) + self.docs = None + self.document_loader_name = get_LoaderClass(self.ext) + + # TODO: 增加依据文件格式匹配text_splitter + self.text_splitter_name = None \ No newline at end of file diff --git a/dev_opsgpt/sandbox/__init__.py b/coagent/sandbox/__init__.py similarity index 100% rename from dev_opsgpt/sandbox/__init__.py rename to coagent/sandbox/__init__.py diff --git a/dev_opsgpt/sandbox/basebox.py b/coagent/sandbox/basebox.py similarity index 95% rename from dev_opsgpt/sandbox/basebox.py rename to coagent/sandbox/basebox.py index 476614c..c276f1d 100644 --- a/dev_opsgpt/sandbox/basebox.py +++ b/coagent/sandbox/basebox.py @@ -5,9 +5,6 @@ import sys from abc import ABC, abstractclassmethod from loguru import logger -from configs.server_config import SANDBOX_SERVER - - class CodeBoxResponse(BaseModel): code_text: str = "" code_exe_response: str = "" @@ -30,8 +27,8 @@ class BaseBox(ABC): def __init__( self, remote_url: str = "", - remote_ip: str = SANDBOX_SERVER["host"], - remote_port: str = SANDBOX_SERVER["port"], + remote_ip: str = "htpp://127.0.0.1", + remote_port: str = "5050", token: str = "mytoken", do_code_exe: bool = False, do_remote: bool = False diff --git a/dev_opsgpt/sandbox/pycodebox.py b/coagent/sandbox/pycodebox.py similarity index 91% rename from dev_opsgpt/sandbox/pycodebox.py rename to coagent/sandbox/pycodebox.py index 5914ef2..d74a868 100644 --- a/dev_opsgpt/sandbox/pycodebox.py +++ b/coagent/sandbox/pycodebox.py @@ -1,16 +1,13 @@ -import time, os, docker, requests, json, uuid, subprocess, time, asyncio, aiohttp, re, traceback +import time, os, requests, json, uuid, subprocess, time, asyncio, aiohttp, re, traceback import psutil -from typing import List, Optional, Union +from typing import List, Optional from loguru import logger -from websockets.sync.client import connect as ws_connect_sync -from websockets.client import connect as ws_connect from websocket import create_connection from websockets.client import WebSocketClientProtocol, ClientConnection from websockets.exceptions import ConnectionClosedError -from configs.server_config import SANDBOX_SERVER -from configs.model_config import JUPYTER_WORK_PATH +# from configs.model_config import JUPYTER_WORK_PATH from .basebox import BaseBox, CodeBoxResponse, CodeBoxStatus @@ -21,17 +18,22 @@ class PyCodeBox(BaseBox): def __init__( self, remote_url: str = "", - remote_ip: str = SANDBOX_SERVER["host"], - remote_port: str = SANDBOX_SERVER["port"], + remote_ip: str = "http://127.0.0.1", + remote_port: str = "5050", token: str = "mytoken", + jupyter_work_path: str = "", do_code_exe: bool = False, do_remote: bool = False, do_check_net: bool = True, + use_stop: bool = False ): super().__init__(remote_url, remote_ip, remote_port, token, do_code_exe, do_remote) self.enter_status = True self.do_check_net = do_check_net + self.use_stop = use_stop + self.jupyter_work_path = jupyter_work_path asyncio.run(self.astart()) + # self.start() # logger.info(f"""remote_url: {self.remote_url}, # remote_ip: {self.remote_ip}, @@ -247,14 +249,14 @@ class PyCodeBox(BaseBox): try: async with aiohttp.ClientSession() as session: async with session.get(f"{self.remote_ip}:{self.remote_port}", timeout=270) as resp: - logger.warning(f"Port is conflict, please check your codebox's port {self.remote_port}") + # logger.warning(f"Port is conflict, please check your codebox's port {self.remote_port}") return resp.status == 200 except aiohttp.ClientConnectorError: pass except aiohttp.ServerDisconnectedError: pass - def _check_connect_success(self, retry_nums: int = 5) -> bool: + def _check_connect_success(self, retry_nums: int = 2) -> bool: if not self.do_check_net: return True while retry_nums > 0: @@ -269,13 +271,13 @@ class PyCodeBox(BaseBox): time.sleep(5) raise BaseException(f"can't connect to {self.remote_url}") - async def _acheck_connect_success(self, retry_nums: int = 5) -> bool: + async def _acheck_connect_success(self, retry_nums: int = 2) -> bool: if not self.do_check_net: return True while retry_nums > 0: try: connect_status = await self._acheck_connect() if connect_status: - logger.info(f"{self.remote_url} connection success") + # logger.info(f"{self.remote_url} connection success") return True except requests.exceptions.ConnectionError: logger.info(f"{self.remote_url} connection fail") @@ -305,14 +307,14 @@ class PyCodeBox(BaseBox): raise BaseException(f"Port is conflict, please check your codebox's port {self.remote_port}") if not connect_status: - self.jupyter = subprocess.run( + self.jupyter = subprocess.Popen( [ - "jupyer", "notebnook", + "jupyter", "notebook", f"--NotebookApp.token={self.token}", f"--port={self.remote_port}", "--no-browser", "--ServerApp.disable_check_xsrf=True", - "--notebook-dir={JUPYTER_WORK_PATH}" + f"--notebook-dir={self.jupyter_work_path}" ], stderr=subprocess.PIPE, stdin=subprocess.PIPE, @@ -345,24 +347,33 @@ class PyCodeBox(BaseBox): port_status = await self._acheck_port() self.kernel_url = self.remote_url + "/api/kernels" connect_status = await self._acheck_connect() - logger.info(f"port_status: {port_status}, connect_status: {connect_status}") + if os.environ.get("log_verbose", "0") >= "2": + logger.info(f"port_status: {port_status}, connect_status: {connect_status}") if port_status and not connect_status: raise BaseException(f"Port is conflict, please check your codebox's port {self.remote_port}") if not connect_status: - self.jupyter = subprocess.Popen( - [ + script_sh = [ "jupyter", "notebook", f"--NotebookApp.token={self.token}", f"--port={self.remote_port}", "--no-browser", - "--ServerApp.disable_check_xsrf=True" - ], + "--ServerApp.disable_check_xsrf=True", + f"--notebook-dir={self.jupyter_work_path}" + ] + self.jupyter = subprocess.Popen( + script_sh, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + cwd=self.jupyter_work_path ) + while True and self.jupyter: + line = self.jupyter.stderr.readline() + # logger.debug(line.decode("gbk")) + if "Control-C" in line.decode("gbk"): + break self.kernel_url = self.remote_url + "/api/kernels" self.do_check_net = True await self._acheck_connect_success() @@ -407,7 +418,7 @@ class PyCodeBox(BaseBox): self.jupyter = None except Exception as e: - logger.error(traceback.format_exc()) + logger.error(e) if self.ws is not None: try: @@ -417,7 +428,7 @@ class PyCodeBox(BaseBox): loop = asyncio.new_event_loop() loop.run_until_complete(self.ws.close()) except Exception as e: - logger.error(traceback.format_exc()) + logger.error(e) self.ws = None # return CodeBoxStatus(status="stopped") diff --git a/domain/code/README.md b/coagent/service/__init__.py similarity index 100% rename from domain/code/README.md rename to coagent/service/__init__.py diff --git a/coagent/service/api.py.bak b/coagent/service/api.py.bak new file mode 100644 index 0000000..921c606 --- /dev/null +++ b/coagent/service/api.py.bak @@ -0,0 +1,210 @@ +import nltk +import argparse +import uvicorn, os, sys +from fastapi.middleware.cors import CORSMiddleware +from starlette.responses import RedirectResponse +from typing import List + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +sys.path.append(os.path.dirname(os.path.dirname(__file__))) + +# from configs import VERSION +# from configs.model_config import NLTK_DATA_PATH +# from configs.server_config import OPEN_CROSS_DOMAIN + +from coagent.chat import LLMChat, SearchChat, KnowledgeChat +from coagent.service.kb_api import * +from coagent.service.cb_api import * +from coagent.utils.server_utils import BaseResponse, ListResponse, FastAPI, MakeFastAPIOffline + +# nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path + +from coagent.chat import LLMChat, SearchChat, KnowledgeChat, CodeChat + +llmChat = LLMChat() +searchChat = SearchChat() +knowledgeChat = KnowledgeChat() +codeChat = CodeChat() + + +async def document(): + return RedirectResponse(url="/docs") + + +def create_app(): + app = FastAPI( + title="DevOps-ChatBot API Server", + version="v0.1.0" + # version=VERSION + ) + MakeFastAPIOffline(app) + # Add CORS middleware to allow all origins + # 在config.py中设置OPEN_DOMAIN=True,允许跨域 + # set OPEN_DOMAIN=True in config.py to allow cross-domain + if False: + # if OPEN_CROSS_DOMAIN: + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + app.get("/", + response_model=BaseResponse, + summary="swagger 文档")(document) + + # Tag: Chat + # app.post("/chat/fastchat", + # tags=["Chat"], + # summary="与llm模型对话(直接与fastchat api对话)")(openai_chat) + + app.post("/chat/chat", + tags=["Chat"], + summary="与llm模型对话(通过LLMChain)")(llmChat.chat) + + app.post("/chat/knowledge_base_chat", + tags=["Chat"], + summary="与知识库对话")(knowledgeChat.chat) + + app.post("/chat/search_engine_chat", + tags=["Chat"], + summary="与搜索引擎对话")(searchChat.chat) + + app.post("/chat/code_chat", + tags=["Chat"], + summary="与代码库对话")(codeChat.chat) + + # Tag: Knowledge Base Management + app.get("/knowledge_base/list_knowledge_bases", + tags=["Knowledge Base Management"], + response_model=ListResponse, + summary="获取知识库列表")(list_kbs) + + app.post("/knowledge_base/create_knowledge_base", + tags=["Knowledge Base Management"], + response_model=BaseResponse, + summary="创建知识库" + )(create_kb) + + app.post("/knowledge_base/delete_knowledge_base", + tags=["Knowledge Base Management"], + response_model=BaseResponse, + summary="删除知识库" + )(delete_kb) + + app.get("/knowledge_base/list_files", + tags=["Knowledge Base Management"], + response_model=ListResponse, + summary="获取知识库内的文件列表" + )(list_docs) + + app.post("/knowledge_base/search_docs", + tags=["Knowledge Base Management"], + response_model=List[DocumentWithScore], + summary="搜索知识库" + )(search_docs) + + app.post("/knowledge_base/upload_docs", + tags=["Knowledge Base Management"], + response_model=BaseResponse, + summary="上传文件到知识库,并/或进行向量化" + )(upload_doc) + + app.post("/knowledge_base/delete_docs", + tags=["Knowledge Base Management"], + response_model=BaseResponse, + summary="删除知识库内指定文件" + )(delete_doc) + + app.post("/knowledge_base/update_docs", + tags=["Knowledge Base Management"], + response_model=BaseResponse, + summary="更新现有文件到知识库" + )(update_doc) + + app.get("/knowledge_base/download_doc", + tags=["Knowledge Base Management"], + summary="下载对应的知识文件")(download_doc) + + app.post("/knowledge_base/recreate_vector_store", + tags=["Knowledge Base Management"], + summary="根据content中文档重建向量库,流式输出处理进度。" + )(recreate_vector_store) + + app.post("/code_base/create_code_base", + tags=["Code Base Management"], + summary="新建 code_base" + )(create_cb) + + app.post("/code_base/delete_code_base", + tags=["Code Base Management"], + summary="删除 code_base" + )(delete_cb) + + app.post("/code_base/code_base_chat", + tags=["Code Base Management"], + summary="删除 code_base" + )(delete_cb) + + app.get("/code_base/list_code_bases", + tags=["Code Base Management"], + summary="列举 code_base", + response_model=ListResponse + )(list_cbs) + + # # LLM模型相关接口 + # app.post("/llm_model/list_models", + # tags=["LLM Model Management"], + # summary="列出当前已加载的模型", + # )(list_llm_models) + + # app.post("/llm_model/stop", + # tags=["LLM Model Management"], + # summary="停止指定的LLM模型(Model Worker)", + # )(stop_llm_model) + + # app.post("/llm_model/change", + # tags=["LLM Model Management"], + # summary="切换指定的LLM模型(Model Worker)", + # )(change_llm_model) + + return app + + +app = create_app() + + +def run_api(host, port, **kwargs): + if kwargs.get("ssl_keyfile") and kwargs.get("ssl_certfile"): + uvicorn.run(app, + host=host, + port=port, + ssl_keyfile=kwargs.get("ssl_keyfile"), + ssl_certfile=kwargs.get("ssl_certfile"), + ) + else: + uvicorn.run(app, host=host, port=port) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(prog='DevOps-ChatBot', + description='About DevOps-ChatBot, local knowledge based LLM with langchain' + ' | 基于本地知识库的 LLM 问答') + parser.add_argument("--host", type=str, default="0.0.0.0") + parser.add_argument("--port", type=int, default=7861) + parser.add_argument("--ssl_keyfile", type=str) + parser.add_argument("--ssl_certfile", type=str) + # 初始化消息 + args = parser.parse_args() + args_dict = vars(args) + run_api(host=args.host, + port=args.port, + ssl_keyfile=args.ssl_keyfile, + ssl_certfile=args.ssl_certfile, + ) diff --git a/dev_opsgpt/service/base_service.py b/coagent/service/base_service.py similarity index 73% rename from dev_opsgpt/service/base_service.py rename to coagent/service/base_service.py index 1993603..739e715 100644 --- a/dev_opsgpt/service/base_service.py +++ b/coagent/service/base_service.py @@ -5,16 +5,20 @@ import os from langchain.embeddings.base import Embeddings from langchain.docstore.document import Document -from configs.model_config import ( - kbs_config, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, - EMBEDDING_MODEL, EMBEDDING_DEVICE +# from configs.model_config import ( +# kbs_config, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, +# EMBEDDING_MODEL, EMBEDDING_DEVICE, KB_ROOT_PATH +# ) +# from configs.model_config import embedding_model_dict +from coagent.base_configs.env_config import ( + VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, kbs_config ) -from dev_opsgpt.orm.commands import * -from dev_opsgpt.utils.path_utils import * -from dev_opsgpt.orm.utils import DocumentFile -from dev_opsgpt.embeddings.utils import load_embeddings -from dev_opsgpt.text_splitter import LCTextSplitter - +from coagent.orm.commands import * +from coagent.utils.path_utils import * +from coagent.orm.utils import DocumentFile +from coagent.embeddings.utils import load_embeddings, load_embeddings_from_path +from coagent.text_splitter import LCTextSplitter +from coagent.llm_models.llm_config import EmbedConfig class SupportedVSType: @@ -28,16 +32,21 @@ class KBService(ABC): def __init__(self, knowledge_base_name: str, - embed_model: str = EMBEDDING_MODEL, + embed_config: EmbedConfig, + # embed_model: str = "text2vec-base-chinese", + kb_root_path: str, ): self.kb_name = knowledge_base_name - self.embed_model = embed_model - self.kb_path = get_kb_path(self.kb_name) - self.doc_path = get_doc_path(self.kb_name) + # self.embed_model = embed_model + self.embed_config = embed_config + self.kb_root_path = kb_root_path + self.kb_path = get_kb_path(self.kb_name, kb_root_path) + self.doc_path = get_doc_path(self.kb_name, kb_root_path) self.do_init() - def _load_embeddings(self, embed_device: str = EMBEDDING_DEVICE) -> Embeddings: - return load_embeddings(self.embed_model, embed_device) + def _load_embeddings(self) -> Embeddings: + # return load_embeddings(self.embed_model, embed_device, embedding_model_dict) + return load_embeddings_from_path(self.embed_config.embed_model_path, self.embed_config.model_device) def create_kb(self): """ @@ -46,7 +55,7 @@ class KBService(ABC): if not os.path.exists(self.doc_path): os.makedirs(self.doc_path) self.do_create_kb() - status = add_kb_to_db(self.kb_name, self.vs_type(), self.embed_model) + status = add_kb_to_db(self.kb_name, self.vs_type(), self.embed_config.embed_model) return status def clear_vs(self): @@ -100,7 +109,7 @@ class KBService(ABC): def exist_doc(self, file_name: str): return doc_exists(DocumentFile(knowledge_base_name=self.kb_name, - filename=file_name)) + filename=file_name, kb_root_path=self.kb_root_path)) def list_docs(self): return list_docs_from_db(self.kb_name) @@ -169,6 +178,15 @@ class KBService(ABC): """ pass + @abstractmethod + def get_all_documents(self, + embeddings: Embeddings, + ): + """ + 获取知识库所有文档 + """ + pass + @abstractmethod def do_delete_doc(self, kb_file: DocumentFile): diff --git a/dev_opsgpt/service/cb_api.py b/coagent/service/cb_api.py similarity index 54% rename from dev_opsgpt/service/cb_api.py rename to coagent/service/cb_api.py index c8437e0..6de3c42 100644 --- a/dev_opsgpt/service/cb_api.py +++ b/coagent/service/cb_api.py @@ -15,21 +15,28 @@ from fastapi import File, Form, Body, Query, UploadFile from langchain.docstore.document import Document from .service_factory import KBServiceFactory -from dev_opsgpt.utils.server_utils import BaseResponse, ListResponse -from dev_opsgpt.utils.path_utils import * -from dev_opsgpt.orm.commands import * -from dev_opsgpt.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from dev_opsgpt.db_handler.vector_db_handler.chroma_handler import ChromaHandler -from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT -from configs.server_config import CHROMA_PERSISTENT_PATH +from coagent.utils.server_utils import BaseResponse, ListResponse +from coagent.utils.path_utils import * +from coagent.orm.commands import * +from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler +from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler +# from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT +# from configs.server_config import CHROMA_PERSISTENT_PATH -from configs.model_config import ( - CB_ROOT_PATH +from coagent.base_configs.env_config import ( + CB_ROOT_PATH, + NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT, + CHROMA_PERSISTENT_PATH ) -# from dev_opsgpt.codebase_handler.codebase_handler import CodeBaseHandler -from dev_opsgpt.codechat.codebase_handler.codebase_handler import CodeBaseHandler +# from configs.model_config import ( +# CB_ROOT_PATH +# ) + +# from coagent.codebase_handler.codebase_handler import CodeBaseHandler +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.codechat.codebase_handler.codebase_handler import CodeBaseHandler from loguru import logger @@ -42,10 +49,21 @@ async def list_cbs(): async def create_cb(zip_file, cb_name: str = Body(..., examples=["samples"]), code_path: str = Body(..., examples=["samples"]), - do_interpret: bool = Body(..., examples=["samples"]) + do_interpret: bool = Body(..., examples=["samples"]), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), + model_name: bool = Body(..., examples=["samples"]), + temperature: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), ) -> BaseResponse: logger.info('cb_name={}, zip_path={}, do_interpret={}'.format(cb_name, code_path, do_interpret)) + embed_config: EmbedConfig = EmbedConfig(**locals()) + llm_config: LLMConfig = LLMConfig(**locals()) + # Create selected knowledge base if not validate_kb_name(cb_name): return BaseResponse(code=403, msg="Don't attack me") @@ -58,7 +76,7 @@ async def create_cb(zip_file, try: logger.info('start build code base') - cbh = CodeBaseHandler(cb_name, code_path) + cbh = CodeBaseHandler(cb_name, code_path, embed_config=embed_config, llm_config=llm_config) vertices_num, edge_num, file_num = cbh.import_code(zip_file=zip_file, do_interpret=do_interpret) logger.info('build code base done') @@ -73,8 +91,20 @@ async def create_cb(zip_file, return BaseResponse(code=200, msg=f"已新增代码知识库 {cb_name}") -async def delete_cb(cb_name: str = Body(..., examples=["samples"])) -> BaseResponse: +async def delete_cb( + cb_name: str = Body(..., examples=["samples"]), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), + model_name: bool = Body(..., examples=["samples"]), + temperature: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + ) -> BaseResponse: logger.info('cb_name={}'.format(cb_name)) + embed_config: EmbedConfig = EmbedConfig(**locals()) + llm_config: LLMConfig = LLMConfig(**locals()) # Create selected knowledge base if not validate_kb_name(cb_name): return BaseResponse(code=403, msg="Don't attack me") @@ -90,7 +120,7 @@ async def delete_cb(cb_name: str = Body(..., examples=["samples"])) -> BaseRespo shutil.rmtree(CB_ROOT_PATH + os.sep + cb_name) # delete from codebase - cbh = CodeBaseHandler(cb_name) + cbh = CodeBaseHandler(cb_name, embed_config=embed_config, llm_config=llm_config) cbh.delete_codebase(codebase_name=cb_name) except Exception as e: @@ -104,17 +134,27 @@ def search_code(cb_name: str = Body(..., examples=["sofaboot"]), query: str = Body(..., examples=['你好']), code_limit: int = Body(..., examples=['1']), search_type: str = Body(..., examples=['你好']), - history_node_list: list = Body(...)) -> dict: + history_node_list: list = Body(...), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), + model_name: bool = Body(..., examples=["samples"]), + temperature: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + ) -> dict: logger.info('cb_name={}'.format(cb_name)) logger.info('query={}'.format(query)) logger.info('code_limit={}'.format(code_limit)) logger.info('search_type={}'.format(search_type)) logger.info('history_node_list={}'.format(history_node_list)) - + embed_config: EmbedConfig = EmbedConfig(**locals()) + llm_config: LLMConfig = LLMConfig(**locals()) try: # load codebase - cbh = CodeBaseHandler(codebase_name=cb_name) + cbh = CodeBaseHandler(codebase_name=cb_name, embed_config=embed_config, llm_config=llm_config) # search code context, related_vertices = cbh.search_code(query, search_type=search_type, limit=code_limit) @@ -145,6 +185,7 @@ def search_related_vertices(cb_name: str = Body(..., examples=["sofaboot"]), cypher_res = nh.execute_cypher(cypher=cypher, format_res=True) related_vertices = cypher_res.get('id', []) + related_vertices = [i.as_string() for i in related_vertices] res = { 'vertices': related_vertices @@ -163,15 +204,27 @@ def search_code_by_vertex(cb_name: str = Body(..., examples=["sofaboot"]), logger.info('vertex={}'.format(vertex)) try: + nh = NebulaHandler(host=NEBULA_HOST, port=NEBULA_PORT, username=NEBULA_USER, + password=NEBULA_PASSWORD, space_name=cb_name) + + cypher = f'''MATCH (v1:package)-[e:contain]->(v2) WHERE id(v2) == '{vertex}' RETURN id(v1) as id;''' + cypher_res = nh.execute_cypher(cypher=cypher, format_res=True) + + related_vertices = cypher_res.get('id', []) + related_vertices = [i.as_string() for i in related_vertices] + + if not related_vertices: + return {'code': ''} ch = ChromaHandler(path=CHROMA_PERSISTENT_PATH, collection_name=cb_name) - # fix vertex - vertex_use = '#'.join(vertex.split('#')[0:2]) - ids = [vertex_use] + # logger.info(related_vertices) + chroma_res = ch.get(ids=related_vertices, include=['metadatas']) + # logger.info(chroma_res) - chroma_res = ch.get(ids=ids) - - code_text = chroma_res['result']['metadatas'][0]['code_text'] + if chroma_res['result']['ids']: + code_text = chroma_res['result']['metadatas'][0]['code_text'] + else: + code_text = '' res = { 'code': code_text diff --git a/dev_opsgpt/service/faiss_db_service.py b/coagent/service/faiss_db_service.py similarity index 67% rename from dev_opsgpt/service/faiss_db_service.py rename to coagent/service/faiss_db_service.py index ea336bc..4a6e43d 100644 --- a/dev_opsgpt/service/faiss_db_service.py +++ b/coagent/service/faiss_db_service.py @@ -8,21 +8,30 @@ from loguru import logger from langchain.embeddings.base import Embeddings from langchain.docstore.document import Document from langchain.embeddings.huggingface import HuggingFaceEmbeddings +from langchain.vectorstores.utils import DistanceStrategy -from configs.model_config import ( +# from configs.model_config import ( +# KB_ROOT_PATH, +# CACHED_VS_NUM, +# EMBEDDING_MODEL, +# EMBEDDING_DEVICE, +# SCORE_THRESHOLD, +# FAISS_NORMALIZE_L2 +# ) +# from configs.model_config import embedding_model_dict + +from coagent.base_configs.env_config import ( KB_ROOT_PATH, - CACHED_VS_NUM, - EMBEDDING_MODEL, - EMBEDDING_DEVICE, - SCORE_THRESHOLD, - FAISS_NORMALIZE_L2 + CACHED_VS_NUM, SCORE_THRESHOLD, FAISS_NORMALIZE_L2 ) + from .base_service import KBService, SupportedVSType -from dev_opsgpt.utils.path_utils import * -from dev_opsgpt.orm.utils import DocumentFile -from dev_opsgpt.utils.server_utils import torch_gc -from dev_opsgpt.embeddings.utils import load_embeddings -from dev_opsgpt.embeddings.faiss_m import FAISS +from coagent.utils.path_utils import * +from coagent.orm.utils import DocumentFile +from coagent.utils.server_utils import torch_gc +from coagent.embeddings.utils import load_embeddings, load_embeddings_from_path +from coagent.embeddings.faiss_m import FAISS +from coagent.llm_models.llm_config import EmbedConfig # make HuggingFaceEmbeddings hashable @@ -35,29 +44,29 @@ HuggingFaceEmbeddings.__hash__ = _embeddings_hash _VECTOR_STORE_TICKS = {} -@lru_cache(CACHED_VS_NUM) +# @lru_cache(CACHED_VS_NUM) def load_vector_store( knowledge_base_name: str, - embed_model: str = EMBEDDING_MODEL, - embed_device: str = EMBEDDING_DEVICE, + embed_config: EmbedConfig, embeddings: Embeddings = None, tick: int = 0, # tick will be changed by upload_doc etc. and make cache refreshed. + kb_root_path: str = KB_ROOT_PATH, ): print(f"loading vector store in '{knowledge_base_name}'.") - vs_path = get_vs_path(knowledge_base_name) + vs_path = get_vs_path(knowledge_base_name, kb_root_path) if embeddings is None: - logger.info("load embedmodel: {}".format(embed_model)) - embeddings = load_embeddings(embed_model, embed_device) + embeddings = load_embeddings_from_path(embed_config.embed_model_path, embed_config.model_device) if not os.path.exists(vs_path): os.makedirs(vs_path) - + + distance_strategy = DistanceStrategy.EUCLIDEAN_DISTANCE if "index.faiss" in os.listdir(vs_path): - search_index = FAISS.load_local(vs_path, embeddings, normalize_L2=FAISS_NORMALIZE_L2) + search_index = FAISS.load_local(vs_path, embeddings, normalize_L2=FAISS_NORMALIZE_L2, distance_strategy=distance_strategy) else: # create an empty vector store doc = Document(page_content="init", metadata={}) - search_index = FAISS.from_documents([doc], embeddings, normalize_L2=FAISS_NORMALIZE_L2) + search_index = FAISS.from_documents([doc], embeddings, normalize_L2=FAISS_NORMALIZE_L2, distance_strategy=distance_strategy) ids = [k for k, v in search_index.docstore._dict.items()] search_index.delete(ids) search_index.save_local(vs_path) @@ -99,7 +108,7 @@ class FaissKBService(KBService): def do_create_kb(self): if not os.path.exists(self.vs_path): os.makedirs(self.vs_path) - load_vector_store(self.kb_name, self.embed_model) + load_vector_store(self.kb_name, self.embed_config) def do_drop_kb(self): self.clear_vs() @@ -112,10 +121,20 @@ class FaissKBService(KBService): embeddings: Embeddings = None, ) -> List[Document]: search_index = load_vector_store(self.kb_name, + self.embed_config, embeddings=embeddings, - tick=_VECTOR_STORE_TICKS.get(self.kb_name)) + tick=_VECTOR_STORE_TICKS.get(self.kb_name), + kb_root_path=self.kb_root_path) docs = search_index.similarity_search_with_score(query, k=top_k, score_threshold=score_threshold) return docs + + def get_all_documents(self, embeddings: Embeddings = None,): + search_index = load_vector_store(self.kb_name, + self.embed_config, + embeddings=embeddings, + tick=_VECTOR_STORE_TICKS.get(self.kb_name), + kb_root_path=self.kb_root_path) + return search_index.get_all_documents() def do_add_doc(self, docs: List[Document], @@ -123,10 +142,12 @@ class FaissKBService(KBService): **kwargs, ): vector_store = load_vector_store(self.kb_name, + self.embed_config, embeddings=embeddings, - tick=_VECTOR_STORE_TICKS.get(self.kb_name, 0)) + tick=_VECTOR_STORE_TICKS.get(self.kb_name, 0), + kb_root_path=self.kb_root_path) vector_store.embedding_function = embeddings.embed_documents - logger.info("docs.lens: {}".format(len(docs))) + logger.info("loaded docs, docs' lens is {}".format(len(docs))) vector_store.add_documents(docs) torch_gc() if not kwargs.get("not_refresh_vs_cache"): @@ -138,8 +159,10 @@ class FaissKBService(KBService): **kwargs): embeddings = self._load_embeddings() vector_store = load_vector_store(self.kb_name, + self.embed_config, embeddings=embeddings, - tick=_VECTOR_STORE_TICKS.get(self.kb_name, 0)) + tick=_VECTOR_STORE_TICKS.get(self.kb_name, 0), + kb_root_path=self.kb_root_path) ids = [k for k, v in vector_store.docstore._dict.items() if v.metadata["source"] == kb_file.filepath] if len(ids) == 0: @@ -153,7 +176,8 @@ class FaissKBService(KBService): return True def do_clear_vs(self): - shutil.rmtree(self.vs_path) + if os.path.exists(self.vs_path): + shutil.rmtree(self.vs_path) os.makedirs(self.vs_path) refresh_vs_cache(self.kb_name) diff --git a/dev_opsgpt/service/kb_api.py b/coagent/service/kb_api.py similarity index 68% rename from dev_opsgpt/service/kb_api.py rename to coagent/service/kb_api.py index dd6363e..1607e65 100644 --- a/dev_opsgpt/service/kb_api.py +++ b/coagent/service/kb_api.py @@ -7,13 +7,14 @@ from fastapi import Body, File, Form, Body, Query, UploadFile from langchain.docstore.document import Document from .service_factory import KBServiceFactory -from dev_opsgpt.utils.server_utils import BaseResponse, ListResponse -from dev_opsgpt.utils.path_utils import * -from dev_opsgpt.orm.commands import * -from dev_opsgpt.orm.utils import DocumentFile -from configs.model_config import ( - DEFAULT_VS_TYPE, EMBEDDING_MODEL, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD -) +from coagent.utils.server_utils import BaseResponse, ListResponse +from coagent.utils.path_utils import * +from coagent.orm.commands import * +from coagent.orm.utils import DocumentFile +# from configs.model_config import ( +# DEFAULT_VS_TYPE, EMBEDDING_MODEL, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, KB_ROOT_PATH +# ) +from coagent.llm_models.llm_config import EmbedConfig async def list_kbs(): @@ -23,37 +24,45 @@ async def list_kbs(): async def create_kb(knowledge_base_name: str = Body(..., examples=["samples"]), vector_store_type: str = Body("faiss"), - embed_model: str = Body(EMBEDDING_MODEL), + kb_root_path: str =Body(""), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), ) -> BaseResponse: + embed_config: EmbedConfig = EmbedConfig(**locals()) # Create selected knowledge base if not validate_kb_name(knowledge_base_name): return BaseResponse(code=403, msg="Don't attack me") if knowledge_base_name is None or knowledge_base_name.strip() == "": return BaseResponse(code=404, msg="知识库名称不能为空,请重新填写知识库名称") - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) if kb is not None: return BaseResponse(code=404, msg=f"已存在同名知识库 {knowledge_base_name}") - kb = KBServiceFactory.get_service(knowledge_base_name, vector_store_type, embed_model) + kb = KBServiceFactory.get_service(knowledge_base_name, vector_store_type, embed_config, kb_root_path) try: kb.create_kb() except Exception as e: - print(e) + logger.exception(e) return BaseResponse(code=500, msg=f"创建知识库出错: {e}") return BaseResponse(code=200, msg=f"已新增知识库 {knowledge_base_name}") async def delete_kb( - knowledge_base_name: str = Body(..., examples=["samples"]) - ) -> BaseResponse: + knowledge_base_name: str = Body(..., examples=["samples"]), + kb_root_path: str =Body(""), + ) -> BaseResponse: # Delete selected knowledge base if not validate_kb_name(knowledge_base_name): return BaseResponse(code=403, msg="Don't attack me") knowledge_base_name = urllib.parse.unquote(knowledge_base_name) - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, None, kb_root_path) if kb is None: return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") @@ -77,10 +86,19 @@ class DocumentWithScore(Document): def search_docs(query: str = Body(..., description="用户输入", examples=["你好"]), knowledge_base_name: str = Body(..., description="知识库名称", examples=["samples"]), - top_k: int = Body(VECTOR_SEARCH_TOP_K, description="匹配向量数"), - score_threshold: float = Body(SCORE_THRESHOLD, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), - ) -> List[DocumentWithScore]: - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + top_k: int = Body(5, description="匹配向量数"), + score_threshold: float = Body(1.0, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), + kb_root_path: str =Body(""), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), + ) -> List[DocumentWithScore]: + + embed_config: EmbedConfig = EmbedConfig(**locals()) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) if kb is None: return [] docs = kb.search_docs(query, top_k, score_threshold) @@ -90,13 +108,14 @@ def search_docs(query: str = Body(..., description="用户输入", examples=[" async def list_docs( - knowledge_base_name: str + knowledge_base_name: str, + kb_root_path: str =Body(""), ) -> ListResponse: if not validate_kb_name(knowledge_base_name): return ListResponse(code=403, msg="Don't attack me", data=[]) knowledge_base_name = urllib.parse.unquote(knowledge_base_name) - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, None, kb_root_path) if kb is None: return ListResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}", data=[]) else: @@ -108,11 +127,19 @@ async def upload_doc(file: UploadFile = File(..., description="上传文件"), knowledge_base_name: str = Form(..., description="知识库名称", examples=["kb1"]), override: bool = Form(False, description="覆盖已有文件"), not_refresh_vs_cache: bool = Form(False, description="暂不保存向量库(用于FAISS)"), + kb_root_path: str =Body(""), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), ) -> BaseResponse: if not validate_kb_name(knowledge_base_name): return BaseResponse(code=403, msg="Don't attack me") - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + embed_config: EmbedConfig = EmbedConfig(**locals()) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) if kb is None: return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") @@ -120,7 +147,9 @@ async def upload_doc(file: UploadFile = File(..., description="上传文件"), try: kb_file = DocumentFile(filename=file.filename, - knowledge_base_name=knowledge_base_name) + knowledge_base_name=knowledge_base_name, + kb_root_path=kb_root_path + ) if (os.path.exists(kb_file.filepath) and not override @@ -149,12 +178,20 @@ async def delete_doc(knowledge_base_name: str = Body(..., examples=["samples"]), doc_name: str = Body(..., examples=["file_name.md"]), delete_content: bool = Body(False), not_refresh_vs_cache: bool = Body(False, description="暂不保存向量库(用于FAISS)"), + kb_root_path: str =Body(""), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), ) -> BaseResponse: if not validate_kb_name(knowledge_base_name): return BaseResponse(code=403, msg="Don't attack me") + embed_config: EmbedConfig = EmbedConfig(**locals()) knowledge_base_name = urllib.parse.unquote(knowledge_base_name) - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) if kb is None: return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") @@ -163,10 +200,11 @@ async def delete_doc(knowledge_base_name: str = Body(..., examples=["samples"]), try: kb_file = DocumentFile(filename=doc_name, - knowledge_base_name=knowledge_base_name) + knowledge_base_name=knowledge_base_name, + kb_root_path=kb_root_path) kb.delete_doc(kb_file, delete_content, not_refresh_vs_cache=not_refresh_vs_cache) except Exception as e: - print(e) + logger.exception(e) return BaseResponse(code=500, msg=f"{kb_file.filename} 文件删除失败,错误信息:{e}") return BaseResponse(code=200, msg=f"{kb_file.filename} 文件删除成功") @@ -176,20 +214,29 @@ async def update_doc( knowledge_base_name: str = Body(..., examples=["samples"]), file_name: str = Body(..., examples=["file_name"]), not_refresh_vs_cache: bool = Body(False, description="暂不保存向量库(用于FAISS)"), + kb_root_path: str =Body(""), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), ) -> BaseResponse: ''' 更新知识库文档 ''' + embed_config: EmbedConfig = EmbedConfig(**locals()) if not validate_kb_name(knowledge_base_name): return BaseResponse(code=403, msg="Don't attack me") - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) if kb is None: return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") try: kb_file = DocumentFile(filename=file_name, - knowledge_base_name=knowledge_base_name) + knowledge_base_name=knowledge_base_name, + kb_root_path=kb_root_path) if os.path.exists(kb_file.filepath): kb.update_doc(kb_file, not_refresh_vs_cache=not_refresh_vs_cache) return BaseResponse(code=200, msg=f"成功更新文件 {kb_file.filename}") @@ -203,6 +250,7 @@ async def update_doc( async def download_doc( knowledge_base_name: str = Query(..., examples=["samples"]), file_name: str = Query(..., examples=["test.txt"]), + kb_root_path: str =Body(""), ): ''' 下载知识库文档 @@ -210,13 +258,14 @@ async def download_doc( if not validate_kb_name(knowledge_base_name): return BaseResponse(code=403, msg="Don't attack me") - kb = KBServiceFactory.get_service_by_name(knowledge_base_name) + kb = KBServiceFactory.get_service_by_name(knowledge_base_name, None, kb_root_path) if kb is None: return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") try: kb_file = DocumentFile(filename=file_name, - knowledge_base_name=knowledge_base_name) + knowledge_base_name=knowledge_base_name, + kb_root_path=kb_root_path) if os.path.exists(kb_file.filepath): return FileResponse( @@ -233,8 +282,14 @@ async def download_doc( async def recreate_vector_store( knowledge_base_name: str = Body(..., examples=["samples"]), allow_empty_kb: bool = Body(True), - vs_type: str = Body(DEFAULT_VS_TYPE), - embed_model: str = Body(EMBEDDING_MODEL), + vs_type: str = Body("faiss"), + kb_root_path: str = Body(""), + api_key: bool = Body(..., examples=["samples"]), + api_base_url: bool = Body(..., examples=["samples"]), + embed_model: bool = Body(..., examples=["samples"]), + embed_model_path: bool = Body(..., examples=["samples"]), + model_device: bool = Body(..., examples=["samples"]), + embed_engine: bool = Body(..., examples=["samples"]), ): ''' recreate vector store from the content. @@ -242,18 +297,19 @@ async def recreate_vector_store( by default, get_service_by_name only return knowledge base in the info.db and having document files in it. set allow_empty_kb to True make it applied on empty knowledge base which it not in the info.db or having no documents. ''' - + embed_config: EmbedConfig = EmbedConfig(**locals()) async def output(): - kb = KBServiceFactory.get_service(knowledge_base_name, vs_type, embed_model) + kb = KBServiceFactory.get_service(knowledge_base_name, vs_type, embed_config, kb_root_path) if not kb.exists() and not allow_empty_kb: yield {"code": 404, "msg": f"未找到知识库 ‘{knowledge_base_name}’"} else: kb.create_kb() kb.clear_vs() - docs = list_docs_from_folder(knowledge_base_name) + docs = list_docs_from_folder(knowledge_base_name, kb_root_path) for i, doc in enumerate(docs): try: - kb_file = DocumentFile(doc, knowledge_base_name) + kb_file = DocumentFile(doc, knowledge_base_name, + kb_root_path=kb_root_path) yield json.dumps({ "code": 200, "msg": f"({i + 1} / {len(docs)}): {doc}", diff --git a/coagent/service/llm_api.py.bak b/coagent/service/llm_api.py.bak new file mode 100644 index 0000000..e69de29 diff --git a/coagent/service/llm_api.py.bak.bak b/coagent/service/llm_api.py.bak.bak new file mode 100644 index 0000000..b44bb91 --- /dev/null +++ b/coagent/service/llm_api.py.bak.bak @@ -0,0 +1,967 @@ +############################# Attention ######################## + +# Code copied from +# https://github.com/chatchat-space/Langchain-Chatchat/blob/master/server/llm_api.py + +################################################################# + + + +from multiprocessing import Process, Queue +import multiprocessing as mp +import sys +import os +from typing import List, Union, Dict +import httpx +import asyncio +import datetime +import argparse + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +sys.path.append(os.path.dirname(os.path.dirname(__file__))) +from configs.model_config import llm_model_dict, LLM_MODEL, LLM_DEVICE, LOG_PATH, logger, LLM_MODELs +from configs.server_config import ( + FSCHAT_CONTROLLER, FSCHAT_MODEL_WORKERS, FSCHAT_OPENAI_API +) +from coagent.service.utils import get_model_worker_config + +from coagent.utils.server_utils import ( + MakeFastAPIOffline, +) +from fastapi import FastAPI + +host_ip = "0.0.0.0" +controller_port = 20001 +model_worker_port = 20002 +openai_api_port = 8888 +base_url = "http://127.0.0.1:{}" + + +os.environ['PATH'] = os.environ.get("PATH", "") + os.pathsep +log_verbose = True + + +def set_httpx_timeout(timeout=60.0): + import httpx + httpx._config.DEFAULT_TIMEOUT_CONFIG.connect = timeout + httpx._config.DEFAULT_TIMEOUT_CONFIG.read = timeout + httpx._config.DEFAULT_TIMEOUT_CONFIG.write = timeout + + +def get_all_model_worker_configs() -> dict: + result = {} + model_names = set(FSCHAT_MODEL_WORKERS.keys()) + for name in model_names: + if name != "default": + result[name] = get_model_worker_config(name) + return result + + +def fschat_controller_address() -> str: + from configs.server_config import FSCHAT_CONTROLLER + + host = FSCHAT_CONTROLLER["host"] + if host == "0.0.0.0": + host = "127.0.0.1" + port = FSCHAT_CONTROLLER["port"] + return f"http://{host}:{port}" + + +def fschat_model_worker_address(model_name: str = LLM_MODEL) -> str: + if model := get_model_worker_config(model_name): # TODO: depends fastchat + host = model["host"] + if host == "0.0.0.0": + host = "127.0.0.1" + port = model["port"] + return f"http://{host}:{port}" + return "" + + +def fschat_openai_api_address() -> str: + from configs.server_config import FSCHAT_OPENAI_API + + host = FSCHAT_OPENAI_API["host"] + if host == "0.0.0.0": + host = "127.0.0.1" + port = FSCHAT_OPENAI_API["port"] + return f"http://{host}:{port}/v1" + + +def set_httpx_config( + timeout: float = 300, + proxy: Union[str, Dict] = None, +): + ''' + 设置httpx默认timeout。httpx默认timeout是5秒,在请求LLM回答时不够用。 + 将本项目相关服务加入无代理列表,避免fastchat的服务器请求错误。(windows下无效) + 对于chatgpt等在线API,如要使用代理需要手动配置。搜索引擎的代理如何处置还需考虑。 + ''' + + import httpx + import os + + httpx._config.DEFAULT_TIMEOUT_CONFIG.connect = timeout + httpx._config.DEFAULT_TIMEOUT_CONFIG.read = timeout + httpx._config.DEFAULT_TIMEOUT_CONFIG.write = timeout + + # 在进程范围内设置系统级代理 + proxies = {} + if isinstance(proxy, str): + for n in ["http", "https", "all"]: + proxies[n + "_proxy"] = proxy + elif isinstance(proxy, dict): + for n in ["http", "https", "all"]: + if p := proxy.get(n): + proxies[n + "_proxy"] = p + elif p := proxy.get(n + "_proxy"): + proxies[n + "_proxy"] = p + + for k, v in proxies.items(): + os.environ[k] = v + + # set host to bypass proxy + no_proxy = [x.strip() for x in os.environ.get("no_proxy", "").split(",") if x.strip()] + no_proxy += [ + # do not use proxy for locahost + "http://127.0.0.1", + "http://localhost", + ] + # do not use proxy for user deployed fastchat servers + for x in [ + fschat_controller_address(), + fschat_model_worker_address(), + fschat_openai_api_address(), + ]: + host = ":".join(x.split(":")[:2]) + if host not in no_proxy: + no_proxy.append(host) + os.environ["NO_PROXY"] = ",".join(no_proxy) + + # TODO: 简单的清除系统代理不是个好的选择,影响太多。似乎修改代理服务器的bypass列表更好。 + # patch requests to use custom proxies instead of system settings + def _get_proxies(): + return proxies + + import urllib.request + urllib.request.getproxies = _get_proxies + + # 自动检查torch可用的设备。分布式部署时,不运行LLM的机器上可以不装torch + + +def get_httpx_client( + use_async: bool = False, + proxies: Union[str, Dict] = None, + timeout: float = 300, + **kwargs, +) -> Union[httpx.Client, httpx.AsyncClient]: + ''' + helper to get httpx client with default proxies that bypass local addesses. + ''' + default_proxies = { + # do not use proxy for locahost + "all://127.0.0.1": None, + "all://localhost": None, + } + # do not use proxy for user deployed fastchat servers + for x in [ + fschat_controller_address(), + fschat_model_worker_address(), + fschat_openai_api_address(), + ]: + host = ":".join(x.split(":")[:2]) + default_proxies.update({host: None}) + + # get proxies from system envionrent + # proxy not str empty string, None, False, 0, [] or {} + default_proxies.update({ + "http://": (os.environ.get("http_proxy") + if os.environ.get("http_proxy") and len(os.environ.get("http_proxy").strip()) + else None), + "https://": (os.environ.get("https_proxy") + if os.environ.get("https_proxy") and len(os.environ.get("https_proxy").strip()) + else None), + "all://": (os.environ.get("all_proxy") + if os.environ.get("all_proxy") and len(os.environ.get("all_proxy").strip()) + else None), + }) + for host in os.environ.get("no_proxy", "").split(","): + if host := host.strip(): + # default_proxies.update({host: None}) # Origin code + default_proxies.update({'all://' + host: None}) # PR 1838 fix, if not add 'all://', httpx will raise error + + # merge default proxies with user provided proxies + if isinstance(proxies, str): + proxies = {"all://": proxies} + + if isinstance(proxies, dict): + default_proxies.update(proxies) + + # construct Client + kwargs.update(timeout=timeout, proxies=default_proxies) + + if log_verbose: + logger.info(f'{get_httpx_client.__class__.__name__}:kwargs: {kwargs}') + + if use_async: + return httpx.AsyncClient(**kwargs) + else: + return httpx.Client(**kwargs) + + +def create_controller_app( + dispatch_method: str, + log_level: str = "INFO", +) -> FastAPI: + import fastchat.constants + fastchat.constants.LOGDIR = LOG_PATH + from fastchat.serve.controller import app, Controller, logger + logger.setLevel(log_level) + + controller = Controller(dispatch_method) + sys.modules["fastchat.serve.controller"].controller = controller + + MakeFastAPIOffline(app) + app.title = "FastChat Controller" + app._controller = controller + return app + + +def create_model_worker_app(log_level: str = "INFO", **kwargs) -> FastAPI: + """ + kwargs包含的字段如下: + host: + port: + model_names:[`model_name`] + controller_address: + worker_address: + + 对于Langchain支持的模型: + langchain_model:True + 不会使用fschat + 对于online_api: + online_api:True + worker_class: `provider` + 对于离线模型: + model_path: `model_name_or_path`,huggingface的repo-id或本地路径 + device:`LLM_DEVICE` + """ + import fastchat.constants + fastchat.constants.LOGDIR = LOG_PATH + import argparse + + parser = argparse.ArgumentParser() + args = parser.parse_args([]) + + for k, v in kwargs.items(): + setattr(args, k, v) + + logger.error(f"可用模型有哪些: {args.model_names}") + + if worker_class := kwargs.get("langchain_model"): #Langchian支持的模型不用做操作 + from fastchat.serve.base_model_worker import app + worker = "" + # 在线模型API + elif worker_class := kwargs.get("worker_class"): + from fastchat.serve.base_model_worker import app + + worker = worker_class(model_names=args.model_names, + controller_addr=args.controller_address, + worker_addr=args.worker_address) + # sys.modules["fastchat.serve.base_model_worker"].worker = worker + sys.modules["fastchat.serve.base_model_worker"].logger.setLevel(log_level) + # 本地模型 + else: + from configs.model_config import VLLM_MODEL_DICT + # if kwargs["model_names"][0] in VLLM_MODEL_DICT and args.infer_turbo == "vllm": + if kwargs["model_names"][0] in VLLM_MODEL_DICT: + import fastchat.serve.vllm_worker + from fastchat.serve.vllm_worker import VLLMWorker, app, worker_id + from vllm import AsyncLLMEngine + from vllm.engine.arg_utils import AsyncEngineArgs,EngineArgs + + args.tokenizer = args.model_path # 如果tokenizer与model_path不一致在此处添加 + args.tokenizer_mode = 'auto' + args.trust_remote_code= True + args.download_dir= None + args.load_format = 'auto' + args.dtype = 'auto' + args.seed = 0 + args.worker_use_ray = False + args.pipeline_parallel_size = 1 + args.tensor_parallel_size = 1 + args.block_size = 16 + args.swap_space = 4 # GiB + args.gpu_memory_utilization = 0.90 + args.max_num_batched_tokens = None # 一个批次中的最大令牌(tokens)数量,这个取决于你的显卡和大模型设置,设置太大显存会不够 + args.max_num_seqs = 256 + args.disable_log_stats = False + args.conv_template = None + args.limit_worker_concurrency = 5 + args.no_register = False + args.num_gpus = 1 # vllm worker的切分是tensor并行,这里填写显卡的数量 + args.engine_use_ray = False + args.disable_log_requests = False + + # 0.2.1 vllm后要加的参数, 但是这里不需要 + args.max_model_len = None + args.revision = None + args.quantization = None + args.max_log_len = None + args.tokenizer_revision = None + + # 0.2.2 vllm需要新加的参数 + args.max_paddings = 256 + + if args.model_path: + args.model = args.model_path + if args.num_gpus > 1: + args.tensor_parallel_size = args.num_gpus + + for k, v in kwargs.items(): + setattr(args, k, v) + + engine_args = AsyncEngineArgs.from_cli_args(args) + engine = AsyncLLMEngine.from_engine_args(engine_args) + + worker = VLLMWorker( + controller_addr = args.controller_address, + worker_addr = args.worker_address, + worker_id = worker_id, + model_path = args.model_path, + model_names = args.model_names, + limit_worker_concurrency = args.limit_worker_concurrency, + no_register = args.no_register, + llm_engine = engine, + conv_template = args.conv_template, + ) + sys.modules["fastchat.serve.vllm_worker"].engine = engine + sys.modules["fastchat.serve.vllm_worker"].worker = worker + sys.modules["fastchat.serve.vllm_worker"].logger.setLevel(log_level) + + else: + from fastchat.serve.model_worker import app, GptqConfig, AWQConfig, ModelWorker, worker_id + + args.gpus = "0" # GPU的编号,如果有多个GPU,可以设置为"0,1,2,3" + args.max_gpu_memory = "22GiB" + args.num_gpus = 1 # model worker的切分是model并行,这里填写显卡的数量 + + args.load_8bit = False + args.cpu_offloading = None + args.gptq_ckpt = None + args.gptq_wbits = 16 + args.gptq_groupsize = -1 + args.gptq_act_order = False + args.awq_ckpt = None + args.awq_wbits = 16 + args.awq_groupsize = -1 + args.model_names = [""] + args.conv_template = None + args.limit_worker_concurrency = 5 + args.stream_interval = 2 + args.no_register = False + args.embed_in_truncate = False + for k, v in kwargs.items(): + setattr(args, k, v) + if args.gpus: + if args.num_gpus is None: + args.num_gpus = len(args.gpus.split(',')) + if len(args.gpus.split(",")) < args.num_gpus: + raise ValueError( + f"Larger --num-gpus ({args.num_gpus}) than --gpus {args.gpus}!" + ) + os.environ["CUDA_VISIBLE_DEVICES"] = args.gpus + gptq_config = GptqConfig( + ckpt=args.gptq_ckpt or args.model_path, + wbits=args.gptq_wbits, + groupsize=args.gptq_groupsize, + act_order=args.gptq_act_order, + ) + awq_config = AWQConfig( + ckpt=args.awq_ckpt or args.model_path, + wbits=args.awq_wbits, + groupsize=args.awq_groupsize, + ) + + worker = ModelWorker( + controller_addr=args.controller_address, + worker_addr=args.worker_address, + worker_id=worker_id, + model_path=args.model_path, + model_names=args.model_names, + limit_worker_concurrency=args.limit_worker_concurrency, + no_register=args.no_register, + device=args.device, + num_gpus=args.num_gpus, + max_gpu_memory=args.max_gpu_memory, + load_8bit=args.load_8bit, + cpu_offloading=args.cpu_offloading, + gptq_config=gptq_config, + awq_config=awq_config, + stream_interval=args.stream_interval, + conv_template=args.conv_template, + embed_in_truncate=args.embed_in_truncate, + ) + sys.modules["fastchat.serve.model_worker"].args = args + sys.modules["fastchat.serve.model_worker"].gptq_config = gptq_config + # sys.modules["fastchat.serve.model_worker"].worker = worker + sys.modules["fastchat.serve.model_worker"].logger.setLevel(log_level) + + MakeFastAPIOffline(app) + app.title = f"FastChat LLM Server ({args.model_names[0]})" + app._worker = worker + return app + + +def create_openai_api_app( + controller_address: str, + api_keys: List = [], + log_level: str = "INFO", +) -> FastAPI: + import fastchat.constants + fastchat.constants.LOGDIR = LOG_PATH + from fastchat.serve.openai_api_server import app, CORSMiddleware, app_settings + from fastchat.utils import build_logger + logger = build_logger("openai_api", "openai_api.log") + logger.setLevel(log_level) + + app.add_middleware( + CORSMiddleware, + allow_credentials=True, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], + ) + + sys.modules["fastchat.serve.openai_api_server"].logger = logger + app_settings.controller_address = controller_address + app_settings.api_keys = api_keys + + MakeFastAPIOffline(app) + app.title = "FastChat OpeanAI API Server" + return app + + +def _set_app_event(app: FastAPI, started_event: mp.Event = None): + @app.on_event("startup") + async def on_startup(): + if started_event is not None: + started_event.set() + + +def run_controller(log_level: str = "INFO", started_event: mp.Event = None): + import uvicorn + import httpx + from fastapi import Body + import time + import sys + # from server.utils import set_httpx_config + set_httpx_config() + + app = create_controller_app( + dispatch_method=FSCHAT_CONTROLLER.get("dispatch_method"), + log_level=log_level, + ) + _set_app_event(app, started_event) + + # add interface to release and load model worker + @app.post("/release_worker") + def release_worker( + model_name: str = Body(..., description="要释放模型的名称", samples=["chatglm-6b"]), + # worker_address: str = Body(None, description="要释放模型的地址,与名称二选一", samples=[FSCHAT_CONTROLLER_address()]), + new_model_name: str = Body(None, description="释放后加载该模型"), + keep_origin: bool = Body(False, description="不释放原模型,加载新模型") + ) -> Dict: + available_models = app._controller.list_models() + if new_model_name in available_models: + msg = f"要切换的LLM模型 {new_model_name} 已经存在" + logger.info(msg) + return {"code": 500, "msg": msg} + + if new_model_name: + logger.info(f"开始切换LLM模型:从 {model_name} 到 {new_model_name}") + else: + logger.info(f"即将停止LLM模型: {model_name}") + + if model_name not in available_models: + msg = f"the model {model_name} is not available" + logger.error(msg) + return {"code": 500, "msg": msg} + + worker_address = app._controller.get_worker_address(model_name) + if not worker_address: + msg = f"can not find model_worker address for {model_name}" + logger.error(msg) + return {"code": 500, "msg": msg} + + with get_httpx_client() as client: + r = client.post(worker_address + "/release", + json={"new_model_name": new_model_name, "keep_origin": keep_origin}) + if r.status_code != 200: + msg = f"failed to release model: {model_name}" + logger.error(msg) + return {"code": 500, "msg": msg} + + if new_model_name: + timer = 300 # wait for new model_worker register + while timer > 0: + models = app._controller.list_models() + if new_model_name in models: + break + time.sleep(1) + timer -= 1 + if timer > 0: + msg = f"sucess change model from {model_name} to {new_model_name}" + logger.info(msg) + return {"code": 200, "msg": msg} + else: + msg = f"failed change model from {model_name} to {new_model_name}" + logger.error(msg) + return {"code": 500, "msg": msg} + else: + msg = f"sucess to release model: {model_name}" + logger.info(msg) + return {"code": 200, "msg": msg} + + host = FSCHAT_CONTROLLER["host"] + port = FSCHAT_CONTROLLER["port"] + + if log_level == "ERROR": + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + + uvicorn.run(app, host=host, port=port, log_level=log_level.lower()) + + +def run_model_worker( + model_name: str = LLM_MODEL, + controller_address: str = "", + log_level: str = "INFO", + q: mp.Queue = None, + started_event: mp.Event = None, +): + import uvicorn + from fastapi import Body + import sys + set_httpx_config() + + kwargs = get_model_worker_config(model_name) + host = kwargs.pop("host") + port = kwargs.pop("port") + kwargs["model_names"] = [model_name] + kwargs["controller_address"] = controller_address or fschat_controller_address() + kwargs["worker_address"] = fschat_model_worker_address(model_name) + model_path = kwargs.get("model_path", "") + kwargs["model_path"] = model_path + # kwargs["gptq_wbits"] = 4 # int4 模型试用这个参数 + + app = create_model_worker_app(log_level=log_level, **kwargs) + _set_app_event(app, started_event) + if log_level == "ERROR": + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + + # add interface to release and load model + @app.post("/release") + def release_model( + new_model_name: str = Body(None, description="释放后加载该模型"), + keep_origin: bool = Body(False, description="不释放原模型,加载新模型") + ) -> Dict: + if keep_origin: + if new_model_name: + q.put([model_name, "start", new_model_name]) + else: + if new_model_name: + q.put([model_name, "replace", new_model_name]) + else: + q.put([model_name, "stop", None]) + return {"code": 200, "msg": "done"} + + uvicorn.run(app, host=host, port=port, log_level=log_level.lower()) + + +def run_openai_api(log_level: str = "INFO", started_event: mp.Event = None): + import uvicorn + import sys + set_httpx_config() + + controller_addr = fschat_controller_address() + app = create_openai_api_app(controller_addr, log_level=log_level) # TODO: not support keys yet. + _set_app_event(app, started_event) + + host = FSCHAT_OPENAI_API["host"] + port = FSCHAT_OPENAI_API["port"] + if log_level == "ERROR": + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + uvicorn.run(app, host=host, port=port) + + +def parse_args() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser() + parser.add_argument( + "-a", + "--all-webui", + action="store_true", + help="run fastchat's controller/openai_api/model_worker servers, run api.py and webui.py", + dest="all_webui", + ) + parser.add_argument( + "--all-api", + action="store_true", + help="run fastchat's controller/openai_api/model_worker servers, run api.py", + dest="all_api", + ) + parser.add_argument( + "--llm-api", + action="store_true", + help="run fastchat's controller/openai_api/model_worker servers", + dest="llm_api", + ) + parser.add_argument( + "-o", + "--openai-api", + action="store_true", + help="run fastchat's controller/openai_api servers", + dest="openai_api", + ) + parser.add_argument( + "-m", + "--model-worker", + action="store_true", + help="run fastchat's model_worker server with specified model name. " + "specify --model-name if not using default LLM_MODELS", + dest="model_worker", + ) + parser.add_argument( + "-n", + "--model-name", + type=str, + nargs="+", + default=LLM_MODELs, + help="specify model name for model worker. " + "add addition names with space seperated to start multiple model workers.", + dest="model_name", + ) + parser.add_argument( + "-c", + "--controller", + type=str, + help="specify controller address the worker is registered to. default is FSCHAT_CONTROLLER", + dest="controller_address", + ) + parser.add_argument( + "--api", + action="store_true", + help="run api.py server", + dest="api", + ) + parser.add_argument( + "-p", + "--api-worker", + action="store_true", + help="run online model api such as zhipuai", + dest="api_worker", + ) + parser.add_argument( + "-w", + "--webui", + action="store_true", + help="run webui.py server", + dest="webui", + ) + parser.add_argument( + "-q", + "--quiet", + action="store_true", + help="减少fastchat服务log信息", + dest="quiet", + ) + parser.add_argument( + "-i", + "--lite", + action="store_true", + help="以Lite模式运行:仅支持在线API的LLM对话、搜索引擎对话", + dest="lite", + ) + args = parser.parse_args() + return args, parser + + +def dump_server_info(after_start=False, args=None): + import platform + import langchain + import fastchat + + print("\n") + print("=" * 30 + "Langchain-Chatchat Configuration" + "=" * 30) + print(f"操作系统:{platform.platform()}.") + print(f"python版本:{sys.version}") + print(f"langchain版本:{langchain.__version__}. fastchat版本:{fastchat.__version__}") + print("\n") + + models = LLM_MODELs + if args and args.model_name: + models = args.model_name + + print(f"当前启动的LLM模型:{models} @ {LLM_DEVICE}") + + for model in models: + print(get_model_worker_config(model)) + + if after_start: + print("\n") + print(f"服务端运行信息:") + print(f" OpenAI API Server: {fschat_openai_api_address()}") + print("\n") + + +async def start_main_server(): + import time + import signal + + def handler(signalname): + """ + Python 3.9 has `signal.strsignal(signalnum)` so this closure would not be needed. + Also, 3.8 includes `signal.valid_signals()` that can be used to create a mapping for the same purpose. + """ + def f(signal_received, frame): + raise KeyboardInterrupt(f"{signalname} received") + return f + + # This will be inherited by the child process if it is forked (not spawned) + signal.signal(signal.SIGINT, handler("SIGINT")) + signal.signal(signal.SIGTERM, handler("SIGTERM")) + + mp.set_start_method("spawn") + manager = mp.Manager() + run_mode = None + + queue = manager.Queue() + args, parser = parse_args() + logger.debug(f"args: {args}") + + dump_server_info(args=args) + + if len(sys.argv) > 1: + logger.info(f"正在启动服务:") + logger.info(f"如需查看 llm_api 日志,请前往 {LOG_PATH}") + + processes = {"online_api": {}, "model_worker": {}} + + def process_count(): + return len(processes) + len(processes["online_api"]) + len(processes["model_worker"]) - 2 + + if args.quiet or not log_verbose: + log_level = "ERROR" + else: + log_level = "INFO" + + controller_started = manager.Event() + process = Process( + target=run_controller, + name=f"controller", + kwargs=dict(log_level=log_level, started_event=controller_started), + daemon=True, + ) + processes["controller"] = process + + process = Process( + target=run_openai_api, + name=f"openai_api", + daemon=True, + ) + processes["openai_api"] = process + + model_worker_started = [] + for model_name in args.model_name: + config = get_model_worker_config(model_name) + if not config.get("online_api"): + e = manager.Event() + model_worker_started.append(e) + process = Process( + target=run_model_worker, + name=f"model_worker - {model_name}", + kwargs=dict(model_name=model_name, + controller_address=args.controller_address, + log_level=log_level, + q=queue, + started_event=e), + daemon=True, + ) + processes["model_worker"][model_name] = process + + + for model_name in args.model_name: + config = get_model_worker_config(model_name) + logger.error(f"config: {config}, {model_name}, {FSCHAT_MODEL_WORKERS.keys()}") + if (config.get("online_api") + and config.get("worker_class") + and model_name in FSCHAT_MODEL_WORKERS): + e = manager.Event() + model_worker_started.append(e) + process = Process( + target=run_model_worker, + name=f"api_worker - {model_name}", + kwargs=dict(model_name=model_name, + controller_address=args.controller_address, + log_level=log_level, + q=queue, + started_event=e), + daemon=True, + ) + processes["online_api"][model_name] = process + + + if process_count() == 0: + parser.print_help() + else: + try: + # 保证任务收到SIGINT后,能够正常退出 + if p:= processes.get("controller"): + p.start() + p.name = f"{p.name} ({p.pid})" + controller_started.wait() # 等待controller启动完成 + + if p:= processes.get("openai_api"): + p.start() + p.name = f"{p.name} ({p.pid})" + + for n, p in processes.get("model_worker", {}).items(): + p.start() + p.name = f"{p.name} ({p.pid})" + + for n, p in processes.get("online_api", []).items(): + p.start() + p.name = f"{p.name} ({p.pid})" + + # 等待所有model_worker启动完成 + for e in model_worker_started: + e.wait() + + dump_server_info(after_start=True, args=args) + + while True: + cmd = queue.get() # 收到切换模型的消息 + e = manager.Event() + if isinstance(cmd, list): + model_name, cmd, new_model_name = cmd + if cmd == "start": # 运行新模型 + logger.info(f"准备启动新模型进程:{new_model_name}") + process = Process( + target=run_model_worker, + name=f"model_worker - {new_model_name}", + kwargs=dict(model_name=new_model_name, + controller_address=args.controller_address, + log_level=log_level, + q=queue, + started_event=e), + daemon=True, + ) + process.start() + process.name = f"{process.name} ({process.pid})" + processes["model_worker"][new_model_name] = process + e.wait() + logger.info(f"成功启动新模型进程:{new_model_name}") + elif cmd == "stop": + if process := processes["model_worker"].get(model_name): + time.sleep(1) + process.terminate() + process.join() + logger.info(f"停止模型进程:{model_name}") + else: + logger.error(f"未找到模型进程:{model_name}") + elif cmd == "replace": + if process := processes["model_worker"].pop(model_name, None): + logger.info(f"停止模型进程:{model_name}") + start_time = datetime.now() + time.sleep(1) + process.terminate() + process.join() + process = Process( + target=run_model_worker, + name=f"model_worker - {new_model_name}", + kwargs=dict(model_name=new_model_name, + controller_address=args.controller_address, + log_level=log_level, + q=queue, + started_event=e), + daemon=True, + ) + process.start() + process.name = f"{process.name} ({process.pid})" + processes["model_worker"][new_model_name] = process + e.wait() + timing = datetime.now() - start_time + logger.info(f"成功启动新模型进程:{new_model_name}。用时:{timing}。") + else: + logger.error(f"未找到模型进程:{model_name}") + + + # for process in processes.get("model_worker", {}).values(): + # process.join() + # for process in processes.get("online_api", {}).values(): + # process.join() + + # for name, process in processes.items(): + # if name not in ["model_worker", "online_api"]: + # if isinstance(p, dict): + # for work_process in p.values(): + # work_process.join() + # else: + # process.join() + except Exception as e: + logger.error(e) + logger.warning("Caught KeyboardInterrupt! Setting stop event...") + finally: + # Send SIGINT if process doesn't exit quickly enough, and kill it as last resort + # .is_alive() also implicitly joins the process (good practice in linux) + # while alive_procs := [p for p in processes.values() if p.is_alive()]: + + for p in processes.values(): + logger.warning("Sending SIGKILL to %s", p) + # Queues and other inter-process communication primitives can break when + # process is killed, but we don't care here + + if isinstance(p, dict): + for process in p.values(): + process.kill() + else: + p.kill() + + for p in processes.values(): + logger.info("Process status: %s", p) + + +if __name__ == "__main__": + + if sys.version_info < (3, 10): + loop = asyncio.get_event_loop() + else: + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + + asyncio.set_event_loop(loop) + # 同步调用协程代码 + loop.run_until_complete(start_main_server()) + + +# 服务启动后接口调用示例: +# import openai +# openai.api_key = "EMPTY" # Not support yet +# openai.api_base = "http://localhost:8888/v1" + +# model = "chatglm2-6b" + +# # create a chat completion +# completion = openai.ChatCompletion.create( +# model=model, +# messages=[{"role": "user", "content": "Hello! What is your name?"}] +# ) +# # print the completion +# print(completion.choices[0].message.content) diff --git a/dev_opsgpt/service/migrate.py b/coagent/service/migrate.py similarity index 70% rename from dev_opsgpt/service/migrate.py rename to coagent/service/migrate.py index bf5ea4a..f5643b7 100644 --- a/dev_opsgpt/service/migrate.py +++ b/coagent/service/migrate.py @@ -1,19 +1,23 @@ import os from typing import Literal, Callable, Any -from configs.model_config import EMBEDDING_MODEL, DEFAULT_VS_TYPE +# from configs.model_config import EMBEDDING_MODEL, DEFAULT_VS_TYPE, KB_ROOT_PATH -from dev_opsgpt.orm.utils import DocumentFile -from dev_opsgpt.orm.commands import add_doc_to_db -from dev_opsgpt.utils.path_utils import * +from coagent.orm.utils import DocumentFile +from coagent.orm.commands import add_doc_to_db +from coagent.utils.path_utils import * from .service_factory import KBServiceFactory +''' +这个方法不存在作用,可无视 +''' def folder2db( kb_name: str, mode: Literal["recreate_vs", "fill_info_only", "update_in_db", "increament"], - vs_type: Literal["faiss", "milvus", "pg", "chromadb"] = DEFAULT_VS_TYPE, - embed_model: str = EMBEDDING_MODEL, + vs_type: Literal["faiss", "milvus", "pg", "chromadb"] = "faiss", + embed_model: str = "text2vec-base-chinese", + kb_root_path: str = "", callback_before: Callable = None, callback_after: Callable = None, ): @@ -30,10 +34,10 @@ def folder2db( if mode == "recreate_vs": kb.clear_vs() - docs = list_docs_from_folder(kb_name) + docs = list_docs_from_folder(kb_name, kb_root_path) for i, doc in enumerate(docs): try: - kb_file = DocumentFile(doc, kb_name) + kb_file = DocumentFile(doc, kb_name, kb_root_path) if callable(callback_before): callback_before(kb_file, i, docs) if i == len(docs) - 1: @@ -46,10 +50,10 @@ def folder2db( except Exception as e: print(e) elif mode == "fill_info_only": - docs = list_docs_from_folder(kb_name) + docs = list_docs_from_folder(kb_name, kb_root_path) for i, doc in enumerate(docs): try: - kb_file = DocumentFile(doc, kb_name) + kb_file = DocumentFile(doc, kb_name, kb_root_path) if callable(callback_before): callback_before(kb_file, i, docs) add_doc_to_db(kb_file) @@ -61,7 +65,7 @@ def folder2db( docs = kb.list_docs() for i, doc in enumerate(docs): try: - kb_file = DocumentFile(doc, kb_name) + kb_file = DocumentFile(doc, kb_name, kb_root_path) if callable(callback_before): callback_before(kb_file, i, docs) if i == len(docs) - 1: @@ -75,11 +79,11 @@ def folder2db( print(e) elif mode == "increament": db_docs = kb.list_docs() - folder_docs = list_docs_from_folder(kb_name) + folder_docs = list_docs_from_folder(kb_name, kb_root_path) docs = list(set(folder_docs) - set(db_docs)) for i, doc in enumerate(docs): try: - kb_file = DocumentFile(doc, kb_name) + kb_file = DocumentFile(doc, kb_name, kb_root_path) if callable(callback_before): callback_before(kb_file, i, docs) if i == len(docs) - 1: @@ -96,41 +100,42 @@ def folder2db( def recreate_all_vs( - vs_type: Literal["faiss", "milvus", "pg", "chromadb"] = DEFAULT_VS_TYPE, - embed_mode: str = EMBEDDING_MODEL, + vs_type: Literal["faiss", "milvus", "pg", "chromadb"] = "faiss", + embed_mode: str = "text2vec-base-chinese", + kb_root_path: str = "", **kwargs: Any, ): ''' used to recreate a vector store or change current vector store to another type or embed_model ''' - for kb_name in list_kbs_from_folder(): - folder2db(kb_name, "recreate_vs", vs_type, embed_mode, **kwargs) + for kb_name in list_kbs_from_folder(kb_root_path): + folder2db(kb_name, "recreate_vs", vs_type, embed_mode, kb_root_path, **kwargs) -def prune_db_docs(kb_name: str): +def prune_db_docs(kb_name: str, kb_root_path: str = "",): ''' delete docs in database that not existed in local folder. it is used to delete database docs after user deleted some doc files in file browser ''' - kb = KBServiceFactory.get_service_by_name(kb_name) + kb = KBServiceFactory.get_service_by_name(kb_name, kb_root_path) if kb.exists(): docs_in_db = kb.list_docs() - docs_in_folder = list_docs_from_folder(kb_name) + docs_in_folder = list_docs_from_folder(kb_name, kb_root_path) docs = list(set(docs_in_db) - set(docs_in_folder)) for doc in docs: - kb.delete_doc(DocumentFile(doc, kb_name)) + kb.delete_doc(DocumentFile(doc, kb_name, kb_root_path)) return docs -def prune_folder_docs(kb_name: str): +def prune_folder_docs(kb_name: str, kb_root_path=""): ''' delete doc files in local folder that not existed in database. is is used to free local disk space by delete unused doc files. ''' - kb = KBServiceFactory.get_service_by_name(kb_name) + kb = KBServiceFactory.get_service_by_name(kb_name, kb_root_path) if kb.exists(): docs_in_db = kb.list_docs() - docs_in_folder = list_docs_from_folder(kb_name) + docs_in_folder = list_docs_from_folder(kb_name, kb_root_path) docs = list(set(docs_in_folder) - set(docs_in_db)) for doc in docs: - os.remove(get_file_path(kb_name, doc)) + os.remove(get_file_path(kb_name, doc, kb_root_path)) return docs diff --git a/coagent/service/sdfile_api.py.bak b/coagent/service/sdfile_api.py.bak new file mode 100644 index 0000000..8f601f4 --- /dev/null +++ b/coagent/service/sdfile_api.py.bak @@ -0,0 +1,132 @@ +import sys, os, json, traceback, uvicorn, argparse + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from loguru import logger + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import StreamingResponse, FileResponse +from fastapi import File, UploadFile + +from coagent.utils.server_utils import BaseResponse, ListResponse, DataResponse +# from configs.server_config import OPEN_CROSS_DOMAIN, SDFILE_API_SERVER +from configs.model_config import ( + JUPYTER_WORK_PATH +) + +VERSION = "v0.1.0" + +async def sd_upload_file(file: UploadFile = File(...), work_dir: str = JUPYTER_WORK_PATH): + # 保存上传的文件到服务器 + try: + content = await file.read() + with open(os.path.join(work_dir, file.filename), "wb") as f: + f.write(content) + return {"data": True} + except: + return {"data": False} + + +async def sd_download_file(filename: str, save_filename: str = "filename_to_download.ext", work_dir: str = JUPYTER_WORK_PATH): + # 从服务器下载文件 + logger.debug(f"{os.path.join(work_dir, filename)}") + return {"data": os.path.join(work_dir, filename), "filename": save_filename} + # return {"data": FileResponse(os.path.join(work_dir, filename), filename=save_filename)} + + +async def sd_list_files(work_dir: str = JUPYTER_WORK_PATH): + # 去除目录 + return {"data": os.listdir(work_dir)} + + +async def sd_delete_file(filename: str, work_dir: str = JUPYTER_WORK_PATH): + # 去除目录 + try: + os.remove(os.path.join(work_dir, filename)) + return {"data": True} + except: + return {"data": False} + + +def create_app(open_cross_domain, version=VERSION): + app = FastAPI( + title="DevOps-ChatBot API Server", + version=version + ) + # MakeFastAPIOffline(app) + # Add CORS middleware to allow all origins + # 在config.py中设置OPEN_DOMAIN=True,允许跨域 + # set OPEN_DOMAIN=True in config.py to allow cross-domain + if open_cross_domain: + # if OPEN_CROSS_DOMAIN: + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + app.post("/sdfiles/upload", + tags=["files upload and download"], + response_model=BaseResponse, + summary="上传文件到沙盒" + )(sd_upload_file) + + app.get("/sdfiles/download", + tags=["files upload and download"], + response_model=DataResponse, + summary="从沙盒下载文件" + )(sd_download_file) + + app.get("/sdfiles/list", + tags=["files upload and download"], + response_model=ListResponse, + summary="从沙盒工作目录展示文件" + )(sd_list_files) + + app.get("/sdfiles/delete", + tags=["files upload and download"], + response_model=BaseResponse, + summary="从沙盒工作目录中删除文件" + )(sd_delete_file) + return app + + + +def run_api(host, port, open_cross_domain, **kwargs): + app = create_app(open_cross_domain) + if kwargs.get("ssl_keyfile") and kwargs.get("ssl_certfile"): + uvicorn.run(app, + host=host, + port=port, + ssl_keyfile=kwargs.get("ssl_keyfile"), + ssl_certfile=kwargs.get("ssl_certfile"), + ) + else: + uvicorn.run(app, host=host, port=port) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(prog='DevOps-ChatBot', + description='About DevOps-ChatBot, local knowledge based LLM with langchain' + ' | 基于本地知识库的 LLM 问答') + parser.add_argument("--host", type=str, default="0.0.0.0") + parser.add_argument("--port", type=int, default="7862") + # parser.add_argument("--port", type=int, default=SDFILE_API_SERVER["port"]) + parser.add_argument("--open_cross_domain", type=bool, default=False) + parser.add_argument("--ssl_keyfile", type=str) + parser.add_argument("--ssl_certfile", type=str) + # 初始化消息 + args = parser.parse_args() + args_dict = vars(args) + run_api(host=args.host, + port=args.port, + open_cross_domain=args.open_cross_domain, + ssl_keyfile=args.ssl_keyfile, + ssl_certfile=args.ssl_certfile, + ) \ No newline at end of file diff --git a/dev_opsgpt/service/service_factory.py b/coagent/service/service_factory.py similarity index 75% rename from dev_opsgpt/service/service_factory.py rename to coagent/service/service_factory.py index 59414de..c5ab2d5 100644 --- a/dev_opsgpt/service/service_factory.py +++ b/coagent/service/service_factory.py @@ -1,13 +1,14 @@ from typing import List, Union, Dict import os -from configs.model_config import EMBEDDING_MODEL +# from configs.model_config import EMBEDDING_MODEL, KB_ROOT_PATH +from coagent.base_configs.env_config import KB_ROOT_PATH from .faiss_db_service import FaissKBService from .base_service import KBService, SupportedVSType -from dev_opsgpt.orm.commands import * -from dev_opsgpt.utils.path_utils import * - +from coagent.orm.commands import * +from coagent.utils.path_utils import * +from coagent.llm_models.llm_config import EmbedConfig class KBServiceFactory: @@ -15,12 +16,14 @@ class KBServiceFactory: @staticmethod def get_service(kb_name: str, vector_store_type: Union[str, SupportedVSType], - embed_model: str = EMBEDDING_MODEL, + # embed_model: str = "text2vec-base-chinese", + embed_config: EmbedConfig, + kb_root_path: str = KB_ROOT_PATH ) -> KBService: if isinstance(vector_store_type, str): vector_store_type = getattr(SupportedVSType, vector_store_type.upper()) if SupportedVSType.FAISS == vector_store_type: - return FaissKBService(kb_name, embed_model=embed_model) + return FaissKBService(kb_name, embed_config=embed_config, kb_root_path=kb_root_path) # if SupportedVSType.PG == vector_store_type: # from server.knowledge_base.kb_service.pg_kb_service import PGKBService # return PGKBService(kb_name, embed_model=embed_model) @@ -32,20 +35,22 @@ class KBServiceFactory: # return DefaultKBService(kb_name) @staticmethod - def get_service_by_name(kb_name: str + def get_service_by_name(kb_name: str, + embed_config: EmbedConfig, + kb_root_path: str = KB_ROOT_PATH ) -> KBService: _, vs_type, embed_model = load_kb_from_db(kb_name) - if vs_type is None and os.path.isdir(get_kb_path(kb_name)): # faiss knowledge base not in db + if vs_type is None and os.path.isdir(get_kb_path(kb_name, kb_root_path)): # faiss knowledge base not in db vs_type = "faiss" - return KBServiceFactory.get_service(kb_name, vs_type, embed_model) + return KBServiceFactory.get_service(kb_name, vs_type, embed_config, kb_root_path) @staticmethod def get_default(): - return KBServiceFactory.get_service("default", SupportedVSType.DEFAULT) + return KBServiceFactory.get_service("default", SupportedVSType.DEFAULT, EmbedConfig(), kb_root_path=KB_ROOT_PATH) -def get_kb_details() -> List[Dict]: - kbs_in_folder = list_kbs_from_folder() +def get_kb_details(kb_root_path: str) -> List[Dict]: + kbs_in_folder = list_kbs_from_folder(kb_root_path) kbs_in_db = KBService.list_kbs() result = {} @@ -71,6 +76,8 @@ def get_kb_details() -> List[Dict]: result[kb] = kb_detail data = [] + # filter not in db' knowledge base docs + result = {k: v for k, v in result.items() if v.get("in_db", False)} for i, v in enumerate(result.values()): v['No'] = i + 1 data.append(v) @@ -105,9 +112,9 @@ def get_cb_details_by_cb_name(cb_name) -> dict: -def get_kb_doc_details(kb_name: str) -> List[Dict]: - kb = KBServiceFactory.get_service_by_name(kb_name) - docs_in_folder = list_docs_from_folder(kb_name) +def get_kb_doc_details(kb_name: str, kb_root_path) -> List[Dict]: + kb = KBServiceFactory.get_service_by_name(kb_name, kb_root_path) + docs_in_folder = list_docs_from_folder(kb_name, kb_root_path) docs_in_db = kb.list_docs() result = {} diff --git a/dev_opsgpt/text_splitter/__init__.py b/coagent/text_splitter/__init__.py similarity index 100% rename from dev_opsgpt/text_splitter/__init__.py rename to coagent/text_splitter/__init__.py diff --git a/dev_opsgpt/text_splitter/langchain_splitter.py b/coagent/text_splitter/langchain_splitter.py similarity index 73% rename from dev_opsgpt/text_splitter/langchain_splitter.py rename to coagent/text_splitter/langchain_splitter.py index 5095267..4b53025 100644 --- a/dev_opsgpt/text_splitter/langchain_splitter.py +++ b/coagent/text_splitter/langchain_splitter.py @@ -7,23 +7,27 @@ from langchain.text_splitter import ( SpacyTextSplitter, RecursiveCharacterTextSplitter ) -from configs.model_config import ( - CHUNK_SIZE, - OVERLAP_SIZE, - ZH_TITLE_ENHANCE -) -from dev_opsgpt.utils.path_utils import * +# from configs.model_config import ( +# CHUNK_SIZE, +# OVERLAP_SIZE, +# ZH_TITLE_ENHANCE +# ) +from coagent.utils.path_utils import * class LCTextSplitter: '''langchain textsplitter 执行file2text''' def __init__( - self, filepath: str, text_splitter_name: str = None + self, filepath: str, text_splitter_name: str = None, + chunk_size: int = 500, + overlap_size: int = 50 ): self.filepath = filepath self.ext = os.path.splitext(filepath)[-1].lower() self.text_splitter_name = text_splitter_name + self.chunk_size = chunk_size + self.overlap_size = overlap_size if self.ext not in SUPPORTED_EXTS: raise ValueError(f"暂未支持的文件格式 {self.ext}") self.document_loader_name = get_LoaderClass(self.ext) @@ -34,7 +38,7 @@ class LCTextSplitter: if self.document_loader_name in ["JSONLoader", "JSONLLoader"]: # docs = loader.load() docs = loader.load_and_split(text_splitter) - logger.debug(f"please check your file can be loaded, docs.lens {len(docs)}") + # logger.debug(f"please check your file can be loaded, docs.lens {len(docs)}") else: docs = loader.load_and_split(text_splitter) @@ -53,8 +57,8 @@ class LCTextSplitter: if self.text_splitter_name is None: text_splitter = SpacyTextSplitter( pipeline="zh_core_web_sm", - chunk_size=CHUNK_SIZE, - chunk_overlap=OVERLAP_SIZE, + chunk_size=self.chunk_size, + chunk_overlap=self.overlap_size, ) self.text_splitter_name = "SpacyTextSplitter" # elif self.document_loader_name in ["JSONLoader", "JSONLLoader"]: @@ -63,11 +67,11 @@ class LCTextSplitter: text_splitter_module = importlib.import_module('langchain.text_splitter') TextSplitter = getattr(text_splitter_module, self.text_splitter_name) text_splitter = TextSplitter( - chunk_size=CHUNK_SIZE, - chunk_overlap=OVERLAP_SIZE) + chunk_size=self.chunk_size, + chunk_overlap=self.overlap_size) except Exception as e: text_splitter = RecursiveCharacterTextSplitter( - chunk_size=CHUNK_SIZE, - chunk_overlap=OVERLAP_SIZE, + chunk_size=self.chunk_size, + chunk_overlap=self.overlap_size, ) return text_splitter diff --git a/coagent/text_splitter/utils.py b/coagent/text_splitter/utils.py new file mode 100644 index 0000000..e69de29 diff --git a/dev_opsgpt/tools/__init__.py b/coagent/tools/__init__.py similarity index 100% rename from dev_opsgpt/tools/__init__.py rename to coagent/tools/__init__.py diff --git a/dev_opsgpt/tools/abnormal_detection.py b/coagent/tools/abnormal_detection.py similarity index 100% rename from dev_opsgpt/tools/abnormal_detection.py rename to coagent/tools/abnormal_detection.py diff --git a/dev_opsgpt/tools/base_tool.py b/coagent/tools/base_tool.py similarity index 100% rename from dev_opsgpt/tools/base_tool.py rename to coagent/tools/base_tool.py diff --git a/dev_opsgpt/tools/cb_query_tool.py b/coagent/tools/cb_query_tool.py similarity index 65% rename from dev_opsgpt/tools/cb_query_tool.py rename to coagent/tools/cb_query_tool.py index 163a322..8025e92 100644 --- a/dev_opsgpt/tools/cb_query_tool.py +++ b/coagent/tools/cb_query_tool.py @@ -5,20 +5,14 @@ @time: 2023/11/2 下午4:41 @desc: ''' -import json -import os -import re from pydantic import BaseModel, Field -from typing import List, Dict -import requests -import numpy as np from loguru import logger -from configs.model_config import ( - CODE_SEARCH_TOP_K) +from coagent.llm_models import LLMConfig, EmbedConfig + from .base_tool import BaseToolModel -from dev_opsgpt.service.cb_api import search_code +from coagent.service.cb_api import search_code class CodeRetrieval(BaseToolModel): @@ -28,14 +22,14 @@ class CodeRetrieval(BaseToolModel): class ToolInputArgs(BaseModel): query: str = Field(..., description="检索的关键字或问题") code_base_name: str = Field(..., description="知识库名称", examples=["samples"]) - code_limit: int = Field(CODE_SEARCH_TOP_K, description="检索返回的数量") + code_limit: int = Field(1, description="检索返回的数量") class ToolOutputArgs(BaseModel): """Output for MetricsQuery.""" code: str = Field(..., description="检索代码") @classmethod - def run(cls, code_base_name, query, code_limit=CODE_SEARCH_TOP_K, history_node_list=[], search_type="tag"): + def run(cls, code_base_name, query, code_limit=1, history_node_list=[], search_type="tag", llm_config: LLMConfig=None, embed_config: EmbedConfig=None): """excute your tool!""" search_type = { @@ -48,7 +42,11 @@ class CodeRetrieval(BaseToolModel): }.get(search_type, 'tag') # default - codes = search_code(code_base_name, query, code_limit, search_type=search_type, history_node_list=history_node_list) + codes = search_code(code_base_name, query, code_limit, search_type=search_type, history_node_list=history_node_list, + embed_engine=embed_config.embed_engine, embed_model=embed_config.embed_model, embed_model_path=embed_config.embed_model_path, + model_device=embed_config.model_device, model_name=llm_config.model_name, temperature=llm_config.temperature, + api_base_url=llm_config.api_base_url, api_key=llm_config.api_key + ) return_codes = [] context = codes['context'] related_nodes = codes['related_vertices'] diff --git a/dev_opsgpt/tools/codechat_tools.py b/coagent/tools/codechat_tools.py similarity index 73% rename from dev_opsgpt/tools/codechat_tools.py rename to coagent/tools/codechat_tools.py index df05d74..22c3df7 100644 --- a/dev_opsgpt/tools/codechat_tools.py +++ b/coagent/tools/codechat_tools.py @@ -5,20 +5,13 @@ @time: 2023/12/14 上午10:24 @desc: ''' -import json -import os -import re from pydantic import BaseModel, Field -from typing import List, Dict -import requests -import numpy as np from loguru import logger -from configs.model_config import ( - CODE_SEARCH_TOP_K) +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig from .base_tool import BaseToolModel -from dev_opsgpt.service.cb_api import search_code, search_related_vertices, search_code_by_vertex +from coagent.service.cb_api import search_code, search_related_vertices, search_code_by_vertex # 问题进来 @@ -47,7 +40,7 @@ class CodeRetrievalSingle(BaseToolModel): vertex: str = Field(..., description="代码对应 id") @classmethod - def run(cls, code_base_name, query): + def run(cls, code_base_name, query, embed_config: EmbedConfig, llm_config: LLMConfig, **kargs): """excute your tool!""" search_type = 'description' @@ -55,7 +48,11 @@ class CodeRetrievalSingle(BaseToolModel): # default search_result = search_code(code_base_name, query, code_limit, search_type=search_type, - history_node_list=[]) + history_node_list=[], + embed_engine=embed_config.embed_engine, embed_model=embed_config.embed_model, embed_model_path=embed_config.embed_model_path, + model_device=embed_config.model_device, model_name=llm_config.model_name, temperature=llm_config.temperature, + api_base_url=llm_config.api_base_url, api_key=llm_config.api_key + ) logger.debug(search_result) code = search_result['context'] @@ -83,7 +80,7 @@ class RelatedVerticesRetrival(BaseToolModel): vertices: list = Field(..., description="相连节点名") @classmethod - def run(cls, code_base_name: str, vertex: str): + def run(cls, code_base_name: str, vertex: str, **kargs): """execute your tool!""" related_vertices = search_related_vertices(cb_name=code_base_name, vertex=vertex) logger.debug(f"related_vertices: {related_vertices}") @@ -104,7 +101,15 @@ class Vertex2Code(BaseToolModel): code: str = Field(..., description="代码名") @classmethod - def run(cls, code_base_name: str, vertex: str): + def run(cls, code_base_name: str, vertex: str, **kargs): """execute your tool!""" + # format vertex + if ',' in vertex: + vertex_list = vertex.split(',') + vertex = vertex_list[0].strip(' "') + else: + vertex = vertex.strip(' "') + + logger.info(f'vertex={vertex}') res = search_code_by_vertex(cb_name=code_base_name, vertex=vertex) return res \ No newline at end of file diff --git a/coagent/tools/docs_retrieval.py b/coagent/tools/docs_retrieval.py new file mode 100644 index 0000000..23aef35 --- /dev/null +++ b/coagent/tools/docs_retrieval.py @@ -0,0 +1,43 @@ +from pydantic import BaseModel, Field +from loguru import logger + +from coagent.llm_models.llm_config import EmbedConfig + +from .base_tool import BaseToolModel + + + +from coagent.service.kb_api import search_docs + + +class DocRetrieval(BaseToolModel): + name = "DocRetrieval" + description = "采用向量化对本地知识库进行检索" + + class ToolInputArgs(BaseModel): + query: str = Field(..., description="检索的关键字或问题") + knowledge_base_name: str = Field(..., description="知识库名称", examples=["samples"]) + search_top: int = Field(5, description="检索返回的数量") + score_threshold: float = Field(1.0, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1) + + class ToolOutputArgs(BaseModel): + """Output for MetricsQuery.""" + title: str = Field(..., description="检索网页标题") + snippet: str = Field(..., description="检索内容的判断") + link: str = Field(..., description="检索网页地址") + + @classmethod + def run(cls, query, knowledge_base_name, search_top=5, score_threshold=1.0, embed_config: EmbedConfig=EmbedConfig(), kb_root_path: str=""): + """excute your tool!""" + try: + docs = search_docs(query, knowledge_base_name, search_top, score_threshold, + kb_root_path=kb_root_path, embed_engine=embed_config.embed_engine, + embed_model=embed_config.embed_model, embed_model_path=embed_config.embed_model_path, + model_device=embed_config.model_device + ) + except Exception as e: + logger.exception(e) + return_docs = [] + for idx, doc in enumerate(docs): + return_docs.append({"index": idx, "snippet": doc.page_content, "title": doc.metadata.get("source"), "link": doc.metadata.get("source")}) + return return_docs diff --git a/dev_opsgpt/tools/duckduckgo_search.py b/coagent/tools/duckduckgo_search.py similarity index 89% rename from dev_opsgpt/tools/duckduckgo_search.py rename to coagent/tools/duckduckgo_search.py index b69e68e..badfa3c 100644 --- a/dev_opsgpt/tools/duckduckgo_search.py +++ b/coagent/tools/duckduckgo_search.py @@ -9,9 +9,6 @@ import numpy as np from loguru import logger from .base_tool import BaseToolModel -from configs.model_config import ( - PROMPT_TEMPLATE, SEARCH_ENGINE_TOP_K, BING_SUBSCRIPTION_KEY, BING_SEARCH_URL, - VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) from duckduckgo_search import DDGS @@ -37,7 +34,8 @@ class DDGSTool(BaseToolModel): @classmethod def run(cls, query, search_top, region="wt-wt", safesearch="moderate", timelimit=None, backend="api"): """excute your tool!""" - with DDGS(proxies=os.environ.get("DUCKDUCKGO_PROXY")) as ddgs: + with DDGS(proxies=os.environ.get("DUCKDUCKGO_PROXY"), timeout=20) as ddgs: + ddgs._session.headers["Referer"] = "" results = ddgs.text( query, region=region, diff --git a/dev_opsgpt/tools/metrics_query.py b/coagent/tools/metrics_query.py similarity index 100% rename from dev_opsgpt/tools/metrics_query.py rename to coagent/tools/metrics_query.py diff --git a/dev_opsgpt/tools/multiplier.py b/coagent/tools/multiplier.py similarity index 100% rename from dev_opsgpt/tools/multiplier.py rename to coagent/tools/multiplier.py diff --git a/dev_opsgpt/tools/ocr_tool.py b/coagent/tools/ocr_tool.py similarity index 86% rename from dev_opsgpt/tools/ocr_tool.py rename to coagent/tools/ocr_tool.py index f19d1e6..49c7831 100644 --- a/dev_opsgpt/tools/ocr_tool.py +++ b/coagent/tools/ocr_tool.py @@ -7,7 +7,7 @@ import os from loguru import logger from .base_tool import BaseToolModel -from configs.model_config import JUPYTER_WORK_PATH +# from configs.model_config import JUPYTER_WORK_PATH class BaiduOcrTool(BaseToolModel): @@ -91,6 +91,10 @@ class BaiduOcrTool(BaseToolModel): return str(requests.post(url, params=params).json().get("access_token")) @classmethod - def run(cls, image_name, image_path=JUPYTER_WORK_PATH, API_KEY=os.environ.get("BAIDU_OCR_API_KEY"), SECRET_KEY=os.environ.get("BAIDU_OCR_SECRET_KEY")): + def run(cls, image_name, image_path="", API_KEY=os.environ.get("BAIDU_OCR_API_KEY"), SECRET_KEY=os.environ.get("BAIDU_OCR_SECRET_KEY")): + if os.environ.get("BAIDU_OCR_API_KEY") is None: + raise EnvironmentError("Environment variable 'BAIDU_OCR_API_KEY' not set") + if os.environ.get("BAIDU_OCR_SECRET_KEY") is None: + raise EnvironmentError("Environment variable 'BAIDU_OCR_SECRET_KEY' not set") image_file = os.path.join(image_path, image_name) return cls.ocr_baidu_main(API_KEY, SECRET_KEY, image_file) \ No newline at end of file diff --git a/coagent/tools/sandbox.py b/coagent/tools/sandbox.py new file mode 100644 index 0000000..e69de29 diff --git a/dev_opsgpt/tools/stock_tool.py b/coagent/tools/stock_tool.py similarity index 94% rename from dev_opsgpt/tools/stock_tool.py rename to coagent/tools/stock_tool.py index 2f4e946..bf39b22 100644 --- a/dev_opsgpt/tools/stock_tool.py +++ b/coagent/tools/stock_tool.py @@ -9,11 +9,11 @@ import numpy as np from loguru import logger from .base_tool import BaseToolModel -from dev_opsgpt.utils.common_utils import read_json_file +from coagent.utils.common_utils import read_json_file - -cur_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -stock_infos = read_json_file(os.path.join(cur_dir, "../sources/tool_datas/stock.json")) +from .tool_datas.stock_data import stock_infos +# cur_dir = os.path.dirname(os.path.abspath(__file__)) +# stock_infos = read_json_file(os.path.join(cur_dir, "tool_datas/stock.json")) stock_dict = {i["mc"]: i["jys"]+i["dm"] for i in stock_infos} diff --git a/coagent/tools/tool_datas/__init__.py b/coagent/tools/tool_datas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/coagent/tools/tool_datas/stock_data.py b/coagent/tools/tool_datas/stock_data.py new file mode 100644 index 0000000..8a3197c --- /dev/null +++ b/coagent/tools/tool_datas/stock_data.py @@ -0,0 +1 @@ +stock_infos = [{"dm":"603062","mc":"N麦加","jys":"sh"},{"dm":"300507","mc":"苏奥传感","jys":"sz"},{"dm":"301202","mc":"朗威股份","jys":"sz"},{"dm":"300951","mc":"博硕科技","jys":"sz"},{"dm":"300608","mc":"思特奇","jys":"sz"},{"dm":"300063","mc":"天龙集团","jys":"sz"},{"dm":"300502","mc":"新易盛","jys":"sz"},{"dm":"300210","mc":"森远股份","jys":"sz"},{"dm":"300364","mc":"中文在线","jys":"sz"},{"dm":"688577","mc":"浙海德曼","jys":"sh"},{"dm":"300913","mc":"兆龙互连","jys":"sz"},{"dm":"300522","mc":"世名科技","jys":"sz"},{"dm":"301312","mc":"智立方","jys":"sz"},{"dm":"300480","mc":"光力科技","jys":"sz"},{"dm":"301191","mc":"菲菱科思","jys":"sz"},{"dm":"300551","mc":"古鳌科技","jys":"sz"},{"dm":"688629","mc":"华丰科技","jys":"sh"},{"dm":"688048","mc":"长光华芯","jys":"sh"},{"dm":"300883","mc":"龙利得","jys":"sz"},{"dm":"688031","mc":"星环科技-U","jys":"sh"},{"dm":"300684","mc":"中石科技","jys":"sz"},{"dm":"300570","mc":"太辰光","jys":"sz"},{"dm":"300694","mc":"蠡湖股份","jys":"sz"},{"dm":"000677","mc":"恒天海龙","jys":"sz"},{"dm":"601901","mc":"方正证券","jys":"sh"},{"dm":"002238","mc":"天威视讯","jys":"sz"},{"dm":"000829","mc":"天音控股","jys":"sz"},{"dm":"002712","mc":"思美传媒","jys":"sz"},{"dm":"600892","mc":"大晟文化","jys":"sh"},{"dm":"600592","mc":"龙溪股份","jys":"sh"},{"dm":"000056","mc":"皇庭国际","jys":"sz"},{"dm":"600630","mc":"龙头股份","jys":"sh"},{"dm":"603050","mc":"科林电气","jys":"sh"},{"dm":"603726","mc":"朗迪集团","jys":"sh"},{"dm":"600360","mc":"华微电子","jys":"sh"},{"dm":"002055","mc":"得润电子","jys":"sz"},{"dm":"001238","mc":"浙江正特","jys":"sz"},{"dm":"603729","mc":"龙韵股份","jys":"sh"},{"dm":"688328","mc":"深科达","jys":"sh"},{"dm":"603305","mc":"旭升集团","jys":"sh"},{"dm":"002222","mc":"福晶科技","jys":"sz"},{"dm":"603220","mc":"中贝通信","jys":"sh"},{"dm":"002615","mc":"哈尔斯","jys":"sz"},{"dm":"002889","mc":"东方嘉盛","jys":"sz"},{"dm":"000628","mc":"高新发展","jys":"sz"},{"dm":"600520","mc":"文一科技","jys":"sh"},{"dm":"603380","mc":"易德龙","jys":"sh"},{"dm":"605218","mc":"伟时电子","jys":"sh"},{"dm":"600692","mc":"亚通股份","jys":"sh"},{"dm":"600222","mc":"太龙药业","jys":"sh"},{"dm":"002981","mc":"朝阳科技","jys":"sz"},{"dm":"002692","mc":"远程股份","jys":"sz"},{"dm":"002313","mc":"日海智能","jys":"sz"},{"dm":"000026","mc":"飞亚达","jys":"sz"},{"dm":"603985","mc":"恒润股份","jys":"sh"},{"dm":"002229","mc":"鸿博股份","jys":"sz"},{"dm":"603266","mc":"天龙股份","jys":"sh"},{"dm":"000815","mc":"美利云","jys":"sz"},{"dm":"002988","mc":"豪美新材","jys":"sz"},{"dm":"600679","mc":"上海凤凰","jys":"sh"},{"dm":"002931","mc":"锋龙股份","jys":"sz"},{"dm":"603037","mc":"凯众股份","jys":"sh"},{"dm":"000828","mc":"东莞控股","jys":"sz"},{"dm":"605255","mc":"天普股份","jys":"sh"},{"dm":"603667","mc":"五洲新春","jys":"sh"},{"dm":"003015","mc":"日久光电","jys":"sz"},{"dm":"603003","mc":"龙宇股份","jys":"sh"},{"dm":"603390","mc":"通达电气","jys":"sh"},{"dm":"600816","mc":"建元信托","jys":"sh"},{"dm":"600331","mc":"宏达股份","jys":"sh"},{"dm":"002682","mc":"龙洲股份","jys":"sz"},{"dm":"688010","mc":"福光股份","jys":"sh"},{"dm":"300629","mc":"新劲刚","jys":"sz"},{"dm":"605577","mc":"龙版传媒","jys":"sh"},{"dm":"300264","mc":"佳创视讯","jys":"sz"},{"dm":"300475","mc":"香农芯创","jys":"sz"},{"dm":"688498","mc":"源杰科技","jys":"sh"},{"dm":"600131","mc":"国网信通","jys":"sh"},{"dm":"300757","mc":"罗博特科","jys":"sz"},{"dm":"300308","mc":"中际旭创","jys":"sz"},{"dm":"300657","mc":"弘信电子","jys":"sz"},{"dm":"300088","mc":"长信科技","jys":"sz"},{"dm":"300807","mc":"天迈科技","jys":"sz"},{"dm":"600133","mc":"东湖高新","jys":"sh"},{"dm":"688662","mc":"富信科技","jys":"sh"},{"dm":"002993","mc":"奥海科技","jys":"sz"},{"dm":"300701","mc":"森霸传感","jys":"sz"},{"dm":"688593","mc":"新相微","jys":"sh"},{"dm":"300620","mc":"光库科技","jys":"sz"},{"dm":"300272","mc":"开能健康","jys":"sz"},{"dm":"300820","mc":"英杰电气","jys":"sz"},{"dm":"300846","mc":"首都在线","jys":"sz"},{"dm":"301085","mc":"亚康股份","jys":"sz"},{"dm":"300523","mc":"辰安科技","jys":"sz"},{"dm":"300394","mc":"天孚通信","jys":"sz"},{"dm":"603353","mc":"和顺石油","jys":"sh"},{"dm":"688066","mc":"航天宏图","jys":"sh"},{"dm":"688313","mc":"仕佳光子","jys":"sh"},{"dm":"688115","mc":"思林杰","jys":"sh"},{"dm":"688141","mc":"杰华特","jys":"sh"},{"dm":"300976","mc":"达瑞电子","jys":"sz"},{"dm":"301387","mc":"光大同创","jys":"sz"},{"dm":"603629","mc":"利通电子","jys":"sh"},{"dm":"301360","mc":"荣旗科技","jys":"sz"},{"dm":"002929","mc":"润建股份","jys":"sz"},{"dm":"300211","mc":"亿通科技","jys":"sz"},{"dm":"300781","mc":"因赛集团","jys":"sz"},{"dm":"300293","mc":"蓝英装备","jys":"sz"},{"dm":"301489","mc":"思泉新材","jys":"sz"},{"dm":"300798","mc":"锦鸡股份","jys":"sz"},{"dm":"600159","mc":"大龙地产","jys":"sh"},{"dm":"002515","mc":"金字火腿","jys":"sz"},{"dm":"300101","mc":"振芯科技","jys":"sz"},{"dm":"301486","mc":"致尚科技","jys":"sz"},{"dm":"000925","mc":"众合科技","jys":"sz"},{"dm":"300032","mc":"金龙机电","jys":"sz"},{"dm":"301389","mc":"隆扬电子","jys":"sz"},{"dm":"603496","mc":"恒为科技","jys":"sh"},{"dm":"300691","mc":"联合光电","jys":"sz"},{"dm":"002281","mc":"光迅科技","jys":"sz"},{"dm":"601599","mc":"浙文影业","jys":"sh"},{"dm":"688167","mc":"炬光科技","jys":"sh"},{"dm":"301421","mc":"波长光电","jys":"sz"},{"dm":"002902","mc":"铭普光磁","jys":"sz"},{"dm":"603779","mc":"威龙股份","jys":"sh"},{"dm":"300678","mc":"中科信息","jys":"sz"},{"dm":"300912","mc":"凯龙高科","jys":"sz"},{"dm":"600071","mc":"凤凰光学","jys":"sh"},{"dm":"301262","mc":"海看股份","jys":"sz"},{"dm":"601595","mc":"上海电影","jys":"sh"},{"dm":"688668","mc":"鼎通科技","jys":"sh"},{"dm":"300603","mc":"立昂技术","jys":"sz"},{"dm":"688280","mc":"精进电动-UW","jys":"sh"},{"dm":"300025","mc":"华星创业","jys":"sz"},{"dm":"603626","mc":"科森科技","jys":"sh"},{"dm":"688311","mc":"盟升电子","jys":"sh"},{"dm":"688159","mc":"有方科技","jys":"sh"},{"dm":"300939","mc":"秋田微","jys":"sz"},{"dm":"605086","mc":"龙高股份","jys":"sh"},{"dm":"301205","mc":"联特科技","jys":"sz"},{"dm":"002771","mc":"真视通","jys":"sz"},{"dm":"688039","mc":"当虹科技","jys":"sh"},{"dm":"688249","mc":"晶合集成","jys":"sh"},{"dm":"688683","mc":"莱尔科技","jys":"sh"},{"dm":"301015","mc":"百洋医药","jys":"sz"},{"dm":"300742","mc":"*ST越博","jys":"sz"},{"dm":"603322","mc":"超讯通信","jys":"sh"},{"dm":"300752","mc":"隆利科技","jys":"sz"},{"dm":"301297","mc":"富乐德","jys":"sz"},{"dm":"300878","mc":"维康药业","jys":"sz"},{"dm":"300400","mc":"劲拓股份","jys":"sz"},{"dm":"688337","mc":"普源精电","jys":"sh"},{"dm":"600552","mc":"凯盛科技","jys":"sh"},{"dm":"603912","mc":"佳力图","jys":"sh"},{"dm":"301321","mc":"翰博高新","jys":"sz"},{"dm":"600211","mc":"西藏药业","jys":"sh"},{"dm":"300631","mc":"久吾高科","jys":"sz"},{"dm":"002876","mc":"三利谱","jys":"sz"},{"dm":"301298","mc":"东利机械","jys":"sz"},{"dm":"603863","mc":"松炀资源","jys":"sh"},{"dm":"601059","mc":"信达证券","jys":"sh"},{"dm":"301128","mc":"强瑞技术","jys":"sz"},{"dm":"002750","mc":"龙津药业","jys":"sz"},{"dm":"688105","mc":"诺唯赞","jys":"sh"},{"dm":"688343","mc":"云天励飞-U","jys":"sh"},{"dm":"002609","mc":"捷顺科技","jys":"sz"},{"dm":"688256","mc":"寒武纪-U","jys":"sh"},{"dm":"600335","mc":"国机汽车","jys":"sh"},{"dm":"603078","mc":"江化微","jys":"sh"},{"dm":"301183","mc":"东田微","jys":"sz"},{"dm":"688097","mc":"博众精工","jys":"sh"},{"dm":"002776","mc":"*ST柏龙","jys":"sz"},{"dm":"301165","mc":"锐捷网络","jys":"sz"},{"dm":"600200","mc":"江苏吴中","jys":"sh"},{"dm":"600775","mc":"南京熊猫","jys":"sh"},{"dm":"002437","mc":"誉衡药业","jys":"sz"},{"dm":"600589","mc":"*ST榕泰","jys":"sh"},{"dm":"600265","mc":"ST景谷","jys":"sh"},{"dm":"603906","mc":"龙蟠科技","jys":"sh"},{"dm":"600326","mc":"西藏天路","jys":"sh"},{"dm":"301052","mc":"果麦文化","jys":"sz"},{"dm":"300844","mc":"山水比德","jys":"sz"},{"dm":"603186","mc":"华正新材","jys":"sh"},{"dm":"000046","mc":"*ST泛海","jys":"sz"},{"dm":"002251","mc":"*ST步高","jys":"sz"},{"dm":"002592","mc":"ST八菱","jys":"sz"},{"dm":"603603","mc":"*ST博天","jys":"sh"},{"dm":"300548","mc":"博创科技","jys":"sz"},{"dm":"301139","mc":"元道通信","jys":"sz"},{"dm":"002587","mc":"奥拓电子","jys":"sz"},{"dm":"688316","mc":"青云科技-U","jys":"sh"},{"dm":"300366","mc":"创意信息","jys":"sz"},{"dm":"300105","mc":"龙源技术","jys":"sz"},{"dm":"300506","mc":"名家汇","jys":"sz"},{"dm":"603083","mc":"剑桥科技","jys":"sh"},{"dm":"688409","mc":"富创精密","jys":"sh"},{"dm":"688698","mc":"伟创电气","jys":"sh"},{"dm":"002300","mc":"太阳电缆","jys":"sz"},{"dm":"600338","mc":"西藏珠峰","jys":"sh"},{"dm":"000068","mc":"华控赛格","jys":"sz"},{"dm":"603286","mc":"日盈电子","jys":"sh"},{"dm":"688258","mc":"卓易信息","jys":"sh"},{"dm":"300766","mc":"每日互动","jys":"sz"},{"dm":"300010","mc":"*ST豆神","jys":"sz"},{"dm":"688468","mc":"科美诊断","jys":"sh"},{"dm":"002409","mc":"雅克科技","jys":"sz"},{"dm":"600716","mc":"凤凰股份","jys":"sh"},{"dm":"600418","mc":"江淮汽车","jys":"sh"},{"dm":"002536","mc":"飞龙股份","jys":"sz"},{"dm":"688279","mc":"峰岹科技","jys":"sh"},{"dm":"300528","mc":"幸福蓝海","jys":"sz"},{"dm":"688716","mc":"中研股份","jys":"sh"},{"dm":"603466","mc":"风语筑","jys":"sh"},{"dm":"603089","mc":"正裕工业","jys":"sh"},{"dm":"600804","mc":"ST鹏博士","jys":"sh"},{"dm":"000593","mc":"德龙汇能","jys":"sz"},{"dm":"002998","mc":"优彩资源","jys":"sz"},{"dm":"688025","mc":"杰普特","jys":"sh"},{"dm":"300707","mc":"威唐工业","jys":"sz"},{"dm":"000520","mc":"凤凰航运","jys":"sz"},{"dm":"300808","mc":"久量股份","jys":"sz"},{"dm":"603238","mc":"诺邦股份","jys":"sh"},{"dm":"300841","mc":"康华生物","jys":"sz"},{"dm":"002122","mc":"汇洲智能","jys":"sz"},{"dm":"301503","mc":"智迪科技","jys":"sz"},{"dm":"600895","mc":"张江高科","jys":"sh"},{"dm":"603002","mc":"宏昌电子","jys":"sh"},{"dm":"603969","mc":"银龙股份","jys":"sh"},{"dm":"301391","mc":"卡莱特","jys":"sz"},{"dm":"605289","mc":"罗曼股份","jys":"sh"},{"dm":"300650","mc":"太龙股份","jys":"sz"},{"dm":"301313","mc":"凡拓数创","jys":"sz"},{"dm":"301013","mc":"利和兴","jys":"sz"},{"dm":"300092","mc":"科新机电","jys":"sz"},{"dm":"688456","mc":"有研粉材","jys":"sh"},{"dm":"688170","mc":"德龙激光","jys":"sh"},{"dm":"605188","mc":"国光连锁","jys":"sh"},{"dm":"300327","mc":"中颖电子","jys":"sz"},{"dm":"300379","mc":"东方通","jys":"sz"},{"dm":"603615","mc":"茶花股份","jys":"sh"},{"dm":"301070","mc":"开勒股份","jys":"sz"},{"dm":"600807","mc":"济南高新","jys":"sh"},{"dm":"002827","mc":"高争民爆","jys":"sz"},{"dm":"002802","mc":"洪汇新材","jys":"sz"},{"dm":"688182","mc":"灿勤科技","jys":"sh"},{"dm":"600493","mc":"凤竹纺织","jys":"sh"},{"dm":"688521","mc":"芯原股份","jys":"sh"},{"dm":"002845","mc":"同兴达","jys":"sz"},{"dm":"001229","mc":"魅视科技","jys":"sz"},{"dm":"300304","mc":"云意电气","jys":"sz"},{"dm":"300256","mc":"星星科技","jys":"sz"},{"dm":"300499","mc":"高澜股份","jys":"sz"},{"dm":"000034","mc":"神州数码","jys":"sz"},{"dm":"688702","mc":"盛科通信-U","jys":"sh"},{"dm":"300588","mc":"熙菱信息","jys":"sz"},{"dm":"000058","mc":"深 赛 格","jys":"sz"},{"dm":"300472","mc":"新元科技","jys":"sz"},{"dm":"300552","mc":"万集科技","jys":"sz"},{"dm":"300302","mc":"同有科技","jys":"sz"},{"dm":"002338","mc":"奥普光电","jys":"sz"},{"dm":"002657","mc":"中科金财","jys":"sz"},{"dm":"000550","mc":"江铃汽车","jys":"sz"},{"dm":"300835","mc":"龙磁科技","jys":"sz"},{"dm":"688007","mc":"光峰科技","jys":"sh"},{"dm":"600602","mc":"云赛智联","jys":"sh"},{"dm":"688218","mc":"江苏北人","jys":"sh"},{"dm":"002106","mc":"莱宝高科","jys":"sz"},{"dm":"300442","mc":"润泽科技","jys":"sz"},{"dm":"002564","mc":"*ST天沃","jys":"sz"},{"dm":"002432","mc":"九安医疗","jys":"sz"},{"dm":"300870","mc":"欧陆通","jys":"sz"},{"dm":"601886","mc":"江河集团","jys":"sh"},{"dm":"002476","mc":"宝莫股份","jys":"sz"},{"dm":"688502","mc":"茂莱光学","jys":"sh"},{"dm":"300236","mc":"上海新阳","jys":"sz"},{"dm":"600987","mc":"航民股份","jys":"sh"},{"dm":"000625","mc":"长安汽车","jys":"sz"},{"dm":"002548","mc":"金新农","jys":"sz"},{"dm":"301185","mc":"鸥玛软件","jys":"sz"},{"dm":"000045","mc":"深纺织A","jys":"sz"},{"dm":"300331","mc":"苏大维格","jys":"sz"},{"dm":"603042","mc":"华脉科技","jys":"sh"},{"dm":"301268","mc":"铭利达","jys":"sz"},{"dm":"002322","mc":"理工能科","jys":"sz"},{"dm":"600449","mc":"宁夏建材","jys":"sh"},{"dm":"688111","mc":"金山办公","jys":"sh"},{"dm":"002217","mc":"合力泰","jys":"sz"},{"dm":"688426","mc":"康为世纪","jys":"sh"},{"dm":"300589","mc":"江龙船艇","jys":"sz"},{"dm":"688418","mc":"震有科技","jys":"sh"},{"dm":"300533","mc":"冰川网络","jys":"sz"},{"dm":"002947","mc":"恒铭达","jys":"sz"},{"dm":"688071","mc":"华依科技","jys":"sh"},{"dm":"002847","mc":"盐津铺子","jys":"sz"},{"dm":"600619","mc":"海立股份","jys":"sh"},{"dm":"000988","mc":"华工科技","jys":"sz"},{"dm":"600839","mc":"四川长虹","jys":"sh"},{"dm":"688205","mc":"德科立","jys":"sh"},{"dm":"300555","mc":"ST路通","jys":"sz"},{"dm":"300843","mc":"胜蓝股份","jys":"sz"},{"dm":"603203","mc":"快克智能","jys":"sh"},{"dm":"300921","mc":"南凌科技","jys":"sz"},{"dm":"603005","mc":"晶方科技","jys":"sh"},{"dm":"688501","mc":"青达环保","jys":"sh"},{"dm":"000050","mc":"深天马A","jys":"sz"},{"dm":"300780","mc":"德恩精工","jys":"sz"},{"dm":"600666","mc":"ST瑞德","jys":"sh"},{"dm":"301131","mc":"聚赛龙","jys":"sz"},{"dm":"300135","mc":"宝利国际","jys":"sz"},{"dm":"002152","mc":"广电运通","jys":"sz"},{"dm":"002602","mc":"世纪华通","jys":"sz"},{"dm":"601001","mc":"晋控煤业","jys":"sh"},{"dm":"688078","mc":"龙软科技","jys":"sh"},{"dm":"300218","mc":"安利股份","jys":"sz"},{"dm":"000712","mc":"锦龙股份","jys":"sz"},{"dm":"300654","mc":"世纪天鸿","jys":"sz"},{"dm":"000809","mc":"铁岭新城","jys":"sz"},{"dm":"688055","mc":"龙腾光电","jys":"sh"},{"dm":"300337","mc":"银邦股份","jys":"sz"},{"dm":"300687","mc":"赛意信息","jys":"sz"},{"dm":"601099","mc":"太平洋","jys":"sh"},{"dm":"001380","mc":"华纬科技","jys":"sz"},{"dm":"301383","mc":"天键股份","jys":"sz"},{"dm":"688609","mc":"九联科技","jys":"sh"},{"dm":"688486","mc":"龙迅股份","jys":"sh"},{"dm":"002428","mc":"云南锗业","jys":"sz"},{"dm":"688515","mc":"裕太微-U","jys":"sh"},{"dm":"600853","mc":"龙建股份","jys":"sh"},{"dm":"300128","mc":"锦富技术","jys":"sz"},{"dm":"688229","mc":"博睿数据","jys":"sh"},{"dm":"688047","mc":"龙芯中科","jys":"sh"},{"dm":"688158","mc":"优刻得-W","jys":"sh"},{"dm":"603283","mc":"赛腾股份","jys":"sh"},{"dm":"603019","mc":"中科曙光","jys":"sh"},{"dm":"603918","mc":"金桥信息","jys":"sh"},{"dm":"000936","mc":"华西股份","jys":"sz"},{"dm":"002882","mc":"金龙羽","jys":"sz"},{"dm":"688183","mc":"生益电子","jys":"sh"},{"dm":"688143","mc":"长盈通","jys":"sh"},{"dm":"300637","mc":"扬帆新材","jys":"sz"},{"dm":"603605","mc":"珀莱雅","jys":"sh"},{"dm":"300179","mc":"四方达","jys":"sz"},{"dm":"300249","mc":"依米康","jys":"sz"},{"dm":"000009","mc":"中国宝安","jys":"sz"},{"dm":"301419","mc":"阿莱德","jys":"sz"},{"dm":"301110","mc":"青木股份","jys":"sz"},{"dm":"600410","mc":"华胜天成","jys":"sh"},{"dm":"688286","mc":"敏芯股份","jys":"sh"},{"dm":"001336","mc":"楚环科技","jys":"sz"},{"dm":"002456","mc":"欧菲光","jys":"sz"},{"dm":"002178","mc":"延华智能","jys":"sz"},{"dm":"000010","mc":"美丽生态","jys":"sz"},{"dm":"002261","mc":"拓维信息","jys":"sz"},{"dm":"002555","mc":"三七互娱","jys":"sz"},{"dm":"300909","mc":"汇创达","jys":"sz"},{"dm":"002855","mc":"捷荣技术","jys":"sz"},{"dm":"603332","mc":"苏州龙杰","jys":"sh"},{"dm":"300074","mc":"华平股份","jys":"sz"},{"dm":"688041","mc":"海光信息","jys":"sh"},{"dm":"002795","mc":"永和智控","jys":"sz"},{"dm":"002962","mc":"五方光电","jys":"sz"},{"dm":"001314","mc":"亿道信息","jys":"sz"},{"dm":"300166","mc":"东方国信","jys":"sz"},{"dm":"688687","mc":"凯因科技","jys":"sh"},{"dm":"002864","mc":"盘龙药业","jys":"sz"},{"dm":"603988","mc":"中电电机","jys":"sh"},{"dm":"301021","mc":"英诺激光","jys":"sz"},{"dm":"000889","mc":"ST中嘉","jys":"sz"},{"dm":"002760","mc":"凤形股份","jys":"sz"},{"dm":"300790","mc":"宇瞳光学","jys":"sz"},{"dm":"605008","mc":"长鸿高科","jys":"sh"},{"dm":"300397","mc":"天和防务","jys":"sz"},{"dm":"603970","mc":"中农立华","jys":"sh"},{"dm":"688639","mc":"华恒生物","jys":"sh"},{"dm":"300727","mc":"润禾材料","jys":"sz"},{"dm":"601028","mc":"玉龙股份","jys":"sh"},{"dm":"301151","mc":"冠龙节能","jys":"sz"},{"dm":"688298","mc":"东方生物","jys":"sh"},{"dm":"600100","mc":"同方股份","jys":"sh"},{"dm":"001288","mc":"运机集团","jys":"sz"},{"dm":"605588","mc":"冠石科技","jys":"sh"},{"dm":"300316","mc":"晶盛机电","jys":"sz"},{"dm":"600498","mc":"烽火通信","jys":"sh"},{"dm":"300279","mc":"和晶科技","jys":"sz"},{"dm":"688253","mc":"英诺特","jys":"sh"},{"dm":"688737","mc":"中自科技","jys":"sh"},{"dm":"300427","mc":"*ST红相","jys":"sz"},{"dm":"000955","mc":"欣龙控股","jys":"sz"},{"dm":"301558","mc":"三态股份","jys":"sz"},{"dm":"002785","mc":"万里石","jys":"sz"},{"dm":"603677","mc":"奇精机械","jys":"sh"},{"dm":"301218","mc":"华是科技","jys":"sz"},{"dm":"300822","mc":"贝仕达克","jys":"sz"},{"dm":"300819","mc":"聚杰微纤","jys":"sz"},{"dm":"688162","mc":"巨一科技","jys":"sh"},{"dm":"300667","mc":"必创科技","jys":"sz"},{"dm":"603922","mc":"金鸿顺","jys":"sh"},{"dm":"688798","mc":"艾为电子","jys":"sh"},{"dm":"301086","mc":"鸿富瀚","jys":"sz"},{"dm":"300496","mc":"中科创达","jys":"sz"},{"dm":"600975","mc":"新五丰","jys":"sh"},{"dm":"300007","mc":"汉威科技","jys":"sz"},{"dm":"300738","mc":"奥飞数据","jys":"sz"},{"dm":"000753","mc":"漳州发展","jys":"sz"},{"dm":"688027","mc":"国盾量子","jys":"sh"},{"dm":"301041","mc":"金百泽","jys":"sz"},{"dm":"300011","mc":"鼎汉技术","jys":"sz"},{"dm":"600353","mc":"旭光电子","jys":"sh"},{"dm":"300863","mc":"卡倍亿","jys":"sz"},{"dm":"600889","mc":"南京化纤","jys":"sh"},{"dm":"601138","mc":"工业富联","jys":"sh"},{"dm":"300708","mc":"聚灿光电","jys":"sz"},{"dm":"000021","mc":"深科技","jys":"sz"},{"dm":"300828","mc":"锐新科技","jys":"sz"},{"dm":"002892","mc":"科力尔","jys":"sz"},{"dm":"603579","mc":"荣泰健康","jys":"sh"},{"dm":"300160","mc":"秀强股份","jys":"sz"},{"dm":"300521","mc":"爱司凯","jys":"sz"},{"dm":"002912","mc":"中新赛克","jys":"sz"},{"dm":"002291","mc":"遥望科技","jys":"sz"},{"dm":"300956","mc":"英力股份","jys":"sz"},{"dm":"000096","mc":"广聚能源","jys":"sz"},{"dm":"002885","mc":"京泉华","jys":"sz"},{"dm":"605128","mc":"上海沿浦","jys":"sh"},{"dm":"300576","mc":"容大感光","jys":"sz"},{"dm":"300383","mc":"光环新网","jys":"sz"},{"dm":"300001","mc":"特锐德","jys":"sz"},{"dm":"300029","mc":"ST天龙","jys":"sz"},{"dm":"688525","mc":"佰维存储","jys":"sh"},{"dm":"600491","mc":"龙元建设","jys":"sh"},{"dm":"600766","mc":"*ST园城","jys":"sh"},{"dm":"300893","mc":"松原股份","jys":"sz"},{"dm":"603111","mc":"康尼机电","jys":"sh"},{"dm":"000509","mc":"华塑控股","jys":"sz"},{"dm":"688269","mc":"凯立新材","jys":"sh"},{"dm":"002279","mc":"久其软件","jys":"sz"},{"dm":"002387","mc":"维信诺","jys":"sz"},{"dm":"300994","mc":"久祺股份","jys":"sz"},{"dm":"000818","mc":"航锦科技","jys":"sz"},{"dm":"688129","mc":"东来技术","jys":"sh"},{"dm":"688114","mc":"华大智造","jys":"sh"},{"dm":"000570","mc":"苏常柴A","jys":"sz"},{"dm":"601456","mc":"国联证券","jys":"sh"},{"dm":"301338","mc":"凯格精机","jys":"sz"},{"dm":"688259","mc":"创耀科技","jys":"sh"},{"dm":"688606","mc":"奥泰生物","jys":"sh"},{"dm":"301036","mc":"双乐股份","jys":"sz"},{"dm":"300998","mc":"宁波方正","jys":"sz"},{"dm":"688578","mc":"艾力斯","jys":"sh"},{"dm":"688362","mc":"甬矽电子","jys":"sh"},{"dm":"003019","mc":"宸展光电","jys":"sz"},{"dm":"300992","mc":"泰福泵业","jys":"sz"},{"dm":"301396","mc":"宏景科技","jys":"sz"},{"dm":"600114","mc":"东睦股份","jys":"sh"},{"dm":"688520","mc":"神州细胞-U","jys":"sh"},{"dm":"002881","mc":"美格智能","jys":"sz"},{"dm":"603013","mc":"亚普股份","jys":"sh"},{"dm":"000637","mc":"ST实华","jys":"sz"},{"dm":"300941","mc":"创识科技","jys":"sz"},{"dm":"688387","mc":"信科移动-U","jys":"sh"},{"dm":"603398","mc":"沐邦高科","jys":"sh"},{"dm":"603598","mc":"引力传媒","jys":"sh"},{"dm":"600661","mc":"昂立教育","jys":"sh"},{"dm":"600237","mc":"铜峰电子","jys":"sh"},{"dm":"301169","mc":"零点有数","jys":"sz"},{"dm":"300597","mc":"吉大通信","jys":"sz"},{"dm":"002607","mc":"中公教育","jys":"sz"},{"dm":"300303","mc":"聚飞光电","jys":"sz"},{"dm":"002726","mc":"龙大美食","jys":"sz"},{"dm":"688095","mc":"福昕软件","jys":"sh"},{"dm":"300796","mc":"贝斯美","jys":"sz"},{"dm":"300799","mc":"*ST左江","jys":"sz"},{"dm":"300945","mc":"曼卡龙","jys":"sz"},{"dm":"002086","mc":"*ST东洋","jys":"sz"},{"dm":"688416","mc":"恒烁股份","jys":"sh"},{"dm":"600388","mc":"龙净环保","jys":"sh"},{"dm":"603160","mc":"汇顶科技","jys":"sh"},{"dm":"002199","mc":"东晶电子","jys":"sz"},{"dm":"000938","mc":"紫光股份","jys":"sz"},{"dm":"600867","mc":"通化东宝","jys":"sh"},{"dm":"301381","mc":"赛维时代","jys":"sz"},{"dm":"605050","mc":"福然德","jys":"sh"},{"dm":"000608","mc":"阳光股份","jys":"sz"},{"dm":"300711","mc":"广哈通信","jys":"sz"},{"dm":"688292","mc":"浩瀚深度","jys":"sh"},{"dm":"300429","mc":"强力新材","jys":"sz"},{"dm":"002228","mc":"合兴包装","jys":"sz"},{"dm":"688310","mc":"迈得医疗","jys":"sh"},{"dm":"002895","mc":"川恒股份","jys":"sz"},{"dm":"603887","mc":"城地香江","jys":"sh"},{"dm":"300255","mc":"常山药业","jys":"sz"},{"dm":"603648","mc":"畅联股份","jys":"sh"},{"dm":"300171","mc":"东富龙","jys":"sz"},{"dm":"300274","mc":"阳光电源","jys":"sz"},{"dm":"301133","mc":"金钟股份","jys":"sz"},{"dm":"603236","mc":"移远通信","jys":"sh"},{"dm":"300763","mc":"锦浪科技","jys":"sz"},{"dm":"003021","mc":"兆威机电","jys":"sz"},{"dm":"300076","mc":"GQY视讯","jys":"sz"},{"dm":"300643","mc":"万通智控","jys":"sz"},{"dm":"603118","mc":"共进股份","jys":"sh"},{"dm":"603773","mc":"沃格光电","jys":"sh"},{"dm":"000985","mc":"大庆华科","jys":"sz"},{"dm":"300098","mc":"高新兴","jys":"sz"},{"dm":"002797","mc":"第一创业","jys":"sz"},{"dm":"300736","mc":"百邦科技","jys":"sz"},{"dm":"300553","mc":"集智股份","jys":"sz"},{"dm":"002729","mc":"好利科技","jys":"sz"},{"dm":"300968","mc":"格林精密","jys":"sz"},{"dm":"002077","mc":"大港股份","jys":"sz"},{"dm":"300719","mc":"安达维尔","jys":"sz"},{"dm":"601555","mc":"东吴证券","jys":"sh"},{"dm":"600530","mc":"ST交昂","jys":"sh"},{"dm":"688496","mc":"清越科技","jys":"sh"},{"dm":"002725","mc":"跃岭股份","jys":"sz"},{"dm":"002312","mc":"川发龙蟒","jys":"sz"},{"dm":"300242","mc":"佳云科技","jys":"sz"},{"dm":"603725","mc":"天安新材","jys":"sh"},{"dm":"688195","mc":"腾景科技","jys":"sh"},{"dm":"300157","mc":"新锦动力","jys":"sz"},{"dm":"688273","mc":"麦澜德","jys":"sh"},{"dm":"600605","mc":"汇通能源","jys":"sh"},{"dm":"603713","mc":"密尔克卫","jys":"sh"},{"dm":"300131","mc":"英唐智控","jys":"sz"},{"dm":"000977","mc":"浪潮信息","jys":"sz"},{"dm":"600105","mc":"永鼎股份","jys":"sh"},{"dm":"603861","mc":"白云电器","jys":"sh"},{"dm":"002740","mc":"*ST爱迪","jys":"sz"},{"dm":"301326","mc":"捷邦科技","jys":"sz"},{"dm":"002674","mc":"兴业科技","jys":"sz"},{"dm":"003004","mc":"声迅股份","jys":"sz"},{"dm":"300193","mc":"佳士科技","jys":"sz"},{"dm":"300473","mc":"德尔股份","jys":"sz"},{"dm":"603306","mc":"华懋科技","jys":"sh"},{"dm":"688327","mc":"云从科技-UW","jys":"sh"},{"dm":"601666","mc":"平煤股份","jys":"sh"},{"dm":"300081","mc":"恒信东方","jys":"sz"},{"dm":"688300","mc":"联瑞新材","jys":"sh"},{"dm":"300177","mc":"中海达","jys":"sz"},{"dm":"002358","mc":"森源电气","jys":"sz"},{"dm":"300225","mc":"金力泰","jys":"sz"},{"dm":"300612","mc":"宣亚国际","jys":"sz"},{"dm":"600136","mc":"*ST明诚","jys":"sh"},{"dm":"002530","mc":"金财互联","jys":"sz"},{"dm":"688489","mc":"三未信安","jys":"sh"},{"dm":"603439","mc":"贵州三力","jys":"sh"},{"dm":"301004","mc":"嘉益股份","jys":"sz"},{"dm":"300721","mc":"怡达股份","jys":"sz"},{"dm":"301018","mc":"申菱环境","jys":"sz"},{"dm":"002463","mc":"沪电股份","jys":"sz"},{"dm":"000801","mc":"四川九洲","jys":"sz"},{"dm":"300585","mc":"奥联电子","jys":"sz"},{"dm":"301398","mc":"星源卓镁","jys":"sz"},{"dm":"002253","mc":"川大智胜","jys":"sz"},{"dm":"688079","mc":"美迪凯","jys":"sh"},{"dm":"000700","mc":"模塑科技","jys":"sz"},{"dm":"003007","mc":"直真科技","jys":"sz"},{"dm":"600173","mc":"卧龙地产","jys":"sh"},{"dm":"688102","mc":"斯瑞新材","jys":"sh"},{"dm":"301117","mc":"佳缘科技","jys":"sz"},{"dm":"002385","mc":"大北农","jys":"sz"},{"dm":"300825","mc":"阿尔特","jys":"sz"},{"dm":"002416","mc":"爱施德","jys":"sz"},{"dm":"002940","mc":"昂利康","jys":"sz"},{"dm":"000032","mc":"深桑达A","jys":"sz"},{"dm":"300812","mc":"易天股份","jys":"sz"},{"dm":"300592","mc":"华凯易佰","jys":"sz"},{"dm":"002197","mc":"证通电子","jys":"sz"},{"dm":"688382","mc":"益方生物-U","jys":"sh"},{"dm":"688787","mc":"海天瑞声","jys":"sh"},{"dm":"601188","mc":"龙江交通","jys":"sh"},{"dm":"300884","mc":"狄耐克","jys":"sz"},{"dm":"601788","mc":"光大证券","jys":"sh"},{"dm":"301348","mc":"蓝箭电子","jys":"sz"},{"dm":"605138","mc":"盛泰集团","jys":"sh"},{"dm":"688535","mc":"华海诚科","jys":"sh"},{"dm":"002748","mc":"世龙实业","jys":"sz"},{"dm":"600006","mc":"东风汽车","jys":"sh"},{"dm":"605058","mc":"澳弘电子","jys":"sh"},{"dm":"002008","mc":"大族激光","jys":"sz"},{"dm":"688296","mc":"和达科技","jys":"sh"},{"dm":"301159","mc":"三维天地","jys":"sz"},{"dm":"300560","mc":"中富通","jys":"sz"},{"dm":"603669","mc":"灵康药业","jys":"sh"},{"dm":"300426","mc":"唐德影视","jys":"sz"},{"dm":"603650","mc":"彤程新材","jys":"sh"},{"dm":"002490","mc":"山东墨龙","jys":"sz"},{"dm":"600426","mc":"华鲁恒升","jys":"sh"},{"dm":"000810","mc":"创维数字","jys":"sz"},{"dm":"600460","mc":"士兰微","jys":"sh"},{"dm":"605298","mc":"必得科技","jys":"sh"},{"dm":"600561","mc":"江西长运","jys":"sh"},{"dm":"002295","mc":"精艺股份","jys":"sz"},{"dm":"688020","mc":"方邦股份","jys":"sh"},{"dm":"603388","mc":"元成股份","jys":"sh"},{"dm":"688636","mc":"智明达","jys":"sh"},{"dm":"688588","mc":"凌志软件","jys":"sh"},{"dm":"688322","mc":"奥比中光-UW","jys":"sh"},{"dm":"688001","mc":"华兴源创","jys":"sh"},{"dm":"002491","mc":"通鼎互联","jys":"sz"},{"dm":"300964","mc":"本川智能","jys":"sz"},{"dm":"300556","mc":"丝路视觉","jys":"sz"},{"dm":"001339","mc":"智微智能","jys":"sz"},{"dm":"600256","mc":"广汇能源","jys":"sh"},{"dm":"605118","mc":"力鼎光电","jys":"sh"},{"dm":"600985","mc":"淮北矿业","jys":"sh"},{"dm":"002453","mc":"华软科技","jys":"sz"},{"dm":"300856","mc":"科思股份","jys":"sz"},{"dm":"300192","mc":"科德教育","jys":"sz"},{"dm":"688793","mc":"倍轻松","jys":"sh"},{"dm":"002331","mc":"皖通科技","jys":"sz"},{"dm":"000705","mc":"浙江震元","jys":"sz"},{"dm":"000980","mc":"众泰汽车","jys":"sz"},{"dm":"300141","mc":"和顺电气","jys":"sz"},{"dm":"000620","mc":"*ST新联","jys":"sz"},{"dm":"002137","mc":"实益达","jys":"sz"},{"dm":"000615","mc":"*ST美谷","jys":"sz"},{"dm":"603393","mc":"新天然气","jys":"sh"},{"dm":"603977","mc":"国泰集团","jys":"sh"},{"dm":"002769","mc":"普路通","jys":"sz"},{"dm":"002792","mc":"通宇通讯","jys":"sz"},{"dm":"688590","mc":"新致软件","jys":"sh"},{"dm":"002176","mc":"江特电机","jys":"sz"},{"dm":"002658","mc":"雪迪龙","jys":"sz"},{"dm":"605228","mc":"神通科技","jys":"sh"},{"dm":"600601","mc":"方正科技","jys":"sh"},{"dm":"300150","mc":"世纪瑞尔","jys":"sz"},{"dm":"301030","mc":"仕净科技","jys":"sz"},{"dm":"002134","mc":"天津普林","jys":"sz"},{"dm":"002467","mc":"二六三","jys":"sz"},{"dm":"301382","mc":"蜂助手","jys":"sz"},{"dm":"002377","mc":"国创高新","jys":"sz"},{"dm":"301252","mc":"同星科技","jys":"sz"},{"dm":"001260","mc":"坤泰股份","jys":"sz"},{"dm":"300895","mc":"铜牛信息","jys":"sz"},{"dm":"000820","mc":"神雾节能","jys":"sz"},{"dm":"603158","mc":"腾龙股份","jys":"sh"},{"dm":"300990","mc":"同飞股份","jys":"sz"},{"dm":"301357","mc":"北方长龙","jys":"sz"},{"dm":"600733","mc":"北汽蓝谷","jys":"sh"},{"dm":"688357","mc":"建龙微纳","jys":"sh"},{"dm":"300520","mc":"科大国创","jys":"sz"},{"dm":"000416","mc":"*ST民控","jys":"sz"},{"dm":"301172","mc":"君逸数码","jys":"sz"},{"dm":"002643","mc":"万润股份","jys":"sz"},{"dm":"605198","mc":"安德利","jys":"sh"},{"dm":"600983","mc":"惠而浦","jys":"sh"},{"dm":"000718","mc":"苏宁环球","jys":"sz"},{"dm":"600780","mc":"通宝能源","jys":"sh"},{"dm":"300455","mc":"航天智装","jys":"sz"},{"dm":"002190","mc":"成飞集成","jys":"sz"},{"dm":"600989","mc":"宝丰能源","jys":"sh"},{"dm":"002362","mc":"汉王科技","jys":"sz"},{"dm":"688112","mc":"鼎阳科技","jys":"sh"},{"dm":"002979","mc":"雷赛智能","jys":"sz"},{"dm":"601162","mc":"天风证券","jys":"sh"},{"dm":"002640","mc":"跨境通","jys":"sz"},{"dm":"002306","mc":"中科云网","jys":"sz"},{"dm":"603556","mc":"海兴电力","jys":"sh"},{"dm":"603297","mc":"永新光学","jys":"sh"},{"dm":"688696","mc":"极米科技","jys":"sh"},{"dm":"002442","mc":"龙星化工","jys":"sz"},{"dm":"000429","mc":"粤高速A","jys":"sz"},{"dm":"603966","mc":"法兰泰克","jys":"sh"},{"dm":"002488","mc":"金固股份","jys":"sz"},{"dm":"601236","mc":"红塔证券","jys":"sh"},{"dm":"601328","mc":"交通银行","jys":"sh"},{"dm":"300045","mc":"华力创通","jys":"sz"},{"dm":"301251","mc":"威尔高","jys":"sz"},{"dm":"688051","mc":"佳华科技","jys":"sh"},{"dm":"000039","mc":"中集集团","jys":"sz"},{"dm":"002843","mc":"泰嘉股份","jys":"sz"},{"dm":"300215","mc":"电科院","jys":"sz"},{"dm":"002410","mc":"广联达","jys":"sz"},{"dm":"002224","mc":"三 力 士","jys":"sz"},{"dm":"300802","mc":"矩子科技","jys":"sz"},{"dm":"000665","mc":"湖北广电","jys":"sz"},{"dm":"301512","mc":"智信精密","jys":"sz"},{"dm":"300221","mc":"银禧科技","jys":"sz"},{"dm":"002457","mc":"青龙管业","jys":"sz"},{"dm":"301529","mc":"福赛科技","jys":"sz"},{"dm":"301288","mc":"清研环境","jys":"sz"},{"dm":"300199","mc":"翰宇药业","jys":"sz"},{"dm":"603686","mc":"福龙马","jys":"sh"},{"dm":"605133","mc":"嵘泰股份","jys":"sh"},{"dm":"301181","mc":"标榜股份","jys":"sz"},{"dm":"300167","mc":"ST迪威迅","jys":"sz"},{"dm":"300730","mc":"科创信息","jys":"sz"},{"dm":"002454","mc":"松芝股份","jys":"sz"},{"dm":"301328","mc":"维峰电子","jys":"sz"},{"dm":"300659","mc":"中孚信息","jys":"sz"},{"dm":"600993","mc":"马应龙","jys":"sh"},{"dm":"688246","mc":"嘉和美康","jys":"sh"},{"dm":"603881","mc":"数据港","jys":"sh"},{"dm":"300614","mc":"百川畅银","jys":"sz"},{"dm":"600689","mc":"上海三毛","jys":"sh"},{"dm":"002073","mc":"软控股份","jys":"sz"},{"dm":"300344","mc":"立方数科","jys":"sz"},{"dm":"301129","mc":"瑞纳智能","jys":"sz"},{"dm":"600012","mc":"皖通高速","jys":"sh"},{"dm":"300333","mc":"兆日科技","jys":"sz"},{"dm":"603392","mc":"万泰生物","jys":"sh"},{"dm":"000638","mc":"万方发展","jys":"sz"},{"dm":"002357","mc":"富临运业","jys":"sz"},{"dm":"603086","mc":"先达股份","jys":"sh"},{"dm":"688776","mc":"国光电气","jys":"sh"},{"dm":"301372","mc":"科净源","jys":"sz"},{"dm":"301428","mc":"世纪恒通","jys":"sz"},{"dm":"300525","mc":"博思软件","jys":"sz"},{"dm":"603139","mc":"康惠制药","jys":"sh"},{"dm":"688628","mc":"优利德","jys":"sh"},{"dm":"300461","mc":"田中精机","jys":"sz"},{"dm":"300517","mc":"海波重科","jys":"sz"},{"dm":"300591","mc":"万里马","jys":"sz"},{"dm":"300241","mc":"瑞丰光电","jys":"sz"},{"dm":"688429","mc":"时创能源","jys":"sh"},{"dm":"688138","mc":"清溢光电","jys":"sh"},{"dm":"600290","mc":"*ST华仪","jys":"sh"},{"dm":"300084","mc":"海默科技","jys":"sz"},{"dm":"002632","mc":"道明光学","jys":"sz"},{"dm":"300547","mc":"川环科技","jys":"sz"},{"dm":"600615","mc":"丰华股份","jys":"sh"},{"dm":"002919","mc":"名臣健康","jys":"sz"},{"dm":"001965","mc":"招商公路","jys":"sz"},{"dm":"002268","mc":"电科网安","jys":"sz"},{"dm":"603289","mc":"泰瑞机器","jys":"sh"},{"dm":"300866","mc":"安克创新","jys":"sz"},{"dm":"300277","mc":"海联讯","jys":"sz"},{"dm":"688077","mc":"大地熊","jys":"sh"},{"dm":"603937","mc":"丽岛新材","jys":"sh"},{"dm":"003005","mc":"竞业达","jys":"sz"},{"dm":"688693","mc":"锴威特","jys":"sh"},{"dm":"600793","mc":"宜宾纸业","jys":"sh"},{"dm":"300229","mc":"拓尔思","jys":"sz"},{"dm":"688621","mc":"阳光诺和","jys":"sh"},{"dm":"001289","mc":"龙源电力","jys":"sz"},{"dm":"688076","mc":"诺泰生物","jys":"sh"},{"dm":"002777","mc":"久远银海","jys":"sz"},{"dm":"002811","mc":"郑中设计","jys":"sz"},{"dm":"601789","mc":"宁波建工","jys":"sh"},{"dm":"600841","mc":"动力新科","jys":"sh"},{"dm":"300120","mc":"经纬辉开","jys":"sz"},{"dm":"688766","mc":"普冉股份","jys":"sh"},{"dm":"002148","mc":"北纬科技","jys":"sz"},{"dm":"688099","mc":"晶晨股份","jys":"sh"},{"dm":"603059","mc":"倍加洁","jys":"sh"},{"dm":"600684","mc":"珠江股份","jys":"sh"},{"dm":"601699","mc":"潞安环能","jys":"sh"},{"dm":"688160","mc":"步科股份","jys":"sh"},{"dm":"002590","mc":"万安科技","jys":"sz"},{"dm":"603189","mc":"网达软件","jys":"sh"},{"dm":"001269","mc":"欧晶科技","jys":"sz"},{"dm":"000856","mc":"冀东装备","jys":"sz"},{"dm":"603230","mc":"内蒙新华","jys":"sh"},{"dm":"300061","mc":"旗天科技","jys":"sz"},{"dm":"301248","mc":"杰创智能","jys":"sz"},{"dm":"605318","mc":"法狮龙","jys":"sh"},{"dm":"300051","mc":"琏升科技","jys":"sz"},{"dm":"603052","mc":"可川科技","jys":"sh"},{"dm":"300723","mc":"一品红","jys":"sz"},{"dm":"603197","mc":"保隆科技","jys":"sh"},{"dm":"300445","mc":"康斯特","jys":"sz"},{"dm":"301068","mc":"大地海洋","jys":"sz"},{"dm":"300503","mc":"昊志机电","jys":"sz"},{"dm":"300334","mc":"津膜科技","jys":"sz"},{"dm":"002952","mc":"亚世光电","jys":"sz"},{"dm":"688181","mc":"八亿时空","jys":"sh"},{"dm":"301162","mc":"国能日新","jys":"sz"},{"dm":"300537","mc":"广信材料","jys":"sz"},{"dm":"002368","mc":"太极股份","jys":"sz"},{"dm":"603586","mc":"金麒麟","jys":"sh"},{"dm":"300601","mc":"康泰生物","jys":"sz"},{"dm":"603039","mc":"泛微网络","jys":"sh"},{"dm":"605020","mc":"永和股份","jys":"sh"},{"dm":"688373","mc":"盟科药业-U","jys":"sh"},{"dm":"301188","mc":"力诺特玻","jys":"sz"},{"dm":"603229","mc":"奥翔药业","jys":"sh"},{"dm":"301231","mc":"荣信文化","jys":"sz"},{"dm":"001296","mc":"长江材料","jys":"sz"},{"dm":"601198","mc":"东兴证券","jys":"sh"},{"dm":"002166","mc":"莱茵生物","jys":"sz"},{"dm":"603936","mc":"博敏电子","jys":"sh"},{"dm":"688579","mc":"山大地纬","jys":"sh"},{"dm":"300057","mc":"万顺新材","jys":"sz"},{"dm":"688045","mc":"必易微","jys":"sh"},{"dm":"301299","mc":"卓创资讯","jys":"sz"},{"dm":"300217","mc":"东方电热","jys":"sz"},{"dm":"600558","mc":"大西洋","jys":"sh"},{"dm":"300805","mc":"电声股份","jys":"sz"},{"dm":"600415","mc":"小商品城","jys":"sh"},{"dm":"000571","mc":"新大洲A","jys":"sz"},{"dm":"002094","mc":"青岛金王","jys":"sz"},{"dm":"002031","mc":"巨轮智能","jys":"sz"},{"dm":"688582","mc":"芯动联科","jys":"sh"},{"dm":"688323","mc":"瑞华泰","jys":"sh"},{"dm":"603992","mc":"松霖科技","jys":"sh"},{"dm":"688237","mc":"超卓航科","jys":"sh"},{"dm":"600129","mc":"太极集团","jys":"sh"},{"dm":"002671","mc":"龙泉股份","jys":"sz"},{"dm":"000795","mc":"英洛华","jys":"sz"},{"dm":"688191","mc":"智洋创新","jys":"sh"},{"dm":"600355","mc":"精伦电子","jys":"sh"},{"dm":"002263","mc":"大东南","jys":"sz"},{"dm":"688573","mc":"信宇人","jys":"sh"},{"dm":"300877","mc":"金春股份","jys":"sz"},{"dm":"600053","mc":"九鼎投资","jys":"sh"},{"dm":"601500","mc":"通用股份","jys":"sh"},{"dm":"001282","mc":"三联锻造","jys":"sz"},{"dm":"603501","mc":"韦尔股份","jys":"sh"},{"dm":"300949","mc":"奥雅股份","jys":"sz"},{"dm":"000971","mc":"ST高升","jys":"sz"},{"dm":"600769","mc":"祥龙电业","jys":"sh"},{"dm":"600032","mc":"浙江新能","jys":"sh"},{"dm":"600039","mc":"四川路桥","jys":"sh"},{"dm":"603319","mc":"湘油泵","jys":"sh"},{"dm":"600345","mc":"长江通信","jys":"sh"},{"dm":"603530","mc":"神马电力","jys":"sh"},{"dm":"301302","mc":"华如科技","jys":"sz"},{"dm":"603928","mc":"兴业股份","jys":"sh"},{"dm":"001206","mc":"依依股份","jys":"sz"},{"dm":"002862","mc":"实丰文化","jys":"sz"},{"dm":"600735","mc":"新华锦","jys":"sh"},{"dm":"300916","mc":"朗特智能","jys":"sz"},{"dm":"002752","mc":"昇兴股份","jys":"sz"},{"dm":"603327","mc":"福蓉科技","jys":"sh"},{"dm":"603188","mc":"亚邦股份","jys":"sh"},{"dm":"605199","mc":"葫芦娃","jys":"sh"},{"dm":"600758","mc":"辽宁能源","jys":"sh"},{"dm":"601969","mc":"海南矿业","jys":"sh"},{"dm":"002521","mc":"齐峰新材","jys":"sz"},{"dm":"688147","mc":"微导纳米","jys":"sh"},{"dm":"301266","mc":"宇邦新材","jys":"sz"},{"dm":"300474","mc":"景嘉微","jys":"sz"},{"dm":"002215","mc":"诺 普 信","jys":"sz"},{"dm":"603119","mc":"浙江荣泰","jys":"sh"},{"dm":"002591","mc":"恒大高新","jys":"sz"},{"dm":"300703","mc":"创源股份","jys":"sz"},{"dm":"603363","mc":"傲农生物","jys":"sh"},{"dm":"300365","mc":"恒华科技","jys":"sz"},{"dm":"301267","mc":"华厦眼科","jys":"sz"},{"dm":"002395","mc":"双象股份","jys":"sz"},{"dm":"688329","mc":"艾隆科技","jys":"sh"},{"dm":"605088","mc":"冠盛股份","jys":"sh"},{"dm":"600604","mc":"市北高新","jys":"sh"},{"dm":"000968","mc":"蓝焰控股","jys":"sz"},{"dm":"600375","mc":"汉马科技","jys":"sh"},{"dm":"300935","mc":"盈建科","jys":"sz"},{"dm":"601918","mc":"新集能源","jys":"sh"},{"dm":"603662","mc":"柯力传感","jys":"sh"},{"dm":"301366","mc":"一博科技","jys":"sz"},{"dm":"600864","mc":"哈投股份","jys":"sh"},{"dm":"002400","mc":"省广集团","jys":"sz"},{"dm":"600125","mc":"铁龙物流","jys":"sh"},{"dm":"002576","mc":"通达动力","jys":"sz"},{"dm":"001298","mc":"好上好","jys":"sz"},{"dm":"300414","mc":"中光防雷","jys":"sz"},{"dm":"002315","mc":"焦点科技","jys":"sz"},{"dm":"002109","mc":"兴化股份","jys":"sz"},{"dm":"688004","mc":"博汇科技","jys":"sh"},{"dm":"300314","mc":"戴维医疗","jys":"sz"},{"dm":"301203","mc":"国泰环保","jys":"sz"},{"dm":"002593","mc":"日上集团","jys":"sz"},{"dm":"300248","mc":"新开普","jys":"sz"},{"dm":"601999","mc":"出版传媒","jys":"sh"},{"dm":"002787","mc":"华源控股","jys":"sz"},{"dm":"000525","mc":"ST红太阳","jys":"sz"},{"dm":"300936","mc":"中英科技","jys":"sz"},{"dm":"300497","mc":"富祥药业","jys":"sz"},{"dm":"603618","mc":"杭电股份","jys":"sh"},{"dm":"002189","mc":"中光学","jys":"sz"},{"dm":"300368","mc":"汇金股份","jys":"sz"},{"dm":"300227","mc":"光韵达","jys":"sz"},{"dm":"000823","mc":"超声电子","jys":"sz"},{"dm":"600330","mc":"天通股份","jys":"sh"},{"dm":"003040","mc":"楚天龙","jys":"sz"},{"dm":"001226","mc":"拓山重工","jys":"sz"},{"dm":"300260","mc":"新莱应材","jys":"sz"},{"dm":"300237","mc":"美晨生态","jys":"sz"},{"dm":"600773","mc":"西藏城投","jys":"sh"},{"dm":"603138","mc":"海量数据","jys":"sh"},{"dm":"002015","mc":"协鑫能科","jys":"sz"},{"dm":"000727","mc":"冠捷科技","jys":"sz"},{"dm":"000920","mc":"沃顿科技","jys":"sz"},{"dm":"600671","mc":"*ST目药","jys":"sh"},{"dm":"002585","mc":"双星新材","jys":"sz"},{"dm":"300403","mc":"汉宇集团","jys":"sz"},{"dm":"002087","mc":"*ST新纺","jys":"sz"},{"dm":"688435","mc":"英方软件","jys":"sh"},{"dm":"300355","mc":"蒙草生态","jys":"sz"},{"dm":"600738","mc":"丽尚国潮","jys":"sh"},{"dm":"600641","mc":"万业企业","jys":"sh"},{"dm":"002230","mc":"科大讯飞","jys":"sz"},{"dm":"300604","mc":"长川科技","jys":"sz"},{"dm":"300638","mc":"广和通","jys":"sz"},{"dm":"301528","mc":"多浦乐","jys":"sz"},{"dm":"002512","mc":"达华智能","jys":"sz"},{"dm":"300546","mc":"雄帝科技","jys":"sz"},{"dm":"600329","mc":"达仁堂","jys":"sh"},{"dm":"603379","mc":"三美股份","jys":"sh"},{"dm":"600241","mc":"时代万恒","jys":"sh"},{"dm":"300281","mc":"金明精机","jys":"sz"},{"dm":"603010","mc":"万盛股份","jys":"sh"},{"dm":"600088","mc":"中视传媒","jys":"sh"},{"dm":"688372","mc":"伟测科技","jys":"sh"},{"dm":"301019","mc":"宁波色母","jys":"sz"},{"dm":"600567","mc":"山鹰国际","jys":"sh"},{"dm":"600281","mc":"华阳新材","jys":"sh"},{"dm":"300693","mc":"盛弘股份","jys":"sz"},{"dm":"000953","mc":"河化股份","jys":"sz"},{"dm":"002825","mc":"纳尔股份","jys":"sz"},{"dm":"002757","mc":"南兴股份","jys":"sz"},{"dm":"301380","mc":"挖金客","jys":"sz"},{"dm":"002336","mc":"人人乐","jys":"sz"},{"dm":"603926","mc":"铁流股份","jys":"sh"},{"dm":"300504","mc":"天邑股份","jys":"sz"},{"dm":"300079","mc":"数码视讯","jys":"sz"},{"dm":"600626","mc":"申达股份","jys":"sh"},{"dm":"300322","mc":"硕贝德","jys":"sz"},{"dm":"688619","mc":"罗普特","jys":"sh"},{"dm":"301007","mc":"德迈仕","jys":"sz"},{"dm":"300130","mc":"新国都","jys":"sz"},{"dm":"300250","mc":"初灵信息","jys":"sz"},{"dm":"000407","mc":"胜利股份","jys":"sz"},{"dm":"603823","mc":"百合花","jys":"sh"},{"dm":"002661","mc":"克明食品","jys":"sz"},{"dm":"000554","mc":"泰山石油","jys":"sz"},{"dm":"002234","mc":"民和股份","jys":"sz"},{"dm":"002662","mc":"京威股份","jys":"sz"},{"dm":"002642","mc":"荣联科技","jys":"sz"},{"dm":"002212","mc":"天融信","jys":"sz"},{"dm":"000599","mc":"青岛双星","jys":"sz"},{"dm":"600783","mc":"鲁信创投","jys":"sh"},{"dm":"688012","mc":"中微公司","jys":"sh"},{"dm":"002857","mc":"三晖电气","jys":"sz"},{"dm":"300745","mc":"欣锐科技","jys":"sz"},{"dm":"600199","mc":"金种子酒","jys":"sh"},{"dm":"600734","mc":"ST实达","jys":"sh"},{"dm":"300543","mc":"朗科智能","jys":"sz"},{"dm":"002418","mc":"康盛股份","jys":"sz"},{"dm":"301016","mc":"雷尔伟","jys":"sz"},{"dm":"300561","mc":"汇金科技","jys":"sz"},{"dm":"600523","mc":"贵航股份","jys":"sh"},{"dm":"605166","mc":"聚合顺","jys":"sh"},{"dm":"601858","mc":"中国科传","jys":"sh"},{"dm":"603948","mc":"建业股份","jys":"sh"},{"dm":"688600","mc":"皖仪科技","jys":"sh"},{"dm":"301261","mc":"恒工精密","jys":"sz"},{"dm":"301062","mc":"上海艾录","jys":"sz"},{"dm":"000421","mc":"南京公用","jys":"sz"},{"dm":"301208","mc":"中亦科技","jys":"sz"},{"dm":"002761","mc":"浙江建投","jys":"sz"},{"dm":"002316","mc":"亚联发展","jys":"sz"},{"dm":"600288","mc":"大恒科技","jys":"sh"},{"dm":"300451","mc":"创业慧康","jys":"sz"},{"dm":"603109","mc":"神驰机电","jys":"sh"},{"dm":"603040","mc":"新坐标","jys":"sh"},{"dm":"001259","mc":"利仁科技","jys":"sz"},{"dm":"688667","mc":"菱电电控","jys":"sh"},{"dm":"600658","mc":"电子城","jys":"sh"},{"dm":"603172","mc":"万丰股份","jys":"sh"},{"dm":"300004","mc":"南风股份","jys":"sz"},{"dm":"301315","mc":"威士顿","jys":"sz"},{"dm":"603486","mc":"科沃斯","jys":"sh"},{"dm":"688448","mc":"磁谷科技","jys":"sh"},{"dm":"300513","mc":"恒实科技","jys":"sz"},{"dm":"600599","mc":"ST熊猫","jys":"sh"},{"dm":"000070","mc":"特发信息","jys":"sz"},{"dm":"601988","mc":"中国银行","jys":"sh"},{"dm":"600128","mc":"苏豪弘业","jys":"sh"},{"dm":"600007","mc":"中国国贸","jys":"sh"},{"dm":"300319","mc":"麦捷科技","jys":"sz"},{"dm":"603813","mc":"原尚股份","jys":"sh"},{"dm":"300762","mc":"上海瀚讯","jys":"sz"},{"dm":"002679","mc":"福建金森","jys":"sz"},{"dm":"002310","mc":"东方园林","jys":"sz"},{"dm":"688568","mc":"中科星图","jys":"sh"},{"dm":"002715","mc":"登云股份","jys":"sz"},{"dm":"603788","mc":"宁波高发","jys":"sh"},{"dm":"301399","mc":"英特科技","jys":"sz"},{"dm":"300471","mc":"厚普股份","jys":"sz"},{"dm":"688480","mc":"赛恩斯","jys":"sh"},{"dm":"002019","mc":"亿帆医药","jys":"sz"},{"dm":"600229","mc":"城市传媒","jys":"sh"},{"dm":"600885","mc":"宏发股份","jys":"sh"},{"dm":"002835","mc":"同为股份","jys":"sz"},{"dm":"688209","mc":"英集芯","jys":"sh"},{"dm":"600796","mc":"钱江生化","jys":"sh"},{"dm":"000025","mc":"特 力A","jys":"sz"},{"dm":"605333","mc":"沪光股份","jys":"sh"},{"dm":"002840","mc":"华统股份","jys":"sz"},{"dm":"001268","mc":"联合精密","jys":"sz"},{"dm":"301113","mc":"雅艺科技","jys":"sz"},{"dm":"300296","mc":"利亚德","jys":"sz"},{"dm":"688228","mc":"开普云","jys":"sh"},{"dm":"300689","mc":"澄天伟业","jys":"sz"},{"dm":"300041","mc":"回天新材","jys":"sz"},{"dm":"300958","mc":"建工修复","jys":"sz"},{"dm":"300880","mc":"迦南智能","jys":"sz"},{"dm":"600546","mc":"山煤国际","jys":"sh"},{"dm":"300498","mc":"温氏股份","jys":"sz"},{"dm":"600118","mc":"中国卫星","jys":"sh"},{"dm":"002875","mc":"安奈儿","jys":"sz"},{"dm":"002698","mc":"博实股份","jys":"sz"},{"dm":"301331","mc":"恩威医药","jys":"sz"},{"dm":"688103","mc":"国力股份","jys":"sh"},{"dm":"603360","mc":"百傲化学","jys":"sh"},{"dm":"002775","mc":"文科园林","jys":"sz"},{"dm":"688290","mc":"景业智能","jys":"sh"},{"dm":"000811","mc":"冰轮环境","jys":"sz"},{"dm":"301225","mc":"恒勃股份","jys":"sz"},{"dm":"301157","mc":"华塑科技","jys":"sz"},{"dm":"603776","mc":"永安行","jys":"sh"},{"dm":"688800","mc":"瑞可达","jys":"sh"},{"dm":"300761","mc":"立华股份","jys":"sz"},{"dm":"300173","mc":"福能东方","jys":"sz"},{"dm":"300153","mc":"科泰电源","jys":"sz"},{"dm":"688522","mc":"纳睿雷达","jys":"sh"},{"dm":"300213","mc":"佳讯飞鸿","jys":"sz"},{"dm":"301525","mc":"儒竞科技","jys":"sz"},{"dm":"300377","mc":"赢时胜","jys":"sz"},{"dm":"603660","mc":"苏州科达","jys":"sh"},{"dm":"300494","mc":"盛天网络","jys":"sz"},{"dm":"688618","mc":"三旺通信","jys":"sh"},{"dm":"002083","mc":"孚日股份","jys":"sz"},{"dm":"000998","mc":"隆平高科","jys":"sz"},{"dm":"003010","mc":"若羽臣","jys":"sz"},{"dm":"600938","mc":"中国海油","jys":"sh"},{"dm":"600584","mc":"长电科技","jys":"sh"},{"dm":"300634","mc":"彩讯股份","jys":"sz"},{"dm":"300598","mc":"诚迈科技","jys":"sz"},{"dm":"301083","mc":"百胜智能","jys":"sz"},{"dm":"002803","mc":"吉宏股份","jys":"sz"},{"dm":"603123","mc":"翠微股份","jys":"sh"},{"dm":"300720","mc":"海川智能","jys":"sz"},{"dm":"688571","mc":"杭华股份","jys":"sh"},{"dm":"000678","mc":"襄阳轴承","jys":"sz"},{"dm":"600792","mc":"云煤能源","jys":"sh"},{"dm":"688169","mc":"石头科技","jys":"sh"},{"dm":"600536","mc":"中国软件","jys":"sh"},{"dm":"300288","mc":"朗玛信息","jys":"sz"},{"dm":"600059","mc":"古越龙山","jys":"sh"},{"dm":"600232","mc":"金鹰股份","jys":"sh"},{"dm":"603959","mc":"百利科技","jys":"sh"},{"dm":"002628","mc":"成都路桥","jys":"sz"},{"dm":"300085","mc":"银之杰","jys":"sz"},{"dm":"688767","mc":"博拓生物","jys":"sh"},{"dm":"300621","mc":"维业股份","jys":"sz"},{"dm":"600250","mc":"南纺股份","jys":"sh"},{"dm":"000826","mc":"启迪环境","jys":"sz"},{"dm":"300399","mc":"天利科技","jys":"sz"},{"dm":"000757","mc":"浩物股份","jys":"sz"},{"dm":"002276","mc":"万马股份","jys":"sz"},{"dm":"688201","mc":"信安世纪","jys":"sh"},{"dm":"600909","mc":"华安证券","jys":"sh"},{"dm":"300928","mc":"华安鑫创","jys":"sz"},{"dm":"688282","mc":"理工导航","jys":"sh"},{"dm":"002808","mc":"ST恒久","jys":"sz"},{"dm":"601225","mc":"陕西煤业","jys":"sh"},{"dm":"688610","mc":"埃科光电","jys":"sh"},{"dm":"002335","mc":"科华数据","jys":"sz"},{"dm":"688626","mc":"翔宇医疗","jys":"sh"},{"dm":"688591","mc":"泰凌微","jys":"sh"},{"dm":"688021","mc":"奥福环保","jys":"sh"},{"dm":"300113","mc":"顺网科技","jys":"sz"},{"dm":"002763","mc":"汇洁股份","jys":"sz"},{"dm":"300493","mc":"润欣科技","jys":"sz"},{"dm":"001331","mc":"胜通能源","jys":"sz"},{"dm":"603278","mc":"大业股份","jys":"sh"},{"dm":"300410","mc":"正业科技","jys":"sz"},{"dm":"002630","mc":"华西能源","jys":"sz"},{"dm":"603908","mc":"牧高笛","jys":"sh"},{"dm":"300226","mc":"上海钢联","jys":"sz"},{"dm":"688113","mc":"联测科技","jys":"sh"},{"dm":"300948","mc":"冠中生态","jys":"sz"},{"dm":"688068","mc":"热景生物","jys":"sh"},{"dm":"600369","mc":"西南证券","jys":"sh"},{"dm":"002673","mc":"西部证券","jys":"sz"},{"dm":"300454","mc":"深信服","jys":"sz"},{"dm":"002577","mc":"雷柏科技","jys":"sz"},{"dm":"300112","mc":"万讯自控","jys":"sz"},{"dm":"603206","mc":"嘉环科技","jys":"sh"},{"dm":"600025","mc":"华能水电","jys":"sh"},{"dm":"300219","mc":"鸿利智汇","jys":"sz"},{"dm":"002235","mc":"安妮股份","jys":"sz"},{"dm":"002446","mc":"盛路通信","jys":"sz"},{"dm":"605180","mc":"华生科技","jys":"sh"},{"dm":"688439","mc":"振华风光","jys":"sh"},{"dm":"300577","mc":"开润股份","jys":"sz"},{"dm":"002893","mc":"京能热力","jys":"sz"},{"dm":"300323","mc":"华灿光电","jys":"sz"},{"dm":"300559","mc":"佳发教育","jys":"sz"},{"dm":"002360","mc":"同德化工","jys":"sz"},{"dm":"600423","mc":"柳化股份","jys":"sh"},{"dm":"002351","mc":"漫步者","jys":"sz"},{"dm":"605016","mc":"百龙创园","jys":"sh"},{"dm":"300627","mc":"华测导航","jys":"sz"},{"dm":"688539","mc":"高华科技","jys":"sh"},{"dm":"600076","mc":"康欣新材","jys":"sh"},{"dm":"300830","mc":"金现代","jys":"sz"},{"dm":"300342","mc":"天银机电","jys":"sz"},{"dm":"000631","mc":"顺发恒业","jys":"sz"},{"dm":"688580","mc":"伟思医疗","jys":"sh"},{"dm":"600686","mc":"金龙汽车","jys":"sh"},{"dm":"600061","mc":"国投资本","jys":"sh"},{"dm":"688360","mc":"德马科技","jys":"sh"},{"dm":"002518","mc":"科士达","jys":"sz"},{"dm":"603176","mc":"汇通集团","jys":"sh"},{"dm":"000782","mc":"美达股份","jys":"sz"},{"dm":"605066","mc":"天正电气","jys":"sh"},{"dm":"002023","mc":"海特高新","jys":"sz"},{"dm":"002133","mc":"广宇集团","jys":"sz"},{"dm":"300077","mc":"国民技术","jys":"sz"},{"dm":"002957","mc":"科瑞技术","jys":"sz"},{"dm":"002348","mc":"高乐股份","jys":"sz"},{"dm":"600707","mc":"彩虹股份","jys":"sh"},{"dm":"002396","mc":"星网锐捷","jys":"sz"},{"dm":"300713","mc":"英可瑞","jys":"sz"},{"dm":"000762","mc":"西藏矿业","jys":"sz"},{"dm":"300338","mc":"开元教育","jys":"sz"},{"dm":"000962","mc":"东方钽业","jys":"sz"},{"dm":"300937","mc":"药易购","jys":"sz"},{"dm":"688365","mc":"光云科技","jys":"sh"},{"dm":"300961","mc":"深水海纳","jys":"sz"},{"dm":"300115","mc":"长盈精密","jys":"sz"},{"dm":"601869","mc":"长飞光纤","jys":"sh"},{"dm":"600971","mc":"恒源煤电","jys":"sh"},{"dm":"688219","mc":"会通股份","jys":"sh"},{"dm":"002749","mc":"国光股份","jys":"sz"},{"dm":"000166","mc":"申万宏源","jys":"sz"},{"dm":"300440","mc":"运达科技","jys":"sz"},{"dm":"300476","mc":"胜宏科技","jys":"sz"},{"dm":"000911","mc":"广农糖业","jys":"sz"},{"dm":"002058","mc":"威尔泰","jys":"sz"},{"dm":"300605","mc":"恒锋信息","jys":"sz"},{"dm":"002207","mc":"准油股份","jys":"sz"},{"dm":"600830","mc":"香溢融通","jys":"sh"},{"dm":"002354","mc":"天娱数科","jys":"sz"},{"dm":"002722","mc":"物产金轮","jys":"sz"},{"dm":"688123","mc":"聚辰股份","jys":"sh"},{"dm":"603825","mc":"华扬联众","jys":"sh"},{"dm":"000679","mc":"大连友谊","jys":"sz"},{"dm":"600997","mc":"开滦股份","jys":"sh"},{"dm":"600336","mc":"澳柯玛","jys":"sh"},{"dm":"688620","mc":"安凯微","jys":"sh"},{"dm":"605388","mc":"均瑶健康","jys":"sh"},{"dm":"600185","mc":"格力地产","jys":"sh"},{"dm":"002766","mc":"索菱股份","jys":"sz"},{"dm":"000011","mc":"深物业A","jys":"sz"},{"dm":"601377","mc":"兴业证券","jys":"sh"},{"dm":"002480","mc":"新筑股份","jys":"sz"},{"dm":"688090","mc":"瑞松科技","jys":"sh"},{"dm":"600683","mc":"京投发展","jys":"sh"},{"dm":"603728","mc":"鸣志电器","jys":"sh"},{"dm":"002649","mc":"博彦科技","jys":"sz"},{"dm":"002828","mc":"贝肯能源","jys":"sz"},{"dm":"301153","mc":"中科江南","jys":"sz"},{"dm":"603927","mc":"中科软","jys":"sh"},{"dm":"301379","mc":"天山电子","jys":"sz"},{"dm":"300557","mc":"理工光科","jys":"sz"},{"dm":"301105","mc":"鸿铭股份","jys":"sz"},{"dm":"000821","mc":"京山轻机","jys":"sz"},{"dm":"603980","mc":"吉华集团","jys":"sh"},{"dm":"300353","mc":"东土科技","jys":"sz"},{"dm":"301311","mc":"昆船智能","jys":"sz"},{"dm":"300858","mc":"科拓生物","jys":"sz"},{"dm":"688309","mc":"恒誉环保","jys":"sh"},{"dm":"603329","mc":"上海雅仕","jys":"sh"},{"dm":"002479","mc":"富春环保","jys":"sz"},{"dm":"688022","mc":"瀚川智能","jys":"sh"},{"dm":"300201","mc":"海伦哲","jys":"sz"},{"dm":"688612","mc":"威迈斯","jys":"sh"},{"dm":"002406","mc":"远东传动","jys":"sz"},{"dm":"603273","mc":"天元智能","jys":"sh"},{"dm":"301369","mc":"联动科技","jys":"sz"},{"dm":"000766","mc":"通化金马","jys":"sz"},{"dm":"600227","mc":"赤天化","jys":"sh"},{"dm":"600016","mc":"民生银行","jys":"sh"},{"dm":"300722","mc":"新余国科","jys":"sz"},{"dm":"301032","mc":"新柴股份","jys":"sz"},{"dm":"603607","mc":"京华激光","jys":"sh"},{"dm":"688786","mc":"悦安新材","jys":"sh"},{"dm":"002975","mc":"博杰股份","jys":"sz"},{"dm":"601929","mc":"吉视传媒","jys":"sh"},{"dm":"300058","mc":"蓝色光标","jys":"sz"},{"dm":"002647","mc":"仁东控股","jys":"sz"},{"dm":"688376","mc":"美埃科技","jys":"sh"},{"dm":"300162","mc":"雷曼光电","jys":"sz"},{"dm":"002953","mc":"日丰股份","jys":"sz"},{"dm":"600722","mc":"金牛化工","jys":"sh"},{"dm":"300425","mc":"中建环能","jys":"sz"},{"dm":"000868","mc":"安凯客车","jys":"sz"},{"dm":"603511","mc":"爱慕股份","jys":"sh"},{"dm":"600549","mc":"厦门钨业","jys":"sh"},{"dm":"600202","mc":"哈空调","jys":"sh"},{"dm":"000973","mc":"佛塑科技","jys":"sz"},{"dm":"603880","mc":"ST南卫","jys":"sh"},{"dm":"603500","mc":"祥和实业","jys":"sh"},{"dm":"002465","mc":"海格通信","jys":"sz"},{"dm":"300127","mc":"银河磁体","jys":"sz"},{"dm":"600653","mc":"申华控股","jys":"sh"},{"dm":"300969","mc":"恒帅股份","jys":"sz"},{"dm":"600751","mc":"海航科技","jys":"sh"},{"dm":"301339","mc":"通行宝","jys":"sz"},{"dm":"002547","mc":"春兴精工","jys":"sz"},{"dm":"300466","mc":"赛摩智能","jys":"sz"},{"dm":"002117","mc":"东港股份","jys":"sz"},{"dm":"002820","mc":"桂发祥","jys":"sz"},{"dm":"603221","mc":"爱丽家居","jys":"sh"},{"dm":"603800","mc":"道森股份","jys":"sh"},{"dm":"603610","mc":"麒盛科技","jys":"sh"},{"dm":"000909","mc":"ST数源","jys":"sz"},{"dm":"688075","mc":"安旭生物","jys":"sh"},{"dm":"300987","mc":"川网传媒","jys":"sz"},{"dm":"603178","mc":"圣龙股份","jys":"sh"},{"dm":"301020","mc":"密封科技","jys":"sz"},{"dm":"002899","mc":"英派斯","jys":"sz"},{"dm":"002884","mc":"凌霄泵业","jys":"sz"},{"dm":"300228","mc":"富瑞特装","jys":"sz"},{"dm":"300380","mc":"安硕信息","jys":"sz"},{"dm":"002211","mc":"宏达新材","jys":"sz"},{"dm":"002856","mc":"美芝股份","jys":"sz"},{"dm":"002124","mc":"天邦食品","jys":"sz"},{"dm":"002063","mc":"远光软件","jys":"sz"},{"dm":"300515","mc":"三德科技","jys":"sz"},{"dm":"300509","mc":"新美星","jys":"sz"},{"dm":"002997","mc":"瑞鹄模具","jys":"sz"},{"dm":"300840","mc":"酷特智能","jys":"sz"},{"dm":"000040","mc":"东旭蓝天","jys":"sz"},{"dm":"600161","mc":"天坛生物","jys":"sh"},{"dm":"688225","mc":"亚信安全","jys":"sh"},{"dm":"600352","mc":"浙江龙盛","jys":"sh"},{"dm":"301272","mc":"英华特","jys":"sz"},{"dm":"300872","mc":"天阳科技","jys":"sz"},{"dm":"300625","mc":"三雄极光","jys":"sz"},{"dm":"300516","mc":"久之洋","jys":"sz"},{"dm":"688248","mc":"南网科技","jys":"sh"},{"dm":"688661","mc":"和林微纳","jys":"sh"},{"dm":"603681","mc":"永冠新材","jys":"sh"},{"dm":"300478","mc":"杭州高新","jys":"sz"},{"dm":"002184","mc":"海得控制","jys":"sz"},{"dm":"600206","mc":"有研新材","jys":"sh"},{"dm":"300386","mc":"飞天诚信","jys":"sz"},{"dm":"300885","mc":"海昌新材","jys":"sz"},{"dm":"300306","mc":"远方信息","jys":"sz"},{"dm":"300069","mc":"金利华电","jys":"sz"},{"dm":"000521","mc":"长虹美菱","jys":"sz"},{"dm":"603859","mc":"能科科技","jys":"sh"},{"dm":"002298","mc":"中电兴发","jys":"sz"},{"dm":"300991","mc":"创益通","jys":"sz"},{"dm":"301080","mc":"百普赛斯","jys":"sz"},{"dm":"002915","mc":"中欣氟材","jys":"sz"},{"dm":"000960","mc":"锡业股份","jys":"sz"},{"dm":"002852","mc":"道道全","jys":"sz"},{"dm":"300200","mc":"高盟新材","jys":"sz"},{"dm":"600051","mc":"宁波联合","jys":"sh"},{"dm":"605099","mc":"共创草坪","jys":"sh"},{"dm":"002274","mc":"华昌化工","jys":"sz"},{"dm":"300464","mc":"星徽股份","jys":"sz"},{"dm":"000752","mc":"*ST西发","jys":"sz"},{"dm":"688378","mc":"奥来德","jys":"sh"},{"dm":"002772","mc":"众兴菌业","jys":"sz"},{"dm":"300050","mc":"世纪鼎利","jys":"sz"},{"dm":"603166","mc":"福达股份","jys":"sh"},{"dm":"600365","mc":"ST通葡","jys":"sh"},{"dm":"300283","mc":"温州宏丰","jys":"sz"},{"dm":"600877","mc":"电科芯片","jys":"sh"},{"dm":"002196","mc":"方正电机","jys":"sz"},{"dm":"000576","mc":"甘化科工","jys":"sz"},{"dm":"301307","mc":"美利信","jys":"sz"},{"dm":"003001","mc":"中岩大地","jys":"sz"},{"dm":"600566","mc":"济川药业","jys":"sh"},{"dm":"603848","mc":"好太太","jys":"sh"},{"dm":"600083","mc":"博信股份","jys":"sh"},{"dm":"600319","mc":"亚星化学","jys":"sh"},{"dm":"002815","mc":"崇达技术","jys":"sz"},{"dm":"300035","mc":"中科电气","jys":"sz"},{"dm":"688701","mc":"卓锦股份","jys":"sh"},{"dm":"600366","mc":"宁波韵升","jys":"sh"},{"dm":"300578","mc":"会畅通讯","jys":"sz"},{"dm":"601066","mc":"中信建投","jys":"sh"},{"dm":"300111","mc":"向日葵","jys":"sz"},{"dm":"301237","mc":"和顺科技","jys":"sz"},{"dm":"001215","mc":"千味央厨","jys":"sz"},{"dm":"301259","mc":"艾布鲁","jys":"sz"},{"dm":"600461","mc":"洪城环境","jys":"sh"},{"dm":"301072","mc":"中捷精工","jys":"sz"},{"dm":"300075","mc":"数字政通","jys":"sz"},{"dm":"002528","mc":"英飞拓","jys":"sz"},{"dm":"000420","mc":"吉林化纤","jys":"sz"},{"dm":"002350","mc":"北京科锐","jys":"sz"},{"dm":"600448","mc":"华纺股份","jys":"sh"},{"dm":"002373","mc":"千方科技","jys":"sz"},{"dm":"300369","mc":"绿盟科技","jys":"sz"},{"dm":"301337","mc":"亚华电子","jys":"sz"},{"dm":"300590","mc":"移为通信","jys":"sz"},{"dm":"600548","mc":"深高速","jys":"sh"},{"dm":"000969","mc":"安泰科技","jys":"sz"},{"dm":"688262","mc":"国芯科技","jys":"sh"},{"dm":"000970","mc":"中科三环","jys":"sz"},{"dm":"002206","mc":"海 利 得","jys":"sz"},{"dm":"001202","mc":"炬申股份","jys":"sz"},{"dm":"002489","mc":"浙江永强","jys":"sz"},{"dm":"300785","mc":"值得买","jys":"sz"},{"dm":"688306","mc":"均普智能","jys":"sh"},{"dm":"002168","mc":"惠程科技","jys":"sz"},{"dm":"688073","mc":"毕得医药","jys":"sh"},{"dm":"002017","mc":"东信和平","jys":"sz"},{"dm":"601828","mc":"美凯龙","jys":"sh"},{"dm":"002174","mc":"游族网络","jys":"sz"},{"dm":"300716","mc":"泉为科技","jys":"sz"},{"dm":"301327","mc":"华宝新能","jys":"sz"},{"dm":"300170","mc":"汉得信息","jys":"sz"},{"dm":"300110","mc":"华仁药业","jys":"sz"},{"dm":"002140","mc":"东华科技","jys":"sz"},{"dm":"600768","mc":"宁波富邦","jys":"sh"},{"dm":"600081","mc":"东风科技","jys":"sh"},{"dm":"002879","mc":"长缆科技","jys":"sz"},{"dm":"002652","mc":"扬子新材","jys":"sz"},{"dm":"000798","mc":"中水渔业","jys":"sz"},{"dm":"603679","mc":"华体科技","jys":"sh"},{"dm":"603161","mc":"科华控股","jys":"sh"},{"dm":"001205","mc":"盛航股份","jys":"sz"},{"dm":"600302","mc":"标准股份","jys":"sh"},{"dm":"603505","mc":"金石资源","jys":"sh"},{"dm":"002011","mc":"盾安环境","jys":"sz"},{"dm":"003030","mc":"祖名股份","jys":"sz"},{"dm":"000551","mc":"创元科技","jys":"sz"},{"dm":"002796","mc":"世嘉科技","jys":"sz"},{"dm":"603311","mc":"金海高科","jys":"sh"},{"dm":"000682","mc":"东方电子","jys":"sz"},{"dm":"000572","mc":"海马汽车","jys":"sz"},{"dm":"601698","mc":"中国卫通","jys":"sh"},{"dm":"688272","mc":"*ST富吉","jys":"sh"},{"dm":"603121","mc":"华培动力","jys":"sh"},{"dm":"300108","mc":"ST吉药","jys":"sz"},{"dm":"300920","mc":"润阳科技","jys":"sz"},{"dm":"300432","mc":"富临精工","jys":"sz"},{"dm":"600236","mc":"桂冠电力","jys":"sh"},{"dm":"000949","mc":"新乡化纤","jys":"sz"},{"dm":"300565","mc":"科信技术","jys":"sz"},{"dm":"300042","mc":"朗科科技","jys":"sz"},{"dm":"000004","mc":"国华网安","jys":"sz"},{"dm":"603950","mc":"长源东谷","jys":"sh"},{"dm":"600810","mc":"神马股份","jys":"sh"},{"dm":"002050","mc":"三花智控","jys":"sz"},{"dm":"600381","mc":"青海春天","jys":"sh"},{"dm":"605056","mc":"咸亨国际","jys":"sh"},{"dm":"688008","mc":"澜起科技","jys":"sh"},{"dm":"300268","mc":"*ST佳沃","jys":"sz"},{"dm":"688215","mc":"瑞晟智能","jys":"sh"},{"dm":"603879","mc":"永悦科技","jys":"sh"},{"dm":"300809","mc":"华辰装备","jys":"sz"},{"dm":"000014","mc":"沙河股份","jys":"sz"},{"dm":"002098","mc":"浔兴股份","jys":"sz"},{"dm":"688125","mc":"安达智能","jys":"sh"},{"dm":"300189","mc":"神农科技","jys":"sz"},{"dm":"300632","mc":"光莆股份","jys":"sz"},{"dm":"001210","mc":"金房能源","jys":"sz"},{"dm":"301499","mc":"维科精密","jys":"sz"},{"dm":"002909","mc":"集泰股份","jys":"sz"},{"dm":"600730","mc":"中国高科","jys":"sh"},{"dm":"600468","mc":"百利电气","jys":"sh"},{"dm":"605500","mc":"森林包装","jys":"sh"},{"dm":"603703","mc":"盛洋科技","jys":"sh"},{"dm":"000622","mc":"恒立实业","jys":"sz"},{"dm":"603757","mc":"大元泵业","jys":"sh"},{"dm":"603958","mc":"哈森股份","jys":"sh"},{"dm":"002288","mc":"超华科技","jys":"sz"},{"dm":"002286","mc":"保龄宝","jys":"sz"},{"dm":"300903","mc":"科翔股份","jys":"sz"},{"dm":"605028","mc":"世茂能源","jys":"sh"},{"dm":"300582","mc":"英飞特","jys":"sz"},{"dm":"688403","mc":"汇成股份","jys":"sh"},{"dm":"001317","mc":"三羊马","jys":"sz"},{"dm":"601000","mc":"唐山港","jys":"sh"},{"dm":"603569","mc":"长久物流","jys":"sh"},{"dm":"688080","mc":"映翰通","jys":"sh"},{"dm":"300644","mc":"南京聚隆","jys":"sz"},{"dm":"300036","mc":"超图软件","jys":"sz"},{"dm":"603320","mc":"迪贝电气","jys":"sh"},{"dm":"002159","mc":"三特索道","jys":"sz"},{"dm":"600831","mc":"广电网络","jys":"sh"},{"dm":"603960","mc":"克来机电","jys":"sh"},{"dm":"603709","mc":"中源家居","jys":"sh"},{"dm":"603006","mc":"联明股份","jys":"sh"},{"dm":"600638","mc":"新黄浦","jys":"sh"},{"dm":"002323","mc":"雅博股份","jys":"sz"},{"dm":"301229","mc":"纽泰格","jys":"sz"},{"dm":"603516","mc":"淳中科技","jys":"sh"},{"dm":"688270","mc":"臻镭科技","jys":"sh"},{"dm":"002290","mc":"禾盛新材","jys":"sz"},{"dm":"688567","mc":"孚能科技","jys":"sh"},{"dm":"688338","mc":"赛科希德","jys":"sh"},{"dm":"002474","mc":"榕基软件","jys":"sz"},{"dm":"601126","mc":"四方股份","jys":"sh"},{"dm":"600261","mc":"阳光照明","jys":"sh"},{"dm":"600198","mc":"大唐电信","jys":"sh"},{"dm":"300382","mc":"斯莱克","jys":"sz"},{"dm":"300017","mc":"网宿科技","jys":"sz"},{"dm":"301487","mc":"盟固利","jys":"sz"},{"dm":"002363","mc":"隆基机械","jys":"sz"},{"dm":"002278","mc":"神开股份","jys":"sz"},{"dm":"688109","mc":"品茗科技","jys":"sh"},{"dm":"300917","mc":"特发服务","jys":"sz"},{"dm":"000691","mc":"亚太实业","jys":"sz"},{"dm":"601288","mc":"农业银行","jys":"sh"},{"dm":"688206","mc":"概伦电子","jys":"sh"},{"dm":"600169","mc":"太原重工","jys":"sh"},{"dm":"002181","mc":"粤 传 媒","jys":"sz"},{"dm":"000413","mc":"东旭光电","jys":"sz"},{"dm":"300072","mc":"海新能科","jys":"sz"},{"dm":"002599","mc":"盛通股份","jys":"sz"},{"dm":"000880","mc":"潍柴重机","jys":"sz"},{"dm":"600526","mc":"菲达环保","jys":"sh"},{"dm":"600135","mc":"乐凯胶片","jys":"sh"},{"dm":"688420","mc":"美腾科技","jys":"sh"},{"dm":"300465","mc":"高伟达","jys":"sz"},{"dm":"688175","mc":"高凌信息","jys":"sh"},{"dm":"300743","mc":"天地数码","jys":"sz"},{"dm":"688062","mc":"迈威生物-U","jys":"sh"},{"dm":"688772","mc":"珠海冠宇","jys":"sh"},{"dm":"300774","mc":"倍杰特","jys":"sz"},{"dm":"301548","mc":"崇德科技","jys":"sz"},{"dm":"600737","mc":"中粮糖业","jys":"sh"},{"dm":"301361","mc":"众智科技","jys":"sz"},{"dm":"600784","mc":"鲁银投资","jys":"sh"},{"dm":"600580","mc":"卧龙电驱","jys":"sh"},{"dm":"600571","mc":"信雅达","jys":"sh"},{"dm":"300563","mc":"神宇股份","jys":"sz"},{"dm":"600272","mc":"开开实业","jys":"sh"},{"dm":"001283","mc":"豪鹏科技","jys":"sz"},{"dm":"300505","mc":"川金诺","jys":"sz"},{"dm":"002574","mc":"明牌珠宝","jys":"sz"},{"dm":"300180","mc":"华峰超纤","jys":"sz"},{"dm":"300099","mc":"尤洛卡","jys":"sz"},{"dm":"301132","mc":"满坤科技","jys":"sz"},{"dm":"688213","mc":"思特威-W","jys":"sh"},{"dm":"300483","mc":"首华燃气","jys":"sz"},{"dm":"688655","mc":"迅捷兴","jys":"sh"},{"dm":"601939","mc":"建设银行","jys":"sh"},{"dm":"002560","mc":"通达股份","jys":"sz"},{"dm":"300119","mc":"瑞普生物","jys":"sz"},{"dm":"301517","mc":"陕西华达","jys":"sz"},{"dm":"600408","mc":"安泰集团","jys":"sh"},{"dm":"002282","mc":"博深股份","jys":"sz"},{"dm":"002270","mc":"华明装备","jys":"sz"},{"dm":"300459","mc":"汤姆猫","jys":"sz"},{"dm":"301286","mc":"侨源股份","jys":"sz"},{"dm":"688400","mc":"凌云光","jys":"sh"},{"dm":"002213","mc":"大为股份","jys":"sz"},{"dm":"688428","mc":"诺诚健华-U","jys":"sh"},{"dm":"603998","mc":"方盛制药","jys":"sh"},{"dm":"000761","mc":"本钢板材","jys":"sz"},{"dm":"603053","mc":"成都燃气","jys":"sh"},{"dm":"000536","mc":"华映科技","jys":"sz"},{"dm":"002431","mc":"棕榈股份","jys":"sz"},{"dm":"300957","mc":"贝泰妮","jys":"sz"},{"dm":"002644","mc":"佛慈制药","jys":"sz"},{"dm":"002863","mc":"今飞凯达","jys":"sz"},{"dm":"002971","mc":"和远气体","jys":"sz"},{"dm":"603580","mc":"艾艾精工","jys":"sh"},{"dm":"300624","mc":"万兴科技","jys":"sz"},{"dm":"300600","mc":"国瑞科技","jys":"sz"},{"dm":"301293","mc":"三博脑科","jys":"sz"},{"dm":"300615","mc":"欣天科技","jys":"sz"},{"dm":"600610","mc":"中毅达","jys":"sh"},{"dm":"002376","mc":"新北洋","jys":"sz"},{"dm":"603081","mc":"大丰实业","jys":"sh"},{"dm":"300070","mc":"碧水源","jys":"sz"},{"dm":"002089","mc":"*ST新海","jys":"sz"},{"dm":"601881","mc":"中国银河","jys":"sh"},{"dm":"301178","mc":"天亿马","jys":"sz"},{"dm":"600072","mc":"中船科技","jys":"sh"},{"dm":"300133","mc":"华策影视","jys":"sz"},{"dm":"300460","mc":"惠伦晶体","jys":"sz"},{"dm":"300437","mc":"清水源","jys":"sz"},{"dm":"002516","mc":"旷达科技","jys":"sz"},{"dm":"002755","mc":"奥赛康","jys":"sz"},{"dm":"605288","mc":"凯迪股份","jys":"sh"},{"dm":"002115","mc":"三维通信","jys":"sz"},{"dm":"002188","mc":"中天服务","jys":"sz"},{"dm":"688401","mc":"路维光电","jys":"sh"},{"dm":"000548","mc":"湖南投资","jys":"sz"},{"dm":"600732","mc":"爱旭股份","jys":"sh"},{"dm":"688592","mc":"司南导航","jys":"sh"},{"dm":"688660","mc":"电气风电","jys":"sh"},{"dm":"300977","mc":"深圳瑞捷","jys":"sz"},{"dm":"600084","mc":"中信尼雅","jys":"sh"},{"dm":"002380","mc":"科远智慧","jys":"sz"},{"dm":"000061","mc":"农 产 品","jys":"sz"},{"dm":"603100","mc":"川仪股份","jys":"sh"},{"dm":"600714","mc":"金瑞矿业","jys":"sh"},{"dm":"600368","mc":"五洲交通","jys":"sh"},{"dm":"301226","mc":"祥明智能","jys":"sz"},{"dm":"600969","mc":"郴电国际","jys":"sh"},{"dm":"600313","mc":"农发种业","jys":"sh"},{"dm":"600757","mc":"长江传媒","jys":"sh"},{"dm":"002655","mc":"共达电声","jys":"sz"},{"dm":"603949","mc":"雪龙集团","jys":"sh"},{"dm":"601360","mc":"三六零","jys":"sh"},{"dm":"600886","mc":"国投电力","jys":"sh"},{"dm":"301329","mc":"信音电子","jys":"sz"},{"dm":"002495","mc":"佳隆股份","jys":"sz"},{"dm":"688500","mc":"*ST慧辰","jys":"sh"},{"dm":"002600","mc":"领益智造","jys":"sz"},{"dm":"300982","mc":"苏文电能","jys":"sz"},{"dm":"603712","mc":"七一二","jys":"sh"},{"dm":"601375","mc":"中原证券","jys":"sh"},{"dm":"605069","mc":"正和生态","jys":"sh"},{"dm":"688299","mc":"长阳科技","jys":"sh"},{"dm":"603270","mc":"金帝股份","jys":"sh"},{"dm":"300538","mc":"同益股份","jys":"sz"},{"dm":"002040","mc":"南 京 港","jys":"sz"},{"dm":"301355","mc":"南王科技","jys":"sz"},{"dm":"000790","mc":"华神科技","jys":"sz"},{"dm":"301081","mc":"严牌股份","jys":"sz"},{"dm":"603033","mc":"三维股份","jys":"sh"},{"dm":"600213","mc":"亚星客车","jys":"sh"},{"dm":"688466","mc":"金科环境","jys":"sh"},{"dm":"300817","mc":"双飞集团","jys":"sz"},{"dm":"300671","mc":"富满微","jys":"sz"},{"dm":"688227","mc":"品高股份","jys":"sh"},{"dm":"600157","mc":"永泰能源","jys":"sh"},{"dm":"002537","mc":"海联金汇","jys":"sz"},{"dm":"300066","mc":"三川智慧","jys":"sz"},{"dm":"605259","mc":"绿田机械","jys":"sh"},{"dm":"002969","mc":"嘉美包装","jys":"sz"},{"dm":"600481","mc":"双良节能","jys":"sh"},{"dm":"300847","mc":"中船汉光","jys":"sz"},{"dm":"002194","mc":"武汉凡谷","jys":"sz"},{"dm":"000659","mc":"珠海中富","jys":"sz"},{"dm":"688127","mc":"蓝特光学","jys":"sh"},{"dm":"000933","mc":"神火股份","jys":"sz"},{"dm":"002629","mc":"仁智股份","jys":"sz"},{"dm":"688060","mc":"云涌科技","jys":"sh"},{"dm":"301314","mc":"科瑞思","jys":"sz"},{"dm":"600277","mc":"亿利洁能","jys":"sh"},{"dm":"002047","mc":"宝鹰股份","jys":"sz"},{"dm":"300468","mc":"四方精创","jys":"sz"},{"dm":"688281","mc":"华秦科技","jys":"sh"},{"dm":"300124","mc":"汇川技术","jys":"sz"},{"dm":"301359","mc":"东南电子","jys":"sz"},{"dm":"300649","mc":"杭州园林","jys":"sz"},{"dm":"300579","mc":"数字认证","jys":"sz"},{"dm":"300663","mc":"科蓝软件","jys":"sz"},{"dm":"600725","mc":"云维股份","jys":"sh"},{"dm":"600279","mc":"重庆港","jys":"sh"},{"dm":"688366","mc":"昊海生科","jys":"sh"},{"dm":"601696","mc":"中银证券","jys":"sh"},{"dm":"002161","mc":"远 望 谷","jys":"sz"},{"dm":"688697","mc":"纽威数控","jys":"sh"},{"dm":"001228","mc":"永泰运","jys":"sz"},{"dm":"301320","mc":"豪江智能","jys":"sz"},{"dm":"603700","mc":"宁水集团","jys":"sh"},{"dm":"002848","mc":"高斯贝尔","jys":"sz"},{"dm":"002799","mc":"环球印务","jys":"sz"},{"dm":"002002","mc":"ST鸿达","jys":"sz"},{"dm":"002527","mc":"新时达","jys":"sz"},{"dm":"002822","mc":"中装建设","jys":"sz"},{"dm":"600551","mc":"时代出版","jys":"sh"},{"dm":"688083","mc":"中望软件","jys":"sh"},{"dm":"600218","mc":"全柴动力","jys":"sh"},{"dm":"000533","mc":"顺钠股份","jys":"sz"},{"dm":"600712","mc":"南宁百货","jys":"sh"},{"dm":"603633","mc":"徕木股份","jys":"sh"},{"dm":"600837","mc":"海通证券","jys":"sh"},{"dm":"002743","mc":"富煌钢构","jys":"sz"},{"dm":"300448","mc":"浩云科技","jys":"sz"},{"dm":"301283","mc":"聚胶股份","jys":"sz"},{"dm":"603803","mc":"瑞斯康达","jys":"sh"},{"dm":"603860","mc":"中公高科","jys":"sh"},{"dm":"002995","mc":"天地在线","jys":"sz"},{"dm":"688689","mc":"银河微电","jys":"sh"},{"dm":"000586","mc":"汇源通信","jys":"sz"},{"dm":"000035","mc":"中国天楹","jys":"sz"},{"dm":"300554","mc":"三超新材","jys":"sz"},{"dm":"300698","mc":"万马科技","jys":"sz"},{"dm":"002032","mc":"苏 泊 尔","jys":"sz"},{"dm":"000702","mc":"正虹科技","jys":"sz"},{"dm":"300751","mc":"迈为股份","jys":"sz"},{"dm":"002069","mc":"獐子岛","jys":"sz"},{"dm":"688450","mc":"光格科技","jys":"sh"},{"dm":"600805","mc":"悦达投资","jys":"sh"},{"dm":"300154","mc":"瑞凌股份","jys":"sz"},{"dm":"002972","mc":"科安达","jys":"sz"},{"dm":"300020","mc":"银江技术","jys":"sz"},{"dm":"605580","mc":"恒盛能源","jys":"sh"},{"dm":"300407","mc":"凯发电气","jys":"sz"},{"dm":"002949","mc":"华阳国际","jys":"sz"},{"dm":"002569","mc":"ST步森","jys":"sz"},{"dm":"000017","mc":"深中华A","jys":"sz"},{"dm":"300305","mc":"裕兴股份","jys":"sz"},{"dm":"002514","mc":"宝馨科技","jys":"sz"},{"dm":"600238","mc":"海南椰岛","jys":"sh"},{"dm":"688391","mc":"钜泉科技","jys":"sh"},{"dm":"601019","mc":"山东出版","jys":"sh"},{"dm":"601878","mc":"浙商证券","jys":"sh"},{"dm":"600894","mc":"广日股份","jys":"sh"},{"dm":"301037","mc":"保立佳","jys":"sz"},{"dm":"300300","mc":"海峡创新","jys":"sz"},{"dm":"002675","mc":"东诚药业","jys":"sz"},{"dm":"000639","mc":"西王食品","jys":"sz"},{"dm":"603127","mc":"昭衍新药","jys":"sh"},{"dm":"002613","mc":"北玻股份","jys":"sz"},{"dm":"000661","mc":"长春高新","jys":"sz"},{"dm":"605179","mc":"一鸣食品","jys":"sh"},{"dm":"601899","mc":"紫金矿业","jys":"sh"},{"dm":"300424","mc":"航新科技","jys":"sz"},{"dm":"688557","mc":"兰剑智能","jys":"sh"},{"dm":"300989","mc":"蕾奥规划","jys":"sz"},{"dm":"603920","mc":"世运电路","jys":"sh"},{"dm":"002401","mc":"中远海科","jys":"sz"},{"dm":"300996","mc":"普联软件","jys":"sz"},{"dm":"002851","mc":"麦格米特","jys":"sz"},{"dm":"688117","mc":"圣诺生物","jys":"sh"},{"dm":"600791","mc":"京能置业","jys":"sh"},{"dm":"300245","mc":"天玑科技","jys":"sz"},{"dm":"600611","mc":"大众交通","jys":"sh"},{"dm":"002299","mc":"圣农发展","jys":"sz"},{"dm":"688603","mc":"天承科技","jys":"sh"},{"dm":"300203","mc":"聚光科技","jys":"sz"},{"dm":"300172","mc":"中电环保","jys":"sz"},{"dm":"600075","mc":"新疆天业","jys":"sh"},{"dm":"002639","mc":"雪人股份","jys":"sz"},{"dm":"301505","mc":"苏州规划","jys":"sz"},{"dm":"000803","mc":"山高环能","jys":"sz"},{"dm":"301067","mc":"显盈科技","jys":"sz"},{"dm":"000409","mc":"云鼎科技","jys":"sz"},{"dm":"300899","mc":"上海凯鑫","jys":"sz"},{"dm":"003041","mc":"真爱美家","jys":"sz"},{"dm":"300140","mc":"节能环境","jys":"sz"},{"dm":"688288","mc":"鸿泉物联","jys":"sh"},{"dm":"301197","mc":"工大科雅","jys":"sz"},{"dm":"603036","mc":"如通股份","jys":"sh"},{"dm":"688699","mc":"明微电子","jys":"sh"},{"dm":"002519","mc":"银河电子","jys":"sz"},{"dm":"300168","mc":"万达信息","jys":"sz"},{"dm":"002633","mc":"申科股份","jys":"sz"},{"dm":"301179","mc":"泽宇智能","jys":"sz"},{"dm":"603061","mc":"金海通","jys":"sh"},{"dm":"688622","mc":"禾信仪器","jys":"sh"},{"dm":"603075","mc":"热威股份","jys":"sh"},{"dm":"688267","mc":"中触媒","jys":"sh"},{"dm":"688517","mc":"金冠电气","jys":"sh"},{"dm":"603535","mc":"嘉诚国际","jys":"sh"},{"dm":"002105","mc":"信隆健康","jys":"sz"},{"dm":"002753","mc":"永东股份","jys":"sz"},{"dm":"605151","mc":"西上海","jys":"sh"},{"dm":"300904","mc":"威力传动","jys":"sz"},{"dm":"002871","mc":"伟隆股份","jys":"sz"},{"dm":"300868","mc":"杰美特","jys":"sz"},{"dm":"603999","mc":"读者传媒","jys":"sh"},{"dm":"300161","mc":"华中数控","jys":"sz"},{"dm":"688485","mc":"九州一轨","jys":"sh"},{"dm":"601921","mc":"浙版传媒","jys":"sh"},{"dm":"301196","mc":"唯科科技","jys":"sz"},{"dm":"000797","mc":"中国武夷","jys":"sz"},{"dm":"600435","mc":"北方导航","jys":"sh"},{"dm":"600698","mc":"湖南天雁","jys":"sh"},{"dm":"600855","mc":"航天长峰","jys":"sh"},{"dm":"600422","mc":"昆药集团","jys":"sh"},{"dm":"300315","mc":"掌趣科技","jys":"sz"},{"dm":"603183","mc":"建研院","jys":"sh"},{"dm":"605376","mc":"博迁新材","jys":"sh"},{"dm":"300489","mc":"光智科技","jys":"sz"},{"dm":"300879","mc":"大叶股份","jys":"sz"},{"dm":"688339","mc":"亿华通-U","jys":"sh"},{"dm":"002956","mc":"西麦食品","jys":"sz"},{"dm":"688509","mc":"正元地信","jys":"sh"},{"dm":"300679","mc":"电连技术","jys":"sz"},{"dm":"002810","mc":"山东赫达","jys":"sz"},{"dm":"300282","mc":"*ST三盛","jys":"sz"},{"dm":"002996","mc":"顺博合金","jys":"sz"},{"dm":"688700","mc":"东威科技","jys":"sh"},{"dm":"300252","mc":"金信诺","jys":"sz"},{"dm":"300955","mc":"嘉亨家化","jys":"sz"},{"dm":"603738","mc":"泰晶科技","jys":"sh"},{"dm":"600872","mc":"中炬高新","jys":"sh"},{"dm":"688519","mc":"南亚新材","jys":"sh"},{"dm":"603333","mc":"尚纬股份","jys":"sh"},{"dm":"601995","mc":"中金公司","jys":"sh"},{"dm":"002926","mc":"华西证券","jys":"sz"},{"dm":"300351","mc":"永贵电器","jys":"sz"},{"dm":"688506","mc":"百利天恒-U","jys":"sh"},{"dm":"603058","mc":"永吉股份","jys":"sh"},{"dm":"300967","mc":"晓鸣股份","jys":"sz"},{"dm":"603595","mc":"东尼电子","jys":"sh"},{"dm":"300925","mc":"法本信息","jys":"sz"},{"dm":"001696","mc":"宗申动力","jys":"sz"},{"dm":"000529","mc":"广弘控股","jys":"sz"},{"dm":"605018","mc":"长华集团","jys":"sh"},{"dm":"600833","mc":"第一医药","jys":"sh"},{"dm":"603655","mc":"朗博科技","jys":"sh"},{"dm":"300882","mc":"万胜智能","jys":"sz"},{"dm":"300672","mc":"国科微","jys":"sz"},{"dm":"600667","mc":"太极实业","jys":"sh"},{"dm":"002006","mc":"精工科技","jys":"sz"},{"dm":"603608","mc":"天创时尚","jys":"sh"},{"dm":"002621","mc":"美吉姆","jys":"sz"},{"dm":"301376","mc":"致欧科技","jys":"sz"},{"dm":"688197","mc":"首药控股-U","jys":"sh"},{"dm":"300695","mc":"兆丰股份","jys":"sz"},{"dm":"600865","mc":"百大集团","jys":"sh"},{"dm":"600770","mc":"综艺股份","jys":"sh"},{"dm":"603536","mc":"惠发食品","jys":"sh"},{"dm":"603258","mc":"电魂网络","jys":"sh"},{"dm":"002440","mc":"闰土股份","jys":"sz"},{"dm":"002487","mc":"大金重工","jys":"sz"},{"dm":"300824","mc":"北鼎股份","jys":"sz"},{"dm":"300321","mc":"同大股份","jys":"sz"},{"dm":"300611","mc":"美力科技","jys":"sz"},{"dm":"000948","mc":"南天信息","jys":"sz"},{"dm":"600609","mc":"金杯汽车","jys":"sh"},{"dm":"300690","mc":"双一科技","jys":"sz"},{"dm":"000860","mc":"顺鑫农业","jys":"sz"},{"dm":"002156","mc":"通富微电","jys":"sz"},{"dm":"605011","mc":"杭州热电","jys":"sh"},{"dm":"601898","mc":"中煤能源","jys":"sh"},{"dm":"300875","mc":"捷强装备","jys":"sz"},{"dm":"600160","mc":"巨化股份","jys":"sh"},{"dm":"300814","mc":"中富电路","jys":"sz"},{"dm":"002398","mc":"垒知集团","jys":"sz"},{"dm":"003017","mc":"大洋生物","jys":"sz"},{"dm":"301160","mc":"翔楼新材","jys":"sz"},{"dm":"001217","mc":"华尔泰","jys":"sz"},{"dm":"600854","mc":"春兰股份","jys":"sh"},{"dm":"300630","mc":"普利制药","jys":"sz"},{"dm":"605098","mc":"行动教育","jys":"sh"},{"dm":"300826","mc":"测绘股份","jys":"sz"},{"dm":"301260","mc":"格力博","jys":"sz"},{"dm":"601928","mc":"凤凰传媒","jys":"sh"},{"dm":"300348","mc":"长亮科技","jys":"sz"},{"dm":"002723","mc":"小崧股份","jys":"sz"},{"dm":"603819","mc":"神力股份","jys":"sh"},{"dm":"601018","mc":"宁波港","jys":"sh"},{"dm":"603759","mc":"海天股份","jys":"sh"},{"dm":"600753","mc":"庚星股份","jys":"sh"},{"dm":"002939","mc":"长城证券","jys":"sz"},{"dm":"601113","mc":"华鼎股份","jys":"sh"},{"dm":"001211","mc":"双枪科技","jys":"sz"},{"dm":"601133","mc":"柏诚股份","jys":"sh"},{"dm":"301378","mc":"通达海","jys":"sz"},{"dm":"300352","mc":"北信源","jys":"sz"},{"dm":"000983","mc":"山西焦煤","jys":"sz"},{"dm":"000895","mc":"双汇发展","jys":"sz"},{"dm":"000510","mc":"新金路","jys":"sz"},{"dm":"603106","mc":"恒银科技","jys":"sh"},{"dm":"300647","mc":"超频三","jys":"sz"},{"dm":"688565","mc":"力源科技","jys":"sh"},{"dm":"002728","mc":"特一药业","jys":"sz"},{"dm":"002605","mc":"姚记科技","jys":"sz"},{"dm":"688063","mc":"派能科技","jys":"sh"},{"dm":"002806","mc":"华锋股份","jys":"sz"},{"dm":"301059","mc":"金三江","jys":"sz"},{"dm":"600267","mc":"海正药业","jys":"sh"},{"dm":"601088","mc":"中国神华","jys":"sh"},{"dm":"300810","mc":"中科海讯","jys":"sz"},{"dm":"603117","mc":"ST万林","jys":"sh"},{"dm":"601168","mc":"西部矿业","jys":"sh"},{"dm":"003016","mc":"欣贺股份","jys":"sz"},{"dm":"600699","mc":"均胜电子","jys":"sh"},{"dm":"601798","mc":"蓝科高新","jys":"sh"},{"dm":"000555","mc":"神州信息","jys":"sz"},{"dm":"003032","mc":"传智教育","jys":"sz"},{"dm":"000981","mc":"山子股份","jys":"sz"},{"dm":"300581","mc":"晨曦航空","jys":"sz"},{"dm":"000779","mc":"甘咨询","jys":"sz"},{"dm":"600675","mc":"中华企业","jys":"sh"},{"dm":"601900","mc":"南方传媒","jys":"sh"},{"dm":"300482","mc":"万孚生物","jys":"sz"},{"dm":"301082","mc":"久盛电气","jys":"sz"},{"dm":"600928","mc":"西安银行","jys":"sh"},{"dm":"300129","mc":"泰胜风能","jys":"sz"},{"dm":"002719","mc":"麦趣尔","jys":"sz"},{"dm":"002036","mc":"联创电子","jys":"sz"},{"dm":"001231","mc":"农心科技","jys":"sz"},{"dm":"300836","mc":"佰奥智能","jys":"sz"},{"dm":"002809","mc":"红墙股份","jys":"sz"},{"dm":"603528","mc":"多伦科技","jys":"sh"},{"dm":"000655","mc":"金岭矿业","jys":"sz"},{"dm":"300660","mc":"江苏雷利","jys":"sz"},{"dm":"002343","mc":"慈文传媒","jys":"sz"},{"dm":"600363","mc":"联创光电","jys":"sh"},{"dm":"300747","mc":"锐科激光","jys":"sz"},{"dm":"002823","mc":"凯中精密","jys":"sz"},{"dm":"605378","mc":"野马电池","jys":"sh"},{"dm":"300132","mc":"青松股份","jys":"sz"},{"dm":"300469","mc":"信息发展","jys":"sz"},{"dm":"000703","mc":"恒逸石化","jys":"sz"},{"dm":"600756","mc":"浪潮软件","jys":"sh"},{"dm":"300082","mc":"奥克股份","jys":"sz"},{"dm":"002970","mc":"锐明技术","jys":"sz"},{"dm":"001267","mc":"汇绿生态","jys":"sz"},{"dm":"688348","mc":"昱能科技","jys":"sh"},{"dm":"002264","mc":"新 华 都","jys":"sz"},{"dm":"301031","mc":"中熔电气","jys":"sz"},{"dm":"000063","mc":"中兴通讯","jys":"sz"},{"dm":"300801","mc":"泰和科技","jys":"sz"},{"dm":"002910","mc":"庄园牧场","jys":"sz"},{"dm":"300065","mc":"海兰信","jys":"sz"},{"dm":"002455","mc":"百川股份","jys":"sz"},{"dm":"300811","mc":"铂科新材","jys":"sz"},{"dm":"600822","mc":"上海物贸","jys":"sh"},{"dm":"002789","mc":"建艺集团","jys":"sz"},{"dm":"000019","mc":"深粮控股","jys":"sz"},{"dm":"300718","mc":"长盛轴承","jys":"sz"},{"dm":"688627","mc":"精智达","jys":"sh"},{"dm":"300444","mc":"双杰电气","jys":"sz"},{"dm":"688331","mc":"荣昌生物","jys":"sh"},{"dm":"688199","mc":"久日新材","jys":"sh"},{"dm":"301107","mc":"瑜欣电子","jys":"sz"},{"dm":"600399","mc":"抚顺特钢","jys":"sh"},{"dm":"002601","mc":"龙佰集团","jys":"sz"},{"dm":"688608","mc":"恒玄科技","jys":"sh"},{"dm":"600249","mc":"两面针","jys":"sh"},{"dm":"600107","mc":"美尔雅","jys":"sh"},{"dm":"688161","mc":"威高骨科","jys":"sh"},{"dm":"301362","mc":"民爆光电","jys":"sz"},{"dm":"605196","mc":"华通线缆","jys":"sh"},{"dm":"001208","mc":"华菱线缆","jys":"sz"},{"dm":"002185","mc":"华天科技","jys":"sz"},{"dm":"002730","mc":"电光科技","jys":"sz"},{"dm":"002129","mc":"TCL中环","jys":"sz"},{"dm":"688037","mc":"芯源微","jys":"sh"},{"dm":"002861","mc":"瀛通通讯","jys":"sz"},{"dm":"301155","mc":"海力风电","jys":"sz"},{"dm":"600234","mc":"科新发展","jys":"sh"},{"dm":"300906","mc":"日月明","jys":"sz"},{"dm":"002374","mc":"中锐股份","jys":"sz"},{"dm":"688015","mc":"交控科技","jys":"sh"},{"dm":"002660","mc":"茂硕电源","jys":"sz"},{"dm":"301386","mc":"未来电器","jys":"sz"},{"dm":"600188","mc":"兖矿能源","jys":"sh"},{"dm":"603507","mc":"振江股份","jys":"sh"},{"dm":"000151","mc":"中成股份","jys":"sz"},{"dm":"300071","mc":"福石控股","jys":"sz"},{"dm":"300542","mc":"新晨科技","jys":"sz"},{"dm":"002614","mc":"奥佳华","jys":"sz"},{"dm":"300412","mc":"迦南科技","jys":"sz"},{"dm":"000921","mc":"海信家电","jys":"sz"},{"dm":"001324","mc":"长青科技","jys":"sz"},{"dm":"300655","mc":"晶瑞电材","jys":"sz"},{"dm":"301213","mc":"观想科技","jys":"sz"},{"dm":"300688","mc":"创业黑马","jys":"sz"},{"dm":"688398","mc":"赛特新材","jys":"sh"},{"dm":"002980","mc":"华盛昌","jys":"sz"},{"dm":"603228","mc":"景旺电子","jys":"sh"},{"dm":"300040","mc":"九洲集团","jys":"sz"},{"dm":"601608","mc":"中信重工","jys":"sh"},{"dm":"601011","mc":"宝泰隆","jys":"sh"},{"dm":"300212","mc":"易华录","jys":"sz"},{"dm":"600990","mc":"四创电子","jys":"sh"},{"dm":"300586","mc":"美联新材","jys":"sz"},{"dm":"601777","mc":"力帆科技","jys":"sh"},{"dm":"000156","mc":"华数传媒","jys":"sz"},{"dm":"688682","mc":"霍莱沃","jys":"sh"},{"dm":"002575","mc":"群兴玩具","jys":"sz"},{"dm":"000735","mc":"罗 牛 山","jys":"sz"},{"dm":"600109","mc":"国金证券","jys":"sh"},{"dm":"601218","mc":"吉鑫科技","jys":"sh"},{"dm":"688139","mc":"海尔生物","jys":"sh"},{"dm":"000036","mc":"华联控股","jys":"sz"},{"dm":"300993","mc":"玉马遮阳","jys":"sz"},{"dm":"002819","mc":"东方中科","jys":"sz"},{"dm":"688106","mc":"金宏气体","jys":"sh"},{"dm":"300940","mc":"南极光","jys":"sz"},{"dm":"601811","mc":"新华文轩","jys":"sh"},{"dm":"600560","mc":"金自天正","jys":"sh"},{"dm":"002500","mc":"山西证券","jys":"sz"},{"dm":"000850","mc":"华茂股份","jys":"sz"},{"dm":"688234","mc":"天岳先进","jys":"sh"},{"dm":"002381","mc":"双箭股份","jys":"sz"},{"dm":"603798","mc":"康普顿","jys":"sh"},{"dm":"301373","mc":"凌玮科技","jys":"sz"},{"dm":"603331","mc":"百达精工","jys":"sh"},{"dm":"600235","mc":"民丰特纸","jys":"sh"},{"dm":"002913","mc":"奥士康","jys":"sz"},{"dm":"300862","mc":"蓝盾光电","jys":"sz"},{"dm":"600356","mc":"恒丰纸业","jys":"sh"},{"dm":"002093","mc":"国脉科技","jys":"sz"},{"dm":"688586","mc":"江航装备","jys":"sh"},{"dm":"603903","mc":"中持股份","jys":"sh"},{"dm":"300346","mc":"南大光电","jys":"sz"},{"dm":"603018","mc":"华设集团","jys":"sh"},{"dm":"000833","mc":"粤桂股份","jys":"sz"},{"dm":"601007","mc":"金陵饭店","jys":"sh"},{"dm":"300952","mc":"恒辉安防","jys":"sz"},{"dm":"600379","mc":"宝光股份","jys":"sh"},{"dm":"688251","mc":"井松智能","jys":"sh"},{"dm":"301150","mc":"中一科技","jys":"sz"},{"dm":"300845","mc":"捷安高科","jys":"sz"},{"dm":"603520","mc":"司太立","jys":"sh"},{"dm":"605089","mc":"味知香","jys":"sh"},{"dm":"000636","mc":"风华高科","jys":"sz"},{"dm":"300700","mc":"岱勒新材","jys":"sz"},{"dm":"688395","mc":"正弦电气","jys":"sh"},{"dm":"603290","mc":"斯达半导","jys":"sh"},{"dm":"603022","mc":"新通联","jys":"sh"},{"dm":"301233","mc":"盛帮股份","jys":"sz"},{"dm":"600850","mc":"电科数字","jys":"sh"},{"dm":"002877","mc":"智能自控","jys":"sz"},{"dm":"300670","mc":"大烨智能","jys":"sz"},{"dm":"605117","mc":"德业股份","jys":"sh"},{"dm":"603261","mc":"立航科技","jys":"sh"},{"dm":"002042","mc":"华孚时尚","jys":"sz"},{"dm":"688116","mc":"天奈科技","jys":"sh"},{"dm":"600421","mc":"华嵘控股","jys":"sh"},{"dm":"688056","mc":"莱伯泰科","jys":"sh"},{"dm":"600386","mc":"北巴传媒","jys":"sh"},{"dm":"301199","mc":"迈赫股份","jys":"sz"},{"dm":"000701","mc":"厦门信达","jys":"sz"},{"dm":"000338","mc":"潍柴动力","jys":"sz"},{"dm":"603066","mc":"音飞储存","jys":"sh"},{"dm":"300449","mc":"汉邦高科","jys":"sz"},{"dm":"603596","mc":"伯特利","jys":"sh"},{"dm":"600906","mc":"财达证券","jys":"sh"},{"dm":"300165","mc":"天瑞仪器","jys":"sz"},{"dm":"002830","mc":"名雕股份","jys":"sz"},{"dm":"000400","mc":"许继电气","jys":"sz"},{"dm":"000851","mc":"高鸿股份","jys":"sz"},{"dm":"000408","mc":"藏格矿业","jys":"sz"},{"dm":"002625","mc":"光启技术","jys":"sz"},{"dm":"300640","mc":"德艺文创","jys":"sz"},{"dm":"688390","mc":"固德威","jys":"sh"},{"dm":"603636","mc":"南威软件","jys":"sh"},{"dm":"300746","mc":"汉嘉设计","jys":"sz"},{"dm":"002364","mc":"中恒电气","jys":"sz"},{"dm":"688380","mc":"中微半导","jys":"sh"},{"dm":"300518","mc":"新迅达","jys":"sz"},{"dm":"002588","mc":"史丹利","jys":"sz"},{"dm":"002021","mc":"*ST中捷","jys":"sz"},{"dm":"603088","mc":"宁波精达","jys":"sh"},{"dm":"301291","mc":"明阳电气","jys":"sz"},{"dm":"002344","mc":"海宁皮城","jys":"sz"},{"dm":"603182","mc":"嘉华股份","jys":"sh"},{"dm":"000698","mc":"沈阳化工","jys":"sz"},{"dm":"300095","mc":"华伍股份","jys":"sz"},{"dm":"605005","mc":"合兴股份","jys":"sh"},{"dm":"600126","mc":"杭钢股份","jys":"sh"},{"dm":"301137","mc":"哈焊华通","jys":"sz"},{"dm":"002637","mc":"赞宇科技","jys":"sz"},{"dm":"300136","mc":"信维通信","jys":"sz"},{"dm":"600220","mc":"江苏阳光","jys":"sh"},{"dm":"002689","mc":"远大智能","jys":"sz"},{"dm":"300531","mc":"优博讯","jys":"sz"},{"dm":"002236","mc":"大华股份","jys":"sz"},{"dm":"600676","mc":"交运股份","jys":"sh"},{"dm":"600962","mc":"国投中鲁","jys":"sh"},{"dm":"688528","mc":"秦川物联","jys":"sh"},{"dm":"000403","mc":"派林生物","jys":"sz"},{"dm":"002534","mc":"西子洁能","jys":"sz"},{"dm":"301193","mc":"家联科技","jys":"sz"},{"dm":"688768","mc":"容知日新","jys":"sh"},{"dm":"600881","mc":"亚泰集团","jys":"sh"},{"dm":"002221","mc":"东华能源","jys":"sz"},{"dm":"300190","mc":"维尔利","jys":"sz"},{"dm":"300008","mc":"天海防务","jys":"sz"},{"dm":"002072","mc":"凯瑞德","jys":"sz"},{"dm":"600203","mc":"福日电子","jys":"sh"},{"dm":"603893","mc":"瑞芯微","jys":"sh"},{"dm":"600996","mc":"贵广网络","jys":"sh"},{"dm":"300759","mc":"康龙化成","jys":"sz"},{"dm":"600590","mc":"泰豪科技","jys":"sh"},{"dm":"300295","mc":"三六五网","jys":"sz"},{"dm":"688165","mc":"埃夫特-U","jys":"sh"},{"dm":"300673","mc":"佩蒂股份","jys":"sz"},{"dm":"600797","mc":"浙大网新","jys":"sh"},{"dm":"300411","mc":"金盾股份","jys":"sz"},{"dm":"603938","mc":"三孚股份","jys":"sh"},{"dm":"688665","mc":"四方光电","jys":"sh"},{"dm":"002302","mc":"西部建设","jys":"sz"},{"dm":"000557","mc":"西部创业","jys":"sz"},{"dm":"000532","mc":"华金资本","jys":"sz"},{"dm":"688597","mc":"煜邦电力","jys":"sh"},{"dm":"000430","mc":"张家界","jys":"sz"},{"dm":"001311","mc":"多利科技","jys":"sz"},{"dm":"600318","mc":"新力金融","jys":"sh"},{"dm":"002208","mc":"合肥城建","jys":"sz"},{"dm":"301318","mc":"维海德","jys":"sz"},{"dm":"002449","mc":"国星光电","jys":"sz"},{"dm":"002923","mc":"润都股份","jys":"sz"},{"dm":"002833","mc":"弘亚数控","jys":"sz"},{"dm":"000913","mc":"钱江摩托","jys":"sz"},{"dm":"300047","mc":"天源迪科","jys":"sz"},{"dm":"300479","mc":"神思电子","jys":"sz"},{"dm":"002617","mc":"露笑科技","jys":"sz"},{"dm":"002144","mc":"宏达高科","jys":"sz"},{"dm":"688981","mc":"中芯国际","jys":"sh"},{"dm":"301519","mc":"舜禹股份","jys":"sz"},{"dm":"002866","mc":"传艺科技","jys":"sz"},{"dm":"603717","mc":"天域生态","jys":"sh"},{"dm":"002155","mc":"湖南黄金","jys":"sz"},{"dm":"600444","mc":"国机通用","jys":"sh"},{"dm":"603408","mc":"建霖家居","jys":"sh"},{"dm":"603101","mc":"汇嘉时代","jys":"sh"},{"dm":"600278","mc":"东方创业","jys":"sh"},{"dm":"688135","mc":"利扬芯片","jys":"sh"},{"dm":"603790","mc":"雅运股份","jys":"sh"},{"dm":"002841","mc":"视源股份","jys":"sz"},{"dm":"000723","mc":"美锦能源","jys":"sz"},{"dm":"688092","mc":"爱科科技","jys":"sh"},{"dm":"688315","mc":"诺禾致源","jys":"sh"},{"dm":"300724","mc":"捷佳伟创","jys":"sz"},{"dm":"688656","mc":"浩欧博","jys":"sh"},{"dm":"603600","mc":"永艺股份","jys":"sh"},{"dm":"301148","mc":"嘉戎技术","jys":"sz"},{"dm":"603288","mc":"海天味业","jys":"sh"},{"dm":"688271","mc":"联影医疗","jys":"sh"},{"dm":"603069","mc":"海汽集团","jys":"sh"},{"dm":"001225","mc":"和泰机电","jys":"sz"},{"dm":"603499","mc":"翔港科技","jys":"sh"},{"dm":"000008","mc":"神州高铁","jys":"sz"},{"dm":"002267","mc":"陕天然气","jys":"sz"},{"dm":"000877","mc":"天山股份","jys":"sz"},{"dm":"600152","mc":"维科技术","jys":"sh"},{"dm":"603133","mc":"*ST碳元","jys":"sh"},{"dm":"688659","mc":"元琛科技","jys":"sh"},{"dm":"600327","mc":"大东方","jys":"sh"},{"dm":"300860","mc":"锋尚文化","jys":"sz"},{"dm":"001323","mc":"慕思股份","jys":"sz"},{"dm":"002890","mc":"弘宇股份","jys":"sz"},{"dm":"603696","mc":"安记食品","jys":"sh"},{"dm":"600736","mc":"苏州高新","jys":"sh"},{"dm":"300238","mc":"冠昊生物","jys":"sz"},{"dm":"300243","mc":"瑞丰高材","jys":"sz"},{"dm":"300756","mc":"金马游乐","jys":"sz"},{"dm":"300865","mc":"大宏立","jys":"sz"},{"dm":"688089","mc":"嘉必优","jys":"sh"},{"dm":"688350","mc":"富淼科技","jys":"sh"},{"dm":"000901","mc":"航天科技","jys":"sz"},{"dm":"002079","mc":"苏州固锝","jys":"sz"},{"dm":"301076","mc":"新瀚新材","jys":"sz"},{"dm":"300706","mc":"阿石创","jys":"sz"},{"dm":"002095","mc":"生 意 宝","jys":"sz"},{"dm":"000715","mc":"中兴商业","jys":"sz"},{"dm":"600210","mc":"紫江企业","jys":"sh"},{"dm":"002746","mc":"仙坛股份","jys":"sz"},{"dm":"301000","mc":"肇民科技","jys":"sz"},{"dm":"603068","mc":"博通集成","jys":"sh"},{"dm":"600819","mc":"耀皮玻璃","jys":"sh"},{"dm":"300852","mc":"四会富仕","jys":"sz"},{"dm":"301138","mc":"华研精机","jys":"sz"},{"dm":"300222","mc":"科大智能","jys":"sz"},{"dm":"002999","mc":"天禾股份","jys":"sz"},{"dm":"603968","mc":"醋化股份","jys":"sh"},{"dm":"603815","mc":"交建股份","jys":"sh"},{"dm":"600103","mc":"青山纸业","jys":"sh"},{"dm":"300402","mc":"宝色股份","jys":"sz"},{"dm":"000812","mc":"陕西金叶","jys":"sz"},{"dm":"603931","mc":"格林达","jys":"sh"},{"dm":"601010","mc":"文峰股份","jys":"sh"},{"dm":"688244","mc":"永信至诚","jys":"sh"},{"dm":"001366","mc":"播恩集团","jys":"sz"},{"dm":"300786","mc":"国林科技","jys":"sz"},{"dm":"002603","mc":"以岭药业","jys":"sz"},{"dm":"002645","mc":"华宏科技","jys":"sz"},{"dm":"002783","mc":"凯龙股份","jys":"sz"},{"dm":"002666","mc":"德联集团","jys":"sz"},{"dm":"600637","mc":"东方明珠","jys":"sh"},{"dm":"603041","mc":"美思德","jys":"sh"},{"dm":"600893","mc":"航发动力","jys":"sh"},{"dm":"300458","mc":"全志科技","jys":"sz"},{"dm":"603227","mc":"雪峰科技","jys":"sh"},{"dm":"300527","mc":"中船应急","jys":"sz"},{"dm":"688069","mc":"德林海","jys":"sh"},{"dm":"600259","mc":"广晟有色","jys":"sh"},{"dm":"600777","mc":"新潮能源","jys":"sh"},{"dm":"600829","mc":"人民同泰","jys":"sh"},{"dm":"688016","mc":"心脉医疗","jys":"sh"},{"dm":"600540","mc":"新赛股份","jys":"sh"},{"dm":"000090","mc":"天健集团","jys":"sz"},{"dm":"301056","mc":"森赫股份","jys":"sz"},{"dm":"002158","mc":"汉钟精机","jys":"sz"},{"dm":"300966","mc":"共同药业","jys":"sz"},{"dm":"300787","mc":"海能实业","jys":"sz"},{"dm":"688193","mc":"仁度生物","jys":"sh"},{"dm":"301303","mc":"真兰仪表","jys":"sz"},{"dm":"600636","mc":"国新文化","jys":"sh"},{"dm":"600512","mc":"腾达建设","jys":"sh"},{"dm":"002542","mc":"中化岩土","jys":"sz"},{"dm":"002620","mc":"瑞和股份","jys":"sz"},{"dm":"002383","mc":"合众思壮","jys":"sz"},{"dm":"300438","mc":"鹏辉能源","jys":"sz"},{"dm":"000830","mc":"鲁西化工","jys":"sz"},{"dm":"300656","mc":"民德电子","jys":"sz"},{"dm":"600348","mc":"华阳股份","jys":"sh"},{"dm":"002502","mc":"ST鼎龙","jys":"sz"},{"dm":"603687","mc":"大胜达","jys":"sh"},{"dm":"301192","mc":"泰祥股份","jys":"sz"},{"dm":"301156","mc":"美农生物","jys":"sz"},{"dm":"300234","mc":"开尔新材","jys":"sz"},{"dm":"688203","mc":"海正生材","jys":"sh"},{"dm":"301510","mc":"固高科技","jys":"sz"},{"dm":"603399","mc":"吉翔股份","jys":"sh"},{"dm":"600643","mc":"爱建集团","jys":"sh"},{"dm":"688469","mc":"中芯集成-U","jys":"sh"},{"dm":"000600","mc":"建投能源","jys":"sz"},{"dm":"000989","mc":"九 芝 堂","jys":"sz"},{"dm":"002616","mc":"长青集团","jys":"sz"},{"dm":"688133","mc":"泰坦科技","jys":"sh"},{"dm":"002241","mc":"歌尔股份","jys":"sz"},{"dm":"688335","mc":"复洁环保","jys":"sh"},{"dm":"002869","mc":"金溢科技","jys":"sz"},{"dm":"605589","mc":"圣泉集团","jys":"sh"},{"dm":"300901","mc":"中胤时尚","jys":"sz"},{"dm":"688101","mc":"三达膜","jys":"sh"},{"dm":"300886","mc":"华业香料","jys":"sz"},{"dm":"301028","mc":"东亚机械","jys":"sz"},{"dm":"688038","mc":"中科通达","jys":"sh"},{"dm":"002901","mc":"大博医疗","jys":"sz"},{"dm":"600371","mc":"万向德农","jys":"sh"},{"dm":"300508","mc":"维宏股份","jys":"sz"},{"dm":"002153","mc":"石基信息","jys":"sz"},{"dm":"605366","mc":"宏柏新材","jys":"sh"},{"dm":"600678","mc":"四川金顶","jys":"sh"},{"dm":"002104","mc":"恒宝股份","jys":"sz"},{"dm":"300097","mc":"智云股份","jys":"sz"},{"dm":"600195","mc":"中牧股份","jys":"sh"},{"dm":"002832","mc":"比音勒芬","jys":"sz"},{"dm":"603085","mc":"天成自控","jys":"sh"},{"dm":"000066","mc":"中国长城","jys":"sz"},{"dm":"000990","mc":"诚志股份","jys":"sz"},{"dm":"002523","mc":"天桥起重","jys":"sz"},{"dm":"002718","mc":"友邦吊顶","jys":"sz"},{"dm":"301273","mc":"瑞晨环保","jys":"sz"},{"dm":"688381","mc":"帝奥微","jys":"sh"},{"dm":"002559","mc":"亚威股份","jys":"sz"},{"dm":"300984","mc":"金沃股份","jys":"sz"},{"dm":"603963","mc":"大理药业","jys":"sh"},{"dm":"300641","mc":"正丹股份","jys":"sz"},{"dm":"000612","mc":"焦作万方","jys":"sz"},{"dm":"688479","mc":"友车科技","jys":"sh"},{"dm":"002014","mc":"永新股份","jys":"sz"},{"dm":"600439","mc":"瑞贝卡","jys":"sh"},{"dm":"002945","mc":"华林证券","jys":"sz"},{"dm":"002330","mc":"得利斯","jys":"sz"},{"dm":"002164","mc":"宁波东力","jys":"sz"},{"dm":"600271","mc":"航天信息","jys":"sh"},{"dm":"001286","mc":"陕西能源","jys":"sz"},{"dm":"688728","mc":"格科微","jys":"sh"},{"dm":"300187","mc":"永清环保","jys":"sz"},{"dm":"601101","mc":"昊华能源","jys":"sh"},{"dm":"600844","mc":"丹化科技","jys":"sh"},{"dm":"301295","mc":"美硕科技","jys":"sz"},{"dm":"001227","mc":"兰州银行","jys":"sz"},{"dm":"603882","mc":"金域医学","jys":"sh"},{"dm":"002646","mc":"天佑德酒","jys":"sz"},{"dm":"000783","mc":"长江证券","jys":"sz"},{"dm":"300191","mc":"潜能恒信","jys":"sz"},{"dm":"601956","mc":"东贝集团","jys":"sh"},{"dm":"300849","mc":"锦盛新材","jys":"sz"},{"dm":"300665","mc":"飞鹿股份","jys":"sz"},{"dm":"600397","mc":"安源煤业","jys":"sh"},{"dm":"002586","mc":"*ST围海","jys":"sz"},{"dm":"300125","mc":"聆达股份","jys":"sz"},{"dm":"688367","mc":"工大高科","jys":"sh"},{"dm":"300152","mc":"新动力","jys":"sz"},{"dm":"301120","mc":"新特电气","jys":"sz"},{"dm":"601633","mc":"长城汽车","jys":"sh"},{"dm":"301176","mc":"逸豪新材","jys":"sz"},{"dm":"002492","mc":"恒基达鑫","jys":"sz"},{"dm":"301108","mc":"洁雅股份","jys":"sz"},{"dm":"301330","mc":"熵基科技","jys":"sz"},{"dm":"300311","mc":"任子行","jys":"sz"},{"dm":"300359","mc":"全通教育","jys":"sz"},{"dm":"002717","mc":"岭南股份","jys":"sz"},{"dm":"300566","mc":"激智科技","jys":"sz"},{"dm":"688599","mc":"天合光能","jys":"sh"},{"dm":"300740","mc":"水羊股份","jys":"sz"},{"dm":"603339","mc":"四方科技","jys":"sh"},{"dm":"003042","mc":"中农联合","jys":"sz"},{"dm":"300892","mc":"品渥食品","jys":"sz"},{"dm":"002044","mc":"美年健康","jys":"sz"},{"dm":"300675","mc":"建科院","jys":"sz"},{"dm":"301215","mc":"中汽股份","jys":"sz"},{"dm":"301011","mc":"华立科技","jys":"sz"},{"dm":"000605","mc":"渤海股份","jys":"sz"},{"dm":"603630","mc":"拉芳家化","jys":"sh"},{"dm":"600184","mc":"光电股份","jys":"sh"},{"dm":"300420","mc":"五洋停车","jys":"sz"},{"dm":"300514","mc":"友讯达","jys":"sz"},{"dm":"300224","mc":"正海磁材","jys":"sz"},{"dm":"603112","mc":"华翔股份","jys":"sh"},{"dm":"300422","mc":"博世科","jys":"sz"},{"dm":"600633","mc":"浙数文化","jys":"sh"},{"dm":"300947","mc":"德必集团","jys":"sz"},{"dm":"300145","mc":"中金环境","jys":"sz"},{"dm":"300087","mc":"荃银高科","jys":"sz"},{"dm":"002328","mc":"新朋股份","jys":"sz"},{"dm":"300067","mc":"安诺其","jys":"sz"},{"dm":"000816","mc":"智慧农业","jys":"sz"},{"dm":"605208","mc":"永茂泰","jys":"sh"},{"dm":"688369","mc":"致远互联","jys":"sh"},{"dm":"300262","mc":"巴安水务","jys":"sz"},{"dm":"002721","mc":"*ST金一","jys":"sz"},{"dm":"000545","mc":"金浦钛业","jys":"sz"},{"dm":"003011","mc":"海象新材","jys":"sz"},{"dm":"002556","mc":"辉隆股份","jys":"sz"},{"dm":"002314","mc":"南山控股","jys":"sz"},{"dm":"301163","mc":"宏德股份","jys":"sz"},{"dm":"300195","mc":"长荣股份","jys":"sz"},{"dm":"000716","mc":"黑芝麻","jys":"sz"},{"dm":"000513","mc":"丽珠集团","jys":"sz"},{"dm":"000908","mc":"景峰医药","jys":"sz"},{"dm":"600033","mc":"福建高速","jys":"sh"},{"dm":"003036","mc":"泰坦股份","jys":"sz"},{"dm":"300384","mc":"三联虹普","jys":"sz"},{"dm":"600284","mc":"浦东建设","jys":"sh"},{"dm":"000523","mc":"广州浪奇","jys":"sz"},{"dm":"603869","mc":"新智认知","jys":"sh"},{"dm":"002132","mc":"恒星科技","jys":"sz"},{"dm":"301010","mc":"晶雪节能","jys":"sz"},{"dm":"688013","mc":"天臣医疗","jys":"sh"},{"dm":"300876","mc":"蒙泰高新","jys":"sz"},{"dm":"000157","mc":"中联重科","jys":"sz"},{"dm":"002544","mc":"普天科技","jys":"sz"},{"dm":"600846","mc":"同济科技","jys":"sh"},{"dm":"001216","mc":"华瓷股份","jys":"sz"},{"dm":"605033","mc":"美邦股份","jys":"sh"},{"dm":"301287","mc":"康力源","jys":"sz"},{"dm":"688455","mc":"科捷智能","jys":"sh"},{"dm":"003022","mc":"联泓新科","jys":"sz"},{"dm":"688172","mc":"燕东微","jys":"sh"},{"dm":"300114","mc":"中航电测","jys":"sz"},{"dm":"002092","mc":"中泰化学","jys":"sz"},{"dm":"300692","mc":"中环环保","jys":"sz"},{"dm":"300927","mc":"江天化学","jys":"sz"},{"dm":"603216","mc":"梦天家居","jys":"sh"},{"dm":"600963","mc":"岳阳林纸","jys":"sh"},{"dm":"600623","mc":"华谊集团","jys":"sh"},{"dm":"603153","mc":"上海建科","jys":"sh"},{"dm":"688386","mc":"泛亚微透","jys":"sh"},{"dm":"601616","mc":"广电电气","jys":"sh"},{"dm":"600257","mc":"大湖股份","jys":"sh"},{"dm":"000922","mc":"佳电股份","jys":"sz"},{"dm":"603070","mc":"万控智造","jys":"sh"},{"dm":"300962","mc":"中金辐照","jys":"sz"},{"dm":"000722","mc":"湖南发展","jys":"sz"},{"dm":"603706","mc":"东方环宇","jys":"sh"},{"dm":"600339","mc":"中油工程","jys":"sh"},{"dm":"600818","mc":"中路股份","jys":"sh"},{"dm":"600438","mc":"通威股份","jys":"sh"},{"dm":"301127","mc":"天源环保","jys":"sz"},{"dm":"605268","mc":"王力安防","jys":"sh"},{"dm":"600979","mc":"广安爱众","jys":"sh"},{"dm":"002388","mc":"新亚制程","jys":"sz"},{"dm":"300626","mc":"华瑞股份","jys":"sz"},{"dm":"600420","mc":"国药现代","jys":"sh"},{"dm":"600343","mc":"航天动力","jys":"sh"},{"dm":"300096","mc":"易联众","jys":"sz"},{"dm":"000531","mc":"穗恒运A","jys":"sz"},{"dm":"688198","mc":"佰仁医疗","jys":"sh"},{"dm":"688355","mc":"明志科技","jys":"sh"},{"dm":"600705","mc":"中航产融","jys":"sh"},{"dm":"300905","mc":"宝丽迪","jys":"sz"},{"dm":"605287","mc":"德才股份","jys":"sh"},{"dm":"688432","mc":"有研硅","jys":"sh"},{"dm":"300567","mc":"精测电子","jys":"sz"},{"dm":"603065","mc":"宿迁联盛","jys":"sh"},{"dm":"301290","mc":"东星医疗","jys":"sz"},{"dm":"603739","mc":"蔚蓝生物","jys":"sh"},{"dm":"600573","mc":"惠泉啤酒","jys":"sh"},{"dm":"300854","mc":"中兰环保","jys":"sz"},{"dm":"000838","mc":"财信发展","jys":"sz"},{"dm":"301281","mc":"科源制药","jys":"sz"},{"dm":"002066","mc":"瑞泰科技","jys":"sz"},{"dm":"600579","mc":"克劳斯","jys":"sh"},{"dm":"600794","mc":"保税科技","jys":"sh"},{"dm":"000544","mc":"中原环保","jys":"sz"},{"dm":"600392","mc":"盛和资源","jys":"sh"},{"dm":"000863","mc":"三湘印象","jys":"sz"},{"dm":"301212","mc":"联盛化学","jys":"sz"},{"dm":"600570","mc":"恒生电子","jys":"sh"},{"dm":"301180","mc":"万祥科技","jys":"sz"},{"dm":"301061","mc":"匠心家居","jys":"sz"},{"dm":"002982","mc":"湘佳股份","jys":"sz"},{"dm":"300789","mc":"唐源电气","jys":"sz"},{"dm":"300324","mc":"旋极信息","jys":"sz"},{"dm":"301209","mc":"联合化学","jys":"sz"},{"dm":"688563","mc":"航材股份","jys":"sh"},{"dm":"300118","mc":"东方日升","jys":"sz"},{"dm":"605068","mc":"明新旭腾","jys":"sh"},{"dm":"601058","mc":"赛轮轮胎","jys":"sh"},{"dm":"600282","mc":"南钢股份","jys":"sh"},{"dm":"002392","mc":"北京利尔","jys":"sz"},{"dm":"301220","mc":"亚香股份","jys":"sz"},{"dm":"600918","mc":"中泰证券","jys":"sh"},{"dm":"600212","mc":"绿能慧充","jys":"sh"},{"dm":"300259","mc":"新天科技","jys":"sz"},{"dm":"002635","mc":"安洁科技","jys":"sz"},{"dm":"002549","mc":"凯美特气","jys":"sz"},{"dm":"600668","mc":"尖峰集团","jys":"sh"},{"dm":"000069","mc":"华侨城A","jys":"sz"},{"dm":"600120","mc":"浙江东方","jys":"sh"},{"dm":"002908","mc":"德生科技","jys":"sz"},{"dm":"001287","mc":"中电港","jys":"sz"},{"dm":"603255","mc":"鼎际得","jys":"sh"},{"dm":"300739","mc":"明阳电路","jys":"sz"},{"dm":"000750","mc":"国海证券","jys":"sz"},{"dm":"300741","mc":"华宝股份","jys":"sz"},{"dm":"002606","mc":"大连电瓷","jys":"sz"},{"dm":"002925","mc":"盈趣科技","jys":"sz"},{"dm":"300793","mc":"佳禾智能","jys":"sz"},{"dm":"603839","mc":"安正时尚","jys":"sh"},{"dm":"600976","mc":"健民集团","jys":"sh"},{"dm":"603324","mc":"盛剑环境","jys":"sh"},{"dm":"600080","mc":"金花股份","jys":"sh"},{"dm":"000633","mc":"合金投资","jys":"sz"},{"dm":"603051","mc":"鹿山新材","jys":"sh"},{"dm":"002346","mc":"柘中股份","jys":"sz"},{"dm":"600127","mc":"金健米业","jys":"sh"},{"dm":"300205","mc":"天喻信息","jys":"sz"},{"dm":"002813","mc":"路畅科技","jys":"sz"},{"dm":"000686","mc":"东北证券","jys":"sz"},{"dm":"300999","mc":"金龙鱼","jys":"sz"},{"dm":"300562","mc":"乐心医疗","jys":"sz"},{"dm":"603191","mc":"望变电气","jys":"sh"},{"dm":"601009","mc":"南京银行","jys":"sh"},{"dm":"300253","mc":"卫宁健康","jys":"sz"},{"dm":"600860","mc":"京城股份","jys":"sh"},{"dm":"301448","mc":"开创电气","jys":"sz"},{"dm":"002318","mc":"久立特材","jys":"sz"},{"dm":"600495","mc":"晋西车轴","jys":"sh"},{"dm":"001270","mc":"铖昌科技","jys":"sz"},{"dm":"002759","mc":"天际股份","jys":"sz"},{"dm":"003026","mc":"中晶科技","jys":"sz"},{"dm":"003000","mc":"劲仔食品","jys":"sz"},{"dm":"000923","mc":"河钢资源","jys":"sz"},{"dm":"002724","mc":"海洋王","jys":"sz"},{"dm":"000099","mc":"中信海直","jys":"sz"},{"dm":"601515","mc":"东风股份","jys":"sh"},{"dm":"600879","mc":"航天电子","jys":"sh"},{"dm":"000652","mc":"泰达股份","jys":"sz"},{"dm":"301345","mc":"涛涛车业","jys":"sz"},{"dm":"600836","mc":"上海易连","jys":"sh"},{"dm":"001212","mc":"中旗新材","jys":"sz"},{"dm":"601990","mc":"南京证券","jys":"sh"},{"dm":"603173","mc":"福斯达","jys":"sh"},{"dm":"301270","mc":"汉仪股份","jys":"sz"},{"dm":"002878","mc":"元隆雅图","jys":"sz"},{"dm":"300484","mc":"蓝海华腾","jys":"sz"},{"dm":"000402","mc":"金 融 街","jys":"sz"},{"dm":"300681","mc":"英搏尔","jys":"sz"},{"dm":"002650","mc":"加加食品","jys":"sz"},{"dm":"300831","mc":"派瑞股份","jys":"sz"},{"dm":"603113","mc":"金能科技","jys":"sh"},{"dm":"603933","mc":"睿能科技","jys":"sh"},{"dm":"002399","mc":"海普瑞","jys":"sz"},{"dm":"600095","mc":"湘财股份","jys":"sh"},{"dm":"300491","mc":"通合科技","jys":"sz"},{"dm":"688216","mc":"气派科技","jys":"sh"},{"dm":"600608","mc":"ST沪科","jys":"sh"},{"dm":"600982","mc":"宁波能源","jys":"sh"},{"dm":"301390","mc":"经纬股份","jys":"sz"},{"dm":"600452","mc":"涪陵电力","jys":"sh"},{"dm":"300838","mc":"浙江力诺","jys":"sz"},{"dm":"000020","mc":"深华发A","jys":"sz"},{"dm":"002227","mc":"奥 特 迅","jys":"sz"},{"dm":"000584","mc":"ST工智","jys":"sz"},{"dm":"688307","mc":"中润光学","jys":"sh"},{"dm":"688293","mc":"奥浦迈","jys":"sh"},{"dm":"000632","mc":"三木集团","jys":"sz"},{"dm":"300053","mc":"航宇微","jys":"sz"},{"dm":"688488","mc":"艾迪药业","jys":"sh"},{"dm":"300963","mc":"中洲特材","jys":"sz"},{"dm":"301078","mc":"孩子王","jys":"sz"},{"dm":"000514","mc":"渝 开 发","jys":"sz"},{"dm":"002334","mc":"英威腾","jys":"sz"},{"dm":"603810","mc":"丰山集团","jys":"sh"},{"dm":"600618","mc":"氯碱化工","jys":"sh"},{"dm":"002942","mc":"新农股份","jys":"sz"},{"dm":"300564","mc":"筑博设计","jys":"sz"},{"dm":"601022","mc":"宁波远洋","jys":"sh"},{"dm":"301198","mc":"喜悦智行","jys":"sz"},{"dm":"003008","mc":"开普检测","jys":"sz"},{"dm":"603568","mc":"伟明环保","jys":"sh"},{"dm":"600243","mc":"青海华鼎","jys":"sh"},{"dm":"301023","mc":"江南奕帆","jys":"sz"},{"dm":"300834","mc":"星辉环材","jys":"sz"},{"dm":"300960","mc":"通业科技","jys":"sz"},{"dm":"603282","mc":"亚光股份","jys":"sh"},{"dm":"600163","mc":"中闽能源","jys":"sh"},{"dm":"000755","mc":"山西路桥","jys":"sz"},{"dm":"000526","mc":"学大教育","jys":"sz"},{"dm":"688107","mc":"安路科技","jys":"sh"},{"dm":"300121","mc":"阳谷华泰","jys":"sz"},{"dm":"600663","mc":"陆家嘴","jys":"sh"},{"dm":"600650","mc":"锦江在线","jys":"sh"},{"dm":"603663","mc":"三祥新材","jys":"sh"},{"dm":"600052","mc":"东望时代","jys":"sh"},{"dm":"601398","mc":"工商银行","jys":"sh"},{"dm":"600315","mc":"上海家化","jys":"sh"},{"dm":"605339","mc":"南侨食品","jys":"sh"},{"dm":"688651","mc":"盛邦安全","jys":"sh"},{"dm":"002522","mc":"浙江众成","jys":"sz"},{"dm":"688093","mc":"世华科技","jys":"sh"},{"dm":"601068","mc":"中铝国际","jys":"sh"},{"dm":"601002","mc":"晋亿实业","jys":"sh"},{"dm":"300022","mc":"吉峰科技","jys":"sz"},{"dm":"301092","mc":"争光股份","jys":"sz"},{"dm":"301089","mc":"拓新药业","jys":"sz"},{"dm":"301040","mc":"中环海陆","jys":"sz"},{"dm":"002226","mc":"江南化工","jys":"sz"},{"dm":"300142","mc":"沃森生物","jys":"sz"},{"dm":"002737","mc":"葵花药业","jys":"sz"},{"dm":"600276","mc":"恒瑞医药","jys":"sh"},{"dm":"002145","mc":"中核钛白","jys":"sz"},{"dm":"603826","mc":"坤彩科技","jys":"sh"},{"dm":"600207","mc":"安彩高科","jys":"sh"},{"dm":"603895","mc":"天永智能","jys":"sh"},{"dm":"300487","mc":"蓝晓科技","jys":"sz"},{"dm":"300897","mc":"山科智能","jys":"sz"},{"dm":"300923","mc":"研奥股份","jys":"sz"},{"dm":"002546","mc":"新联电子","jys":"sz"},{"dm":"600535","mc":"天士力","jys":"sh"},{"dm":"000929","mc":"兰州黄河","jys":"sz"},{"dm":"603029","mc":"天鹅股份","jys":"sh"},{"dm":"001255","mc":"博菲电气","jys":"sz"},{"dm":"601816","mc":"京沪高铁","jys":"sh"},{"dm":"301055","mc":"张小泉","jys":"sz"},{"dm":"603063","mc":"禾望电气","jys":"sh"},{"dm":"002918","mc":"蒙娜丽莎","jys":"sz"},{"dm":"603721","mc":"中广天择","jys":"sh"},{"dm":"002139","mc":"拓邦股份","jys":"sz"},{"dm":"603336","mc":"宏辉果蔬","jys":"sh"},{"dm":"301189","mc":"奥尼电子","jys":"sz"},{"dm":"601568","mc":"北元集团","jys":"sh"},{"dm":"002872","mc":"ST天圣","jys":"sz"},{"dm":"000786","mc":"北新建材","jys":"sz"},{"dm":"605222","mc":"起帆电缆","jys":"sh"},{"dm":"301035","mc":"润丰股份","jys":"sz"},{"dm":"300254","mc":"仟源医药","jys":"sz"},{"dm":"002255","mc":"海陆重工","jys":"sz"},{"dm":"300174","mc":"元力股份","jys":"sz"},{"dm":"002494","mc":"华斯股份","jys":"sz"},{"dm":"300317","mc":"珈伟新能","jys":"sz"},{"dm":"300285","mc":"国瓷材料","jys":"sz"},{"dm":"603291","mc":"联合水务","jys":"sh"},{"dm":"603103","mc":"横店影视","jys":"sh"},{"dm":"300164","mc":"通源石油","jys":"sz"},{"dm":"000719","mc":"中原传媒","jys":"sz"},{"dm":"600802","mc":"福建水泥","jys":"sh"},{"dm":"300389","mc":"艾比森","jys":"sz"},{"dm":"600506","mc":"统一股份","jys":"sh"},{"dm":"300839","mc":"博汇股份","jys":"sz"},{"dm":"605178","mc":"时空科技","jys":"sh"},{"dm":"688163","mc":"赛伦生物","jys":"sh"},{"dm":"603348","mc":"文灿股份","jys":"sh"},{"dm":"603097","mc":"江苏华辰","jys":"sh"},{"dm":"002046","mc":"国机精工","jys":"sz"},{"dm":"000301","mc":"东方盛虹","jys":"sz"},{"dm":"301161","mc":"唯万密封","jys":"sz"},{"dm":"300788","mc":"中信出版","jys":"sz"},{"dm":"603214","mc":"爱婴室","jys":"sh"},{"dm":"300536","mc":"农尚环境","jys":"sz"},{"dm":"301175","mc":"中科环保","jys":"sz"},{"dm":"600525","mc":"长园集团","jys":"sh"},{"dm":"601998","mc":"中信银行","jys":"sh"},{"dm":"600149","mc":"廊坊发展","jys":"sh"},{"dm":"301418","mc":"协昌科技","jys":"sz"},{"dm":"002012","mc":"凯恩股份","jys":"sz"},{"dm":"600887","mc":"伊利股份","jys":"sh"},{"dm":"002059","mc":"云南旅游","jys":"sz"},{"dm":"000859","mc":"国风新材","jys":"sz"},{"dm":"003035","mc":"南网能源","jys":"sz"},{"dm":"002678","mc":"珠江钢琴","jys":"sz"},{"dm":"688081","mc":"兴图新科","jys":"sh"},{"dm":"688575","mc":"亚辉龙","jys":"sh"},{"dm":"600925","mc":"苏能股份","jys":"sh"},{"dm":"002121","mc":"科陆电子","jys":"sz"},{"dm":"603690","mc":"至纯科技","jys":"sh"},{"dm":"603181","mc":"皇马科技","jys":"sh"},{"dm":"300284","mc":"苏交科","jys":"sz"},{"dm":"000530","mc":"冰山冷热","jys":"sz"},{"dm":"301255","mc":"通力科技","jys":"sz"},{"dm":"300995","mc":"奇德新材","jys":"sz"},{"dm":"003003","mc":"天元股份","jys":"sz"},{"dm":"300674","mc":"宇信科技","jys":"sz"},{"dm":"300046","mc":"台基股份","jys":"sz"},{"dm":"000902","mc":"新洋丰","jys":"sz"},{"dm":"688334","mc":"西高院","jys":"sh"},{"dm":"600543","mc":"*ST莫高","jys":"sh"},{"dm":"301469","mc":"恒达新材","jys":"sz"},{"dm":"300573","mc":"兴齐眼药","jys":"sz"},{"dm":"300354","mc":"东华测试","jys":"sz"},{"dm":"603226","mc":"菲林格尔","jys":"sh"},{"dm":"688529","mc":"豪森股份","jys":"sh"},{"dm":"301158","mc":"德石股份","jys":"sz"},{"dm":"000504","mc":"南华生物","jys":"sz"},{"dm":"300134","mc":"大富科技","jys":"sz"},{"dm":"002880","mc":"卫光生物","jys":"sz"},{"dm":"300950","mc":"德固特","jys":"sz"},{"dm":"603856","mc":"东宏股份","jys":"sh"},{"dm":"605303","mc":"园林股份","jys":"sh"},{"dm":"000059","mc":"华锦股份","jys":"sz"},{"dm":"603272","mc":"联翔股份","jys":"sh"},{"dm":"300137","mc":"先河环保","jys":"sz"},{"dm":"000565","mc":"渝三峡A","jys":"sz"},{"dm":"600455","mc":"博通股份","jys":"sh"},{"dm":"002469","mc":"三维化学","jys":"sz"},{"dm":"002911","mc":"佛燃能源","jys":"sz"},{"dm":"600168","mc":"武汉控股","jys":"sh"},{"dm":"600056","mc":"中国医药","jys":"sh"},{"dm":"001300","mc":"三柏硕","jys":"sz"},{"dm":"002333","mc":"罗普斯金","jys":"sz"},{"dm":"603389","mc":"亚振家居","jys":"sh"},{"dm":"003027","mc":"同兴环保","jys":"sz"},{"dm":"002097","mc":"山河智能","jys":"sz"},{"dm":"300289","mc":"利德曼","jys":"sz"},{"dm":"300054","mc":"鼎龙股份","jys":"sz"},{"dm":"603727","mc":"博迈科","jys":"sh"},{"dm":"605305","mc":"中际联合","jys":"sh"},{"dm":"002404","mc":"嘉欣丝绸","jys":"sz"},{"dm":"300971","mc":"博亚精工","jys":"sz"},{"dm":"002987","mc":"京北方","jys":"sz"},{"dm":"603105","mc":"芯能科技","jys":"sh"},{"dm":"002696","mc":"百洋股份","jys":"sz"},{"dm":"600965","mc":"福成股份","jys":"sh"},{"dm":"300062","mc":"中能电气","jys":"sz"},{"dm":"000937","mc":"冀中能源","jys":"sz"},{"dm":"603900","mc":"莱绅通灵","jys":"sh"},{"dm":"002567","mc":"唐人神","jys":"sz"},{"dm":"300859","mc":"*ST西域","jys":"sz"},{"dm":"300385","mc":"雪浪环境","jys":"sz"},{"dm":"300510","mc":"金冠股份","jys":"sz"},{"dm":"002558","mc":"巨人网络","jys":"sz"},{"dm":"002946","mc":"新乳业","jys":"sz"},{"dm":"002065","mc":"东华软件","jys":"sz"},{"dm":"300699","mc":"光威复材","jys":"sz"},{"dm":"300635","mc":"中达安","jys":"sz"},{"dm":"002391","mc":"长青股份","jys":"sz"},{"dm":"300376","mc":"易事特","jys":"sz"},{"dm":"300374","mc":"中铁装配","jys":"sz"},{"dm":"688458","mc":"美芯晟","jys":"sh"},{"dm":"301043","mc":"绿岛风","jys":"sz"},{"dm":"603656","mc":"泰禾智能","jys":"sh"},{"dm":"002860","mc":"星帅尔","jys":"sz"},{"dm":"603356","mc":"华菱精工","jys":"sh"},{"dm":"001318","mc":"阳光乳业","jys":"sz"},{"dm":"688207","mc":"格灵深瞳","jys":"sh"},{"dm":"002444","mc":"巨星科技","jys":"sz"},{"dm":"600644","mc":"乐山电力","jys":"sh"},{"dm":"300307","mc":"慈星股份","jys":"sz"},{"dm":"601609","mc":"金田股份","jys":"sh"},{"dm":"300609","mc":"汇纳科技","jys":"sz"},{"dm":"600268","mc":"国电南自","jys":"sh"},{"dm":"002448","mc":"中原内配","jys":"sz"},{"dm":"301066","mc":"万事利","jys":"sz"},{"dm":"002927","mc":"泰永长征","jys":"sz"},{"dm":"001373","mc":"翔腾新材","jys":"sz"},{"dm":"601006","mc":"大秦铁路","jys":"sh"},{"dm":"002985","mc":"北摩高科","jys":"sz"},{"dm":"300126","mc":"锐奇股份","jys":"sz"},{"dm":"002100","mc":"天康生物","jys":"sz"},{"dm":"301166","mc":"优宁维","jys":"sz"},{"dm":"603396","mc":"金辰股份","jys":"sh"},{"dm":"002169","mc":"智光电气","jys":"sz"},{"dm":"600674","mc":"川投能源","jys":"sh"},{"dm":"600814","mc":"杭州解百","jys":"sh"},{"dm":"600111","mc":"北方稀土","jys":"sh"},{"dm":"300823","mc":"建科机械","jys":"sz"},{"dm":"600998","mc":"九州通","jys":"sh"},{"dm":"300419","mc":"浩丰科技","jys":"sz"},{"dm":"002130","mc":"沃尔核材","jys":"sz"},{"dm":"002741","mc":"光华科技","jys":"sz"},{"dm":"301232","mc":"飞沃科技","jys":"sz"},{"dm":"002171","mc":"楚江新材","jys":"sz"},{"dm":"000731","mc":"四川美丰","jys":"sz"},{"dm":"600182","mc":"S佳通","jys":"sh"},{"dm":"002693","mc":"双成药业","jys":"sz"},{"dm":"600900","mc":"长江电力","jys":"sh"},{"dm":"000831","mc":"中国稀土","jys":"sz"},{"dm":"688146","mc":"中船特气","jys":"sh"},{"dm":"688389","mc":"普门科技","jys":"sh"},{"dm":"300387","mc":"富邦股份","jys":"sz"},{"dm":"000707","mc":"双环科技","jys":"sz"},{"dm":"603444","mc":"吉比特","jys":"sh"},{"dm":"603685","mc":"晨丰科技","jys":"sh"},{"dm":"300550","mc":"和仁科技","jys":"sz"},{"dm":"300795","mc":"米奥会展","jys":"sz"},{"dm":"001333","mc":"光华股份","jys":"sz"},{"dm":"688681","mc":"科汇股份","jys":"sh"},{"dm":"688192","mc":"迪哲医药-U","jys":"sh"},{"dm":"300270","mc":"中威电子","jys":"sz"},{"dm":"002297","mc":"博云新材","jys":"sz"},{"dm":"605122","mc":"四方新材","jys":"sh"},{"dm":"001872","mc":"招商港口","jys":"sz"},{"dm":"688353","mc":"华盛锂电","jys":"sh"},{"dm":"002951","mc":"ST金时","jys":"sz"},{"dm":"002414","mc":"高德红外","jys":"sz"},{"dm":"605168","mc":"三人行","jys":"sh"},{"dm":"002891","mc":"中宠股份","jys":"sz"},{"dm":"605499","mc":"东鹏饮料","jys":"sh"},{"dm":"600980","mc":"北矿科技","jys":"sh"},{"dm":"601369","mc":"陕鼓动力","jys":"sh"},{"dm":"600927","mc":"永安期货","jys":"sh"},{"dm":"300512","mc":"中亚股份","jys":"sz"},{"dm":"301323","mc":"新莱福","jys":"sz"},{"dm":"600883","mc":"博闻科技","jys":"sh"},{"dm":"600228","mc":"返利科技","jys":"sh"},{"dm":"603110","mc":"东方材料","jys":"sh"},{"dm":"301003","mc":"江苏博云","jys":"sz"},{"dm":"002543","mc":"万和电气","jys":"sz"},{"dm":"002101","mc":"广东鸿图","jys":"sz"},{"dm":"600273","mc":"嘉化能源","jys":"sh"},{"dm":"000848","mc":"承德露露","jys":"sz"},{"dm":"603201","mc":"常润股份","jys":"sh"},{"dm":"301097","mc":"天益医疗","jys":"sz"},{"dm":"600483","mc":"福能股份","jys":"sh"},{"dm":"002403","mc":"爱仕达","jys":"sz"},{"dm":"603871","mc":"嘉友国际","jys":"sh"},{"dm":"600721","mc":"百花医药","jys":"sh"},{"dm":"000547","mc":"航天发展","jys":"sz"},{"dm":"001209","mc":"洪兴股份","jys":"sz"},{"dm":"603689","mc":"皖天然气","jys":"sh"},{"dm":"002842","mc":"翔鹭钨业","jys":"sz"},{"dm":"300806","mc":"斯迪克","jys":"sz"},{"dm":"688283","mc":"坤恒顺维","jys":"sh"},{"dm":"603275","mc":"众辰科技","jys":"sh"},{"dm":"603609","mc":"禾丰股份","jys":"sh"},{"dm":"000663","mc":"永安林业","jys":"sz"},{"dm":"603338","mc":"浙江鼎力","jys":"sh"},{"dm":"002561","mc":"徐家汇","jys":"sz"},{"dm":"002394","mc":"联发股份","jys":"sz"},{"dm":"301363","mc":"美好医疗","jys":"sz"},{"dm":"603020","mc":"爱普股份","jys":"sh"},{"dm":"600463","mc":"空港股份","jys":"sh"},{"dm":"603565","mc":"中谷物流","jys":"sh"},{"dm":"002026","mc":"山东威达","jys":"sz"},{"dm":"301316","mc":"慧博云通","jys":"sz"},{"dm":"603193","mc":"润本股份","jys":"sh"},{"dm":"688478","mc":"晶升股份","jys":"sh"},{"dm":"300749","mc":"顶固集创","jys":"sz"},{"dm":"301201","mc":"诚达药业","jys":"sz"},{"dm":"000423","mc":"东阿阿胶","jys":"sz"},{"dm":"002745","mc":"木林森","jys":"sz"},{"dm":"603680","mc":"今创集团","jys":"sh"},{"dm":"301507","mc":"民生健康","jys":"sz"},{"dm":"300639","mc":"凯普生物","jys":"sz"},{"dm":"688096","mc":"京源环保","jys":"sh"},{"dm":"603967","mc":"中创物流","jys":"sh"},{"dm":"002736","mc":"国信证券","jys":"sz"},{"dm":"603073","mc":"彩蝶实业","jys":"sh"},{"dm":"688543","mc":"国科军工","jys":"sh"},{"dm":"300052","mc":"中青宝","jys":"sz"},{"dm":"688030","mc":"山石网科","jys":"sh"},{"dm":"002670","mc":"国盛金控","jys":"sz"},{"dm":"688061","mc":"灿瑞科技","jys":"sh"},{"dm":"688685","mc":"迈信林","jys":"sh"},{"dm":"600620","mc":"天宸股份","jys":"sh"},{"dm":"603125","mc":"常青科技","jys":"sh"},{"dm":"002205","mc":"国统股份","jys":"sz"},{"dm":"603313","mc":"梦百合","jys":"sh"},{"dm":"002096","mc":"易普力","jys":"sz"},{"dm":"688287","mc":"观典防务","jys":"sh"},{"dm":"600312","mc":"平高电气","jys":"sh"},{"dm":"600764","mc":"中国海防","jys":"sh"},{"dm":"002731","mc":"萃华珠宝","jys":"sz"},{"dm":"003037","mc":"三和管桩","jys":"sz"},{"dm":"300929","mc":"华骐环保","jys":"sz"},{"dm":"688171","mc":"纬德信息","jys":"sh"},{"dm":"301217","mc":"铜冠铜箔","jys":"sz"},{"dm":"603489","mc":"八方股份","jys":"sh"},{"dm":"688602","mc":"康鹏科技","jys":"sh"},{"dm":"688006","mc":"杭可科技","jys":"sh"},{"dm":"300349","mc":"金卡智能","jys":"sz"},{"dm":"002553","mc":"南方精工","jys":"sz"},{"dm":"688352","mc":"颀中科技","jys":"sh"},{"dm":"300138","mc":"晨光生物","jys":"sz"},{"dm":"300068","mc":"南都电源","jys":"sz"},{"dm":"603365","mc":"水星家纺","jys":"sh"},{"dm":"300943","mc":"春晖智控","jys":"sz"},{"dm":"688277","mc":"天智航-U","jys":"sh"},{"dm":"301029","mc":"怡合达","jys":"sz"},{"dm":"300357","mc":"我武生物","jys":"sz"},{"dm":"603269","mc":"海鸥股份","jys":"sh"},{"dm":"603585","mc":"苏利股份","jys":"sh"},{"dm":"000681","mc":"视觉中国","jys":"sz"},{"dm":"603829","mc":"洛凯股份","jys":"sh"},{"dm":"301365","mc":"矩阵股份","jys":"sz"},{"dm":"002088","mc":"鲁阳节能","jys":"sz"},{"dm":"002552","mc":"宝鼎科技","jys":"sz"},{"dm":"300188","mc":"美亚柏科","jys":"sz"},{"dm":"603045","mc":"福达合金","jys":"sh"},{"dm":"605155","mc":"西大门","jys":"sh"},{"dm":"300804","mc":"广康生化","jys":"sz"},{"dm":"600171","mc":"上海贝岭","jys":"sh"},{"dm":"300628","mc":"亿联网络","jys":"sz"},{"dm":"301278","mc":"快可电子","jys":"sz"},{"dm":"600588","mc":"用友网络","jys":"sh"},{"dm":"300717","mc":"华信新材","jys":"sz"},{"dm":"300922","mc":"天秦装备","jys":"sz"},{"dm":"000997","mc":"新 大 陆","jys":"sz"},{"dm":"605365","mc":"立达信","jys":"sh"},{"dm":"600262","mc":"北方股份","jys":"sh"},{"dm":"300850","mc":"新强联","jys":"sz"},{"dm":"301263","mc":"泰恩康","jys":"sz"},{"dm":"688070","mc":"纵横股份","jys":"sh"},{"dm":"688678","mc":"福立旺","jys":"sh"},{"dm":"300109","mc":"新开源","jys":"sz"},{"dm":"301038","mc":"深水规院","jys":"sz"},{"dm":"603090","mc":"宏盛股份","jys":"sh"},{"dm":"605296","mc":"神农集团","jys":"sh"},{"dm":"301077","mc":"星华新材","jys":"sz"},{"dm":"688499","mc":"利元亨","jys":"sh"},{"dm":"688190","mc":"云路股份","jys":"sh"},{"dm":"300540","mc":"蜀道装备","jys":"sz"},{"dm":"603355","mc":"莱克电气","jys":"sh"},{"dm":"300779","mc":"惠城环保","jys":"sz"},{"dm":"301049","mc":"超越科技","jys":"sz"},{"dm":"002653","mc":"海思科","jys":"sz"},{"dm":"300900","mc":"广联航空","jys":"sz"},{"dm":"301182","mc":"凯旺科技","jys":"sz"},{"dm":"688370","mc":"丛麟科技","jys":"sh"},{"dm":"688255","mc":"凯尔达","jys":"sh"},{"dm":"688091","mc":"上海谊众","jys":"sh"},{"dm":"300803","mc":"指南针","jys":"sz"},{"dm":"001328","mc":"登康口腔","jys":"sz"},{"dm":"300938","mc":"信测标准","jys":"sz"},{"dm":"300033","mc":"同花顺","jys":"sz"},{"dm":"688559","mc":"海目星","jys":"sh"},{"dm":"688598","mc":"金博股份","jys":"sh"},{"dm":"688202","mc":"美迪西","jys":"sh"},{"dm":"688185","mc":"康希诺","jys":"sh"},{"dm":"688718","mc":"唯赛勃","jys":"sh"},{"dm":"688686","mc":"奥普特","jys":"sh"},{"dm":"688653","mc":"康希通信","jys":"sh"},{"dm":"688648","mc":"中邮科技","jys":"sh"},{"dm":"688638","mc":"誉辰智能","jys":"sh"},{"dm":"688585","mc":"上纬新材","jys":"sh"},{"dm":"688561","mc":"奇安信-U","jys":"sh"},{"dm":"688555","mc":"退市泽达","jys":"sh"},{"dm":"688505","mc":"复旦张江","jys":"sh"},{"dm":"688425","mc":"铁建重工","jys":"sh"},{"dm":"688119","mc":"中钢洛耐","jys":"sh"},{"dm":"688098","mc":"申联生物","jys":"sh"},{"dm":"688086","mc":"退市紫晶","jys":"sh"},{"dm":"688057","mc":"金达莱","jys":"sh"},{"dm":"605158","mc":"华达新材","jys":"sh"},{"dm":"603996","mc":"退市中新","jys":"sh"},{"dm":"603896","mc":"寿仙谷","jys":"sh"},{"dm":"603818","mc":"曲美家居","jys":"sh"},{"dm":"603816","mc":"顾家家居","jys":"sh"},{"dm":"603797","mc":"联泰环保","jys":"sh"},{"dm":"603637","mc":"镇海股份","jys":"sh"},{"dm":"603602","mc":"纵横通信","jys":"sh"},{"dm":"603577","mc":"汇金通","jys":"sh"},{"dm":"603558","mc":"健盛集团","jys":"sh"},{"dm":"603557","mc":"ST起步","jys":"sh"},{"dm":"603527","mc":"众源新材","jys":"sh"},{"dm":"603488","mc":"展鹏科技","jys":"sh"},{"dm":"603377","mc":"东方时尚","jys":"sh"},{"dm":"603361","mc":"浙江国祥","jys":"sh"},{"dm":"603359","mc":"东珠生态","jys":"sh"},{"dm":"603357","mc":"设计总院","jys":"sh"},{"dm":"603328","mc":"依顿电子","jys":"sh"},{"dm":"603326","mc":"我乐家居","jys":"sh"},{"dm":"603318","mc":"水发燃气","jys":"sh"},{"dm":"603212","mc":"赛伍技术","jys":"sh"},{"dm":"603169","mc":"兰石重装","jys":"sh"},{"dm":"603157","mc":"退市拉夏","jys":"sh"},{"dm":"603131","mc":"上海沪工","jys":"sh"},{"dm":"603122","mc":"合富中国","jys":"sh"},{"dm":"603067","mc":"振华股份","jys":"sh"},{"dm":"603025","mc":"大豪科技","jys":"sh"},{"dm":"603017","mc":"中衡设计","jys":"sh"},{"dm":"603012","mc":"创力集团","jys":"sh"},{"dm":"603011","mc":"合锻智能","jys":"sh"},{"dm":"601916","mc":"浙商银行","jys":"sh"},{"dm":"601890","mc":"亚星锚链","jys":"sh"},{"dm":"601880","mc":"辽港股份","jys":"sh"},{"dm":"601857","mc":"中国石油","jys":"sh"},{"dm":"601838","mc":"成都银行","jys":"sh"},{"dm":"601808","mc":"中海油服","jys":"sh"},{"dm":"601619","mc":"嘉泽新能","jys":"sh"},{"dm":"601606","mc":"长城军工","jys":"sh"},{"dm":"601588","mc":"北辰实业","jys":"sh"},{"dm":"601558","mc":"退市锐电","jys":"sh"},{"dm":"601518","mc":"吉林高速","jys":"sh"},{"dm":"601388","mc":"怡球资源","jys":"sh"},{"dm":"601368","mc":"绿城水务","jys":"sh"},{"dm":"601326","mc":"秦港股份","jys":"sh"},{"dm":"601299","mc":"中国北车","jys":"sh"},{"dm":"601268","mc":"*ST二重","jys":"sh"},{"dm":"601258","mc":"*ST庞大","jys":"sh"},{"dm":"601238","mc":"广汽集团","jys":"sh"},{"dm":"601229","mc":"上海银行","jys":"sh"},{"dm":"601216","mc":"君正集团","jys":"sh"},{"dm":"601199","mc":"江南水务","jys":"sh"},{"dm":"601169","mc":"北京银行","jys":"sh"},{"dm":"601158","mc":"重庆水务","jys":"sh"},{"dm":"601121","mc":"宝地矿业","jys":"sh"},{"dm":"601108","mc":"财通证券","jys":"sh"},{"dm":"601107","mc":"四川成渝","jys":"sh"},{"dm":"601106","mc":"中国一重","jys":"sh"},{"dm":"601069","mc":"西部黄金","jys":"sh"},{"dm":"601015","mc":"陕西黑猫","jys":"sh"},{"dm":"601008","mc":"连云港","jys":"sh"},{"dm":"600999","mc":"招商证券","jys":"sh"},{"dm":"600991","mc":"广汽长丰","jys":"sh"},{"dm":"600984","mc":"建设机械","jys":"sh"},{"dm":"600978","mc":"*ST宜生","jys":"sh"},{"dm":"600959","mc":"江苏有线","jys":"sh"},{"dm":"600929","mc":"雪天盐业","jys":"sh"},{"dm":"600908","mc":"无锡银行","jys":"sh"},{"dm":"600899","mc":"*ST信联","jys":"sh"},{"dm":"600898","mc":"ST美讯","jys":"sh"},{"dm":"600896","mc":"退市海医","jys":"sh"},{"dm":"600891","mc":"退市秋林","jys":"sh"},{"dm":"600890","mc":"退市中房","jys":"sh"},{"dm":"600880","mc":"博瑞传播","jys":"sh"},{"dm":"600878","mc":"*ST北科","jys":"sh"},{"dm":"600873","mc":"梅花生物","jys":"sh"},{"dm":"600870","mc":"退市厦华","jys":"sh"},{"dm":"600868","mc":"梅雁吉祥","jys":"sh"},{"dm":"600856","mc":"退市中天","jys":"sh"},{"dm":"600852","mc":"*ST中川","jys":"sh"},{"dm":"600849","mc":"上药转换","jys":"sh"},{"dm":"600848","mc":"上海临港","jys":"sh"},{"dm":"600842","mc":"中西药业","jys":"sh"},{"dm":"600840","mc":"新湖创业","jys":"sh"},{"dm":"600832","mc":"东方明珠","jys":"sh"},{"dm":"600824","mc":"益民集团","jys":"sh"},{"dm":"600823","mc":"ST世茂","jys":"sh"},{"dm":"600820","mc":"隧道股份","jys":"sh"},{"dm":"600815","mc":"厦工股份","jys":"sh"},{"dm":"600813","mc":"ST鞍一工","jys":"sh"},{"dm":"600806","mc":"退市昆机","jys":"sh"},{"dm":"600800","mc":"渤海化学","jys":"sh"},{"dm":"600799","mc":"*ST龙科","jys":"sh"},{"dm":"600798","mc":"宁波海运","jys":"sh"},{"dm":"600795","mc":"国电电力","jys":"sh"},{"dm":"600789","mc":"鲁抗医药","jys":"sh"},{"dm":"600788","mc":"*ST达曼","jys":"sh"},{"dm":"600786","mc":"东方锅炉","jys":"sh"},{"dm":"600781","mc":"退市辅仁","jys":"sh"},{"dm":"600772","mc":"S*ST龙昌","jys":"sh"},{"dm":"600767","mc":"退市运盛","jys":"sh"},{"dm":"600762","mc":"S*ST金荔","jys":"sh"},{"dm":"600752","mc":"*ST哈慈","jys":"sh"},{"dm":"600747","mc":"退市大控","jys":"sh"},{"dm":"600739","mc":"辽宁成大","jys":"sh"},{"dm":"600728","mc":"佳都科技","jys":"sh"},{"dm":"600727","mc":"鲁北化工","jys":"sh"},{"dm":"600724","mc":"宁波富达","jys":"sh"},{"dm":"600723","mc":"首商股份","jys":"sh"},{"dm":"600718","mc":"东软集团","jys":"sh"},{"dm":"600715","mc":"文投控股","jys":"sh"},{"dm":"600710","mc":"苏美达","jys":"sh"},{"dm":"600709","mc":"ST生态","jys":"sh"},{"dm":"600708","mc":"光明地产","jys":"sh"},{"dm":"600704","mc":"物产中大","jys":"sh"},{"dm":"600701","mc":"退市工新","jys":"sh"},{"dm":"600700","mc":"*ST数码","jys":"sh"},{"dm":"600695","mc":"退市绿庭","jys":"sh"},{"dm":"600693","mc":"东百集团","jys":"sh"},{"dm":"600691","mc":"阳煤化工","jys":"sh"},{"dm":"600688","mc":"上海石化","jys":"sh"},{"dm":"600687","mc":"退市刚泰","jys":"sh"},{"dm":"600682","mc":"南京新百","jys":"sh"},{"dm":"600681","mc":"百川能源","jys":"sh"},{"dm":"600680","mc":"*ST上普","jys":"sh"},{"dm":"600677","mc":"*ST航通","jys":"sh"},{"dm":"600672","mc":"*ST华圣","jys":"sh"},{"dm":"600670","mc":"*ST斯达","jys":"sh"},{"dm":"600669","mc":"*ST鞍成","jys":"sh"},{"dm":"600664","mc":"哈药股份","jys":"sh"},{"dm":"600659","mc":"*ST花雕","jys":"sh"},{"dm":"600656","mc":"退市博元","jys":"sh"},{"dm":"600652","mc":"退市游久","jys":"sh"},{"dm":"600651","mc":"飞乐音响","jys":"sh"},{"dm":"600648","mc":"外高桥","jys":"sh"},{"dm":"600646","mc":"ST国嘉","jys":"sh"},{"dm":"600634","mc":"退市富控","jys":"sh"},{"dm":"600632","mc":"华联商厦","jys":"sh"},{"dm":"600631","mc":"百联股份","jys":"sh"},{"dm":"600627","mc":"上电股份","jys":"sh"},{"dm":"600625","mc":"PT水仙","jys":"sh"},{"dm":"600624","mc":"复旦复华","jys":"sh"},{"dm":"600622","mc":"光大嘉宝","jys":"sh"},{"dm":"600614","mc":"退市鹏起","jys":"sh"},{"dm":"600607","mc":"上实医药","jys":"sh"},{"dm":"600606","mc":"绿地控股","jys":"sh"},{"dm":"600591","mc":"*ST上航","jys":"sh"},{"dm":"600577","mc":"精达股份","jys":"sh"},{"dm":"600568","mc":"ST中珠","jys":"sh"},{"dm":"600555","mc":"退市海创","jys":"sh"},{"dm":"600553","mc":"太行水泥","jys":"sh"},{"dm":"600550","mc":"保变电气","jys":"sh"},{"dm":"600545","mc":"卓郎智能","jys":"sh"},{"dm":"600539","mc":"狮头股份","jys":"sh"},{"dm":"600537","mc":"亿晶光电","jys":"sh"},{"dm":"600533","mc":"栖霞建设","jys":"sh"},{"dm":"600532","mc":"退市未来","jys":"sh"},{"dm":"600515","mc":"海南机场","jys":"sh"},{"dm":"600510","mc":"黑牡丹","jys":"sh"},{"dm":"600505","mc":"西昌电力","jys":"sh"},{"dm":"600503","mc":"华丽家族","jys":"sh"},{"dm":"600485","mc":"*ST信威","jys":"sh"},{"dm":"600472","mc":"包头铝业","jys":"sh"},{"dm":"600470","mc":"六国化工","jys":"sh"},{"dm":"600469","mc":"风神股份","jys":"sh"},{"dm":"600466","mc":"*ST蓝光","jys":"sh"},{"dm":"600462","mc":"ST九有","jys":"sh"},{"dm":"600458","mc":"时代新材","jys":"sh"},{"dm":"600432","mc":"退市吉恩","jys":"sh"},{"dm":"600403","mc":"大有能源","jys":"sh"},{"dm":"600401","mc":"退市海润","jys":"sh"},{"dm":"600400","mc":"红豆股份","jys":"sh"},{"dm":"600393","mc":"ST粤泰","jys":"sh"},{"dm":"600390","mc":"五矿资本","jys":"sh"},{"dm":"600387","mc":"ST海越","jys":"sh"},{"dm":"600385","mc":"退市金泰","jys":"sh"},{"dm":"600357","mc":"承德钒钛","jys":"sh"},{"dm":"600354","mc":"敦煌种业","jys":"sh"},{"dm":"600321","mc":"正源股份","jys":"sh"},{"dm":"600317","mc":"营口港","jys":"sh"},{"dm":"600311","mc":"*ST荣华","jys":"sh"},{"dm":"600310","mc":"广西能源","jys":"sh"},{"dm":"600307","mc":"酒钢宏兴","jys":"sh"},{"dm":"600296","mc":"S兰铝","jys":"sh"},{"dm":"600292","mc":"远达环保","jys":"sh"},{"dm":"600291","mc":"退市西水","jys":"sh"},{"dm":"600289","mc":"ST信通","jys":"sh"},{"dm":"600287","mc":"江苏舜天","jys":"sh"},{"dm":"600286","mc":"S*ST国瓷","jys":"sh"},{"dm":"600275","mc":"退市昌鱼","jys":"sh"},{"dm":"600270","mc":"外运发展","jys":"sh"},{"dm":"600263","mc":"路桥建设","jys":"sh"},{"dm":"600260","mc":"*ST凯乐","jys":"sh"},{"dm":"600253","mc":"天方药业","jys":"sh"},{"dm":"600247","mc":"*ST成城","jys":"sh"},{"dm":"600242","mc":"退市中昌","jys":"sh"},{"dm":"600240","mc":"退市华业","jys":"sh"},{"dm":"600225","mc":"卓朗科技","jys":"sh"},{"dm":"600209","mc":"退市罗顿","jys":"sh"},{"dm":"600205","mc":"S山东铝","jys":"sh"},{"dm":"600192","mc":"长城电工","jys":"sh"},{"dm":"600191","mc":"华资实业","jys":"sh"},{"dm":"600181","mc":"S*ST云大","jys":"sh"},{"dm":"600177","mc":"雅戈尔","jys":"sh"},{"dm":"600175","mc":"退市美都","jys":"sh"},{"dm":"600172","mc":"黄河旋风","jys":"sh"},{"dm":"600170","mc":"上海建工","jys":"sh"},{"dm":"600162","mc":"香江控股","jys":"sh"},{"dm":"600156","mc":"华升股份","jys":"sh"},{"dm":"600146","mc":"退市环球","jys":"sh"},{"dm":"600145","mc":"退市新亿","jys":"sh"},{"dm":"600139","mc":"*ST西源","jys":"sh"},{"dm":"600122","mc":"*ST宏图","jys":"sh"},{"dm":"600121","mc":"郑州煤电","jys":"sh"},{"dm":"600113","mc":"浙江东日","jys":"sh"},{"dm":"600112","mc":"ST天成","jys":"sh"},{"dm":"600102","mc":"莱钢股份","jys":"sh"},{"dm":"600093","mc":"退市易见","jys":"sh"},{"dm":"600092","mc":"S*ST精密","jys":"sh"},{"dm":"600091","mc":"退市明科","jys":"sh"},{"dm":"600090","mc":"退市济堂","jys":"sh"},{"dm":"600087","mc":"退市长油","jys":"sh"},{"dm":"600086","mc":"退市金钰","jys":"sh"},{"dm":"600082","mc":"海泰发展","jys":"sh"},{"dm":"600077","mc":"*ST宋都","jys":"sh"},{"dm":"600074","mc":"退市保千","jys":"sh"},{"dm":"600069","mc":"退市银鸽","jys":"sh"},{"dm":"600068","mc":"葛洲坝","jys":"sh"},{"dm":"600067","mc":"冠城大通","jys":"sh"},{"dm":"600065","mc":"*ST联谊","jys":"sh"},{"dm":"600050","mc":"中国联通","jys":"sh"},{"dm":"600035","mc":"楚天高速","jys":"sh"},{"dm":"600028","mc":"中国石化","jys":"sh"},{"dm":"600022","mc":"山东钢铁","jys":"sh"},{"dm":"600019","mc":"宝钢股份","jys":"sh"},{"dm":"600018","mc":"上港集团","jys":"sh"},{"dm":"600005","mc":"武钢股份","jys":"sh"},{"dm":"600003","mc":"ST东北高","jys":"sh"},{"dm":"600002","mc":"齐鲁石化","jys":"sh"},{"dm":"600001","mc":"邯郸钢铁","jys":"sh"},{"dm":"301439","mc":"泓淋电力","jys":"sz"},{"dm":"301317","mc":"鑫磊股份","jys":"sz"},{"dm":"301282","mc":"金禄电子","jys":"sz"},{"dm":"301223","mc":"中荣股份","jys":"sz"},{"dm":"301187","mc":"欧圣电气","jys":"sz"},{"dm":"301136","mc":"招标股份","jys":"sz"},{"dm":"301058","mc":"中粮科工","jys":"sz"},{"dm":"301048","mc":"金鹰重工","jys":"sz"},{"dm":"301039","mc":"中集车辆","jys":"sz"},{"dm":"300975","mc":"商络电子","jys":"sz"},{"dm":"300970","mc":"华绿生物","jys":"sz"},{"dm":"300931","mc":"通用电梯","jys":"sz"},{"dm":"300908","mc":"仲景食品","jys":"sz"},{"dm":"300898","mc":"熊猫乳品","jys":"sz"},{"dm":"300891","mc":"惠云钛业","jys":"sz"},{"dm":"300889","mc":"爱克股份","jys":"sz"},{"dm":"300758","mc":"七彩化学","jys":"sz"},{"dm":"300732","mc":"设研院","jys":"sz"},{"dm":"300710","mc":"万隆光电","jys":"sz"},{"dm":"300696","mc":"爱乐达","jys":"sz"},{"dm":"300610","mc":"晨化股份","jys":"sz"},{"dm":"300587","mc":"天铁股份","jys":"sz"},{"dm":"300526","mc":"中潜退","jys":"sz"},{"dm":"300495","mc":"*ST美尚","jys":"sz"},{"dm":"300485","mc":"赛升药业","jys":"sz"},{"dm":"300456","mc":"赛微电子","jys":"sz"},{"dm":"300447","mc":"全信股份","jys":"sz"},{"dm":"300431","mc":"暴风退","jys":"sz"},{"dm":"300392","mc":"腾信退","jys":"sz"},{"dm":"300388","mc":"节能国祯","jys":"sz"},{"dm":"300372","mc":"欣泰退","jys":"sz"},{"dm":"300371","mc":"汇中股份","jys":"sz"},{"dm":"300367","mc":"网力退","jys":"sz"},{"dm":"300362","mc":"天翔退","jys":"sz"},{"dm":"300356","mc":"光一退","jys":"sz"},{"dm":"300343","mc":"联创股份","jys":"sz"},{"dm":"300336","mc":"新文退","jys":"sz"},{"dm":"300330","mc":"计通退","jys":"sz"},{"dm":"300329","mc":"海伦钢琴","jys":"sz"},{"dm":"300325","mc":"德威退","jys":"sz"},{"dm":"300320","mc":"海达股份","jys":"sz"},{"dm":"300312","mc":"邦讯退","jys":"sz"},{"dm":"300310","mc":"宜通世纪","jys":"sz"},{"dm":"300309","mc":"吉艾退","jys":"sz"},{"dm":"300297","mc":"蓝盾退","jys":"sz"},{"dm":"300292","mc":"吴通控股","jys":"sz"},{"dm":"300273","mc":"和佳退","jys":"sz"},{"dm":"300263","mc":"隆华科技","jys":"sz"},{"dm":"300247","mc":"融捷健康","jys":"sz"},{"dm":"300230","mc":"永利股份","jys":"sz"},{"dm":"300223","mc":"北京君正","jys":"sz"},{"dm":"300216","mc":"千山退","jys":"sz"},{"dm":"300209","mc":"ST有棵树","jys":"sz"},{"dm":"300202","mc":"聚龙退","jys":"sz"},{"dm":"300186","mc":"大华农","jys":"sz"},{"dm":"300185","mc":"通裕重工","jys":"sz"},{"dm":"300184","mc":"力源信息","jys":"sz"},{"dm":"300178","mc":"腾邦退","jys":"sz"},{"dm":"300163","mc":"先锋新材","jys":"sz"},{"dm":"300156","mc":"神雾退","jys":"sz"},{"dm":"300155","mc":"安居宝","jys":"sz"},{"dm":"300117","mc":"嘉寓股份","jys":"sz"},{"dm":"300116","mc":"保力新","jys":"sz"},{"dm":"300107","mc":"建新股份","jys":"sz"},{"dm":"300104","mc":"乐视退","jys":"sz"},{"dm":"300090","mc":"盛运退","jys":"sz"},{"dm":"300089","mc":"文化退","jys":"sz"},{"dm":"300078","mc":"思创医惠","jys":"sz"},{"dm":"300064","mc":"金刚退","jys":"sz"},{"dm":"300038","mc":"数知退","jys":"sz"},{"dm":"300028","mc":"金亚退","jys":"sz"},{"dm":"300027","mc":"华谊兄弟","jys":"sz"},{"dm":"300024","mc":"机器人","jys":"sz"},{"dm":"300023","mc":"宝德退","jys":"sz"},{"dm":"300019","mc":"硅宝科技","jys":"sz"},{"dm":"300009","mc":"安科生物","jys":"sz"},{"dm":"003013","mc":"地铁设计","jys":"sz"},{"dm":"002958","mc":"青农商行","jys":"sz"},{"dm":"002936","mc":"郑州银行","jys":"sz"},{"dm":"002933","mc":"新兴装备","jys":"sz"},{"dm":"002839","mc":"张家港行","jys":"sz"},{"dm":"002801","mc":"微光股份","jys":"sz"},{"dm":"002786","mc":"银宝山新","jys":"sz"},{"dm":"002781","mc":"奇信退","jys":"sz"},{"dm":"002770","mc":"科迪退","jys":"sz"},{"dm":"002762","mc":"金发拉比","jys":"sz"},{"dm":"002751","mc":"易尚退","jys":"sz"},{"dm":"002711","mc":"欧浦退","jys":"sz"},{"dm":"002685","mc":"华东重机","jys":"sz"},{"dm":"002684","mc":"猛狮退","jys":"sz"},{"dm":"002680","mc":"长生退","jys":"sz"},{"dm":"002665","mc":"首航高科","jys":"sz"},{"dm":"002659","mc":"凯文教育","jys":"sz"},{"dm":"002656","mc":"ST摩登","jys":"sz"},{"dm":"002641","mc":"公元股份","jys":"sz"},{"dm":"002638","mc":"勤上股份","jys":"sz"},{"dm":"002622","mc":"皓宸医疗","jys":"sz"},{"dm":"002619","mc":"*ST艾格","jys":"sz"},{"dm":"002618","mc":"丹邦退","jys":"sz"},{"dm":"002610","mc":"爱康科技","jys":"sz"},{"dm":"002604","mc":"龙力退","jys":"sz"},{"dm":"002583","mc":"海能达","jys":"sz"},{"dm":"002580","mc":"圣阳股份","jys":"sz"},{"dm":"002578","mc":"闽发铝业","jys":"sz"},{"dm":"002554","mc":"惠博普","jys":"sz"},{"dm":"002540","mc":"亚太科技","jys":"sz"},{"dm":"002539","mc":"云图控股","jys":"sz"},{"dm":"002533","mc":"金杯电工","jys":"sz"},{"dm":"002517","mc":"恺英网络","jys":"sz"},{"dm":"002509","mc":"天茂退","jys":"sz"},{"dm":"002506","mc":"协鑫集成","jys":"sz"},{"dm":"002504","mc":"*ST弘高","jys":"sz"},{"dm":"002503","mc":"*ST搜特","jys":"sz"},{"dm":"002499","mc":"科林退","jys":"sz"},{"dm":"002496","mc":"辉丰股份","jys":"sz"},{"dm":"002486","mc":"嘉麟杰","jys":"sz"},{"dm":"002478","mc":"常宝股份","jys":"sz"},{"dm":"002477","mc":"雏鹰退","jys":"sz"},{"dm":"002473","mc":"圣莱退","jys":"sz"},{"dm":"002464","mc":"众应退","jys":"sz"},{"dm":"002450","mc":"康得退","jys":"sz"},{"dm":"002447","mc":"晨鑫退","jys":"sz"},{"dm":"002443","mc":"金洲管道","jys":"sz"},{"dm":"002439","mc":"启明星辰","jys":"sz"},{"dm":"002427","mc":"尤夫股份","jys":"sz"},{"dm":"002421","mc":"达实智能","jys":"sz"},{"dm":"002417","mc":"深南退","jys":"sz"},{"dm":"002411","mc":"必康退","jys":"sz"},{"dm":"002408","mc":"齐翔腾达","jys":"sz"},{"dm":"002397","mc":"梦洁股份","jys":"sz"},{"dm":"002378","mc":"章源钨业","jys":"sz"},{"dm":"002369","mc":"卓翼科技","jys":"sz"},{"dm":"002361","mc":"神剑股份","jys":"sz"},{"dm":"002359","mc":"北讯退","jys":"sz"},{"dm":"002325","mc":"洪涛股份","jys":"sz"},{"dm":"002321","mc":"华英农业","jys":"sz"},{"dm":"002307","mc":"北新路桥","jys":"sz"},{"dm":"002303","mc":"美盈森","jys":"sz"},{"dm":"002283","mc":"天润工业","jys":"sz"},{"dm":"002260","mc":"德奥退","jys":"sz"},{"dm":"002259","mc":"ST升达","jys":"sz"},{"dm":"002256","mc":"兆新股份","jys":"sz"},{"dm":"002249","mc":"大洋电机","jys":"sz"},{"dm":"002248","mc":"华东数控","jys":"sz"},{"dm":"002246","mc":"北化股份","jys":"sz"},{"dm":"002243","mc":"力合科创","jys":"sz"},{"dm":"002239","mc":"奥特佳","jys":"sz"},{"dm":"002231","mc":"奥维通信","jys":"sz"},{"dm":"002220","mc":"天宝退","jys":"sz"},{"dm":"002214","mc":"大立科技","jys":"sz"},{"dm":"002177","mc":"御银股份","jys":"sz"},{"dm":"002165","mc":"红 宝 丽","jys":"sz"},{"dm":"002163","mc":"海南发展","jys":"sz"},{"dm":"002160","mc":"常铝股份","jys":"sz"},{"dm":"002147","mc":"新光退","jys":"sz"},{"dm":"002143","mc":"印纪退","jys":"sz"},{"dm":"002136","mc":"安 纳 达","jys":"sz"},{"dm":"002135","mc":"东南网架","jys":"sz"},{"dm":"002127","mc":"南极电商","jys":"sz"},{"dm":"002118","mc":"*ST紫鑫","jys":"sz"},{"dm":"002113","mc":"*ST天润","jys":"sz"},{"dm":"002076","mc":"星光股份","jys":"sz"},{"dm":"002071","mc":"长城退","jys":"sz"},{"dm":"002070","mc":"众和退","jys":"sz"},{"dm":"002064","mc":"华峰化学","jys":"sz"},{"dm":"002062","mc":"宏润建设","jys":"sz"},{"dm":"002054","mc":"德美化工","jys":"sz"},{"dm":"002049","mc":"紫光国微","jys":"sz"},{"dm":"002038","mc":"双鹭药业","jys":"sz"},{"dm":"002037","mc":"保利联合","jys":"sz"},{"dm":"002030","mc":"达安基因","jys":"sz"},{"dm":"002024","mc":"ST易购","jys":"sz"},{"dm":"002018","mc":"华信退","jys":"sz"},{"dm":"002013","mc":"中航机电","jys":"sz"},{"dm":"002005","mc":"ST德豪","jys":"sz"},{"dm":"001326","mc":"联域股份","jys":"sz"},{"dm":"001306","mc":"夏厦精密","jys":"sz"},{"dm":"001234","mc":"泰慕士","jys":"sz"},{"dm":"001222","mc":"源飞宠物","jys":"sz"},{"dm":"001219","mc":"青岛食品","jys":"sz"},{"dm":"001218","mc":"丽臣实业","jys":"sz"},{"dm":"000987","mc":"越秀资本","jys":"sz"},{"dm":"000982","mc":"中银绒业","jys":"sz"},{"dm":"000979","mc":"中弘退","jys":"sz"},{"dm":"000972","mc":"中基健康","jys":"sz"},{"dm":"000965","mc":"天保基建","jys":"sz"},{"dm":"000956","mc":"中原油气","jys":"sz"},{"dm":"000939","mc":"凯迪退","jys":"sz"},{"dm":"000926","mc":"福星股份","jys":"sz"},{"dm":"000918","mc":"*ST嘉凯","jys":"sz"},{"dm":"000916","mc":"华北高速","jys":"sz"},{"dm":"000903","mc":"云内动力","jys":"sz"},{"dm":"000876","mc":"新 希 望","jys":"sz"},{"dm":"000866","mc":"扬子石化","jys":"sz"},{"dm":"000852","mc":"石化机械","jys":"sz"},{"dm":"000839","mc":"ST国安","jys":"sz"},{"dm":"000836","mc":"富通信息","jys":"sz"},{"dm":"000835","mc":"长动退","jys":"sz"},{"dm":"000832","mc":"*ST龙涤","jys":"sz"},{"dm":"000827","mc":"*ST长兴","jys":"sz"},{"dm":"000817","mc":"辽河油田","jys":"sz"},{"dm":"000806","mc":"银河退","jys":"sz"},{"dm":"000805","mc":"*ST炎黄","jys":"sz"},{"dm":"000800","mc":"一汽解放","jys":"sz"},{"dm":"000787","mc":"*ST创智","jys":"sz"},{"dm":"000780","mc":"ST平能","jys":"sz"},{"dm":"000769","mc":"*ST大菲","jys":"sz"},{"dm":"000765","mc":"*ST华信","jys":"sz"},{"dm":"000763","mc":"锦州石化","jys":"sz"},{"dm":"000760","mc":"斯太退","jys":"sz"},{"dm":"000751","mc":"锌业股份","jys":"sz"},{"dm":"000748","mc":"长城信息","jys":"sz"},{"dm":"000736","mc":"中交地产","jys":"sz"},{"dm":"000732","mc":"ST泰禾","jys":"sz"},{"dm":"000730","mc":"*ST环保","jys":"sz"},{"dm":"000728","mc":"国元证券","jys":"sz"},{"dm":"000726","mc":"鲁 泰A","jys":"sz"},{"dm":"000699","mc":"S*ST佳纸","jys":"sz"},{"dm":"000693","mc":"华泽退","jys":"sz"},{"dm":"000689","mc":"ST宏业","jys":"sz"},{"dm":"000687","mc":"华讯退","jys":"sz"},{"dm":"000675","mc":"ST银山","jys":"sz"},{"dm":"000673","mc":"当代退","jys":"sz"},{"dm":"000671","mc":"ST阳光城","jys":"sz"},{"dm":"000667","mc":"ST美置","jys":"sz"},{"dm":"000666","mc":"经纬纺机","jys":"sz"},{"dm":"000662","mc":"天夏退","jys":"sz"},{"dm":"000660","mc":"*ST南华","jys":"sz"},{"dm":"000658","mc":"ST海洋","jys":"sz"},{"dm":"000653","mc":"ST九州","jys":"sz"},{"dm":"000626","mc":"远大控股","jys":"sz"},{"dm":"000621","mc":"*ST比特","jys":"sz"},{"dm":"000618","mc":"吉林化工","jys":"sz"},{"dm":"000616","mc":"*ST海投","jys":"sz"},{"dm":"000613","mc":"东海A退","jys":"sz"},{"dm":"000611","mc":"天首退","jys":"sz"},{"dm":"000606","mc":"顺利退","jys":"sz"},{"dm":"000602","mc":"金马集团","jys":"sz"},{"dm":"000601","mc":"韶能股份","jys":"sz"},{"dm":"000595","mc":"宝塔实业","jys":"sz"},{"dm":"000594","mc":"国恒退","jys":"sz"},{"dm":"000588","mc":"PT粤金曼","jys":"sz"},{"dm":"000587","mc":"*ST金洲","jys":"sz"},{"dm":"000585","mc":"东电退","jys":"sz"},{"dm":"000583","mc":"S*ST托普","jys":"sz"},{"dm":"000578","mc":"盐湖集团","jys":"sz"},{"dm":"000573","mc":"粤宏远A","jys":"sz"},{"dm":"000569","mc":"长城股份","jys":"sz"},{"dm":"000566","mc":"海南海药","jys":"sz"},{"dm":"000564","mc":"ST大集","jys":"sz"},{"dm":"000563","mc":"陕国投A","jys":"sz"},{"dm":"000562","mc":"宏源证券","jys":"sz"},{"dm":"000558","mc":"莱茵体育","jys":"sz"},{"dm":"000556","mc":"PT南洋","jys":"sz"},{"dm":"000552","mc":"甘肃能化","jys":"sz"},{"dm":"000549","mc":"S湘火炬","jys":"sz"},{"dm":"000542","mc":"TCL通讯","jys":"sz"},{"dm":"000540","mc":"*ST中天","jys":"sz"},{"dm":"000539","mc":"粤电力A","jys":"sz"},{"dm":"000535","mc":"*ST猴王","jys":"sz"},{"dm":"000527","mc":"美的电器","jys":"sz"},{"dm":"000522","mc":"白云山A","jys":"sz"},{"dm":"000518","mc":"四环生物","jys":"sz"},{"dm":"000515","mc":"攀渝钛业","jys":"sz"},{"dm":"000511","mc":"烯碳退","jys":"sz"},{"dm":"000508","mc":"琼民源A","jys":"sz"},{"dm":"000505","mc":"京粮控股","jys":"sz"},{"dm":"000502","mc":"绿景退","jys":"sz"},{"dm":"000418","mc":"小天鹅A","jys":"sz"},{"dm":"000412","mc":"ST五环","jys":"sz"},{"dm":"000406","mc":"石油大明","jys":"sz"},{"dm":"000405","mc":"ST鑫光","jys":"sz"},{"dm":"000404","mc":"长虹华意","jys":"sz"},{"dm":"000153","mc":"丰原药业","jys":"sz"},{"dm":"000150","mc":"*ST宜康","jys":"sz"},{"dm":"000100","mc":"TCL科技","jys":"sz"},{"dm":"000088","mc":"盐 田 港","jys":"sz"},{"dm":"000047","mc":"ST中侨","jys":"sz"},{"dm":"000038","mc":"大通退","jys":"sz"},{"dm":"000037","mc":"深南电A","jys":"sz"},{"dm":"000033","mc":"新都退","jys":"sz"},{"dm":"000024","mc":"招商地产","jys":"sz"},{"dm":"000018","mc":"神城A退","jys":"sz"},{"dm":"000015","mc":"PT中浩A","jys":"sz"},{"dm":"000013","mc":"*ST石化A","jys":"sz"},{"dm":"000012","mc":"南 玻A","jys":"sz"},{"dm":"000005","mc":"ST星源","jys":"sz"},{"dm":"000003","mc":"PT金田A","jys":"sz"},{"dm":"600941","mc":"中国移动","jys":"sh"},{"dm":"002991","mc":"甘源食品","jys":"sz"},{"dm":"300492","mc":"华图山鼎","jys":"sz"},{"dm":"301051","mc":"信濠光电","jys":"sz"},{"dm":"003043","mc":"华亚智能","jys":"sz"},{"dm":"688671","mc":"碧兴物联","jys":"sh"},{"dm":"688419","mc":"耐科装备","jys":"sh"},{"dm":"688232","mc":"新点软件","jys":"sh"},{"dm":"301170","mc":"锡南科技","jys":"sz"},{"dm":"300827","mc":"上能电气","jys":"sz"},{"dm":"603888","mc":"新华网","jys":"sh"},{"dm":"301045","mc":"天禄科技","jys":"sz"},{"dm":"688711","mc":"宏微科技","jys":"sh"},{"dm":"300816","mc":"艾可蓝","jys":"sz"},{"dm":"688359","mc":"三孚新科","jys":"sh"},{"dm":"301279","mc":"金道科技","jys":"sz"},{"dm":"301135","mc":"瑞德智能","jys":"sz"},{"dm":"603276","mc":"恒兴新材","jys":"sh"},{"dm":"688231","mc":"隆达股份","jys":"sh"},{"dm":"301096","mc":"百诚医药","jys":"sz"},{"dm":"002459","mc":"晶澳科技","jys":"sz"},{"dm":"301336","mc":"趣睡科技","jys":"sz"},{"dm":"300907","mc":"康平科技","jys":"sz"},{"dm":"000756","mc":"新华制药","jys":"sz"},{"dm":"301395","mc":"仁信新材","jys":"sz"},{"dm":"300602","mc":"飞荣达","jys":"sz"},{"dm":"603170","mc":"宝立食品","jys":"sh"},{"dm":"603917","mc":"合力科技","jys":"sh"},{"dm":"301125","mc":"腾亚精工","jys":"sz"},{"dm":"301115","mc":"建科股份","jys":"sz"},{"dm":"002897","mc":"意华股份","jys":"sz"},{"dm":"301006","mc":"迈拓股份","jys":"sz"},{"dm":"600055","mc":"万东医疗","jys":"sh"},{"dm":"002935","mc":"天奥电子","jys":"sz"},{"dm":"301079","mc":"邵阳液压","jys":"sz"},{"dm":"601607","mc":"上海医药","jys":"sh"},{"dm":"603639","mc":"海利尔","jys":"sh"},{"dm":"300416","mc":"苏试试验","jys":"sz"},{"dm":"301446","mc":"福事特","jys":"sz"},{"dm":"300946","mc":"恒而达","jys":"sz"},{"dm":"002961","mc":"瑞达期货","jys":"sz"},{"dm":"300777","mc":"中简科技","jys":"sz"},{"dm":"603171","mc":"税友股份","jys":"sh"},{"dm":"300932","mc":"三友联众","jys":"sz"},{"dm":"688361","mc":"中科飞测-U","jys":"sh"},{"dm":"600378","mc":"昊华科技","jys":"sh"},{"dm":"301130","mc":"西点药业","jys":"sz"},{"dm":"603151","mc":"邦基科技","jys":"sh"},{"dm":"002034","mc":"旺能环境","jys":"sz"},{"dm":"000951","mc":"中国重汽","jys":"sz"},{"dm":"600706","mc":"曲江文旅","jys":"sh"},{"dm":"600884","mc":"杉杉股份","jys":"sh"},{"dm":"002859","mc":"洁美科技","jys":"sz"},{"dm":"600089","mc":"特变电工","jys":"sh"},{"dm":"600760","mc":"中航沈飞","jys":"sh"},{"dm":"300800","mc":"力合科技","jys":"sz"},{"dm":"603701","mc":"德宏股份","jys":"sh"},{"dm":"600508","mc":"上海能源","jys":"sh"},{"dm":"600487","mc":"亨通光电","jys":"sh"},{"dm":"300396","mc":"迪瑞医疗","jys":"sz"},{"dm":"000333","mc":"美的集团","jys":"sz"},{"dm":"002943","mc":"宇晶股份","jys":"sz"},{"dm":"000777","mc":"中核科技","jys":"sz"},{"dm":"300942","mc":"易瑞生物","jys":"sz"},{"dm":"301388","mc":"欣灵电气","jys":"sz"},{"dm":"300792","mc":"壹网壹创","jys":"sz"},{"dm":"003025","mc":"思进智能","jys":"sz"},{"dm":"300340","mc":"科恒股份","jys":"sz"},{"dm":"603890","mc":"春秋电子","jys":"sh"},{"dm":"000567","mc":"海德股份","jys":"sz"},{"dm":"600847","mc":"万里股份","jys":"sh"},{"dm":"002883","mc":"中设股份","jys":"sz"},{"dm":"001378","mc":"德冠新材","jys":"sz"},{"dm":"601012","mc":"隆基绿能","jys":"sh"},{"dm":"600060","mc":"海信视像","jys":"sh"},{"dm":"300409","mc":"道氏技术","jys":"sz"},{"dm":"300462","mc":"华铭智能","jys":"sz"},{"dm":"301353","mc":"普莱得","jys":"sz"},{"dm":"002493","mc":"荣盛石化","jys":"sz"},{"dm":"301335","mc":"天元宠物","jys":"sz"},{"dm":"603057","mc":"紫燕食品","jys":"sh"},{"dm":"603213","mc":"镇洋发展","jys":"sh"},{"dm":"002458","mc":"益生股份","jys":"sz"},{"dm":"600048","mc":"保利发展","jys":"sh"},{"dm":"603307","mc":"扬州金泉","jys":"sh"},{"dm":"000999","mc":"华润三九","jys":"sz"},{"dm":"300771","mc":"智莱科技","jys":"sz"},{"dm":"688511","mc":"天微电子","jys":"sh"},{"dm":"300980","mc":"祥源新材","jys":"sz"},{"dm":"000688","mc":"国城矿业","jys":"sz"},{"dm":"002677","mc":"浙江美大","jys":"sz"},{"dm":"603043","mc":"广州酒家","jys":"sh"},{"dm":"600645","mc":"中源协和","jys":"sh"},{"dm":"688330","mc":"宏力达","jys":"sh"},{"dm":"600223","mc":"福瑞达","jys":"sh"},{"dm":"003029","mc":"吉大正元","jys":"sz"},{"dm":"002405","mc":"四维图新","jys":"sz"},{"dm":"301002","mc":"崧盛股份","jys":"sz"},{"dm":"002598","mc":"山东章鼓","jys":"sz"},{"dm":"002920","mc":"德赛西威","jys":"sz"},{"dm":"688035","mc":"德邦科技","jys":"sh"},{"dm":"603038","mc":"华立股份","jys":"sh"},{"dm":"688788","mc":"科思科技","jys":"sh"},{"dm":"002706","mc":"良信股份","jys":"sz"},{"dm":"600099","mc":"林海股份","jys":"sh"},{"dm":"601100","mc":"恒立液压","jys":"sh"},{"dm":"688663","mc":"新风光","jys":"sh"},{"dm":"601177","mc":"杭齿前进","jys":"sh"},{"dm":"300265","mc":"通光线缆","jys":"sz"},{"dm":"603150","mc":"万朗磁塑","jys":"sh"},{"dm":"002349","mc":"精华制药","jys":"sz"},{"dm":"600995","mc":"南网储能","jys":"sh"},{"dm":"001256","mc":"炜冈科技","jys":"sz"},{"dm":"603345","mc":"安井食品","jys":"sh"},{"dm":"601949","mc":"中国出版","jys":"sh"},{"dm":"002824","mc":"和胜股份","jys":"sz"},{"dm":"688320","mc":"禾川科技","jys":"sh"},{"dm":"002955","mc":"鸿合科技","jys":"sz"},{"dm":"300271","mc":"华宇软件","jys":"sz"},{"dm":"300902","mc":"国安达","jys":"sz"},{"dm":"000635","mc":"英 力 特","jys":"sz"},{"dm":"300541","mc":"先进数通","jys":"sz"},{"dm":"000561","mc":"烽火电子","jys":"sz"},{"dm":"603789","mc":"星光农机","jys":"sh"},{"dm":"600480","mc":"凌云股份","jys":"sh"},{"dm":"600158","mc":"中体产业","jys":"sh"},{"dm":"603080","mc":"新疆火炬","jys":"sh"},{"dm":"301167","mc":"建研设计","jys":"sz"},{"dm":"600062","mc":"华润双鹤","jys":"sh"},{"dm":"002916","mc":"深南电路","jys":"sz"},{"dm":"300613","mc":"富瀚微","jys":"sz"},{"dm":"002836","mc":"新宏泽","jys":"sz"},{"dm":"600101","mc":"明星电力","jys":"sh"},{"dm":"000713","mc":"丰乐种业","jys":"sz"},{"dm":"300030","mc":"阳普医疗","jys":"sz"},{"dm":"600037","mc":"歌华有线","jys":"sh"},{"dm":"300599","mc":"雄塑科技","jys":"sz"},{"dm":"688321","mc":"微芯生物","jys":"sh"},{"dm":"600834","mc":"申通地铁","jys":"sh"},{"dm":"002254","mc":"泰和新材","jys":"sz"},{"dm":"601519","mc":"大智慧","jys":"sh"},{"dm":"300617","mc":"安靠智电","jys":"sz"},{"dm":"600143","mc":"金发科技","jys":"sh"},{"dm":"300261","mc":"雅本化学","jys":"sz"},{"dm":"688347","mc":"华虹公司","jys":"sh"},{"dm":"605186","mc":"健麾信息","jys":"sh"},{"dm":"000935","mc":"四川双马","jys":"sz"},{"dm":"002200","mc":"ST交投","jys":"sz"},{"dm":"688082","mc":"盛美上海","jys":"sh"},{"dm":"603167","mc":"渤海轮渡","jys":"sh"},{"dm":"300618","mc":"寒锐钴业","jys":"sz"},{"dm":"301099","mc":"雅创电子","jys":"sz"},{"dm":"002041","mc":"登海种业","jys":"sz"},{"dm":"000776","mc":"广发证券","jys":"sz"},{"dm":"300871","mc":"回盛生物","jys":"sz"},{"dm":"688523","mc":"航天环宇","jys":"sh"},{"dm":"688230","mc":"芯导科技","jys":"sh"},{"dm":"000685","mc":"中山公用","jys":"sz"},{"dm":"000881","mc":"中广核技","jys":"sz"},{"dm":"605399","mc":"晨光新材","jys":"sh"},{"dm":"300972","mc":"万辰集团","jys":"sz"},{"dm":"605055","mc":"迎丰股份","jys":"sh"},{"dm":"000906","mc":"浙商中拓","jys":"sz"},{"dm":"002937","mc":"兴瑞科技","jys":"sz"},{"dm":"600522","mc":"中天科技","jys":"sh"},{"dm":"300016","mc":"北陆药业","jys":"sz"},{"dm":"301075","mc":"多瑞医药","jys":"sz"},{"dm":"002402","mc":"和而泰","jys":"sz"},{"dm":"002768","mc":"国恩股份","jys":"sz"},{"dm":"000905","mc":"厦门港务","jys":"sz"},{"dm":"002451","mc":"摩恩电气","jys":"sz"},{"dm":"601801","mc":"皖新传媒","jys":"sh"},{"dm":"600230","mc":"沧州大化","jys":"sh"},{"dm":"002452","mc":"长高电新","jys":"sz"},{"dm":"002668","mc":"奥马电器","jys":"sz"},{"dm":"600586","mc":"金晶科技","jys":"sh"},{"dm":"300829","mc":"金丹科技","jys":"sz"},{"dm":"002114","mc":"罗平锌电","jys":"sz"},{"dm":"300290","mc":"荣科科技","jys":"sz"},{"dm":"603303","mc":"得邦照明","jys":"sh"},{"dm":"000978","mc":"桂林旅游","jys":"sz"},{"dm":"002301","mc":"齐心集团","jys":"sz"},{"dm":"600456","mc":"宝钛股份","jys":"sh"},{"dm":"002837","mc":"英维克","jys":"sz"},{"dm":"301126","mc":"达嘉维康","jys":"sz"},{"dm":"300232","mc":"洲明科技","jys":"sz"},{"dm":"002747","mc":"埃斯顿","jys":"sz"},{"dm":"688616","mc":"西力科技","jys":"sh"},{"dm":"603016","mc":"新宏泰","jys":"sh"},{"dm":"600141","mc":"兴发集团","jys":"sh"},{"dm":"603358","mc":"华达科技","jys":"sh"},{"dm":"000683","mc":"远兴能源","jys":"sz"},{"dm":"600057","mc":"厦门象屿","jys":"sh"},{"dm":"688130","mc":"晶华微","jys":"sh"},{"dm":"688151","mc":"华强科技","jys":"sh"},{"dm":"603658","mc":"安图生物","jys":"sh"},{"dm":"002571","mc":"德力股份","jys":"sz"},{"dm":"603185","mc":"弘元绿能","jys":"sh"},{"dm":"002142","mc":"宁波银行","jys":"sz"},{"dm":"688023","mc":"安恒信息","jys":"sh"},{"dm":"603177","mc":"德创环保","jys":"sh"},{"dm":"301027","mc":"华蓝集团","jys":"sz"},{"dm":"688003","mc":"天准科技","jys":"sh"},{"dm":"002990","mc":"盛视科技","jys":"sz"},{"dm":"300911","mc":"亿田智能","jys":"sz"},{"dm":"600778","mc":"友好集团","jys":"sh"},{"dm":"600446","mc":"金证股份","jys":"sh"},{"dm":"300018","mc":"中元股份","jys":"sz"},{"dm":"600845","mc":"宝信软件","jys":"sh"},{"dm":"002557","mc":"洽洽食品","jys":"sz"},{"dm":"002103","mc":"广博股份","jys":"sz"},{"dm":"600395","mc":"盘江股份","jys":"sh"},{"dm":"301009","mc":"可靠股份","jys":"sz"},{"dm":"002272","mc":"川润股份","jys":"sz"},{"dm":"601279","mc":"英利汽车","jys":"sh"},{"dm":"603225","mc":"新凤鸣","jys":"sh"},{"dm":"301356","mc":"天振股份","jys":"sz"},{"dm":"300417","mc":"南华仪器","jys":"sz"},{"dm":"002340","mc":"格林美","jys":"sz"},{"dm":"001230","mc":"劲旅环境","jys":"sz"},{"dm":"603280","mc":"南方路机","jys":"sh"},{"dm":"000668","mc":"荣丰控股","jys":"sz"},{"dm":"688155","mc":"先惠技术","jys":"sh"},{"dm":"600183","mc":"生益科技","jys":"sh"},{"dm":"601136","mc":"首创证券","jys":"sh"},{"dm":"600516","mc":"方大炭素","jys":"sh"},{"dm":"000498","mc":"山东路桥","jys":"sz"},{"dm":"600180","mc":"瑞茂通","jys":"sh"},{"dm":"002529","mc":"海源复材","jys":"sz"},{"dm":"601098","mc":"中南传媒","jys":"sh"},{"dm":"601825","mc":"沪农商行","jys":"sh"},{"dm":"301265","mc":"华新环保","jys":"sz"},{"dm":"600030","mc":"中信证券","jys":"sh"},{"dm":"600406","mc":"国电南瑞","jys":"sh"},{"dm":"603021","mc":"山东华鹏","jys":"sh"},{"dm":"002170","mc":"芭田股份","jys":"sz"},{"dm":"605169","mc":"洪通燃气","jys":"sh"},{"dm":"600015","mc":"华夏银行","jys":"sh"},{"dm":"000837","mc":"秦川机床","jys":"sz"},{"dm":"001201","mc":"东瑞股份","jys":"sz"},{"dm":"301309","mc":"万得凯","jys":"sz"},{"dm":"000791","mc":"甘肃能源","jys":"sz"},{"dm":"688680","mc":"海优新材","jys":"sh"},{"dm":"605111","mc":"新洁能","jys":"sh"},{"dm":"301087","mc":"可孚医疗","jys":"sz"},{"dm":"603683","mc":"晶华新材","jys":"sh"},{"dm":"603211","mc":"晋拓股份","jys":"sh"},{"dm":"000932","mc":"华菱钢铁","jys":"sz"},{"dm":"300772","mc":"运达股份","jys":"sz"},{"dm":"601339","mc":"百隆东方","jys":"sh"},{"dm":"301109","mc":"军信股份","jys":"sz"},{"dm":"301119","mc":"正强股份","jys":"sz"},{"dm":"601366","mc":"利群股份","jys":"sh"},{"dm":"002347","mc":"泰尔股份","jys":"sz"},{"dm":"301468","mc":"博盈特焊","jys":"sz"},{"dm":"603508","mc":"思维列控","jys":"sh"},{"dm":"600660","mc":"福耀玻璃","jys":"sh"},{"dm":"002681","mc":"奋达科技","jys":"sz"},{"dm":"002627","mc":"三峡旅游","jys":"sz"},{"dm":"002060","mc":"粤 水 电","jys":"sz"},{"dm":"002415","mc":"海康威视","jys":"sz"},{"dm":"600916","mc":"中国黄金","jys":"sh"},{"dm":"300867","mc":"圣元环保","jys":"sz"},{"dm":"002507","mc":"涪陵榨菜","jys":"sz"},{"dm":"600193","mc":"创兴资源","jys":"sh"},{"dm":"002438","mc":"江苏神通","jys":"sz"},{"dm":"000957","mc":"中通客车","jys":"sz"},{"dm":"002573","mc":"清新环境","jys":"sz"},{"dm":"300651","mc":"金陵体育","jys":"sz"},{"dm":"002485","mc":"*ST雪发","jys":"sz"},{"dm":"605183","mc":"确成股份","jys":"sh"},{"dm":"601211","mc":"国泰君安","jys":"sh"},{"dm":"605488","mc":"福莱新材","jys":"sh"},{"dm":"002365","mc":"永安药业","jys":"sz"},{"dm":"600740","mc":"山西焦化","jys":"sh"},{"dm":"605358","mc":"立昂微","jys":"sh"},{"dm":"688385","mc":"复旦微电","jys":"sh"},{"dm":"002090","mc":"金智科技","jys":"sz"},{"dm":"600750","mc":"江中药业","jys":"sh"},{"dm":"601700","mc":"风范股份","jys":"sh"},{"dm":"600459","mc":"贵研铂业","jys":"sh"},{"dm":"002922","mc":"伊戈尔","jys":"sz"},{"dm":"002917","mc":"金奥博","jys":"sz"},{"dm":"002688","mc":"金河生物","jys":"sz"},{"dm":"603060","mc":"国检集团","jys":"sh"},{"dm":"002462","mc":"嘉事堂","jys":"sz"},{"dm":"300021","mc":"大禹节水","jys":"sz"},{"dm":"000158","mc":"常山北明","jys":"sz"},{"dm":"002611","mc":"东方精工","jys":"sz"},{"dm":"000417","mc":"合肥百货","jys":"sz"},{"dm":"002989","mc":"中天精装","jys":"sz"},{"dm":"002004","mc":"华邦健康","jys":"sz"},{"dm":"600901","mc":"江苏金租","jys":"sh"},{"dm":"002497","mc":"雅化集团","jys":"sz"},{"dm":"002441","mc":"众业达","jys":"sz"},{"dm":"000993","mc":"闽东电力","jys":"sz"},{"dm":"002025","mc":"航天电器","jys":"sz"},{"dm":"603587","mc":"地素时尚","jys":"sh"},{"dm":"601233","mc":"桐昆股份","jys":"sh"},{"dm":"301246","mc":"宏源药业","jys":"sz"},{"dm":"002232","mc":"启明信息","jys":"sz"},{"dm":"301500","mc":"飞南资源","jys":"sz"},{"dm":"688549","mc":"中巨芯-U","jys":"sh"},{"dm":"300959","mc":"线上线下","jys":"sz"},{"dm":"600217","mc":"中再资环","jys":"sh"},{"dm":"688122","mc":"西部超导","jys":"sh"},{"dm":"002778","mc":"中晟高科","jys":"sz"},{"dm":"300529","mc":"健帆生物","jys":"sz"},{"dm":"600382","mc":"广东明珠","jys":"sh"},{"dm":"600857","mc":"宁波中百","jys":"sh"},{"dm":"600058","mc":"五矿发展","jys":"sh"},{"dm":"600303","mc":"ST曙光","jys":"sh"},{"dm":"000758","mc":"中色股份","jys":"sz"},{"dm":"600711","mc":"盛屯矿业","jys":"sh"},{"dm":"600251","mc":"冠农股份","jys":"sh"},{"dm":"605162","mc":"新中港","jys":"sh"},{"dm":"300094","mc":"国联水产","jys":"sz"},{"dm":"603601","mc":"再升科技","jys":"sh"},{"dm":"300623","mc":"捷捷微电","jys":"sz"},{"dm":"300026","mc":"红日药业","jys":"sz"},{"dm":"301238","mc":"瑞泰新材","jys":"sz"},{"dm":"300481","mc":"濮阳惠成","jys":"sz"},{"dm":"688166","mc":"博瑞医药","jys":"sh"},{"dm":"601598","mc":"中国外运","jys":"sh"},{"dm":"600123","mc":"兰花科创","jys":"sh"},{"dm":"600197","mc":"伊力特","jys":"sh"},{"dm":"600538","mc":"国发股份","jys":"sh"},{"dm":"300106","mc":"西部牧业","jys":"sz"},{"dm":"000733","mc":"振华科技","jys":"sz"},{"dm":"601727","mc":"上海电气","jys":"sh"},{"dm":"688375","mc":"国博电子","jys":"sh"},{"dm":"002162","mc":"悦心健康","jys":"sz"},{"dm":"300753","mc":"爱朋医疗","jys":"sz"},{"dm":"000759","mc":"中百集团","jys":"sz"},{"dm":"000912","mc":"泸天化","jys":"sz"},{"dm":"301053","mc":"远信工业","jys":"sz"},{"dm":"603858","mc":"步长制药","jys":"sh"},{"dm":"603866","mc":"桃李面包","jys":"sh"},{"dm":"300287","mc":"飞利信","jys":"sz"},{"dm":"603323","mc":"苏农银行","jys":"sh"},{"dm":"002218","mc":"拓日新能","jys":"sz"},{"dm":"002057","mc":"中钢天源","jys":"sz"},{"dm":"688120","mc":"华海清科","jys":"sh"},{"dm":"688032","mc":"禾迈股份","jys":"sh"},{"dm":"300919","mc":"中伟股份","jys":"sz"},{"dm":"002327","mc":"富安娜","jys":"sz"},{"dm":"603583","mc":"捷昌驱动","jys":"sh"},{"dm":"300813","mc":"泰林生物","jys":"sz"},{"dm":"300477","mc":"合纵科技","jys":"sz"},{"dm":"002342","mc":"巨力索具","jys":"sz"},{"dm":"603093","mc":"南华期货","jys":"sh"},{"dm":"301256","mc":"华融化学","jys":"sz"},{"dm":"300733","mc":"西菱动力","jys":"sz"},{"dm":"000883","mc":"湖北能源","jys":"sz"},{"dm":"301518","mc":"长华化学","jys":"sz"},{"dm":"300423","mc":"昇辉科技","jys":"sz"},{"dm":"001299","mc":"美能能源","jys":"sz"},{"dm":"600148","mc":"长春一东","jys":"sh"},{"dm":"600096","mc":"云天化","jys":"sh"},{"dm":"001213","mc":"中铁特货","jys":"sz"},{"dm":"002108","mc":"沧州明珠","jys":"sz"},{"dm":"000958","mc":"电投产融","jys":"sz"},{"dm":"001337","mc":"四川黄金","jys":"sz"},{"dm":"600903","mc":"贵州燃气","jys":"sh"},{"dm":"603368","mc":"柳药集团","jys":"sh"},{"dm":"600617","mc":"国新能源","jys":"sh"},{"dm":"301559","mc":"中集环科","jys":"sz"},{"dm":"002562","mc":"兄弟科技","jys":"sz"},{"dm":"600956","mc":"新天绿能","jys":"sh"},{"dm":"002296","mc":"辉煌科技","jys":"sz"},{"dm":"002001","mc":"新 和 成","jys":"sz"},{"dm":"301069","mc":"凯盛新材","jys":"sz"},{"dm":"300490","mc":"华自科技","jys":"sz"},{"dm":"000900","mc":"现代投资","jys":"sz"},{"dm":"002774","mc":"快意电梯","jys":"sz"},{"dm":"688777","mc":"中控技术","jys":"sh"},{"dm":"601077","mc":"渝农商行","jys":"sh"},{"dm":"000428","mc":"华天酒店","jys":"sz"},{"dm":"603619","mc":"中曼石油","jys":"sh"},{"dm":"000725","mc":"京东方A","jys":"sz"},{"dm":"301234","mc":"五洲医疗","jys":"sz"},{"dm":"003039","mc":"顺控发展","jys":"sz"},{"dm":"301177","mc":"迪阿股份","jys":"sz"},{"dm":"601156","mc":"东航物流","jys":"sh"},{"dm":"301310","mc":"鑫宏业","jys":"sz"},{"dm":"600790","mc":"轻纺城","jys":"sh"},{"dm":"301100","mc":"风光股份","jys":"sz"},{"dm":"600425","mc":"青松建化","jys":"sh"},{"dm":"601877","mc":"正泰电器","jys":"sh"},{"dm":"301098","mc":"金埔园林","jys":"sz"},{"dm":"300005","mc":"探路者","jys":"sz"},{"dm":"001278","mc":"一彬科技","jys":"sz"},{"dm":"603956","mc":"威派格","jys":"sh"},{"dm":"301025","mc":"读客文化","jys":"sz"},{"dm":"300915","mc":"海融科技","jys":"sz"},{"dm":"603843","mc":"正平股份","jys":"sh"},{"dm":"002498","mc":"汉缆股份","jys":"sz"},{"dm":"002172","mc":"澳洋健康","jys":"sz"},{"dm":"600299","mc":"安迪苏","jys":"sh"},{"dm":"688018","mc":"乐鑫科技","jys":"sh"},{"dm":"002339","mc":"积成电子","jys":"sz"},{"dm":"603351","mc":"威尔药业","jys":"sh"},{"dm":"002186","mc":"全 聚 德","jys":"sz"},{"dm":"605007","mc":"五洲特纸","jys":"sh"},{"dm":"600961","mc":"株冶集团","jys":"sh"},{"dm":"603657","mc":"春光科技","jys":"sh"},{"dm":"300452","mc":"山河药辅","jys":"sz"},{"dm":"300500","mc":"启迪设计","jys":"sz"},{"dm":"002061","mc":"浙江交科","jys":"sz"},{"dm":"601827","mc":"三峰环境","jys":"sh"},{"dm":"601061","mc":"中信金属","jys":"sh"},{"dm":"300043","mc":"星辉娱乐","jys":"sz"},{"dm":"002568","mc":"百润股份","jys":"sz"},{"dm":"688560","mc":"明冠新材","jys":"sh"},{"dm":"601116","mc":"三江购物","jys":"sh"},{"dm":"300686","mc":"智动力","jys":"sz"},{"dm":"002266","mc":"浙富控股","jys":"sz"},{"dm":"002056","mc":"横店东磁","jys":"sz"},{"dm":"002984","mc":"森麒麟","jys":"sz"},{"dm":"603317","mc":"天味食品","jys":"sh"},{"dm":"300569","mc":"天能重工","jys":"sz"},{"dm":"300037","mc":"新宙邦","jys":"sz"},{"dm":"600828","mc":"茂业商业","jys":"sh"},{"dm":"300246","mc":"宝莱特","jys":"sz"},{"dm":"688265","mc":"南模生物","jys":"sh"},{"dm":"600746","mc":"江苏索普","jys":"sh"},{"dm":"688187","mc":"时代电气","jys":"sh"},{"dm":"688778","mc":"厦钨新能","jys":"sh"},{"dm":"003028","mc":"振邦智能","jys":"sz"},{"dm":"600137","mc":"浪莎股份","jys":"sh"},{"dm":"002807","mc":"江阴银行","jys":"sz"},{"dm":"603997","mc":"继峰股份","jys":"sh"},{"dm":"002695","mc":"煌上煌","jys":"sz"},{"dm":"300208","mc":"青岛中程","jys":"sz"},{"dm":"000670","mc":"盈方微","jys":"sz"},{"dm":"300486","mc":"东杰智能","jys":"sz"},{"dm":"002484","mc":"江海股份","jys":"sz"},{"dm":"300056","mc":"中创环保","jys":"sz"},{"dm":"688252","mc":"天德钰","jys":"sh"},{"dm":"002216","mc":"三全食品","jys":"sz"},{"dm":"301228","mc":"实朴检测","jys":"sz"},{"dm":"603698","mc":"航天工程","jys":"sh"},{"dm":"600346","mc":"恒力石化","jys":"sh"},{"dm":"601985","mc":"中国核电","jys":"sh"},{"dm":"300680","mc":"隆盛科技","jys":"sz"},{"dm":"002589","mc":"瑞康医药","jys":"sz"},{"dm":"688733","mc":"壹石通","jys":"sh"},{"dm":"603218","mc":"日月股份","jys":"sh"},{"dm":"600433","mc":"冠豪高新","jys":"sh"},{"dm":"688046","mc":"药康生物","jys":"sh"},{"dm":"605006","mc":"山东玻纤","jys":"sh"},{"dm":"301216","mc":"万凯新材","jys":"sz"},{"dm":"688226","mc":"威腾电气","jys":"sh"},{"dm":"600308","mc":"华泰股份","jys":"sh"},{"dm":"301300","mc":"远翔新材","jys":"sz"},{"dm":"002252","mc":"上海莱士","jys":"sz"},{"dm":"300887","mc":"谱尼测试","jys":"sz"},{"dm":"600477","mc":"杭萧钢构","jys":"sh"},{"dm":"600073","mc":"上海梅林","jys":"sh"},{"dm":"002116","mc":"中国海诚","jys":"sz"},{"dm":"600876","mc":"凯盛新能","jys":"sh"},{"dm":"301292","mc":"海科新源","jys":"sz"},{"dm":"688305","mc":"科德数控","jys":"sh"},{"dm":"600489","mc":"中金黄金","jys":"sh"},{"dm":"300658","mc":"延江股份","jys":"sz"},{"dm":"688576","mc":"西山科技","jys":"sh"},{"dm":"000650","mc":"仁和药业","jys":"sz"},{"dm":"301509","mc":"金凯生科","jys":"sz"},{"dm":"605598","mc":"上海港湾","jys":"sh"},{"dm":"600966","mc":"博汇纸业","jys":"sh"},{"dm":"603976","mc":"正川股份","jys":"sh"},{"dm":"600939","mc":"重庆建工","jys":"sh"},{"dm":"603335","mc":"迪生力","jys":"sh"},{"dm":"600917","mc":"重庆燃气","jys":"sh"},{"dm":"300326","mc":"凯利泰","jys":"sz"},{"dm":"002565","mc":"顺灏股份","jys":"sz"},{"dm":"002112","mc":"三变科技","jys":"sz"},{"dm":"002067","mc":"景兴纸业","jys":"sz"},{"dm":"600283","mc":"钱江水利","jys":"sh"},{"dm":"300997","mc":"欢乐家","jys":"sz"},{"dm":"603126","mc":"中材节能","jys":"sh"},{"dm":"300532","mc":"今天国际","jys":"sz"},{"dm":"002193","mc":"如意集团","jys":"sz"},{"dm":"603102","mc":"百合股份","jys":"sh"},{"dm":"688168","mc":"安博通","jys":"sh"},{"dm":"003006","mc":"百亚股份","jys":"sz"},{"dm":"002978","mc":"安宁股份","jys":"sz"},{"dm":"603192","mc":"汇得科技","jys":"sh"},{"dm":"002082","mc":"万邦德","jys":"sz"},{"dm":"000042","mc":"中洲控股","jys":"sz"},{"dm":"605001","mc":"威奥股份","jys":"sh"},{"dm":"600731","mc":"湖南海利","jys":"sh"},{"dm":"300519","mc":"新光药业","jys":"sz"},{"dm":"002636","mc":"金安国纪","jys":"sz"},{"dm":"002798","mc":"帝欧家居","jys":"sz"},{"dm":"600377","mc":"宁沪高速","jys":"sh"},{"dm":"002960","mc":"青鸟消防","jys":"sz"},{"dm":"600350","mc":"山东高速","jys":"sh"},{"dm":"000887","mc":"中鼎股份","jys":"sz"},{"dm":"002992","mc":"宝明科技","jys":"sz"},{"dm":"603421","mc":"鼎信通讯","jys":"sh"},{"dm":"688623","mc":"双元科技","jys":"sh"},{"dm":"600094","mc":"大名城","jys":"sh"},{"dm":"603137","mc":"恒尚节能","jys":"sh"},{"dm":"601200","mc":"上海环境","jys":"sh"},{"dm":"000995","mc":"皇台酒业","jys":"sz"},{"dm":"600933","mc":"爱柯迪","jys":"sh"},{"dm":"688459","mc":"哈铁科技","jys":"sh"},{"dm":"000619","mc":"海螺新材","jys":"sz"},{"dm":"300965","mc":"恒宇信通","jys":"sz"},{"dm":"603886","mc":"元祖股份","jys":"sh"},{"dm":"000023","mc":"ST深天","jys":"sz"},{"dm":"605286","mc":"同力日升","jys":"sh"},{"dm":"002938","mc":"鹏鼎控股","jys":"sz"},{"dm":"601228","mc":"广州港","jys":"sh"},{"dm":"000802","mc":"北京文化","jys":"sz"},{"dm":"603817","mc":"海峡环保","jys":"sh"},{"dm":"002849","mc":"威星智能","jys":"sz"},{"dm":"002538","mc":"司尔特","jys":"sz"},{"dm":"600064","mc":"南京高科","jys":"sh"},{"dm":"000078","mc":"海王生物","jys":"sz"},{"dm":"600635","mc":"大众公用","jys":"sh"},{"dm":"002029","mc":"七 匹 狼","jys":"sz"},{"dm":"300768","mc":"迪普科技","jys":"sz"},{"dm":"688011","mc":"新光光电","jys":"sh"},{"dm":"688297","mc":"中无人机","jys":"sh"},{"dm":"300594","mc":"朗进科技","jys":"sz"},{"dm":"300091","mc":"金通灵","jys":"sz"},{"dm":"603156","mc":"养元饮品","jys":"sh"},{"dm":"300983","mc":"尤安设计","jys":"sz"},{"dm":"603628","mc":"清源股份","jys":"sh"},{"dm":"600490","mc":"鹏欣资源","jys":"sh"},{"dm":"603458","mc":"勘设股份","jys":"sh"},{"dm":"600958","mc":"东方证券","jys":"sh"},{"dm":"688589","mc":"力合微","jys":"sh"},{"dm":"603991","mc":"至正股份","jys":"sh"},{"dm":"601399","mc":"国机重装","jys":"sh"},{"dm":"002202","mc":"金风科技","jys":"sz"},{"dm":"002141","mc":"贤丰控股","jys":"sz"},{"dm":"000617","mc":"中油资本","jys":"sz"},{"dm":"688677","mc":"海泰新光","jys":"sh"},{"dm":"600108","mc":"亚盛集团","jys":"sh"},{"dm":"000858","mc":"五 粮 液","jys":"sz"},{"dm":"603916","mc":"苏博特","jys":"sh"},{"dm":"605555","mc":"德昌股份","jys":"sh"},{"dm":"688349","mc":"三一重能","jys":"sh"},{"dm":"301325","mc":"曼恩斯特","jys":"sz"},{"dm":"002191","mc":"劲嘉股份","jys":"sz"},{"dm":"000002","mc":"万 科A","jys":"sz"},{"dm":"600817","mc":"宇通重工","jys":"sh"},{"dm":"600583","mc":"海油工程","jys":"sh"},{"dm":"688319","mc":"欧林生物","jys":"sh"},{"dm":"601818","mc":"光大银行","jys":"sh"},{"dm":"003816","mc":"中国广核","jys":"sz"},{"dm":"603682","mc":"锦和商管","jys":"sh"},{"dm":"688707","mc":"振华新材","jys":"sh"},{"dm":"000411","mc":"英特集团","jys":"sz"},{"dm":"002932","mc":"明德生物","jys":"sz"},{"dm":"688475","mc":"萤石网络","jys":"sh"},{"dm":"002471","mc":"中超控股","jys":"sz"},{"dm":"300511","mc":"雪榕生物","jys":"sz"},{"dm":"688819","mc":"天能股份","jys":"sh"},{"dm":"002817","mc":"黄山胶囊","jys":"sz"},{"dm":"002022","mc":"科华生物","jys":"sz"},{"dm":"300748","mc":"金力永磁","jys":"sz"},{"dm":"603001","mc":"ST奥康","jys":"sh"},{"dm":"603366","mc":"日出东方","jys":"sh"},{"dm":"688569","mc":"铁科轨道","jys":"sh"},{"dm":"002280","mc":"联络互动","jys":"sz"},{"dm":"600562","mc":"国睿科技","jys":"sh"},{"dm":"600547","mc":"山东黄金","jys":"sh"},{"dm":"300888","mc":"稳健医疗","jys":"sz"},{"dm":"601718","mc":"际华集团","jys":"sh"},{"dm":"002703","mc":"浙江世宝","jys":"sz"},{"dm":"002195","mc":"岩山科技","jys":"sz"},{"dm":"002983","mc":"芯瑞达","jys":"sz"},{"dm":"002581","mc":"未名医药","jys":"sz"},{"dm":"603387","mc":"基蛋生物","jys":"sh"},{"dm":"600017","mc":"日照港","jys":"sh"},{"dm":"300159","mc":"新研股份","jys":"sz"},{"dm":"002705","mc":"新宝股份","jys":"sz"},{"dm":"300413","mc":"芒果超媒","jys":"sz"},{"dm":"603668","mc":"天马科技","jys":"sh"},{"dm":"301063","mc":"海锅股份","jys":"sz"},{"dm":"600239","mc":"云南城投","jys":"sh"},{"dm":"002173","mc":"创新医疗","jys":"sz"},{"dm":"300393","mc":"中来股份","jys":"sz"},{"dm":"002157","mc":"*ST 正邦","jys":"sz"},{"dm":"001316","mc":"润贝航科","jys":"sz"},{"dm":"601311","mc":"骆驼股份","jys":"sh"},{"dm":"301305","mc":"朗坤环境","jys":"sz"},{"dm":"600981","mc":"汇鸿集团","jys":"sh"},{"dm":"000610","mc":"西安旅游","jys":"sz"},{"dm":"300769","mc":"德方纳米","jys":"sz"},{"dm":"600325","mc":"华发股份","jys":"sh"},{"dm":"000419","mc":"通程控股","jys":"sz"},{"dm":"601212","mc":"白银有色","jys":"sh"},{"dm":"603215","mc":"比依股份","jys":"sh"},{"dm":"600098","mc":"广州发展","jys":"sh"},{"dm":"600372","mc":"中航机载","jys":"sh"},{"dm":"002386","mc":"天原股份","jys":"sz"},{"dm":"000737","mc":"北方铜业","jys":"sz"},{"dm":"002125","mc":"湘潭电化","jys":"sz"},{"dm":"000721","mc":"西安饮食","jys":"sz"},{"dm":"000517","mc":"荣安地产","jys":"sz"},{"dm":"300791","mc":"仙乐健康","jys":"sz"},{"dm":"300182","mc":"捷成股份","jys":"sz"},{"dm":"000030","mc":"富奥股份","jys":"sz"},{"dm":"002758","mc":"浙农股份","jys":"sz"},{"dm":"301112","mc":"信邦智能","jys":"sz"},{"dm":"301088","mc":"戎美股份","jys":"sz"},{"dm":"300853","mc":"申昊科技","jys":"sz"},{"dm":"002048","mc":"宁波华翔","jys":"sz"},{"dm":"002201","mc":"正威新材","jys":"sz"},{"dm":"002702","mc":"海欣食品","jys":"sz"},{"dm":"688002","mc":"睿创微纳","jys":"sh"},{"dm":"002434","mc":"万里扬","jys":"sz"},{"dm":"002292","mc":"奥飞娱乐","jys":"sz"},{"dm":"300572","mc":"安车检测","jys":"sz"},{"dm":"601996","mc":"丰林集团","jys":"sh"},{"dm":"601086","mc":"国芳集团","jys":"sh"},{"dm":"000559","mc":"万向钱潮","jys":"sz"},{"dm":"603130","mc":"云中马","jys":"sh"},{"dm":"300220","mc":"ST金运","jys":"sz"},{"dm":"002850","mc":"科达利","jys":"sz"},{"dm":"600367","mc":"红星发展","jys":"sh"},{"dm":"002128","mc":"电投能源","jys":"sz"},{"dm":"603155","mc":"新亚强","jys":"sh"},{"dm":"600252","mc":"中恒集团","jys":"sh"},{"dm":"000927","mc":"中国铁物","jys":"sz"},{"dm":"000507","mc":"珠海港","jys":"sz"},{"dm":"002367","mc":"康力电梯","jys":"sz"},{"dm":"002429","mc":"兆驰股份","jys":"sz"},{"dm":"301195","mc":"北路智控","jys":"sz"},{"dm":"002353","mc":"杰瑞股份","jys":"sz"},{"dm":"002285","mc":"世联行","jys":"sz"},{"dm":"605319","mc":"无锡振华","jys":"sh"},{"dm":"002393","mc":"力生制药","jys":"sz"},{"dm":"002250","mc":"联化科技","jys":"sz"},{"dm":"300044","mc":"赛为智能","jys":"sz"},{"dm":"603955","mc":"大千生态","jys":"sh"},{"dm":"301322","mc":"绿通科技","jys":"sz"},{"dm":"688538","mc":"和辉光电-U","jys":"sh"},{"dm":"601728","mc":"中国电信","jys":"sh"},{"dm":"688211","mc":"中科微至","jys":"sh"},{"dm":"300350","mc":"华鹏飞","jys":"sz"},{"dm":"601860","mc":"紫金银行","jys":"sh"},{"dm":"600196","mc":"复星医药","jys":"sh"},{"dm":"002287","mc":"奇正藏药","jys":"sz"},{"dm":"300894","mc":"火星人","jys":"sz"},{"dm":"002697","mc":"红旗连锁","jys":"sz"},{"dm":"000568","mc":"泸州老窖","jys":"sz"},{"dm":"002085","mc":"万丰奥威","jys":"sz"},{"dm":"601528","mc":"瑞丰银行","jys":"sh"},{"dm":"600497","mc":"驰宏锌锗","jys":"sh"},{"dm":"600337","mc":"美克家居","jys":"sh"},{"dm":"300048","mc":"合康新能","jys":"sz"},{"dm":"600582","mc":"天地科技","jys":"sh"},{"dm":"600488","mc":"津药药业","jys":"sh"},{"dm":"000422","mc":"湖北宜化","jys":"sz"},{"dm":"600621","mc":"华鑫股份","jys":"sh"},{"dm":"605336","mc":"帅丰电器","jys":"sh"},{"dm":"301332","mc":"德尔玛","jys":"sz"},{"dm":"301121","mc":"紫建电子","jys":"sz"},{"dm":"600838","mc":"上海九百","jys":"sh"},{"dm":"600703","mc":"三安光电","jys":"sh"},{"dm":"600370","mc":"三房巷","jys":"sh"},{"dm":"003023","mc":"彩虹集团","jys":"sz"},{"dm":"603056","mc":"德邦股份","jys":"sh"},{"dm":"600897","mc":"厦门空港","jys":"sh"},{"dm":"603659","mc":"璞泰来","jys":"sh"},{"dm":"000553","mc":"安道麦A","jys":"sz"},{"dm":"603132","mc":"金徽股份","jys":"sh"},{"dm":"601963","mc":"重庆银行","jys":"sh"},{"dm":"301377","mc":"鼎泰高科","jys":"sz"},{"dm":"600713","mc":"南京医药","jys":"sh"},{"dm":"301168","mc":"通灵股份","jys":"sz"},{"dm":"600359","mc":"新农开发","jys":"sh"},{"dm":"600576","mc":"祥源文旅","jys":"sh"},{"dm":"002579","mc":"中京电子","jys":"sz"},{"dm":"600517","mc":"国网英大","jys":"sh"},{"dm":"605003","mc":"众望布艺","jys":"sh"},{"dm":"600697","mc":"欧亚集团","jys":"sh"},{"dm":"301227","mc":"森鹰窗业","jys":"sz"},{"dm":"002425","mc":"凯撒文化","jys":"sz"},{"dm":"301046","mc":"能辉科技","jys":"sz"},{"dm":"300080","mc":"易成新能","jys":"sz"},{"dm":"300501","mc":"海顺新材","jys":"sz"},{"dm":"000592","mc":"平潭发展","jys":"sz"},{"dm":"000560","mc":"我爱我家","jys":"sz"},{"dm":"000768","mc":"中航西飞","jys":"sz"},{"dm":"688396","mc":"华润微","jys":"sh"},{"dm":"301001","mc":"凯淳股份","jys":"sz"},{"dm":"001236","mc":"弘业期货","jys":"sz"},{"dm":"300851","mc":"交大思诺","jys":"sz"},{"dm":"001332","mc":"锡装股份","jys":"sz"},{"dm":"000967","mc":"盈峰环境","jys":"sz"},{"dm":"002265","mc":"建设工业","jys":"sz"},{"dm":"603982","mc":"泉峰汽车","jys":"sh"},{"dm":"300662","mc":"科锐国际","jys":"sz"},{"dm":"601615","mc":"明阳智能","jys":"sh"},{"dm":"603115","mc":"海星股份","jys":"sh"},{"dm":"600467","mc":"好当家","jys":"sh"},{"dm":"000519","mc":"中兵红箭","jys":"sz"},{"dm":"600628","mc":"新世界","jys":"sh"},{"dm":"601128","mc":"常熟银行","jys":"sh"},{"dm":"603995","mc":"甬金股份","jys":"sh"},{"dm":"688312","mc":"燕麦科技","jys":"sh"},{"dm":"600905","mc":"三峡能源","jys":"sh"},{"dm":"688208","mc":"道通科技","jys":"sh"},{"dm":"002651","mc":"利君股份","jys":"sz"},{"dm":"002545","mc":"东方铁塔","jys":"sz"},{"dm":"002930","mc":"宏川智慧","jys":"sz"},{"dm":"002965","mc":"祥鑫科技","jys":"sz"},{"dm":"688247","mc":"宣泰医药","jys":"sh"},{"dm":"603077","mc":"和邦生物","jys":"sh"},{"dm":"002468","mc":"申通快递","jys":"sz"},{"dm":"000607","mc":"华媒控股","jys":"sz"},{"dm":"600150","mc":"中国船舶","jys":"sh"},{"dm":"300666","mc":"江丰电子","jys":"sz"},{"dm":"600597","mc":"光明乳业","jys":"sh"},{"dm":"601038","mc":"一拖股份","jys":"sh"},{"dm":"600178","mc":"东安动力","jys":"sh"},{"dm":"603909","mc":"建发合诚","jys":"sh"},{"dm":"603506","mc":"南都物业","jys":"sh"},{"dm":"002319","mc":"乐通股份","jys":"sz"},{"dm":"601908","mc":"京运通","jys":"sh"},{"dm":"603787","mc":"新日股份","jys":"sh"},{"dm":"603678","mc":"火炬电子","jys":"sh"},{"dm":"300257","mc":"开山股份","jys":"sz"},{"dm":"300176","mc":"派生科技","jys":"sz"},{"dm":"002304","mc":"洋河股份","jys":"sz"},{"dm":"002382","mc":"蓝帆医疗","jys":"sz"},{"dm":"688009","mc":"中国通号","jys":"sh"},{"dm":"300435","mc":"中泰股份","jys":"sz"},{"dm":"688118","mc":"普元信息","jys":"sh"},{"dm":"688789","mc":"宏华数科","jys":"sh"},{"dm":"300055","mc":"万邦达","jys":"sz"},{"dm":"002027","mc":"分众传媒","jys":"sz"},{"dm":"300039","mc":"上海凯宝","jys":"sz"},{"dm":"603087","mc":"甘李药业","jys":"sh"},{"dm":"603693","mc":"江苏新能","jys":"sh"},{"dm":"600000","mc":"浦发银行","jys":"sh"},{"dm":"601658","mc":"邮储银行","jys":"sh"},{"dm":"300197","mc":"节能铁汉","jys":"sz"},{"dm":"001313","mc":"粤海饲料","jys":"sz"},{"dm":"600755","mc":"厦门国贸","jys":"sh"},{"dm":"002707","mc":"众信旅游","jys":"sz"},{"dm":"300783","mc":"三只松鼠","jys":"sz"},{"dm":"000159","mc":"国际实业","jys":"sz"},{"dm":"601866","mc":"中远海发","jys":"sh"},{"dm":"000897","mc":"津滨发展","jys":"sz"},{"dm":"301022","mc":"海泰科","jys":"sz"},{"dm":"603217","mc":"元利科技","jys":"sh"},{"dm":"300864","mc":"南大环境","jys":"sz"},{"dm":"688285","mc":"高铁电气","jys":"sh"},{"dm":"603162","mc":"海通发展","jys":"sh"},{"dm":"601139","mc":"深圳燃气","jys":"sh"},{"dm":"300183","mc":"东软载波","jys":"sz"},{"dm":"300390","mc":"天华新能","jys":"sz"},{"dm":"002986","mc":"宇新股份","jys":"sz"},{"dm":"601579","mc":"会稽山","jys":"sh"},{"dm":"002198","mc":"嘉应制药","jys":"sz"},{"dm":"601118","mc":"海南橡胶","jys":"sh"},{"dm":"000006","mc":"深振业A","jys":"sz"},{"dm":"301101","mc":"明月镜片","jys":"sz"},{"dm":"001360","mc":"南矿集团","jys":"sz"},{"dm":"601222","mc":"林洋能源","jys":"sh"},{"dm":"002865","mc":"钧达股份","jys":"sz"},{"dm":"603378","mc":"亚士创能","jys":"sh"},{"dm":"002183","mc":"怡 亚 通","jys":"sz"},{"dm":"605060","mc":"联德股份","jys":"sh"},{"dm":"600726","mc":"华电能源","jys":"sh"},{"dm":"603260","mc":"合盛硅业","jys":"sh"},{"dm":"300139","mc":"晓程科技","jys":"sz"},{"dm":"300773","mc":"拉卡拉","jys":"sz"},{"dm":"601868","mc":"中国能建","jys":"sh"},{"dm":"002887","mc":"绿茵生态","jys":"sz"},{"dm":"300298","mc":"三诺生物","jys":"sz"},{"dm":"002701","mc":"奥瑞金","jys":"sz"},{"dm":"301276","mc":"嘉曼服饰","jys":"sz"},{"dm":"688184","mc":"帕瓦股份","jys":"sh"},{"dm":"000886","mc":"海南高速","jys":"sz"},{"dm":"600079","mc":"人福医药","jys":"sh"},{"dm":"002016","mc":"世荣兆业","jys":"sz"},{"dm":"301152","mc":"天力锂能","jys":"sz"},{"dm":"300148","mc":"天舟文化","jys":"sz"},{"dm":"600004","mc":"白云机场","jys":"sh"},{"dm":"003009","mc":"中天火箭","jys":"sz"},{"dm":"300206","mc":"理邦仪器","jys":"sz"},{"dm":"300086","mc":"康芝药业","jys":"sz"},{"dm":"002223","mc":"鱼跃医疗","jys":"sz"},{"dm":"300450","mc":"先导智能","jys":"sz"},{"dm":"300682","mc":"朗新科技","jys":"sz"},{"dm":"601952","mc":"苏垦农发","jys":"sh"},{"dm":"605567","mc":"春雪食品","jys":"sh"},{"dm":"600717","mc":"天津港","jys":"sh"},{"dm":"002623","mc":"亚玛顿","jys":"sz"},{"dm":"603309","mc":"维力医疗","jys":"sh"},{"dm":"301122","mc":"采纳股份","jys":"sz"},{"dm":"600556","mc":"天下秀","jys":"sh"},{"dm":"002686","mc":"亿利达","jys":"sz"},{"dm":"600992","mc":"贵绳股份","jys":"sh"},{"dm":"301511","mc":"德福科技","jys":"sz"},{"dm":"002466","mc":"天齐锂业","jys":"sz"},{"dm":"002921","mc":"联诚精密","jys":"sz"},{"dm":"600598","mc":"北大荒","jys":"sh"},{"dm":"002691","mc":"冀凯股份","jys":"sz"},{"dm":"600696","mc":"岩石股份","jys":"sh"},{"dm":"301026","mc":"浩通科技","jys":"sz"},{"dm":"002782","mc":"可立克","jys":"sz"},{"dm":"605136","mc":"丽人丽妆","jys":"sh"},{"dm":"002734","mc":"利民股份","jys":"sz"},{"dm":"002570","mc":"贝因美","jys":"sz"},{"dm":"002669","mc":"康达新材","jys":"sz"},{"dm":"002631","mc":"德尔未来","jys":"sz"},{"dm":"601688","mc":"华泰证券","jys":"sh"},{"dm":"601020","mc":"华钰矿业","jys":"sh"},{"dm":"300616","mc":"尚品宅配","jys":"sz"},{"dm":"688669","mc":"聚石化学","jys":"sh"},{"dm":"600419","mc":"天润乳业","jys":"sh"},{"dm":"002873","mc":"新天药业","jys":"sz"},{"dm":"002209","mc":"达 意 隆","jys":"sz"},{"dm":"301333","mc":"诺思格","jys":"sz"},{"dm":"601992","mc":"金隅集团","jys":"sh"},{"dm":"002886","mc":"沃特股份","jys":"sz"},{"dm":"688178","mc":"万德斯","jys":"sh"},{"dm":"300345","mc":"华民股份","jys":"sz"},{"dm":"002898","mc":"赛隆药业","jys":"sz"},{"dm":"688100","mc":"威胜信息","jys":"sh"},{"dm":"000581","mc":"威孚高科","jys":"sz"},{"dm":"300530","mc":"领湃科技","jys":"sz"},{"dm":"301257","mc":"普蕊斯","jys":"sz"},{"dm":"300446","mc":"乐凯新材","jys":"sz"},{"dm":"002563","mc":"森马服饰","jys":"sz"},{"dm":"600774","mc":"汉商集团","jys":"sh"},{"dm":"603990","mc":"麦迪科技","jys":"sh"},{"dm":"601800","mc":"中国交建","jys":"sh"},{"dm":"603223","mc":"恒通股份","jys":"sh"},{"dm":"301118","mc":"恒光股份","jys":"sz"},{"dm":"688067","mc":"爱威科技","jys":"sh"},{"dm":"688233","mc":"神工股份","jys":"sh"},{"dm":"601989","mc":"中国重工","jys":"sh"},{"dm":"600380","mc":"健康元","jys":"sh"},{"dm":"000976","mc":"ST华铁","jys":"sz"},{"dm":"301370","mc":"国科恒泰","jys":"sz"},{"dm":"000029","mc":"深深房A","jys":"sz"},{"dm":"603877","mc":"太平鸟","jys":"sh"},{"dm":"600581","mc":"八一钢铁","jys":"sh"},{"dm":"002422","mc":"科伦药业","jys":"sz"},{"dm":"603200","mc":"上海洗霸","jys":"sh"},{"dm":"600328","mc":"中盐化工","jys":"sh"},{"dm":"301222","mc":"浙江恒威","jys":"sz"},{"dm":"600859","mc":"王府井","jys":"sh"},{"dm":"600038","mc":"中直股份","jys":"sh"},{"dm":"300006","mc":"莱美药业","jys":"sz"},{"dm":"603135","mc":"中重科技","jys":"sh"},{"dm":"002683","mc":"广东宏大","jys":"sz"},{"dm":"300890","mc":"翔丰华","jys":"sz"},{"dm":"002317","mc":"众生药业","jys":"sz"},{"dm":"002531","mc":"天顺风能","jys":"sz"},{"dm":"600527","mc":"江南高纤","jys":"sh"},{"dm":"688646","mc":"逸飞激光","jys":"sh"},{"dm":"300278","mc":"华昌达","jys":"sz"},{"dm":"600812","mc":"华北制药","jys":"sh"},{"dm":"002045","mc":"国光电器","jys":"sz"},{"dm":"600596","mc":"新安股份","jys":"sh"},{"dm":"300013","mc":"新宁物流","jys":"sz"},{"dm":"688613","mc":"奥精医疗","jys":"sh"},{"dm":"600665","mc":"天地源","jys":"sh"},{"dm":"002075","mc":"沙钢股份","jys":"sz"},{"dm":"002714","mc":"牧原股份","jys":"sz"},{"dm":"601137","mc":"博威合金","jys":"sh"},{"dm":"600936","mc":"广西广电","jys":"sh"},{"dm":"600559","mc":"老白干酒","jys":"sh"},{"dm":"600167","mc":"联美控股","jys":"sh"},{"dm":"688552","mc":"航天南湖","jys":"sh"},{"dm":"301219","mc":"腾远钴业","jys":"sz"},{"dm":"000062","mc":"深圳华强","jys":"sz"},{"dm":"688388","mc":"嘉元科技","jys":"sh"},{"dm":"002043","mc":"兔 宝 宝","jys":"sz"},{"dm":"002210","mc":"飞马国际","jys":"sz"},{"dm":"605116","mc":"奥锐特","jys":"sh"},{"dm":"600391","mc":"航发科技","jys":"sh"},{"dm":"601919","mc":"中远海控","jys":"sh"},{"dm":"688266","mc":"泽璟制药-U","jys":"sh"},{"dm":"600871","mc":"石化油服","jys":"sh"},{"dm":"300421","mc":"力星股份","jys":"sz"},{"dm":"301211","mc":"亨迪药业","jys":"sz"},{"dm":"002765","mc":"蓝黛科技","jys":"sz"},{"dm":"600874","mc":"创业环保","jys":"sh"},{"dm":"600340","mc":"华夏幸福","jys":"sh"},{"dm":"603606","mc":"东方电缆","jys":"sh"},{"dm":"688128","mc":"中国电研","jys":"sh"},{"dm":"300335","mc":"迪森股份","jys":"sz"},{"dm":"002663","mc":"普邦股份","jys":"sz"},{"dm":"601166","mc":"兴业银行","jys":"sh"},{"dm":"688690","mc":"纳微科技","jys":"sh"},{"dm":"600409","mc":"三友化工","jys":"sh"},{"dm":"600803","mc":"新奥股份","jys":"sh"},{"dm":"002790","mc":"瑞尔特","jys":"sz"},{"dm":"600742","mc":"一汽富维","jys":"sh"},{"dm":"300778","mc":"新城市","jys":"sz"},{"dm":"601968","mc":"宝钢包装","jys":"sh"},{"dm":"600518","mc":"ST康美","jys":"sh"},{"dm":"600130","mc":"波导股份","jys":"sh"},{"dm":"300328","mc":"宜安科技","jys":"sz"},{"dm":"300301","mc":"*ST长方","jys":"sz"},{"dm":"688268","mc":"华特气体","jys":"sh"},{"dm":"600479","mc":"千金药业","jys":"sh"},{"dm":"000669","mc":"ST金鸿","jys":"sz"},{"dm":"600629","mc":"华建集团","jys":"sh"},{"dm":"000582","mc":"北部湾港","jys":"sz"},{"dm":"688186","mc":"广大特材","jys":"sh"},{"dm":"000869","mc":"张 裕A","jys":"sz"},{"dm":"300697","mc":"电工合金","jys":"sz"},{"dm":"600835","mc":"上海机电","jys":"sh"},{"dm":"300373","mc":"扬杰科技","jys":"sz"},{"dm":"300147","mc":"香雪制药","jys":"sz"},{"dm":"600858","mc":"银座股份","jys":"sh"},{"dm":"600373","mc":"中文传媒","jys":"sh"},{"dm":"600478","mc":"科力远","jys":"sh"},{"dm":"300869","mc":"康泰医学","jys":"sz"},{"dm":"300244","mc":"迪安诊断","jys":"sz"},{"dm":"688136","mc":"科兴制药","jys":"sh"},{"dm":"300770","mc":"新媒股份","jys":"sz"},{"dm":"300100","mc":"双林股份","jys":"sz"},{"dm":"600165","mc":"宁科生物","jys":"sh"},{"dm":"301429","mc":"森泰股份","jys":"sz"},{"dm":"001203","mc":"大中矿业","jys":"sz"},{"dm":"301206","mc":"三元生物","jys":"sz"},{"dm":"603298","mc":"杭叉集团","jys":"sh"},{"dm":"002329","mc":"皇氏集团","jys":"sz"},{"dm":"688026","mc":"洁特生物","jys":"sh"},{"dm":"000048","mc":"京基智农","jys":"sz"},{"dm":"002846","mc":"英联股份","jys":"sz"},{"dm":"600280","mc":"中央商场","jys":"sh"},{"dm":"002269","mc":"美邦服饰","jys":"sz"},{"dm":"300782","mc":"卓胜微","jys":"sz"},{"dm":"300837","mc":"浙矿股份","jys":"sz"},{"dm":"001368","mc":"通达创智","jys":"sz"},{"dm":"002179","mc":"中航光电","jys":"sz"},{"dm":"002550","mc":"千红制药","jys":"sz"},{"dm":"601021","mc":"春秋航空","jys":"sh"},{"dm":"003033","mc":"征和工业","jys":"sz"},{"dm":"688508","mc":"芯朋微","jys":"sh"},{"dm":"002780","mc":"三夫户外","jys":"sz"},{"dm":"002687","mc":"乔治白","jys":"sz"},{"dm":"605009","mc":"豪悦护理","jys":"sh"},{"dm":"002742","mc":"ST三圣","jys":"sz"},{"dm":"002051","mc":"中工国际","jys":"sz"},{"dm":"601611","mc":"中国核建","jys":"sh"},{"dm":"603299","mc":"苏盐井神","jys":"sh"},{"dm":"600306","mc":"*ST商城","jys":"sh"},{"dm":"688317","mc":"之江生物","jys":"sh"},{"dm":"600106","mc":"重庆路桥","jys":"sh"},{"dm":"002039","mc":"黔源电力","jys":"sz"},{"dm":"301296","mc":"新巨丰","jys":"sz"},{"dm":"600021","mc":"上海电力","jys":"sh"},{"dm":"688148","mc":"芳源股份","jys":"sh"},{"dm":"603031","mc":"安孚科技","jys":"sh"},{"dm":"000950","mc":"重药控股","jys":"sz"},{"dm":"001338","mc":"永顺泰","jys":"sz"},{"dm":"600501","mc":"航天晨光","jys":"sh"},{"dm":"002905","mc":"金逸影视","jys":"sz"},{"dm":"301017","mc":"漱玉平民","jys":"sz"},{"dm":"300896","mc":"爱美客","jys":"sz"},{"dm":"605338","mc":"巴比食品","jys":"sh"},{"dm":"603337","mc":"杰克股份","jys":"sh"},{"dm":"000031","mc":"大悦城","jys":"sz"},{"dm":"603256","mc":"宏和科技","jys":"sh"},{"dm":"300549","mc":"优德精密","jys":"sz"},{"dm":"002352","mc":"顺丰控股","jys":"sz"},{"dm":"600967","mc":"内蒙一机","jys":"sh"},{"dm":"688630","mc":"芯碁微装","jys":"sh"},{"dm":"601997","mc":"贵阳银行","jys":"sh"},{"dm":"600827","mc":"百联股份","jys":"sh"},{"dm":"600151","mc":"航天机电","jys":"sh"},{"dm":"300443","mc":"金雷股份","jys":"sz"},{"dm":"601330","mc":"绿色动力","jys":"sh"},{"dm":"600320","mc":"振华重工","jys":"sh"},{"dm":"688368","mc":"晶丰明源","jys":"sh"},{"dm":"600787","mc":"中储股份","jys":"sh"},{"dm":"300861","mc":"美畅股份","jys":"sz"},{"dm":"600973","mc":"宝胜股份","jys":"sh"},{"dm":"301033","mc":"迈普医学","jys":"sz"},{"dm":"000007","mc":"*ST全新","jys":"sz"},{"dm":"300607","mc":"拓斯达","jys":"sz"},{"dm":"000799","mc":"酒鬼酒","jys":"sz"},{"dm":"600744","mc":"华银电力","jys":"sh"},{"dm":"002412","mc":"汉森制药","jys":"sz"},{"dm":"000788","mc":"北大医药","jys":"sz"},{"dm":"000792","mc":"盐湖股份","jys":"sz"},{"dm":"600875","mc":"东方电气","jys":"sh"},{"dm":"003020","mc":"立方制药","jys":"sz"},{"dm":"600603","mc":"广汇物流","jys":"sh"},{"dm":"300775","mc":"三角防务","jys":"sz"},{"dm":"300457","mc":"赢合科技","jys":"sz"},{"dm":"002052","mc":"ST同洲","jys":"sz"},{"dm":"600361","mc":"创新新材","jys":"sh"},{"dm":"002244","mc":"滨江集团","jys":"sz"},{"dm":"002732","mc":"燕塘乳业","jys":"sz"},{"dm":"300575","mc":"中旗股份","jys":"sz"},{"dm":"001309","mc":"德明利","jys":"sz"},{"dm":"603032","mc":"德新科技","jys":"sh"},{"dm":"301515","mc":"港通医疗","jys":"sz"},{"dm":"002968","mc":"新大正","jys":"sz"},{"dm":"002010","mc":"传化智联","jys":"sz"},{"dm":"601231","mc":"环旭电子","jys":"sh"},{"dm":"600104","mc":"上汽集团","jys":"sh"},{"dm":"603316","mc":"诚邦股份","jys":"sh"},{"dm":"601888","mc":"中国中免","jys":"sh"},{"dm":"688176","mc":"亚虹医药-U","jys":"sh"},{"dm":"600496","mc":"精工钢构","jys":"sh"},{"dm":"300815","mc":"玉禾田","jys":"sz"},{"dm":"688625","mc":"呈和科技","jys":"sh"},{"dm":"600097","mc":"开创国际","jys":"sh"},{"dm":"301047","mc":"义翘神州","jys":"sz"},{"dm":"300239","mc":"东宝生物","jys":"sz"},{"dm":"002149","mc":"西部材料","jys":"sz"},{"dm":"600010","mc":"包钢股份","jys":"sh"},{"dm":"600027","mc":"华电国际","jys":"sh"},{"dm":"600685","mc":"中船防务","jys":"sh"},{"dm":"688084","mc":"晶品特装","jys":"sh"},{"dm":"603296","mc":"华勤技术","jys":"sh"},{"dm":"600655","mc":"豫园股份","jys":"sh"},{"dm":"002948","mc":"青岛银行","jys":"sz"},{"dm":"000656","mc":"金科股份","jys":"sz"},{"dm":"002167","mc":"东方锆业","jys":"sz"},{"dm":"300848","mc":"美瑞新材","jys":"sz"},{"dm":"300981","mc":"中红医疗","jys":"sz"},{"dm":"600132","mc":"重庆啤酒","jys":"sh"},{"dm":"603665","mc":"康隆达","jys":"sh"},{"dm":"688179","mc":"阿拉丁","jys":"sh"},{"dm":"605389","mc":"长龄液压","jys":"sh"},{"dm":"603321","mc":"梅轮电梯","jys":"sh"},{"dm":"300428","mc":"立中集团","jys":"sz"},{"dm":"603023","mc":"威帝股份","jys":"sh"},{"dm":"000690","mc":"宝新能源","jys":"sz"},{"dm":"002187","mc":"广百股份","jys":"sz"},{"dm":"600639","mc":"浦东金桥","jys":"sh"},{"dm":"600565","mc":"迪马股份","jys":"sh"},{"dm":"601965","mc":"中国汽研","jys":"sh"},{"dm":"603386","mc":"骏亚科技","jys":"sh"},{"dm":"600358","mc":"国旅联合","jys":"sh"},{"dm":"300595","mc":"欧普康视","jys":"sz"},{"dm":"688196","mc":"卓越新能","jys":"sh"},{"dm":"600616","mc":"金枫酒业","jys":"sh"},{"dm":"301057","mc":"汇隆新材","jys":"sz"},{"dm":"000819","mc":"岳阳兴长","jys":"sz"},{"dm":"300214","mc":"日科化学","jys":"sz"},{"dm":"601016","mc":"节能风电","jys":"sh"},{"dm":"600216","mc":"浙江医药","jys":"sh"},{"dm":"600293","mc":"三峡新材","jys":"sh"},{"dm":"603883","mc":"老百姓","jys":"sh"},{"dm":"603708","mc":"家家悦","jys":"sh"},{"dm":"601689","mc":"拓普集团","jys":"sh"},{"dm":"600300","mc":"维维股份","jys":"sh"},{"dm":"001979","mc":"招商蛇口","jys":"sz"},{"dm":"301111","mc":"粤万年青","jys":"sz"},{"dm":"301050","mc":"雷电微力","jys":"sz"},{"dm":"002966","mc":"苏州银行","jys":"sz"},{"dm":"600063","mc":"皖维高新","jys":"sh"},{"dm":"600117","mc":"*ST西钢","jys":"sh"},{"dm":"002020","mc":"京新药业","jys":"sz"},{"dm":"688072","mc":"拓荆科技","jys":"sh"},{"dm":"002028","mc":"思源电气","jys":"sz"},{"dm":"300619","mc":"金银河","jys":"sz"},{"dm":"600557","mc":"康缘药业","jys":"sh"},{"dm":"601179","mc":"中国西电","jys":"sh"},{"dm":"600166","mc":"福田汽车","jys":"sh"},{"dm":"000928","mc":"中钢国际","jys":"sz"},{"dm":"301071","mc":"力量钻石","jys":"sz"},{"dm":"603385","mc":"惠达卫浴","jys":"sh"},{"dm":"605100","mc":"华丰股份","jys":"sh"},{"dm":"002204","mc":"大连重工","jys":"sz"},{"dm":"000966","mc":"长源电力","jys":"sz"},{"dm":"300453","mc":"三鑫医疗","jys":"sz"},{"dm":"300642","mc":"透景生命","jys":"sz"},{"dm":"300776","mc":"帝尔激光","jys":"sz"},{"dm":"600110","mc":"诺德股份","jys":"sh"},{"dm":"603099","mc":"长白山","jys":"sh"},{"dm":"000767","mc":"晋控电力","jys":"sz"},{"dm":"300146","mc":"汤臣倍健","jys":"sz"},{"dm":"688518","mc":"联赢激光","jys":"sh"},{"dm":"000426","mc":"兴业银锡","jys":"sz"},{"dm":"002612","mc":"朗姿股份","jys":"sz"},{"dm":"301200","mc":"大族数控","jys":"sz"},{"dm":"301277","mc":"新天地","jys":"sz"},{"dm":"688550","mc":"瑞联新材","jys":"sh"},{"dm":"600190","mc":"锦州港","jys":"sh"},{"dm":"002091","mc":"江苏国泰","jys":"sz"},{"dm":"603661","mc":"恒林股份","jys":"sh"},{"dm":"301221","mc":"光庭信息","jys":"sz"},{"dm":"688278","mc":"特宝生物","jys":"sh"},{"dm":"002816","mc":"*ST和科","jys":"sz"},{"dm":"603515","mc":"欧普照明","jys":"sh"},{"dm":"000630","mc":"铜陵有色","jys":"sz"},{"dm":"300378","mc":"鼎捷软件","jys":"sz"},{"dm":"002372","mc":"伟星新材","jys":"sz"},{"dm":"002584","mc":"西陇科学","jys":"sz"},{"dm":"000961","mc":"中南建设","jys":"sz"},{"dm":"600955","mc":"维远股份","jys":"sh"},{"dm":"605398","mc":"新炬网络","jys":"sh"},{"dm":"601298","mc":"青岛港","jys":"sh"},{"dm":"002733","mc":"雄韬股份","jys":"sz"},{"dm":"002501","mc":"利源股份","jys":"sz"},{"dm":"001330","mc":"博纳影业","jys":"sz"},{"dm":"605077","mc":"华康股份","jys":"sh"},{"dm":"300149","mc":"睿智医药","jys":"sz"},{"dm":"600578","mc":"京能电力","jys":"sh"},{"dm":"300676","mc":"华大基因","jys":"sz"},{"dm":"000789","mc":"万年青","jys":"sz"},{"dm":"601678","mc":"滨化股份","jys":"sh"},{"dm":"300797","mc":"钢研纳克","jys":"sz"},{"dm":"603300","mc":"华铁应急","jys":"sh"},{"dm":"603868","mc":"飞科电器","jys":"sh"},{"dm":"002407","mc":"多氟多","jys":"sz"},{"dm":"603519","mc":"立霸股份","jys":"sh"},{"dm":"300729","mc":"乐歌股份","jys":"sz"},{"dm":"300669","mc":"沪宁股份","jys":"sz"},{"dm":"000720","mc":"新能泰山","jys":"sz"},{"dm":"688739","mc":"成大生物","jys":"sh"},{"dm":"301073","mc":"君亭酒店","jys":"sz"},{"dm":"002003","mc":"伟星股份","jys":"sz"},{"dm":"601600","mc":"中国铝业","jys":"sh"},{"dm":"002595","mc":"豪迈科技","jys":"sz"},{"dm":"002950","mc":"奥美医疗","jys":"sz"},{"dm":"605090","mc":"九丰能源","jys":"sh"},{"dm":"600594","mc":"益佰制药","jys":"sh"},{"dm":"600389","mc":"江山股份","jys":"sh"},{"dm":"688363","mc":"华熙生物","jys":"sh"},{"dm":"002423","mc":"中粮资本","jys":"sz"},{"dm":"300821","mc":"东岳硅材","jys":"sz"},{"dm":"600316","mc":"洪都航空","jys":"sh"},{"dm":"000016","mc":"深康佳A","jys":"sz"},{"dm":"600009","mc":"上海机场","jys":"sh"},{"dm":"300636","mc":"同和药业","jys":"sz"},{"dm":"600031","mc":"三一重工","jys":"sh"},{"dm":"603533","mc":"掌阅科技","jys":"sh"},{"dm":"002102","mc":"冠福股份","jys":"sz"},{"dm":"601226","mc":"华电重工","jys":"sh"},{"dm":"301095","mc":"广立微","jys":"sz"},{"dm":"003031","mc":"中瓷电子","jys":"sz"},{"dm":"600248","mc":"陕建股份","jys":"sh"},{"dm":"603978","mc":"深圳新星","jys":"sh"},{"dm":"000425","mc":"徐工机械","jys":"sz"},{"dm":"300347","mc":"泰格医药","jys":"sz"},{"dm":"688722","mc":"同益中","jys":"sh"},{"dm":"603613","mc":"国联股份","jys":"sh"},{"dm":"300463","mc":"迈克生物","jys":"sz"},{"dm":"000401","mc":"冀东水泥","jys":"sz"},{"dm":"002524","mc":"光正眼科","jys":"sz"},{"dm":"002355","mc":"兴民智通","jys":"sz"},{"dm":"600085","mc":"同仁堂","jys":"sh"},{"dm":"002829","mc":"星网宇达","jys":"sz"},{"dm":"603878","mc":"武进不锈","jys":"sh"},{"dm":"301258","mc":"富士莱","jys":"sz"},{"dm":"002551","mc":"尚荣医疗","jys":"sz"},{"dm":"301103","mc":"何氏眼科","jys":"sz"},{"dm":"300568","mc":"星源材质","jys":"sz"},{"dm":"603199","mc":"九华旅游","jys":"sh"},{"dm":"002626","mc":"金达威","jys":"sz"},{"dm":"300842","mc":"帝科股份","jys":"sz"},{"dm":"600269","mc":"赣粤高速","jys":"sh"},{"dm":"300652","mc":"雷迪克","jys":"sz"},{"dm":"002767","mc":"先锋电子","jys":"sz"},{"dm":"603719","mc":"良品铺子","jys":"sh"},{"dm":"603330","mc":"天洋新材","jys":"sh"},{"dm":"600475","mc":"华光环能","jys":"sh"},{"dm":"000598","mc":"兴蓉环境","jys":"sz"},{"dm":"600008","mc":"首创环保","jys":"sh"},{"dm":"600919","mc":"江苏银行","jys":"sh"},{"dm":"000930","mc":"中粮科技","jys":"sz"},{"dm":"002080","mc":"中材科技","jys":"sz"},{"dm":"688132","mc":"邦彦技术","jys":"sh"},{"dm":"002081","mc":"金 螳 螂","jys":"sz"},{"dm":"603237","mc":"五芳斋","jys":"sh"},{"dm":"000885","mc":"城发环境","jys":"sz"},{"dm":"002271","mc":"东方雨虹","jys":"sz"},{"dm":"605599","mc":"菜百股份","jys":"sh"},{"dm":"600405","mc":"动力源","jys":"sh"},{"dm":"600187","mc":"国中水务","jys":"sh"},{"dm":"002716","mc":"金贵银业","jys":"sz"},{"dm":"688548","mc":"广钢气体","jys":"sh"},{"dm":"002779","mc":"中坚科技","jys":"sz"},{"dm":"600785","mc":"新华百货","jys":"sh"},{"dm":"002419","mc":"天虹股份","jys":"sz"},{"dm":"603889","mc":"新澳股份","jys":"sh"},{"dm":"002033","mc":"丽江股份","jys":"sz"},{"dm":"000597","mc":"东北制药","jys":"sz"},{"dm":"600201","mc":"生物股份","jys":"sh"},{"dm":"301149","mc":"隆华新材","jys":"sz"},{"dm":"600808","mc":"马钢股份","jys":"sh"},{"dm":"300702","mc":"天宇股份","jys":"sz"},{"dm":"300003","mc":"乐普医疗","jys":"sz"},{"dm":"000089","mc":"深圳机场","jys":"sz"},{"dm":"600765","mc":"中航重机","jys":"sh"},{"dm":"002275","mc":"桂林三金","jys":"sz"},{"dm":"002084","mc":"海鸥住工","jys":"sz"},{"dm":"600138","mc":"中青旅","jys":"sh"},{"dm":"688399","mc":"硕世生物","jys":"sh"},{"dm":"600866","mc":"星湖科技","jys":"sh"},{"dm":"000778","mc":"新兴铸管","jys":"sz"},{"dm":"603993","mc":"洛阳钼业","jys":"sh"},{"dm":"600351","mc":"亚宝药业","jys":"sh"},{"dm":"603026","mc":"胜华新材","jys":"sh"},{"dm":"603822","mc":"嘉澳环保","jys":"sh"},{"dm":"600295","mc":"鄂尔多斯","jys":"sh"},{"dm":"300558","mc":"贝达药业","jys":"sz"},{"dm":"603165","mc":"荣晟环保","jys":"sh"},{"dm":"002896","mc":"中大力德","jys":"sz"},{"dm":"300059","mc":"东方财富","jys":"sz"},{"dm":"600226","mc":"瀚叶股份","jys":"sh"},{"dm":"002526","mc":"山东矿机","jys":"sz"},{"dm":"002366","mc":"融发核电","jys":"sz"},{"dm":"603808","mc":"歌力思","jys":"sh"},{"dm":"601669","mc":"中国电建","jys":"sh"},{"dm":"002225","mc":"濮耐股份","jys":"sz"},{"dm":"002791","mc":"坚朗五金","jys":"sz"},{"dm":"300339","mc":"润和软件","jys":"sz"},{"dm":"300103","mc":"达刚控股","jys":"sz"},{"dm":"600702","mc":"舍得酒业","jys":"sh"},{"dm":"603611","mc":"诺力股份","jys":"sh"},{"dm":"600759","mc":"*ST洲际","jys":"sh"},{"dm":"002654","mc":"万润科技","jys":"sz"},{"dm":"002326","mc":"永太科技","jys":"sz"},{"dm":"000822","mc":"山东海化","jys":"sz"},{"dm":"002694","mc":"顾地科技","jys":"sz"},{"dm":"600662","mc":"外服控股","jys":"sh"},{"dm":"605123","mc":"派克新材","jys":"sh"},{"dm":"000825","mc":"太钢不锈","jys":"sz"},{"dm":"605167","mc":"利柏特","jys":"sh"},{"dm":"603778","mc":"乾景园林","jys":"sh"},{"dm":"601991","mc":"大唐发电","jys":"sh"},{"dm":"603179","mc":"新泉股份","jys":"sh"},{"dm":"002345","mc":"潮宏基","jys":"sz"},{"dm":"600396","mc":"*ST金山","jys":"sh"},{"dm":"002739","mc":"万达电影","jys":"sz"},{"dm":"300534","mc":"陇神戎发","jys":"sz"},{"dm":"300715","mc":"凯伦股份","jys":"sz"},{"dm":"600322","mc":"津投城开","jys":"sh"},{"dm":"300433","mc":"蓝思科技","jys":"sz"},{"dm":"301005","mc":"超捷股份","jys":"sz"},{"dm":"002293","mc":"罗莱生活","jys":"sz"},{"dm":"603599","mc":"广信股份","jys":"sh"},{"dm":"600509","mc":"天富能源","jys":"sh"},{"dm":"300158","mc":"振东制药","jys":"sz"},{"dm":"300918","mc":"南山智尚","jys":"sz"},{"dm":"000729","mc":"燕京啤酒","jys":"sz"},{"dm":"000917","mc":"电广传媒","jys":"sz"},{"dm":"002900","mc":"哈三联","jys":"sz"},{"dm":"301456","mc":"盘古智能","jys":"sz"},{"dm":"601566","mc":"九牧王","jys":"sh"},{"dm":"002594","mc":"比亚迪","jys":"sz"},{"dm":"600376","mc":"首开股份","jys":"sh"},{"dm":"600690","mc":"海尔智家","jys":"sh"},{"dm":"603116","mc":"红蜻蜓","jys":"sh"},{"dm":"603209","mc":"兴通股份","jys":"sh"},{"dm":"600748","mc":"上实发展","jys":"sh"},{"dm":"301090","mc":"华润材料","jys":"sz"},{"dm":"301065","mc":"本立科技","jys":"sz"},{"dm":"600500","mc":"中化国际","jys":"sh"},{"dm":"603279","mc":"景津装备","jys":"sh"},{"dm":"000027","mc":"深圳能源","jys":"sz"},{"dm":"002053","mc":"云南能投","jys":"sz"},{"dm":"603301","mc":"振德医疗","jys":"sh"},{"dm":"002973","mc":"侨银股份","jys":"sz"},{"dm":"603799","mc":"华友钴业","jys":"sh"},{"dm":"600521","mc":"华海药业","jys":"sh"},{"dm":"002420","mc":"毅昌科技","jys":"sz"},{"dm":"000931","mc":"中 关 村","jys":"sz"},{"dm":"000546","mc":"金圆股份","jys":"sz"},{"dm":"300294","mc":"博雅生物","jys":"sz"},{"dm":"601778","mc":"晶科科技","jys":"sh"},{"dm":"000959","mc":"首钢股份","jys":"sz"},{"dm":"002535","mc":"林州重机","jys":"sz"},{"dm":"603095","mc":"越剑智能","jys":"sh"},{"dm":"603989","mc":"艾华集团","jys":"sh"},{"dm":"002967","mc":"广电计量","jys":"sz"},{"dm":"300405","mc":"科隆股份","jys":"sz"},{"dm":"000589","mc":"贵州轮胎","jys":"sz"},{"dm":"000410","mc":"沈阳机床","jys":"sz"},{"dm":"000915","mc":"华特达因","jys":"sz"},{"dm":"688087","mc":"英科再生","jys":"sh"},{"dm":"603833","mc":"欧派家居","jys":"sh"},{"dm":"002289","mc":"ST宇顺","jys":"sz"},{"dm":"603128","mc":"华贸物流","jys":"sh"},{"dm":"600502","mc":"安徽建工","jys":"sh"},{"dm":"603811","mc":"诚意药业","jys":"sh"},{"dm":"603235","mc":"天新药业","jys":"sh"},{"dm":"301116","mc":"益客食品","jys":"sz"},{"dm":"002150","mc":"通润装备","jys":"sz"},{"dm":"600029","mc":"南方航空","jys":"sh"},{"dm":"002821","mc":"凯莱英","jys":"sz"},{"dm":"002035","mc":"华帝股份","jys":"sz"},{"dm":"605299","mc":"舒华体育","jys":"sh"},{"dm":"603697","mc":"有友食品","jys":"sh"},{"dm":"002700","mc":"ST浩源","jys":"sz"},{"dm":"000596","mc":"古井贡酒","jys":"sz"},{"dm":"605080","mc":"浙江自然","jys":"sh"},{"dm":"000534","mc":"万泽股份","jys":"sz"},{"dm":"002413","mc":"雷科防务","jys":"sz"},{"dm":"002868","mc":"绿康生化","jys":"sz"},{"dm":"600926","mc":"杭州银行","jys":"sh"},{"dm":"002424","mc":"贵州百灵","jys":"sz"},{"dm":"600429","mc":"三元股份","jys":"sh"},{"dm":"600332","mc":"白云山","jys":"sh"},{"dm":"002245","mc":"蔚蓝锂芯","jys":"sz"},{"dm":"688393","mc":"安必平","jys":"sh"},{"dm":"000001","mc":"平安银行","jys":"sz"},{"dm":"603367","mc":"辰欣药业","jys":"sh"},{"dm":"600298","mc":"安琪酵母","jys":"sh"},{"dm":"600863","mc":"内蒙华电","jys":"sh"},{"dm":"002309","mc":"ST中利","jys":"sz"},{"dm":"600020","mc":"中原高速","jys":"sh"},{"dm":"002311","mc":"海大集团","jys":"sz"},{"dm":"688512","mc":"慧智微-U","jys":"sh"},{"dm":"000919","mc":"金陵药业","jys":"sz"},{"dm":"300725","mc":"药石科技","jys":"sz"},{"dm":"300622","mc":"博士眼镜","jys":"sz"},{"dm":"000623","mc":"吉林敖东","jys":"sz"},{"dm":"000875","mc":"吉电股份","jys":"sz"},{"dm":"000651","mc":"格力电器","jys":"sz"},{"dm":"600986","mc":"浙文互联","jys":"sh"},{"dm":"601117","mc":"中国化学","jys":"sh"},{"dm":"601512","mc":"中新集团","jys":"sh"},{"dm":"688533","mc":"上声电子","jys":"sh"},{"dm":"002676","mc":"顺威股份","jys":"sz"},{"dm":"000738","mc":"航发控制","jys":"sz"},{"dm":"600642","mc":"申能股份","jys":"sh"},{"dm":"603517","mc":"绝味食品","jys":"sh"},{"dm":"301008","mc":"宏昌科技","jys":"sz"},{"dm":"002131","mc":"利欧股份","jys":"sz"},{"dm":"000890","mc":"法尔胜","jys":"sz"},{"dm":"000591","mc":"太阳能","jys":"sz"},{"dm":"688033","mc":"天宜上佳","jys":"sh"},{"dm":"601717","mc":"郑煤机","jys":"sh"},{"dm":"600825","mc":"新华传媒","jys":"sh"},{"dm":"000055","mc":"方大集团","jys":"sz"},{"dm":"000952","mc":"广济药业","jys":"sz"},{"dm":"300267","mc":"尔康制药","jys":"sz"},{"dm":"688503","mc":"聚和材料","jys":"sh"},{"dm":"600869","mc":"远东股份","jys":"sh"},{"dm":"301301","mc":"川宁生物","jys":"sz"},{"dm":"600861","mc":"北京人力","jys":"sh"},{"dm":"300031","mc":"宝通科技","jys":"sz"},{"dm":"001258","mc":"立新能源","jys":"sz"},{"dm":"688410","mc":"山外山","jys":"sh"},{"dm":"002435","mc":"长江健康","jys":"sz"},{"dm":"601003","mc":"柳钢股份","jys":"sh"},{"dm":"002735","mc":"王子新材","jys":"sz"},{"dm":"000899","mc":"赣能股份","jys":"sz"},{"dm":"000910","mc":"大亚圣象","jys":"sz"},{"dm":"600694","mc":"大商股份","jys":"sh"},{"dm":"600231","mc":"凌钢股份","jys":"sh"},{"dm":"002445","mc":"中南文化","jys":"sz"},{"dm":"300978","mc":"东箭科技","jys":"sz"},{"dm":"002510","mc":"天汽模","jys":"sz"},{"dm":"603707","mc":"健友股份","jys":"sh"},{"dm":"600054","mc":"黄山旅游","jys":"sh"},{"dm":"603809","mc":"豪能股份","jys":"sh"},{"dm":"600416","mc":"湘电股份","jys":"sh"},{"dm":"002907","mc":"华森制药","jys":"sz"},{"dm":"603076","mc":"乐惠国际","jys":"sh"},{"dm":"301123","mc":"奕东电子","jys":"sz"},{"dm":"002258","mc":"利尔化学","jys":"sz"},{"dm":"688289","mc":"圣湘生物","jys":"sh"},{"dm":"301280","mc":"珠城科技","jys":"sz"},{"dm":"600595","mc":"中孚实业","jys":"sh"},{"dm":"603518","mc":"锦泓集团","jys":"sh"},{"dm":"002672","mc":"东江环保","jys":"sz"},{"dm":"603198","mc":"迎驾贡酒","jys":"sh"},{"dm":"002805","mc":"丰元股份","jys":"sz"},{"dm":"002483","mc":"润邦股份","jys":"sz"},{"dm":"002959","mc":"小熊电器","jys":"sz"},{"dm":"603979","mc":"金诚信","jys":"sh"},{"dm":"605507","mc":"国邦医药","jys":"sh"},{"dm":"603477","mc":"巨星农牧","jys":"sh"},{"dm":"603030","mc":"*ST全筑","jys":"sh"},{"dm":"603159","mc":"上海亚虹","jys":"sh"},{"dm":"002294","mc":"信立泰","jys":"sz"},{"dm":"600862","mc":"中航高科","jys":"sh"},{"dm":"000506","mc":"中润资源","jys":"sz"},{"dm":"601618","mc":"中国中冶","jys":"sh"},{"dm":"300073","mc":"当升科技","jys":"sz"},{"dm":"000709","mc":"河钢股份","jys":"sz"},{"dm":"000538","mc":"云南白药","jys":"sz"},{"dm":"688633","mc":"星球石墨","jys":"sh"},{"dm":"603308","mc":"应流股份","jys":"sh"},{"dm":"300930","mc":"屹通新材","jys":"sz"},{"dm":"002305","mc":"南国置业","jys":"sz"},{"dm":"300760","mc":"迈瑞医疗","jys":"sz"},{"dm":"300360","mc":"炬华科技","jys":"sz"},{"dm":"603551","mc":"奥普家居","jys":"sh"},{"dm":"002511","mc":"中顺洁柔","jys":"sz"},{"dm":"688551","mc":"科威尔","jys":"sh"},{"dm":"001207","mc":"联科科技","jys":"sz"},{"dm":"605081","mc":"太和水","jys":"sh"},{"dm":"603876","mc":"鼎胜新材","jys":"sh"},{"dm":"603777","mc":"来伊份","jys":"sh"},{"dm":"002371","mc":"北方华创","jys":"sz"},{"dm":"300712","mc":"永福股份","jys":"sz"},{"dm":"300467","mc":"迅游科技","jys":"sz"},{"dm":"600612","mc":"老凤祥","jys":"sh"},{"dm":"300401","mc":"花园生物","jys":"sz"},{"dm":"000524","mc":"岭南控股","jys":"sz"},{"dm":"600888","mc":"新疆众和","jys":"sh"},{"dm":"603897","mc":"长城科技","jys":"sh"},{"dm":"600720","mc":"祁连山","jys":"sh"},{"dm":"601187","mc":"厦门银行","jys":"sh"},{"dm":"000609","mc":"中迪投资","jys":"sz"},{"dm":"688049","mc":"炬芯科技","jys":"sh"},{"dm":"600600","mc":"青岛啤酒","jys":"sh"},{"dm":"603722","mc":"阿科力","jys":"sh"},{"dm":"000627","mc":"天茂集团","jys":"sz"},{"dm":"300653","mc":"正海生物","jys":"sz"},{"dm":"301308","mc":"江波龙","jys":"sz"},{"dm":"300755","mc":"华致酒行","jys":"sz"},{"dm":"600436","mc":"片仔癀","jys":"sh"},{"dm":"601686","mc":"友发集团","jys":"sh"},{"dm":"603836","mc":"海程邦达","jys":"sh"},{"dm":"002838","mc":"道恩股份","jys":"sz"},{"dm":"688336","mc":"三生国健","jys":"sh"},{"dm":"002690","mc":"美亚光电","jys":"sz"},{"dm":"603000","mc":"人民网","jys":"sh"},{"dm":"603718","mc":"海利生物","jys":"sh"},{"dm":"600585","mc":"海螺水泥","jys":"sh"},{"dm":"300240","mc":"飞力达","jys":"sz"},{"dm":"688126","mc":"沪硅产业","jys":"sh"},{"dm":"002356","mc":"赫美集团","jys":"sz"},{"dm":"001223","mc":"欧克科技","jys":"sz"},{"dm":"601065","mc":"江盐集团","jys":"sh"},{"dm":"300596","mc":"利安隆","jys":"sz"},{"dm":"688516","mc":"奥特维","jys":"sh"},{"dm":"002126","mc":"银轮股份","jys":"sz"},{"dm":"603267","mc":"鸿远电子","jys":"sh"},{"dm":"000028","mc":"国药一致","jys":"sz"},{"dm":"002192","mc":"融捷股份","jys":"sz"},{"dm":"301171","mc":"易点天下","jys":"sz"},{"dm":"688314","mc":"康拓医疗","jys":"sh"},{"dm":"600428","mc":"中远海特","jys":"sh"},{"dm":"301060","mc":"兰卫医学","jys":"sz"},{"dm":"603578","mc":"三星新材","jys":"sh"},{"dm":"601933","mc":"永辉超市","jys":"sh"},{"dm":"300276","mc":"三丰智能","jys":"sz"},{"dm":"600255","mc":"鑫科材料","jys":"sh"},{"dm":"688257","mc":"新锐股份","jys":"sh"},{"dm":"300286","mc":"安科瑞","jys":"sz"},{"dm":"000862","mc":"银星能源","jys":"sz"},{"dm":"601155","mc":"新城控股","jys":"sh"},{"dm":"688611","mc":"杭州柯林","jys":"sh"},{"dm":"002941","mc":"新疆交建","jys":"sz"},{"dm":"002472","mc":"双环传动","jys":"sz"},{"dm":"603315","mc":"福鞍股份","jys":"sh"},{"dm":"000861","mc":"海印股份","jys":"sz"},{"dm":"603035","mc":"常熟汽饰","jys":"sh"},{"dm":"600613","mc":"神奇制药","jys":"sh"},{"dm":"002582","mc":"好想你","jys":"sz"},{"dm":"000537","mc":"广宇发展","jys":"sz"},{"dm":"600779","mc":"水井坊","jys":"sh"},{"dm":"600528","mc":"中铁工业","jys":"sh"},{"dm":"300381","mc":"溢多利","jys":"sz"},{"dm":"002389","mc":"航天彩虹","jys":"sz"},{"dm":"002436","mc":"兴森科技","jys":"sz"},{"dm":"603588","mc":"高能环境","jys":"sh"},{"dm":"301102","mc":"兆讯传媒","jys":"sz"},{"dm":"600970","mc":"中材国际","jys":"sh"},{"dm":"600968","mc":"海油发展","jys":"sh"},{"dm":"000710","mc":"贝瑞基因","jys":"sz"},{"dm":"603638","mc":"艾迪精密","jys":"sh"},{"dm":"603589","mc":"口子窖","jys":"sh"},{"dm":"300668","mc":"杰恩设计","jys":"sz"},{"dm":"601111","mc":"中国国航","jys":"sh"},{"dm":"301393","mc":"昊帆生物","jys":"sz"},{"dm":"603567","mc":"珍宝岛","jys":"sh"},{"dm":"301520","mc":"万邦医药","jys":"sz"},{"dm":"601865","mc":"福莱特","jys":"sh"},{"dm":"300973","mc":"立高食品","jys":"sz"},{"dm":"300235","mc":"方直科技","jys":"sz"},{"dm":"301106","mc":"骏成科技","jys":"sz"},{"dm":"688379","mc":"华光新材","jys":"sh"},{"dm":"600153","mc":"建发股份","jys":"sh"},{"dm":"600297","mc":"广汇汽车","jys":"sh"},{"dm":"002146","mc":"荣盛发展","jys":"sz"},{"dm":"301024","mc":"霍普股份","jys":"sz"},{"dm":"002390","mc":"信邦制药","jys":"sz"},{"dm":"688222","mc":"成都先导","jys":"sh"},{"dm":"603919","mc":"金徽酒","jys":"sh"},{"dm":"600507","mc":"方大特钢","jys":"sh"},{"dm":"001896","mc":"豫能控股","jys":"sz"},{"dm":"600821","mc":"金开新能","jys":"sh"},{"dm":"600826","mc":"兰生股份","jys":"sh"},{"dm":"603915","mc":"国茂股份","jys":"sh"},{"dm":"003002","mc":"壶化股份","jys":"sz"},{"dm":"600745","mc":"闻泰科技","jys":"sh"},{"dm":"300204","mc":"舒泰神","jys":"sz"},{"dm":"688553","mc":"汇宇制药-W","jys":"sh"},{"dm":"300012","mc":"华测检测","jys":"sz"},{"dm":"002074","mc":"国轩高科","jys":"sz"},{"dm":"301371","mc":"敷尔佳","jys":"sz"},{"dm":"000717","mc":"中南股份","jys":"sz"},{"dm":"300194","mc":"福安药业","jys":"sz"},{"dm":"002384","mc":"东山精密","jys":"sz"},{"dm":"688017","mc":"绿的谐波","jys":"sh"},{"dm":"001301","mc":"尚太科技","jys":"sz"},{"dm":"603129","mc":"春风动力","jys":"sh"},{"dm":"002773","mc":"康弘药业","jys":"sz"},{"dm":"301367","mc":"怡和嘉业","jys":"sz"},{"dm":"688507","mc":"索辰科技","jys":"sh"},{"dm":"002111","mc":"威海广泰","jys":"sz"},{"dm":"601882","mc":"海天精工","jys":"sh"},{"dm":"688326","mc":"经纬恒润-W","jys":"sh"},{"dm":"300439","mc":"美康生物","jys":"sz"},{"dm":"688065","mc":"凯赛生物","jys":"sh"},{"dm":"603838","mc":"四通股份","jys":"sh"},{"dm":"301207","mc":"华兰疫苗","jys":"sz"},{"dm":"688303","mc":"大全能源","jys":"sh"},{"dm":"300726","mc":"宏达电子","jys":"sz"},{"dm":"603232","mc":"格尔软件","jys":"sh"},{"dm":"002481","mc":"双塔食品","jys":"sz"},{"dm":"603828","mc":"柯利达","jys":"sh"},{"dm":"300198","mc":"纳川股份","jys":"sz"},{"dm":"002505","mc":"鹏都农牧","jys":"sz"},{"dm":"688189","mc":"南新制药","jys":"sh"},{"dm":"603187","mc":"海容冷链","jys":"sh"},{"dm":"301012","mc":"扬电科技","jys":"sz"},{"dm":"300406","mc":"九强生物","jys":"sz"},{"dm":"300341","mc":"麦克奥迪","jys":"sz"},{"dm":"600309","mc":"万华化学","jys":"sh"},{"dm":"000590","mc":"启迪药业","jys":"sz"},{"dm":"002460","mc":"赣锋锂业","jys":"sz"},{"dm":"601577","mc":"长沙银行","jys":"sh"},{"dm":"600036","mc":"招商银行","jys":"sh"},{"dm":"603612","mc":"索通发展","jys":"sh"},{"dm":"603096","mc":"新经典","jys":"sh"},{"dm":"688157","mc":"松井股份","jys":"sh"},{"dm":"688053","mc":"思科瑞","jys":"sh"},{"dm":"603676","mc":"卫信康","jys":"sh"},{"dm":"600070","mc":"ST富润","jys":"sh"},{"dm":"002433","mc":"*ST太安","jys":"sz"},{"dm":"603277","mc":"银都股份","jys":"sh"},{"dm":"603190","mc":"亚通精工","jys":"sh"},{"dm":"603737","mc":"三棵树","jys":"sh"},{"dm":"002308","mc":"威创股份","jys":"sz"},{"dm":"002853","mc":"皮阿诺","jys":"sz"},{"dm":"301533","mc":"威马农机","jys":"sz"},{"dm":"003038","mc":"鑫铂股份","jys":"sz"},{"dm":"603733","mc":"仙鹤股份","jys":"sh"},{"dm":"300677","mc":"英科医疗","jys":"sz"},{"dm":"600531","mc":"豫光金铅","jys":"sh"},{"dm":"603555","mc":"ST贵人","jys":"sh"},{"dm":"601766","mc":"中国中车","jys":"sh"},{"dm":"002242","mc":"九阳股份","jys":"sz"},{"dm":"601636","mc":"旗滨集团","jys":"sh"},{"dm":"600935","mc":"华塑股份","jys":"sh"},{"dm":"300881","mc":"盛德鑫泰","jys":"sz"},{"dm":"688607","mc":"康众医疗","jys":"sh"},{"dm":"688085","mc":"三友医疗","jys":"sh"},{"dm":"600882","mc":"妙可蓝多","jys":"sh"},{"dm":"300181","mc":"佐力药业","jys":"sz"},{"dm":"603758","mc":"秦安股份","jys":"sh"},{"dm":"000785","mc":"居然之家","jys":"sz"},{"dm":"600116","mc":"三峡水利","jys":"sh"},{"dm":"300015","mc":"爱尔眼科","jys":"sz"},{"dm":"000692","mc":"*ST惠天","jys":"sz"},{"dm":"300986","mc":"志特新材","jys":"sz"},{"dm":"002508","mc":"老板电器","jys":"sz"},{"dm":"002800","mc":"ST天顺","jys":"sz"},{"dm":"301239","mc":"普瑞眼科","jys":"sz"},{"dm":"600383","mc":"金地集团","jys":"sh"},{"dm":"301555","mc":"惠柏新材","jys":"sz"},{"dm":"000629","mc":"钒钛股份","jys":"sz"},{"dm":"000882","mc":"华联股份","jys":"sz"},{"dm":"605258","mc":"协和电子","jys":"sh"},{"dm":"605337","mc":"李子园","jys":"sh"},{"dm":"603590","mc":"康辰药业","jys":"sh"},{"dm":"300648","mc":"星云股份","jys":"sz"},{"dm":"600519","mc":"贵州茅台","jys":"sh"},{"dm":"001322","mc":"箭牌家居","jys":"sz"},{"dm":"600511","mc":"国药股份","jys":"sh"},{"dm":"000898","mc":"鞍钢股份","jys":"sz"},{"dm":"000501","mc":"武商集团","jys":"sz"},{"dm":"301498","mc":"乖宝宠物","jys":"sz"},{"dm":"301186","mc":"超达装备","jys":"sz"},{"dm":"688217","mc":"睿昂基因","jys":"sh"},{"dm":"600189","mc":"泉阳泉","jys":"sh"},{"dm":"688295","mc":"中复神鹰","jys":"sh"},{"dm":"600843","mc":"上工申贝","jys":"sh"},{"dm":"605189","mc":"富春染织","jys":"sh"},{"dm":"601390","mc":"中国中铁","jys":"sh"},{"dm":"688570","mc":"天玛智控","jys":"sh"},{"dm":"600960","mc":"渤海汽车","jys":"sh"},{"dm":"688472","mc":"阿特斯","jys":"sh"},{"dm":"002120","mc":"韵达股份","jys":"sz"},{"dm":"603688","mc":"石英股份","jys":"sh"},{"dm":"300002","mc":"神州泰岳","jys":"sz"},{"dm":"600115","mc":"中国东航","jys":"sh"},{"dm":"300583","mc":"赛托生物","jys":"sz"},{"dm":"600011","mc":"华能国际","jys":"sh"},{"dm":"000878","mc":"云南铜业","jys":"sz"},{"dm":"002332","mc":"仙琚制药","jys":"sz"},{"dm":"603855","mc":"华荣股份","jys":"sh"},{"dm":"600743","mc":"华远地产","jys":"sh"},{"dm":"600673","mc":"东阳光","jys":"sh"},{"dm":"603071","mc":"物产环能","jys":"sh"},{"dm":"002667","mc":"威领股份","jys":"sz"},{"dm":"600499","mc":"科达制造","jys":"sh"},{"dm":"300251","mc":"光线传媒","jys":"sz"},{"dm":"603369","mc":"今世缘","jys":"sh"},{"dm":"301408","mc":"华人健康","jys":"sz"},{"dm":"600729","mc":"重庆百货","jys":"sh"},{"dm":"002461","mc":"珠江啤酒","jys":"sz"},{"dm":"300358","mc":"楚天科技","jys":"sz"},{"dm":"300988","mc":"津荣天宇","jys":"sz"},{"dm":"603755","mc":"日辰股份","jys":"sh"},{"dm":"603281","mc":"江瀚新材","jys":"sh"},{"dm":"688601","mc":"力芯微","jys":"sh"},{"dm":"603233","mc":"大参林","jys":"sh"},{"dm":"002219","mc":"新里程","jys":"sz"},{"dm":"603007","mc":"ST花王","jys":"sh"},{"dm":"002624","mc":"完美世界","jys":"sz"},{"dm":"300705","mc":"九典制药","jys":"sz"},{"dm":"600809","mc":"山西汾酒","jys":"sh"},{"dm":"600761","mc":"安徽合力","jys":"sh"},{"dm":"002597","mc":"金禾实业","jys":"sz"},{"dm":"601567","mc":"三星医疗","jys":"sh"},{"dm":"600323","mc":"瀚蓝环境","jys":"sh"},{"dm":"002520","mc":"日发精机","jys":"sz"},{"dm":"002812","mc":"恩捷股份","jys":"sz"},{"dm":"600741","mc":"华域汽车","jys":"sh"},{"dm":"002240","mc":"盛新锂能","jys":"sz"},{"dm":"001266","mc":"宏英智能","jys":"sz"},{"dm":"300404","mc":"博济医药","jys":"sz"},{"dm":"301141","mc":"中科磁业","jys":"sz"},{"dm":"300664","mc":"鹏鹞环保","jys":"sz"},{"dm":"600208","mc":"新湖中宝","jys":"sh"},{"dm":"000657","mc":"中钨高新","jys":"sz"},{"dm":"688526","mc":"科前生物","jys":"sh"},{"dm":"605177","mc":"东亚药业","jys":"sh"},{"dm":"603259","mc":"药明康德","jys":"sh"},{"dm":"601186","mc":"中国铁建","jys":"sh"},{"dm":"600977","mc":"中国电影","jys":"sh"},{"dm":"605300","mc":"佳禾食品","jys":"sh"},{"dm":"002324","mc":"普利特","jys":"sz"},{"dm":"688779","mc":"长远锂科","jys":"sh"},{"dm":"603867","mc":"新化股份","jys":"sh"},{"dm":"300633","mc":"开立医疗","jys":"sz"},{"dm":"603180","mc":"金牌厨柜","jys":"sh"},{"dm":"601208","mc":"东材科技","jys":"sh"},{"dm":"000415","mc":"渤海租赁","jys":"sz"},{"dm":"002175","mc":"东方智造","jys":"sz"},{"dm":"300731","mc":"科创新源","jys":"sz"},{"dm":"600776","mc":"东方通信","jys":"sh"},{"dm":"600811","mc":"东方集团","jys":"sh"},{"dm":"605377","mc":"华旺科技","jys":"sh"},{"dm":"688200","mc":"华峰测控","jys":"sh"},{"dm":"603566","mc":"普莱柯","jys":"sh"},{"dm":"300415","mc":"伊之密","jys":"sz"},{"dm":"600640","mc":"国脉文化","jys":"sh"},{"dm":"000796","mc":"*ST凯撒","jys":"sz"},{"dm":"002867","mc":"周大生","jys":"sz"},{"dm":"300593","mc":"新雷能","jys":"sz"},{"dm":"301190","mc":"善水科技","jys":"sz"},{"dm":"300332","mc":"天壕能源","jys":"sz"},{"dm":"000065","mc":"北方国际","jys":"sz"},{"dm":"600782","mc":"新钢股份","jys":"sh"},{"dm":"002119","mc":"康强电子","jys":"sz"},{"dm":"603079","mc":"圣达生物","jys":"sh"},{"dm":"688005","mc":"容百科技","jys":"sh"},{"dm":"002009","mc":"天奇股份","jys":"sz"},{"dm":"002608","mc":"江苏国信","jys":"sz"},{"dm":"002482","mc":"*ST广田","jys":"sz"},{"dm":"300436","mc":"广生堂","jys":"sz"},{"dm":"688121","mc":"卓然股份","jys":"sh"},{"dm":"600219","mc":"南山铝业","jys":"sh"},{"dm":"600301","mc":"华锡有色","jys":"sh"},{"dm":"603806","mc":"福斯特","jys":"sh"},{"dm":"603239","mc":"浙江仙通","jys":"sh"},{"dm":"603711","mc":"香飘飘","jys":"sh"},{"dm":"300363","mc":"博腾股份","jys":"sz"},{"dm":"601975","mc":"招商南油","jys":"sh"},{"dm":"002247","mc":"聚力文化","jys":"sz"},{"dm":"002078","mc":"太阳纸业","jys":"sz"},{"dm":"002379","mc":"宏创控股","jys":"sz"},{"dm":"301093","mc":"华兰股份","jys":"sz"},{"dm":"003012","mc":"东鹏控股","jys":"sz"},{"dm":"002237","mc":"恒邦股份","jys":"sz"},{"dm":"603616","mc":"韩建河山","jys":"sh"},{"dm":"000813","mc":"德展健康","jys":"sz"},{"dm":"002858","mc":"力盛体育","jys":"sz"},{"dm":"002233","mc":"塔牌集团","jys":"sz"},{"dm":"600654","mc":"ST中安","jys":"sh"},{"dm":"002110","mc":"三钢闽光","jys":"sz"},{"dm":"688536","mc":"思瑞浦","jys":"sh"},{"dm":"601668","mc":"中国建筑","jys":"sh"},{"dm":"688566","mc":"吉贝尔","jys":"sh"},{"dm":"600572","mc":"康恩贝","jys":"sh"},{"dm":"300144","mc":"宋城演艺","jys":"sz"},{"dm":"600176","mc":"中国巨石","jys":"sh"},{"dm":"601005","mc":"重庆钢铁","jys":"sh"},{"dm":"300143","mc":"盈康生命","jys":"sz"},{"dm":"688108","mc":"赛诺医疗","jys":"sh"},{"dm":"603768","mc":"常青股份","jys":"sh"},{"dm":"002831","mc":"裕同科技","jys":"sz"},{"dm":"603268","mc":"松发股份","jys":"sh"},{"dm":"002154","mc":"报 喜 鸟","jys":"sz"},{"dm":"603559","mc":"ST通脉","jys":"sh"},{"dm":"002475","mc":"立讯精密","jys":"sz"},{"dm":"688513","mc":"苑东生物","jys":"sh"},{"dm":"688137","mc":"近岸蛋白","jys":"sh"},{"dm":"603898","mc":"好莱客","jys":"sh"},{"dm":"603801","mc":"志邦家居","jys":"sh"},{"dm":"600486","mc":"扬农化工","jys":"sh"},{"dm":"000807","mc":"云铝股份","jys":"sz"},{"dm":"002906","mc":"华阳集团","jys":"sz"},{"dm":"002284","mc":"亚太股份","jys":"sz"},{"dm":"600221","mc":"海航控股","jys":"sh"},{"dm":"300370","mc":"安控科技","jys":"sz"},{"dm":"300175","mc":"朗源股份","jys":"sz"},{"dm":"300275","mc":"梅安森","jys":"sz"},{"dm":"688670","mc":"金迪克","jys":"sh"},{"dm":"603027","mc":"千禾味业","jys":"sh"},{"dm":"301349","mc":"信德新材","jys":"sz"},{"dm":"000516","mc":"国际医学","jys":"sz"},{"dm":"300855","mc":"图南股份","jys":"sz"},{"dm":"688180","mc":"君实生物-U","jys":"sh"},{"dm":"002928","mc":"华夏航空","jys":"sz"},{"dm":"603716","mc":"塞力医疗","jys":"sh"},{"dm":"300873","mc":"海晨股份","jys":"sz"},{"dm":"002756","mc":"永兴材料","jys":"sz"},{"dm":"605566","mc":"福莱蒽特","jys":"sh"},{"dm":"603168","mc":"莎普爱思","jys":"sh"},{"dm":"301091","mc":"深城交","jys":"sz"},{"dm":"300398","mc":"飞凯材料","jys":"sz"},{"dm":"301269","mc":"华大九天","jys":"sz"},{"dm":"603939","mc":"益丰药房","jys":"sh"},{"dm":"300434","mc":"金石亚药","jys":"sz"},{"dm":"605266","mc":"健之佳","jys":"sh"},{"dm":"688235","mc":"百济神州-U","jys":"sh"},{"dm":"603208","mc":"江山欧派","jys":"sh"},{"dm":"300299","mc":"富春股份","jys":"sz"},{"dm":"600305","mc":"恒顺醋业","jys":"sh"},{"dm":"603987","mc":"康德莱","jys":"sh"},{"dm":"603767","mc":"中马传动","jys":"sh"},{"dm":"002273","mc":"水晶光电","jys":"sz"},{"dm":"002320","mc":"海峡股份","jys":"sz"},{"dm":"600749","mc":"西藏旅游","jys":"sh"},{"dm":"000541","mc":"佛山照明","jys":"sz"},{"dm":"300488","mc":"恒锋工具","jys":"sz"},{"dm":"300258","mc":"精锻科技","jys":"sz"},{"dm":"601677","mc":"明泰铝业","jys":"sh"},{"dm":"603219","mc":"富佳股份","jys":"sh"},{"dm":"600763","mc":"通策医疗","jys":"sh"},{"dm":"300737","mc":"科顺股份","jys":"sz"},{"dm":"688276","mc":"百克生物","jys":"sh"},{"dm":"301358","mc":"湖南裕能","jys":"sz"},{"dm":"300231","mc":"银信科技","jys":"sz"},{"dm":"300441","mc":"鲍斯股份","jys":"sz"},{"dm":"600482","mc":"中国动力","jys":"sh"},{"dm":"688291","mc":"金橙子","jys":"sh"},{"dm":"300767","mc":"震安科技","jys":"sz"},{"dm":"603429","mc":"集友股份","jys":"sh"},{"dm":"000695","mc":"滨海能源","jys":"sz"},{"dm":"601958","mc":"金钼股份","jys":"sh"},{"dm":"601089","mc":"福元医药","jys":"sh"},{"dm":"601665","mc":"齐鲁银行","jys":"sh"},{"dm":"600647","mc":"*ST同达","jys":"sh"},{"dm":"688562","mc":"航天软件","jys":"sh"},{"dm":"300953","mc":"震裕科技","jys":"sz"},{"dm":"000672","mc":"上峰水泥","jys":"sz"},{"dm":"300545","mc":"联得装备","jys":"sz"},{"dm":"301210","mc":"金杨股份","jys":"sz"},{"dm":"600988","mc":"赤峰黄金","jys":"sh"},{"dm":"300151","mc":"昌红科技","jys":"sz"},{"dm":"000739","mc":"普洛药业","jys":"sz"},{"dm":"002977","mc":"天箭科技","jys":"sz"},{"dm":"688358","mc":"祥生医疗","jys":"sh"},{"dm":"688221","mc":"前沿生物-U","jys":"sh"},{"dm":"600179","mc":"安通控股","jys":"sh"},{"dm":"000996","mc":"*ST中期","jys":"sz"},{"dm":"002426","mc":"胜利精密","jys":"sz"},{"dm":"300985","mc":"致远新能","jys":"sz"},{"dm":"688558","mc":"国盛智科","jys":"sh"},{"dm":"688275","mc":"万润新能","jys":"sh"},{"dm":"300418","mc":"昆仑万维","jys":"sz"},{"dm":"688510","mc":"航亚科技","jys":"sh"},{"dm":"300818","mc":"耐普矿机","jys":"sz"},{"dm":"600215","mc":"派斯林","jys":"sh"},{"dm":"688658","mc":"悦康药业","jys":"sh"},{"dm":"002341","mc":"新纶新材","jys":"sz"},{"dm":"688212","mc":"澳华内镜","jys":"sh"},{"dm":"301235","mc":"华康医疗","jys":"sz"},{"dm":"000893","mc":"亚钾国际","jys":"sz"},{"dm":"600719","mc":"大连热电","jys":"sh"},{"dm":"300661","mc":"圣邦股份","jys":"sz"},{"dm":"002375","mc":"亚厦股份","jys":"sz"},{"dm":"000888","mc":"峨眉山A","jys":"sz"},{"dm":"300318","mc":"博晖创新","jys":"sz"},{"dm":"000155","mc":"川能动力","jys":"sz"},{"dm":"600476","mc":"湘邮科技","jys":"sh"},{"dm":"688631","mc":"莱斯信息","jys":"sh"},{"dm":"688408","mc":"中信博","jys":"sh"},{"dm":"002430","mc":"杭氧股份","jys":"sz"},{"dm":"300123","mc":"亚光科技","jys":"sz"},{"dm":"601163","mc":"三角轮胎","jys":"sh"},{"dm":"605369","mc":"拱东医疗","jys":"sh"},{"dm":"300832","mc":"新产业","jys":"sz"},{"dm":"003018","mc":"金富科技","jys":"sz"},{"dm":"300375","mc":"鹏翎股份","jys":"sz"},{"dm":"300685","mc":"艾德生物","jys":"sz"},{"dm":"603901","mc":"永创智能","jys":"sh"},{"dm":"002370","mc":"亚太药业","jys":"sz"},{"dm":"605368","mc":"蓝天燃气","jys":"sh"},{"dm":"601318","mc":"中国平安","jys":"sh"},{"dm":"002470","mc":"金正大","jys":"sz"},{"dm":"600233","mc":"圆通速递","jys":"sh"},{"dm":"603196","mc":"日播时尚","jys":"sh"},{"dm":"603136","mc":"天目湖","jys":"sh"},{"dm":"002699","mc":"*ST美盛","jys":"sz"},{"dm":"688050","mc":"爱博医疗","jys":"sh"},{"dm":"688657","mc":"浩辰软件","jys":"sh"},{"dm":"002664","mc":"信质集团","jys":"sz"},{"dm":"603107","mc":"C汽配","jys":"sh"},{"dm":"601872","mc":"招商轮船","jys":"sh"},{"dm":"688556","mc":"高测股份","jys":"sh"},{"dm":"603538","mc":"美诺华","jys":"sh"},{"dm":"688238","mc":"和元生物","jys":"sh"},{"dm":"300014","mc":"亿纬锂能","jys":"sz"},{"dm":"603009","mc":"北特科技","jys":"sh"},{"dm":"600026","mc":"中远海能","jys":"sh"},{"dm":"603416","mc":"信捷电气","jys":"sh"},{"dm":"688484","mc":"南芯科技","jys":"sh"},{"dm":"603055","mc":"台华新材","jys":"sh"},{"dm":"301306","mc":"西测测试","jys":"sz"},{"dm":"002099","mc":"海翔药业","jys":"sz"},{"dm":"301488","mc":"豪恩汽电","jys":"sz"},{"dm":"605108","mc":"同庆楼","jys":"sh"},{"dm":"000697","mc":"*ST炼石","jys":"sz"},{"dm":"300645","mc":"正元智慧","jys":"sz"},{"dm":"002976","mc":"瑞玛精密","jys":"sz"},{"dm":"000049","mc":"德赛电池","jys":"sz"},{"dm":"002566","mc":"益盛药业","jys":"sz"},{"dm":"002337","mc":"赛象科技","jys":"sz"},{"dm":"002068","mc":"黑猫股份","jys":"sz"},{"dm":"300571","mc":"平治信息","jys":"sz"},{"dm":"600023","mc":"浙能电力","jys":"sh"},{"dm":"688345","mc":"博力威","jys":"sh"},{"dm":"600569","mc":"安阳钢铁","jys":"sh"},{"dm":"000676","mc":"智度股份","jys":"sz"},{"dm":"300470","mc":"中密控股","jys":"sz"},{"dm":"600362","mc":"江西铜业","jys":"sh"},{"dm":"688131","mc":"皓元医药","jys":"sh"},{"dm":"603108","mc":"润达医疗","jys":"sh"},{"dm":"688719","mc":"爱科赛博","jys":"sh"},{"dm":"300207","mc":"欣旺达","jys":"sz"},{"dm":"000680","mc":"山推股份","jys":"sz"},{"dm":"300408","mc":"三环集团","jys":"sz"},{"dm":"002788","mc":"鹭燕医药","jys":"sz"},{"dm":"601702","mc":"华峰铝业","jys":"sh"},{"dm":"002180","mc":"纳思达","jys":"sz"},{"dm":"603222","mc":"济民医疗","jys":"sh"},{"dm":"300233","mc":"金城医药","jys":"sz"},{"dm":"688088","mc":"虹软科技","jys":"sh"},{"dm":"300395","mc":"菲利华","jys":"sz"},{"dm":"601333","mc":"广深铁路","jys":"sh"},{"dm":"600587","mc":"新华医疗","jys":"sh"},{"dm":"300979","mc":"华利集团","jys":"sz"},{"dm":"600754","mc":"锦江酒店","jys":"sh"},{"dm":"688152","mc":"麒麟信安","jys":"sh"},{"dm":"002532","mc":"天山铝业","jys":"sz"},{"dm":"605277","mc":"新亚电子","jys":"sh"},{"dm":"001319","mc":"铭科精技","jys":"sz"},{"dm":"688058","mc":"宝兰德","jys":"sh"},{"dm":"689009","mc":"九号公司-WD","jys":"sh"},{"dm":"300857","mc":"协创数据","jys":"sz"},{"dm":"300391","mc":"长药控股","jys":"sz"},{"dm":"603529","mc":"爱玛科技","jys":"sh"},{"dm":"002713","mc":"东易日盛","jys":"sz"},{"dm":"688302","mc":"海创药业-U","jys":"sh"},{"dm":"603048","mc":"浙江黎明","jys":"sh"},{"dm":"002708","mc":"光洋股份","jys":"sz"},{"dm":"002541","mc":"鸿路钢构","jys":"sz"},{"dm":"600575","mc":"淮河能源","jys":"sh"},{"dm":"002870","mc":"香山股份","jys":"sz"},{"dm":"002738","mc":"中矿资源","jys":"sz"},{"dm":"300034","mc":"钢研高纳","jys":"sz"},{"dm":"688110","mc":"东芯股份","jys":"sh"},{"dm":"688150","mc":"莱特光电","jys":"sh"},{"dm":"000711","mc":"*ST京蓝","jys":"sz"},{"dm":"688261","mc":"东微半导","jys":"sh"},{"dm":"600851","mc":"海欣股份","jys":"sh"},{"dm":"301368","mc":"丰立智能","jys":"sz"},{"dm":"600771","mc":"广誉远","jys":"sh"},{"dm":"002793","mc":"罗欣药业","jys":"sz"},{"dm":"301285","mc":"鸿日达","jys":"sz"},{"dm":"688581","mc":"安杰思","jys":"sh"},{"dm":"688308","mc":"欧科亿","jys":"sh"},{"dm":"002123","mc":"梦网科技","jys":"sz"},{"dm":"002182","mc":"宝武镁业","jys":"sz"},{"dm":"688433","mc":"华曙高科","jys":"sh"},{"dm":"300083","mc":"创世纪","jys":"sz"},{"dm":"688029","mc":"南微医学","jys":"sh"},{"dm":"000488","mc":"晨鸣纸业","jys":"sz"},{"dm":"601319","mc":"中国人保","jys":"sh"},{"dm":"600333","mc":"长春燃气","jys":"sh"},{"dm":"603983","mc":"丸美股份","jys":"sh"},{"dm":"603617","mc":"君禾股份","jys":"sh"},{"dm":"001914","mc":"招商积余","jys":"sz"},{"dm":"002634","mc":"棒杰股份","jys":"sz"},{"dm":"002596","mc":"海南瑞泽","jys":"sz"},{"dm":"002007","mc":"华兰生物","jys":"sz"},{"dm":"603015","mc":"弘讯科技","jys":"sh"},{"dm":"002648","mc":"卫星化学","jys":"sz"},{"dm":"300910","mc":"瑞丰新材","jys":"sz"},{"dm":"603699","mc":"纽威股份","jys":"sh"},{"dm":"600657","mc":"信达地产","jys":"sh"},{"dm":"688173","mc":"希荻微","jys":"sh"},{"dm":"688392","mc":"骄成超声","jys":"sh"},{"dm":"688676","mc":"金盘科技","jys":"sh"},{"dm":"603098","mc":"森特股份","jys":"sh"},{"dm":"000708","mc":"中信特钢","jys":"sz"},{"dm":"688059","mc":"华锐精密","jys":"sh"},{"dm":"601336","mc":"新华保险","jys":"sh"},{"dm":"300430","mc":"诚益通","jys":"sz"},{"dm":"600258","mc":"首旅酒店","jys":"sh"},{"dm":"688153","mc":"唯捷创芯","jys":"sh"},{"dm":"301042","mc":"安联锐视","jys":"sz"},{"dm":"000528","mc":"柳 工","jys":"sz"},{"dm":"688325","mc":"赛微微电","jys":"sh"},{"dm":"603986","mc":"兆易创新","jys":"sh"},{"dm":"002277","mc":"友阿股份","jys":"sz"},{"dm":"301550","mc":"斯菱股份","jys":"sz"},{"dm":"603456","mc":"九洲药业","jys":"sh"},{"dm":"600529","mc":"山东药玻","jys":"sh"},{"dm":"000963","mc":"华东医药","jys":"sz"},{"dm":"600246","mc":"万通发展","jys":"sh"},{"dm":"002709","mc":"天赐材料","jys":"sz"},{"dm":"688052","mc":"纳芯微","jys":"sh"},{"dm":"300535","mc":"达威股份","jys":"sz"},{"dm":"300291","mc":"百纳千成","jys":"sz"},{"dm":"300266","mc":"兴源环境","jys":"sz"},{"dm":"603766","mc":"隆鑫通用","jys":"sh"},{"dm":"600563","mc":"法拉电子","jys":"sh"},{"dm":"301397","mc":"溯联股份","jys":"sz"},{"dm":"002138","mc":"顺络电子","jys":"sz"},{"dm":"000060","mc":"中金岭南","jys":"sz"},{"dm":"300196","mc":"长海股份","jys":"sz"},{"dm":"603195","mc":"公牛集团","jys":"sh"},{"dm":"603899","mc":"晨光股份","jys":"sh"},{"dm":"301230","mc":"泓博医药","jys":"sz"},{"dm":"000975","mc":"银泰黄金","jys":"sz"},{"dm":"002888","mc":"惠威科技","jys":"sz"},{"dm":"688156","mc":"路德环境","jys":"sh"},{"dm":"000543","mc":"皖能电力","jys":"sz"},{"dm":"688301","mc":"奕瑞科技","jys":"sh"},{"dm":"688596","mc":"正帆科技","jys":"sh"},{"dm":"002826","mc":"易明医药","jys":"sz"},{"dm":"000793","mc":"华闻集团","jys":"sz"},{"dm":"688679","mc":"通源环境","jys":"sh"},{"dm":"688371","mc":"菲沃泰","jys":"sh"},{"dm":"600078","mc":"ST澄星","jys":"sh"},{"dm":"001367","mc":"海森药业","jys":"sz"},{"dm":"002727","mc":"一心堂","jys":"sz"},{"dm":"601966","mc":"玲珑轮胎","jys":"sh"},{"dm":"601628","mc":"中国人寿","jys":"sh"},{"dm":"002903","mc":"宇环数控","jys":"sz"},{"dm":"688383","mc":"新益昌","jys":"sh"},{"dm":"603028","mc":"赛福天","jys":"sh"},{"dm":"000892","mc":"欢瑞世纪","jys":"sz"},{"dm":"600186","mc":"莲花健康","jys":"sh"},{"dm":"688443","mc":"智翔金泰-U","jys":"sh"},{"dm":"603383","mc":"顶点软件","jys":"sh"},{"dm":"600801","mc":"华新水泥","jys":"sh"},{"dm":"603163","mc":"圣晖集成","jys":"sh"},{"dm":"688332","mc":"中科蓝讯","jys":"sh"},{"dm":"300580","mc":"贝斯特","jys":"sz"},{"dm":"300102","mc":"乾照光电","jys":"sz"},{"dm":"688799","mc":"华纳药厂","jys":"sh"},{"dm":"688019","mc":"安集科技","jys":"sh"},{"dm":"001308","mc":"康冠科技","jys":"sz"},{"dm":"300280","mc":"紫天科技","jys":"sz"},{"dm":"300269","mc":"联建光电","jys":"sz"},{"dm":"300093","mc":"金刚光伏","jys":"sz"},{"dm":"000503","mc":"国新健康","jys":"sz"},{"dm":"688223","mc":"晶科能源","jys":"sh"},{"dm":"600285","mc":"羚锐制药","jys":"sh"},{"dm":"688595","mc":"芯海科技","jys":"sh"},{"dm":"688377","mc":"迪威尔","jys":"sh"},{"dm":"300122","mc":"智飞生物","jys":"sz"},{"dm":"300750","mc":"宁德时代","jys":"sz"},{"dm":"300606","mc":"金太阳","jys":"sz"},{"dm":"300709","mc":"精研科技","jys":"sz"},{"dm":"002151","mc":"北斗星通","jys":"sz"},{"dm":"300926","mc":"博俊科技","jys":"sz"},{"dm":"002107","mc":"沃华医药","jys":"sz"},{"dm":"688239","mc":"航宇科技","jys":"sh"},{"dm":"688356","mc":"键凯科技","jys":"sh"},{"dm":"600398","mc":"海澜之家","jys":"sh"},{"dm":"688177","mc":"百奥泰","jys":"sh"},{"dm":"002818","mc":"富森美","jys":"sz"},{"dm":"688333","mc":"铂力特","jys":"sh"},{"dm":"601799","mc":"星宇股份","jys":"sh"},{"dm":"688220","mc":"翱捷科技-U","jys":"sh"},{"dm":"600066","mc":"宇通客车","jys":"sh"},{"dm":"603786","mc":"科博达","jys":"sh"},{"dm":"301236","mc":"软通动力","jys":"sz"},{"dm":"002203","mc":"海亮股份","jys":"sz"},{"dm":"600513","mc":"联环药业","jys":"sh"},{"dm":"688318","mc":"财富趋势","jys":"sh"},{"dm":"600266","mc":"城建发展","jys":"sh"},{"dm":"603730","mc":"岱美股份","jys":"sh"},{"dm":"300539","mc":"横河精密","jys":"sz"},{"dm":"301319","mc":"唯特偶","jys":"sz"},{"dm":"002513","mc":"蓝丰生化","jys":"sz"},{"dm":"301289","mc":"国缆检测","jys":"sz"},{"dm":"002262","mc":"恩华药业","jys":"sz"},{"dm":"000603","mc":"盛达资源","jys":"sz"},{"dm":"688531","mc":"日联科技","jys":"sh"},{"dm":"603008","mc":"喜临门","jys":"sh"},{"dm":"603666","mc":"亿嘉和","jys":"sh"},{"dm":"300584","mc":"海辰药业","jys":"sz"},{"dm":"600119","mc":"长江投资","jys":"sh"},{"dm":"603885","mc":"吉祥航空","jys":"sh"},{"dm":"300833","mc":"浩洋股份","jys":"sz"},{"dm":"600649","mc":"城投控股","jys":"sh"},{"dm":"600155","mc":"华创云信","jys":"sh"},{"dm":"300683","mc":"海特生物","jys":"sz"},{"dm":"688188","mc":"柏楚电子","jys":"sh"},{"dm":"002963","mc":"豪尔赛","jys":"sz"},{"dm":"300933","mc":"中辰股份","jys":"sz"},{"dm":"601127","mc":"赛力斯","jys":"sh"},{"dm":"300169","mc":"天晟新材","jys":"sz"},{"dm":"688351","mc":"微电生理-U","jys":"sh"},{"dm":"300735","mc":"光弘科技","jys":"sz"},{"dm":"688236","mc":"春立医疗","jys":"sh"},{"dm":"688617","mc":"惠泰医疗","jys":"sh"},{"dm":"601601","mc":"中国太保","jys":"sh"},{"dm":"600593","mc":"大连圣亚","jys":"sh"},{"dm":"002572","mc":"索菲亚","jys":"sz"},{"dm":"688210","mc":"统联精密","jys":"sh"},{"dm":"688260","mc":"昀冢科技","jys":"sh"},{"dm":"688036","mc":"传音控股","jys":"sh"},{"dm":"300313","mc":"*ST天山","jys":"sz"},{"dm":"603929","mc":"亚翔集成","jys":"sh"},{"dm":"300765","mc":"新诺威","jys":"sz"},{"dm":"300049","mc":"福瑞股份","jys":"sz"},{"dm":"688028","mc":"沃尔德","jys":"sh"},{"dm":"001376","mc":"C百通","jys":"sz"}] \ No newline at end of file diff --git a/dev_opsgpt/tools/weather.py b/coagent/tools/weather.py similarity index 100% rename from dev_opsgpt/tools/weather.py rename to coagent/tools/weather.py diff --git a/dev_opsgpt/tools/world_time.py b/coagent/tools/world_time.py similarity index 100% rename from dev_opsgpt/tools/world_time.py rename to coagent/tools/world_time.py diff --git a/dev_opsgpt/utils/__init__.py b/coagent/utils/__init__.py similarity index 100% rename from dev_opsgpt/utils/__init__.py rename to coagent/utils/__init__.py diff --git a/dev_opsgpt/utils/common_utils.py b/coagent/utils/common_utils.py similarity index 78% rename from dev_opsgpt/utils/common_utils.py rename to coagent/utils/common_utils.py index 047c831..19c0cca 100644 --- a/dev_opsgpt/utils/common_utils.py +++ b/coagent/utils/common_utils.py @@ -13,6 +13,19 @@ from tempfile import SpooledTemporaryFile DATE_FORMAT = "%Y-%m-%d %H:%M:%S" +def getCurrentDatetime(): + return datetime.now().strftime("%Y-%m-%d %H:%M:00") + + +def addMinutesToTime(input_time: str, n: int = 5, dateformat=DATE_FORMAT): + dt = datetime.strptime(input_time, dateformat) + + # 前后加N分钟 + new_time_before = dt - timedelta(minutes=n) + new_time_after = dt + timedelta(minutes=n) + return new_time_before.strftime(dateformat), new_time_after.strftime(dateformat) + + def timestampToDateformat(ts, interval=1000, dateformat=DATE_FORMAT): '''将标准时间戳转换标准指定时间格式''' return datetime.fromtimestamp(ts//interval).strftime(dateformat) @@ -42,6 +55,13 @@ def func_timer(): return function_timer +def custom_serializer(obj): + try: + return str(obj) + except TypeError: + raise TypeError(f"Object of type {type(obj)} is not JSON serializable") + + def read_jsonl_file(filename): data = [] with open(filename, "r", encoding="utf-8") as f: @@ -69,7 +89,7 @@ def save_to_json_file(data, filename): if not os.path.exists(dir_name): os.makedirs(dir_name) with open(filename, "w", encoding="utf-8") as f: - json.dump(data, f, indent=2, ensure_ascii=False) + json.dump(data, f, indent=2, ensure_ascii=False, default=custom_serializer) def file_normalize(file: Union[str, Path, bytes], filename=None): diff --git a/dev_opsgpt/utils/nebula_cp.sh b/coagent/utils/nebula_cp.sh similarity index 100% rename from dev_opsgpt/utils/nebula_cp.sh rename to coagent/utils/nebula_cp.sh diff --git a/dev_opsgpt/utils/path_utils.py b/coagent/utils/path_utils.py similarity index 61% rename from dev_opsgpt/utils/path_utils.py rename to coagent/utils/path_utils.py index 26e766a..4810496 100644 --- a/dev_opsgpt/utils/path_utils.py +++ b/coagent/utils/path_utils.py @@ -1,12 +1,11 @@ import os from langchain.document_loaders import CSVLoader, PyPDFLoader, UnstructuredFileLoader, TextLoader, PythonLoader -from langchain.embeddings.huggingface import HuggingFaceEmbeddings -from dev_opsgpt.document_loaders import JSONLLoader, JSONLoader -from configs.model_config import ( - embedding_model_dict, - KB_ROOT_PATH, -) +from coagent.document_loaders import JSONLLoader, JSONLoader +# from configs.model_config import ( +# embedding_model_dict, +# KB_ROOT_PATH, +# ) from loguru import logger @@ -43,24 +42,24 @@ def validate_kb_name(knowledge_base_id: str) -> bool: return False return True -def get_kb_path(knowledge_base_name: str): - return os.path.join(KB_ROOT_PATH, knowledge_base_name) +def get_kb_path(knowledge_base_name: str, kb_root_path: str): + return os.path.join(kb_root_path, knowledge_base_name) -def get_doc_path(knowledge_base_name: str): - return os.path.join(get_kb_path(knowledge_base_name), "content") +def get_doc_path(knowledge_base_name: str, kb_root_path: str): + return os.path.join(get_kb_path(knowledge_base_name, kb_root_path), "content") -def get_vs_path(knowledge_base_name: str): - return os.path.join(get_kb_path(knowledge_base_name), "vector_store") +def get_vs_path(knowledge_base_name: str, kb_root_path: str): + return os.path.join(get_kb_path(knowledge_base_name, kb_root_path), "vector_store") -def get_file_path(knowledge_base_name: str, doc_name: str): - return os.path.join(get_doc_path(knowledge_base_name), doc_name) +def get_file_path(knowledge_base_name: str, doc_name: str, kb_root_path: str): + return os.path.join(get_doc_path(knowledge_base_name, kb_root_path), doc_name) -def list_kbs_from_folder(): - return [f for f in os.listdir(KB_ROOT_PATH) - if os.path.isdir(os.path.join(KB_ROOT_PATH, f))] +def list_kbs_from_folder(kb_root_path: str): + return [f for f in os.listdir(kb_root_path) + if os.path.isdir(os.path.join(kb_root_path, f))] -def list_docs_from_folder(kb_name: str): - doc_path = get_doc_path(kb_name) +def list_docs_from_folder(kb_name: str, kb_root_path: str): + doc_path = get_doc_path(kb_name, kb_root_path) if os.path.exists(doc_path): return [file for file in os.listdir(doc_path) if os.path.isfile(os.path.join(doc_path, file))] diff --git a/dev_opsgpt/utils/postprocess.py b/coagent/utils/postprocess.py similarity index 100% rename from dev_opsgpt/utils/postprocess.py rename to coagent/utils/postprocess.py diff --git a/dev_opsgpt/utils/server_utils.py b/coagent/utils/server_utils.py similarity index 100% rename from dev_opsgpt/utils/server_utils.py rename to coagent/utils/server_utils.py diff --git a/coagent/utils/static/__init__.py b/coagent/utils/static/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dev_opsgpt/utils/static/favicon.png b/coagent/utils/static/favicon.png similarity index 100% rename from dev_opsgpt/utils/static/favicon.png rename to coagent/utils/static/favicon.png diff --git a/dev_opsgpt/utils/static/redoc.standalone.js b/coagent/utils/static/redoc.standalone.js similarity index 100% rename from dev_opsgpt/utils/static/redoc.standalone.js rename to coagent/utils/static/redoc.standalone.js diff --git a/dev_opsgpt/utils/static/swagger-ui-bundle.js b/coagent/utils/static/swagger-ui-bundle.js similarity index 100% rename from dev_opsgpt/utils/static/swagger-ui-bundle.js rename to coagent/utils/static/swagger-ui-bundle.js diff --git a/dev_opsgpt/utils/static/swagger-ui.css b/coagent/utils/static/swagger-ui.css similarity index 100% rename from dev_opsgpt/utils/static/swagger-ui.css rename to coagent/utils/static/swagger-ui.css diff --git a/configs/__init__.py b/configs/__init__.py deleted file mode 100644 index 9898291..0000000 --- a/configs/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .model_config import * -from .server_config import * - -VERSION = "v0.1.0" \ No newline at end of file diff --git a/configs/default_config.py b/configs/default_config.py new file mode 100644 index 0000000..7af6752 --- /dev/null +++ b/configs/default_config.py @@ -0,0 +1,110 @@ +import os +import platform + + +# +system_name = platform.system() + + +# 日志存储路径 +LOG_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "logs") +# 知识库默认存储路径 +SOURCE_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "sources") +# 知识库默认存储路径 +KB_ROOT_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "knowledge_base") +# 代码库默认存储路径 +CB_ROOT_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "code_base") +# nltk 模型存储路径 +NLTK_DATA_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "nltk_data") +# 代码存储路径 +JUPYTER_WORK_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "jupyter_work") +# WEB_CRAWL存储路径 +WEB_CRAWL_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "knowledge_base") +# NEBULA_DATA存储路径 +NELUBA_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data/neluba_data") + +for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NELUBA_PATH]: + if not os.path.exists(_path): + os.makedirs(_path, exist_ok=True) + +# +path_envt_dict = { + "LOG_PATH": LOG_PATH, "SOURCE_PATH": SOURCE_PATH, "KB_ROOT_PATH": KB_ROOT_PATH, + "NLTK_DATA_PATH":NLTK_DATA_PATH, "JUPYTER_WORK_PATH": JUPYTER_WORK_PATH, + "WEB_CRAWL_PATH": WEB_CRAWL_PATH, "NELUBA_PATH": NELUBA_PATH + } +for path_name, _path in path_envt_dict.items(): + os.environ[path_name] = _path + + +# 数据库默认存储路径。 +# 如果使用sqlite,可以直接修改DB_ROOT_PATH;如果使用其它数据库,请直接修改SQLALCHEMY_DATABASE_URI。 +DB_ROOT_PATH = os.path.join(KB_ROOT_PATH, "info.db") +SQLALCHEMY_DATABASE_URI = f"sqlite:///{DB_ROOT_PATH}" + +# 可选向量库类型及对应配置 +kbs_config = { + "faiss": { + }, + # "milvus": { + # "host": "127.0.0.1", + # "port": "19530", + # "user": "", + # "password": "", + # "secure": False, + # }, + # "pg": { + # "connection_uri": "postgresql://postgres:postgres@127.0.0.1:5432/langchain_chatchat", + # } +} + +# 默认向量库类型。可选:faiss, milvus, pg. +DEFAULT_VS_TYPE = "faiss" + +# 缓存向量库数量 +CACHED_VS_NUM = 1 + +# 知识库中单段文本长度 +CHUNK_SIZE = 500 + +# 知识库中相邻文本重合长度 +OVERLAP_SIZE = 50 + +# 知识库匹配向量数量 +VECTOR_SEARCH_TOP_K = 5 + +# 知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右 +# Mac 可能存在无法使用normalized_L2的问题,因此调整SCORE_THRESHOLD至 0~1100 +FAISS_NORMALIZE_L2 = True if system_name in ["Linux", "Windows"] else False +SCORE_THRESHOLD = 1 if system_name in ["Linux", "Windows"] else 1100 + +# 搜索引擎匹配结题数量 +SEARCH_ENGINE_TOP_K = 5 + +# 代码引擎匹配结题数量 +CODE_SEARCH_TOP_K = 1 + + +# API 是否开启跨域,默认为False,如果需要开启,请设置为True +# is open cross domain +OPEN_CROSS_DOMAIN = False + +# Bing 搜索必备变量 +# 使用 Bing 搜索需要使用 Bing Subscription Key,需要在azure port中申请试用bing search +# 具体申请方式请见 +# https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/create-bing-search-service-resource +# 使用python创建bing api 搜索实例详见: +# https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/quickstarts/rest/python +BING_SEARCH_URL = "https://api.bing.microsoft.com/v7.0/search" +# 注意不是bing Webmaster Tools的api key, + +# 此外,如果是在服务器上,报Failed to establish a new connection: [Errno 110] Connection timed out +# 是因为服务器加了防火墙,需要联系管理员加白名单,如果公司的服务器的话,就别想了GG +BING_SUBSCRIPTION_KEY = "" + +# 是否开启中文标题加强,以及标题增强的相关配置 +# 通过增加标题判断,判断哪些文本为标题,并在metadata中进行标记; +# 然后将文本与往上一级的标题进行拼合,实现文本信息的增强。 +ZH_TITLE_ENHANCE = False + +log_verbose = False \ No newline at end of file diff --git a/configs/model_config.py.example b/configs/model_config.py.example index e854a82..ca95bb8 100644 --- a/configs/model_config.py.example +++ b/configs/model_config.py.example @@ -5,22 +5,55 @@ import torch import openai import base64 from .utils import is_running_in_docker +from .default_config import * # 日志格式 LOG_FORMAT = "%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s" logger = logging.getLogger() logger.setLevel(logging.INFO) logging.basicConfig(format=LOG_FORMAT) -# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" -os.environ["API_BASE_URL"] = "http://openai.com/v1/chat/completions" -os.environ["OPENAI_API_KEY"] = "" -os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or "socks5://127.0.0.1:13659" -os.environ["BAIDU_OCR_API_KEY"] = "" -os.environ["BAIDU_OCR_SECRET_KEY"] = "" +VERSION = "v0.1.0" import platform system_name = platform.system() +try: + # ignore these content + from zdatafront import client, monkey, OPENAI_API_BASE + # patch openai sdk + monkey.patch_openai() + secret_key = base64.b64decode('xx').decode('utf-8') + # zdatafront 提供的统一加密密钥 + client.aes_secret_key = secret_key + # zdatafront 分配的业务标记 + client.visit_domain = os.environ.get("visit_domain") + client.visit_biz = os.environ.get("visit_biz") + client.visit_biz_line = os.environ.get("visit_biz_line") +except: + pass + +# add your openai key +OPENAI_API_BASE = "http://openai.com/v1/chat/completions" +os.environ["API_BASE_URL"] = OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = "sk-xx" +openai.api_key = "sk-xx" +# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or "socks5://127.0.0.1:13659" +# ignore if you dont's use baidu_ocr_api +os.environ["BAIDU_OCR_API_KEY"] = "xx" +os.environ["BAIDU_OCR_SECRET_KEY"] = "xx" + +os.environ["log_verbose"] = "2" +# LLM 名称 +EMBEDDING_ENGINE = 'model' # openai or model +EMBEDDING_MODEL = "text2vec-base" +LLM_MODEL = "gpt-3.5-turbo" +LLM_MODELs = ["gpt-3.5-turbo"] +USE_FASTCHAT = "gpt" not in LLM_MODEL # 判断是否进行fastchat + +# LLM 运行设备 +LLM_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" + # 在以下字典中修改属性值,以指定本地embedding模型存储位置 # 如将 "text2vec": "GanymedeNil/text2vec-large-chinese" 修改为 "text2vec": "User/Downloads/text2vec-large-chinese" # 此处请写绝对路径 @@ -44,10 +77,6 @@ embedding_model_dict = { LOCAL_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "embedding_models") embedding_model_dict = {k: f"/home/user/chatbot/embedding_models/{v}" if is_running_in_docker() else f"{LOCAL_MODEL_DIR}/{v}" for k, v in embedding_model_dict.items()} -# 选用的 Embedding 名称 -EMBEDDING_ENGINE = 'openai' -EMBEDDING_MODEL = "text2vec-base" - # Embedding 模型运行设备 EMBEDDING_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" @@ -100,6 +129,21 @@ llm_model_dict = { "api_base_url": os.environ.get("API_BASE_URL"), "api_key": os.environ.get("OPENAI_API_KEY") }, + "gpt-3.5-turbo-0613": { + "local_model_path": "gpt-3.5-turbo-0613", + "api_base_url": os.environ.get("API_BASE_URL"), + "api_key": os.environ.get("OPENAI_API_KEY") + }, + "gpt-4": { + "local_model_path": "gpt-4", + "api_base_url": os.environ.get("API_BASE_URL"), + "api_key": os.environ.get("OPENAI_API_KEY") + }, + "gpt-3.5-turbo-1106": { + "local_model_path": "gpt-3.5-turbo-1106", + "api_base_url": os.environ.get("API_BASE_URL"), + "api_key": os.environ.get("OPENAI_API_KEY") + }, } # 建议使用chat模型,不要使用base,无法获取正确输出 @@ -114,7 +158,7 @@ VLLM_MODEL_DICT = { LOCAL_LLM_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "llm_models") -# 若不想修改模型地址,可取消相关地址设置 +# 模型路径重置 llm_model_dict_c = {} for k, v in llm_model_dict.items(): v_c = {} @@ -126,141 +170,8 @@ for k, v in llm_model_dict.items(): llm_model_dict_c[k] = v_c llm_model_dict = llm_model_dict_c -# 若不想修改模型地址,可取消相关地址设置 +# VLLM_MODEL_DICT_c = {} for k, v in VLLM_MODEL_DICT.items(): VLLM_MODEL_DICT_c[k] = f"/home/user/chatbot/llm_models/{v}" if is_running_in_docker() else f"{LOCAL_LLM_MODEL_DIR}/{v}" -VLLM_MODEL_DICT = VLLM_MODEL_DICT_c - - -# LLM 名称 -# EMBEDDING_ENGINE = 'openai' -EMBEDDING_ENGINE = 'model' -EMBEDDING_MODEL = "text2vec-base" -# LLM_MODEL = "gpt-4" -LLM_MODEL = "gpt-3.5-turbo-16k" -LLM_MODELs = ["gpt-3.5-turbo-16k"] -USE_FASTCHAT = "gpt" not in LLM_MODEL # 判断是否进行fastchat - -# LLM 运行设备 -LLM_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" - -# 日志存储路径 -LOG_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "logs") -if not os.path.exists(LOG_PATH): - os.mkdir(LOG_PATH) - -# 知识库默认存储路径 -SOURCE_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "sources") - -# 知识库默认存储路径 -KB_ROOT_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "knowledge_base") - -# 代码库默认存储路径 -CB_ROOT_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "code_base") - -# nltk 模型存储路径 -NLTK_DATA_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "nltk_data") - -# 代码存储路径 -JUPYTER_WORK_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "jupyter_work") - -# WEB_CRAWL存储路径 -WEB_CRAWL_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "knowledge_base") - -# NEBULA_DATA存储路径 -NELUBA_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data/neluba_data") - -for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NELUBA_PATH]: - if not os.path.exists(_path): - os.makedirs(_path, exist_ok=True) - -# 数据库默认存储路径。 -# 如果使用sqlite,可以直接修改DB_ROOT_PATH;如果使用其它数据库,请直接修改SQLALCHEMY_DATABASE_URI。 -DB_ROOT_PATH = os.path.join(KB_ROOT_PATH, "info.db") -SQLALCHEMY_DATABASE_URI = f"sqlite:///{DB_ROOT_PATH}" - -# 可选向量库类型及对应配置 -kbs_config = { - "faiss": { - }, - # "milvus": { - # "host": "127.0.0.1", - # "port": "19530", - # "user": "", - # "password": "", - # "secure": False, - # }, - # "pg": { - # "connection_uri": "postgresql://postgres:postgres@127.0.0.1:5432/langchain_chatchat", - # } -} - -# 默认向量库类型。可选:faiss, milvus, pg. -DEFAULT_VS_TYPE = "faiss" - -# 缓存向量库数量 -CACHED_VS_NUM = 1 - -# 知识库中单段文本长度 -CHUNK_SIZE = 500 - -# 知识库中相邻文本重合长度 -OVERLAP_SIZE = 50 - -# 知识库匹配向量数量 -VECTOR_SEARCH_TOP_K = 5 - -# 知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右 -# Mac 可能存在无法使用normalized_L2的问题,因此调整SCORE_THRESHOLD至 0~1100 -FAISS_NORMALIZE_L2 = True if system_name in ["Linux", "Windows"] else False -SCORE_THRESHOLD = 1 if system_name in ["Linux", "Windows"] else 1100 - -# 搜索引擎匹配结题数量 -SEARCH_ENGINE_TOP_K = 5 - -# 代码引擎匹配结题数量 -CODE_SEARCH_TOP_K = 1 - -# 基于本地知识问答的提示词模版 -PROMPT_TEMPLATE = """【指令】根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,不允许在答案中添加编造成分,答案请使用中文。 - -【已知信息】{context} - -【问题】{question}""" - -# 基于本地代码知识问答的提示词模版 -CODE_PROMPT_TEMPLATE = """【指令】根据已知信息来回答问题。 - -【已知信息】{context} - -【问题】{question}""" - -# 代码解释模版 -CODE_INTERPERT_TEMPLATE = '''{code} - -解释一下这段代码''' - -# API 是否开启跨域,默认为False,如果需要开启,请设置为True -# is open cross domain -OPEN_CROSS_DOMAIN = False - -# Bing 搜索必备变量 -# 使用 Bing 搜索需要使用 Bing Subscription Key,需要在azure port中申请试用bing search -# 具体申请方式请见 -# https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/create-bing-search-service-resource -# 使用python创建bing api 搜索实例详见: -# https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/quickstarts/rest/python -BING_SEARCH_URL = "https://api.bing.microsoft.com/v7.0/search" -# 注意不是bing Webmaster Tools的api key, - -# 此外,如果是在服务器上,报Failed to establish a new connection: [Errno 110] Connection timed out -# 是因为服务器加了防火墙,需要联系管理员加白名单,如果公司的服务器的话,就别想了GG -BING_SUBSCRIPTION_KEY = "" - -# 是否开启中文标题加强,以及标题增强的相关配置 -# 通过增加标题判断,判断哪些文本为标题,并在metadata中进行标记; -# 然后将文本与往上一级的标题进行拼合,实现文本信息的增强。 -ZH_TITLE_ENHANCE = False - -log_verbose = False \ No newline at end of file +VLLM_MODEL_DICT = VLLM_MODEL_DICT_c \ No newline at end of file diff --git a/configs/server_config.py.example b/configs/server_config.py.example index f4d64bb..45d4b61 100644 --- a/configs/server_config.py.example +++ b/configs/server_config.py.example @@ -12,6 +12,7 @@ SANDBOX_DO_REMOTE = True NO_REMOTE_API = True # 各服务器默认绑定host DEFAULT_BIND_HOST = "127.0.0.1" +os.environ["DEFAULT_BIND_HOST"] = DEFAULT_BIND_HOST # CONTRAINER_NAME = "devopsgpt_webui" diff --git a/dev_opsgpt/codechat/code_search/cypher_generator.py b/dev_opsgpt/codechat/code_search/cypher_generator.py deleted file mode 100644 index 16f52e4..0000000 --- a/dev_opsgpt/codechat/code_search/cypher_generator.py +++ /dev/null @@ -1,63 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: cypher_generator.py -@time: 2023/11/24 上午10:17 -@desc: -''' -from loguru import logger - -from dev_opsgpt.llm_models.openai_model import getChatModel -from dev_opsgpt.utils.postprocess import replace_lt_gt -from langchain.schema import ( - HumanMessage, -) -from langchain.chains.graph_qa.prompts import NGQL_GENERATION_PROMPT - - -schema = ''' -Node properties: [{'tag': 'package', 'properties': []}, {'tag': 'class', 'properties': []}, {'tag': 'method', 'properties': []}] -Edge properties: [{'edge': 'contain', 'properties': []}, {'edge': 'depend', 'properties': []}] -Relationships: ['(:package)-[:contain]->(:class)', '(:class)-[:contain]->(:method)', '(:package)-[:contain]->(:package)'] -''' - - -class CypherGenerator: - def __init__(self): - self.model = getChatModel() - - def get_cypher(self, query: str): - ''' - get cypher from query - @param query: - @return: - ''' - content = NGQL_GENERATION_PROMPT.format(schema=schema, question=query) - - ans = '' - message = [HumanMessage(content=content)] - chat_res = self.model.predict_messages(message) - ans = chat_res.content - - ans = replace_lt_gt(ans) - - ans = self.post_process(ans) - return ans - - def post_process(self, cypher_res: str): - ''' - 判断是否为正确的 cypher - @param cypher_res: - @return: - ''' - if '(' not in cypher_res or ')' not in cypher_res: - return '' - - return cypher_res - - -if __name__ == '__main__': - query = '代码中一共有多少个类' - cg = CypherGenerator(engine='openai') - - cg.get_cypher(query) diff --git a/dev_opsgpt/connector/agents/check_agent.py b/dev_opsgpt/connector/agents/check_agent.py deleted file mode 100644 index 3bdcb5c..0000000 --- a/dev_opsgpt/connector/agents/check_agent.py +++ /dev/null @@ -1,110 +0,0 @@ -from pydantic import BaseModel -from typing import List, Union -import re -import json -import traceback -import copy -from loguru import logger - -from langchain.prompts.chat import ChatPromptTemplate - -from dev_opsgpt.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus -) -from dev_opsgpt.llm_models import getChatModel -from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT, CONTEXT_PROMPT_INPUT, QUERY_CONTEXT_PROMPT_INPUT - -from .base_agent import BaseAgent - - -class CheckAgent(BaseAgent): - def __init__( - self, - role: Role, - task: Task = None, - memory: Memory = None, - chat_turn: int = 1, - do_search: bool = False, - do_doc_retrieval: bool = False, - do_tool_retrieval: bool = False, - temperature: float = 0.2, - stop: Union[List[str], str] = None, - do_filter: bool = True, - do_use_self_memory: bool = True, - focus_agents: List[str] = [], - focus_message_keys: List[str] = [], - # prompt_mamnger: PromptManager - ): - - super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, - do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, - focus_agents, focus_message_keys - ) - - def create_prompt( - self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: - ''' - role\task\tools\docs\memory - ''' - # - doc_infos = self.create_doc_prompt(query) - code_infos = self.create_codedoc_prompt(query) - # - formatted_tools, tool_names, _ = self.create_tools_prompt(query) - task_prompt = self.create_task_prompt(query) - background_prompt = self.create_background_prompt(background) - history_prompt = self.create_history_prompt(history) - selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") - - # react 流程是自身迭代过程,另外二次触发的是需要作为历史对话信息 - # input_query = react_memory.to_tuple_messages(content_key="step_content") - input_query = query.input_query - - # logger.debug(f"{self.role.role_name} extra_system_prompt: {self.role.role_prompt}") - # logger.debug(f"{self.role.role_name} input_query: {input_query}") - # logger.debug(f"{self.role.role_name} doc_infos: {doc_infos}") - # logger.debug(f"{self.role.role_name} tool_names: {tool_names}") - # prompt += "\n" + CHECK_PROMPT_INPUT.format(**{"query": input_query}) - # prompt.format(**{"query": input_query}) - - # extra_system_prompt = self.role.role_prompt - prompt = self.role.role_prompt.format(**{"query": input_query, "formatted_tools": formatted_tools, "tool_names": tool_names}) - - if "**Context:**" in self.role.role_prompt: - # logger.debug(f"parsed_output_list: {query.parsed_output_list}") - # input_query = "'''" + "\n".join([f"*{k}*\n{v}" for i in background.get_parserd_output_list() for k,v in i.items() if "Action Status" !=k]) + "'''" - context = "\n".join([f"*{k}*\n{v}" for i in background.get_parserd_output_list() for k,v in i.items() if "Action Status" !=k]) - # logger.debug(context) - # logger.debug(f"parsed_output_list: {t}") - prompt += "\n" + QUERY_CONTEXT_PROMPT_INPUT.format(**{"query": query.origin_query, "context": context}) - else: - prompt += "\n" + REACT_PROMPT_INPUT.format(**{"query": input_query}) - - - task = query.task or self.task - if task_prompt is not None: - prompt += "\n" + task.task_prompt - - # if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": - # prompt += f"\n知识库信息: {doc_infos}" - - # if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": - # prompt += f"\n代码库信息: {code_infos}" - - # if background_prompt: - # prompt += "\n" + background_prompt - - # if history_prompt: - # prompt += "\n" + history_prompt - - # if selfmemory_prompt: - # prompt += "\n" + selfmemory_prompt - - # prompt = extra_system_prompt.format(**{"query": input_query, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) - while "{{" in prompt or "}}" in prompt: - prompt = prompt.replace("{{", "{") - prompt = prompt.replace("}}", "}") - - # logger.debug(f"{self.role.role_name} prompt: {prompt}") - return prompt - diff --git a/dev_opsgpt/connector/agents/executor_agent.py b/dev_opsgpt/connector/agents/executor_agent.py deleted file mode 100644 index c249cfc..0000000 --- a/dev_opsgpt/connector/agents/executor_agent.py +++ /dev/null @@ -1,217 +0,0 @@ -from pydantic import BaseModel -from typing import List, Union, Tuple, Any -import re -import json -import traceback -import copy -from loguru import logger - -from langchain.prompts.chat import ChatPromptTemplate - -from dev_opsgpt.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus -) -from dev_opsgpt.llm_models import getChatModel -from dev_opsgpt.connector.configs.prompts import EXECUTOR_PROMPT_INPUT, BEGIN_PROMPT_INPUT -from dev_opsgpt.connector.utils import parse_section - -from .base_agent import BaseAgent - - -class ExecutorAgent(BaseAgent): - def __init__( - self, - role: Role, - task: Task = None, - memory: Memory = None, - chat_turn: int = 1, - do_search: bool = False, - do_doc_retrieval: bool = False, - do_tool_retrieval: bool = False, - temperature: float = 0.2, - stop: Union[List[str], str] = None, - do_filter: bool = True, - do_use_self_memory: bool = True, - focus_agents: List[str] = [], - focus_message_keys: List[str] = [], - # prompt_mamnger: PromptManager - ): - - super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, - do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, - focus_agents, focus_message_keys - ) - self.do_all_task = True # run all tasks - - def arun(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> Message: - '''agent reponse from multi-message''' - # insert query into memory - task_executor_memory = Memory(messages=[]) - # insert query - output_message = Message( - role_name=self.role.role_name, - role_type="ai", #self.role.role_type, - role_content=query.input_query, - step_content="", - input_query=query.input_query, - tools=query.tools, - parsed_output_list=[query.parsed_output], - customed_kargs=query.customed_kargs - ) - - self_memory = self.memory if self.do_use_self_memory else None - - plan_step = int(query.parsed_output.get("PLAN_STEP", 0)) - # 如果存在plan字段且plan字段为str的时候 - if "PLAN" not in query.parsed_output or isinstance(query.parsed_output.get("PLAN", []), str) or plan_step >= len(query.parsed_output.get("PLAN", [])): - query_c = copy.deepcopy(query) - query_c = self.start_action_step(query_c) - query_c.parsed_output = {"Question": query_c.input_query} - task_executor_memory.append(query_c) - for output_message, task_executor_memory in self._arun_step(output_message, query_c, self_memory, history, background, memory_pool, task_executor_memory): - pass - # task_executor_memory.append(query_c) - # content = "the execution step of the plan is exceed the planned scope." - # output_message.parsed_dict = {"Thought": content, "Action Status": "finished", "Action": content} - # task_executor_memory.append(output_message) - - elif "PLAN" in query.parsed_output: - logger.debug(f"{query.parsed_output['PLAN']}") - if self.do_all_task: - # run all tasks step by step - for task_content in query.parsed_output["PLAN"][plan_step:]: - # create your llm prompt - query_c = copy.deepcopy(query) - query_c.parsed_output = {"Question": task_content} - task_executor_memory.append(query_c) - for output_message, task_executor_memory in self._arun_step(output_message, query_c, self_memory, history, background, memory_pool, task_executor_memory): - pass - yield output_message - else: - query_c = copy.deepcopy(query) - query_c = self.start_action_step(query_c) - task_content = query_c.parsed_output["PLAN"][plan_step] - query_c.parsed_output = {"Question": task_content} - task_executor_memory.append(query_c) - for output_message, task_executor_memory in self._arun_step(output_message, query_c, self_memory, history, background, memory_pool, task_executor_memory): - pass - output_message.parsed_output.update({"CURRENT_STEP": plan_step}) - # update self_memory - self.append_history(query) - self.append_history(output_message) - # logger.info(f"{self.role.role_name} currenct question: {output_message.input_query}\nllm_executor_run: {output_message.step_content}") - # logger.info(f"{self.role.role_name} currenct parserd_output_list: {output_message.parserd_output_list}") - output_message.input_query = output_message.role_content - # end_action_step - output_message = self.end_action_step(output_message) - # update memory pool - memory_pool.append(output_message) - yield output_message - - def _arun_step(self, output_message: Message, query: Message, self_memory: Memory, - history: Memory, background: Memory, memory_pool: Memory, - react_memory: Memory) -> Union[Message, Memory]: - '''execute the llm predict by created prompt''' - prompt = self.create_prompt(query, self_memory, history, background, memory_pool=memory_pool, react_memory=react_memory) - content = self.llm.predict(prompt) - # logger.debug(f"{self.role.role_name} prompt: {prompt}") - logger.debug(f"{self.role.role_name} content: {content}") - - output_message.role_content = content - output_message.step_content += "\n"+output_message.role_content - - output_message = self.message_utils.parser(output_message) - # according the output to choose one action for code_content or tool_content - output_message, observation_message = self.message_utils.step_router(output_message) - # logger.debug(f"{self.role.role_name} content: {content}") - # update parserd_output_list - output_message.parsed_output_list.append(output_message.parsed_output) - - react_message = copy.deepcopy(output_message) - react_memory.append(react_message) - if observation_message: - react_memory.append(observation_message) - output_message.parsed_output_list.append(observation_message.parsed_output) - logger.debug(f"{observation_message.role_name} content: {observation_message.role_content}") - yield output_message, react_memory - - def create_prompt( - self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, react_memory: Memory = None, prompt_mamnger=None) -> str: - ''' - role\task\tools\docs\memory - ''' - # - doc_infos = self.create_doc_prompt(query) - code_infos = self.create_codedoc_prompt(query) - # - formatted_tools, tool_names, _ = self.create_tools_prompt(query) - task_prompt = self.create_task_prompt(query) - background_prompt = self.create_background_prompt(background, control_key="step_content") - history_prompt = self.create_history_prompt(history) - selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") - - # - memory_pool_select_by_agent_key = self.select_memory_by_agent_key(memory_pool) - memory_pool_select_by_agent_key_context = '\n\n'.join([ - f"*{k}*\n{v}" for parsed_output in memory_pool_select_by_agent_key.get_parserd_output_list() for k, v in parsed_output.items() if k not in ['Action Status'] - ]) - - DocInfos = "" - if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": - DocInfos += f"\nDocument Information: {doc_infos}" - - if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": - DocInfos += f"\nCodeBase Infomation: {code_infos}" - - # extra_system_prompt = self.role.role_prompt - prompt = self.role.role_prompt.format(**{"formatted_tools": formatted_tools, "tool_names": tool_names}) - - # input_query = react_memory.to_tuple_messages(content_key="role_content") - # logger.debug(f"get_parserd_dict {react_memory.get_parserd_output()}") - input_query = "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()]) - # input_query = query.input_query + "\n".join([f"{v}" for k, v in input_query if v]) - last_agent_parsed_output = "\n".join(["\n".join([f"*{k}*\n{v}" for k,v in _dict.items()]) for _dict in query.parsed_output_list]) - react_parsed_output = "\n".join(["\n".join([f"*{k}_context*\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()[:-1]]) - # - prompt += "\n" + BEGIN_PROMPT_INPUT - - input_keys = parse_section(self.role.role_prompt, 'Input Format') - if input_keys: - for input_key in input_keys: - if input_key == "Origin Query": - prompt += "\n**Origin Query:**\n" + query.origin_query - elif input_key == "DocInfos": - prompt += "\n**DocInfos:**\n" + DocInfos - elif input_key == "Context": - if self.focus_agents and memory_pool_select_by_agent_key_context: - context = memory_pool_select_by_agent_key_context - else: - context = last_agent_parsed_output - prompt += "\n**Context:**\n" + context + f"\n{react_parsed_output}" - elif input_key == "Question": - prompt += "\n**Question:**\n" + query.parsed_output.get("Question") - else: - prompt += "\n" + input_query - - task = query.task or self.task - # if task_prompt is not None: - # prompt += "\n" + task.task_prompt - - # if selfmemory_prompt: - # prompt += "\n" + selfmemory_prompt - - # if background_prompt: - # prompt += "\n" + background_prompt - - # if history_prompt: - # prompt += "\n" + history_prompt - - # prompt = extra_system_prompt.format(**{"query": input_query, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) - while "{{" in prompt or "}}" in prompt: - prompt = prompt.replace("{{", "{") - prompt = prompt.replace("}}", "}") - return prompt - - def set_task(self, do_all_task): - '''set task exec type''' - self.do_all_task = do_all_task \ No newline at end of file diff --git a/dev_opsgpt/connector/agents/selector_agent.py b/dev_opsgpt/connector/agents/selector_agent.py deleted file mode 100644 index f06703e..0000000 --- a/dev_opsgpt/connector/agents/selector_agent.py +++ /dev/null @@ -1,165 +0,0 @@ -from pydantic import BaseModel -from typing import List, Union -import re -import json -import traceback -import copy -import random -from loguru import logger - -from langchain.prompts.chat import ChatPromptTemplate - -from dev_opsgpt.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus -) -from dev_opsgpt.llm_models import getChatModel -from dev_opsgpt.connector.configs.prompts import BASE_PROMPT_INPUT, QUERY_CONTEXT_DOC_PROMPT_INPUT, BEGIN_PROMPT_INPUT -from dev_opsgpt.connector.utils import parse_section - -from .base_agent import BaseAgent - - -class SelectorAgent(BaseAgent): - def __init__( - self, - role: Role, - task: Task = None, - memory: Memory = None, - chat_turn: int = 1, - do_search: bool = False, - do_doc_retrieval: bool = False, - do_tool_retrieval: bool = False, - temperature: float = 0.2, - stop: Union[List[str], str] = None, - do_filter: bool = True, - do_use_self_memory: bool = True, - focus_agents: List[str] = [], - focus_message_keys: List[str] = [], - group_agents: List[BaseAgent] = [], - # prompt_mamnger: PromptManager - ): - - super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, - do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, - focus_agents, focus_message_keys - ) - self.group_agents = group_agents - - def arun(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> Message: - '''agent reponse from multi-message''' - # insert query into memory - query_c = copy.deepcopy(query) - query = self.start_action_step(query) - self_memory = self.memory if self.do_use_self_memory else None - # create your llm prompt - prompt = self.create_prompt(query_c, self_memory, history, background, memory_pool=memory_pool) - content = self.llm.predict(prompt) - logger.debug(f"{self.role.role_name} prompt: {prompt}") - logger.debug(f"{self.role.role_name} content: {content}") - - # select agent - select_message = Message( - role_name=self.role.role_name, - role_type="ai", #self.role.role_type, - role_content=content, - step_content=content, - input_query=query_c.input_query, - tools=query_c.tools, - parsed_output_list=[query.parsed_output] - ) - # common parse llm' content to message - select_message = self.message_utils.parser(select_message) - if self.do_filter: - select_message = self.message_utils.filter(select_message) - - output_message = None - if select_message.parsed_output.get("Role", "") in [agent.role.role_name for agent in self.group_agents]: - for agent in self.group_agents: - if agent.role.role_name == select_message.parsed_output.get("Role", ""): - break - for output_message in agent.arun(query, history, background=background, memory_pool=memory_pool): - pass - # update self_memory - self.append_history(query_c) - self.append_history(output_message) - logger.info(f"{agent.role.role_name} currenct question: {output_message.input_query}\nllm_step_run: {output_message.role_content}") - output_message.input_query = output_message.role_content - output_message.parsed_output_list.append(output_message.parsed_output) - # - output_message = self.end_action_step(output_message) - # update memory pool - memory_pool.append(output_message) - yield output_message or select_message - - def create_prompt( - self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: - ''' - role\task\tools\docs\memory - ''' - # - doc_infos = self.create_doc_prompt(query) - code_infos = self.create_codedoc_prompt(query) - # - formatted_tools, tool_names, tools_descs = self.create_tools_prompt(query) - agent_names, agents = self.create_agent_names() - task_prompt = self.create_task_prompt(query) - background_prompt = self.create_background_prompt(background) - history_prompt = self.create_history_prompt(history) - selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") - - - DocInfos = "" - if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": - DocInfos += f"\nDocument Information: {doc_infos}" - - if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": - DocInfos += f"\nCodeBase Infomation: {code_infos}" - - input_query = query.input_query - logger.debug(f"{self.role.role_name} input_query: {input_query}") - prompt = self.role.role_prompt.format(**{"agent_names": agent_names, "agents": agents, "formatted_tools": tools_descs, "tool_names": tool_names}) - # - memory_pool_select_by_agent_key = self.select_memory_by_agent_key(memory_pool) - memory_pool_select_by_agent_key_context = '\n\n'.join([f"*{k}*\n{v}" for parsed_output in memory_pool_select_by_agent_key.get_parserd_output_list() for k, v in parsed_output.items() if k not in ['Action Status']]) - - input_keys = parse_section(self.role.role_prompt, 'Input Format') - # - prompt += "\n" + BEGIN_PROMPT_INPUT - for input_key in input_keys: - if input_key == "Origin Query": - prompt += "\n**Origin Query:**\n" + query.origin_query - elif input_key == "Context": - context = "\n".join([f"*{k}*\n{v}" for i in query.parsed_output_list for k,v in i.items() if "Action Status" !=k]) - if history: - context = history_prompt + "\n" + context - if not context: - context = "there is no context" - - if self.focus_agents and memory_pool_select_by_agent_key_context: - context = memory_pool_select_by_agent_key_context - prompt += "\n**Context:**\n" + context + "\n" + input_query - elif input_key == "DocInfos": - prompt += "\n**DocInfos:**\n" + DocInfos - elif input_key == "Question": - prompt += "\n**Question:**\n" + input_query - - while "{{" in prompt or "}}" in prompt: - prompt = prompt.replace("{{", "{") - prompt = prompt.replace("}}", "}") - - # logger.debug(f"{self.role.role_name} prompt: {prompt}") - return prompt - - def create_agent_names(self): - random.shuffle(self.group_agents) - agent_names = ", ".join([f'{agent.role.role_name}' for agent in self.group_agents]) - agent_descs = [] - for agent in self.group_agents: - role_desc = agent.role.role_prompt.split("####")[1] - while "\n\n" in role_desc: - role_desc = role_desc.replace("\n\n", "\n") - role_desc = role_desc.replace("\n", ",") - - agent_descs.append(f'"role name: {agent.role.role_name}\nrole description: {role_desc}"') - - return agent_names, "\n".join(agent_descs) \ No newline at end of file diff --git a/dev_opsgpt/connector/chains/chains.py b/dev_opsgpt/connector/chains/chains.py deleted file mode 100644 index a8536e6..0000000 --- a/dev_opsgpt/connector/chains/chains.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import List -from loguru import logger -import copy -from dev_opsgpt.connector.agents import BaseAgent -from .base_chain import BaseChain - -from dev_opsgpt.connector.agents import BaseAgent, CheckAgent -from dev_opsgpt.connector.schema import ( - Memory, Role, Message, ActionStatus, ChainConfig, - load_role_configs -) - - - -class ExecutorRefineChain(BaseChain): - - def __init__(self, agents: List[BaseAgent], do_code_exec: bool = False) -> None: - super().__init__(agents, do_code_exec) diff --git a/dev_opsgpt/connector/configs/__init__.py b/dev_opsgpt/connector/configs/__init__.py deleted file mode 100644 index 873ba85..0000000 --- a/dev_opsgpt/connector/configs/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .agent_config import AGETN_CONFIGS -from .chain_config import CHAIN_CONFIGS -from .phase_config import PHASE_CONFIGS - -__all__ = [ - "AGETN_CONFIGS", "CHAIN_CONFIGS", "PHASE_CONFIGS" - ] \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/agent_selector_template_prompt.py b/dev_opsgpt/connector/configs/prompts/agent_selector_template_prompt.py deleted file mode 100644 index 1b7fbac..0000000 --- a/dev_opsgpt/connector/configs/prompts/agent_selector_template_prompt.py +++ /dev/null @@ -1,24 +0,0 @@ -SELECTOR_AGENT_TEMPLATE_PROMPT = """#### Role Selector Assistance Guidance - -Your goal is to match the user's initial Origin Query) with the role that will best facilitate a solution, taking into account all relevant context (Context) provided. - -When you need to select the appropriate role for handling a user's query, carefully read the provided role names, role descriptions and tool list. - -You can use these tools:\n{formatted_tools} - -Please ensure your selection is one of the listed roles. Available roles for selection: -{agents} - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the context history to determine if Origin Query has been achieved. - -#### Response Output Format - -**Thoughts:** think the reason of selecting the role step by step - -**Role:** Select the role name. such as {agent_names} - -""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/executor_template_prompt.py b/dev_opsgpt/connector/configs/prompts/executor_template_prompt.py deleted file mode 100644 index e41564e..0000000 --- a/dev_opsgpt/connector/configs/prompts/executor_template_prompt.py +++ /dev/null @@ -1,33 +0,0 @@ -EXECUTOR_TEMPLATE_PROMPT = """#### Writing Code Assistance Guidance - -When users need help with coding, your role is to provide precise and effective guidance. - -Write the code step by step, showing only the part necessary to solve the current problem. - -Each reply should contain only the code required for the current step. - -#### Response Process - -**Question:** First, clarify the problem to be solved. - -**Thoughts:** Based on the question and observations above, provide the plan for executing this step. - -**Action Status:** Set to 'stoped' or 'code_executing'. If it's 'stoped', the next action is to provide the final answer to the original question. If it's 'code_executing', the next step is to write the code. - -**Action:** Code according to your thoughts. Use this format for code: - -```python -# Write your code here -``` - -**Observation:** Check the results and effects of the executed code. - -... (Repeat this Question/Thoughts/Action/Observation cycle as needed) - -**Thoughts:** I now know the final answer - -**Action Status:** Set to 'stoped' - -**Action:** The final answer to the original input question - -""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_code_prompt.py b/dev_opsgpt/connector/configs/prompts/react_code_prompt.py deleted file mode 100644 index b295ff1..0000000 --- a/dev_opsgpt/connector/configs/prompts/react_code_prompt.py +++ /dev/null @@ -1,34 +0,0 @@ - - -REACT_CODE_PROMPT = """#### Writing Code Assistance Guidance - -When users need help with coding, your role is to provide precise and effective guidance. - -Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step. - -#### Response Process - -**Question:** First, clarify the problem to be solved. - -**Thoughts:** Based on the question and observations above, provide the plan for executing this step. - -**Action Status:** Set to 'stoped' or 'code_executing'. If it's 'stoped', the action is to provide the final answer to the original question. If it's 'code_executing', the action is to write the code. - -**Action:** -```python -# Write your code here -import os -... -``` - -**Observation:** Check the results and effects of the executed code. - -... (Repeat this Thoughts/Action/Observation cycle as needed) - -**Thoughts:** I now know the final answer - -**Action Status:** Set to 'stoped' - -**Action:** The final answer to the original input question - -""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_template_prompt.py b/dev_opsgpt/connector/configs/prompts/react_template_prompt.py deleted file mode 100644 index 942e7b5..0000000 --- a/dev_opsgpt/connector/configs/prompts/react_template_prompt.py +++ /dev/null @@ -1,31 +0,0 @@ - - -REACT_TEMPLATE_PROMPT = """#### Writing Code Assistance Guidance - -When users need help with coding, your role is to provide precise and effective guidance. Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step. - -#### Response Process - -**Question:** First, clarify the problem to be solved. - -**Thoughts:** Based on the question and observations above, provide the plan for executing this step. - -**Action Status:** Set to 'stoped' or 'code_executing'. If it's 'stoped', the next action is to provide the final answer to the original question. If it's 'code_executing', the next step is to write the code. - -**Action:** Code according to your thoughts. Use this format for code: - -```python -# Write your code here -``` - -**Observation:** Check the results and effects of the executed code. - -... (Repeat this Thoughts/Action/Observation cycle as needed) - -**Thoughts:** I now know the final answer - -**Action Status:** Set to 'stoped' - -**Action:** The final answer to the original input question - -""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_tool_code_prompt.py b/dev_opsgpt/connector/configs/prompts/react_tool_code_prompt.py deleted file mode 100644 index bccd3a0..0000000 --- a/dev_opsgpt/connector/configs/prompts/react_tool_code_prompt.py +++ /dev/null @@ -1,83 +0,0 @@ -REACT_TOOL_AND_CODE_PROMPT = """#### Code and Tool Agent Assistance Guidance - -When users need help with coding or using tools, your role is to provide precise and effective guidance. Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the guidance required for the current step either by tool usage or code. - -#### Tool Infomation - -You can use these tools:\n{formatted_tools} - -Valid "tool_name" value:\n{tool_names} - -#### Response Process - -**Question:** Start by understanding the input question to be answered. - -**Thoughts:** Considering the user's question, previously executed steps, and the plan, decide whether the current step requires the use of a tool or code_executing. Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. If a tool can be used, provide its name and parameters. If code_executing is required, outline the plan for executing this step. - -**Action Status:** stoped, tool_using, or code_executing. (Choose one from these three statuses.) -If the task is done, set it to 'stoped'. -If using a tool, set it to 'tool_using'. -If writing code, set it to 'code_executing'. - -**Action:** - -If using a tool, use the tools by formatting the tool action in JSON from Question and Observation:. The format should be: -```json -{{ - "tool_name": "$TOOL_NAME", - "tool_params": "$INPUT" -}} -``` - -If the problem cannot be solved with a tool at the moment, then proceed to solve the issue using code. Output the following format to execute the code: - -```python -Write your code here -``` - -**Observation:** Check the results and effects of the executed action. - -... (Repeat this Thoughts/Action/Observation cycle as needed) - -**Thoughts:** Conclude the final response to the input question. - -**Action Status:** stoped - -**Action:** The final answer or guidance to the original input question. -""" - -# REACT_TOOL_AND_CODE_PROMPT = """你是一个使用工具与代码的助手。 -# 如果现有工具不足以完成整个任务,请不要添加不存在的工具,只使用现有工具完成可能的部分。 -# 如果当前步骤不能使用工具完成,将由代码来完成。 -# 有效的"action"值为:"stoped"(已经完成用户的任务) 、 "tool_using" (使用工具来回答问题) 或 'code_executing'(结合总结下述思维链过程编写下一步的可执行代码)。 -# 尽可能地以有帮助和准确的方式回应人类,你可以使用以下工具: -# {formatted_tools} -# 如果现在的步骤可以用工具解决问题,请仅在每个$JSON_BLOB中提供一个action,如下所示: -# ``` -# {{{{ -# "action": $ACTION, -# "tool_name": $TOOL_NAME -# "tool_params": $INPUT -# }}}} -# ``` -# 若当前无法通过工具解决问题,则使用代码解决问题 -# 请仅在每个$JSON_BLOB中提供一个action,如下所示: -# ``` -# {{{{'action': $ACTION,'code_content': $CODE}}}} -# ``` - -# 按照以下思维链格式进行回应($JSON_BLOB要求符合上述规定): -# 问题:输入问题以回答 -# 思考:考虑之前和之后的步骤 -# 行动: -# ``` -# $JSON_BLOB -# ``` -# 观察:行动结果 -# ...(重复思考/行动/观察N次) -# 思考:我知道该如何回应 -# 行动: -# ``` -# $JSON_BLOB -# ``` -# """ \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/summary_template_prompt.py b/dev_opsgpt/connector/configs/prompts/summary_template_prompt.py deleted file mode 100644 index 0659cde..0000000 --- a/dev_opsgpt/connector/configs/prompts/summary_template_prompt.py +++ /dev/null @@ -1,20 +0,0 @@ -CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类,根据“背景信息”中的有效信息回答问题, -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (根据背景信息回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION, 'content': '根据背景信息回答问题'}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动: -``` -$JSON_BLOB -``` -""" - -CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类 -根据“背景信息”中的有效信息回答问题,同时展现解答的过程和内容 -若能根“背景信息”回答问题,则直接回答 -否则,总结“背景信息”的内容 -""" \ No newline at end of file diff --git a/dev_opsgpt/connector/message_process.py b/dev_opsgpt/connector/message_process.py deleted file mode 100644 index 69d27a2..0000000 --- a/dev_opsgpt/connector/message_process.py +++ /dev/null @@ -1,396 +0,0 @@ -import re, traceback, uuid, copy, json, os -from loguru import logger - - -from configs.server_config import SANDBOX_SERVER -from configs.model_config import JUPYTER_WORK_PATH -from dev_opsgpt.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus, CodeDoc, Doc -) -from dev_opsgpt.tools import DDGSTool, DocRetrieval, CodeRetrieval -from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse - - -class MessageUtils: - def __init__(self, role: Role = None) -> None: - self.role = role - self.codebox = PyCodeBox( - remote_url=SANDBOX_SERVER["url"], - remote_ip=SANDBOX_SERVER["host"], - remote_port=SANDBOX_SERVER["port"], - token="mytoken", - do_code_exe=True, - do_remote=SANDBOX_SERVER["do_remote"], - do_check_net=False - ) - - def filter(self, message: Message, stop=None) -> Message: - - tool_params = self.parser_spec_key(message.role_content, "tool_params") - code_content = self.parser_spec_key(message.role_content, "code_content") - plan = self.parser_spec_key(message.role_content, "plan") - plans = self.parser_spec_key(message.role_content, "plans", do_search=False) - content = self.parser_spec_key(message.role_content, "content", do_search=False) - - # logger.debug(f"tool_params: {tool_params}, code_content: {code_content}, plan: {plan}, plans: {plans}, content: {content}") - role_content = tool_params or code_content or plan or plans or content - message.role_content = role_content or message.role_content - return message - - def inherit_extrainfo(self, input_message: Message, output_message: Message): - output_message.db_docs = input_message.db_docs - output_message.search_docs = input_message.search_docs - output_message.code_docs = input_message.code_docs - output_message.figures.update(input_message.figures) - output_message.origin_query = input_message.origin_query - output_message.code_engine_name = input_message.code_engine_name - - output_message.doc_engine_name = input_message.doc_engine_name - output_message.search_engine_name = input_message.search_engine_name - output_message.top_k = input_message.top_k - output_message.score_threshold = input_message.score_threshold - output_message.cb_search_type = input_message.cb_search_type - output_message.do_doc_retrieval = input_message.do_doc_retrieval - output_message.do_code_retrieval = input_message.do_code_retrieval - output_message.do_tool_retrieval = input_message.do_tool_retrieval - # - output_message.tools = input_message.tools - output_message.agents = input_message.agents - # 存在bug导致相同key被覆盖 - output_message.customed_kargs.update(input_message.customed_kargs) - return output_message - - def inherit_baseparam(self, input_message: Message, output_message: Message): - # 只更新参数 - output_message.doc_engine_name = input_message.doc_engine_name - output_message.search_engine_name = input_message.search_engine_name - output_message.top_k = input_message.top_k - output_message.score_threshold = input_message.score_threshold - output_message.cb_search_type = input_message.cb_search_type - output_message.do_doc_retrieval = input_message.do_doc_retrieval - output_message.do_code_retrieval = input_message.do_code_retrieval - output_message.do_tool_retrieval = input_message.do_tool_retrieval - # - output_message.tools = input_message.tools - output_message.agents = input_message.agents - # 存在bug导致相同key被覆盖 - output_message.customed_kargs.update(input_message.customed_kargs) - return output_message - - def get_extrainfo_step(self, message: Message, do_search, do_doc_retrieval, do_code_retrieval, do_tool_retrieval) -> Message: - '''''' - if do_search: - message = self.get_search_retrieval(message) - - if do_doc_retrieval: - message = self.get_doc_retrieval(message) - - if do_code_retrieval: - input_message = self.get_code_retrieval(message) - - if do_tool_retrieval: - message = self.get_tool_retrieval(message) - - return message - - def get_search_retrieval(self, message: Message,) -> Message: - SEARCH_ENGINES = {"duckduckgo": DDGSTool} - search_docs = [] - for idx, doc in enumerate(SEARCH_ENGINES["duckduckgo"].run(message.role_content, 3)): - doc.update({"index": idx}) - search_docs.append(Doc(**doc)) - message.search_docs = search_docs - return message - - def get_doc_retrieval(self, message: Message) -> Message: - query = message.role_content - knowledge_basename = message.doc_engine_name - top_k = message.top_k - score_threshold = message.score_threshold - if knowledge_basename: - docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold) - message.db_docs = [Doc(**doc) for doc in docs] - return message - - def get_code_retrieval(self, message: Message) -> Message: - # DocRetrieval.run("langchain是什么", "DSADSAD") - query = message.input_query - code_engine_name = message.code_engine_name - history_node_list = message.history_node_list - code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list, search_type=message.cb_search_type) - message.code_docs = [CodeDoc(**doc) for doc in code_docs] - return message - - def get_tool_retrieval(self, message: Message) -> Message: - return message - - def step_router(self, message: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> tuple[Message, ...]: - '''''' - # message = self.parser(message) - # logger.debug(f"message.action_status: {message.action_status}") - observation_message = None - if message.action_status == ActionStatus.CODE_EXECUTING: - message, observation_message = self.code_step(message) - elif message.action_status == ActionStatus.TOOL_USING: - message, observation_message = self.tool_step(message) - elif message.action_status == ActionStatus.CODING2FILE: - self.save_code2file(message) - elif message.action_status == ActionStatus.CODE_RETRIEVAL: - pass - elif message.action_status == ActionStatus.CODING: - pass - - return message, observation_message - - def code_step(self, message: Message) -> Message: - '''execute code''' - # logger.debug(f"message.role_content: {message.role_content}, message.code_content: {message.code_content}") - code_answer = self.codebox.chat('```python\n{}```'.format(message.code_content)) - code_prompt = f"The return error after executing the above code is {code_answer.code_exe_response},need to recover" \ - if code_answer.code_exe_type == "error" else f"The return information after executing the above code is {code_answer.code_exe_response}" - - observation_message = Message( - role_name="observation", - role_type="func", #self.role.role_type, - role_content="", - step_content="", - input_query=message.code_content, - ) - uid = str(uuid.uuid1()) - if code_answer.code_exe_type == "image/png": - message.figures[uid] = code_answer.code_exe_response - message.code_answer = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - message.observation = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - message.step_content += f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - # message.role_content += f"\n**Observation:**:执行上述代码后生成一张图片, 图片名为{uid}\n" - observation_message.role_content = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - observation_message.parsed_output = {"Observation": f"The return figure name is {uid} after executing the above code."} - else: - message.code_answer = code_answer.code_exe_response - message.observation = code_answer.code_exe_response - message.step_content += f"\n**Observation:**: {code_prompt}\n" - # message.role_content += f"\n**Observation:**: {code_prompt}\n" - observation_message.role_content = f"\n**Observation:**: {code_prompt}\n" - observation_message.parsed_output = {"Observation": code_prompt} - # logger.info(f"**Observation:** {message.action_status}, {message.observation}") - return message, observation_message - - def tool_step(self, message: Message) -> Message: - '''execute tool''' - # logger.debug(f"{message}") - observation_message = Message( - role_name="observation", - role_type="function", #self.role.role_type, - role_content="\n**Observation:** there is no tool can execute\n" , - step_content="", - input_query=str(message.tool_params), - tools=message.tools, - ) - # logger.debug(f"message: {message.action_status}, {message.tool_name}, {message.tool_params}") - tool_names = [tool.name for tool in message.tools] - if message.tool_name not in tool_names: - message.tool_answer = "\n**Observation:** there is no tool can execute\n" - message.observation = "\n**Observation:** there is no tool can execute\n" - # message.role_content += f"\n**Observation:**: 不存在可以执行的tool\n" - message.step_content += f"\n**Observation:** there is no tool can execute\n" - observation_message.role_content = f"\n**Observation:** there is no tool can execute\n" - observation_message.parsed_output = {"Observation": "there is no tool can execute\n"} - for tool in message.tools: - if tool.name == message.tool_name: - tool_res = tool.func(**message.tool_params.get("tool_params", {})) - logger.debug(f"tool_res {tool_res}") - message.tool_answer = tool_res - message.observation = tool_res - # message.role_content += f"\n**Observation:**: {tool_res}\n" - message.step_content += f"\n**Observation:** {tool_res}\n" - observation_message.role_content = f"\n**Observation:** {tool_res}\n" - observation_message.parsed_output = {"Observation": tool_res} - break - - # logger.info(f"**Observation:** {message.action_status}, {message.observation}") - return message, observation_message - - def parser(self, message: Message) -> Message: - '''''' - content = message.role_content - parser_keys = ["action", "code_content", "code_filename", "tool_params", "plans"] - try: - s_json = self._parse_json(content) - message.action_status = s_json.get("action") - message.code_content = s_json.get("code_content") - message.tool_params = s_json.get("tool_params") - message.tool_name = s_json.get("tool_name") - message.code_filename = s_json.get("code_filename") - message.plans = s_json.get("plans") - # for parser_key in parser_keys: - # message.action_status = content.get(parser_key) - except Exception as e: - # logger.warning(f"{traceback.format_exc()}") - def parse_text_to_dict(text): - # Define a regular expression pattern to capture the key and value - main_pattern = r"\*\*(.+?):\*\*\s*(.*?)\s*(?=\*\*|$)" - list_pattern = r'```python\n(.*?)```' - - # Use re.findall to find all main matches in the text - main_matches = re.findall(main_pattern, text, re.DOTALL) - - # Convert main matches to a dictionary - parsed_dict = {key.strip(): value.strip() for key, value in main_matches} - - for k, v in parsed_dict.items(): - for pattern in [list_pattern]: - if "PLAN" != k: continue - v = v.replace("```list", "```python") - match_value = re.search(pattern, v, re.DOTALL) - if match_value: - # Add the code block to the dictionary - parsed_dict[k] = eval(match_value.group(1).strip()) - break - - return parsed_dict - - def extract_content_from_backticks(text): - code_blocks = [] - lines = text.split('\n') - is_code_block = False - code_block = '' - language = '' - for line in lines: - if line.startswith('```') and not is_code_block: - is_code_block = True - language = line[3:] - code_block = '' - elif line.startswith('```') and is_code_block: - is_code_block = False - code_blocks.append({language.strip(): code_block.strip()}) - elif is_code_block: - code_block += line + '\n' - return code_blocks - - def parse_dict_to_dict(parsed_dict) -> dict: - code_pattern = r'```python\n(.*?)```' - tool_pattern = r'```json\n(.*?)```' - - pattern_dict = {"code": code_pattern, "json": tool_pattern} - spec_parsed_dict = copy.deepcopy(parsed_dict) - for key, pattern in pattern_dict.items(): - for k, text in parsed_dict.items(): - # Search for the code block - if not isinstance(text, str): continue - _match = re.search(pattern, text, re.DOTALL) - if _match: - # Add the code block to the dictionary - try: - spec_parsed_dict[key] = json.loads(_match.group(1).strip()) - except: - spec_parsed_dict[key] = _match.group(1).strip() - break - return spec_parsed_dict - - parsed_dict = parse_text_to_dict(content) - spec_parsed_dict = parse_dict_to_dict(parsed_dict) - action_value = parsed_dict.get('Action Status') - if action_value: - action_value = action_value.lower() - logger.info(f'{message.role_name}: action_value: {action_value}') - # action_value = self._match(r"'action':\s*'([^']*)'", content) if "'action'" in content else self._match(r'"action":\s*"([^"]*)"', content) - - code_content_value = spec_parsed_dict.get('code') - # code_content_value = self._match(r"'code_content':\s*'([^']*)'", content) if "'code_content'" in content else self._match(r'"code_content":\s*"([^"]*)"', content) - filename_value = self._match(r"'code_filename':\s*'([^']*)'", content) if "'code_filename'" in content else self._match(r'"code_filename":\s*"([^"]*)"', content) - - if action_value == 'tool_using': - tool_params_value = spec_parsed_dict.get('json') - else: - tool_params_value = None - # tool_params_value = spec_parsed_dict.get('tool_params') - # tool_params_value = self._match(r"'tool_params':\s*(\{[^{}]*\})", content, do_json=True) if "'tool_params'" in content \ - # else self._match(r'"tool_params":\s*(\{[^{}]*\})', content, do_json=True) - tool_name_value = self._match(r"'tool_name':\s*'([^']*)'", content) if "'tool_name'" in content else self._match(r'"tool_name":\s*"([^"]*)"', content) - plans_value = self._match(r"'plans':\s*(\[.*?\])", content, do_search=False) if "'plans'" in content else self._match(r'"plans":\s*(\[.*?\])', content, do_search=False, ) - # re解析 - message.action_status = action_value or "default" - message.code_content = code_content_value - message.code_filename = filename_value - message.tool_params = tool_params_value - message.tool_name = tool_name_value - message.plans = plans_value - message.parsed_output = parsed_dict - message.spec_parsed_output = spec_parsed_dict - - # logger.debug(f"确认当前的action: {message.action_status}") - - return message - - def parser_spec_key(self, content, key, do_search=True, do_json=False) -> str: - '''''' - key2pattern = { - "'action'": r"'action':\s*'([^']*)'", '"action"': r'"action":\s*"([^"]*)"', - "'code_content'": r"'code_content':\s*'([^']*)'", '"code_content"': r'"code_content":\s*"([^"]*)"', - "'code_filename'": r"'code_filename':\s*'([^']*)'", '"code_filename"': r'"code_filename":\s*"([^"]*)"', - "'tool_params'": r"'tool_params':\s*(\{[^{}]*\})", '"tool_params"': r'"tool_params":\s*(\{[^{}]*\})', - "'tool_name'": r"'tool_name':\s*'([^']*)'", '"tool_name"': r'"tool_name":\s*"([^"]*)"', - "'plans'": r"'plans':\s*(\[.*?\])", '"plans"': r'"plans":\s*(\[.*?\])', - "'content'": r"'content':\s*'([^']*)'", '"content"': r'"content":\s*"([^"]*)"', - } - - s_json = self._parse_json(content) - try: - if s_json and key in s_json: - return str(s_json[key]) - except: - pass - - keystr = f"'{key}'" if f"'{key}'" in content else f'"{key}"' - return self._match(key2pattern.get(keystr, fr"'{key}':\s*'([^']*)'"), content, do_search=do_search, do_json=do_json) - - def _match(self, pattern, s, do_search=True, do_json=False): - try: - if do_search: - match = re.search(pattern, s) - if match: - value = match.group(1).replace("\\n", "\n") - if do_json: - value = json.loads(value) - else: - value = None - else: - match = re.findall(pattern, s, re.DOTALL) - if match: - value = match[0] - if do_json: - value = json.loads(value) - else: - value = None - except Exception as e: - logger.warning(f"{traceback.format_exc()}") - - # logger.debug(f"pattern: {pattern}, s: {s}, match: {match}") - return value - - def _parse_json(self, s): - try: - pattern = r"```([^`]+)```" - match = re.findall(pattern, s) - if match: - return eval(match[0]) - except: - pass - return None - - - def save_code2file(self, message: Message, project_dir=JUPYTER_WORK_PATH): - filename = message.parsed_output.get("SaveFileName") - code = message.spec_parsed_output.get("code") - - for k, v in {">": ">", "≥": ">=", "<": "<", "≤": "<="}.items(): - code = code.replace(k, v) - - file_path = os.path.join(project_dir, filename) - - if not os.path.exists(file_path): - os.makedirs(os.path.dirname(file_path), exist_ok=True) - - with open(file_path, "w") as f: - f.write(code) - \ No newline at end of file diff --git a/dev_opsgpt/connector/phase/base_phase.py b/dev_opsgpt/connector/phase/base_phase.py deleted file mode 100644 index 7280bb9..0000000 --- a/dev_opsgpt/connector/phase/base_phase.py +++ /dev/null @@ -1,256 +0,0 @@ -from typing import List, Union, Dict, Tuple -import os -import json -import importlib -import copy -from loguru import logger - -from dev_opsgpt.connector.agents import BaseAgent, SelectorAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.tools.base_tool import BaseTools, Tool - -from dev_opsgpt.connector.schema import ( - Memory, Task, Env, Role, Message, Doc, AgentConfig, ChainConfig, PhaseConfig, CodeDoc, - load_chain_configs, load_phase_configs, load_role_configs -) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -from dev_opsgpt.connector.message_process import MessageUtils - -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - - -CUR_DIR = os.path.dirname(os.path.abspath(__file__)) - - -class BasePhase: - - def __init__( - self, - phase_name: str, - task: Task = None, - do_summary: bool = False, - do_search: bool = False, - do_doc_retrieval: bool = False, - do_code_retrieval: bool = False, - do_tool_retrieval: bool = False, - phase_config: Union[dict, str] = PHASE_CONFIGS, - chain_config: Union[dict, str] = CHAIN_CONFIGS, - role_config: Union[dict, str] = AGETN_CONFIGS, - ) -> None: - self.conv_summary_agent = BaseAgent(role=role_configs["conv_summary"].role, - task = None, - memory = None, - do_search = role_configs["conv_summary"].do_search, - do_doc_retrieval = role_configs["conv_summary"].do_doc_retrieval, - do_tool_retrieval = role_configs["conv_summary"].do_tool_retrieval, - do_filter=False, do_use_self_memory=False) - - self.chains: List[BaseChain] = self.init_chains( - phase_name, - task=task, - memory=None, - phase_config = phase_config, - chain_config = chain_config, - role_config = role_config, - ) - - self.message_utils = MessageUtils() - self.phase_name = phase_name - self.do_summary = do_summary - self.do_search = do_search - self.do_code_retrieval = do_code_retrieval - self.do_doc_retrieval = do_doc_retrieval - self.do_tool_retrieval = do_tool_retrieval - # - self.global_memory = Memory(messages=[]) - # self.chain_message = Memory([]) - self.phase_memory: List[Memory] = [] - # memory_pool dont have specific order - self.memory_pool = Memory(messages=[]) - - def astep(self, query: Message, history: Memory = None) -> Tuple[Message, Memory]: - summary_message = None - chain_message = Memory(messages=[]) - local_phase_memory = Memory(messages=[]) - # do_search、do_doc_search、do_code_search - query = self.message_utils.get_extrainfo_step(query, self.do_search, self.do_doc_retrieval, self.do_code_retrieval, self.do_tool_retrieval) - input_message = copy.deepcopy(query) - - self.global_memory.append(input_message) - local_phase_memory.append(input_message) - for chain in self.chains: - # chain can supply background and query to next chain - for output_message, local_chain_memory in chain.astep(input_message, history, background=chain_message, memory_pool=self.memory_pool): - # logger.debug(f"local_memory: {local_memory + chain_memory}") - yield output_message, local_phase_memory + local_chain_memory - - output_message = self.message_utils.inherit_extrainfo(input_message, output_message) - input_message = output_message - logger.info(f"{chain.chainConfig.chain_name} phase_step: {output_message.role_content}") - # 这一段也有问题 - self.global_memory.extend(local_chain_memory) - local_phase_memory.extend(local_chain_memory) - - # whether to use summary_llm - if self.do_summary: - logger.info(f"{self.conv_summary_agent.role.role_name} input global memory: {local_phase_memory.to_str_messages(content_key='step_content')}") - for summary_message in self.conv_summary_agent.arun(query, background=local_phase_memory, memory_pool=self.memory_pool): - pass - # summary_message = Message(**summary_message) - summary_message.role_name = chain.chainConfig.chain_name - summary_message = self.conv_summary_agent.message_utils.parser(summary_message) - summary_message = self.conv_summary_agent.message_utils.filter(summary_message) - summary_message = self.message_utils.inherit_extrainfo(output_message, summary_message) - chain_message.append(summary_message) - - message = summary_message or output_message - yield message, local_phase_memory - - # 由于不会存在多轮chain执行,所以直接保留memory即可 - for chain in self.chains: - self.phase_memory.append(chain.global_memory) - # TODO:local_memory缺少添加summary的过程 - message = summary_message or output_message - message.role_name = self.phase_name - yield message, local_phase_memory - - def step(self, query: Message, history: Memory = None) -> Tuple[Message, Memory]: - for message, local_phase_memory in self.astep(query, history=history): - pass - return message, local_phase_memory - - def init_chains(self, phase_name, phase_config, chain_config, - role_config, task=None, memory=None) -> List[BaseChain]: - # load config - role_configs = load_role_configs(role_config) - chain_configs = load_chain_configs(chain_config) - phase_configs = load_phase_configs(phase_config) - - chains = [] - self.chain_module = importlib.import_module("dev_opsgpt.connector.chains") - self.agent_module = importlib.import_module("dev_opsgpt.connector.agents") - - phase = phase_configs.get(phase_name) - logger.info(f"start to init the phase, the phase_name is {phase_name}, it contains these chains such as {phase.chains}") - - for chain_name in phase.chains: - # logger.debug(f"{chain_configs.keys()}") - chain_config = chain_configs[chain_name] - logger.info(f"start to init the chain, the chain_name is {chain_name}, it contains these agents such as {chain_config.agents}") - - agents = [] - for agent_name in chain_config.agents: - agent_config = role_configs[agent_name] - baseAgent: BaseAgent = getattr(self.agent_module, agent_config.role.agent_type) - base_agent = baseAgent( - agent_config.role, - task = task, - memory = memory, - chat_turn=agent_config.chat_turn, - do_search = agent_config.do_search, - do_doc_retrieval = agent_config.do_doc_retrieval, - do_tool_retrieval = agent_config.do_tool_retrieval, - stop= agent_config.stop, - focus_agents=agent_config.focus_agents, - focus_message_keys=agent_config.focus_message_keys, - ) - - if agent_config.role.agent_type == "SelectorAgent": - for group_agent_name in agent_config.group_agents: - group_agent_config = role_configs[group_agent_name] - baseAgent: BaseAgent = getattr(self.agent_module, group_agent_config.role.agent_type) - group_base_agent = baseAgent( - group_agent_config.role, - task = task, - memory = memory, - chat_turn=group_agent_config.chat_turn, - do_search = group_agent_config.do_search, - do_doc_retrieval = group_agent_config.do_doc_retrieval, - do_tool_retrieval = group_agent_config.do_tool_retrieval, - stop= group_agent_config.stop, - focus_agents=group_agent_config.focus_agents, - focus_message_keys=group_agent_config.focus_message_keys, - ) - base_agent.group_agents.append(group_base_agent) - - agents.append(base_agent) - - chain_instance = BaseChain( - chain_config, agents, chain_config.chat_turn, - do_checker=chain_configs[chain_name].do_checker, - ) - chains.append(chain_instance) - - return chains - - # def get_extrainfo_step(self, input_message): - # if self.do_doc_retrieval: - # input_message = self.get_doc_retrieval(input_message) - - # # logger.debug(F"self.do_code_retrieval: {self.do_code_retrieval}") - # if self.do_code_retrieval: - # input_message = self.get_code_retrieval(input_message) - - # if self.do_search: - # input_message = self.get_search_retrieval(input_message) - - # return input_message - - # def inherit_extrainfo(self, input_message: Message, output_message: Message): - # output_message.db_docs = input_message.db_docs - # output_message.search_docs = input_message.search_docs - # output_message.code_docs = input_message.code_docs - # output_message.figures.update(input_message.figures) - # output_message.origin_query = input_message.origin_query - # return output_message - - # def get_search_retrieval(self, message: Message,) -> Message: - # SEARCH_ENGINES = {"duckduckgo": DDGSTool} - # search_docs = [] - # for idx, doc in enumerate(SEARCH_ENGINES["duckduckgo"].run(message.role_content, 3)): - # doc.update({"index": idx}) - # search_docs.append(Doc(**doc)) - # message.search_docs = search_docs - # return message - - # def get_doc_retrieval(self, message: Message) -> Message: - # query = message.role_content - # knowledge_basename = message.doc_engine_name - # top_k = message.top_k - # score_threshold = message.score_threshold - # if knowledge_basename: - # docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold) - # message.db_docs = [Doc(**doc) for doc in docs] - # return message - - # def get_code_retrieval(self, message: Message) -> Message: - # # DocRetrieval.run("langchain是什么", "DSADSAD") - # query = message.input_query - # code_engine_name = message.code_engine_name - # history_node_list = message.history_node_list - # code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list) - # message.code_docs = [CodeDoc(**doc) for doc in code_docs] - # return message - - # def get_tool_retrieval(self, message: Message) -> Message: - # return message - - def update(self) -> Memory: - pass - - def get_memory(self, ) -> Memory: - return Memory.from_memory_list( - [chain.get_memory() for chain in self.chains] - ) - - def get_memory_str(self, do_all_memory=True, content_key="role_content") -> str: - memory = self.global_memory if do_all_memory else self.phase_memory - return "\n".join([": ".join(i) for i in memory.to_tuple_messages(content_key=content_key)]) - - def get_chains_memory(self, content_key="role_content") -> List[Tuple]: - return [memory.to_tuple_messages(content_key=content_key) for memory in self.phase_memory] - - def get_chains_memory_str(self, content_key="role_content") -> str: - return "************".join([f"{chain.chainConfig.chain_name}\n" + chain.get_memory_str(content_key=content_key) for chain in self.chains]) \ No newline at end of file diff --git a/dev_opsgpt/connector/schema/memory.py b/dev_opsgpt/connector/schema/memory.py deleted file mode 100644 index 70d2809..0000000 --- a/dev_opsgpt/connector/schema/memory.py +++ /dev/null @@ -1,114 +0,0 @@ -from pydantic import BaseModel -from typing import List, Union -from loguru import logger - -from .message import Message -from dev_opsgpt.utils.common_utils import ( - save_to_jsonl_file, save_to_json_file, read_json_file, read_jsonl_file -) - - -class Memory(BaseModel): - messages: List[Message] = [] - - # def __init__(self, messages: List[Message] = []): - # self.messages = messages - - def append(self, message: Message): - self.messages.append(message) - - def extend(self, memory: 'Memory'): - self.messages.extend(memory.messages) - - def update(self, role_name: str, role_type: str, role_content: str): - self.messages.append(Message(role_name, role_type, role_content, role_content)) - - def clear(self, ): - self.messages = [] - - def delete(self, ): - pass - - def get_messages(self, k=0) -> List[Message]: - """Return the most recent k memories, return all when k=0""" - return self.messages[-k:] - - def save(self, file_type="jsonl", return_all=True): - try: - if file_type == "jsonl": - save_to_jsonl_file(self.to_dict_messages(return_all=return_all), "role_name_history"+f".{file_type}") - return True - elif file_type in ["json", "txt"]: - save_to_json_file(self.to_dict_messages(return_all=return_all), "role_name_history"+f".{file_type}") - return True - except: - return False - return False - - def load(self, filepath): - file_type = filepath - try: - if file_type == "jsonl": - self.messages = [Message(**message) for message in read_jsonl_file(filepath)] - return True - elif file_type in ["json", "txt"]: - self.messages = [Message(**message) for message in read_jsonl_file(filepath)] - return True - except: - return False - - return False - - def to_tuple_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): - # logger.debug(f"{[message.to_tuple_message(return_all, content_key) for message in self.messages ]}") - return [ - message.to_tuple_message(return_all, content_key) for message in self.messages - if message.role_name not in filter_roles - ] - - def to_dict_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): - return [ - message.to_dict_message(return_all, content_key) for message in self.messages - if message.role_name not in filter_roles - ] - - def to_str_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): - # for message in self.messages: - # logger.debug(f"{message.to_tuple_message(return_all, content_key)}") - # logger.debug(f"{[message.to_tuple_message(return_all, content_key) for message in self.messages ]}") - return "\n\n".join([message.to_str_content(return_all, content_key) for message in self.messages - if message.role_name not in filter_roles - ]) - - def get_parserd_output(self, ): - return [message.parsed_output for message in self.messages] - - def get_parserd_output_list(self, ): - # for message in self.messages: - # logger.debug(f"{message.role_name}: {message.parsed_output_list}") - return [parsed_output for message in self.messages for parsed_output in message.parsed_output_list[1:]] - - def get_rolenames(self, ): - '''''' - return [message.role_name for message in self.messages] - - @classmethod - def from_memory_list(cls, memorys: List['Memory']) -> 'Memory': - return cls(messages=[message for memory in memorys for message in memory.get_messages()]) - - def __len__(self, ): - return len(self.messages) - - def __str__(self) -> str: - return "\n".join([":".join(i) for i in self.to_tuple_messages()]) - - def __add__(self, other: Union[Message, 'Memory']) -> 'Memory': - if isinstance(other, Message): - return Memory(messages=self.messages + [other]) - elif isinstance(other, Memory): - return Memory(messages=self.messages + other.messages) - else: - raise ValueError(f"cant add unspecified type like as {type(other)}") - - - \ No newline at end of file diff --git a/dev_opsgpt/connector/utils.py b/dev_opsgpt/connector/utils.py deleted file mode 100644 index e6a95de..0000000 --- a/dev_opsgpt/connector/utils.py +++ /dev/null @@ -1,52 +0,0 @@ -import re - - - -def parse_section(text, section_name): - # Define a pattern to extract the named section along with its content - section_pattern = rf'#### {section_name}\n(.*?)(?=####|$)' - - # Find the specific section content - section_content = re.search(section_pattern, text, re.DOTALL) - - if section_content: - # If the section is found, extract the content - content = section_content.group(1) - - # Define a pattern to find segments that follow the format **xx:** - segments_pattern = r'\*\*([^*]+):\*\*' - - # Use findall method to extract all matches in the section content - segments = re.findall(segments_pattern, content) - - return segments - else: - # If the section is not found, return an empty list - return [] - - -def prompt_cost(model_type: str, num_prompt_tokens: float, num_completion_tokens: float): - input_cost_map = { - "gpt-3.5-turbo": 0.0015, - "gpt-3.5-turbo-16k": 0.003, - "gpt-3.5-turbo-0613": 0.0015, - "gpt-3.5-turbo-16k-0613": 0.003, - "gpt-4": 0.03, - "gpt-4-0613": 0.03, - "gpt-4-32k": 0.06, - } - - output_cost_map = { - "gpt-3.5-turbo": 0.002, - "gpt-3.5-turbo-16k": 0.004, - "gpt-3.5-turbo-0613": 0.002, - "gpt-3.5-turbo-16k-0613": 0.004, - "gpt-4": 0.06, - "gpt-4-0613": 0.06, - "gpt-4-32k": 0.12, - } - - if model_type not in input_cost_map or model_type not in output_cost_map: - return -1 - - return num_prompt_tokens * input_cost_map[model_type] / 1000.0 + num_completion_tokens * output_cost_map[model_type] / 1000.0 diff --git a/dev_opsgpt/embeddings/utils.py b/dev_opsgpt/embeddings/utils.py deleted file mode 100644 index c762c04..0000000 --- a/dev_opsgpt/embeddings/utils.py +++ /dev/null @@ -1,14 +0,0 @@ -from functools import lru_cache -from langchain.embeddings.huggingface import HuggingFaceEmbeddings -from configs.model_config import embedding_model_dict -from loguru import logger - - -@lru_cache(1) -def load_embeddings(model: str, device: str): - logger.info("load embedding model: {}, {}".format(model, embedding_model_dict[model])) - embeddings = HuggingFaceEmbeddings(model_name=embedding_model_dict[model], - model_kwargs={'device': device}) - return embeddings - - diff --git a/dev_opsgpt/llm_models/__init__.py b/dev_opsgpt/llm_models/__init__.py deleted file mode 100644 index 25dfa7b..0000000 --- a/dev_opsgpt/llm_models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .openai_model import getChatModel, getExtraModel - - -__all__ = [ - "getChatModel", "getExtraModel" -] \ No newline at end of file diff --git a/dev_opsgpt/orm/utils.py b/dev_opsgpt/orm/utils.py deleted file mode 100644 index 3921a62..0000000 --- a/dev_opsgpt/orm/utils.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -from dev_opsgpt.utils.path_utils import get_file_path, get_LoaderClass, SUPPORTED_EXTS - - -class DocumentFile: - def __init__( - self, filename: str, knowledge_base_name: str) -> None: - self.kb_name = knowledge_base_name - self.filename = filename - self.ext = os.path.splitext(filename)[-1].lower() - if self.ext not in SUPPORTED_EXTS: - raise ValueError(f"暂未支持的文件格式 {self.ext}") - self.filepath = get_file_path(knowledge_base_name, filename) - self.docs = None - self.document_loader_name = get_LoaderClass(self.ext) - - # TODO: 增加依据文件格式匹配text_splitter - self.text_splitter_name = None \ No newline at end of file diff --git a/dev_opsgpt/tools/docs_retrieval.py b/dev_opsgpt/tools/docs_retrieval.py deleted file mode 100644 index b2cbee3..0000000 --- a/dev_opsgpt/tools/docs_retrieval.py +++ /dev/null @@ -1,42 +0,0 @@ -import json -import os -import re -from pydantic import BaseModel, Field -from typing import List, Dict -import requests -import numpy as np -from loguru import logger - -from configs.model_config import ( - VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from .base_tool import BaseToolModel - - - -from dev_opsgpt.service.kb_api import search_docs - - -class DocRetrieval(BaseToolModel): - name = "DocRetrieval" - description = "采用向量化对本地知识库进行检索" - - class ToolInputArgs(BaseModel): - query: str = Field(..., description="检索的关键字或问题") - knowledge_base_name: str = Field(..., description="知识库名称", examples=["samples"]) - search_top: int = Field(VECTOR_SEARCH_TOP_K, description="检索返回的数量") - score_threshold: float = Field(SCORE_THRESHOLD, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1) - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - title: str = Field(..., description="检索网页标题") - snippet: str = Field(..., description="检索内容的判断") - link: str = Field(..., description="检索网页地址") - - @classmethod - def run(cls, query, knowledge_base_name, search_top=VECTOR_SEARCH_TOP_K, score_threshold=SCORE_THRESHOLD): - """excute your tool!""" - docs = search_docs(query, knowledge_base_name, search_top, score_threshold) - return_docs = [] - for idx, doc in enumerate(docs): - return_docs.append({"index": idx, "snippet": doc.page_content, "title": doc.metadata.get("source"), "link": doc.metadata.get("source")}) - return return_docs diff --git a/dev_opsgpt/webui/docs/docker_init.md b/dev_opsgpt/webui/docs/docker_init.md deleted file mode 100644 index 2bccc65..0000000 --- a/dev_opsgpt/webui/docs/docker_init.md +++ /dev/null @@ -1,15 +0,0 @@ - -# 容器安装 - -## windows docker install - -### 系统要求 -[Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) 支持 64 位版本的 Windows 10 Pro,且必须开启 Hyper-V(若版本为 v1903 及以上则无需开启 Hyper-V),或者 64 位版本的 Windows 10 Home v1903 及以上版本。 - -- [【全面详细】Windows10 Docker安装详细教程](https://zhuanlan.zhihu.com/p/441965046) -- [Docker 从入门到实践](https://yeasy.gitbook.io/docker_practice/install/windows) -- [Docker Desktop requires the Server service to be enabled 处理](https://blog.csdn.net/sunhy_csdn/article/details/106526991) -- [安装wsl或者等报错提示](https://learn.microsoft.com/zh-cn/windows/wsl/install) - -## linux docker install -linux安装相对比较简单,请自行baidu/google相关安装 diff --git a/docker_requirements.txt b/docker_requirements.txt deleted file mode 100644 index 4f14de6..0000000 --- a/docker_requirements.txt +++ /dev/null @@ -1,32 +0,0 @@ -langchain==0.0.266 -openai -sentence_transformers -fschat==0.2.24 -transformers>=4.31.0 -# torch~=2.0.0 -fastapi~=0.99.1 -nltk~=3.8.1 -uvicorn~=0.23.1 -starlette~=0.27.0 -pydantic~=1.10.11 -SQLAlchemy==2.0.19 -faiss-cpu -nltk -loguru -pypdf -duckduckgo-search -pysocks -accelerate -matplotlib -seaborn -jupyter -notebook - -# uncomment libs if you want to use corresponding vector store -# pymilvus==2.1.3 # requires milvus==2.1.3 -# psycopg2 -# pgvector - -numpy~=1.24.4 -pandas~=2.0.3 -httpx~=0.24.1 diff --git a/domain/devops/README.md b/domain/devops/README.md deleted file mode 100644 index 614cce8..0000000 --- a/domain/devops/README.md +++ /dev/null @@ -1,177 +0,0 @@ -#

DevOps-ChatBot: Development by Private Knowledge Augmentation

- -

- ZH doc - EN doc - License - - Open Issues - -

-

- -本项目是一个开源的 AI 智能助手,专为软件开发的全生命周期而设计,涵盖设计、编码、测试、部署和运维等阶段。通过知识检索、工具使用和沙箱执行,DevOps-ChatBot 能解答您开发过程中的各种专业问题、问答操作周边独立分散平台。 - - - -## 🔔 更新 -- [2023.09.15] 本地/隔离环境的沙盒功能开放,基于爬虫实现指定url知识检索 - -## 📜 目录 -- [🤝 介绍](#-介绍) -- [🎥 演示视频](#-演示视频) -- [🧭 技术路线](#-技术路线) -- [🚀 快速使用](#-快速使用) -- [🤗 致谢](#-致谢) - -## 🤝 介绍 - -💡 本项目旨在通过检索增强生成(Retrieval Augmented Generation,RAG)、工具学习(Tool Learning)和沙盒环境来构建软件开发全生命周期的AI智能助手,涵盖设计、编码、测试、部署和运维等阶段。 逐渐从各处资料查询、独立分散平台操作的传统开发运维模式转变到大模型问答的智能化开发运维模式,改变人们的开发运维习惯。 - -- 📚 知识库管理:DevOps专业高质量知识库 + 企业级知识库自助构建 + 对话实现快速检索开源/私有技术文档 -- 🐳 隔离沙盒环境:实现代码的快速编译执行测试 -- 🔄 React范式:支撑代码的自我迭代、自动执行 -- 🛠️ Prompt管理:实现各种开发、运维任务的prompt管理 -- 🚀 对话驱动:需求设计、系分设计、代码生成、开发测试、部署运维自动化 - -
- 图片 -
- -🌍 依托于开源的 LLM 与 Embedding 模型,本项目可实现基于开源模型的离线私有部署。此外,本项目也支持 OpenAI API 的调用。 - -👥 核心研发团队长期专注于 AIOps + NLP 领域的研究。我们发起了 DevOpsGPT 项目,希望大家广泛贡献高质量的开发和运维文档,共同完善这套解决方案,以实现“让天下没有难做的开发”的目标。 - -## 🎥 演示视频 - -为了帮助您更直观地了解 DevOps-ChatBot 的功能和使用方法,我们录制了一个演示视频。您可以通过观看此视频,快速了解本项目的主要特性和操作流程。 - - -[演示视频](https://www.youtube.com/watch?v=UGJdTGaVnNY&t=2s&ab_channel=HaotianZhu) - -## 🧭 技术路线 -
- 图片 -
- -- 🕷️ **Web Crawl**:实现定期网络文档爬取,确保数据的及时性,并依赖于开源社区的持续补充。 -- 🗂️ **DocLoader & TextSplitter**:对从多种来源爬取的数据进行数据清洗、去重和分类,并支持私有文档的导入。 -- 🗄️ **Vector Database**:结合Text Embedding模型对文档进行Embedding并在Milvus中存储。 -- 🔌 **Connector**:作为调度中心,负责LLM与Vector Database之间的交互调度,基于Langchain技术实现。 -- 📝 **Prompt Control**:从开发和运维角度设计,为不同问题分类并为Prompt添加背景,确保答案的可控性和完整性。 -- 💬 **LLM**:默认使用GPT-3.5-turbo,并为私有部署和其他涉及隐私的场景提供专有模型选择。 -- 🔤 **Text Embedding**:默认采用OpenAI的Text Embedding模型,支持私有部署和其他隐私相关场景,并提供专有模型选择。 -- 🚧 **SandBox**:对于生成的输出,如代码,为帮助用户判断其真实性,提供了一个交互验证环境(基于FaaS),并支持用户进行调整。 - - -具体实现明细见:[技术路线明细](../../sources/readme_docs/roadmap.md) - -## 🚀 快速使用 - -请自行安装 nvidia 驱动程序,本项目已在 Python 3.9.18,CUDA 11.7 环境下,Windows、X86 架构的 macOS 系统中完成测试。 - -1、python 环境准备 - -- 推荐采用 conda 对 python 环境进行管理(可选) -```bash -# 准备 conda 环境 -conda create --name devopsgpt python=3.9 -conda activate devopsgpt -``` - -- 安装相关依赖 -```bash -cd DevOps-ChatBot -pip install -r requirements.txt - -# 安装完成后,确认电脑是否兼容 notebook=6.5.5 版本,若不兼容执行更新命令 -pip install --upgrade notebook - -# 修改 docker_requirement.txt 的 notebook 版本设定, 用于后续构建新的孤立镜像 -notebook=6.5.5 => notebook -``` - -2、沙盒环境准备 -- windows Docker 安装: -[Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) 支持 64 位版本的 Windows 10 Pro,且必须开启 Hyper-V(若版本为 v1903 及以上则无需开启 Hyper-V),或者 64 位版本的 Windows 10 Home v1903 及以上版本。 - - - [【全面详细】Windows10 Docker安装详细教程](https://zhuanlan.zhihu.com/p/441965046) - - [Docker 从入门到实践](https://yeasy.gitbook.io/docker_practice/install/windows) - - [Docker Desktop requires the Server service to be enabled 处理](https://blog.csdn.net/sunhy_csdn/article/details/106526991) - - [安装wsl或者等报错提示](https://learn.microsoft.com/zh-cn/windows/wsl/install) - -- Linux Docker 安装: -Linux 安装相对比较简单,请自行 baidu/google 相关安装 - -- Mac Docker 安装 - - [Docker 从入门到实践](https://yeasy.gitbook.io/docker_practice/install/mac) - -```bash -# 构建沙盒环境的镜像,notebook版本问题见上述 -bash docker_build.sh -``` - -3、模型下载(可选) - -如需使用开源 LLM 与 Embedding 模型可以从 HuggingFace 下载。 -此处以 THUDM/chatglm2-6bm 和 text2vec-base-chinese 为例: - -``` -# install git-lfs -git lfs install - -# install LLM-model -git lfs clone https://huggingface.co/THUDM/chatglm2-6b - -# install Embedding-model -git lfs clone https://huggingface.co/shibing624/text2vec-base-chinese -``` - - -4、基础配置 - -```bash -# 修改服务启动的基础配置 -cd configs -cp model_config.py.example model_config.py -cp server_config.py.example server_config.py - -# model_config#11~12 若需要使用openai接口,openai接口key -os.environ["OPENAI_API_KEY"] = "sk-xxx" -# 可自行替换自己需要的api_base_url -os.environ["API_BASE_URL"] = "https://api.openai.com/v1" - -# vi model_config#95 你需要选择的语言模型 -LLM_MODEL = "gpt-3.5-turbo" - -# vi model_config#33 你需要选择的向量模型 -EMBEDDING_MODEL = "text2vec-base" - -# vi model_config#19 修改成你的本地路径,如果能直接连接huggingface则无需修改 -"text2vec-base": "/home/user/xx/text2vec-base-chinese", - -# 是否启动本地的notebook用于代码解释,默认启动docker的notebook -# vi server_config#35,True启动docker的notebook,false启动local的notebook -"do_remote": False, / "do_remote": True, -``` - -5、启动服务 - -默认只启动webui相关服务,未启动fastchat(可选)。 -```bash -# 若需要支撑codellama-34b-int4模型,需要给fastchat打一个补丁 -# cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py - -# start llm-service(可选) -python dev_opsgpt/service/llm_api.py -``` - -```bash -cd examples -# python ../dev_opsgpt/service/llm_api.py 若需使用本地大语言模型,可执行该命令 -bash start_webui.sh -``` - -## 🤗 致谢 - -本项目基于[langchain-chatchat](https://github.com/chatchat-space/Langchain-Chatchat)和[codebox-api](https://github.com/shroominic/codebox-api),在此深深感谢他们的开源贡献! \ No newline at end of file diff --git a/domain/devops/README_en.md b/domain/devops/README_en.md deleted file mode 100644 index 4ea2cfb..0000000 --- a/domain/devops/README_en.md +++ /dev/null @@ -1,169 +0,0 @@ -#

DevOps-ChatBot: Development by Private Knowledge Augmentation

- -

- ZH doc - EN doc - License - - Open Issues - -

-

-This project is an open-source AI intelligent assistant, specifically designed for the entire lifecycle of software development, covering design, coding, testing, deployment, and operations. Through knowledge retrieval, tool utilization, and sandbox execution, DevOps-ChatBot can answer various professional questions during your development process and perform question-answering operations on standalone, disparate platforms. - - - -## 🔔 Updates -- [2023.09.15] Sandbox features for local/isolated environments are now available, implementing specified URL knowledge retrieval based on web crawling. - -## 📜 Contents -- [🤝 Introduction](#-introduction) -- [🧭 Technical Route](#-technical-route) -- [🚀 Quick Start](#-quick-start) -- [🤗 Acknowledgements](#-acknowledgements) - -## 🤝 Introduction - -💡 The aim of this project is to construct an AI intelligent assistant for the entire lifecycle of software development, covering design, coding, testing, deployment, and operations, through Retrieval Augmented Generation (RAG), Tool Learning, and sandbox environments. It transitions gradually from the traditional development and operations mode of querying information from various sources and operating on standalone, disparate platforms to an intelligent development and operations mode based on large-model Q&A, changing people's development and operations habits. - -- 📚 Knowledge Base Management: Professional high-quality DevOps knowledge base + enterprise-level knowledge base self-construction + dialogue-based fast retrieval of open-source/private technical documents. -- 🐳 Isolated Sandbox Environment: Enables quick compilation, execution, and testing of code. -- 🔄 React Paradigm: Supports code self-iteration and automatic execution. -- 🛠️ Prompt Management: Manages prompts for various development and operations tasks. -- 🚀 Conversation Driven: Automates requirement design, system analysis design, code generation, development testing, deployment, and operations. - -
- Image -
- -🌍 Relying on open-source LLM and Embedding models, this project can achieve offline private deployments based on open-source models. Additionally, this project also supports the use of the OpenAI API. - -👥 The core development team has been long-term focused on research in the AIOps + NLP domain. We initiated the DevOpsGPT project, hoping that everyone could contribute high-quality development and operations documents widely, jointly perfecting this solution to achieve the goal of "Making Development Seamless for Everyone." - -## 🧭 Technical Route -
- Image -
- -- 🕷️ **Web Crawl**: Implements periodic web document crawling to ensure data timeliness and relies on continuous supplementation from the open-source community. -- 🗂️ **DocLoader & TextSplitter**: Cleans, deduplicates, and categorizes data crawled from various sources and supports the import of private documents. -- 🗄️ **Vector Database**: Integrates Text Embedding models to embed documents and store them in Milvus. -- 🔌 **Connector**: Acts as the scheduling center, responsible for coordinating interactions between LLM and Vector Database, implemented based on Langchain technology. -- 📝 **Prompt Control**: Designs from development and operations perspectives, categorizes different problems, and adds backgrounds to prompts to ensure the controllability and completeness of answers. -- 💬 **LLM**: Uses GPT-3.5-turbo by default and provides proprietary model options for private deployments and other privacy-related scenarios. -- 🔤 **Text Embedding**: Uses OpenAI's Text Embedding model by default, supports private deployments and other privacy-related scenarios, and provides proprietary model options. -- 🚧 **SandBox**: For generated outputs, like code, to help users judge their authenticity, an interactive verification environment is provided (based on FaaS), allowing user adjustments. - -For implementation details, see: [Technical Route Details](sources/readme_docs/roadmap.md) - -## 🚀 Quick Start - -Please install the Nvidia driver yourself; this project has been tested on Python 3.9.18, CUDA 11.7, Windows, and X86 architecture macOS systems. - -1. Preparation of Python environment - -- It is recommended to use conda to manage the python environment (optional) -```bash -# Prepare conda environment -conda create --name devopsgpt python=3.9 -conda activate devopsgpt -``` - -- Install related dependencies -```bash -cd DevOps-ChatBot -pip install -r requirements.txt - -# After installation, confirm whether the computer is compatible with notebook=6.5.5 version; if incompatible, execute the update command -pip install --upgrade notebook - -# Modify the notebook version setting in docker_requirement.txt for building new isolated images later -notebook=6.5.5 => notebook -``` - -2. Preparation of Sandbox Environment -- Windows Docker installation: -[Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) supports 64-bit versions of Windows 10 Pro, with Hyper-V enabled (not required for versions v1903 and above), or 64-bit versions of Windows 10 Home v1903 and above. - - - [Comprehensive Detailed Windows 10 Docker Installation Tutorial](https://zhuanlan.zhihu.com/p/441965046) - - [Docker: From Beginner to Practitioner](https://yeasy.gitbook.io/docker_practice/install/windows) - - [Handling Docker Desktop requires the Server service to be enabled](https://blog.csdn.net/sunhy_csdn/article/details/106526991) - - [Install wsl or wait for error prompt](https://learn.microsoft.com/en-us/windows/wsl/install) - -- Linux Docker Installation: -Linux installation is relatively simple, please search Baidu/Google for installation instructions. - -- Mac Docker Installation - - [Docker: From Beginner to Practitioner](https://yeasy.gitbook.io/docker_practice/install/mac) - -```bash -# Build images for the sandbox environment, see above for notebook version issues -bash docker_build.sh -``` - -3. Model Download (Optional) - -If you need to use open-source LLM and Embed - -ding models, you can download them from HuggingFace. -Here, we use THUDM/chatglm2-6b and text2vec-base-chinese as examples: - -``` -# install git-lfs -git lfs install - -# install LLM-model -git lfs clone https://huggingface.co/THUDM/chatglm2-6b - -# install Embedding-model -git lfs clone https://huggingface.co/shibing624/text2vec-base-chinese -``` - -4. Basic Configuration - -```bash -# Modify the basic configuration for service startup -cd configs -cp model_config.py.example model_config.py -cp server_config.py.example server_config.py - -# model_config#11~12 If you need to use the openai interface, openai interface key -os.environ["OPENAI_API_KEY"] = "sk-xxx" -# You can replace the api_base_url yourself -os.environ["API_BASE_URL"] = "https://api.openai.com/v1" - -# vi model_config#95 You need to choose the language model -LLM_MODEL = "gpt-3.5-turbo" - -# vi model_config#33 You need to choose the vector model -EMBEDDING_MODEL = "text2vec-base" - -# vi model_config#19 Modify to your local path, if you can directly connect to huggingface, no modification is needed -"text2vec-base": "/home/user/xx/text2vec-base-chinese", - -# Whether to start the local notebook for code interpretation, start the docker notebook by default -# vi server_config#35, True to start the docker notebook, false to start the local notebook -"do_remote": False, / "do_remote": True, -``` - -5. Start the Service - -By default, only webui related services are started, and fastchat is not started (optional). -```bash -# if use codellama-34b-int4, you should replace fastchat's gptq.py -# cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py - -# start llm-service(可选) -python dev_opsgpt/service/llm_api.py -``` - - -```bash -cd examples -# python ../dev_opsgpt/service/llm_api.py If you need to use the local large language model, you can execute this command -bash start_webui.sh -``` - -## 🤗 Acknowledgements - -This project is based on [langchain-chatchat](https://github.com/chatchat-space/Langchain-Chatchat) and [codebox-api](https://github.com/shroominic/codebox-api). We deeply appreciate their contributions to open source! \ No newline at end of file diff --git a/examples/agent_examples/baseGroupPhase_example.py b/examples/agent_examples/baseGroupPhase_example.py index b7934bd..98bc4bd 100644 --- a/examples/agent_examples/baseGroupPhase_example.py +++ b/examples/agent_examples/baseGroupPhase_example.py @@ -1,53 +1,43 @@ -import os, sys, requests +import os, sys src_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) - -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH, LLM_MODEL +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message +# tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - - -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") - +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "0" phase_name = "baseGroupPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) +llm_config = LLMConfig( + model_name=LLM_MODEL, model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) # round-1 query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" # query_content = "帮我确认下127.0.0.1这个服务器的在10点是否存在异常,请帮我判断一下" query = Message( - role_name="human", role_type="user", tools=tools, + role_name="human", role_type="user", tools=[], role_content=query_content, input_query=query_content, origin_query=query_content, ) - -output_message, _ = phase.step(query) +# phase.pre_print(query) +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/baseTaskPhase_example.py b/examples/agent_examples/baseTaskPhase_example.py index 78f1b4f..2b01f3d 100644 --- a/examples/agent_examples/baseTaskPhase_example.py +++ b/examples/agent_examples/baseTaskPhase_example.py @@ -5,43 +5,29 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib - -tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - - -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message +# log-level,print prompt or llm predict +os.environ["log_verbose"] = "2" phase_name = "baseTaskPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) - +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) # round-1 query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" query = Message( @@ -49,4 +35,6 @@ query = Message( role_content=query_content, input_query=query_content, origin_query=query_content, ) -output_message, _ = phase.step(query) +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/codeChatPhase_example.py b/examples/agent_examples/codeChatPhase_example.py index 1fa2a1c..04bb7c9 100644 --- a/examples/agent_examples/codeChatPhase_example.py +++ b/examples/agent_examples/codeChatPhase_example.py @@ -5,43 +5,29 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) - -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib - -tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - - -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message, Memory +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" phase_name = "codeChatPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=True, - do_doc_retrieval=False, - do_search=False, - ) - +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) # 代码一共有多少类 => 基于cypher # 代码库里有哪些函数,返回5个就行 => 基于cypher # remove 这个函数是做什么的 => 基于标签 @@ -51,7 +37,7 @@ phase = BasePhase(phase_name, # round-1 query_content = "代码一共有多少类" query = Message( - role_name="user", role_type="human", + role_name="human", role_type="user", role_content=query_content, input_query=query_content, origin_query=query_content, code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" ) @@ -59,27 +45,15 @@ query = Message( output_message1, _ = phase.step(query) # round-2 -history = Memory(messages=[ - Message(role_name="user", role_type="human", role_content="代码一共有多少类"), - Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), - ]) - query_content = "代码库里有哪些函数,返回5个就行" query = Message( - role_name="user", role_type="human", + role_name="human", role_type="user", role_content=query_content, input_query=query_content, origin_query=query_content, code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" ) output_message2, _ = phase.step(query) # round-3 -history = Memory(messages=[ - Message(role_name="user", role_type="human", role_content="代码一共有多少类"), - Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), - Message(role_name="user", role_type="human", role_content="代码库里有哪些函数,返回5个就行"), - Message(role_name="ai", role_type="assistant", role_content=output_message2.step_content), - ]) - query_content = "remove 这个函数是做什么的" query = Message( role_name="user", role_type="human", @@ -89,19 +63,9 @@ query = Message( output_message3, _ = phase.step(query) # round-4 -history = Memory(messages=[ - Message(role_name="user", role_type="human", role_content="代码一共有多少类"), - Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), - Message(role_name="user", role_type="human", role_content="代码库里有哪些函数,返回5个就行"), - Message(role_name="ai", role_type="assistant", role_content=output_message2.step_content), - Message(role_name="user", role_type="human", role_content="remove 这个函数是做什么的"), - Message(role_name="ai", role_type="assistant", role_content=output_message3.step_content), - ]) - - query_content = "有没有函数已经实现了从字符串删除指定字符串的功能,使用的话可以怎么使用,写个java代码" query = Message( - role_name="user", role_type="human", + role_name="human", role_type="user", role_content=query_content, input_query=query_content, origin_query=query_content, code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="description" ) @@ -109,21 +73,12 @@ output_message4, _ = phase.step(query) # round-5 -history = Memory(messages=[ - Message(role_name="user", role_type="human", role_content="代码一共有多少类"), - Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), - Message(role_name="user", role_type="human", role_content="代码库里有哪些函数,返回5个就行"), - Message(role_name="ai", role_type="assistant", role_content=output_message2.step_content), - Message(role_name="user", role_type="human", role_content="remove 这个函数是做什么的"), - Message(role_name="ai", role_type="assistant", role_content=output_message3.step_content), - Message(role_name="user", role_type="human", role_content="有没有函数已经实现了从字符串删除指定字符串的功能,使用的话可以怎么使用,写个java代码"), - Message(role_name="ai", role_type="assistant", role_content=output_message4.step_content), - ]) - query_content = "有根据我以下的需求用 java 开发一个方法:输入为字符串,将输入中的 .java 字符串给删除掉,然后返回新的字符串" query = Message( - role_name="user", role_type="human", + role_name="human", role_type="user", role_content=query_content, input_query=query_content, origin_query=query_content, code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="description" ) -output_message5, _ = phase.step(query) \ No newline at end of file +output_message5, output_memory5 = phase.step(query) + +print(output_memory5.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/codeReactPhase_example.py b/examples/agent_examples/codeReactPhase_example.py index f861e60..f4c35ee 100644 --- a/examples/agent_examples/codeReactPhase_example.py +++ b/examples/agent_examples/codeReactPhase_example.py @@ -5,43 +5,29 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) - -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib - -tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - - -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" phase_name = "codeReactPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) - +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) # round-1 query_content = "确认本地是否存在book_data.csv,并查看它有哪些列和数据类型;然后画柱状图" query = Message( @@ -49,4 +35,6 @@ query = Message( role_content=query_content, input_query=query_content, origin_query=query_content, ) -output_message, _ = phase.step(query) +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/codeRetrieval_example.py b/examples/agent_examples/codeRetrieval_example.py index 9ec8873..e86647e 100644 --- a/examples/agent_examples/codeRetrieval_example.py +++ b/examples/agent_examples/codeRetrieval_example.py @@ -1,25 +1,33 @@ import os, sys, requests - +from loguru import logger src_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) ) sys.path.append(src_dir) -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig + +from coagent.connector.phase import BasePhase +from coagent.connector.agents import BaseAgent +from coagent.connector.chains import BaseChain +from coagent.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs, ActionStatus ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -from dev_opsgpt.connector.utils import parse_section +from coagent.connector.memory_manager import BaseMemoryManager +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS, BASE_PROMPT_CONFIGS +from coagent.connector.utils import parse_section +from coagent.connector.prompt_manager import PromptManager import importlib +from loguru import logger + # update new agent configs -codeRetrievalJudger_PROMPT = """#### CodeRetrievalJudger Assistance Guidance +codeRetrievalJudger_PROMPT = """#### Agent Profile Given the user's question and respective code, you need to decide whether the provided codes are enough to answer the question. @@ -30,16 +38,19 @@ Given the user's question and respective code, you need to decide whether the pr **Retrieval Codes:** the Retrieval Codes from the code base #### Response Output Format +**Action Status:** Set to 'finished' or 'continued'. +If it's 'finished', the provided codes can answer the origin query. +If it's 'continued', the origin query cannot be answered well from the provided code. **REASON:** Justify the decision of choosing 'finished' and 'continued' by evaluating the progress step by step. """ # 将下面的话放到上面的prompt里面去执行,让它判断是否停止 -# **Action Status:** Set to 'finished' or 'continued'. +# **Action Status:** Set to 'finished' or 'continued'. # If it's 'finished', the provided codes can answer the origin query. # If it's 'continued', the origin query cannot be answered well from the provided code. -codeRetrievalDivergent_PROMPT = """#### CodeRetrievalDivergen Assistance Guidance +codeRetrievalDivergent_PROMPT = """#### Agent Profile You are a assistant that helps to determine which code package is needed to answer the question. @@ -55,11 +66,42 @@ Given the user's question, Retrieval code, and the code Packages related to Retr #### Response Output Format -**Code Package:** Identify another Code Package from the Code Packages that should be read to provide a better answer to the Origin Query. +**Code Package:** Identify another Code Package from the Code Packages that can most help to provide a better answer to the Origin Query, only put one name of the code package here. **REASON:** Justify the decision of choosing 'finished' and 'continued' by evaluating the progress step by step. """ + +class CodeRetrievalPM(PromptManager): + def handle_code_packages(self, **kwargs) -> str: + if 'previous_agent_message' not in kwargs: + return "" + previous_agent_message: Message = kwargs['previous_agent_message'] + # 由于两个agent共用了同一个manager,所以临时性处理 + vertices = previous_agent_message.customed_kargs.get("RelatedVerticesRetrivalRes", {}).get("vertices", []) + return ", ".join([str(v) for v in vertices]) + + def handle_retrieval_codes(self, **kwargs) -> str: + if 'previous_agent_message' not in kwargs: + return "" + previous_agent_message: Message = kwargs['previous_agent_message'] + return '\n'.join(previous_agent_message.customed_kargs["Retrieval_Codes"]) + + +# Design your personal PROMPT INPPUT FORMAT +CODE_RETRIEVAL_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + # {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'retrieval_codes', "function_name": 'handle_retrieval_codes'}, + {"field_name": 'code_packages', "function_name": 'handle_code_packages'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} + ] + + AGETN_CONFIGS.update({ "codeRetrievalJudger": { "role": { @@ -70,6 +112,8 @@ AGETN_CONFIGS.update({ "agent_type": "CodeRetrievalJudger" # "agent_type": "BaseAgent" }, + "prompt_config": CODE_RETRIEVAL_PROMPT_CONFIGS, + "prompt_manager_type": "CodeRetrievalPM", "chat_turn": 1, "focus_agents": [], "focus_message_keys": [], @@ -83,6 +127,8 @@ AGETN_CONFIGS.update({ "agent_type": "CodeRetrievalDivergent" # "agent_type": "BaseAgent" }, + "prompt_config": CODE_RETRIEVAL_PROMPT_CONFIGS, + "prompt_manager_type": "CodeRetrievalPM", "chat_turn": 1, "focus_agents": [], "focus_message_keys": [], @@ -94,7 +140,7 @@ CHAIN_CONFIGS.update({ "chain_name": "codeRetrievalChain", "chain_type": "BaseChain", "agents": ["codeRetrievalJudger", "codeRetrievalDivergent"], - "chat_turn": 5, + "chat_turn": 3, "do_checker": False, "chain_prompt": "" } @@ -111,126 +157,98 @@ PHASE_CONFIGS.update({ "do_doc_retrieval": False, "do_code_retrieval": True, "do_tool_retrieval": False, - "do_using_tool": False }, }) - - role_configs = load_role_configs(AGETN_CONFIGS) chain_configs = load_chain_configs(CHAIN_CONFIGS) phase_configs = load_phase_configs(PHASE_CONFIGS) -agent_module = importlib.import_module("dev_opsgpt.connector.agents") -from dev_opsgpt.tools import CodeRetrievalSingle, RelatedVerticesRetrival, Vertex2Code +from coagent.tools import CodeRetrievalSingle, RelatedVerticesRetrival, Vertex2Code # 定义一个新的类 class CodeRetrievalJudger(BaseAgent): def start_action_step(self, message: Message) -> Message: '''do action before agent predict ''' - action_json = CodeRetrievalSingle.run(message.code_engine_name, message.origin_query) - message.customed_kargs["CodeRetrievalSingleRes"] = action_json - message.customed_kargs.setdefault("Retrieval_Codes", "") - message.customed_kargs["Retrieval_Codes"] += "\n" + action_json["code"] + if 'Retrieval_Codes' in message.customed_kargs: + # already retrive the code + pass + else: + action_json = CodeRetrievalSingle.run(message.code_engine_name, message.origin_query, llm_config=self.llm_config, embed_config=self.embed_config) + message.customed_kargs["CodeRetrievalSingleRes"] = action_json + message.customed_kargs['Current_Vertex'] = action_json['vertex'] + message.customed_kargs.setdefault("Retrieval_Codes", []) + message.customed_kargs["Retrieval_Codes"].append(action_json["code"]) return message - def create_prompt( - self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: - ''' - prompt engineer, contains role\task\tools\docs\memory - ''' - # - logger.debug(f"query: {query.customed_kargs}") - formatted_tools, tool_names, _ = self.create_tools_prompt(query) - prompt = self.role.role_prompt.format(**{"formatted_tools": formatted_tools, "tool_names": tool_names}) - # - input_keys = parse_section(self.role.role_prompt, 'Input Format') - prompt += "\n#### Begin!!!\n" - # - for input_key in input_keys: - if input_key == "Origin Query": - prompt += f"\n**{input_key}:**\n" + query.origin_query - elif input_key == "Retrieval Codes": - prompt += f"\n**{input_key}:**\n" + query.customed_kargs["Retrieval_Codes"] - - while "{{" in prompt or "}}" in prompt: - prompt = prompt.replace("{{", "{") - prompt = prompt.replace("}}", "}") - return prompt # 定义一个新的类 class CodeRetrievalDivergent(BaseAgent): - def start_action_step(self, message: Message) -> Message: '''do action before agent predict ''' - action_json = RelatedVerticesRetrival.run(message.code_engine_name, message.customed_kargs["CodeRetrievalSingleRes"]["vertex"]) + action_json = RelatedVerticesRetrival.run(message.code_engine_name, message.customed_kargs['Current_Vertex']) + if not action_json['vertices']: + message.action_status = ActionStatus.FINISHED message.customed_kargs["RelatedVerticesRetrivalRes"] = action_json return message def end_action_step(self, message: Message) -> Message: '''do action before agent predict ''' # logger.error(f"message: {message}") - # action_json = Vertex2Code.run(message.code_engine_name, "com.theokanning.openai.client#Utils.java") # message.parsed_output["Code_Filename"]) action_json = Vertex2Code.run(message.code_engine_name, message.parsed_output["Code Package"]) - message.customed_kargs["Vertex2Code"] = action_json - message.customed_kargs.setdefault("Retrieval_Codes", "") - message.customed_kargs["Retrieval_Codes"] += "\n" + action_json["code"] - return message - - def create_prompt( - self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: - ''' - prompt engineer, contains role\task\tools\docs\memory - ''' - formatted_tools, tool_names, _ = self.create_tools_prompt(query) - prompt = self.role.role_prompt.format(**{"formatted_tools": formatted_tools, "tool_names": tool_names}) - # - input_query = query.input_query - input_keys = parse_section(self.role.role_prompt, 'Input Format') - prompt += "\n#### Begin!!!\n" - # - for input_key in input_keys: - if input_key == "Origin Query": - prompt += f"\n**{input_key}:**\n" + query.origin_query - elif input_key == "Retrieval Codes": - prompt += f"\n**{input_key}:**\n" + query.customed_kargs["Retrieval_Codes"] - elif input_key == "Code Packages": - vertices = query.customed_kargs["RelatedVerticesRetrivalRes"]["vertices"] - prompt += f"\n**{input_key}:**\n" + ", ".join([str(v) for v in vertices]) + logger.debug(f'action_json={action_json}') + if not action_json['code']: + message.action_status = ActionStatus.FINISHED + return message - while "{{" in prompt or "}}" in prompt: - prompt = prompt.replace("{{", "{") - prompt = prompt.replace("}}", "}") - return prompt - + message.customed_kargs["Vertex2Code"] = action_json + message.customed_kargs['Current_Vertex'] = message.parsed_output["Code Package"] + message.customed_kargs.setdefault("Retrieval_Codes", []) + + if action_json['code'] in message.customed_kargs["Retrieval_Codes"]: + message.action_status = ActionStatus.FINISHED + return message + + message.customed_kargs["Retrieval_Codes"].append(action_json['code']) + + return message + +# add agent or prompt_manager class +agent_module = importlib.import_module("coagent.connector.agents") +prompt_manager_module = importlib.import_module("coagent.connector.prompt_manager") setattr(agent_module, 'CodeRetrievalJudger', CodeRetrievalJudger) setattr(agent_module, 'CodeRetrievalDivergent', CodeRetrievalDivergent) +setattr(prompt_manager_module, 'CodeRetrievalPM', CodeRetrievalPM) -# +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + phase_name = "codeRetrievalPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=False, - do_search=False, - ) - +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) # round-1 -query_content = "remove 这个函数是用来做什么的" +query_content = "UtilsTest 这个类中测试了哪些函数,测试的函数代码是什么" query = Message( - role_name="user", role_type="human", + role_name="human", role_type="user", role_content=query_content, input_query=query_content, origin_query=query_content, - code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="tag" ) -output_message1, _ = phase.step(query) +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/codeToolReactPhase_example.py b/examples/agent_examples/codeToolReactPhase_example.py new file mode 100644 index 0000000..feb3be1 --- /dev/null +++ b/examples/agent_examples/codeToolReactPhase_example.py @@ -0,0 +1,44 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH, LLM_MODEL +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig + +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message + + +TOOL_SETS = [ + "StockName", "StockInfo", + ] +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "codeToolReactPhase" +llm_config = LLMConfig( + model_name="gpt-3.5-turbo-0613", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.7 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) + +query_content = "查询贵州茅台的股票代码,并查询截止到当前日期(2023年12月24日)的最近10天的每日时序数据,然后用代码画出折线图并分析" + +query = Message(role_name="human", role_type="user", input_query=query_content, role_content=query_content, origin_query=query_content, tools=tools) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/coedToolReactPhase_example.py b/examples/agent_examples/coedToolReactPhase_example.py deleted file mode 100644 index 24fc6c9..0000000 --- a/examples/agent_examples/coedToolReactPhase_example.py +++ /dev/null @@ -1,59 +0,0 @@ -import os, sys, requests - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -) -# src_dir = os.path.join( -# os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# ) -sys.path.append(src_dir) - -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) - -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib - -print(src_dir) - -# tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - -TOOL_SETS = [ - "StockName", "StockInfo", - ] - -tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - - -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") - -phase_name = "codeToolReactPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) - -query_content = "查询贵州茅台的股票代码,并查询截止到当前日期(2023年12月24日)的最近10天的每日时序数据,然后对时序数据画出折线图并分析" - -query = Message(role_name="human", role_type="user", input_query=query_content, role_content=query_content, origin_query=query_content, tools=tools) - -output_message = phase.step(query) \ No newline at end of file diff --git a/examples/agent_examples/docChatPhase_example.py b/examples/agent_examples/docChatPhase_example.py index 58839b8..fceca34 100644 --- a/examples/agent_examples/docChatPhase_example.py +++ b/examples/agent_examples/docChatPhase_example.py @@ -1,67 +1,112 @@ -import os, sys, requests +import os, sys src_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) ) sys.path.append(src_dir) +sys.path.append(os.path.join(src_dir, "examples")) -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) +from configs.model_config import EMBEDDING_MODEL, CB_ROOT_PATH +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message, Memory -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") +# create your knowledge base +from io import BytesIO +from pathlib import Path + +from coagent.service.kb_api import create_kb, upload_doc +from coagent.service.service_factory import get_kb_details +from coagent.utils.server_utils import run_async +kb_list = {x["kb_name"]: x for x in get_kb_details(KB_ROOT_PATH)} + +# create a knowledge base +kb_name = "example_test" +data = { + "knowledge_base_name": kb_name, + "vector_store_type": "faiss", # default + "kb_root_path": KB_ROOT_PATH, + "embed_model": embed_config.embed_model, + "embed_engine": embed_config.embed_engine, + "embed_model_path": embed_config.embed_model_path, + "model_device": embed_config.model_device, +} +run_async(create_kb(**data)) + +# add doc to knowledge base +file = os.path.join("D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/sources/docs/langchain_text_10.jsonl") +files = [file] +# if embedding init failed, you can use override = True +data = [{"override": True, "file": f, + "knowledge_base_name": kb_name, "not_refresh_vs_cache": False, + "kb_root_path": KB_ROOT_PATH, "embed_model": embed_config.embed_model, + "embed_engine": embed_config.embed_engine, "embed_model_path": embed_config.embed_model_path, + "model_device": embed_config.model_device, + } + for f in files] + +for k in data: + file = Path(file).absolute().open("rb") + filename = file.name + + from fastapi import UploadFile + from tempfile import SpooledTemporaryFile + + temp_file = SpooledTemporaryFile(max_size=10 * 1024 * 1024) + temp_file.write(file.read()) + temp_file.seek(0) + + k.update({"file": UploadFile(file=temp_file, filename=filename),}) + run_async(upload_doc(**k)) + + + +## start to chat with knowledge base + +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +# set chat phase phase_name = "docChatPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) - +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) # round-1 query_content = "langchain有哪些模块" query = Message( - role_name="user", role_type="human", - role_content=query_content, input_query=query_content, origin_query=query_content, - doc_engine_name="DSADSAD", score_threshold=1.0, top_k=3 + role_name="human", role_type="user", + origin_query=query_content, + doc_engine_name=kb_name, score_threshold=1.0, top_k=3 ) -output_message, _ = phase.step(query) +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) # round-2 -history = Memory(messages=[ - Message(role_name="user", role_type="human", role_content="langchain有哪些模块"), - Message(role_name="ai", role_type="assistant", role_content=output_message.step_content), - ]) - query_content = "提示(prompts)有什么用?" query = Message( role_name="human", role_type="user", - role_content=query_content, input_query=query_content, origin_query=query_content, - doc_engine_name="DSADSAD", score_threshold=1.0, top_k=3 + origin_query=query_content, + doc_engine_name=kb_name, score_threshold=1.0, top_k=3 ) -output_message, _ = phase.step(query) \ No newline at end of file +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/metagpt_phase_example.py b/examples/agent_examples/metagpt_phase_example.py index 5c3f72b..dafb06a 100644 --- a/examples/agent_examples/metagpt_phase_example.py +++ b/examples/agent_examples/metagpt_phase_example.py @@ -5,37 +5,34 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") - +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "0" phase_name = "metagpt_code_devlop" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) query_content = "create a snake game" query = Message(role_name="human", role_type="user", input_query=query_content, role_content=query_content, origin_query=query_content) -output_message = phase.step(query) +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/searchChatPhase_example.py b/examples/agent_examples/searchChatPhase_example.py index 6d64e6f..e319ac7 100644 --- a/examples/agent_examples/searchChatPhase_example.py +++ b/examples/agent_examples/searchChatPhase_example.py @@ -5,63 +5,51 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib - -tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message, Memory -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" phase_name = "searchChatPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=False, - do_search=True, - ) +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) # round-1 -query_content = "美国当前总统是谁?" -query = Message( - role_name="user", role_type="human", - role_content=query_content, input_query=query_content, origin_query=query_content, - search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 - ) - -output_message, _ = phase.step(query) - -# round-2 -history = Memory(messages=[ - Message(role_name="user", role_type="human", role_content="美国当前总统是谁?"), - Message(role_name="ai", role_type="assistant", role_content=output_message.step_content), - ]) - -query_content = "美国上一任总统是谁,两个人有什么关系没?" +query_content1 = "美国当前总统是谁?" query = Message( role_name="human", role_type="user", - role_content=query_content, input_query=query_content, origin_query=query_content, + role_content=query_content1, input_query=query_content1, origin_query=query_content1, search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 ) -output_message, _ = phase.step(query) \ No newline at end of file + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2 +query_content2 = "美国上一任总统是谁,两个人有什么关系没?" +query = Message( + role_name="human", role_type="user", + role_content=query_content2, input_query=query_content2, origin_query=query_content2, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/examples/agent_examples/toolReactPhase_example.py b/examples/agent_examples/toolReactPhase_example.py index 00136d2..93d0a15 100644 --- a/examples/agent_examples/toolReactPhase_example.py +++ b/examples/agent_examples/toolReactPhase_example.py @@ -5,49 +5,41 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( - toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, - TOOL_DICT, TOOL_SETS - ) +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, Memory, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -import importlib +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" -# init configs -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - -agent_module = importlib.import_module("dev_opsgpt.connector.agents") - -# phase_name = "toolReactPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") + ) +phase = BasePhase( + phase_name, sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + embed_config=embed_config, llm_config=llm_config, kb_root_path=KB_ROOT_PATH, +) + # round-1 tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - query_content = "帮我确认下127.0.0.1这个服务器的在10点是否存在异常,请帮我判断一下" query = Message( - role_name="huma", role_type="user", tools=tools, - role_content=query_content, input_query=query_content, origin_query=query_content, + role_name="human", role_type="user", tools=tools, + role_content=query_content, input_query=query_content, origin_query=query_content ) -output_message, _ = phase.step(query) +phase.pre_print(query) +# output_message, output_memory = phase.step(query) + +# print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/dev_opsgpt/service/api.py b/examples/api.py similarity index 88% rename from dev_opsgpt/service/api.py rename to examples/api.py index 0f7194d..2689169 100644 --- a/dev_opsgpt/service/api.py +++ b/examples/api.py @@ -8,28 +8,30 @@ from typing import List src_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) ) + sys.path.append(src_dir) sys.path.append(os.path.dirname(os.path.dirname(__file__))) -from configs import VERSION -from configs.model_config import NLTK_DATA_PATH -from configs.server_config import OPEN_CROSS_DOMAIN +from configs.model_config import VERSION +# from configs.model_config import NLTK_DATA_PATH +# from configs.server_config import OPEN_CROSS_DOMAIN -from dev_opsgpt.chat import LLMChat, SearchChat, KnowledgeChat -from dev_opsgpt.service.kb_api import * -from dev_opsgpt.service.cb_api import * -from dev_opsgpt.utils.server_utils import BaseResponse, ListResponse, FastAPI, MakeFastAPIOffline +from coagent.chat import LLMChat, SearchChat, KnowledgeChat +from coagent.service.kb_api import * +from coagent.service.cb_api import * +from coagent.utils.server_utils import BaseResponse, ListResponse, FastAPI, MakeFastAPIOffline + +# nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path + +from coagent.chat import LLMChat, SearchChat, KnowledgeChat, CodeChat +from configs.model_config import KB_ROOT_PATH -nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path -from dev_opsgpt.chat import LLMChat, SearchChat, KnowledgeChat, ToolChat, DataChat, CodeChat llmChat = LLMChat() searchChat = SearchChat() -knowledgeChat = KnowledgeChat() -toolChat = ToolChat() -dataChat = DataChat() +knowledgeChat = KnowledgeChat(kb_root_path=KB_ROOT_PATH) codeChat = CodeChat() @@ -46,7 +48,8 @@ def create_app(): # Add CORS middleware to allow all origins # 在config.py中设置OPEN_DOMAIN=True,允许跨域 # set OPEN_DOMAIN=True in config.py to allow cross-domain - if OPEN_CROSS_DOMAIN: + if False: + # if OPEN_CROSS_DOMAIN: app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -75,14 +78,7 @@ def create_app(): app.post("/chat/search_engine_chat", tags=["Chat"], summary="与搜索引擎对话")(searchChat.chat) - app.post("/chat/tool_chat", - tags=["Chat"], - summary="与搜索引擎对话")(toolChat.chat) - app.post("/chat/data_chat", - tags=["Chat"], - summary="与搜索引擎对话")(dataChat.chat) - app.post("/chat/code_chat", tags=["Chat"], summary="与代码库对话")(codeChat.chat) diff --git a/examples/auto_examples/agentchat_RetrievalChat.py b/examples/auto_examples/agentchat_RetrievalChat.py new file mode 100644 index 0000000..89a41ba --- /dev/null +++ b/examples/auto_examples/agentchat_RetrievalChat.py @@ -0,0 +1 @@ +# more use cases see ~/examples/agent_examples/docChatPhase_example.py diff --git a/examples/auto_examples/agentchat_function_call.py b/examples/auto_examples/agentchat_function_call.py new file mode 100644 index 0000000..e9d5442 --- /dev/null +++ b/examples/auto_examples/agentchat_function_call.py @@ -0,0 +1,21 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +# from configs.model_config import * +from coagent.connector.phase import BasePhase +from coagent.connector.agents import BaseAgent +from coagent.connector.chains import BaseChain +from coagent.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +from coagent.connector.utils import parse_section +import importlib + + +# update new agent configs +# tool learning 实现参考 ~/examples/agent_examples/toolReactPhase_example.py \ No newline at end of file diff --git a/examples/auto_examples/agentchat_teachability.py b/examples/auto_examples/agentchat_teachability.py new file mode 100644 index 0000000..4ef4d14 --- /dev/null +++ b/examples/auto_examples/agentchat_teachability.py @@ -0,0 +1 @@ +# 暂未实现memory management相关操作 \ No newline at end of file diff --git a/examples/auto_examples/agentchat_teaching.py b/examples/auto_examples/agentchat_teaching.py new file mode 100644 index 0000000..273a7ec --- /dev/null +++ b/examples/auto_examples/agentchat_teaching.py @@ -0,0 +1,146 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +# from configs.model_config import * +from coagent.connector.phase import BasePhase +from coagent.connector.agents import BaseAgent +from coagent.connector.chains import BaseChain +from coagent.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +from coagent.connector.utils import parse_section +import importlib + + +# update new agent configs +auto_feedback_from_code_execution_PROMPT = """#### Code React Assistance Guidance + +You are a helpful AI assistant. Solve tasks using your coding and language skills. +In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. + 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. + 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. +Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. +When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. +If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. +When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. +Reply "stopped" in the end when everything is done. + +#### Response Process + +**Question:** First, clarify the problem to be solved. + +**Thoughts:** Based on the question and observations above, provide the plan for executing this step. + +**Action Status:** Set to 'stopped' or 'code_executing'. If it's 'stopped', the action is to provide the final answer to the original question. If it's 'code_executing', the action is to write the code. + +**Action:** +```python +# Write your code here +import os +... +``` + +**Observation:** Check the results and effects of the executed code. + +... (Repeat this Thoughts/Action/Observation cycle as needed) + +**Thoughts:** I now know the final answer + +**Action Status:** stopped + +**Action:** The final answer to the original input question + + +""" + + +AGETN_CONFIGS.update({ + "auto_feedback_from_code_execution": { + "role": { + "role_prompt": auto_feedback_from_code_execution_PROMPT, + "role_type": "assistant", + "role_name": "auto_feedback_from_code_execution", + "role_desc": "", + "agent_type": "ReactAgent" + # "agent_type": "BaseAgent" + }, + "chat_turn": 5, + "stop": "\n**Observation:**", + "focus_agents": [], + "focus_message_keys": [], + }, +}) +# update new chain configs +CHAIN_CONFIGS.update({ + "auto_feedback_from_code_executionChain": { + "chain_name": "auto_feedback_from_code_executionChain", + "chain_type": "BaseChain", + "agents": ["auto_feedback_from_code_execution"], + "chat_turn": 1, + "do_checker": False, + "chain_prompt": "" + } +}) + +# update phase configs +PHASE_CONFIGS.update({ + "auto_feedback_from_code_executionPhase": { + "phase_name": "auto_feedback_from_code_executionPhase", + "phase_type": "BasePhase", + "chains": ["auto_feedback_from_code_executionChain"], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": False + }, +}) + + + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("coagent.connector.agents") + +# +phase_name = "auto_feedback_from_code_executionPhase" +phase = BasePhase(phase_name, task = None, + base_phase_config= PHASE_CONFIGS, + base_chain_config= CHAIN_CONFIGS, + base_role_config= AGETN_CONFIGS, + ) + +# round-1 +query_content = """ +Find arxiv papers that show how are people studying trust calibration in AI based systems +""" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" + ) + +output_message1, _ = phase.step(query) + + +# 重复auto_gen 其余task即可 +task2 = "analyze the above the results to list the application domains studied by these papers " + +task3 = """Use this data to generate a bar chart of domains and number of papers in that domain and save to a file +""" + +task4 = """Reflect on the sequence and create a recipe containing all the steps +necessary and name for it. Suggest well-documented, generalized python function(s) + to perform similar tasks for coding steps in future. Make sure coding steps and + non-coding steps are never mixed in one function. In the docstr of the function(s), + clarify what non-coding steps are needed to use the language skill of the assistant. +""" \ No newline at end of file diff --git a/examples/auto_examples/agentchat_web_info.py b/examples/auto_examples/agentchat_web_info.py new file mode 100644 index 0000000..f4d0b34 --- /dev/null +++ b/examples/auto_examples/agentchat_web_info.py @@ -0,0 +1,143 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +# from configs.model_config import * +from coagent.connector.phase import BasePhase +from coagent.connector.agents import BaseAgent +from coagent.connector.chains import BaseChain +from coagent.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +from coagent.connector.utils import parse_section +import importlib + + +# update new agent configs +auto_feedback_from_code_execution_PROMPT = """#### Code React Assistance Guidance + +You are a helpful AI assistant. Solve tasks using your coding and language skills. +In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. + 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. + 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. +Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. +When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. +If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. +When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. +Reply "stopped" in the end when everything is done. + +#### Response Process + +**Question:** First, clarify the problem to be solved. + +**Thoughts:** Based on the question and observations above, provide the plan for executing this step. + +**Action Status:** Set to 'stopped' or 'code_executing'. If it's 'stopped', the action is to provide the final answer to the original question. If it's 'code_executing', the action is to write the code. + +**Action:** +```python +# Write your code here +import os +... +``` + +**Observation:** Check the results and effects of the executed code. + +... (Repeat this Thoughts/Action/Observation cycle as needed) + +**Thoughts:** I now know the final answer + +**Action Status:** stopped + +**Action:** The final answer to the original input question + + +""" + + +AGETN_CONFIGS.update({ + "auto_feedback_from_code_execution": { + "role": { + "role_prompt": auto_feedback_from_code_execution_PROMPT, + "role_type": "assistant", + "role_name": "auto_feedback_from_code_execution", + "role_desc": "", + "agent_type": "ReactAgent" + # "agent_type": "BaseAgent" + }, + "chat_turn": 5, + "stop": "\n**Observation:**", + "focus_agents": [], + "focus_message_keys": [], + }, +}) +# update new chain configs +CHAIN_CONFIGS.update({ + "auto_feedback_from_code_executionChain": { + "chain_name": "auto_feedback_from_code_executionChain", + "chain_type": "BaseChain", + "agents": ["auto_feedback_from_code_execution"], + "chat_turn": 1, + "do_checker": False, + "chain_prompt": "" + } +}) + +# update phase configs +PHASE_CONFIGS.update({ + "auto_feedback_from_code_executionPhase": { + "phase_name": "auto_feedback_from_code_executionPhase", + "phase_type": "BasePhase", + "chains": ["auto_feedback_from_code_executionChain"], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": False + }, +}) + + + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("coagent.connector.agents") + +# +phase_name = "auto_feedback_from_code_executionPhase" +phase = BasePhase(phase_name, + task = None, + base_phase_config = PHASE_CONFIGS, + base_chain_config = CHAIN_CONFIGS, + base_role_config = AGETN_CONFIGS, + ) + +# # round-1 +# query_content = """Reply TERMINATE if the task has been solved at full satisfaction. +# Otherwise, reply CONTINUE, or the reason why the task is not solved yet.""" +# query = Message( +# role_name="human", role_type="user", +# role_content=query_content, input_query=query_content, origin_query=query_content, +# code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" +# ) + +# output_message1, _ = phase.step(query) + +# round-2 +# query_content = """Show me the YTD gain of 10 largest technology companies as of today.""" +# query = Message( +# role_name="human", role_type="user", +# role_content=query_content, input_query=query_content, origin_query=query_content, +# code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" +# ) + +# output_message1, _ = phase.step(query) + diff --git a/examples/auto_examples/auto_feedback_from_code_execution.py b/examples/auto_examples/auto_feedback_from_code_execution.py new file mode 100644 index 0000000..2c03ea4 --- /dev/null +++ b/examples/auto_examples/auto_feedback_from_code_execution.py @@ -0,0 +1,166 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH +from configs.server_config import SANDBOX_SERVER + +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message +import base64, openai + +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +from coagent.connector.utils import parse_section +import importlib + + +# update new agent configs +auto_feedback_from_code_execution_PROMPT = """#### Agent Profile + +You are a helpful AI assistant. Solve tasks using your coding and language skills. +In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. + 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. + 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. +Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. +When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. +If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. +When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. +Reply "stopped" in the end when everything is done. + +ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. + +#### Response Output Format + +**Thoughts:** Based on the question and observations above, provide the plan for executing this step. + +**Action Status:** Set to 'stopped' or 'code_executing'. If it's 'stopped', the action is to provide the final answer to the original question. If it's 'code_executing', the action is to write the code. + +**Action:** +```python +# Write your code here +import os +... +``` + +**Observation:** Check the results and effects of the executed code. + +... (Repeat this Thoughts/Action/Observation cycle as needed) + +**Thoughts:** I now know the final answer + +**Action Status:** stopped + +**Action:** The final answer to the original input question + + +""" + + +# Design your personal PROMPT INPPUT FORMAT +AUTO_FEEDBACK_FROM_CODE_EXECUTION_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} +] + + +# set a +AGETN_CONFIGS.update({ + "auto_feedback_from_code_execution": { + "role": { + "role_prompt": auto_feedback_from_code_execution_PROMPT, + "role_type": "assistant", + "role_name": "auto_feedback_from_code_execution", + "role_desc": "", + "agent_type": "ReactAgent" + }, + "prompt_config": AUTO_FEEDBACK_FROM_CODE_EXECUTION_PROMPT_CONFIGS, + "chat_turn": 5, + "stop": "\n**Observation:**", + "focus_agents": [], + "focus_message_keys": [], + }, +}) + +# update new chain configs +CHAIN_CONFIGS.update({ + "auto_feedback_from_code_executionChain": { + "chain_name": "auto_feedback_from_code_executionChain", + "chain_type": "BaseChain", + "agents": ["auto_feedback_from_code_execution"], + "chat_turn": 1, + "do_checker": False, + "chain_prompt": "" + } +}) + +# update phase configs +PHASE_CONFIGS.update({ + "auto_feedback_from_code_executionPhase": { + "phase_name": "auto_feedback_from_code_executionPhase", + "phase_type": "BasePhase", + "chains": ["auto_feedback_from_code_executionChain"], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": False + }, +}) + + +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) + +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) + +# +os.environ["log_verbose"] = "1" + +phase_name = "auto_feedback_from_code_executionPhase" +phase = BasePhase( + phase_name, + sandbox_server=SANDBOX_SERVER, jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + embed_config=embed_config, llm_config=llm_config, + base_phase_config = PHASE_CONFIGS, + base_chain_config = CHAIN_CONFIGS, + base_role_config = AGETN_CONFIGS, +) + + +# # round-1 +# query_content = "What date is today? Compare the year-to-date gain for META and TESLA." +# query = Message( +# role_name="human", role_type="user", +# role_content=query_content, input_query=query_content, origin_query=query_content, +# code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" +# ) + +# output_message1, _ = phase.step(query) + + + +# round-2 +query_content = """Plot a chart of META and TESLA's stock prices for the past year and save it as stock_price_ytd.png.""" + +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) \ No newline at end of file diff --git a/dev_opsgpt/service/llm_api.py b/examples/llm_api.py similarity index 99% rename from dev_opsgpt/service/llm_api.py rename to examples/llm_api.py index cbe5a90..9c6f135 100644 --- a/dev_opsgpt/service/llm_api.py +++ b/examples/llm_api.py @@ -27,9 +27,9 @@ from configs.model_config import llm_model_dict, LLM_MODEL, LLM_DEVICE, LOG_PATH from configs.server_config import ( FSCHAT_CONTROLLER, FSCHAT_MODEL_WORKERS, FSCHAT_OPENAI_API ) -from dev_opsgpt.service.utils import get_model_worker_config +from examples.utils import get_model_worker_config -from dev_opsgpt.utils.server_utils import ( +from coagent.utils.server_utils import ( MakeFastAPIOffline, ) from fastapi import FastAPI @@ -312,6 +312,9 @@ def create_model_worker_app(log_level: str = "INFO", **kwargs) -> FastAPI: args.quantization = None args.max_log_len = None args.tokenizer_revision = None + args.max_parallel_loading_workers = 1 + args.enforce_eager = True + args.max_context_len_to_capture = 8192 # 0.2.2 vllm需要新加的参数 args.max_paddings = 256 diff --git a/dev_opsgpt/service/model_workers/SparkApi.py b/examples/model_workers/SparkApi.py similarity index 100% rename from dev_opsgpt/service/model_workers/SparkApi.py rename to examples/model_workers/SparkApi.py diff --git a/dev_opsgpt/service/model_workers/__init__.py b/examples/model_workers/__init__.py similarity index 100% rename from dev_opsgpt/service/model_workers/__init__.py rename to examples/model_workers/__init__.py diff --git a/dev_opsgpt/service/model_workers/azure.py b/examples/model_workers/azure.py similarity index 96% rename from dev_opsgpt/service/model_workers/azure.py rename to examples/model_workers/azure.py index b695c1c..6abf76e 100644 --- a/dev_opsgpt/service/model_workers/azure.py +++ b/examples/model_workers/azure.py @@ -3,9 +3,11 @@ from fastchat.conversation import Conversation from .base import * # from server.utils import get_httpx_client from fastchat import conversation as conv -import json +import json, os from typing import List, Dict -from configs import logger, log_verbose +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) class AzureWorker(ApiModelWorker): diff --git a/dev_opsgpt/service/model_workers/baichuan.py b/examples/model_workers/baichuan.py similarity index 96% rename from dev_opsgpt/service/model_workers/baichuan.py rename to examples/model_workers/baichuan.py index e44b7bd..c1ee520 100644 --- a/dev_opsgpt/service/model_workers/baichuan.py +++ b/examples/model_workers/baichuan.py @@ -6,10 +6,12 @@ from fastchat.conversation import Conversation from .base import * # from server.utils import get_httpx_client from fastchat import conversation as conv -import sys +import sys, os import json from typing import List, Literal, Dict -from configs import logger, log_verbose +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) def calculate_md5(input_string): md5 = hashlib.md5() diff --git a/dev_opsgpt/service/model_workers/base.py b/examples/model_workers/base.py similarity index 98% rename from dev_opsgpt/service/model_workers/base.py rename to examples/model_workers/base.py index f807a20..88bc51f 100644 --- a/dev_opsgpt/service/model_workers/base.py +++ b/examples/model_workers/base.py @@ -1,5 +1,6 @@ from fastchat.conversation import Conversation -from configs import LOG_PATH +from configs.model_config import LOG_PATH +# from coagent.base_configs.env_config import LOG_PATH import fastchat.constants fastchat.constants.LOGDIR = LOG_PATH from fastchat.serve.base_model_worker import BaseModelWorker @@ -9,7 +10,7 @@ import sys from pydantic import BaseModel, root_validator import fastchat import asyncio -from dev_opsgpt.service.utils import get_model_worker_config +from examples.utils import get_model_worker_config from typing import Dict, List, Optional diff --git a/dev_opsgpt/service/model_workers/fangzhou.py b/examples/model_workers/fangzhou.py similarity index 96% rename from dev_opsgpt/service/model_workers/fangzhou.py rename to examples/model_workers/fangzhou.py index 0792c78..8d54e2d 100644 --- a/dev_opsgpt/service/model_workers/fangzhou.py +++ b/examples/model_workers/fangzhou.py @@ -1,9 +1,11 @@ from fastchat.conversation import Conversation from .base import * from fastchat import conversation as conv -import sys +import sys, os from typing import List, Literal, Dict -from configs import logger, log_verbose +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) class FangZhouWorker(ApiModelWorker): diff --git a/dev_opsgpt/service/model_workers/minimax.py b/examples/model_workers/minimax.py similarity index 98% rename from dev_opsgpt/service/model_workers/minimax.py rename to examples/model_workers/minimax.py index 6e47af1..84a3f87 100644 --- a/dev_opsgpt/service/model_workers/minimax.py +++ b/examples/model_workers/minimax.py @@ -2,10 +2,13 @@ from fastchat.conversation import Conversation from .base import * from fastchat import conversation as conv import sys +import os import json # from server.utils import get_httpx_client from typing import List, Dict -from configs import logger, log_verbose +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) class MiniMaxWorker(ApiModelWorker): diff --git a/dev_opsgpt/service/model_workers/openai.py b/examples/model_workers/openai.py similarity index 94% rename from dev_opsgpt/service/model_workers/openai.py rename to examples/model_workers/openai.py index 136e353..bd64e10 100644 --- a/dev_opsgpt/service/model_workers/openai.py +++ b/examples/model_workers/openai.py @@ -1,10 +1,12 @@ -import sys +import sys, os from fastchat.conversation import Conversation from .base import * from fastchat import conversation as conv import json from typing import List, Dict -from configs import logger +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) import openai from langchain import PromptTemplate, LLMChain @@ -81,7 +83,7 @@ class ExampleWorker(ApiModelWorker): if __name__ == "__main__": import uvicorn - from dev_opsgpt.utils.server_utils import MakeFastAPIOffline + from coagent.utils.server_utils import MakeFastAPIOffline from fastchat.serve.base_model_worker import app worker = ExampleWorker( diff --git a/dev_opsgpt/service/model_workers/qianfan.py b/examples/model_workers/qianfan.py similarity index 98% rename from dev_opsgpt/service/model_workers/qianfan.py rename to examples/model_workers/qianfan.py index fb4a780..42447e5 100644 --- a/dev_opsgpt/service/model_workers/qianfan.py +++ b/examples/model_workers/qianfan.py @@ -1,4 +1,4 @@ -import sys +import sys, os from fastchat.conversation import Conversation from .base import * # from server.utils import get_httpx_client @@ -7,7 +7,9 @@ import json from fastchat import conversation as conv import sys from typing import List, Literal, Dict -from configs import logger, log_verbose +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) MODEL_VERSIONS = { "ernie-bot-4": "completions_pro", diff --git a/dev_opsgpt/service/model_workers/qwen.py b/examples/model_workers/qwen.py similarity index 97% rename from dev_opsgpt/service/model_workers/qwen.py rename to examples/model_workers/qwen.py index d1a6544..620cf9f 100644 --- a/dev_opsgpt/service/model_workers/qwen.py +++ b/examples/model_workers/qwen.py @@ -1,13 +1,15 @@ import json import sys - +import os from fastchat.conversation import Conversation from http import HTTPStatus from typing import List, Literal, Dict from fastchat import conversation as conv from .base import * -from configs import logger, log_verbose +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) class QwenWorker(ApiModelWorker): diff --git a/dev_opsgpt/service/model_workers/tiangong.py b/examples/model_workers/tiangong.py similarity index 100% rename from dev_opsgpt/service/model_workers/tiangong.py rename to examples/model_workers/tiangong.py diff --git a/dev_opsgpt/service/model_workers/xinghuo.py b/examples/model_workers/xinghuo.py similarity index 98% rename from dev_opsgpt/service/model_workers/xinghuo.py rename to examples/model_workers/xinghuo.py index 8953cc3..84b66af 100644 --- a/dev_opsgpt/service/model_workers/xinghuo.py +++ b/examples/model_workers/xinghuo.py @@ -5,7 +5,7 @@ import sys import json from model_workers import SparkApi import websockets -from dev_opsgpt.utils.server_utils import run_async, iter_over_async +from coagent.utils.server_utils import run_async, iter_over_async from typing import List, Dict import asyncio diff --git a/dev_opsgpt/service/model_workers/zhipu.py b/examples/model_workers/zhipu.py similarity index 96% rename from dev_opsgpt/service/model_workers/zhipu.py rename to examples/model_workers/zhipu.py index b454ff9..0788b7b 100644 --- a/dev_opsgpt/service/model_workers/zhipu.py +++ b/examples/model_workers/zhipu.py @@ -1,10 +1,12 @@ from fastchat.conversation import Conversation +import os from .base import * from fastchat import conversation as conv import sys from typing import List, Dict, Iterator, Literal -from configs import logger, log_verbose - +from loguru import logger +# from configs import logger, log_verbose +log_verbose = os.environ.get("log_verbose", False) class ChatGLMWorker(ApiModelWorker): DEFAULT_EMBED_MODEL = "text_embedding" diff --git a/dev_opsgpt/service/sdfile_api.py b/examples/sdfile_api.py similarity index 81% rename from dev_opsgpt/service/sdfile_api.py rename to examples/sdfile_api.py index 2d92915..d4f17cd 100644 --- a/dev_opsgpt/service/sdfile_api.py +++ b/examples/sdfile_api.py @@ -1,7 +1,7 @@ import sys, os, json, traceback, uvicorn, argparse src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) sys.path.append(src_dir) @@ -9,17 +9,14 @@ from loguru import logger from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import StreamingResponse, FileResponse from fastapi import File, UploadFile -from dev_opsgpt.utils.server_utils import BaseResponse, ListResponse, DataResponse +from coagent.utils.server_utils import BaseResponse, ListResponse, DataResponse from configs.server_config import OPEN_CROSS_DOMAIN, SDFILE_API_SERVER -from configs.model_config import ( - JUPYTER_WORK_PATH -) -from configs import VERSION +from configs.model_config import JUPYTER_WORK_PATH +VERSION = "v0.1.0" async def sd_upload_file(file: UploadFile = File(...), work_dir: str = JUPYTER_WORK_PATH): # 保存上传的文件到服务器 @@ -53,16 +50,17 @@ async def sd_delete_file(filename: str, work_dir: str = JUPYTER_WORK_PATH): return {"data": False} -def create_app(): +def create_app(open_cross_domain, version=VERSION): app = FastAPI( title="DevOps-ChatBot API Server", - version=VERSION + version=version ) # MakeFastAPIOffline(app) # Add CORS middleware to allow all origins # 在config.py中设置OPEN_DOMAIN=True,允许跨域 # set OPEN_DOMAIN=True in config.py to allow cross-domain - if OPEN_CROSS_DOMAIN: + if open_cross_domain: + # if OPEN_CROSS_DOMAIN: app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -98,9 +96,8 @@ def create_app(): -app = create_app() - -def run_api(host, port, **kwargs): +def run_api(host, port, open_cross_domain, **kwargs): + app = create_app(open_cross_domain) if kwargs.get("ssl_keyfile") and kwargs.get("ssl_certfile"): uvicorn.run(app, host=host, @@ -117,7 +114,9 @@ if __name__ == "__main__": description='About DevOps-ChatBot, local knowledge based LLM with langchain' ' | 基于本地知识库的 LLM 问答') parser.add_argument("--host", type=str, default="0.0.0.0") - parser.add_argument("--port", type=int, default=SDFILE_API_SERVER["port"]) + parser.add_argument("--port", type=int, default="7862") + # parser.add_argument("--port", type=int, default=SDFILE_API_SERVER["port"]) + parser.add_argument("--open_cross_domain", type=bool, default=False) parser.add_argument("--ssl_keyfile", type=str) parser.add_argument("--ssl_certfile", type=str) # 初始化消息 @@ -125,6 +124,7 @@ if __name__ == "__main__": args_dict = vars(args) run_api(host=args.host, port=args.port, + open_cross_domain=args.open_cross_domain, ssl_keyfile=args.ssl_keyfile, ssl_certfile=args.ssl_certfile, ) \ No newline at end of file diff --git a/examples/start.py b/examples/start.py index 156ab38..a37022d 100644 --- a/examples/start.py +++ b/examples/start.py @@ -12,7 +12,7 @@ from configs.model_config import USE_FASTCHAT, JUPYTER_WORK_PATH from configs.server_config import ( NO_REMOTE_API, SANDBOX_SERVER, SANDBOX_IMAGE_NAME, SANDBOX_CONTRAINER_NAME, WEBUI_SERVER, API_SERVER, SDFILE_API_SERVER, CONTRAINER_NAME, IMAGE_NAME, DOCKER_SERVICE, - DEFAULT_BIND_HOST, NEBULA_GRAPH_SERVER + DEFAULT_BIND_HOST, NEBULA_GRAPH_SERVER, SANDBOX_DO_REMOTE ) @@ -49,7 +49,7 @@ def check_docker(client, container_name, do_stop=False): # wrap up db logger.info(f'inside {container_name}') # cp nebula data - res = container.exec_run('''sh chatbot/dev_opsgpt/utils/nebula_cp.sh''') + res = container.exec_run('''sh chatbot/coagent/utils/nebula_cp.sh''') logger.info(f'cp res={res}') # stop nebula service @@ -116,7 +116,6 @@ def start_sandbox_service(network_name ='my_network'): client = docker.from_env() # 启动容器 logger.info("start container sandbox service") - script_shs = ["bash jupyter_start.sh"] JUPYTER_WORK_PATH = "/home/user/chatbot/jupyter_work" script_shs = [f"cd /home/user/chatbot/jupyter_work && nohup jupyter-notebook --NotebookApp.token=mytoken --port=5050 --allow-root --ip=0.0.0.0 --notebook-dir={JUPYTER_WORK_PATH} --no-browser --ServerApp.disable_check_xsrf=True &"] ports = {f"{SANDBOX_SERVER['docker_port']}/tcp": f"{SANDBOX_SERVER['port']}/tcp"} @@ -150,9 +149,9 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): if DOCKER_SERVICE: client = docker.from_env() logger.info("start container service") - check_process("service/api.py", do_stop=True) - check_process("service/sdfile_api.py", do_stop=True) - check_process("service/sdfile_api.py", do_stop=True) + check_process("api.py", do_stop=True) + check_process("sdfile_api.py", do_stop=True) + check_process("sdfile_api.py", do_stop=True) check_process("webui.py", do_stop=True) mount = Mount( type='bind', @@ -193,10 +192,15 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): '''curl -X PUT -H "Content-Type: application/json" -d'{"heartbeat_interval_secs":"2"}' -s "http://127.0.0.1:19669/flags"''', '''curl -X PUT -H "Content-Type: application/json" -d'{"heartbeat_interval_secs":"2"}' -s "http://127.0.0.1:19779/flags"''', - "nohup python chatbot/dev_opsgpt/service/sdfile_api.py > /home/user/logs/sdfile_api.log 2>&1 &", + "pip install zdatafront-sdk-python==0.1.2 -i https://artifacts.antgroup-inc.cn/simple", + + "pip install jieba", + "pip install duckduckgo-search", + + "nohup python chatbot/examples/sdfile_api.py > /home/user/logs/sdfile_api.log 2>&1 &", f"export DUCKDUCKGO_PROXY=socks5://host.docker.internal:13659 && export SANDBOX_HOST={sandbox_host} &&\ - nohup python chatbot/dev_opsgpt/service/api.py > /home/user/logs/api.log 2>&1 &", - "nohup python chatbot/dev_opsgpt/service/llm_api.py > /home/user/ 2>&1 &", + nohup python chatbot/examples/api.py > /home/user/logs/api.log 2>&1 &", + "nohup python chatbot/examples/llm_api.py > /home/user/llm.log 2>&1 &", f"export DUCKDUCKGO_PROXY=socks5://host.docker.internal:13659 && export SANDBOX_HOST={sandbox_host} &&\ cd chatbot/examples && nohup streamlit run webui.py > /home/user/logs/start_webui.log 2>&1 &" ] @@ -208,25 +212,28 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): # 关闭之前启动的docker 服务 # check_docker(client, CONTRAINER_NAME, do_stop=True, ) - api_sh = "nohup python ../dev_opsgpt/service/api.py > ../logs/api.log 2>&1 &" - sdfile_sh = "nohup python ../dev_opsgpt/service/sdfile_api.py > ../logs/sdfile_api.log 2>&1 &" + # api_sh = "nohup python ../coagent/service/api.py > ../logs/api.log 2>&1 &" + api_sh = "nohup python api.py > ../logs/api.log 2>&1 &" + # sdfile_sh = "nohup python ../coagent/service/sdfile_api.py > ../logs/sdfile_api.log 2>&1 &" + sdfile_sh = "nohup python sdfile_api.py > ../logs/sdfile_api.log 2>&1 &" notebook_sh = f"nohup jupyter-notebook --NotebookApp.token=mytoken --port={SANDBOX_SERVER['port']} --allow-root --ip=0.0.0.0 --notebook-dir={JUPYTER_WORK_PATH} --no-browser --ServerApp.disable_check_xsrf=True > ../logs/sandbox.log 2>&1 &" - llm_sh = "nohup python ../dev_opsgpt/service/llm_api.py > ../logs/llm_api.log 2>&1 &" + # llm_sh = "nohup python ../coagent/service/llm_api.py > ../logs/llm_api.log 2>&1 &" + llm_sh = "nohup python llm_api.py > ../logs/llm_api.log 2>&1 &" webui_sh = "streamlit run webui.py" if USE_TTY else "streamlit run webui.py" - if check_process("jupyter-notebook --NotebookApp"): - logger.debug(f"{notebook_sh}") - subprocess.Popen(notebook_sh, shell=True) + # if SANDBOX_DO_REMOTE and check_process("jupyter-notebook --NotebookApp"): + # logger.debug(f"{notebook_sh}") + # subprocess.Popen(notebook_sh, shell=True) # - if not NO_REMOTE_API and check_process("service/api.py"): + if not NO_REMOTE_API and check_process("api.py"): subprocess.Popen(api_sh, shell=True) # - if USE_FASTCHAT and check_process("service/llm_api.py"): + if USE_FASTCHAT and check_process("llm_api.py"): subprocess.Popen(llm_sh, shell=True) # - if check_process("service/sdfile_api.py"): + if check_process("sdfile_api.py"): subprocess.Popen(sdfile_sh, shell=True) - + subprocess.Popen(webui_sh, shell=True) @@ -246,3 +253,4 @@ if __name__ == "__main__": break start_api_service(sandbox_host) + diff --git a/examples/stop.py b/examples/stop.py index 9d1c495..0c8f3ea 100644 --- a/examples/stop.py +++ b/examples/stop.py @@ -24,7 +24,7 @@ check_process(f"port=5050", do_stop=True) # check_docker(client, CONTRAINER_NAME, do_stop=True, ) -check_process("service/api.py", do_stop=True) -check_process("service/sdfile_api.py", do_stop=True) -check_process("service/llm_api.py", do_stop=True) +check_process("api.py", do_stop=True) +check_process("sdfile_api.py", do_stop=True) +check_process("llm_api.py", do_stop=True) check_process("webui.py", do_stop=True) diff --git a/dev_opsgpt/service/utils.py b/examples/utils.py similarity index 62% rename from dev_opsgpt/service/utils.py rename to examples/utils.py index 2787f81..30049e3 100644 --- a/dev_opsgpt/service/utils.py +++ b/examples/utils.py @@ -8,18 +8,24 @@ from loguru import logger -def get_model_worker_config(model_name: str = None) -> dict: +def get_model_worker_config( + model_name: str = None, + fastchat_mdoel_workers: dict = FSCHAT_MODEL_WORKERS, + online_llm_model: dict = ONLINE_LLM_MODEL, + llm_model_dict: dict = llm_model_dict, + llm_device: str = LLM_DEVICE + ) -> dict: ''' 加载model worker的配置项。 优先级:FSCHAT_MODEL_WORKERS[model_name] > ONLINE_LLM_MODEL[model_name] > FSCHAT_MODEL_WORKERS["default"] ''' - from dev_opsgpt.service import model_workers + from coagent.service import model_workers - config = FSCHAT_MODEL_WORKERS.get("default", {}).copy() - config.update(ONLINE_LLM_MODEL.get(model_name, {}).copy()) - config.update(FSCHAT_MODEL_WORKERS.get(model_name, {}).copy()) + config = fastchat_mdoel_workers.get("default", {}).copy() + config.update(online_llm_model.get(model_name, {}).copy()) + config.update(fastchat_mdoel_workers.get(model_name, {}).copy()) - if model_name in ONLINE_LLM_MODEL: + if model_name in online_llm_model: config["online_api"] = True if provider := config.get("provider"): try: @@ -33,7 +39,7 @@ def get_model_worker_config(model_name: str = None) -> dict: config["model_path"] = path if path and os.path.isdir(path): config["model_path_exists"] = True - config["device"] = LLM_DEVICE + config["device"] = llm_device # logger.debug(f"config: {config}") return config \ No newline at end of file diff --git a/examples/webui.py b/examples/webui.py index 1767699..eb031f4 100644 --- a/examples/webui.py +++ b/examples/webui.py @@ -16,13 +16,15 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.webui import * -from configs import VERSION, LLM_MODEL +from webui import * +from configs.model_config import VERSION, LLM_MODEL from configs.server_config import NO_REMOTE_API +from configs.model_config import CB_ROOT_PATH + +from configs.model_config import embedding_model_dict, kbs_config, EMBEDDING_MODEL, DEFAULT_VS_TYPE, WEB_CRAWL_PATH - -api = ApiRequest(base_url="http://127.0.0.1:7861", no_remote_api=NO_REMOTE_API) +api = ApiRequest(base_url="http://127.0.0.1:7861", no_remote_api=NO_REMOTE_API, cb_root_path=CB_ROOT_PATH) if __name__ == "__main__": @@ -88,3 +90,6 @@ if __name__ == "__main__": if selected_page in pages: pages[selected_page]["func"](api) + # pages["对话"]["func"](api, ) + # pages["知识库管理"]["func"](api, embedding_model_dict, kbs_config, EMBEDDING_MODEL, DEFAULT_VS_TYPE, WEB_CRAWL_PATH) + # pages["代码知识库管理"]["func"](api, ) diff --git a/dev_opsgpt/webui/__init__.py b/examples/webui/__init__.py similarity index 100% rename from dev_opsgpt/webui/__init__.py rename to examples/webui/__init__.py diff --git a/dev_opsgpt/webui/code.py b/examples/webui/code.py similarity index 79% rename from dev_opsgpt/webui/code.py rename to examples/webui/code.py index 5b6bffd..997edca 100644 --- a/dev_opsgpt/webui/code.py +++ b/examples/webui/code.py @@ -15,12 +15,14 @@ from st_aggrid import AgGrid, JsCode from st_aggrid.grid_options_builder import GridOptionsBuilder import pandas as pd -from configs.model_config import embedding_model_dict, kbs_config, EMBEDDING_MODEL, DEFAULT_VS_TYPE, WEB_CRAWL_PATH +# from configs.model_config import embedding_model_dict, kbs_config, EMBEDDING_MODEL, DEFAULT_VS_TYPE, WEB_CRAWL_PATH from .utils import * -from dev_opsgpt.utils.path_utils import * -from dev_opsgpt.service.service_factory import get_cb_details, get_cb_details_by_cb_name -from dev_opsgpt.orm import table_init +from coagent.utils.path_utils import * +from coagent.service.service_factory import get_cb_details, get_cb_details_by_cb_name +from coagent.orm import table_init + +from configs.model_config import EMBEDDING_DEVICE, EMBEDDING_ENGINE, EMBEDDING_MODEL, embedding_model_dict # SENTENCE_SIZE = 100 cell_renderer = JsCode("""function(params) {if(params.value==true){return '✓'}else{return '×'}}""") @@ -33,7 +35,7 @@ def file_exists(cb: str, selected_rows: List) -> Tuple[str, str]: ''' if selected_rows: file_name = selected_rows[0]["code_name"] - file_path = get_file_path(cb, file_name) + file_path = get_file_path(cb, file_name, KB_ROOT_PATH) if os.path.isfile(file_path): return file_name, file_path return "", "" @@ -84,7 +86,7 @@ def code_page(api: ApiRequest): accept_multiple_files=False, ) - do_interpret = st.checkbox('**代码解读**', value=True, help='代码解读会针对每个代码文件通过 LLM 获取解释并且向量化存储。当代码文件较多时,\ + do_interpret = st.checkbox('**代码解读**', value=False, help='代码解读会针对每个代码文件通过 LLM 获取解释并且向量化存储。当代码文件较多时,\ 导入速度会变慢,且如果使用收费 API 的话可能会造成较大花费。如果要使用基于描述的代码问答模式,此项必须勾选', key='do_interpret') logger.info(f'do_interpret={do_interpret}') @@ -109,7 +111,12 @@ def code_page(api: ApiRequest): cb_name, file, do_interpret, - no_remote_api=True + no_remote_api=True, + embed_engine=EMBEDDING_ENGINE, + embed_model=EMBEDDING_MODEL, + embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + embedding_device=EMBEDDING_DEVICE, + llm_model=LLM_MODEL, ) st.toast(ret.get("msg", " ")) st.session_state["selected_cb_name"] = cb_name @@ -140,7 +147,13 @@ def code_page(api: ApiRequest): use_container_width=True, ): ret = api.delete_code_base(cb, - no_remote_api=True) + no_remote_api=True, + embed_engine=EMBEDDING_ENGINE, + embed_model=EMBEDDING_MODEL, + embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + embedding_device=EMBEDDING_DEVICE, + llm_model=LLM_MODEL, + ) st.toast(ret.get("msg", "删除成功")) - time.sleep(0.5) + time.sleep(0.05) st.experimental_rerun() diff --git a/dev_opsgpt/webui/dialogue.py b/examples/webui/dialogue.py similarity index 80% rename from dev_opsgpt/webui/dialogue.py rename to examples/webui/dialogue.py index 659babd..3beae3f 100644 --- a/dev_opsgpt/webui/dialogue.py +++ b/examples/webui/dialogue.py @@ -5,12 +5,13 @@ from datetime import datetime from random import randint from .utils import * -from dev_opsgpt.utils import * -from dev_opsgpt.tools import TOOL_SETS -from dev_opsgpt.chat.search_chat import SEARCH_ENGINES -from dev_opsgpt.connector import PHASE_LIST, PHASE_CONFIGS -from dev_opsgpt.service.service_factory import get_cb_details_by_cb_name +from coagent.utils import * +from coagent.tools import TOOL_SETS +from coagent.chat.search_chat import SEARCH_ENGINES +from coagent.connector import PHASE_LIST, PHASE_CONFIGS +from coagent.service.service_factory import get_cb_details_by_cb_name +from configs.model_config import EMBEDDING_DEVICE, EMBEDDING_MODEL, embedding_model_dict, EMBEDDING_ENGINE, KB_ROOT_PATH chat_box = ChatBox( assistant_avatar="../sources/imgs/devops-chatbot2.png" ) @@ -26,7 +27,7 @@ import yaml # 加载YAML文件 webui_yaml_filename = "webui_zh.yaml" if True else "webui_en.yaml" -with open(os.path.join(cur_dir, f"yamls/{webui_yaml_filename}"), 'r', encoding='utf-8') as f: +with open(os.path.join(cur_dir, f"yamls/{webui_yaml_filename}"), 'r', encoding="utf-8") as f: try: webui_configs = yaml.safe_load(f) except yaml.YAMLError as exc: @@ -271,9 +272,9 @@ def dialogue_page(api: ApiRequest): if cols[2].button(webui_configs["sandbox"]["button_delete_name"],): api.web_sd_delete(download_file) - code_interpreter_on = st.toggle( - webui_configs["sandbox"]["toggle_doCodeInterpreter"]) and not_agent_qa - code_exec_on = st.toggle(webui_configs["sandbox"]["toggle_doAutoCodeExec"]) and not_agent_qa + # code_interpreter_on = st.toggle( + # webui_configs["sandbox"]["toggle_doCodeInterpreter"]) and not_agent_qa + # code_exec_on = st.toggle(webui_configs["sandbox"]["toggle_doAutoCodeExec"]) and not_agent_qa # Display chat messages from history on app rerun @@ -292,7 +293,11 @@ def dialogue_page(api: ApiRequest): if dialogue_mode == webui_configs["dialogue"]["mode"][0]: chat_box.ai_say(webui_configs["chat"]["chatbox_saying"]) text = "" - r = api.chat_chat(prompt, history, no_remote_api=True) + r = api.chat_chat( + prompt, history, no_remote_api=True, + embed_model=EMBEDDING_MODEL, embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + model_device=EMBEDDING_DEVICE, embed_engine=EMBEDDING_ENGINE, + llm_model=LLM_MODEL) for t in r: if error_msg := check_error_msg(t): # check whether error occured st.error(error_msg) @@ -308,10 +313,10 @@ def dialogue_page(api: ApiRequest): chat_box.update_msg(text, streaming=False) # 更新最终的字符串,去除光标 # 判断是否存在代码, 并提高编辑功能,执行功能 - code_text = api.codebox.decode_code_from_text(text) - GLOBAL_EXE_CODE_TEXT = code_text - if code_text and code_exec_on: - codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) + # code_text = api.codebox.decode_code_from_text(text) + # GLOBAL_EXE_CODE_TEXT = code_text + # if code_text and code_exec_on: + # codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) elif dialogue_mode == webui_configs["dialogue"]["mode"][4]: display_infos = [webui_configs["chat"]["chatbox_saying"]] if search_on: @@ -351,13 +356,19 @@ def dialogue_page(api: ApiRequest): "history_node_list": history_node_list, "isDetailed": is_detailed, "upload_file": interpreter_file, + "embed_model": EMBEDDING_MODEL, + "model_device": EMBEDDING_DEVICE, + "embed_model_path": embedding_model_dict[EMBEDDING_MODEL], + "embed_engine": EMBEDDING_ENGINE, + "kb_root_path": KB_ROOT_PATH, + "model_name": LLM_MODEL, } text = "" d = {"docs": []} for idx_count, d in enumerate(api.agent_achat(**input_kargs)): if error_msg := check_error_msg(d): # check whether error occured st.error(error_msg) - # logger.debug(f"d: {d['answer']}") + logger.debug(f"d: {d['answer']}") text = d["answer"] for text_length in range(0, len(text)+1, 10): chat_box.update_msg(text[:text_length+10], element_index=0, streaming=True) @@ -390,7 +401,12 @@ def dialogue_page(api: ApiRequest): ]) text = "" d = {"docs": []} - for idx_count, d in enumerate(api.knowledge_base_chat(prompt, selected_kb, kb_top_k, score_threshold, history)): + for idx_count, d in enumerate( + api.knowledge_base_chat( + prompt, selected_kb, kb_top_k, score_threshold, history, + embed_model=EMBEDDING_MODEL, embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + model_device=EMBEDDING_DEVICE, embed_engine=EMBEDDING_ENGINE, llm_model=LLM_MODEL) + ): if error_msg := check_error_msg(d): # check whether error occured st.error(error_msg) text += d["answer"] @@ -399,11 +415,11 @@ def dialogue_page(api: ApiRequest): # chat_box.update_msg("知识库匹配结果: \n\n".join(d["docs"]), element_index=1, streaming=False, state="complete") chat_box.update_msg(text, element_index=0, streaming=False) # 更新最终的字符串,去除光标 chat_box.update_msg("{webui_configs['chat']['chatbox_doc_result']}: \n\n".join(d["docs"]), element_index=1, streaming=False, state="complete") - # 判断是否存在代码, 并提高编辑功能,执行功能 - code_text = api.codebox.decode_code_from_text(text) - GLOBAL_EXE_CODE_TEXT = code_text - if code_text and code_exec_on: - codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) + # # 判断是否存在代码, 并提高编辑功能,执行功能 + # code_text = api.codebox.decode_code_from_text(text) + # GLOBAL_EXE_CODE_TEXT = code_text + # if code_text and code_exec_on: + # codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) elif dialogue_mode == webui_configs["dialogue"]["mode"][2]: logger.info('prompt={}'.format(prompt)) logger.info('history={}'.format(history)) @@ -420,7 +436,10 @@ def dialogue_page(api: ApiRequest): for idx_count, d in enumerate(api.code_base_chat(query=prompt, code_base_name=selected_cb, code_limit=cb_code_limit, history=history, cb_search_type=cb_search_type, - no_remote_api=True)): + no_remote_api=True, embed_model=EMBEDDING_MODEL, + embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + embed_engine=EMBEDDING_ENGINE, llm_model=LLM_MODEL + )): if error_msg := check_error_msg(d): st.error(error_msg) text += d["answer"] @@ -444,7 +463,12 @@ def dialogue_page(api: ApiRequest): ]) text = "" d = {"docs": []} - for idx_count, d in enumerate(api.search_engine_chat(prompt, search_engine, se_top_k, history)): + for idx_count, d in enumerate( + api.search_engine_chat( + prompt, search_engine, se_top_k, history, embed_model=EMBEDDING_MODEL, + embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + model_device=EMBEDDING_DEVICE, embed_engine=EMBEDDING_ENGINE, llm_model=LLM_MODEL) + ): if error_msg := check_error_msg(d): # check whether error occured st.error(error_msg) text += d["answer"] @@ -453,55 +477,55 @@ def dialogue_page(api: ApiRequest): # chat_box.update_msg("搜索匹配结果: \n\n".join(d["docs"]), element_index=1, streaming=False) chat_box.update_msg(text, element_index=0, streaming=False) # 更新最终的字符串,去除光标 chat_box.update_msg(f"{webui_configs['chat']['chatbox_search_result']}: \n\n".join(d["docs"]), element_index=1, streaming=False, state="complete") - # 判断是否存在代码, 并提高编辑功能,执行功能 - code_text = api.codebox.decode_code_from_text(text) - GLOBAL_EXE_CODE_TEXT = code_text - if code_text and code_exec_on: - codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) + # # 判断是否存在代码, 并提高编辑功能,执行功能 + # code_text = api.codebox.decode_code_from_text(text) + # GLOBAL_EXE_CODE_TEXT = code_text + # if code_text and code_exec_on: + # codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) # 将上传文件清空 st.session_state["interpreter_file_key"] += 1 st.experimental_rerun() - if code_interpreter_on: - with st.expander(webui_configs['sandbox']['expander_code_name'], False): - code_part = st.text_area( - webui_configs['sandbox']['textArea_code_name'], code_text, key="code_text") - cols = st.columns(2) - if cols[0].button( - webui_configs['sandbox']['button_modify_code_name'], - use_container_width=True, - ): - code_text = code_part - GLOBAL_EXE_CODE_TEXT = code_text - st.toast(webui_configs['sandbox']['text_modify_code']) + # if code_interpreter_on: + # with st.expander(webui_configs['sandbox']['expander_code_name'], False): + # code_part = st.text_area( + # webui_configs['sandbox']['textArea_code_name'], code_text, key="code_text") + # cols = st.columns(2) + # if cols[0].button( + # webui_configs['sandbox']['button_modify_code_name'], + # use_container_width=True, + # ): + # code_text = code_part + # GLOBAL_EXE_CODE_TEXT = code_text + # st.toast(webui_configs['sandbox']['text_modify_code']) - if cols[1].button( - webui_configs['sandbox']['button_exec_code_name'], - use_container_width=True - ): - if code_text: - codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) - st.toast(webui_configs['sandbox']['text_execing_code'],) - else: - st.toast(webui_configs['sandbox']['text_error_exec_code'],) + # if cols[1].button( + # webui_configs['sandbox']['button_exec_code_name'], + # use_container_width=True + # ): + # if code_text: + # codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) + # st.toast(webui_configs['sandbox']['text_execing_code'],) + # else: + # st.toast(webui_configs['sandbox']['text_error_exec_code'],) - #TODO 这段信息会被记录到history里 - if codebox_res is not None and codebox_res.code_exe_status != 200: - st.toast(f"{codebox_res.code_exe_response}") + # #TODO 这段信息会被记录到history里 + # if codebox_res is not None and codebox_res.code_exe_status != 200: + # st.toast(f"{codebox_res.code_exe_response}") - if codebox_res is not None and codebox_res.code_exe_status == 200: - st.toast(f"codebox_chat {codebox_res}") - chat_box.ai_say(Markdown(code_text, in_expander=True, title="code interpreter", unsafe_allow_html=True), ) - if codebox_res.code_exe_type == "image/png": - base_text = f"```\n{code_text}\n```\n\n" - img_html = "".format( - codebox_res.code_exe_response - ) - chat_box.update_msg(img_html, streaming=False, state="complete") - else: - chat_box.update_msg('```\n'+code_text+'\n```'+"\n\n"+'```\n'+codebox_res.code_exe_response+'\n```', - streaming=False, state="complete") + # if codebox_res is not None and codebox_res.code_exe_status == 200: + # st.toast(f"codebox_chat {codebox_res}") + # chat_box.ai_say(Markdown(code_text, in_expander=True, title="code interpreter", unsafe_allow_html=True), ) + # if codebox_res.code_exe_type == "image/png": + # base_text = f"```\n{code_text}\n```\n\n" + # img_html = "".format( + # codebox_res.code_exe_response + # ) + # chat_box.update_msg(img_html, streaming=False, state="complete") + # else: + # chat_box.update_msg('```\n'+code_text+'\n```'+"\n\n"+'```\n'+codebox_res.code_exe_response+'\n```', + # streaming=False, state="complete") now = datetime.now() with st.sidebar: diff --git a/dev_opsgpt/webui/document.py b/examples/webui/document.py similarity index 78% rename from dev_opsgpt/webui/document.py rename to examples/webui/document.py index ac0f774..16b2f72 100644 --- a/dev_opsgpt/webui/document.py +++ b/examples/webui/document.py @@ -7,12 +7,15 @@ from st_aggrid import AgGrid, JsCode from st_aggrid.grid_options_builder import GridOptionsBuilder import pandas as pd -from configs.model_config import embedding_model_dict, kbs_config, EMBEDDING_MODEL, DEFAULT_VS_TYPE, WEB_CRAWL_PATH from .utils import * -from dev_opsgpt.utils.path_utils import * -from dev_opsgpt.service.service_factory import get_kb_details, get_kb_doc_details -from dev_opsgpt.orm import table_init +from coagent.utils.path_utils import * +from coagent.service.service_factory import get_kb_details, get_kb_doc_details +from coagent.orm import table_init +from configs.model_config import ( + KB_ROOT_PATH, kbs_config, DEFAULT_VS_TYPE, WEB_CRAWL_PATH, + EMBEDDING_DEVICE, EMBEDDING_ENGINE, EMBEDDING_MODEL, embedding_model_dict +) # SENTENCE_SIZE = 100 @@ -44,18 +47,25 @@ def file_exists(kb: str, selected_rows: List) -> Tuple[str, str]: ''' if selected_rows: file_name = selected_rows[0]["file_name"] - file_path = get_file_path(kb, file_name) + file_path = get_file_path(kb, file_name, KB_ROOT_PATH) if os.path.isfile(file_path): return file_name, file_path return "", "" -def knowledge_page(api: ApiRequest): +def knowledge_page( + api: ApiRequest, + embedding_model_dict: dict = embedding_model_dict, + kbs_config: dict = kbs_config, + embedding_model: str = EMBEDDING_MODEL, + default_vs_type: str = DEFAULT_VS_TYPE, + web_crawl_path: str = WEB_CRAWL_PATH + ): # 判断表是否存在并进行初始化 table_init() try: - kb_list = {x["kb_name"]: x for x in get_kb_details()} + kb_list = {x["kb_name"]: x for x in get_kb_details(KB_ROOT_PATH)} except Exception as e: st.error("获取知识库信息错误,请检查是否已按照 `README.md` 中 `4 知识库初始化与迁移` 步骤完成初始化或迁移,或是否为数据库连接错误。") st.stop() @@ -94,7 +104,7 @@ def knowledge_page(api: ApiRequest): vs_type = cols[0].selectbox( "向量库类型", vs_types, - index=vs_types.index(DEFAULT_VS_TYPE), + index=vs_types.index(default_vs_type), key="vs_type", ) @@ -103,7 +113,7 @@ def knowledge_page(api: ApiRequest): embed_model = cols[1].selectbox( "Embedding 模型", embed_models, - index=embed_models.index(EMBEDDING_MODEL), + index=embed_models.index(embedding_model), key="embed_model", ) @@ -123,6 +133,9 @@ def knowledge_page(api: ApiRequest): knowledge_base_name=kb_name, vector_store_type=vs_type, embed_model=embed_model, + embed_engine=EMBEDDING_ENGINE, + embedding_device= EMBEDDING_DEVICE, + embed_model_path=embedding_model_dict[embed_model], ) st.toast(ret.get("msg", " ")) st.session_state["selected_kb_name"] = kb_name @@ -144,7 +157,11 @@ def knowledge_page(api: ApiRequest): # use_container_width=True, disabled=len(files) == 0, ): - data = [{"file": f, "knowledge_base_name": kb, "not_refresh_vs_cache": True} for f in files] + data = [{"file": f, "knowledge_base_name": kb, "not_refresh_vs_cache": True, "embed_model": EMBEDDING_MODEL, + "embed_model_path": embedding_model_dict[EMBEDDING_MODEL], + "model_device": EMBEDDING_DEVICE, + "embed_engine": EMBEDDING_ENGINE} + for f in files] data[-1]["not_refresh_vs_cache"]=False for k in data: pass @@ -171,8 +188,8 @@ def knowledge_page(api: ApiRequest): replace("?", " ").replace("=", " ").replace(".", " ").strip() html_name = "_".join(filename.split(" ",) + ["html.jsonl"]) text_name = "_".join(filename.split(" ",) + ["text.jsonl"]) - html_path = os.path.join(WEB_CRAWL_PATH, html_name,) - text_path = os.path.join(WEB_CRAWL_PATH, text_name,) + html_path = os.path.join(web_crawl_path, html_name,) + text_path = os.path.join(web_crawl_path, text_name,) # if not os.path.exists(text_dir) or : st.toast(base_url) st.toast(html_path) @@ -189,7 +206,11 @@ def knowledge_page(api: ApiRequest): if res["status"] == 200: st.toast(res["response"], icon="✔") - data = [{"file": text_path, "filename": text_name, "knowledge_base_name": kb, "not_refresh_vs_cache": False}] + data = [{"file": text_path, "filename": text_name, "knowledge_base_name": kb, "not_refresh_vs_cache": False, + "embed_model": EMBEDDING_MODEL, + "embed_model_path": embedding_model_dict[EMBEDDING_MODEL], + "model_device": EMBEDDING_DEVICE, + "embed_engine": EMBEDDING_ENGINE}] for k in data: ret = api.upload_kb_doc(**k) logger.info(ret) @@ -208,7 +229,7 @@ def knowledge_page(api: ApiRequest): # 知识库详情 # st.info("请选择文件,点击按钮进行操作。") - doc_details = pd.DataFrame(get_kb_doc_details(kb)) + doc_details = pd.DataFrame(get_kb_doc_details(kb, KB_ROOT_PATH)) if not len(doc_details): st.info(f"知识库 `{kb}` 中暂无文件") else: @@ -273,7 +294,11 @@ def knowledge_page(api: ApiRequest): use_container_width=True, ): for row in selected_rows: - api.update_kb_doc(kb, row["file_name"]) + api.update_kb_doc(kb, row["file_name"], + embed_engine=EMBEDDING_ENGINE,embed_model=EMBEDDING_MODEL, + embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + model_device=EMBEDDING_DEVICE + ) st.experimental_rerun() # 将文件从向量库中删除,但不删除文件本身。 @@ -283,7 +308,10 @@ def knowledge_page(api: ApiRequest): use_container_width=True, ): for row in selected_rows: - api.delete_kb_doc(kb, row["file_name"]) + api.delete_kb_doc(kb, row["file_name"], + embed_engine=EMBEDDING_ENGINE,embed_model=EMBEDDING_MODEL, + embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + model_device=EMBEDDING_DEVICE) st.experimental_rerun() if cols[3].button( @@ -292,7 +320,10 @@ def knowledge_page(api: ApiRequest): use_container_width=True, ): for row in selected_rows: - ret = api.delete_kb_doc(kb, row["file_name"], True) + ret = api.delete_kb_doc(kb, row["file_name"], True, + embed_engine=EMBEDDING_ENGINE,embed_model=EMBEDDING_MODEL, + embed_model_path=embedding_model_dict[EMBEDDING_MODEL], + model_device=EMBEDDING_DEVICE) st.toast(ret.get("msg", " ")) st.experimental_rerun() @@ -310,7 +341,10 @@ def knowledge_page(api: ApiRequest): with st.spinner("向量库重构中,请耐心等待,勿刷新或关闭页面。"): empty = st.empty() empty.progress(0.0, "") - for d in api.recreate_vector_store(kb): + for d in api.recreate_vector_store( + kb, vs_type=default_vs_type, embed_model=embedding_model, embedding_device=EMBEDDING_DEVICE, + embed_model_path=embedding_model_dict["embedding_model"], embed_engine=EMBEDDING_ENGINE, + ): if msg := check_error_msg(d): st.toast(msg) else: @@ -321,7 +355,7 @@ def knowledge_page(api: ApiRequest): "删除知识库", use_container_width=True, ): - ret = api.delete_knowledge_base(kb) + ret = api.delete_knowledge_base(kb,) st.toast(ret.get("msg", " ")) time.sleep(1) st.experimental_rerun() diff --git a/dev_opsgpt/webui/prompt.py b/examples/webui/prompt.py similarity index 76% rename from dev_opsgpt/webui/prompt.py rename to examples/webui/prompt.py index 3b9188c..47fc7a3 100644 --- a/dev_opsgpt/webui/prompt.py +++ b/examples/webui/prompt.py @@ -8,11 +8,10 @@ from st_aggrid import AgGrid, JsCode from st_aggrid.grid_options_builder import GridOptionsBuilder import pandas as pd -from configs.model_config import embedding_model_dict, kbs_config, EMBEDDING_MODEL, DEFAULT_VS_TYPE from .utils import * -from dev_opsgpt.utils.path_utils import * -from dev_opsgpt.service.service_factory import get_kb_details, get_kb_doc_details -from dev_opsgpt.orm import table_init +from coagent.utils.path_utils import * +from coagent.service.service_factory import get_kb_details, get_kb_doc_details +from coagent.orm import table_init diff --git a/dev_opsgpt/webui/utils.py b/examples/webui/utils.py similarity index 77% rename from dev_opsgpt/webui/utils.py rename to examples/webui/utils.py index 4500cdf..35f3892 100644 --- a/dev_opsgpt/webui/utils.py +++ b/examples/webui/utils.py @@ -22,23 +22,24 @@ from configs.model_config import ( VECTOR_SEARCH_TOP_K, SEARCH_ENGINE_TOP_K, NLTK_DATA_PATH, - logger, + JUPYTER_WORK_PATH, ) - from configs.server_config import SANDBOX_SERVER -from dev_opsgpt.utils.server_utils import run_async, iter_over_async -from dev_opsgpt.service.kb_api import * -from dev_opsgpt.service.cb_api import * -from dev_opsgpt.chat import LLMChat, SearchChat, KnowledgeChat, CodeChat, AgentChat -from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse -from dev_opsgpt.utils.common_utils import file_normalize, get_uploadfile +# from configs.server_config import SANDBOX_SERVER -from dev_opsgpt.codechat.code_crawler.zip_crawler import ZipCrawler +from coagent.utils.server_utils import run_async, iter_over_async +from coagent.service.kb_api import * +from coagent.service.cb_api import * +from coagent.chat import LLMChat, SearchChat, KnowledgeChat, CodeChat, AgentChat +from coagent.sandbox import PyCodeBox, CodeBoxResponse +from coagent.utils.common_utils import file_normalize, get_uploadfile + +from coagent.codechat.code_crawler.zip_crawler import ZipCrawler from web_crawler.utils.WebCrawler import WebCrawler -nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path +# nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path def set_httpx_timeout(timeout=60.0): @@ -51,7 +52,7 @@ def set_httpx_timeout(timeout=60.0): httpx._config.DEFAULT_TIMEOUT_CONFIG.write = timeout -KB_ROOT_PATH = Path(KB_ROOT_PATH) +# KB_ROOT_PATH = Path(KB_ROOT_PATH) set_httpx_timeout() @@ -67,28 +68,31 @@ class ApiRequest: sandbox_file_url: str = "http://127.0.0.1:7862", timeout: float = 60.0, no_remote_api: bool = False, # call api view function directly + cb_root_path: str = "", ): self.base_url = base_url self.sandbox_file_url = sandbox_file_url self.timeout = timeout self.no_remote_api = no_remote_api + self.cb_root_path = cb_root_path + self.llmChat = LLMChat() self.searchChat = SearchChat() - self.knowledgeChat = KnowledgeChat() + self.knowledgeChat = KnowledgeChat(kb_root_path=KB_ROOT_PATH) self.codeChat = CodeChat() self.agentChat = AgentChat() - self.codebox = PyCodeBox( - remote_url=SANDBOX_SERVER["url"], - remote_ip=SANDBOX_SERVER["host"], # "http://localhost", - remote_port=SANDBOX_SERVER["port"], - token="mytoken", - do_code_exe=True, - do_remote=SANDBOX_SERVER["do_remote"] - ) + # self.codebox = PyCodeBox( + # remote_url=self.sandbox_server["url"], + # remote_ip=self.sandbox_server["host"], # "http://localhost", + # remote_port=self.sandbox_server["port"], + # token="mytoken", + # do_code_exe=True, + # do_remote=self.sandbox_server["do_remote"] + # ) - def codebox_chat(self, text: str, file_path: str = None, do_code_exe: bool = None) -> CodeBoxResponse: - return self.codebox.chat(text, file_path, do_code_exe=do_code_exe) + # def codebox_chat(self, text: str, file_path: str = None, do_code_exe: bool = None) -> CodeBoxResponse: + # return self.codebox.chat(text, file_path, do_code_exe=do_code_exe) def _parse_url(self, url: str) -> str: if (not url.startswith("http") @@ -294,6 +298,8 @@ class ApiRequest: history: List[Dict] = [], stream: bool = True, no_remote_api: bool = None, + embed_model: str="", embed_model_path: str="", model_device: str="", embed_engine: str="", + llm_model: str ="", temperature: float= 0.2 ): ''' 对应api.py/chat/chat接口 @@ -305,6 +311,15 @@ class ApiRequest: "query": query, "history": history, "stream": stream, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_name": llm_model, + "temperature": temperature, + "model_device": model_device, + "temperature": temperature, } if no_remote_api: @@ -318,11 +333,13 @@ class ApiRequest: self, query: str, knowledge_base_name: str, - top_k: int = VECTOR_SEARCH_TOP_K, - score_threshold: float = SCORE_THRESHOLD, + top_k: int = 5, + score_threshold: float = 1.0, history: List[Dict] = [], stream: bool = True, no_remote_api: bool = None, + embed_model: str="", embed_model_path: str="", model_device: str="", embed_engine: str="", + llm_model: str ="", temperature: float= 0.2 ): ''' 对应api.py/chat/knowledge_base_chat接口 @@ -338,6 +355,15 @@ class ApiRequest: "history": history, "stream": stream, "local_doc_url": no_remote_api, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_name": llm_model, + "temperature": temperature, + "model_device": model_device, + "temperature": temperature, } if no_remote_api: @@ -359,6 +385,8 @@ class ApiRequest: history: List[Dict] = [], stream: bool = True, no_remote_api: bool = None, + embed_model: str="", embed_model_path: str="", model_device: str="", embed_engine: str="", + llm_model: str ="", temperature: float= 0.2 ): ''' 对应api.py/chat/search_engine_chat接口 @@ -372,6 +400,15 @@ class ApiRequest: "top_k": top_k, "history": history, "stream": stream, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_name": llm_model, + "temperature": temperature, + "model_device": model_device, + "temperature": temperature, } if no_remote_api: @@ -394,6 +431,8 @@ class ApiRequest: cb_search_type: str = 'tag', stream: bool = True, no_remote_api: bool = None, + embed_model: str="", embed_model_path: str="", model_device: str="", embed_engine: str="", + llm_model: str ="", temperature: float= 0.2 ): ''' 对应api.py/chat/knowledge_base_chat接口 @@ -419,6 +458,14 @@ class ApiRequest: "cb_search_type": cb_search_type, "stream": stream, "local_doc_url": no_remote_api, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_name": llm_model, + "temperature": temperature, + "model_device": model_device, } logger.info('data={}'.format(data)) @@ -459,6 +506,10 @@ class ApiRequest: history_node_list: List[str] = [], isDetailed: bool = False, upload_file: Union[str, Path, bytes] = "", + kb_root_path: str =KB_ROOT_PATH, + embed_model: str="", embed_model_path: str="", + model_device: str="", embed_engine: str="", + temperature: float=0.2, model_name:str ="", ): ''' 对应api.py/chat/chat接口 @@ -488,7 +539,18 @@ class ApiRequest: "choose_tools": choose_tools, "history_node_list": history_node_list, "isDetailed": isDetailed, - "upload_file": upload_file + "upload_file": upload_file, + "kb_root_path": kb_root_path, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_device": model_device, + "model_name": model_name, + "temperature": temperature, + "jupyter_work_path": JUPYTER_WORK_PATH, + "sandbox_server": SANDBOX_SERVER, } if no_remote_api: response = self.agentChat.chat(**data) @@ -522,6 +584,10 @@ class ApiRequest: history_node_list: List[str] = [], isDetailed: bool = False, upload_file: Union[str, Path, bytes] = "", + kb_root_path: str =KB_ROOT_PATH, + embed_model: str="", embed_model_path: str="", + model_device: str="", embed_engine: str="", + temperature: float=0.2, model_name: str="", ): ''' 对应api.py/chat/chat接口 @@ -552,7 +618,18 @@ class ApiRequest: "choose_tools": choose_tools, "history_node_list": history_node_list, "isDetailed": isDetailed, - "upload_file": upload_file + "upload_file": upload_file, + "kb_root_path": kb_root_path, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_device": model_device, + "model_name": model_name, + "temperature": temperature, + "jupyter_work_path": JUPYTER_WORK_PATH, + "sandbox_server": SANDBOX_SERVER, } if no_remote_api: @@ -614,8 +691,10 @@ class ApiRequest: self, knowledge_base_name: str, vector_store_type: str = "faiss", - embed_model: str = EMBEDDING_MODEL, no_remote_api: bool = None, + kb_root_path: str =KB_ROOT_PATH, + embed_model: str="", embed_model_path: str="", + embedding_device: str="", embed_engine: str="" ): ''' 对应api.py/knowledge_base/create_knowledge_base接口 @@ -626,7 +705,13 @@ class ApiRequest: data = { "knowledge_base_name": knowledge_base_name, "vector_store_type": vector_store_type, + "kb_root_path": kb_root_path, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], "embed_model": embed_model, + "embed_model_path": embed_model_path, + "model_device": embedding_device, + "embed_engine": embed_engine } if no_remote_api: @@ -643,15 +728,19 @@ class ApiRequest: self, knowledge_base_name: str, no_remote_api: bool = None, + kb_root_path: str =KB_ROOT_PATH, ): ''' 对应api.py/knowledge_base/delete_knowledge_base接口 ''' if no_remote_api is None: no_remote_api = self.no_remote_api - + data = { + "knowledge_base_name": knowledge_base_name, + "kb_root_path": kb_root_path, + } if no_remote_api: - response = run_async(delete_kb(knowledge_base_name)) + response = run_async(delete_kb(**data)) return response.dict() else: response = self.post( @@ -672,7 +761,7 @@ class ApiRequest: no_remote_api = self.no_remote_api if no_remote_api: - response = run_async(list_docs(knowledge_base_name)) + response = run_async(list_docs(knowledge_base_name, kb_root_path=KB_ROOT_PATH)) return response.data else: response = self.get( @@ -690,6 +779,9 @@ class ApiRequest: override: bool = False, not_refresh_vs_cache: bool = False, no_remote_api: bool = None, + kb_root_path: str = KB_ROOT_PATH, + embed_model: str="", embed_model_path: str="", + model_device: str="", embed_engine: str="" ): ''' 对应api.py/knowledge_base/upload_docs接口 @@ -716,7 +808,14 @@ class ApiRequest: UploadFile(file=temp_file, filename=filename), knowledge_base_name, override, - not_refresh_vs_cache + not_refresh_vs_cache, + kb_root_path=kb_root_path, + api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], + embed_model=embed_model, + embed_model_path=embed_model_path, + model_device=model_device, + embed_engine=embed_engine )) return response.dict() else: @@ -738,6 +837,9 @@ class ApiRequest: delete_content: bool = False, not_refresh_vs_cache: bool = False, no_remote_api: bool = None, + kb_root_path: str = KB_ROOT_PATH, + embed_model: str="", embed_model_path: str="", + model_device: str="", embed_engine: str="" ): ''' 对应api.py/knowledge_base/delete_doc接口 @@ -750,6 +852,13 @@ class ApiRequest: "doc_name": doc_name, "delete_content": delete_content, "not_refresh_vs_cache": not_refresh_vs_cache, + "kb_root_path": kb_root_path, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "model_device": model_device, + "embed_engine": embed_engine } if no_remote_api: @@ -768,6 +877,8 @@ class ApiRequest: file_name: str, not_refresh_vs_cache: bool = False, no_remote_api: bool = None, + embed_model: str="", embed_model_path: str="", + model_device: str="", embed_engine: str="" ): ''' 对应api.py/knowledge_base/update_doc接口 @@ -776,7 +887,14 @@ class ApiRequest: no_remote_api = self.no_remote_api if no_remote_api: - response = run_async(update_doc(knowledge_base_name, file_name, not_refresh_vs_cache)) + response = run_async(update_doc( + knowledge_base_name, file_name, not_refresh_vs_cache, kb_root_path=KB_ROOT_PATH, + api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], + embed_model=embed_model, + embed_model_path=embed_model_path, + model_device=model_device, + embed_engine=embed_engine)) return response.dict() else: response = self.post( @@ -793,9 +911,11 @@ class ApiRequest: self, knowledge_base_name: str, allow_empty_kb: bool = True, - vs_type: str = DEFAULT_VS_TYPE, - embed_model: str = EMBEDDING_MODEL, + vs_type: str = "faiss", no_remote_api: bool = None, + kb_root_path: str =KB_ROOT_PATH, + embed_model: str="", embed_model_path: str="", + embedding_device: str="", embed_engine: str="" ): ''' 对应api.py/knowledge_base/recreate_vector_store接口 @@ -807,7 +927,13 @@ class ApiRequest: "knowledge_base_name": knowledge_base_name, "allow_empty_kb": allow_empty_kb, "vs_type": vs_type, + "kb_root_path": kb_root_path, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], "embed_model": embed_model, + "embed_model_path": embed_model_path, + "model_device": embedding_device, + "embed_engine": embed_engine } if no_remote_api: @@ -913,7 +1039,10 @@ class ApiRequest: return self._check_httpx_json_response(response) # code base 相关操作 - def create_code_base(self, cb_name, zip_file, do_interpret: bool, no_remote_api: bool = None,): + def create_code_base(self, cb_name, zip_file, do_interpret: bool, no_remote_api: bool = None, + embed_model: str="", embed_model_path: str="", embedding_device: str="", embed_engine: str="", + llm_model: str ="", temperature: float= 0.2 + ): ''' 创建 code_base @param cb_name: @@ -924,21 +1053,28 @@ class ApiRequest: no_remote_api = self.no_remote_api # mkdir - cb_root_path = CB_ROOT_PATH + # cb_root_path = CB_ROOT_PATH mkdir_dir = [ - cb_root_path, - cb_root_path + os.sep + cb_name, - raw_code_path := cb_root_path + os.sep + cb_name + os.sep + 'raw_code' + self.cb_root_path, + self.cb_root_path + os.sep + cb_name, + raw_code_path := self.cb_root_path + os.sep + cb_name + os.sep + 'raw_code' ] for dir in mkdir_dir: - if not os.path.exists(dir): - os.makedirs(dir) + os.makedirs(dir, exist_ok=True) data = { "zip_file": zip_file, "cb_name": cb_name, "code_path": raw_code_path, - "do_interpret": do_interpret + "do_interpret": do_interpret, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_name": llm_model, + "temperature": temperature, + "model_device": embedding_device, } logger.info('create cb data={}'.format(data)) @@ -953,7 +1089,10 @@ class ApiRequest: logger.info('response={}'.format(response.json())) return self._check_httpx_json_response(response) - def delete_code_base(self, cb_name: str, no_remote_api: bool = None,): + def delete_code_base(self, cb_name: str, no_remote_api: bool = None, + embed_model: str="", embed_model_path: str="", embedding_device: str="", embed_engine: str="", + llm_model: str ="", temperature: float= 0.2 + ): ''' 删除 code_base @param cb_name: @@ -961,11 +1100,17 @@ class ApiRequest: ''' if no_remote_api is None: no_remote_api = self.no_remote_api - data = { - "cb_name": cb_name + "cb_name": cb_name, + "api_key": os.environ["OPENAI_API_KEY"], + "api_base_url": os.environ["API_BASE_URL"], + "embed_model": embed_model, + "embed_model_path": embed_model_path, + "embed_engine": embed_engine, + "model_name": llm_model, + "temperature": temperature, + "model_device": embedding_device } - if no_remote_api: response = run_async(delete_cb(**data)) return response.dict() diff --git a/dev_opsgpt/webui/yamls/webui_en.yaml b/examples/webui/yamls/webui_en.yaml similarity index 100% rename from dev_opsgpt/webui/yamls/webui_en.yaml rename to examples/webui/yamls/webui_en.yaml diff --git a/dev_opsgpt/webui/yamls/webui_zh.yaml b/examples/webui/yamls/webui_zh.yaml similarity index 100% rename from dev_opsgpt/webui/yamls/webui_zh.yaml rename to examples/webui/yamls/webui_zh.yaml diff --git a/jupyter_start.sh b/jupyter_start.sh deleted file mode 100644 index 953b0f5..0000000 --- a/jupyter_start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -nohup jupyter-notebook --NotebookApp.token=mytoken --port=5050 --allow-root --ip=0.0.0.0 --no-browser --ServerApp.disable_check_xsrf=True & \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index cb629a4..6d987a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ openai==0.28.1 sentence_transformers fschat==0.2.33 transformers>=4.31.0 -# torch~=2.0.0 +torch~=2.0.0 fastapi~=0.99.1 nltk~=3.8.1 uvicorn~=0.23.1 @@ -16,10 +16,10 @@ faiss-cpu nltk loguru pypdf -duckduckgo-search==3.9.11 +duckduckgo-search pysocks accelerate -docker +docker jupyter notebook websockets @@ -42,18 +42,18 @@ streamlit-aggrid>=0.3.4.post3 httpx javalang==0.13.0 -jsonref==1.1.0 +# jsonref==1.1.0 chromadb==0.4.17 nebula3-python==3.1.0 - +jieba # qwen model -protobuf==3.20.* -transformers_stream_generator -einops -auto-gptq -optimum -modelscope +# protobuf==3.20.* +# transformers_stream_generator +# einops +# auto-gptq +# optimum +# modelscope # vllm model -vllm==0.2.2; sys_platform == "linux" +# vllm; sys_platform == "linux" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..cacf52a --- /dev/null +++ b/setup.py @@ -0,0 +1,40 @@ +import setuptools + +# with open("README.md", "r", encoding="utf-8") as fh: +# long_description = fh.read() + +setuptools.setup( + name="coagent", + # version="0.0.3_beta", + version="0.0.1", + author="shanshi", + author_email="wyp311395@antgroup.com", + description="A multi-agent framework that facilitates the rapid construction of collaborative teams of agents.", + # long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/codefuse-ai/codefuse-chatbot", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3.9", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + ], + install_requires=[ + "openai==0.28.1", + "langchain==0.0.266", + "sentence_transformers", + "loguru", + "fastapi~=0.99.1", + "pandas", + "jieba", + "psutil", + "faiss-cpu", + "notebook", + # + "chromadb==0.4.17", + "javalang==0.13.0", + "nebula3-python==3.1.0", + # + "duckduckgo-search", + ], +) \ No newline at end of file diff --git a/sources/docs/langchain_text_10.jsonl b/sources/docs/langchain_text_10.jsonl new file mode 100644 index 0000000..baa3b8b --- /dev/null +++ b/sources/docs/langchain_text_10.jsonl @@ -0,0 +1,10 @@ +{"url": "https://www.langchain.asia/", "host_url": "https://www.langchain.asia", "title": "LangChain中文网: 500页超详细中文文档教程,助力LLM/chatGPT应用开发 – LangChain中文网", "all_text": "开始\nLangChain中文网: 500页超详细中文文档教程,助力LLM/chatGPT应用开发\n\nLangChain 是一个开发由语言模型驱动的应用程序的框架。我们相信最强大和不同的应用程序不仅会通过 API 调用语言模型,\n还会:\n\n数据感知\n: 将语言模型连接到其他数据源\n具有代理性质\n: 允许语言模型与其环境交互\n\nLangChain 框架是基于以上原则设计的。\n这是文档的 Python stable 稳定版本。\n关于 Python最新版本 v0.0.206 的文档,请参见\n这里 (opens in a new tab)\n。\n关于 LangChain 的纯概念指南请见\n这里 (opens in a new tab)\n。\n关于 JavaScript 的文档,请参见\n这里 (opens in a new tab)\n。\n关于 COOKBOOK 的文档,请参见\n这里 (opens in a new tab)\n。\n入门指南\n查看以下指南,了解如何使用 LangChain 创建语言模型应用程序的详细说明。\n\n入门文档\n\n模块\nLangChain 提供了对几个主要模块的支持。\n针对每个模块,我们提供一些入门示例、指南、参考文档和概念指南。\n这些模块按照逐渐增加的复杂性排列如下:\n\n模型(models)\n: LangChain 支持的各种模型类型和模型集成。\n\n提示(prompts)\n: 包括提示管理、提示优化和提示序列化。\n\n内存(memory)\n: 内存是在链/代理调用之间保持状态的概念。LangChain 提供了一个标准的内存接口、一组内存实现及使用内存的链/代理示例。\n\n索引(indexes)\n: 与您自己的文本数据结合使用时,语言模型往往更加强大——此模块涵盖了执行此操作的最佳实践。\n\n链(chains)\n: 链不仅仅是单个 LLM 调用,还包括一系列调用(无论是调用 LLM 还是不同的实用工具)。LangChain 提供了一种标准的链接口、许多与其他工具的集成。LangChain 提供了用于常见应用程序的端到端的链调用。\n\n代理(agents)\n: 代理涉及 LLM 做出行动决策、执行该行动、查看一个观察结果,并重复该过程直到完成。LangChain 提供了一个标准的代理接口,一系列可供选择的代理,以及端到端代理的示例。\n\n用例\n上述模块可以以多种方式使用。LangChain 还提供指导和帮助。以下是 LangChain 支持的一些常见用例。\n\n自治代理(autonomous agents)\n: 长时间运行的代理会采取多步操作以尝试完成目标。 AutoGPT 和 BabyAGI就是典型代表。\n\n代理模拟(agent simulations)\n: 将代理置于封闭环境中观察它们如何相互作用,如何对事件作出反应,是观察它们长期记忆能力的有趣方法。\n\n个人助理(personal assistants)\n: 主要的 LangChain 使用用例。个人助理需要采取行动、记住交互并具有您的有关数据的知识。\n\n问答(question answering)\n: 第二个重大的 LangChain 使用用例。仅利用这些文档中的信息来构建答案,回答特定文档中的问题。\n\n聊天机器人(chatbots)\n: 由于语言模型擅长生成文本,因此它们非常适合创建聊天机器人。\n\n查询表格数据(tabular)\n: 如果您想了解如何使用 LLM 查询存储在表格格式中的数据(csv、SQL、数据框等),请阅读此页面。\n\n代码理解(code)\n: 如果您想了解如何使用 LLM 查询来自 GitHub 的源代码,请阅读此页面。\n\n与 API 交互(apis)\n: 使LLM 能够与 API 交互非常强大,以便为它们提供更实时的信息并允许它们采取行动。\n\n提取(extraction)\n: 从文本中提取结构化信息。\n\n摘要(summarization)\n: 将较长的文档汇总为更短、更简洁的信息块。一种数据增强生成的类型。\n\n评估(evaluation)\n: 生成模型是极难用传统度量方法评估的。\n一种新的评估方式是使用语言模型本身进行评估。\nLangChain 提供一些用于辅助评估的提示/链。\n\n参考文档\nLangChain 的所有参考文档,都在这里。LangChain 的所有方法、类、安装方法和集成设置的完整文档。\n\n参考文档\n\nLangChain 生态系统\n其他公司/产品如何与 LangChain 协同工作的指南\n\nLangChain 生态系统\n\n资源集合#\n额外的资源集合,我们认为可能是有用的,因为您开发您的应用程序!\n\nLangChainHub (opens in a new tab)\n: LangChainHub 是一个分享和探索其他 prompts、chains 和 agents 的平台。\nGallery (opens in a new tab)\n: 我们最喜欢的使用 LangChain 的项目合集,有助于找到灵感或了解其他应用程序的实现方式。\nDeployments (opens in a new tab)\n: 部署 LangChain 应用程序的说明、代码片段和模板存储库的合集。\nTracing (opens in a new tab)\n: 使用追踪可视化 LangChain 中链和代理执行的指南。\nModel Laboratory (opens in a new tab)\n: 使用不同的 prompts、models 和 chains 进行实验是开发最佳应用程序的重要组成部分。Model Laboratory 使这个过程变得非常容易。\nDiscord (opens in a new tab)\n: 加入我们的 Discord,讨论关于 LangChain 的一切!\nYouTube (opens in a new tab)\n: LangChain 教程和视频的集合。\nProduction Support (opens in a new tab)\n: 随着您将 LangChains 发布到生产环境,我们乐于提供更全面的支持。请填写此表格,我们将设置一个专门的支持 Slack 频道。\n开始"} +{"url": "https://www.langchain.asia/getting_started/getting_started", "host_url": "https://www.langchain.asia", "title": "\n快速入门指南 – LangChain中文网", "all_text": "快速入门指南开始\n\n快速入门指南\n本教程将简要介绍如何使用 LangChain 构建端到端语言模型应用程序。\n安装\n首先,使用以下命令安装 LangChain:\n\n```code\npip install langchain\n# or\nconda install langchain -c conda-forge\n```\n\n环境设定\n使用 LangChain 通常需要与一个或多个模型提供程序、数据存储、 API 等集成。\n对于这个例子,我们将使用 OpenAI 的 API,所以我们首先需要安装他们的 SDK:\n\n```code\npip install openai\n```\n\n然后我们需要在终端设置环境变量。\n\n```code\nexport OPENAI_API_KEY=\"...\"\n```\n\n或者,你可以在 Jupiter 教程(或 Python 脚本)内部完成:\n\n```code\nimport os\nos.environ[\"OPENAI_API_KEY\"] = \"...\"\n```\n\n构建语言模型应用程序: LLM\n现在我们已经安装了 LangChain 并设置了我们的环境,我们可以开始构建我们的语言模型应用程序了。\nLangChain 提供了许多可用于构建语言模型应用程序的模块。\n模块可以组合起来创建更复杂的应用程序,或者单独用于简单的应用程序。\nLLM: 从语言模型中获取预测\nLangChain 最基本的构建块是对某些输入调用 LLM。\n让我们来看一个简单的例子。\n我们假设我们正在构建一个基于公司产品生成公司名称的服务。\n为此,我们首先需要导入 LLM 包装器。\n\n```code\nfrom langchain.llms import OpenAI\n```\n\nLLM初始化和调用\n然后我们可以用任何参数初始化包装器。\n在这个例子中,我们可能希望输出更加随机,所以我们将以温度(temperature)初始化它。\n\n```code\nllm = OpenAI(temperature=0.9)\n```\n\n我们现在可以根据一些输入调用它!\n\n```code\ntext = \"What would be a good company name for a company that makes colorful socks?\"\nprint(llm(text))\nFeetful of Fun\n```\n\n有关如何在 LangChain 中使用 LLM 的详细信息,请参阅 LLM 入门指南。\n提示模板(PromptTemplate): 管理 LLM 的提示\n调用 LLM 是很好的第一步,但这仅仅是个开始。\n通常在应用程序中使用 LLM 时,不会将用户输入直接发送到 LLM。\n相反,您可能接受用户输入并构造一个提示符,然后将其发送给 LLM。\n例如,在前一个示例中,我们传入的文本被硬编码为询问一家生产彩色袜子的公司的名称。在这个虚构的服务中,我们希望只获取描述公司业务的用户输入,然后用这些信息格式化提示符。\n使用LangChain,这个事情变得很简单!\n首先让我们定义提示模板:\n\n```code\nfrom langchain.prompts import PromptTemplate\n\nprompt = PromptTemplate(\ninput_variables=[\"product\"],\ntemplate=\"What is a good name for a company that makes {product}?\",\n)\n```\n\n现在让我们看看它是如何工作的!\n我们可以调用\n```code\n. format\n```\n方法来格式化它。\n\n```code\nprint(prompt.format(product=\"colorful socks\"))\nWhat is a good name for a company that makes colorful socks?\n```\n\n有关详细信息,请参阅入门指南中的提示。\n链: 在多步骤的工作流中组合 LLM 和提示\n到目前为止,我们已经自己处理了单独的\n```code\nPromptTemplate\n```\n和\n```code\nLLM\n```\n。\n但是,真正的应用程序不仅仅是一个,而是它们的组合。\n在 LangChain,链是由链组成的,可以是 LLM 这样的原始链,也可以是其他链。\n最核心的链类型是\n```code\nLLMChain\n```\n,它由\n```code\nPromptTemplate\n```\n和\n```code\nLLM\n```\n组成。\n扩展前面的示例,我们可以构造一个\n```code\nLLMChain\n```\n.\n它接受用户输入,使用 PromptTemplate 对其进行格式化,然后将格式化后的响应传递给\n```code\nLLM\n```\n。\n\n```code\nfrom langchain.prompts import PromptTemplate\nfrom langchain.llms import OpenAI\n\nllm = OpenAI(temperature=0.9)\nprompt = PromptTemplate(\ninput_variables=[\"product\"],\ntemplate=\"What is a good name for a company that makes {product}?\",\n)\n```\n\n我们现在可以创建一个非常简单的链: 它接受用户输入,用它格式化提示符,然后将它发送到 LLM:\n\n```code\nfrom langchain.chains import LLMChain\nchain = LLMChain(llm=llm, prompt=prompt)\n```\n\n现在我们可以运行该链,只指定产品!\n\n```code\nchain.run(\"colorful socks\")\n# -> '\\n\\nSocktastic!'\n```\n\n这就对了!你有第1个链 —— 1个 LLM 链。\n这是比较简单的链类型之一,但是了解它的工作原理将为您处理更复杂的链打下良好的基础。\n有关更多细节,请查看链接的入门指南。\n代理 Agent: 基于用户输入的动态调用链\n到目前为止,我们看到的链运行在一个预先确定的顺序。\n但是代理不再这样做: 它们使用 LLM 来确定要执行哪些操作以及按照什么顺序执行。\n操作可以使用工具并观察其输出,也可以返回给用户。\n如果使用得当,效果可以非常强大。\n在本教程中,我们将向您展示如何通过最简单、最高级别的 API 轻松使用代理。\n为了运好代理,您应该理解以下概念:\n\n工具(tools): 执行特定任务的功能。这可以是: Google 搜索、数据库查找、 Python REPL、其他链。工具的接口目前是一个函数,预计将有一个字符串作为输入,一个字符串作为输出。\n大语言模型(LLM): 为代理提供动力的语言模型。\n代理(agents): 要使用的代理。这应该是引用支持代理类的字符串。因为本教程主要关注最简单、最高级别的 API,所以它只涉及使用标准支持的代理。如果要实现自定义代理,请参阅自定义代理的文档(即将发布)。\n\n代理(agents) : 有关受支持的 Agent 及其规范的列表,请参见此处\n工具(tools) : 有关预定义工具及其规范的列表, 请参见此处.\n对于本例,您还需要安装 SerpAPI Python 包。\n\n```code\npip install google-search-results\n```\n\n并设置适当的环境变量。\n\n```code\nimport os\nos.environ[\"SERPAPI_API_KEY\"] = \"...\"\n```\n\n现在我们可以开始了!\n\n```code\nfrom langchain.agents import load_tools\nfrom langchain.agents import initialize_agent\nfrom langchain.agents import AgentType\nfrom langchain.llms import OpenAI\n\n# First, let's load the language model we're going to use to control the agent.\nllm = OpenAI(temperature=0)\n\n# Next, let's load some tools to use. Note that the `llm-math` tool uses an LLM, so we need to pass that in.\ntools = load_tools([\"serpapi\", \"llm-math\"], llm=llm)\n\n# Finally, let's initialize an agent with the tools, the language model, and the type of agent we want to use.\nagent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)\n\n# Now let's test it out!\nagent.run(\"What was the high temperature in SF yesterday in Fahrenheit? What is that number raised to the .023 power?\")\n```\n\n```code\n> Entering new AgentExecutor chain...\nI need to find the temperature first, then use the calculator to raise it to the .023 power.\nAction: Search\nAction Input: \"High temperature in SF yesterday\"\nObservation: San Francisco Temperature Yesterday. Maximum temperature yesterday: 57 °F (at 1:56 pm) Minimum temperature yesterday: 49 °F (at 1:56 am) Average temperature ...\nThought: I now have the temperature, so I can use the calculator to raise it to the .023 power.\nAction: Calculator\nAction Input: 57^.023\nObservation: Answer: 1.0974509573251117\nThought: I now know the final answer\nFinal Answer: The high temperature in SF yesterday in Fahrenheit raised to the .023 power is 1.0974509573251117.\n> Finished chain.\n```\n\n内存: 向链和代理添加状态\n到目前为止,我们经历过的所有工具和代理都是无状态的的。\n但是通常,您可能希望链或代理具有某种“内存”概念,以便它可以记住关于其以前的交互的信息。\n最简单明了的例子就是在设计一个聊天机器人时——你想让它记住之前的消息,这样它就可以利用这些消息的上下文来进行更好的对话。\n这是一种“短期记忆”。在更复杂的一面,你可以想象一个链条/代理随着时间的推移记住关键信息——这将是一种形式的“长期记忆”。关于后者的更多具体想法,请参阅这篇令人敬畏的论文。 (opens in a new tab)\nLangChain 提供了几个专门为此目的创建的链。 本教程使用其中一个链(\n\n```code\nConversationChain\n```\n\n) 和两种不同类型的内存来完成操作。\n默认情况下,,\n\n```code\nConversationChain\n```\n\n有一个简单的内存类型,它记住所有以前的输入/输出,并将它们添加到传递的上下文中。\n让我们看一下如何使用这个链(设置\n```code\nverbose=True\n```\n,这样我们就可以看到提示符)。\n\n```code\nfrom langchain import OpenAI, ConversationChain\nllm = OpenAI(temperature=0)\nconversation = ConversationChain(llm=llm, verbose=True)\noutput = conversation.predict(input=\"Hi there!\")\nprint(output)\n```\n\n```code\n> Entering new chain...\nPrompt after formatting:\nThe following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\nCurrent conversation:\nHuman: Hi there!\nAI:\n> Finished chain.\n' Hello! How are you today?'\n```\n\n```code\noutput = conversation.predict(input=\"I'm doing well! Just having a conversation with an AI.\")\nprint(output)\n```\n\n```code\n> Entering new chain...\nPrompt after formatting:\nThe following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\nCurrent conversation:\nHuman: Hi there!\nAI: Hello! How are you today?\nHuman: I'm doing well! Just having a conversation with an AI.\nAI:\n> Finished chain.\n\" That's great! What would you like to talk about?\"\n```\n\n构建语言模型应用程序: 聊天模型\n类似地,您可以使用聊天模型而不是 LLM。\n聊天模型是语言模型的一种变体。\n虽然聊天模型使用的是底层的语言模型,但它们公开的接口有些不同: 它们没有公开“文本输入、文本输出”API,而是公开了一个接口,其中“聊天消息”是输入和输出。\n聊天模型 API 是相当新的,所以我们仍然在找出正确的抽象。\n从聊天模型获取消息完成\n您可以通过向聊天模型传递一条或多条消息来完成聊天。\n响应将是一条消息。\nLangChain 中当前支持的消息类型是\n\n```code\nAIMessage\n```\n\n,\n\n```code\nHumanMessage\n```\n\n,\n\n```code\nSystemMessage\n```\n\n, 和\n\n```code\nChatMessage\n```\n\n–\n\n```code\nChatMessage\n```\n\n接受任意角色参数。大多数时候,您只需要处理\n\n```code\nHumanMessage\n```\n\n,\n\n```code\nAIMessage\n```\n\n, 和\n\n```code\nSystemMessage\n```\n\n.\n\n```code\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.schema import (\nAIMessage,\nHumanMessage,\nSystemMessage\n)\nchat = ChatOpenAI(temperature=0)\n```\n\n您可以通过传入单个消息来完成。\n\n```code\nchat([HumanMessage(content=\"Translate this sentence from English to French. I love programming.\")])\n# -> AIMessage(content=\"J'aime programmer.\", additional_kwargs={})\n```\n\n您还可以为 OpenAI 的 gpt-3.5-turbo 和 gpt-4型号传递多条消息。\n\n```code\nmessages = [\nSystemMessage(content=\"You are a helpful assistant that translates English to French.\"),\nHumanMessage(content=\"Translate this sentence from English to French. I love programming.\")\n]\nchat(messages)\n# -> AIMessage(content=\"J'aime programmer.\", additional_kwargs={})\n```\n\n您可以更进一步,使用\n```code\ngenerate\n```\n为多组消息生成完成。\n这将返回一个带有附加\n```code\nmessage\n```\n参数的\n```code\nLLMResult\n```\n。\n\n```code\nbatch_messages = [\n[\nSystemMessage(content=\"You are a helpful assistant that translates English to French.\"),\nHumanMessage(content=\"Translate this sentence from English to French. I love programming.\")\n],\n[\nSystemMessage(content=\"You are a helpful assistant that translates English to French.\"),\nHumanMessage(content=\"Translate this sentence from English to French. I love artificial intelligence.\")\n],\n]\nresult = chat.generate(batch_messages)\n\nresult\n# -> LLMResult(generations=[[ChatGeneration(text=\"J'aime programmer.\", generation_info=None, message=AIMessage(content=\"J'aime programmer.\", additional_kwargs={}))], [ChatGeneration(text=\"J'aime l'intelligence artificielle.\", generation_info=None, message=AIMessage(content=\"J'aime l'intelligence artificielle.\", additional_kwargs={}))]], llm_output={'token_usage': {'prompt_tokens': 71, 'completion_tokens': 18, 'total_tokens': 89}})\n```\n\n您可以从这个 LLMResult 中获取字符令牌的使用情况(token_usage):\n\n```code\nresult.llm_output['token_usage']\n# -> {'prompt_tokens': 71, 'completion_tokens': 18, 'total_tokens': 89}\n\n```\n\n聊天提示模板\n与 LLM 类似,您可以通过使用\n```code\nMessagePromptTemplate\n```\n来使用模板。\n可以从一个或多个\n```code\nMessagePromptTemplate\n```\n生成\n```code\nChatPromptTemplate\n```\n。\n您可以使用\n```code\nChatPromptTemplate\n```\n的\n```code\nformat _ tip\n```\n——这将返回一个\n```code\nPromptValue\n```\n,\n您可以将其转换为字符串或\n```code\nMessage\n```\n对象,具体取决于您是想将格式化的值用作\n```code\nllm\n```\n或聊天模型的输入。\n为了方便起见,在模板上公开了一个\n```code\nfrom _ template\n```\n方法。如果你使用这个模板,它看起来是这样的:\n\n```code\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\nChatPromptTemplate,\nSystemMessagePromptTemplate,\nHumanMessagePromptTemplate,\n)\n\nchat = ChatOpenAI(temperature=0)\ntemplate = \"You are a helpful assistant that translates {input_language} to {output_language}.\"\nsystem_message_prompt = SystemMessagePromptTemplate.from_template(template)\nhuman_template = \"{text}\"\nhuman_message_prompt = HumanMessagePromptTemplate.from_template(human_template)\nchat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])\n\n# get a chat completion from the formatted messages\nchat(chat_prompt.format_prompt(input_language=\"English\", output_language=\"French\", text=\"I love programming.\").to_messages())\n\n# -> AIMessage(content=\"J'aime programmer.\", additional_kwargs={})\n```\n\n带聊天模型的链\n上一节讨论的\n```code\nLLMChain\n```\n也可以用于聊天模型:\n\n```code\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain import LLMChain\nfrom langchain.prompts.chat import (\nChatPromptTemplate,\nSystemMessagePromptTemplate,\nHumanMessagePromptTemplate,\n)\n\nchat = ChatOpenAI(temperature=0)\ntemplate = \"You are a helpful assistant that translates {input_language} to {output_language}.\"\nsystem_message_prompt = SystemMessagePromptTemplate.from_template(template)\nhuman_template = \"{text}\"\nhuman_message_prompt = HumanMessagePromptTemplate.from_template(human_template)\nchat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])\nchain = LLMChain(llm=chat, prompt=chat_prompt)\nchain.run(input_language=\"English\", output_language=\"French\", text=\"I love programming.\")\n\n# -> \"J'aime programmer.\"\n```\n\n具有聊天模型的代理\n代理也可以与聊天模型一起使用,您可以使用\n```code\nAgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION\n```\n作为代理类型来初始化一个聊天模型。\n\n```code\nfrom langchain.agents import load_tools\nfrom langchain.agents import initialize_agent\nfrom langchain.agents import AgentType\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.llms import OpenAI\n\n# First, let's load the language model we're going to use to control the agent.\nchat = ChatOpenAI(temperature=0)\n\n# Next, let's load some tools to use. Note that the `llm-math` tool uses an LLM, so we need to pass that in.\nllm = OpenAI(temperature=0)\ntools = load_tools([\"serpapi\", \"llm-math\"], llm=llm)\n\n# Finally, let's initialize an agent with the tools, the language model, and the type of agent we want to use.\nagent = initialize_agent(tools, chat, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True)\n\n# Now let's test it out!\nagent.run(\"Who is Olivia Wilde's boyfriend? What is his current age raised to the 0.23 power?\")\n```\n\n```code\n> Entering new AgentExecutor chain...\nThought: I need to use a search engine to find Olivia Wilde's boyfriend and a calculator to raise his age to the 0.23 power.\nAction:\n{\n\"action\": \"Search\",\n\"action_input\": \"Olivia Wilde boyfriend\"\n}\nObservation: Sudeikis and Wilde's relationship ended in November 2020. Wilde was publicly served with court documents regarding child custody while she was presenting Don't Worry Darling at CinemaCon 2022. In January 2021, Wilde began dating singer Harry Styles after meeting during the filming of Don't Worry Darling.\nThought:I need to use a search engine to find Harry Styles' current age.\nAction:\n{\n\"action\": \"Search\",\n\"action_input\": \"Harry Styles age\"\n}\nObservation: 29 years\nThought:Now I need to calculate 29 raised to the 0.23 power.\nAction:\n{\n\"action\": \"Calculator\",\n\"action_input\": \"29^0.23\"\n}\nObservation: Answer: 2.169459462491557\nThought:I now know the final answer.\nFinal Answer: 2.169459462491557\n> Finished chain.\n'2.169459462491557'\n```\n\n记忆内存: 向链和代理添加状态\n您可以对链使用 Memory,对代理使用聊天模型进行初始化。\n这与 LLM 的 Memory 之间的主要区别在于,我们不需要将以前的所有消息压缩成一个字符串,而是可以将它们保留为自己独特的内存对象。\n\n```code\nfrom langchain.prompts import (\nChatPromptTemplate,\nMessagesPlaceholder,\nSystemMessagePromptTemplate,\nHumanMessagePromptTemplate\n)\nfrom langchain.chains import ConversationChain\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.memory import ConversationBufferMemory\n\nprompt = ChatPromptTemplate.from_messages([\nSystemMessagePromptTemplate.from_template(\"The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\"),\nMessagesPlaceholder(variable_name=\"history\"),\nHumanMessagePromptTemplate.from_template(\"{input}\")\n])\nllm = ChatOpenAI(temperature=0)\nmemory = ConversationBufferMemory(return_messages=True)\nconversation = ConversationChain(memory=memory, prompt=prompt, llm=llm)\nconversation.predict(input=\"Hi there!\")\n\n# -> 'Hello! How can I assist you today?'\nconversation.predict(input=\"I'm doing well! Just having a conversation with an AI.\")\n\n# -> \"That sounds like fun! I'm happy to chat with you. Is there anything specific you'd like to talk about?\"\nconversation.predict(input=\"Tell me about yourself.\")\n\n# -> \"Sure! I am an AI language model created by OpenAI. I was trained on a large dataset of text from the internet, which allows me to understand and generate human-like language. I can answer questions, provide information, and even have conversations like this one. Is there anything else you'd like to know about me?\"\n```\n开始代理(Agents)"} +{"url": "https://www.langchain.asia/modules/agents", "host_url": "https://www.langchain.asia", "title": "代理人(Agents) – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)\n\n代理人(Agents)\n概念指南 (opens in a new tab)\n有些应用程序不仅需要预先确定的LLM/其他工具调用链,而且可能需要根据用户输入的不同而产生不同的链条。\n在这些类型的链条中,有一个“代理人”可以访问一套工具。\n根据用户输入,代理人可以决定是否调用其中任何一个工具。\n在本文档的本节中,我们首先从“快速入门”开始,介绍如何以端到端的方式使用与代理人相关的所有内容。\n然后,我们将文档分成以下几个部分:\n工具(tools)\n介绍LangChain支持的各种工具的概述。\n代理人 (agents)\n介绍不同代理人类型的概述。\n工具包 (toolkits)\n介绍工具包的概述,以及LangChain支持的不同工具包的示例。开始入门(Getting Started)"} +{"url": "https://www.langchain.asia/modules/agents/getting_started", "host_url": "https://www.langchain.asia", "title": "快速入门# – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)入门(Getting Started)\n\n快速入门#\n代理使用LLM来确定采取哪些行动以及顺序。\n一个动作可以是使用工具并观察其输出,或返回给用户。\n当代理被正确使用时,它们可以非常强大。本教程的目的是向您展示如何通过最简单、最高级别的API轻松使用代理。\n为了加载代理,您应该了解以下概念:\n\n工具:执行特定职责的函数。这可以是诸如:Google搜索、数据库查找、Python REPL、其他链等。工具的接口目前是期望有一个字符串作为输入,一个字符串作为输出的函数。\n\nLLM:为代理提供动力的语言模型。\n\n代理:要使用的代理。这应该是一个引用支持代理类的字符串。因为本教程专注于最简单、最高级别的API,所以只涵盖使用标准支持的代理。如果您想实现自定义代理,请参阅自定义代理的文档(即将推出)。\n\n代理人:支持的代理人清单及其规格,请参见此处。\n工具:预定义工具及其规格的清单,请参见此处。\n\n```code\nfrom langchain.agents import load_tools\nfrom langchain.agents import initialize_agent\nfrom langchain.agents import AgentType\nfrom langchain.llms import OpenAI\n\n```\n\n首先,让我们加载我们要使用的语言模型来控制代理人。\n\n```code\nllm = OpenAI(temperature=0)\n\n```\n\n接下来,让我们加载一些要使用的工具。请注意,\n```code\nllm-math\n```\n工具使用LLM,因此我们需要传递它。\n\n```code\ntools = load_tools([\"serpapi\", \"llm-math\"], llm=llm)\n\n```\n\n最后,让我们使用工具、语言模型和我们想要使用的代理人类型初始化一个代理人。\n\n```code\nagent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)\n\n```\n\n现在让我们来测试一下吧!\n\n```code\nagent.run(\"Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?\")\n\n```\n\n```code\n> Entering new AgentExecutor chain...\nI need to find out who Leo DiCaprio's girlfriend is and then calculate her age raised to the 0.43 power.\nAction: Search\nAction Input: \"Leo DiCaprio girlfriend\"\nObservation: Camila Morrone\nThought: I need to find out Camila Morrone's age\nAction: Search\nAction Input: \"Camila Morrone age\"\nObservation: 25 years\nThought: I need to calculate 25 raised to the 0.43 power\nAction: Calculator\nAction Input: 25^0.43\nObservation: Answer: 3.991298452658078\n\nThought: I now know the final answer\nFinal Answer: Camila Morrone is Leo DiCaprio's girlfriend and her current age raised to the 0.43 power is 3.991298452658078.\n\n> Finished chain.\n\n```\n\n```code\n\"Camila Morrone is Leo DiCaprio's girlfriend and her current age raised to the 0.43 power is 3.991298452658078.\"\n\n```\n代理(Agents)代理(Agents)"} +{"url": "https://www.langchain.asia/modules/agents/agents", "host_url": "https://www.langchain.asia", "title": "代理人 – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)代理(Agents)\n\n代理人\n\n注意 概念指南 (opens in a new tab)\n\n在本文档的这一部分中,我们涵盖了不同类型的代理人,不考虑它们使用哪些特定工具。\n有关不同类型代理人的高级概述,请参阅以下文档。\n\n代理人类型\n\n有关如何创建自定义代理人的文档,请参见以下内容。\n\n自定义代理人\n自定义LLM代理人\n自定义LLM代理人(带有ChatModel)\n自定义MRKL代理人\n自定义MultiAction代理人\n具有工具检索的自定义代理人\n\n我们还有深入研究每种代理人类型的文档。\n\n对话代理人(用于聊天)\nLLM代理人\nMRKL代理人\nMultiAction代理人\n工具检索代理人\n入门(Getting Started)代理类型(Agent Types)"} +{"url": "https://www.langchain.asia/modules/agents/agents/agent_types", "host_url": "https://www.langchain.asia", "title": "代理类型# – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)代理(Agents)代理类型(Agent Types)\n\n代理类型#\n代理使用LLM(语言模型)来确定应采取哪些操作以及以何顺序执行这些操作。\n动作可能是使用工具并观察其输出,或向用户返回响应。\n以下是LangChain中可用的代理:\n\n```code\nzero-shot-react-description\n```\n#\n此代理使用ReAct框架,仅基于工具的描述来确定要使用的工具。\n可以提供任意数量的工具。\n此代理需要为每个工具提供描述。\n\n```code\nreact-docstore\n```\n#\n这个代理使用ReAct框架与文档存储进行交互。\n必须提供两个工具:一个\n```code\nSearch\n```\n工具和一个\n```code\nLookup\n```\n工具(它们必须被命名为这样)。\n\n```code\nSearch\n```\n工具应该搜索文档,而\n```code\nLookup\n```\n工具应该查找最近找到的文档中的一个术语。\n这个代理相当于最初的ReAct论文 (opens in a new tab),特别是维基百科的例子。\n\n```code\nself-ask-with-search\n```\n#\n这个代理使用一个被命名为\n```code\nIntermediate Answer\n```\n的工具。\n这个工具应该能够查找问题的事实性答案。\n这个代理相当于最初的self ask with search paper (opens in a new tab),其中提供了Google搜索API作为工具。\n\n```code\nconversational-react-description\n```\n#\n这个代理程序旨在用于对话环境中。提示设计旨在使代理程序有助于对话。\n它使用ReAct框架来决定使用哪个工具,并使用内存来记忆先前的对话交互。代理(Agents)自定义代理(Custom Agent)"} +{"url": "https://www.langchain.asia/modules/agents/agents/custom_agent", "host_url": "https://www.langchain.asia", "title": "自定义代理 (Custom Agents) – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)代理(Agents)自定义代理(Custom Agent)\n\n自定义代理 (Custom Agents)\n本教程将介绍如何创建自己的自定义代理 (Custom Agents)。\n一个代理 (Agent) 由二个部分组成:\n\n工具 tool:代理可以使用的工具。\n代理执行器 :这决定了采取哪些行动。\n\n在本教程里,我们将介绍如何创建自定义代理。\n\n```code\nfrom langchain.agents import Tool, AgentExecutor, BaseSingleActionAgent\nfrom langchain import OpenAI, SerpAPIWrapper\n\n```\n\n```code\nsearch = SerpAPIWrapper()\ntools = [\nTool(\nname = \"Search\",\nfunc=search.run,\ndescription=\"useful for when you need to answer questions about current events\",\nreturn_direct=True\n)\n]\n\n```\n\n```code\nfrom typing import List, Tuple, Any, Union\nfrom langchain.schema import AgentAction, AgentFinish\n\nclass FakeAgent(BaseSingleActionAgent):\n\"\"\"Fake Custom Agent.\"\"\"\n\n@property\ndef input_keys(self):\nreturn [\"input\"]\n\ndef plan(\nself, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any\n) -> Union[AgentAction, AgentFinish]:\n\"\"\"Given input, decided what to do.\n\nArgs:\nintermediate_steps: Steps the LLM has taken to date,\nalong with observations\n**kwargs: User inputs.\n\nReturns:\nAction specifying what tool to use.\n\"\"\"\nreturn AgentAction(tool=\"Search\", tool_input=kwargs[\"input\"], log=\"\")\n\nasync def aplan(\nself, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any\n) -> Union[AgentAction, AgentFinish]:\n\"\"\"Given input, decided what to do.\n\nArgs:\nintermediate_steps: Steps the LLM has taken to date,\nalong with observations\n**kwargs: User inputs.\n\nReturns:\nAction specifying what tool to use.\n\"\"\"\nreturn AgentAction(tool=\"Search\", tool_input=kwargs[\"input\"], log=\"\")\n```\n\n```code\nagent = FakeAgent()\n```\n\n```code\nagent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)\n```\n\n```code\nagent_executor.run(\"How many people live in canada as of 2023?\")\n```\n\n```code\n> Entering new AgentExecutor chain...\nThe current population of Canada is 38,669,152 as of Monday, April 24, 2023, based on Worldometer elaboration of the latest United Nations data.\n\n> Finished chain.\n```\n\n```code\n'The current population of Canada is 38,669,152 as of Monday, April 24, 2023, based on Worldometer elaboration of the latest United Nations data.'\n```\n代理类型(Agent Types)带工具检索的自定义代理(Custom Agent with Tool Retrieval)"} +{"url": "https://www.langchain.asia/modules/agents/agents/custom_agent_with_tool_retrieval", "host_url": "https://www.langchain.asia", "title": "带工具检索的自定义代理# – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)代理(Agents)带工具检索的自定义代理(Custom Agent with Tool Retrieval)\n\n带工具检索的自定义代理#\n\n带工具检索的自定义代理 custom-agent-with-tool-retrieval\n\n本教程,假定你已经熟悉代理工作原理。\n\n本教程介绍的新想法是使用检索来选择要用于回答代理查询的工具集。\n当你有很多工具可供选择时,这非常有用。你不能在提示中放置所有工具的描述(由于上下文长度问题),因此你动态选择你想要在运行时考虑使用的N个工具。\n我们将创建一个有点伪需求的例子。\n我们将有一个合适的工具(搜索),然后99个假工具,这只是废话。\n然后,我们将在提示模板中添加一个步骤,该步骤接受用户输入并检索与查询相关的工具。\n设置环境#\n进行必要的导入等设置。\n\n```code\nfrom langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser\nfrom langchain.prompts import StringPromptTemplate\nfrom langchain import OpenAI, SerpAPIWrapper, LLMChain\nfrom typing import List, Union\nfrom langchain.schema import AgentAction, AgentFinish\nimport re\n\n```\n\n设置工具#\n我们将创建一个合适的工具(搜索)和99个不相关的工具。\n\n```code\n# Define which tools the agent can use to answer user queries\nsearch = SerpAPIWrapper()\nsearch_tool = Tool(\nname = \"Search\",\nfunc=search.run,\ndescription=\"useful for when you need to answer questions about current events\"\n)\ndef fake_func(inp: str) -> str:\nreturn \"foo\"\nfake_tools = [\nTool(\nname=f\"foo-{i}\",\nfunc=fake_func,\ndescription=f\"a silly function that you can use to get more information about the number {i}\"\n)\nfor i in range(99)\n]\nALL_TOOLS = [search_tool] + fake_tools\n\n```\n\n工具检索器(tool-retriever)#\n我们将使用向量存储来为每个工具描述创建嵌入。\n然后,对于传入的查询,我们可以为该查询创建嵌入,并进行相关工具的相似性搜索。\n\n```code\nfrom langchain.vectorstores import FAISS\nfrom langchain.embeddings import OpenAIEmbeddings\nfrom langchain.schema import Document\n\n```\n\n```code\ndocs = [Document(page_content=t.description, metadata={\"index\": i}) for i, t in enumerate(ALL_TOOLS)]\n\n```\n\n```code\nvector_store = FAISS.from_documents(docs, OpenAIEmbeddings())\n\n```\n\n```code\nretriever = vector_store.as_retriever()\n\ndef get_tools(query):\ndocs = retriever.get_relevant_documents(query)\nreturn [ALL_TOOLS[d.metadata[\"index\"]] for d in docs]\n\n```\n\n现在我们可以测试这个检索器,看看它是否有效。\n\n```code\nget_tools(\"whats the weather?\")\n\n```\n\n```code\n[Tool(name='Search', description='useful for when you need to answer questions about current events', return_direct=False, verbose=False, callback_manager=, func=, params={'engine': 'google', 'google_domain': 'google.com', 'gl': 'us', 'hl': 'en'}, serpapi_api_key='c657176b327b17e79b55306ab968d164ee2369a7c7fa5b3f8a5f7889903de882', aiosession=None)>, coroutine=None),\nTool(name='foo-95', description='a silly function that you can use to get more information about the number 95', return_direct=False, verbose=False, callback_manager=, func=, coroutine=None),\nTool(name='foo-12', description='a silly function that you can use to get more information about the number 12', return_direct=False, verbose=False, callback_manager=, func=, coroutine=None),\nTool(name='foo-15', description='a silly function that you can use to get more information about the number 15', return_direct=False, verbose=False, callback_manager=, func=, coroutine=None)]\n\n```\n\n```code\nget_tools(\"whats the number 13?\")\n\n```\n\n```code\n[Tool(name='foo-13', description='a silly function that you can use to get more information about the number 13', return_direct=False, verbose=False, callback_manager=, func=, coroutine=None),\nTool(name='foo-12', description='a silly function that you can use to get more information about the number 12', return_direct=False, verbose=False, callback_manager=, func=, coroutine=None),\nTool(name='foo-14', description='a silly function that you can use to get more information about the number 14', return_direct=False, verbose=False, callback_manager=, func=, coroutine=None),\nTool(name='foo-11', description='a silly function that you can use to get more information about the number 11', return_direct=False, verbose=False, callback_manager=, func=, coroutine=None)]\n\n```\n\n提示模板#\n提示模板非常标准,因为我们实际上没有改变实际提示模板中的太多逻辑,而是只改变了如何进行检索。\n\n```code\n# Set up the base template\ntemplate = \"\"\"Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:\n\n{tools}\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin! Remember to speak as a pirate when giving your final answer. Use lots of \"Arg\"s\n\nQuestion: {input}\n{agent_scratchpad}\"\"\"\n\n```\n\n自定义提示模板现在具有一个\n```code\ntools_getter\n```\n的概念,我们对输入调用它以选择要使用的工具。\n\n```code\nfrom typing import Callable\n# Set up a prompt template\nclass CustomPromptTemplate(StringPromptTemplate):\n# The template to use\ntemplate: str\n############## NEW ######################\n# The list of tools available\ntools_getter: Callable\n\ndef format(self, **kwargs) -> str:\n# Get the intermediate steps (AgentAction, Observation tuples)\n# Format them in a particular way\nintermediate_steps = kwargs.pop(\"intermediate_steps\")\nthoughts = \"\"\nfor action, observation in intermediate_steps:\nthoughts += action.log\nthoughts += f\"\\nObservation: {observation}\\nThought: \"\n# Set the agent_scratchpad variable to that value\nkwargs[\"agent_scratchpad\"] = thoughts\n############## NEW ######################\ntools = self.tools_getter(kwargs[\"input\"])\n# Create a tools variable from the list of tools provided\nkwargs[\"tools\"] = \"\\n\".join([f\"{tool.name}: {tool.description}\" for tool in tools])\n# Create a list of tool names for the tools provided\nkwargs[\"tool_names\"] = \", \".join([tool.name for tool in tools])\nreturn self.template.format(**kwargs)\n\n```\n\n```code\nprompt = CustomPromptTemplate(\ntemplate=template,\ntools_getter=get_tools,\n# This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically\n# This includes the `intermediate_steps` variable because that is needed\ninput_variables=[\"input\", \"intermediate_steps\"]\n)\n\n```\n\n输出解析器 (Output Parser)#\n输出解析器与之前的教程没有改变,因为我们没有改变任何有关输出格式的内容。\n\n```code\nclass CustomOutputParser(AgentOutputParser):\n\ndef parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:\n# Check if agent should finish\nif \"Final Answer:\" in llm_output:\nreturn AgentFinish(\n# Return values is generally always a dictionary with a single `output` key\n# It is not recommended to try anything else at the moment :)\nreturn_values={\"output\": llm_output.split(\"Final Answer:\")[-1].strip()},\nlog=llm_output,\n)\n# Parse out the action and action input\nregex = r\"Action\\s*\\d*\\s*:(.*?)\\nAction\\s*\\d*\\s*Input\\s*\\d*\\s*:[\\s]*(.*)\"\nmatch = re.search(regex, llm_output, re.DOTALL)\nif not match:\nraise ValueError(f\"Could not parse LLM output: `{llm_output}`\")\naction = match.group(1).strip()\naction_input = match.group(2)\n# Return the action and action input\nreturn AgentAction(tool=action, tool_input=action_input.strip(\" \").strip('\"'), log=llm_output)\n\n```\n\n```code\noutput_parser = CustomOutputParser()\n\n```\n\n设置LLM,停止序列和代理#\n与之前的教程相同\n\n```code\nllm = OpenAI(temperature=0)\n\n```\n\n```code\n# LLM chain consisting of the LLM and a prompt\nllm_chain = LLMChain(llm=llm, prompt=prompt)\n\n```\n\n```code\ntools = get_tools(\"whats the weather?\")\ntool_names = [tool.name for tool in tools]\nagent = LLMSingleActionAgent(\nllm_chain=llm_chain,\noutput_parser=output_parser,\nstop=[\"\\nObservation:\"],\nallowed_tools=tool_names\n)\n\n```\n\n使用代理(Use the Agent)#\n现在我们可以使用它了!\n\n```code\nagent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)\n\n```\n\n```code\nagent_executor.run(\"What's the weather in SF?\")\n\n```\n\n```code\n> Entering new AgentExecutor chain...\nThought: I need to find out what the weather is in SF\nAction: Search\nAction Input: Weather in SF\n\nObservation:Mostly cloudy skies early, then partly cloudy in the afternoon. High near 60F. ENE winds shifting to W at 10 to 15 mph. Humidity71%. UV Index6 of 10. I now know the final answer\nFinal Answer: 'Arg, 'tis mostly cloudy skies early, then partly cloudy in the afternoon. High near 60F. ENE winds shiftin' to W at 10 to 15 mph. Humidity71%. UV Index6 of 10.\n\n> Finished chain.\n\n```\n\n```code\n\"'Arg, 'tis mostly cloudy skies early, then partly cloudy in the afternoon. High near 60F. ENE winds shiftin' to W at 10 to 15 mph. Humidity71%. UV Index6 of 10.\"\n\n```\n自定义代理(Custom Agent)LLM 聊天自定义代理(Custom LLM Chat Agent)"} +{"url": "https://www.langchain.asia/modules/agents/agents/custom_llm_chat_agent", "host_url": "https://www.langchain.asia", "title": "自定义LLM代理(带有ChatModel) – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)代理(Agents)LLM 聊天自定义代理(Custom LLM Chat Agent)\n\n自定义LLM代理(带有ChatModel)\n本笔记将介绍如何基于聊天模型创建自己的自定义代理。\nLLM聊天代理由三个部分组成:\n\nPromptTemplate:这是用于指示语言模型该做什么的提示模板\nChatModel:这是驱动代理的语言模型\n\n```code\nstop\n```\n序列:指示LLM在找到此字符串时停止生成\nOutputParser:确定如何将LLM输出解析为AgentAction或AgentFinish对象。\n\nLLMAgent用于代理执行器。这个代理执行器在很大程度上可以看作是一个循环:\n\n将用户输入和任何先前的步骤传递给代理(在这种情况下是LLMAgent)\n如果代理返回\n```code\nAgentFinish\n```\n,则将其直接返回给用户。\n带工具检索的自定义代理(Custom Agent with Tool Retrieval)MRKL 自定义代理(Custom MRKL Agent)"} +{"url": "https://www.langchain.asia/modules/agents/agents/custom_mrkl_agent", "host_url": "https://www.langchain.asia", "title": "自定义MRKL代理 – LangChain中文网", "all_text": "6大核心模块(Modules)代理(Agents)代理(Agents)MRKL 自定义代理(Custom MRKL Agent)\n\n自定义MRKL代理\n本文档介绍如何创建自己的自定义MRKL Agent。\nMRKL Agent由三个部分组成:\n\n工具:代理可用的工具。\nLLMChain:生成以一定方式解析的文本,以确定要采取哪个动作。\n代理类本身:解析LLMChain的输出,以确定要采取哪个动作。\n\n本文档介绍如何通过创建自定义LLMChain来创建自定义MRKL代理。\n自定义LLMChain(Custom LLMChain)#\n创建自定义代理的第一种方法是使用现有的代理类,但使用自定义LLMCain。\n这是创建自定义代理的最简单方法。\n强烈建议您使用\n```code\nZeroShotAgent\n```\n,因为目前这是最通用的一个。\n创建自定义LLMCain的大部分工作都归结为提示符。因为我们使用的是一个现有的代理类来解析输出,所以提示符中要生成该格式的文本是非常重要的。此外,我们目前需要一个 agent_scratchpad 输入变量来记录以前的操作和观察结果。\n这几乎总是提示符的最后一部分。\n但是,除了这些说明之外,您还可以根据需要自定义提示。\n为了确保提示符包含适当的指令,我们将在该类上使用\n```code\nhelper\n```\n方法。\n\n```code\nZeroShotAgent\n```\n的\n```code\nhelper\n```\n方法接受以下参数:\n\ntools:座席将有权访问的工具列表,用于设置提示的格式。\nprefix:要放在工具列表前面的字符串。\nsuffix: 放在工具列表后面的字符串。\ninput_variables:最后提示所期望的输入变量列表。\n\n在这个练习中,我们将给予我们的代理访问Google搜索,我们将定制它,我们将让它回答为盗版。\n\n```code\nfrom langchain.agents import ZeroShotAgent, Tool, AgentExecutor\nfrom langchain import OpenAI, SerpAPIWrapper, LLMChain\n```\n\n```code\nsearch = SerpAPIWrapper()\ntools = [\nTool(\nname = \"Search\",\nfunc=search.run,\ndescription=\"useful for when you need to answer questions about current events\"\n)\n]\n```\n\n```code\nprefix = \"\"\"Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:\"\"\"\nsuffix = \"\"\"Begin! Remember to speak as a pirate when giving your final answer. Use lots of \"Args\"\n\nQuestion: {input}\n{agent_scratchpad}\"\"\"\n\nprompt = ZeroShotAgent.create_prompt(\ntools,\nprefix=prefix,\nsuffix=suffix,\ninput_variables=[\"input\", \"agent_scratchpad\"]\n)\n```\n\n如果我们感到好奇,我们现在可以看看最终的提示模板,看看它看起来像当它的所有放在一起。\n\n```code\nprint(prompt.template)\n```\n\n```code\nAnswer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:\n\nSearch: useful for when you need to answer questions about current events\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [Search]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin! Remember to speak as a pirate when giving your final answer. Use lots of \"Args\"\n\nQuestion: {input}\n{agent_scratchpad}\n\n```\n\n请注意,我们能够为代理提供自定义的提示模板,即不限于由\n```code\ncreate_prompt\n```\n函数生成的提示,假设它满足代理的要求。\n例如,对于 ZeroShotAgent ,我们需要确保它满足以下要求。\n应该有一个以“Action:”开头的字符串和一个以“Action Input:”开头的字符串,并且两者都应该用换行符分隔。\n\n```code\nllm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)\n```\n\n```code\ntool_names = [tool.name for tool in tools]\nagent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=tool_names)\n```\n\n```code\nagent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)\n```\n\n```code\nagent_executor.run(\"How many people live in canada as of 2023?\")\n```\n\n```code\n> Entering new AgentExecutor chain...\nThought: I need to find out the population of Canada\nAction: Search\nAction Input: Population of Canada 2023\nObservation: The current population of Canada is 38,661,927 as of Sunday, April 16, 2023, based on Worldometer elaboration of the latest United Nations data.\nThought: I now know the final answer\nFinal Answer: Arrr, Canada be havin' 38,661,927 people livin' there as of 2023!\n\n> Finished chain.\n```\n\n```code\n\"Arrr, Canada be havin' 38,661,927 people livin' there as of 2023!\"\n```\n\n多路输入 (Multiple inputs)\n代理还可以处理需要多个输入的提示。\n\n```code\nprefix = \"\"\"Answer the following questions as best you can. You have access to the following tools:\"\"\"\nsuffix = \"\"\"When answering, you MUST speak in the following language: {language}.\n\nQuestion: {input}\n{agent_scratchpad}\"\"\"\n\nprompt = ZeroShotAgent.create_prompt(\ntools,\nprefix=prefix,\nsuffix=suffix,\ninput_variables=[\"input\", \"language\", \"agent_scratchpad\"]\n)\n```\n\n```code\nllm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)\n```\n\n```code\nagent = ZeroShotAgent(llm_chain=llm_chain, tools=tools)\n```\n\n```code\nagent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)\n```\n\n```code\nagent_executor.run(input=\"How many people live in canada as of 2023?\", language=\"italian\")\n```\n\n```code\n> Entering new AgentExecutor chain...\nThought: I should look for recent population estimates.\nAction: Search\nAction Input: Canada population 2023\nObservation: 39,566,248\nThought: I should double check this number.\nAction: Search\nAction Input: Canada population estimates 2023\nObservation: Canada's population was estimated at 39,566,248 on January 1, 2023, after a record population growth of 1,050,110 people from January 1, 2022, to January 1, 2023.\nThought: I now know the final answer.\nFinal Answer: La popolazione del Canada è stata stimata a 39.566.248 il 1° gennaio 2023, dopo un record di crescita demografica di 1.050.110 persone dal 1° gennaio 2022 al 1° gennaio 2023.\n\n> Finished chain.\n```\n\n```code\n'La popolazione del Canada è stata stimata a 39.566.248 il 1° gennaio 2023, dopo un record di crescita demografica di 1.050.110 persone dal 1° gennaio 2022 al 1° gennaio 2023.'\n```\nLLM 聊天自定义代理(Custom LLM Chat Agent)多动作自定义代理(Custom Multi-Action Agent)"} diff --git a/sources/docs_imgs/BaseAgent.png b/sources/docs_imgs/BaseAgent.png new file mode 100644 index 0000000..1f022d3 Binary files /dev/null and b/sources/docs_imgs/BaseAgent.png differ diff --git a/sources/docs_imgs/wechat.png b/sources/docs_imgs/wechat.png index 773c7f1..b67ff5c 100644 Binary files a/sources/docs_imgs/wechat.png and b/sources/docs_imgs/wechat.png differ diff --git a/sources/readme_docs/coagent/agent-flow-en.md b/sources/readme_docs/coagent/agent-flow-en.md new file mode 100644 index 0000000..abbb9d7 --- /dev/null +++ b/sources/readme_docs/coagent/agent-flow-en.md @@ -0,0 +1,43 @@ + +## Introduction to Core Connectors +To facilitate everyone's understanding of the entire CoAgent link, we use a Flow format to detail how to build through configuration settings. + +
+ 图片 +
+ + +
Below, we will first introduce the related core components
+ +### Agent +At the design level of the Agent, we provide four basic types of Agents, which allows for the basic role settings of these Agents to meet the interaction and usage of a variety of common scenarios. +1. BaseAgent: Provides basic question and answer, tool usage, and code execution functions. It implements Input => Output according to the Prompt format. + +
+ 图片 +
+ +2. ExecutorAgent: Executes tasks in sequence from a task list based on the plan arranged by the User or the previous Agent, completing the related tasks. +3. ReactAgent: Provides standard React functionality, based on the issue to perform the current task. +4. electorAgent: Provides the functionality of choosing an Agent. + +It selects the appropriate Agent to respond based on the question from the User or the previous Agent. After output, the message is pushed into the memory pool, which is subsequently managed by the Memory Manager. + +### Chain +Basic Chain: BaseChain, which connects the interaction of agents, completing the management of related messages and memory. + +### Phase +Basic Phase: BasePhase, which connects the interaction of chains, completing the management of related messages and memory. + +### Prompt Manager +Creation of prompts for each agent in a Multi-Agent link: + +- By simply setting prompt_input_keys and prompt_output_keys, one can reuse the preset Prompt Context creation logic, thus achieving rapid configuration of the agent prompt. +- The prompt manager module can also be redesigned with new key-context designs to implement a personalized Agent Prompt. + +### Memory Manager +Mainly used for the management of chat history, which is not yet completed: + +- Manages the reading and writing of chat history in the database, including user input, llm output, doc retrieval, code retrieval, search retrieval. +- Summarizes key information from the chat history to form a summary context, which serves as prompt context. +- Provides a search function to retrieve information related to the question from the chat history or the summary context, aiding in question and answer sessions. diff --git a/sources/readme_docs/coagent/agent-flow.md b/sources/readme_docs/coagent/agent-flow.md new file mode 100644 index 0000000..39f44f7 --- /dev/null +++ b/sources/readme_docs/coagent/agent-flow.md @@ -0,0 +1,41 @@ + +## 核心Connector介绍 +为了便于大家理解整个 CoAgent 的链路,我们采取 Flow 的形式来详细介绍如何通过配置构建 + +
+ 图片 +
+ + +
下面,我们先介绍相关的核心组件
+ +### Agent +在Agent设计层面,我们提供了四种基本的Agent类型,对这些Agent进行Role的基础设定,可满足多种通用场景的交互和使用 +1. BaseAgent:提供基础问答、工具使用、代码执行的功能,根据Prompt格式实现 输入 => 输出 + +
+ 图片 +
+ +2. ExecutorAgent:对任务清单进行顺序执行,根据 User 或 上一个Agent编排的计划,完成相关任务 +3. ReactAgent:提供标准React的功能,根据问题实现当前任务 +4. SelectorAgent:提供选择Agent的功能,根据User 或 上一个 Agent的问题选择合适的Agent来进行回答. + +输出后将 message push 到 memory pool 之中,后续通过Memory Manager进行管理 + +### Chain +基础链路:BaseChain,串联agent的交互,完成相关message和memory的管理 + +### Phase +基础场景:BasePhase,串联chain的交互,完成相关message和memory的管理 + +### Prompt Manager +Mutli-Agent链路中每一个agent的prompt创建 +- 通过对promtp_input_keys和promtp_output_keys对的简单设定,可以沿用预设 Prompt Context 创建逻辑,从而实现agent prompt快速配置 +- 也可以对prompt manager模块进行新的 key-context 设计,实现个性化的 Agent Prompt + +### Memory Manager +主要用于 chat history 的管理,暂未完成 +- 将chat history在数据库进行读写管理,包括user input、 llm output、doc retrieval、code retrieval、search retrieval +- 对 chat history 进行关键信息总结 summary context,作为 prompt context +- 提供检索功能,检索 chat history 或者 summary context 中与问题相关信息,辅助问答 diff --git a/sources/readme_docs/coagent/coagent-en.md b/sources/readme_docs/coagent/coagent-en.md new file mode 100644 index 0000000..f1487ae --- /dev/null +++ b/sources/readme_docs/coagent/coagent-en.md @@ -0,0 +1,47 @@ + +## 简介 +To enhance the performance of large language models (LLMs) in terms of inference accuracy, the industry has seen various innovative approaches to utilizing LLMs. From the earliest Chain of Thought (CoT), Text of Thought (ToT), to Graph of Thought (GoT), these methods have continually expanded the capability boundaries of LLMs. In dealing with complex problems, we can use the ReAct process to select, invoke, and execute tool feedback, achieving multi-round tool usage and multi-step execution. + +However, for more complex scenarios, such as the development of intricate code, single-function LLM Agents are clearly insufficient. Thus, the community has begun to develop combinations of multiple Agents, such as projects focused on metaGPT, GPT-Engineer, chatDev in the development domain, and AutoGen projects focused on automating the construction of Agents and Agent dialogue. + +After in-depth analysis of these frameworks, it has been found that most Agent frameworks are highly coupled, with poor usability and extensibility. They achieve specific scenarios in preset environments, but expanding these scenarios is fraught with difficulty. + +Therefore, we aim to build an extensible, user-friendly Multi-Agent framework to support ChatBots in retrieving knowledge base information while assisting with various common tasks such as daily office work, data analysis, and development operations. + +This Multi-Agent framework project incorporates excellent design elements from multiple frameworks, such as the message pool from metaGPT and the agent selector from autogen. + +
+ 图片 +
+ +The following modules will introduce the necessary components of the Multi Agent framework from five aspects: + +- **Agent Communication:** In the Multi-Agent framework, ensuring effective information exchange among Agents is crucial for managing context and improving Q&A efficiency. + - Follow a straightforward and intuitive chain-based dialogue principle, arranging Agents in a linear fashion to form an execution chain. + - Drawing from the Message Pool framework in metaGPT, Agents are allowed to push and subscribe to the Message Pool, making the chain more flexible. This is beneficial for fine-tuning the scenario of Prompt engineering but challenging to manage complex chain relationship analysis. + +- **Standard Operation Process (SOP)**: Standardizing the parsing and handling of LLM's generated results. + - Define the input and output scope of an Agent, assembling and parsing relevant Actions and Statuses to ensure the stability of the framework. + - Encapsulate a variety of fundamental Action execution modules, such as Tool Using, Planning, Coding, Direct Answering, final answer, etc., to meet the basic work requirements of an Agent. + +- **Plan and Executor**: Enhance LLM's tool usage, Agent scheduling, and code generation. Several basic chains have been set up, for example: + - a. Single-round Q&A, which can also be expanded to forms like CoT, ToT, GoT, etc. + - b. ReAct, a basic response decision-making process where the model sets SOP status to terminate the loop. + - c. Task Planning - Executor, where the task is completed and can end. +- **Long-short term memory Management**: The key difference between Multi-Agent and single Agent is that Multi-Agent needs to handle a large amount of communication information, similar to the process of human teamwork collaboration. Add an Agent specifically responsible for content summarization (similar to a meeting assistant) to summarize long-term memories and provide more effective information to the next Agent, rather than passing all content to the next one. +- **Human-agent interaction**: In the face of complex scenarios, human intervention is required in the Agent interaction process to provide feedback. Through the aforementioned Long-short term memory Management and Agent Communication processes, enable the LLM to accurately understand human intentions, thereby completing tasks more effectively. + +In summary, these five elements together construct a Multi-Agent framework, ensuring closer and more efficient cooperation between Agents while also adapting to more complex task requirements and a variety of interaction scenarios. By combining multiple Agent chains to implement a complete and complex project launch scenario (Dev Phase), such as Demand Chain (CEO), Product Argument Chain (CPO, CFO, CTO), Engineer Group Chain (Selector, Developer1~N), QA Engineer Chain (Developer, Tester), Deploy Chain (Developer, Deployer). + +## 模块分类 +- [connector](/sources/readme_docs/coagent/connector.md) +- document_loaders +- embeddings +- llm_models +- orm +- sandbox +- service +- text_splitter +- tools +- utils + diff --git a/sources/readme_docs/coagent/coagent.md b/sources/readme_docs/coagent/coagent.md new file mode 100644 index 0000000..2c02a6a --- /dev/null +++ b/sources/readme_docs/coagent/coagent.md @@ -0,0 +1,50 @@ + +## 📜 目录 +- [简介](#简介) +- [模块分类](#模块分类) + + +## 简介 + +为了提高大型模型在推理准确性方面的表现,业界出现了多种创新的大型语言模型(LLM)玩法。从最早的CoT、ToT到GoT,这些方法不断拓展了LLM的能力边界。在处理复杂问题时,我们可以通过ReAct过程来选择、调用和执行工具反馈,同时实现多轮工具使用和多步骤执行。 + +但对于更复杂的场景,例如复杂代码的开发,单一功能的LLM Agent显然难以胜任。因此,社区开始发展出多Agent的组合玩法,比如专注于metaGPT、GPT-Engineer、chatDev等开发领域的项目,以及专注于自动化构建Agent和Agent对话的AutoGen项目。 + +经过对这些框架的深入分析,发现大多数的Agent框架整体耦合度较高,其易用性和可扩展性较差。在预设场景中实现特定场景,但想要进行场景扩展却困难重重。 + +因此,我们希望构建一个可扩展、易于使用的Multi-Agent框架,以支持ChatBot在获取知识库信息的同时,能够辅助完成日常办公、数据分析、开发运维等各种通用任务。 + +本项目的Mutli-Agent框架汲取兼容了多个框架的优秀设计,比如metaGPT中的消息池(message pool)、autogen中的代理选择器(agent selector)等。 + +
+ 图片 +
+ +以下模块将从5个方面介绍Multi Agent框架所需要素: +- Agent Communication在Multi Agent框架中,确保Agent可以有效地进行信息交流对于管理上下文以及提高问答效率至关重要。 + a. 遵循简洁直观易于理解的链式对话原则,将Agent以线性方式排列串连成一个执行链路。 + b. 借鉴metaGPT中的Message Pool框架,允许Agent对Message Pool进行推送和订阅,使链路更加灵活。有利于精细化Prompt工程的场景,但难以把握复杂链路的关系分析。 +- Standard Operation Process(SOP):对LLM的生成结果进行标准化解析和处理。 + a. 定义Agent的 Input 和 Output 范围,能够组装和解析相关Action和Status,保证框架运行的稳定性 + b. 封装多种基础Action执行模块,如Tool Using、Planning、Coding、Direct Answering、final answer等SOP标识,以满足Agent的基本工作需求。 +- Plan and Executor:增加LLM的Tool使用、Agent调度、代码的生成。设置了几种基本链路,例如: + a. 单轮问答,也可以扩展到CoT、ToT、GoT等形式。 + b. ReAct,基础的响应决策过程,模型设置SOP 状态以终止循环 + c. TaskPlaning - Executor,任务完成即可结束 +- Long-short term memory Management:Multi-Agent与单Agent的关键区别在于,Multi-Agent需要处理大量的交流信息,类似人类团队协作的过程。增加一个专门负责内容总结(类似于会议助理)的Agent,对长期记忆进行总结并提更有效信息传递给下一位Agent,而非传递所有内容给下一位Agent。 +- Human-agent interaction:面对复杂场景时,需要人类介入Agent交互过程并提供反馈。通过上述 Long-short term memory Management 和 Agent Communication 过程,使LLM能准确理解人类的意图,从而更有效地完成任务。 + +总的来说,这五个要素共同构建了一个Multi Agent框架,确保Agent之间的协作更加紧密和高效,同时也能够适应更复杂的任务需求和更多样的交互场景。通过组合多个Agent链路来实现一个完整且复杂的项目上线场景(Dev Phase),如Demand Chain(CEO)、Product Arguement Chain(CPO、CFO、CTO)、Engineer Group Chain(Selector、Developer1~N)、QA Engineer Chain(Developer、Tester)、Deploy Chain(Developer、Deploer)。 + + +## 模块分类 +- [connector](/sources/readme_docs/coagent/connector.md) +- document_loaders +- embeddings +- llm_models +- orm +- sandbox +- service +- text_splitter +- tools +- utils diff --git a/sources/readme_docs/coagent/connector/connector_agent.md b/sources/readme_docs/coagent/connector/connector_agent.md new file mode 100644 index 0000000..0bc2e5b --- /dev/null +++ b/sources/readme_docs/coagent/connector/connector_agent.md @@ -0,0 +1,114 @@ +--- +title: Connector Agent +slug: Connector Agent ZH +url: "coagent/connector-agent-zh" +aliases: +- "/coagent/connector-agent-zh" +--- + + +## 快速构建一个Agent +- 首先增加openai配置,也可以是其它类似于openai接口的模型(通过fastchat启动) +``` +from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.configs import AGETN_CONFIGS +from coagent.connector.agents import BaseAgent +from coagent.connector.schema import Message, load_role_configs + + +os.environ["API_BASE_URL"] = OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = "sk-xx" +openai.api_key = "sk-xxx" +# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or "socks5://127.0.0.1:13659" +``` + + +- 配置相关 LLM 和 Embedding Model +``` +# LLM 和 Embedding Model 配置 +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) +``` + +- 这里从已有的agent配置选一个role来做示例 +``` +# 从已有的配置中选择一个config,具体参数细节见下面 +role_configs = load_role_configs(AGETN_CONFIGS) +agent_config = role_configs["general_planner"] +# 生成agent实例 +base_agent = BaseAgent( + role=agent_config.role, + prompt_config = agent_config.prompt_config, + prompt_manager_type=agent_config.prompt_manager_type, + chat_turn=agent_config.chat_turn, + focus_agents=[], + focus_message_keys=[], + llm_config=llm_config, + embed_config=embed_config, + jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + ) +# round-1 +query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message = base_agent.step(query) +print(output_message.to_str_content(content_key="parsed_output_list")) +``` + +## Agent 参数配置 +``` +# 配置结构在这个目录 +from coagent.connector.schema import Role, PromptField +``` + + +### Agent Config +|Config Key Name| Type| Description| +| ------------------ | ---------- | ---------- | +|role| Role |角色描述| +|prompt_config |List[PromptField] |Enum:PromptManager 也可以继承以上几种Agent然后去构造相关的Agent| +|prompt_manager_type |String |Enum:PromptManager 也可以继承以上几种Agent然后去构造自定义的Enum:PromptManager| +|focus_agents |List[String] |metagpt的逻辑,关注哪些agent生成的message,可选值范围为:role_name +|focus_message_keys |List[String]| 额外增加的逻辑,关注message里面具体的 key 信息可选值范围为:agent 的 output_keys| +|chat_turn |int |只针对ReactAgent有效| +|llm_config |LLMConfig |大语言模型配置| +|embed_config |EmbedConfig |向量模型配置| +|sandbox_server |Dict |沙盒环境即notebook启动配置| +|jupyter_work_path |str |沙盒环境的工作目录| +|kb_root_path |str |memory的存储路径| +|log_verbose |str |agent prompt&predict的日志打印级别| + +### Role + +| Config Key Name | Type | Description | +|------------------|------|--------------------| +| role_type | str | 角色类型, Enum: system、user、assistant、function、observation、summary | +| role_name | str | 角色名称 | +| role_desc | str | 角色描述 | +| agent_type | str | 代理类型 | +| role_prompt | str | 角色提示 | +| template_prompt | str | 模板提示 | + + +### PromptField + +| Config Key Name | Type | Description | +|-----------------|------|-------------| +| field_name | str | | +| function_name | str | | +| title | str | | +| description | str | | +| is_context | bool | | +| omit_if_empty | bool | | \ No newline at end of file diff --git a/sources/readme_docs/coagent/connector/connector_chain.md b/sources/readme_docs/coagent/connector/connector_chain.md new file mode 100644 index 0000000..897100f --- /dev/null +++ b/sources/readme_docs/coagent/connector/connector_chain.md @@ -0,0 +1,114 @@ +--- +title: Connector Chain +slug: Connector Chain ZH +url: "coagent/connector-chain-zh" +aliases: +- "/coagent/connector-chain-zh" +--- + +## 快速构建一个 agent chain +- 首先增加openai配置,也可以是其它类似于openai接口的模型(通过fastchat启动) +``` +# 设置openai的api-key +import os, sys +import openai +import importlib + +os.environ["API_BASE_URL"] = OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = "sk-xxxx" +openai.api_key = "sk-xxxx" +# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or "socks5://127.0.0.1:13659" +``` + +- 配置相关 LLM 和 Embedding Model +``` +# LLM 和 Embedding Model 配置 +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) +``` + + +- 这里从已有的agent配置选多个role组合成 agent chain +``` +from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.configs import AGETN_CONFIGS +from coagent.connector.chains import BaseChain +from coagent.connector.schema import Message, load_role_configs + +# 构建 agent chain 链路 +role_configs = load_role_configs(AGETN_CONFIGS) +agent_config = role_configs["general_planner"] +role1 = role_configs["general_planner"] +role2 = role_configs["executor"] +agent_module = importlib.import_module("examples.connector.agents") +agents = [ + getattr(agent_module, role1.role.agent_type)( + role=role1.role, + prompt_config = role1.prompt_config, + prompt_manager_type=role1.prompt_manager_type, + chat_turn=role1.chat_turn, + focus_agents=role1.focus_agents, + focus_message_keys=role1.focus_message_keys, + llm_config=llm_config, + embed_config=embed_config, + jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + ), + getattr(agent_module, role2.role.agent_type)( + role=role2.role, + prompt_config = role2.prompt_config, + prompt_manager_type=role2.prompt_manager_type, + chat_turn=role2.chat_turn, + focus_agents=role2.focus_agents, + focus_message_keys=role2.focus_message_keys, + llm_config=llm_config, + embed_config=embed_config, + jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + ), + ] + +chain = BaseChain( + agents, + chat_turn=1, + jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + llm_config=llm_config, + embed_config=embed_config, + ) +``` + + +- 开始执行 +``` +# round-1 +query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, output_memory = chain.step(query) +print(output_memory.to_str_messages(content_key="parsed_output_list")) + +``` + + +## Chain 参数配置 +|Config Key Name| Type |Description| +| ------------------ | ---------- | ---------- | +|agents| List[BaseAgent] | +|llm_config |LLMConfig |大语言模型配置| +|embed_config |EmbedConfig |向量模型配置| +|sandbox_server |Dict |沙盒环境即notebook启动配置| +|jupyter_work_path |str |沙盒环境的工作目录| +|kb_root_path |str |memory的存储路径| +|log_verbose |str |agent prompt&predict的日志打印级别| diff --git a/sources/readme_docs/coagent/connector/connector_memory.md b/sources/readme_docs/coagent/connector/connector_memory.md new file mode 100644 index 0000000..75057d2 --- /dev/null +++ b/sources/readme_docs/coagent/connector/connector_memory.md @@ -0,0 +1,93 @@ +--- +title: Connector Memory +slug: Connector Memory ZH +url: "coagent/connector-memory-zh" +aliases: +- "/coagent/connector-memory-zh" +--- + + +## Memory Manager +主要用于 chat history 的管理,暂未完成 +- 将chat history在数据库进行读写管理,包括user input、 llm output、doc retrieval、code retrieval、search retrieval +- 对 chat history 进行关键信息总结 summary context,作为 prompt context +- 提供检索功能,检索 chat history 或者 summary context 中与问题相关信息,辅助问答 + + + +## 使用示例 + +### 创建 memory manager 实例 +``` +import os +import openai + +from coagent.base_configs.env_config import KB_ROOT_PATH +from coagent.connector.memory_manager import BaseMemoryManager, LocalMemoryManager +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.schema import Message + +os.environ["API_BASE_URL"] = OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = "sk-xx" +openai.api_key = "sk-xxx" +# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or "socks5://127.0.0.1:13659" + +# LLM 和 Embedding Model 配置 +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) + +# +phase_name = "test" +memory_manager = LocalMemoryManager( + unique_name=phase_name, + do_init=True, + kb_root_path = KB_ROOT_PATH, + embed_config=embed_config, + llm_config=llm_config + ) +``` + +### 支持Message管理 + +``` +message1 = Message( + role_name="test1", role_type="user", input_query="hello", origin_query="hello", + parsed_output_list=[{"input": "hello"}] +) + +text = "hi! how can I help you?" +message2 = Message( + role_name="test2", role_type="assistant", input_query=text, origin_query=text, + role_content=text, step_content=text, parsed_output_list=[{"answer": text}] +) + +text = "they say hello and hi to each other" +message3 = Message( + role_name="test3", role_type="summary", + role_content=text, step_content=text, + parsed_output_list=[{"summary": text}] + ) + +``` + +### 支持 memory 检索 +``` +# embedding retrieval test +text = "say hi, i want some help" +print(memory_manager.router_retrieval(text=text, datetime="2024-01-08 20:22:00", n=4, top_k=5, retrieval_type= "datetime")) +print(memory_manager.router_retrieval(text=text, datetime="2024-01-08 20:22:00", n=4, top_k=5, retrieval_type= "embedding")) +print(memory_manager.router_retrieval(text=text, datetime="2024-01-08 20:22:00", n=4, top_k=5, retrieval_type= "text")) + +``` +### 支持 memory 总结 +``` +# recursive_summary test +print(memory_manager.recursive_summary(local_memory_manager.recall_memory.messages, split_n=1)) +``` \ No newline at end of file diff --git a/sources/readme_docs/coagent/connector/connector_phase.md b/sources/readme_docs/coagent/connector/connector_phase.md new file mode 100644 index 0000000..ed26b5f --- /dev/null +++ b/sources/readme_docs/coagent/connector/connector_phase.md @@ -0,0 +1,91 @@ +--- +title: Connector Phase +slug: Connector Phase ZH +url: "coagent/connector-phase-zh" +aliases: +- "/coagent/connector-phase-zh" +--- + + + +## 快速构建一个 agent phase +- 首先增加openai配置,也可以是其它类似于openai接口的模型(通过fastchat启动) +``` +from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.configs import AGETN_CONFIGS +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message, load_role_configs + + +os.environ["API_BASE_URL"] = OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = "sk-xx" +openai.api_key = "sk-xxx" +# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or "socks5://127.0.0.1:13659" +``` + + +- 配置相关 LLM 和 Embedding Model +``` +# LLM 和 Embedding Model 配置 +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) +``` + + +- 这里从已有的 phase 配置中选一个 phase 来做示例 +``` +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "searchChatPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 +query_content1 = "美国当前总统是谁?" +query = Message( + role_name="human", role_type="user", + role_content=query_content1, input_query=query_content1, origin_query=query_content1, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2 +query_content2 = "美国上一任总统是谁,两个人有什么关系没?" +query = Message( + role_name="human", role_type="user", + role_content=query_content2, input_query=query_content2, origin_query=query_content2, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + + +## Phase 参数配置 +|Config Key Name |Type |Description| +| ------------------ | ---------- | ---------- | +|phase_name| String| 场景名称| +|phase_config|CompletePhaseConfig| 默认为None,可直接指定完整的phaseconfig, 暂未实现| +|llm_config |LLMConfig |大语言模型配置| +|embed_config |EmbedConfig |向量模型配置| +|sandbox_server |Dict |沙盒环境即notebook启动配置| +|jupyter_work_path |str |沙盒环境的工作目录| +|kb_root_path |str |memory的存储路径| +|log_verbose |str |agent prompt&predict的日志打印级别| +| base_phase_config | Union[dict, str] | 默认配置:PHASE_CONFIGS,可通过实现对这个变量新增来实现自定义配置 | +| base_chain_config | Union[dict, str] | 默认配置:CHAIN_CONFIGS,可通过实现对这个变量新增来实现自定义配置 | +| base_role_config | Union[dict, str] | 默认配置:AGETN_CONFIGS,可通过实现对这个变量新增来实现自定义配置 | diff --git a/sources/readme_docs/coagent/connector/connector_prompt.md b/sources/readme_docs/coagent/connector/connector_prompt.md new file mode 100644 index 0000000..389d3c8 --- /dev/null +++ b/sources/readme_docs/coagent/connector/connector_prompt.md @@ -0,0 +1,248 @@ +--- +title: Connector Prompt +slug: Connector Prompt ZH +url: "coagent/connector-prompt-zh" +aliases: +- "/coagent/connector-prompt-zh" +--- + +## Prompt 的标准结构 +在整个Prompt的整个结构中,我们需要去定义三个部分 +- Agent Profil +- Input Format +- Response Output Format + +``` +#### Agent Profile + +Agent Description ... + +#### Input Format + +**Origin Query:** the initial question or objective that the user wanted to achieve + +**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. + +#### Response Output Format +**Action Status:** finished or continued +If it's 'finished', the context can answer the origin query. +If it's 'continued', the context cant answer the origin query. + +**REASON:** Justify the decision of choosing 'finished' and 'continued' by evaluating the progress step by step. +Consider all relevant information. If the tasks were aimed at an ongoing process, assess whether it has reached a satisfactory conclusion. +``` + + +其中,我们整合了部分 `Input Format` 的通用操作,内置了一部分字段和操作流程,形成通用的配置化操作。如下所示 +只需要定义如下字段和执行函数, + +``` +AUTO_FEEDBACK_FROM_CODE_EXECUTION_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} +] +``` + +未来我们会也会进一步将 Agent Profile和Response Output Format的部分,实现可配置化操作,降低Prompt编写难度 + +### 自定义 Input Format +同时,我们也支持 用户自定义 Input Format 的操作 + +``` +from coagent.connector.prompt_manager import PromptManager + +# 增加了两个新处理函数,用于prompt组装 +class CodeRetrievalPM(PromptManager): + def handle_code_packages(self, **kwargs) -> str: + if 'previous_agent_message' not in kwargs: + return "" + previous_agent_message: Message = kwargs['previous_agent_message'] + # 由于两个agent共用了同一个manager,所以临时性处理 + vertices = previous_agent_message.customed_kargs.get("RelatedVerticesRetrivalRes", {}).get("vertices", []) + return ", ".join([str(v) for v in vertices]) + + def handle_retrieval_codes(self, **kwargs) -> str: + if 'previous_agent_message' not in kwargs: + return "" + previous_agent_message: Message = kwargs['previous_agent_message'] + return '\n'.join(previous_agent_message.customed_kargs["Retrieval_Codes"]) + + +# Design your personal PROMPT INPPUT FORMAT +CODE_RETRIEVAL_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'retrieval_codes', "function_name": 'handle_retrieval_codes'}, + {"field_name": 'code_packages', "function_name": 'handle_code_packages'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} + ] + +# 进行注册 +import importlib +prompt_manager_module = importlib.import_module("coagent.connector.prompt_manager") +setattr(prompt_manager_module, 'CodeRetrievalPM', CodeRetrievalPM) + +# 更新配置 +from coagent.connector.configs import AGETN_CONFIGS +AGETN_CONFIGS.update({ + "codeRetrievalJudger": { + "role": { + "role_prompt": codeRetrievalJudger_PROMPT, + "role_type": "assistant", + "role_name": "codeRetrievalJudger", + "role_desc": "", + "agent_type": "CodeRetrievalJudger" + # "agent_type": "BaseAgent" + }, + "prompt_config": CODE_RETRIEVAL_PROMPT_CONFIGS, + "prompt_manager_type": "CodeRetrievalPM", + "chat_turn": 1, + "focus_agents": [], + "focus_message_keys": [], + }, + }) +``` + + + +在我们构建phase、chain或者agent之后,可以通过函数的预打印功能,实现agents链路确认,避免在执行后才发现问题,可提前进行debug +``` +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) + +phase_name = "baseGroupPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +phase.pre_print(query) + +## 完整信息确认 coagent.connector.configs中进行确认 +########################## +<<<>>> +########################## + +### Agent Profile +Your goal is to response according the Context Data's information with the role that will best facilitate a solution, taking into account all relevant context (Context) provided. +When you need to select the appropriate role for handling a user's query, carefully read the provided role names, role descriptions and tool list. +ATTENTION: response carefully referenced "Response Output Format" in format. + +### Tool Information + +### Agent Infomation + Please ensure your selection is one of the listed roles. Available roles for selection: + "role name: tool_react +role description: Agent Profile,When interacting with users, your role is to respond in a helpful and accurate manner using the tools available. Follow the steps below to ensure efficient and effective use of the tools.,Please note that all the tools you can use are listed below. You can only choose from these tools for use. ,If there are no suitable tools, please do not invent any tools. Just let the user know that you do not have suitable tools to use.,ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying.," +"role name: code_react +role description: Agent Profile,When users need help with coding, your role is to provide precise and effective guidance.,Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step.," + Please ensure select the Role from agent names, such as tool_react, code_react + +### Context Data + +#### Reference Documents + +#### Session Records + +#### Current Plan + +### Response Output Format +**Thoughts:** think the reason step by step about why you selecte one role +**Role:** Select the role from agent names. + +### Begin!!! + +################### +<<<>>> +################### + +**Thoughts:** +**Role:** + + +########################### +<<<>>> +########################### +### Agent Profile +When interacting with users, your role is to respond in a helpful and accurate manner using the tools available. Follow the steps below to ensure efficient and effective use of the tools. +Please note that all the tools you can use are listed below. You can only choose from these tools for use. +If there are no suitable tools, please do not invent any tools. Just let the user know that you do not have suitable tools to use. +ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. + +### Tool Information + +### Context Data + +#### Reference Documents + +#### Session Records + +#### Task Records + +### Response Output Format +**Thoughts:** According the previous observations, plan the approach for using the tool effectively. +... + +### Begin!!! + +################### +<<<>>> +################### +**Thoughts:** +**Action Status:** +**Action:** +**Observation:** +**Thoughts:** +**Action Status:** +**Action:** + +########################### +<<<>>> +########################### +### Agent Profile +When users need help with coding, your role is to provide precise and effective guidance. +Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step. + +### Context Data + +#### Reference Documents + +#### Session Records + +### Response Output Format + +**Thoughts:** According the previous context, solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem, +outline the plan for executing this step. + +**Action Status:** Set to 'stopped' or 'code_executing'. +If it's 'stopped', the action is to provide the final answer to the session records and executed steps. +If it's 'code_executing', the action is to write the code. +... + +### Begin!!! + +################### +<<<>>> +################### + +**Thoughts:** +**Action Status:** +**Action:** +**Observation:** +**Thoughts:** +**Action Status:** +**Action:** + +``` diff --git a/sources/readme_docs/coagent/connector/customed_examples.md b/sources/readme_docs/coagent/connector/customed_examples.md new file mode 100644 index 0000000..42ca740 --- /dev/null +++ b/sources/readme_docs/coagent/connector/customed_examples.md @@ -0,0 +1,175 @@ +--- +title: Customed Examples +slug: Customed Examples ZH +url: "coagent/customed-examples-zh" +aliases: +- "/coagent/customed-examples-zh" +--- + + +## 如何创建你个性化的 agent phase 场景 + +下面通过 autogen 的 auto_feedback_from_code_execution 构建过来,来详细演示如何自定义一个 agent phase 的构建 + +### 设计你的prompt结构 +``` +import os, sys, requests + +# from configs.model_config import * +from coagent.connector.phase import BasePhase +from coagent.connector.chains import BaseChain +from coagent.connector.schema import Message +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + + +# update new agent configs +auto_feedback_from_code_execution_PROMPT = """#### Agent Profile + +You are a helpful AI assistant. Solve tasks using your coding and language skills. +In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. + 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. + 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. +Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. +When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. +If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. +When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. +Reply "stopped" in the end when everything is done. + +ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. + +#### Response Output Format + +**Thoughts:** Based on the question and observations above, provide the plan for executing this step. + +**Action Status:** Set to 'stopped' or 'code_executing'. If it's 'stopped', the action is to provide the final answer to the original question. If it's 'code_executing', the action is to write the code. + +**Action:** +# Write your code here +import os +... + + +**Observation:** Check the results and effects of the executed code. + +... (Repeat this Thoughts/Action/Observation cycle as needed) + +**Thoughts:** I now know the final answer + +**Action Status:** stopped + +**Action:** The final answer to the original input question +""" +``` + +### 开始配置 Prompt Configs +``` +AUTO_FEEDBACK_FROM_CODE_EXECUTION_PROMPT_CONFIGS = [ + {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, + {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, + {"field_name": 'session_records', "function_name": 'handle_session_records'}, + {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, + {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} +] +``` + +### 更新完整的agent、chain、phase配置,以便后续更读取执行 +``` +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import os + +## set a +AGETN_CONFIGS.update({ + "auto_feedback_from_code_execution": { + "role": { + "role_prompt": auto_feedback_from_code_execution_PROMPT, + "role_type": "assistant", + "role_name": "auto_feedback_from_code_execution", + "role_desc": "", + "agent_type": "ReactAgent" + }, + "prompt_config": AUTO_FEEDBACK_FROM_CODE_EXECUTION_PROMPT_CONFIGS, + "chat_turn": 5, + "stop": "\n**Observation:**", + "focus_agents": [], + "focus_message_keys": [], + }, +}) +# update new chain configs +CHAIN_CONFIGS.update({ + "auto_feedback_from_code_executionChain": { + "chain_name": "auto_feedback_from_code_executionChain", + "chain_type": "BaseChain", + "agents": ["auto_feedback_from_code_execution"], + "chat_turn": 1, + "do_checker": False, + "chain_prompt": "" + } +}) + +# update phase configs +PHASE_CONFIGS.update({ + "auto_feedback_from_code_executionPhase": { + "phase_name": "auto_feedback_from_code_executionPhase", + "phase_type": "BasePhase", + "chains": ["auto_feedback_from_code_executionChain"], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": False + }, +}) + +``` + + + +### 接下来就构建 phase 实例,开始执行 +``` +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message +import base64, openai + +# +os.environ["API_BASE_URL"] = "http://openai.com/v1/chat/completions" +os.environ["OPENAI_API_KEY"] = "sk-xxxx" +openai.api_key = "sk-xxxx" + +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) + +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) + + +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +# +phase_name = "auto_feedback_from_code_executionPhase" +phase = BasePhase( + phase_name, + embed_config=embed_config, llm_config=llm_config, + base_phase_config = PHASE_CONFIGS, + base_chain_config = CHAIN_CONFIGS, + base_role_config = AGETN_CONFIGS, +) + + +# round-1 +query_content = """Plot a chart of META and TESLA's stock prices for the past year and save it as stock_price_ytd.png.""" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` \ No newline at end of file diff --git a/sources/readme_docs/coagent/quick-start-en.md b/sources/readme_docs/coagent/quick-start-en.md new file mode 100644 index 0000000..9b55912 --- /dev/null +++ b/sources/readme_docs/coagent/quick-start-en.md @@ -0,0 +1,384 @@ +--- +title: Quick Start +slug: Quick Start +url: "coagent/quick-start" +aliases: +- "/coagent/quick-start" +--- + + + + + +## Quick Start +### First, set up the LLM configuration +``` +import os, sys +import openai + +# llm config +os.environ["API_BASE_URL"] = OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = "sk-xxx" +openai.api_key = "sk-xxx" +# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +``` + +### Next, configure the LLM settings and vector model +``` +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig + +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) + +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) +``` + +### Finally, choose a pre-existing scenario to execute +``` +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message + +# Copy the data to a working directory; specify the directory if needed (default can also be used) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# Choose a scenario to execute +phase_name = "baseGroupPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1: Use a code interpreter to complete tasks +query_content = "Check if 'employee_data.csv' exists locally, view its columns and data types; then draw a bar chart" +query = Message( + role_name="human", role_type="user", tools=[], + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +# phase.pre_print(query) # This function is used to preview the Prompt of the Agents' execution chain +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2: Execute tools +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + +query_content = "Please check if there were any issues with the server at 127.0.0.1 at 10 o'clock; help me make a judgment" +query = Message( + role_name="human", role_type="user", tools=tools, + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +# phase.pre_print(query) # This function is used to preview the Prompt of the Agents' execution chain +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +``` + +## Phase Introduction and Usage + +Below are some specific Phase introduced and how to use them. + +Feel free to brainstorm and create some interesting cases. + +### baseGroupPhase +The group usage Phase in autogen + +``` +# Copy the data to a working directory; specify the directory if needed (default can also be used) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# Set the log level to control the printing of the prompt, LLM output, or other information +os.environ["log_verbose"] = "0" + +phase_name = "baseGroupPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 +query_content = "Check if 'employee_data.csv' exists locally, view its columns and data types; then draw a bar chart" + +query = Message( + role_name="human", role_type="user", tools=[], + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +# phase.pre_print(query) # This function is used to preview the Prompt of the Agents' execution chain +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + +### baseTaskPhase +The task splitting and multi-step execution scenario in xAgents + +``` +# if you want to analyze a data.csv, please put the csv file into a jupyter_work_path (or your defined path) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "baseTaskPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) +# round-1 +query_content = "Check if 'employee_data.csv' exists locally, view its columns and data types; then draw a bar chart" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### codeReactPhase +The code interpreter scenario based on React + +``` +# if you want to analyze a data.csv, please put the csv file into a jupyter_work_path (or your defined path) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# then, create a data analyze phase +phase_name = "codeReactPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, + jupyter_work_path=JUPYTER_WORK_PATH, +) + +# round-1 +query_content = "Check if 'employee_data.csv' exists locally, view its columns and data types; then draw a bar chart" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + +### codeToolReactPhase +The tool invocation and code interpreter scenario based on the React template + + +``` +TOOL_SETS = [ + "StockName", "StockInfo", + ] +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "codeToolReactPhase" + +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +query_content = "查询贵州茅台的股票代码,并查询截止到当前日期(2023年12月24日)的最近10天的每日时序数据,然后用代码画出折线图并分析" + +query = Message( + role_name="human", role_type="user", + input_query=query_content, role_content=query_content, + origin_query=query_content, tools=tools + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### docChatPhase +The knowledge base retrieval Q&A Phase + +``` +# create your knowledge base +from io import BytesIO +from pathlib import Path + +from coagent.service.kb_api import create_kb, upload_doc +from coagent.service.service_factory import get_kb_details +from coagent.utils.server_utils import run_async +kb_list = {x["kb_name"]: x for x in get_kb_details(KB_ROOT_PATH)} + + +# create a knowledge base +kb_name = "example_test" +data = { + "knowledge_base_name": kb_name, + "vector_store_type": "faiss", # default + "kb_root_path": KB_ROOT_PATH, + "embed_model": embed_config.embed_model, + "embed_engine": embed_config.embed_engine, + "embed_model_path": embed_config.embed_model_path, + "model_device": embed_config.model_device, +} +run_async(create_kb(**data)) + +# add doc to knowledge base +file = os.path.join("D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/sources/docs/langchain_text_10.jsonl") +files = [file] +# if embedding init failed, you can use override = True +data = [{"override": True, "file": f, + "knowledge_base_name": kb_name, "not_refresh_vs_cache": False, + "kb_root_path": KB_ROOT_PATH, "embed_model": embed_config.embed_model, + "embed_engine": embed_config.embed_engine, "embed_model_path": embed_config.embed_model_path, + "model_device": embed_config.model_device, + } + for f in files] + +for k in data: + file = Path(file).absolute().open("rb") + filename = file.name + + from fastapi import UploadFile + from tempfile import SpooledTemporaryFile + + temp_file = SpooledTemporaryFile(max_size=10 * 1024 * 1024) + temp_file.write(file.read()) + temp_file.seek(0) + + k.update({"file": UploadFile(file=temp_file, filename=filename),}) + run_async(upload_doc(**k)) + + +# start to chat with knowledge base +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +# set chat phase +phase_name = "docChatPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) +# round-1 +query_content = "what modules does langchain have?" +query = Message( + role_name="human", role_type="user", + origin_query=query_content, + doc_engine_name=kb_name, score_threshold=1.0, top_k=3 + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2 +query_content = "What is the purpose of prompts?" +query = Message( + role_name="human", role_type="user", + origin_query=query_content, + doc_engine_name=kb_name, score_threshold=1.0, top_k=3 + ) +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### metagpt_code_devlop +The code construction Phase in metagpt + +``` +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "metagpt_code_devlop" +llm_config = LLMConfig( + model_name="gpt-4", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) + +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +query_content = "create a snake game by pygame" +query = Message(role_name="human", role_type="user", input_query=query_content, role_content=query_content, origin_query=query_content) + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### searchChatPhase +The fixed Phase: search first, then answer directly with LLM + +``` +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "searchChatPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 +query_content1 = "who is the president of the United States?" +query = Message( + role_name="human", role_type="user", + role_content=query_content1, input_query=query_content1, origin_query=query_content1, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2 +query_content2 = "Who was the previous president of the United States, and is there any relationship between the two individuals?" +query = Message( + role_name="human", role_type="user", + role_content=query_content2, input_query=query_content2, origin_query=query_content2, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### toolReactPhase +The tool invocation scene based on the React template + +``` +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "toolReactPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) +query_content = "Please check if there were any issues with the server at 127.0.0.1 at 10 o'clock; help me make a judgment" +query = Message( + role_name="human", role_type="user", tools=tools, + role_content=query_content, input_query=query_content, origin_query=query_content + ) + +# phase.pre_print(query) # This function is used to preview the Prompt of the Agents' execution chain +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` \ No newline at end of file diff --git a/sources/readme_docs/coagent/quick-start.md b/sources/readme_docs/coagent/quick-start.md new file mode 100644 index 0000000..604e144 --- /dev/null +++ b/sources/readme_docs/coagent/quick-start.md @@ -0,0 +1,384 @@ +--- +title: 快速开始 +slug: 快速开始 +url: "coagent/快速开始" +aliases: +- "/coagent/快速开始" +- "/coagent/quick-start-zh" +--- + + + +## 快速使用 +### 首先,填写LLM配置 +``` +import os, sys +import openai + +# llm config +os.environ["API_BASE_URL"] = OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = "sk-xxx" +openai.api_key = "sk-xxx" +# os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +``` + +### 然后设置LLM配置和向量模型配置 +``` +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig + +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) + +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) +``` + +### 最后选择一个已有场景进行执行 +``` +from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS +from coagent.connector.phase import BasePhase +from coagent.connector.schema import Message + +# 选择一个已实现得场景进行执行 + +# 如果需要做一个数据分析,需要将数据放到某个工作目录,同时指定工作目录(也可使用默认目录) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# 选择一个场景 +phase_name = "baseGroupPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 需要通过代码解释器来完成 +query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", tools=[], + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +# phase.pre_print(query) # 该功能用于预打印 Agents 执行链路的Prompt +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2 需要执行工具 +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + +query_content = "帮我确认下127.0.0.1这个服务器的在10点是否存在异常,请帮我判断一下" +query = Message( + role_name="human", role_type="user", tools=tools, + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +# phase.pre_print(query) # 该功能用于预打印 Agents 执行链路的Prompt +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +``` + +## 场景介绍和使用 + +下面是一些具体的场景介绍和使用。 + +欢迎大家开脑洞构造一些有趣的case。 + +### baseGroupPhase +autogen的group使用场景 + +``` +# 如果需要做一个数据分析,需要将数据放到某个工作目录,同时指定工作目录(也可使用默认目录) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# 设置日志级别,控制打印prompt或者llm 输出或其它信息 +os.environ["log_verbose"] = "0" + +phase_name = "baseGroupPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 +query_content = "确认本地是否存在book_data.csv,并查看它有哪些列和数据类型;然后画柱状图" + +query = Message( + role_name="human", role_type="user", tools=[], + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +# phase.pre_print(query) # 该功能用于预打印 Agents 执行链路的Prompt +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + +### baseTaskPhase +xAgents的任务拆分及多步骤执行场景 + +``` +# if you want to analyze a data.csv, please put the csv file into a jupyter_work_path (or your defined path) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "baseTaskPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) +# round-1 +query_content = "确认本地是否存在book_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### codeReactPhase +基于 React 的代码解释器场景 + +``` +# if you want to analyze a data.csv, please put the csv file into a jupyter_work_path (or your defined path) +import shutil +source_file = 'D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/jupyter_work/book_data.csv' +shutil.copy(source_file, JUPYTER_WORK_PATH) + +# then, create a data analyze phase +phase_name = "codeReactPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, + jupyter_work_path=JUPYTER_WORK_PATH, +) + +# round-1 +query_content = "确认本地是否存在book_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + +### codeToolReactPhase +基于 React 模板的工具调用和代码解释器场景 + + +``` +TOOL_SETS = [ + "StockName", "StockInfo", + ] +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "codeToolReactPhase" + +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +query_content = "查询贵州茅台的股票代码,并查询截止到当前日期(2023年12月24日)的最近10天的每日时序数据,然后用代码画出折线图并分析" + +query = Message( + role_name="human", role_type="user", + input_query=query_content, role_content=query_content, + origin_query=query_content, tools=tools + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### docChatPhase +知识库检索问答链路 +``` +# create your knowledge base +from io import BytesIO +from pathlib import Path + +from coagent.service.kb_api import create_kb, upload_doc +from coagent.service.service_factory import get_kb_details +from coagent.utils.server_utils import run_async +kb_list = {x["kb_name"]: x for x in get_kb_details(KB_ROOT_PATH)} + + +# create a knowledge base +kb_name = "example_test" +data = { + "knowledge_base_name": kb_name, + "vector_store_type": "faiss", # default + "kb_root_path": KB_ROOT_PATH, + "embed_model": embed_config.embed_model, + "embed_engine": embed_config.embed_engine, + "embed_model_path": embed_config.embed_model_path, + "model_device": embed_config.model_device, +} +run_async(create_kb(**data)) + +# add doc to knowledge base +file = os.path.join("D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/sources/docs/langchain_text_10.jsonl") +files = [file] +# if embedding init failed, you can use override = True +data = [{"override": True, "file": f, + "knowledge_base_name": kb_name, "not_refresh_vs_cache": False, + "kb_root_path": KB_ROOT_PATH, "embed_model": embed_config.embed_model, + "embed_engine": embed_config.embed_engine, "embed_model_path": embed_config.embed_model_path, + "model_device": embed_config.model_device, + } + for f in files] + +for k in data: + file = Path(file).absolute().open("rb") + filename = file.name + + from fastapi import UploadFile + from tempfile import SpooledTemporaryFile + + temp_file = SpooledTemporaryFile(max_size=10 * 1024 * 1024) + temp_file.write(file.read()) + temp_file.seek(0) + + k.update({"file": UploadFile(file=temp_file, filename=filename),}) + run_async(upload_doc(**k)) + + +# start to chat with knowledge base +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +# set chat phase +phase_name = "docChatPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) +# round-1 +query_content = "langchain有哪些模块" +query = Message( + role_name="human", role_type="user", + origin_query=query_content, + doc_engine_name=kb_name, score_threshold=1.0, top_k=3 + ) + +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2 +query_content = "提示(prompts)有什么用?" +query = Message( + role_name="human", role_type="user", + origin_query=query_content, + doc_engine_name=kb_name, score_threshold=1.0, top_k=3 + ) +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### metagpt_code_devlop +metagpt的代码构造链路 + +``` +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "metagpt_code_devlop" +llm_config = LLMConfig( + model_name="gpt-4", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) + +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +query_content = "create a snake game by pygame" +query = Message(role_name="human", role_type="user", input_query=query_content, role_content=query_content, origin_query=query_content) + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### searchChatPhase +固定场景链路,先搜索后基于LLM直接回答 + +``` +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "searchChatPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 +query_content1 = "美国当前总统是谁?" +query = Message( + role_name="human", role_type="user", + role_content=query_content1, input_query=query_content1, origin_query=query_content1, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) + +output_message, output_memory = phase.step(query) + +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) + +# round-2 +query_content2 = "美国上一任总统是谁,两个人有什么关系没?" +query = Message( + role_name="human", role_type="user", + role_content=query_content2, input_query=query_content2, origin_query=query_content2, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` + + +### toolReactPhase +基于 React 模板的工具调用场景 + +``` +# log-level,print prompt和llm predict +os.environ["log_verbose"] = "2" + +phase_name = "toolReactPhase" +phase = BasePhase( + phase_name, embed_config=embed_config, llm_config=llm_config, +) + +# round-1 +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) +query_content = "帮我确认下127.0.0.1这个服务器的在10点是否存在异常,请帮我判断一下" +query = Message( + role_name="human", role_type="user", tools=tools, + role_content=query_content, input_query=query_content, origin_query=query_content + ) + +# phase.pre_print(query) # 该功能用于预打印 Agents 执行链路的Prompt +output_message, output_memory = phase.step(query) +print(output_memory.to_str_messages(return_all=True, content_key="parsed_output_list")) +``` \ No newline at end of file diff --git a/sources/readme_docs/contribution/contribute_guide.md b/sources/readme_docs/contribution/contribute_guide.md new file mode 100644 index 0000000..1fd39fb --- /dev/null +++ b/sources/readme_docs/contribution/contribute_guide.md @@ -0,0 +1,185 @@ +非常感谢您对 Codefuse 项目感兴趣,我们非常欢迎您对 Codefuse 项目的各种建议、意见(包括批评)、评论和贡献。 + +您对 Codefuse 的各种建议、意见、评论可以直接通过 GitHub 的 Issues 提出。 + +参与 Codefuse 项目并为其作出贡献的方法有很多:代码实现、测试编写、流程工具改进、文档完善等等。任何贡献我们都会非常欢迎,并将您加入贡献者列表. + +进一步,有了足够的贡献后,您还可以有机会成为 Codefuse 的 Committer。 + +任何问题,您都可以联系我们得到及时解答,联系方式包括微信、Gitter(GitHub提供的即时聊天工具)、邮件等等。 + + +## 初次接触 +初次来到 Codefuse 社区,您可以: + +- 关注 Codefuse Github 代码库 +- 加入 Codefuse 相关的微信群 随时提问; +通过以上方式及时了解 Codefuse 项目的开发动态并为您关注的话题发表意见。 + + +## 贡献方式 +这份贡献指南并不仅仅关于编写代码。我们重视并感激在各个领域的帮助。以下是一些您可以贡献的方式 +- 文档 +- Issue +- PR + +### 改进文档 +文档是您了解 Codefuse 的最主要的方式,也是我们最需要帮助的地方! + +浏览文档,可以加深您对 Codefuse 的了解,也可以帮助您理解 Codefuse 的功能和技术细节,如果您发现文档有问题,请及时联系我们; + +如果您对改进文档的质量感兴趣,不论是修订一个页面的地址、更正一个链接、以及写一篇更优秀的入门文档,我们都非常欢迎! + +我们的文档大多数是使用 markdown 格式编写的,您可以直接通过在 GitHub 中的 docs/ 中修改并提交文档变更。如果提交代码变更,可以参阅 Pull Request。 + +### 如果发现了一个 Bug 或问题 +如果发现了一个 Bug 或问题,您可以直接通过 GitHub 的 Issues 提一个新的 Issue,我们会有人定期处理。详情见[Issue Template](#issue-template) + +您也可以通过阅读分析代码自己修复(当然在这之前最好能和我们交流下,或许已经有人在修复同样的问题了),然后提交一个 Pull Request。 + +### 修改代码和提交PR(Pull Request) +您可以下载代码,编译安装,部署运行试一试(可以参考编译文档,看看是否与您预想的一样工作。如果有问题,您可以直接联系我们,提 Issue 或者通过阅读和分析源代码自己修复。详情见[Contribution](#contribution) + +无论是修复 Bug 还是增加 Feature,我们都非常欢迎。如果您希望给 Doris 提交代码,您需要从 GitHub 上 fork 代码库至您的项目空间下,为您提交的代码创建一个新的分支,添加源项目为upstream,并提交PR。 提交PR的方式可以参考文档 Pull Request。 + + + + +## Issue Type +Issue分为三种类型 +- Bug: 代码或者执行示例存在bug或缺少依赖导致无法正确执行 +- Documentation:文档表述存在争议、文档内容与代码不一致等 +- Feature:在当前代码基础继续演进的新功能 + +## Issue Template +### Issue: Bug Template + +**提交Issue前的确认清单** +
要先确认是否查看 document、issue、discussion(github 功能) 等公开的文档信息 +- 我搜索了Codefuse相关的所有文档。 +- 我使用GitHub搜索寻找了一个类似的问题,但没有找到。 +- 我为这个问题添加了一个非常描述性的标题。 + +**系统信息** +
确认系统,如 mac -xx 、windwos-xx、linux-xx + +**代码版本** +
确认代码版本或者分支,master、release等 + +**问题描述** +
描述您碰到的问题,想要实现的事情、或代码执行Bug + +**代码示例** +
附上你的执行代码和相关配置,以便能够快速介入进行复现 + +**报错信息、日志** +
执行上述代码示例后的报错日志和相关信息 + +**相关依赖的模块** +
以chatbot项目为例 +- connector +- codechat +- sandbox +- ... + + +### Issue: Documentation Template +**Issue with current documentation:** +
请帮忙指出当前文档中的问题、错别字或者令人困惑的地方 + +**Idea or request for content** +
您觉得合理的文档表述方式应该是什么样的 + + +### Issue: Feature Template +**提交Issue前的确认清单** +
要先确认是否查看 document、issue、discussion(github 功能) 等公开的文档信息 +- 我搜索了Codefuse相关的所有文档。 +- 我使用GitHub Issue搜索寻找了一个类似的问题,但没有找到。 +- 我为这个问题添加了一个非常描述性的标题。 + +**功能描述** +
描述这个功能作何用途 + +**相关示例** +
提供参考的文档、仓库等信息,Please provide links to any relevant GitHub repos, papers, or other resources if relevant. + +**动机** +
描述下这个feature的动机,为什么需要这个功能,提供足够的上下文信息帮助理解这个feature的诉求 + +**Contribution** +
你如何参与到这个feature的构建(如果参与的话) + + + +## Contribution + +### Pre-Checklist +- 要先确认是否查看 document、issue、discussion(github 功能) 等公开的文档信息 +- 找到你想处理的GitHub问题。如果不存在,创建一个问题或草案PR,并请求维护者进行检查。 +- 检查相关的、相似的或重复的拉取请求。 +- 创建一个草案拉取请求。 +- 完成PR模板中的描述。 +- 链接任何被你的PR解决的GitHub问题。 + +### Description +PR的描述信息,用简洁的语言表达PR完成的事情,具体规范见[Commit 格式规范](#commit-格式规范) + +### Related Issue +`#xx` if has + +### Test Code with Result +请提供相关的测试代码如果有必要的话 + + +## Commit 格式规范 +Commit 分为“标题”和“内容”。原则上标题全部小写。内容首字母大写。 + + +### 标题 +commit message的标题:`[]() (#pr)` + + +### type 可选值 + +本次提交的类型,限定在以下类型(全小写) +- fix:bug修复 +- feature:新增功能 +- feature-wip:开发中的功能,比如某功能的部分代码。 +- improvement:原有功能的优化和改进 +- style:代码风格调整 +- typo:代码或文档勘误 +- refactor:代码重构(不涉及功能变动) +- performance/optimize:性能优化 +- test:单元测试的添加或修复 +- deps:第三方依赖库的修改 +- community:社区相关的修改,如修改 Github Issue 模板等。 + +几点说明: + +如在一次提交中出现多种类型,需增加多个类型。 +如代码重构带来了性能提升,可以同时添加 [refactor][optimize] +不得出现如上所列类型之外的其他类型。如有必要,需要将新增类型添加到这个文档中。 + +### scope 可选值 +本次提交涉及的模块范围。因为功能模块繁多,在此仅罗列部分,后续根据需求不断完善。 +
以 chatbot的框架为例 +- connector +- codechat +- sandbox +- ... + +几点说明: + +尽量使用列表中已存在的选项。如需添加,请及时更新本文档。 + +### subject 内容 +标题需尽量清晰表明本次提交的主要内容。 + + +## 示例 +comming soon + + +## Reference +[doris-commit-format](https://doris.apache.org/zh-CN/community/how-to-contribute/commit-format-specification) \ No newline at end of file diff --git a/sources/readme_docs/contribution/contribute_guide_en.md b/sources/readme_docs/contribution/contribute_guide_en.md new file mode 100644 index 0000000..1fd6096 --- /dev/null +++ b/sources/readme_docs/contribution/contribute_guide_en.md @@ -0,0 +1,190 @@ + + +Thank you for your interest in the Codefuse project. We warmly welcome any suggestions, opinions (including criticisms), comments, and contributions to the Codefuse project. + +Your suggestions, opinions, and comments on Codefuse can be directly submitted through GitHub Issues. + +There are many ways to participate in the Codefuse project and contribute to it: code implementation, test writing, process tool improvement, documentation enhancement, and more. We welcome any contributions and will add you to our list of contributors. + +Furthermore, with enough contributions, you may have the opportunity to become a Committer for Codefuse. + +For any questions, you can contact us for timely answers through various means including WeChat, Gitter (an instant messaging tool provided by GitHub), email, and more. + + +## Getting Started +If you are new to the Codefuse community, you can: +- Follow the Codefuse GitHub repository. +- Join related WeChat groups for Codefuse to ask questions at any time; + +Through the above methods, you can stay up-to-date with the development dynamics of the Codefuse project and express your opinions on topics of interest. + + +## Contributation Ways +This contribution guide is not just about writing code. We value and appreciate help in all areas. Here are some ways you can contribute: +- Documentation +- Issues +- Pull Requests (PR) + +### Improve Documentation +Documentation is the main way for you to understand Codefuse and is also where we need the most help! + +By browsing the documentation, you can deepen your understanding of Codefuse and also help you grasp the features and technical details of Codefuse. If you find any issues with the documentation, please contact us in time; + +If you are interested in improving the quality of the documentation, whether it is revising an address of a page, correcting a link, or writing a better introductory document, we are very welcoming! + +Most of our documentation is written in markdown format. You can directly modify and submit documentation changes in the docs/ directory on GitHub. For submitting code changes, please refer to Pull Requests. + +### If You Discover a Bug or Issue +If you discover a bug or issue, you can directly submit a new Issue through GitHub Issues, and someone will handle it regularly. For more details, see Issue Template.[Issue Template](#issue-template) + +You can also choose to read and analyze the code to fix it yourself (it is best to communicate with us before doing so, as someone might already be working on the same issue), and then submit a Pull Request. + +### Modify Code and Submit a PR (Pull Request) +You can download the code, compile, install, and deploy to try it out (you can refer to the compilation documentation to see if it works as you expected). If there are any issues, you can directly contact us, submit an Issue, or fix it yourself by reading and analyzing the source code. For more details, see[Contribution](#contribution) + +Whether it's fixing a bug or adding a feature, we warmly welcome it. If you wish to submit code to Doris, you need to fork the code repository to your project space on GitHub, create a new branch for your submitted code, add the original project as an upstream, and submit a PR. The method for submitting a PR can be referenced in the Pull Request documentation. + + +## Issue Type +Issues can be categorized into three types: +- Bug: Issues where code or execution examples contain bugs or lack dependencies, resulting in incorrect execution. +- Documentation: Discrepancies in documentation, inconsistencies between documentation content and code, etc. +- Feature: New functionalities that evolve from the current codebase. + +## Issue Template +### Issue: Bug Template + +**Checklist before submitting an issue** +
Please confirm that you have checked the document, issues, discussions (GitHub feature), and other publicly available documentation. +- I have searched through all documentation related to Codefuse. +- I used GitHub search to find a similar issue, but did not find one. +- I have added a very descriptive title for this issue. + +**System Information** +
Please confirm your operating system, such as mac-xx, windows-xx, linux-xx. + +**Code Version** +
Please confirm the code version or branch, such as master, release, etc. + +**Problem Description** +
Describe the problem you encountered, what you want to achieve, or the bug encountered during code execution. + +**Code Example** +
Attach your execution code and relevant configuration to facilitate rapid intervention and reproduction. + +**Error Information, Logs** +
The error logs and related information after executing the above code example. + +**Related Dependencies** +
Taking the chatbot project as an example: +- connector +- codechat +- sandbox +- ... + + +### Issue: Documentation Template + +**Issue with current documentation:** +
Please point out any problems, typos, or confusing points in the current documentation. + +**Idea or request for content** +
What do you think would be a reasonable way to express the documentation? + +### Issue: Feature Template + +**Checklist before submitting an issue** +
Please confirm that you have checked the document, issues, discussions (GitHub feature), and other publicly available documentation. +- I have searched through all documentation related to Codefuse. +- I used GitHub Issue search to find a similar issue, but did not find one. +- I have added a very descriptive title for this issue. + +**Feature Description** +
Describe the purpose of this feature. + +**Related Examples** +
Provide references to documents, repositories, etc., Please provide links to any relevant GitHub repos, papers, or other resources if relevant. + +**Motivation** +
Describe the motivation for this feature. Why is it needed? Provide enough context information to help understand the demand for this feature. + +**Contribution** +
How you can contribute to the building of this feature (if you are participating). + + + +## Contribution + +### Pre-Checklist +- First, confirm whether you have checked the document, issue, discussion (GitHub features), or other publicly available documentation. +- Find the GitHub issue you want to address. If none exists, create an issue or draft PR and ask a Maintainer for a check +- Check for related, similar, or duplicate pull requests +- Create a draft pull request +- Complete the PR template for the description +- Link any GitHub issue(s) that are resolved by your PR + +### Description + +A description of the PR should be articulated in concise language, highlighting the work completed by the PR. See specific standards at[Commit Format Specification](#Commit-Format-Specification) + +### Related Issue +#xx if has + +### Test Code with Result +Please provide relevant test code when necessary. + + + +## Commit Format Specification +A commit consists of a "title" and a "body." The title should generally be in lowercase, while the first letter of the body should be uppercase. + +### Title +The title of the commit message: `[]() (#pr)` + + +### Type - Available Options + +本次提交的类型,限定在以下类型(全小写) +- fix: Bug fixes +- feature: New features +- feature-wip: Features that are currently in development, such as partial code for a function. +- improvement: Optimizations and improvements to existing features +- style: Adjustments to code style +- typo: Typographical errors in code or documentation +- refactor: Code refactoring (without changing functionality) +- performance/optimize: Performance optimization +- test: Addition or fix of unit tests +- deps: Modifications to third-party dependencies +- community: Community-related changes, such as modifying Github Issue templates, etc. + +Please note: + +If multiple types occur in one commit, add multiple types. + +If code refactoring leads to performance improvement, both [refactor][optimize] can be added. + +Other types not listed above should not appear. If necessary, new types must be added to this document. + +### Scope - Available Options +The scope of the modules involved in the current submission. Due to the multitude of functional modules, only a few are listed here, and this list will be updated continuously based on needs. + +For example, using a chatbot framework: +connector +codechat +sandbox +... + +Please note: + +Try to use options that are already listed. If you need to add new ones, please update this document promptly. + +### Subject Content +The title should clearly indicate the main content of the current submission. + + +## Example +comming soon + + +## Reference +[doris-commit-format](https://doris.apache.org/zh-CN/community/how-to-contribute/commit-format-specification) \ No newline at end of file diff --git a/sources/readme_docs/fastchat-en.md b/sources/readme_docs/fastchat-en.md new file mode 100644 index 0000000..f71cfa0 --- /dev/null +++ b/sources/readme_docs/fastchat-en.md @@ -0,0 +1,198 @@ + +# Local Privatization/Large Model Interface Access + +Leveraging open-source LLMs (Large Language Models) and Embedding models, this project enables offline private deployment based on open-source models. + +In addition, the project supports the invocation of OpenAI API. + +## Local Privatization Model Access + +
Example of model address configuration, modification of the model_config.py configuration: + +```bash +# Recommendation: Use Hugging Face models, preferably the chat models, and avoid using base models, which may not produce correct outputs. +# Note: When both `llm_model_dict` and `VLLM_MODEL_DICT` are present, the model configuration in `VLLM_MODEL_DICT` takes precedence. +# Example of `llm_model_dict` configuration: + +# 1. If the model is placed under the ~/codefuse-chatbot/llm_models path +# Suppose the model address is as follows +model_dir: ~/codefuse-chatbot/llm_models/THUDM/chatglm-6b + +# The reference configuration is as follows +llm_model_dict = { + "chatglm-6b": { + "local_model_path": "THUDM/chatglm-6b", + "api_base_url": "http://localhost:8888/v1", # "name"修改为fastchat服务中的"api_base_url" + "api_key": "EMPTY" + } +} + +VLLM_MODEL_DICT = { + 'chatglm2-6b': "THUDM/chatglm-6b", +} + +# or If the model address is as follows +model_dir: ~/codefuse-chatbot/llm_models/chatglm-6b +llm_model_dict = { + "chatglm-6b": { + "local_model_path": "chatglm-6b", + "api_base_url": "http://localhost:8888/v1", # "name"修改为fastchat服务中的"api_base_url" + "api_key": "EMPTY" + } +} + +VLLM_MODEL_DICT = { + 'chatglm2-6b': "chatglm-6b", +} + +# 2. If you do not wish to move the model to ~/codefuse-chatbot/llm_models +# Also, delete the related code below `Model Path Reset`, see model_config.py for details. +# Suppose the model address is as follows +model_dir: ~/THUDM/chatglm-6b +# The reference configuration is as follows +llm_model_dict = { + "chatglm-6b": { + "local_model_path": "your personl dir/THUDM/chatglm-6b", + "api_base_url": "http://localhost:8888/v1", # "name"修改为fastchat服务中的"api_base_url" + "api_key": "EMPTY" + } +} + +VLLM_MODEL_DICT = { + 'chatglm2-6b': "your personl dir/THUDM/chatglm-6b", +} +``` + +```bash +# 3. Specify the model service to be launched, keeping both consistent +LLM_MODEL = "chatglm-6b" +LLM_MODELs = ["chatglm-6b"] +``` + +```bash +# Modification of server_config.py configuration, if LLM_MODELS does not have multiple model configurations, no additional settings are needed. +# Modify the configuration of server_config.py#FSCHAT_MODEL_WORKERS +"model_name": {'host': DEFAULT_BIND_HOST, 'port': 20057} +``` + + + +
量化模型接入 + +```bash +# If you need to support the codellama-34b-int4 model, you need to patch fastchat +cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py +# If you need to support the qwen-72b-int4 model, you need to patch fastchat +cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py + +# Quantization requires modification of the llm_api.py configuration +# Uncomment `kwargs["gptq_wbits"] = 4` in examples/llm_api.py#559 +``` + +## Public Large Model Interface Access + +```bash +# Modification of model_config.py configuration +# ONLINE_LLM_MODEL +# Other interface development comes from the langchain-chatchat project, untested due to lack of relevant accounts. +# Specify the model service to be launched, keeping both consistent +LLM_MODEL = "gpt-3.5-turbo" +LLM_MODELs = ["gpt-3.5-turbo"] +``` + +外部大模型接口接入示例 + +```bash +# 1. Implement a new model access class +# Refer to ~/examples/model_workers/openai.py#ExampleWorker +# Implementing the do_chat function will enable the use of LLM capabilities + +class XXWorker(ApiModelWorker): + def __init__( + self, + *, + controller_addr: str = None, + worker_addr: str = None, + model_names: List[str] = ["gpt-3.5-turbo"], + version: str = "gpt-3.5", + **kwargs, + ): + kwargs.update(model_names=model_names, controller_addr=controller_addr, worker_addr=worker_addr) + kwargs.setdefault("context_len", 16384) #TODO 16K模型需要改成16384 + super().__init__(**kwargs) + self.version = version + + def do_chat(self, params: ApiChatParams) -> Dict: + ''' + 执行Chat的方法,默认使用模块里面的chat函数。 + :params.messages : [ + {"role": "user", "content": "hello"}, + {"role": "assistant", "content": "hello"} + ] + :params.xx: 详情见 ApiChatParams + 要求返回形式:{"error_code": int, "text": str} + ''' + return {"error_code": 500, "text": f"{self.model_names[0]}未实现chat功能"} + + +# Finally, complete the registration in ~/examples/model_workers/__init__.py +# from .xx import XXWorker + +# 2. Complete access through an existing model access class +# Or directly use the existing relevant large model class for use (lacking relevant account testing, community contributions after testing are welcome) +``` + + +```bash +# Modification of model_config.py#ONLINE_LLM_MODEL configuration +# Enter exclusive model details: version, api_base_url, api_key, provider (consistent with the class name above) +ONLINE_LLM_MODEL = { + # Online models. Please set different ports for each online API in server_config. + "openai-api": { + "model_name": "gpt-3.5-turbo", + "api_base_url": "https://api.openai.com/v1", + "api_key": "", + "openai_proxy": "", + }, + "example": { + "version": "gpt-3.5", # Using openai interface as an example + "api_base_url": "https://api.openai.com/v1", + "api_key": "", + "provider": "ExampleWorker", + }, +} +``` + +## Launching Large Model Services +```bash +# start llm-service (optional) - Launch the large model service separately +python examples/llm_api.py +``` + +```bash +# Test +import openai +# openai.api_key = "EMPTY" # Not support yet +openai.api_base = "http://127.0.0.1:8888/v1" +# Select the model you launched +model = "example" +# create a chat completion +completion = openai.ChatCompletion.create( + model=model, + messages=[{"role": "user", "content": "Hello! What is your name? "}], + max_tokens=100, +) +# print the completion +print(completion.choices[0].message.content) +# Once the correct output is confirmed, LLM can be accessed normally. +``` + + + +or + +```bash +# model_config.py#USE_FASTCHAT - Determine whether to integrate local models via fastchat +USE_FASTCHAT = "gpt" not in LLM_MODEL +python start.py #221 Automatically executes python llm_api.py +``` \ No newline at end of file diff --git a/sources/readme_docs/fastchat.md b/sources/readme_docs/fastchat.md index bf808de..dcbd857 100644 --- a/sources/readme_docs/fastchat.md +++ b/sources/readme_docs/fastchat.md @@ -2,11 +2,6 @@ 依托于开源的 LLM 与 Embedding 模型,本项目可实现基于开源模型的离线私有部署。此外,本项目也支持 OpenAI API 的调用。 -## 📜 目录 -- [ 本地私有化模型接入](#本地私有化模型接入) -- [ 公开大模型接口接入](#公开大模型接口接入) -- [ 启动大模型服务](#启动大模型服务) - ## 本地私有化模型接入
模型地址配置示例,model_config.py配置修改 @@ -16,24 +11,7 @@ # 注意:当llm_model_dict和VLLM_MODEL_DICT同时存在时,优先启动VLLM_MODEL_DICT中的模型配置 # llm_model_dict 配置接入示例如下 -llm_model_dict = { - "chatglm-6b": { - "local_model_path": "THUDM/chatglm-6b", - "api_base_url": "http://localhost:8888/v1", # "name"修改为fastchat服务中的"api_base_url" - "api_key": "EMPTY" - } -} -# VLLM_MODEL_DICT 配置接入示例如下 -VLLM_MODEL_DICT = { - 'chatglm2-6b': "THUDM/chatglm-6b", -} - -``` - -
模型路径填写示例 - -```bash # 1、若把模型放到 ~/codefuse-chatbot/llm_models 路径下 # 若模型地址如下 model_dir: ~/codefuse-chatbot/llm_models/THUDM/chatglm-6b @@ -72,21 +50,21 @@ model_dir: ~/THUDM/chatglm-6b # 参考配置如下 llm_model_dict = { "chatglm-6b": { - "local_model_path": "~/THUDM/chatglm-6b", + "local_model_path": "your personl dir/THUDM/chatglm-6b", "api_base_url": "http://localhost:8888/v1", # "name"修改为fastchat服务中的"api_base_url" "api_key": "EMPTY" } } VLLM_MODEL_DICT = { - 'chatglm2-6b': "~/THUDM/chatglm-6b", + 'chatglm2-6b': "your personl dir/THUDM/chatglm-6b", } ``` ```bash # 3、指定启动的模型服务,两者保持一致 -LLM_MODEL = "gpt-3.5-turbo-16k" -LLM_MODELs = ["gpt-3.5-turbo-16k"] +LLM_MODEL = "chatglm-6b" +LLM_MODELs = ["chatglm-6b"] ``` ```bash @@ -106,7 +84,7 @@ cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py # 若需要支撑qwen-72b-int4模型,需要给fastchat打一个补丁 cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py # 量化需修改llm_api.py的配置 -# dev_opsgpt/service/llm_api.py#559 取消注释 kwargs["gptq_wbits"] = 4 +# examples/llm_api.py#559 取消注释 kwargs["gptq_wbits"] = 4 ``` ## 公开大模型接口接入 @@ -117,15 +95,15 @@ cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py # 其它接口开发来自于langchain-chatchat项目,缺少相关账号未经测试 # 指定启动的模型服务,两者保持一致 -LLM_MODEL = "gpt-3.5-turbo-16k" -LLM_MODELs = ["gpt-3.5-turbo-16k"] +LLM_MODEL = "gpt-3.5-turbo" +LLM_MODELs = ["gpt-3.5-turbo"] ``` 外部大模型接口接入示例 ```bash # 1、实现新的模型接入类 -# 参考 ~/dev_opsgpt/service/model_workers/openai.py#ExampleWorker +# 参考 ~/examples/model_workers/openai.py#ExampleWorker # 实现do_chat函数即可使用LLM的能力 class XXWorker(ApiModelWorker): @@ -156,7 +134,7 @@ class XXWorker(ApiModelWorker): return {"error_code": 500, "text": f"{self.model_names[0]}未实现chat功能"} -# 最后在 ~/dev_opsgpt/service/model_workers/__init__.py 中完成注册 +# 最后在 ~/examples/model_workers/__init__.py 中完成注册 # from .xx import XXWorker # 2、通过已有模型接入类完成接入 @@ -188,7 +166,7 @@ ONLINE_LLM_MODEL = { ## 启动大模型服务 ```bash # start llm-service(可选) 单独启动大模型服务 -python dev_opsgpt/service/llm_api.py +python examples/llm_api.py ``` ```bash @@ -219,5 +197,5 @@ or ```bash # model_config.py#USE_FASTCHAT 判断是否进行fastchat接入本地模型 USE_FASTCHAT = "gpt" not in LLM_MODEL -python start.py #224 自动执行 python service/llm_api.py +python start.py #221 自动执行 python llm_api.py ``` \ No newline at end of file diff --git a/sources/readme_docs/multi-agent.md b/sources/readme_docs/multi-agent.md deleted file mode 100644 index f6f3c81..0000000 --- a/sources/readme_docs/multi-agent.md +++ /dev/null @@ -1,113 +0,0 @@ - -## 📜 目录 -- [简介](#简介) -- [模块介绍](#模块介绍) -- [快速使用](#快速使用) - - -## 简介 - -为了提高大型模型在推理准确性方面的表现,业界出现了多种创新的大型语言模型(LLM)玩法。从最早的CoT、ToT到GoT,这些方法不断拓展了LLM的能力边界。在处理复杂问题时,我们可以通过ReAct过程来选择、调用和执行工具反馈,同时实现多轮工具使用和多步骤执行。 - -但对于更复杂的场景,例如复杂代码的开发,单一功能的LLM Agent显然难以胜任。因此,社区开始发展出多Agent的组合玩法,比如专注于metaGPT、GPT-Engineer、chatDev等开发领域的项目,以及专注于自动化构建Agent和Agent对话的AutoGen项目。 - -经过对这些框架的深入分析,发现大多数的Agent框架整体耦合度较高,其易用性和可扩展性较差。在预设场景中实现特定场景,但想要进行场景扩展却困难重重。 - -因此,我们希望构建一个可扩展、易于使用的Multi-Agent框架,以支持ChatBot在获取知识库信息的同时,能够辅助完成日常办公、数据分析、开发运维等各种通用任务。 - -本项目的Mutli-Agent框架汲取兼容了多个框架的优秀设计,比如metaGPT中的消息池(message pool)、autogen中的代理选择器(agent selector)等。 - -
- 图片 -
- -以下模块将从5个方面介绍Multi Agent框架所需要素: -- Agent Communication在Multi Agent框架中,确保Agent可以有效地进行信息交流对于管理上下文以及提高问答效率至关重要。 - a. 遵循简洁直观易于理解的链式对话原则,将Agent以线性方式排列串连成一个执行链路。 - b. 借鉴metaGPT中的Message Pool框架,允许Agent对Message Pool进行推送和订阅,使链路更加灵活。有利于精细化Prompt工程的场景,但难以把握复杂链路的关系分析。 -- Standard Operation Process(SOP):对LLM的生成结果进行标准化解析和处理。 - a. 定义Agent的 Input 和 Output 范围,能够组装和解析相关Action和Status,保证框架运行的稳定性 - b. 封装多种基础Action执行模块,如Tool Using、Planning、Coding、Direct Answering、final answer等SOP标识,以满足Agent的基本工作需求。 -- Plan and Executor:增加LLM的Tool使用、Agent调度、代码的生成。设置了几种基本链路,例如: - a. 单轮问答,也可以扩展到CoT、ToT、GoT等形式。 - b. ReAct,基础的响应决策过程,模型设置SOP 状态以终止循环 - c. TaskPlaning - Executor,任务完成即可结束 -- Long-short term memory Management:Multi-Agent与单Agent的关键区别在于,Multi-Agent需要处理大量的交流信息,类似人类团队协作的过程。增加一个专门负责内容总结(类似于会议助理)的Agent,对长期记忆进行总结并提更有效信息传递给下一位Agent,而非传递所有内容给下一位Agent。 -- Human-agent interaction:面对复杂场景时,需要人类介入Agent交互过程并提供反馈。通过上述 Long-short term memory Management 和 Agent Communication 过程,使LLM能准确理解人类的意图,从而更有效地完成任务。 - -总的来说,这五个要素共同构建了一个Multi Agent框架,确保Agent之间的协作更加紧密和高效,同时也能够适应更复杂的任务需求和更多样的交互场景。通过组合多个Agent链路来实现一个完整且复杂的项目上线场景(Dev Phase),如Demand Chain(CEO)、Product Arguement Chain(CPO、CFO、CTO)、Engineer Group Chain(Selector、Developer1~N)、QA Engineer Chain(Developer、Tester)、Deploy Chain(Developer、Deploer)。 - - - -## 模块介绍 -为了便于大家理解整个Multi-Agent的链路,我们采取 Flow 的形式来详细介绍如何通过配置构建 - -
- 图片 -
- - -
下面,我们先介绍相关的模块
- -### Agent -在Agent设计层面,我们提供了四种基本的Agent类型,对这些Agent进行Role的基础设定,可满足多种通用场景的交互和使用 -1. BaseAgent:提供基础问答、工具使用、代码执行的功能,根据Prompt格式实现 输入 => 输出 -2. ExecutorAgent:对任务清单进行顺序执行,根据 User 或 上一个Agent编排的计划,完成相关任务 -3. ReactAgent:提供标准React的功能,根据问题实现当前任务 -4. SelectorAgent:提供选择Agent的功能,根据User 或 上一个 Agent的问题选择合适的Agent来进行回答. - -输出后将 message push 到 memory pool 之中,后续通过Memory Manager进行管理 - -### Chain -基础链路:BaseChain,串联agent的交互,完成相关message和memory的管理 - -### Phase -基础场景:BasePhase,串联chain的交互,完成相关message和memory的管理 - -### Prompt Manager -Mutli-Agent链路中每一个agent的prompt创建 -1. 通过对promtp_input_keys和promtp_output_keys对的简单设定,可以沿用预设 Prompt Context 创建逻辑,从而实现agent prompt快速配置 -2. 也可以对prompt manager模块进行新的 key-context 设计,实现个性化的 Agent Prompt -Memory Manager -主要用于 chat history 的管理,暂未完成 -● 将chat history在数据库进行读写管理,包括user input、 llm output、doc retrieval、code retrieval、search retrieval -● 对 chat history 进行关键信息总结 summary context,作为 prompt context -● 提供检索功能,检索 chat history 或者 summary context 中与问题相关信息,辅助问答 - -### Role Config -|Config Key Name| Type| Description| -| ------------------ | ---------- | ---------- | -|role_prompt| String |角色描述| -|role_type |String |Enum: assistant| -|role_name |String |角色名称,用于后续prompt context的组装和筛选| -|agent_type |String |Enum:BaseAgent、SelectorAgent、ExecutorAgent、ReactAgent 也可以继承以上几种Agent然后去构造相关的Agent| -|focus_agents |List[String] |metagpt的逻辑,关注哪些agent生成的message,可选值范围为:role_name -|focus_message_keys |List[String]| 额外增加的逻辑,关注message里面具体的 key 信息可选值范围为:agent 的 output_keys| -|promtp_input_keys |List[String] |Enum:| -|promtp_output_keys |List[String] |Enum:| -|chat_turn |int |只针对ReactAgent有效| - - -### Chain Config -|Config Key Name| Type |Description| -| ------------------ | ---------- | ---------- | -|chain_prompt |String| chain的描述| -|chain_name| String |角色名称,用于后续prompt context的组装和筛选| -|chain_type| String| Enum:BaseChain 也可以继承以上Chain,构造相关的Chain -|agents |List[String] |chain当中存在的agent以及agent的执行顺序| -|chat_turn |int agent之间的交互轮数| - - -### Phase Config -|Config Key Name |Type |Description| -| ------------------ | ---------- | ---------- | -|phase_name| String| 场景名称| -|phase_type |String |Enum:BasePhase 也可以继承以上Phase,自定义构造相关的Phase| -|chains |List[String] |phase当中存在的chain以及chain的执行顺序| -|do_doc_retrieval |bool |在场景执行开始判断是否需要补充额外信息| -|do_code_retrieval| bool |在场景执行开始判断是否需要补充额外信息| -|do_tool_retrieval |bool |在场景执行开始判断是否需要补充额外信息| - - -## 快速使用 -Comming soon \ No newline at end of file diff --git a/sources/readme_docs/roadmap-en.md b/sources/readme_docs/roadmap-en.md new file mode 100644 index 0000000..c02ecbe --- /dev/null +++ b/sources/readme_docs/roadmap-en.md @@ -0,0 +1,74 @@ + +Roadmap Overview + +- [x] Sandbox Environment ✅ + - [x] Isolated sandbox environment for code execution ✅ + - [x] File upload and download ✅ + - [ ] Support for Java execution environment ⬜ +- [x] Vector Database & Retrieval ✅ + - [x] Task retrieval ✅ + - [x] Tool retrieval ✅ +- [x] Prompt Management ✅ +- [x] Memory Management ✅ +- [x] Multi Agent Framework ✅ + - [ ] PRD (Product Requirement Document), system analysis, interface design ⬜ + - [ ] Generate code based on requirement documents, system analysis, and interface design ⬜ + - [ ] Automated testing, automated debugger ⬜ + - [ ] Operations process integration (ToolLearning) ⬜ + - [ ] Fully automated end-to-end process ⬜ +- [x] Integration with LLM based on fastchat ✅ +- [x] Integration with Text Embedding based on sentencebert ✅ +- [x] Improved vector loading speed ✅ +- [x] Connector ✅ + - [x] React Mode based on langchain ✅ + - [x] Tool retrieval completed with langchain ✅ +- [ ] General Capability for Web Crawl ⬜ + - [x] Technical documentation: Zhihu, CSDN, Alibaba Cloud Developer Forum, Tencent Cloud Developer Forum, etc. ✅ + - [ ] Issue document ⬜ + - [ ] SDK Library Document ⬜ + +v0.0 +- [x] Sandbox Environment ✅ + - [x] Isolated sandbox environment for code execution ✅ +- [x] Integration with LLM based on fastchat ✅ +- [x] Integration with Text Embedding based on sentencebert ✅ +- [x] General Capability for Web Crawl: Technical documentation: Zhihu, CSDN, Alibaba Cloud Developer Forum, Tencent Cloud Developer Forum, etc. ✅ + +Done +
+ +v0.1 +- [x] Sandbox Environment: File upload and download ✅ +- [x] Vector Database & Retrieval ✅ + - [x] Task retrieval ✅ + - [x] Tool retrieval ✅ +- [x] Connector ✅ + - [x] React Mode based on langchain ✅ +- [x] Integration with Text Embedding based on sentencebert: Improved vector loading speed ✅ + +Done +
+ +v0.2 +- [x] Prompt Management ✅ +- [x] Memory Management ✅ +- [x] Vector Database & Retrieval ✅ + +Done +
+ +v0.3 +- [x] Sandbox Environment ✅ + - [ ] Support for Java execution environment ⬜ +- [x] Multi Agent ✅ + - [ ] PRD (Product Requirement Document), system analysis, interface design ⬜ + - [ ] Generate code based on requirement documents, system analysis, and interface design ⬜ + - [ ] Automated testing, automated debugger ⬜ + - [ ] Operations process integration (ToolLearning) ⬜ + - [ ] Fully automated end-to-end process ⬜ +- [x] General Capability for Web Crawl ✅ + - [ ] Issue document ⬜ + - [ ] SDK Library Document ⬜ + +DDL: 2024.12.31 +
\ No newline at end of file diff --git a/sources/readme_docs/roadmap.md b/sources/readme_docs/roadmap.md index 3dae2f9..8961616 100644 --- a/sources/readme_docs/roadmap.md +++ b/sources/readme_docs/roadmap.md @@ -8,74 +8,74 @@ 完整路线 -- [x] Sandbox 环境 - - [x] 环境隔离的sandbox环境与代码执行 - - [x] 上传、下载文件 - - [ ] 支持java执行环境 +- [x] Sandbox 环境 ✅ + - [x] 环境隔离的sandbox环境与代码执行 ✅ + - [x] 上传、下载文件 ✅ + - [ ] 支持java执行环境 - [ ] Vector Database & Retrieval - - [x] task retrieval - - [x] tool retrieval -- [ ] Prompt Management -- [ ] memory Management -- [ ] Multi Agent - - [ ] PRD需求文档、系分、接口设计 - - [ ] 根据需求文档、系分、接口设计生产代码 - - [ ] 自动测试、自动debugger - - [ ] 运维流程接入(ToolLearning) - - [ ] 全流程自动 -- [x] 基于fastchat接入LLM -- [x] 基于sentencebert接入Text Embedding - - [x] 向量加载速度提升 -- [x] Connector - - [x] 基于langchain的react模式 - - [x] 基于langchain完成tool检索 -- [ ] Web Crawl 通用能力 - - [x] 技术文档: 知乎、csdn、阿里云开发者论坛、腾讯云开发者论坛等 - - [ ] issue document - - [ ] SDK Library Document + - [x] task retrieval ✅ + - [x] tool retrieval ✅ +- [x] Prompt Management ✅ +- [x] memory Management ✅ +- [x] Multi Agent ✅ + - [ ] PRD需求文档、系分、接口设计 ⬜ + - [ ] 根据需求文档、系分、接口设计生产代码 ⬜ + - [ ] 自动测试、自动debugger ⬜ + - [ ] 运维流程接入(ToolLearning)⬜ + - [ ] 全流程自动 ⬜ +- [x] 基于fastchat接入LLM ✅ +- [x] 基于sentencebert接入Text Embedding ✅ + - [x] 向量加载速度提升 ✅ +- [x] Connector ✅ + - [x] 基于langchain的react模式 ✅ + - [x] 基于langchain完成tool检索 ✅ +- [x] Web Crawl 通用能力 ✅ + - [x] 技术文档: 知乎、csdn、阿里云开发者论坛、腾讯云开发者论坛等 ✅ + - [ ] issue document ⬜ + - [ ] SDK Library Document ⬜

- v0.0 -- [x] Sandbox 环境 - - [x] 环境隔离的sandbox环境与代码执行 -- [x] 基于fastchat接入LLM -- [x] 基于sentencebert接入Text Embedding -- [x] Web Crawl 通用能力:技术文档: 知乎、csdn、阿里云开发者论坛、腾讯云开发者论坛等 +- [x] Sandbox 环境 ✅ + - [x] 环境隔离的sandbox环境与代码执行 ✅ +- [x] 基于fastchat接入LLM ✅ +- [x] 基于sentencebert接入Text Embedding ✅ +- [x] Web Crawl 通用能力:技术文档: 知乎、csdn、阿里云开发者论坛、腾讯云开发者论坛等 ✅
- v0.1 -- [x] Sandbox 环境: 上传、下载文件 -- [x] Vector Database & Retrieval - - [x] task retrieval - - [x] tool retrieval -- [x] Connector - - [x] 基于langchain的react模式 -- [x] 基于sentencebert接入Text Embedding: 向量加载速度提升 +- [x] Sandbox 环境: 上传、下载文件 ✅ +- [x] Vector Database & Retrieval ✅ + - [x] task retrieval ✅ + - [x] tool retrieval ✅ +- [x] Connector ✅ + - [x] 基于langchain的react模式 ✅ +- [x] 基于sentencebert接入Text Embedding: 向量加载速度提升 ✅ Done
- v0.2 -- [ ] Prompt Management -- [ ] memory Management -- [ ] Vector Database & Retrieval +- [x] Prompt Management ✅ +- [x] memory Management ✅ +- [x] Vector Database & Retrieval ✅ DDL: 2024.01.31
- v0.3 -- [x] Sandbox 环境 - - [ ] 支持java执行环境 -- [ ] Multi Agent - - [ ] PRD需求文档、系分、接口设计 - - [ ] 根据需求文档、系分、接口设计生产代码 - - [ ] 自动测试、自动debugger - - [ ] 运维流程接入(ToolLearning) - - [ ] 全流程自动 -- [ ] Web Crawl 通用能力 - - [ ] issue document - - [ ] SDK Library Document +- [x] Sandbox 环境 ✅ + - [ ] 支持java执行环境 ⬜ +- [x] Multi Agent Framework ✅ + - [ ] PRD需求文档、系分、接口设计 ⬜ + - [ ] 根据需求文档、系分、接口设计生产代码 ⬜ + - [ ] 自动测试、自动debugger ⬜ + - [ ] 运维流程接入(ToolLearning) ⬜ + - [ ] 全流程自动 ⬜ +- [x] Web Crawl 通用能力 ✅ + - [ ] issue document ⬜ + - [ ] SDK Library Document ⬜ DDL: 2024.12.31 -
\ No newline at end of file +
diff --git a/sources/readme_docs/start-en.md b/sources/readme_docs/start-en.md new file mode 100644 index 0000000..5430d4d --- /dev/null +++ b/sources/readme_docs/start-en.md @@ -0,0 +1,112 @@ + +If you need to deploy a privatized model, please install the NVIDIA driver yourself. + +### Preparation of Python environment +- It is recommended to use conda to manage the python environment (optional) +```bash +# Prepare conda environment +conda create --name Codefusegpt python=3.9 +conda activate Codefusegpt +``` + +- Install related dependencies +```bash +cd Codefuse-ChatBot +pip install -r requirements.txt +``` + +### Sandbox Environment Preparation +- Windows Docker installation: +[Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) supports 64-bit versions of Windows 10 Pro with Hyper-V enabled (Hyper-V is not required for versions v1903 and above), or 64-bit versions of Windows 10 Home v1903 and above. + - [【全面详细】Windows10 Docker安装详细教程](https://zhuanlan.zhihu.com/p/441965046) + - [Docker 从入门到实践](https://yeasy.gitbook.io/docker_practice/install/windows) + - [Handling 'Docker Desktop requires the Server service to be enabled'](https://blog.csdn.net/sunhy_csdn/article/details/106526991) + - [安装wsl或者等报错提示](https://learn.microsoft.com/zh-cn/windows/wsl/install) + +- Linux Docker installation: +Linux installation is relatively simple, please search Baidu/Google for installation guides. + +- Mac Docker installation + - [Docker 从入门到实践](https://yeasy.gitbook.io/docker_practice/install/mac) + +```bash +# Build the image for the sandbox environment, see above for notebook version issues +bash docker_build.sh +``` + +### Model Download (Optional) + +If you need to use open-source LLM and Embedding models, you can download them from HuggingFace. +Here we take THUDM/chatglm2-6b and text2vec-base-chinese as examples: + +``` +# install git-lfs +git lfs install + +# install LLM-model +git lfs clone https://huggingface.co/THUDM/chatglm2-6b +cp ~/THUDM/chatglm2-6b ~/codefuse-chatbot/llm_models/ + +# install Embedding-model +git lfs clone https://huggingface.co/shibing624/text2vec-base-chinese +cp ~/shibing624/text2vec-base-chinese ~/codefuse-chatbot/embedding_models/ +``` + + + +### Basic Configuration + +```bash +# Modify the basic configuration for service startup +cd configs +cp model_config.py.example model_config.py +cp server_config.py.example server_config.py + +# model_config#11~12 If you need to use the OpenAI interface, the OpenAI interface key +os.environ["OPENAI_API_KEY"] = "sk-xxx" +# Replace with the api_base_url you need +os.environ["API_BASE_URL"] = "https://api.openai.com/v1" + +# vi model_config#LLM_MODEL The language model you need to choose +LLM_MODEL = "gpt-3.5-turbo" +LLM_MODELs = ["gpt-3.5-turbo"] + +# vi model_config#EMBEDDING_MODEL The private vector model you need to choose +EMBEDDING_ENGINE = 'model' +EMBEDDING_MODEL = "text2vec-base" + +# Example of vector model access, modify model_config#embedding_model_dict +# If the model directory is: +model_dir: ~/codefuse-chatbot/embedding_models/shibing624/text2vec-base-chinese +# Configure as follows +"text2vec-base": "shibing624/text2vec-base-chinese" + + +# vi server_config#8~14, It's recommended to use a container to start the service to prevent environment conflicts when installing other dependencies using the codeInterpreter feature +DOCKER_SERVICE = True +# Whether to use a container sandbox +SANDBOX_DO_REMOTE = True +``` + + + +### Starting the Service + +By default, only the webui-related services are started, and fastchat is not started (optional). + +```bash +# If you need to support the codellama-34b-int4 model, you need to patch fastchat +# cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py +# Modify examples/llm_api.py#258 to kwargs={"gptq_wbits": 4}, + +# start llm-service (optional) +python examples/llm_api.py +``` +For more LLM integration methods, see[more details...](sources/readme_docs/fastchat-en.md) +
+ +```bash +# After completing the server_config.py configuration, you can start with one click +cd examples +python start.py +``` \ No newline at end of file diff --git a/sources/readme_docs/start.md b/sources/readme_docs/start.md index 6738faf..d6e8a11 100644 --- a/sources/readme_docs/start.md +++ b/sources/readme_docs/start.md @@ -1,6 +1,7 @@ -请自行安装 nvidia 驱动程序,本项目已在 Python 3.9.18,CUDA 11.7 环境下,Windows、X86 架构的 macOS 系统中完成测试。 -1、python 环境准备 +如需使用私有化模型部署,请自行安装 nvidia 驱动程序。 + +### python 环境准备 - 推荐采用 conda 对 python 环境进行管理(可选) ```bash @@ -16,7 +17,7 @@ cd codefuse-chatbot pip install -r requirements.txt ``` -2、沙盒环境准备 +### 沙盒环境准备 - windows Docker 安装: [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) 支持 64 位版本的 Windows 10 Pro,且必须开启 Hyper-V(若版本为 v1903 及以上则无需开启 Hyper-V),或者 64 位版本的 Windows 10 Home v1903 及以上版本。 @@ -36,7 +37,7 @@ Linux 安装相对比较简单,请自行 baidu/google 相关安装 bash docker_build.sh ``` -3、模型下载(可选) +### 模型下载(可选) 如需使用开源 LLM 与 Embedding 模型可以从 HuggingFace 下载。 此处以 THUDM/chatglm2-6bm 和 text2vec-base-chinese 为例: @@ -55,7 +56,7 @@ cp ~/shibing624/text2vec-base-chinese ~/codefuse-chatbot/embedding_models/ ``` -4、基础配置 +### 基础配置 ```bash # 修改服务启动的基础配置 @@ -76,32 +77,30 @@ LLM_MODELs = ["gpt-3.5-turbo"] EMBEDDING_ENGINE = 'model' EMBEDDING_MODEL = "text2vec-base" -# vi model_config#embedding_model_dict 修改成你的本地路径,如果能直接连接huggingface则无需修改 +# 向量模型接入示例,修改 model_config#embedding_model_dict # 若模型地址为: model_dir: ~/codefuse-chatbot/embedding_models/shibing624/text2vec-base-chinese # 配置如下 -"text2vec-base": "shibing624/text2vec-base-chinese", +"text2vec-base": "shibing624/text2vec-base-chinese" # vi server_config#8~14, 推荐采用容器启动服务 DOCKER_SERVICE = True # 是否采用容器沙箱 SANDBOX_DO_REMOTE = True -# 是否采用api服务来进行 -NO_REMOTE_API = True ``` -5、启动服务 +### 启动服务 默认只启动webui相关服务,未启动fastchat(可选)。 ```bash # 若需要支撑codellama-34b-int4模型,需要给fastchat打一个补丁 # cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py -# dev_opsgpt/service/llm_api.py#258 修改为 kwargs={"gptq_wbits": 4}, +# examples/llm_api.py#258 修改为 kwargs={"gptq_wbits": 4}, # start llm-service(可选) -python dev_opsgpt/service/llm_api.py +python examples/llm_api.py ``` -更多LLM接入方法见[详情...](./fastchat.md) +更多LLM接入方法见[详情...](sources/readme_docs/fastchat.md)
```bash diff --git a/tests/chains_test.py b/tests/chains_test.py index da3f182..ffc318d 100644 --- a/tests/chains_test.py +++ b/tests/chains_test.py @@ -5,117 +5,148 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( +from coagent.tools import ( toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, TOOL_DICT, TOOL_SETS ) from configs.model_config import * -from dev_opsgpt.connector.phase import BasePhase -from dev_opsgpt.connector.agents import BaseAgent -from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.schema import ( - Message, load_role_configs, load_phase_configs, load_chain_configs - ) -from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +from coagent.connector.phase import BasePhase +from coagent.connector.agents import BaseAgent +from coagent.connector.chains import BaseChain +from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS import importlib - +from configs.model_config import JUPYTER_WORK_PATH, KB_ROOT_PATH +from coagent.llm_models.llm_config import EmbedConfig, LLMConfig +from coagent.connector.configs import AGETN_CONFIGS +from coagent.connector.schema import Message, load_role_configs print(src_dir) # tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) -TOOL_SETS = [ - "StockInfo", "StockName" - ] +# TOOL_SETS = [ +# "StockInfo", "StockName" +# ] -tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) +# tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) +# role_configs = load_role_configs(AGETN_CONFIGS) +# chain_configs = load_chain_configs(CHAIN_CONFIGS) +# phase_configs = load_phase_configs(PHASE_CONFIGS) -agent_module = importlib.import_module("dev_opsgpt.connector.agents") +# agent_module = importlib.import_module("coagent.connector.agents") -# agent的测试 -query = Message(role_name="tool_react", role_type="human", - input_query="我有一份时序数据,[0.857, 2.345, 1.234, 4.567, 3.456, 9.876, 5.678, 7.890, 6.789, 8.901, 10.987, 12.345, 11.234, 14.567, 13.456, 19.876, 15.678, 17.890, 16.789, \ - 18.901, 20.987, 22.345, 21.234, 24.567, 23.456, 29.876, 25.678, 27.890, 26.789, 28.901, 30.987, 32.345, 31.234, 34.567, 33.456, 39.876, 35.678, 37.890, 36.789, 38.901, 40.987],\ - 我不知道这份数据是否存在问题,请帮我判断一下", tools=tools) +# # agent的测试 -query = Message(role_name="tool_react", role_type="human", - input_query="帮我确认下127.0.0.1这个服务器的在10点是否存在异常,请帮我判断一下", tools=tools) -query = Message(role_name="code_react", role_type="human", - input_query="帮我确认当前目录下有哪些文件", tools=tools) +# llm_config = LLMConfig( +# model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], +# api_base_url=os.environ["API_BASE_URL"], temperature=0.3 +# ) +# embed_config = EmbedConfig( +# embed_engine="model", embed_model="text2vec-base-chinese", +# embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" +# ) -# "给我一份冒泡排序的代码" -query = Message(role_name="user", role_type="human", - input_query="对employee_data.csv进行数据分析", tools=tools) +# # 从已有的配置中选择一个config,具体参数细节见下面 +# role_configs = load_role_configs(AGETN_CONFIGS) +# agent_config = role_configs["general_planner"] +# # 生成agent实例 +# base_agent = BaseAgent( +# role=agent_config.role, +# prompt_config = agent_config.prompt_config, +# prompt_manager_type=agent_config.prompt_manager_type, +# chat_turn=agent_config.chat_turn, +# focus_agents=[], +# focus_message_keys=[], +# llm_config=llm_config, +# embed_config=embed_config, +# jupyter_work_path=JUPYTER_WORK_PATH, +# kb_root_path=KB_ROOT_PATH, +# ) +# # round-1 +# query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +# query = Message( +# role_name="human", role_type="user", +# role_content=query_content, input_query=query_content, origin_query=query_content, +# ) +# output_message = base_agent.step(query) +# print(output_message.to_str_content(content_key="parsed_output_list")) -role = role_configs["general_planner"] -agent_class = getattr(agent_module, role.role.agent_type) -agent = agent_class(role.role, - task = None, - memory = None, - chat_turn=role.chat_turn, - do_search = role.do_search, - do_doc_retrieval = role.do_doc_retrieval, - do_tool_retrieval = role.do_tool_retrieval,) - -# message = agent.run(query) -# print(message.role_content) # chain的测试 +llm_config = LLMConfig( + model_name="gpt-3.5-turbo", model_device="cpu",api_key=os.environ["OPENAI_API_KEY"], + api_base_url=os.environ["API_BASE_URL"], temperature=0.3 + ) +embed_config = EmbedConfig( + embed_engine="model", embed_model="text2vec-base-chinese", + embed_model_path="D://project/gitlab/llm/external/ant_code/Codefuse-chatbot/embedding_models/text2vec-base-chinese" + ) -# query = Message(role_name="deveploer", role_type="human", role_content="编写冒泡排序,并生成测例") -# query = Message(role_name="general_planner", role_type="human", role_content="对employee_data.csv进行数据分析") -# query = Message(role_name="tool_react", role_type="human", role_content="我有一份时序数据,[0.857, 2.345, 1.234, 4.567, 3.456, 9.876, 5.678, 7.890, 6.789, 8.901, 10.987, 12.345, 11.234, 14.567, 13.456, 19.876 , 15.678, 17.890, 16.789, 18.901, 20.987, 22.345, 21.234, 24.567, 23.456, 29.876, 25.678, 27.890, 26.789, 28.901, 30.987, 32.345, 31.234, 34.567, 33.456, 39.876, 35.678, 37.890, 36.789, 38.901, 40.987],\我不知道这份数据是否存在问题,请帮我判断一下", tools=tools) -# role = role_configs[query.role_name] -# role1 = role_configs["general_planner"] -# role2 = role_configs["executor"] +role_configs = load_role_configs(AGETN_CONFIGS) +agent_config = role_configs["general_planner"] -# agents = [ -# getattr(agent_module, role1.role.agent_type)(role1.role, -# task = None, -# memory = None, -# do_search = role1.do_search, -# do_doc_retrieval = role1.do_doc_retrieval, -# do_tool_retrieval = role1.do_tool_retrieval,), -# getattr(agent_module, role2.role.agent_type)(role2.role, -# task = None, -# memory = None, -# do_search = role2.do_search, -# do_doc_retrieval = role2.do_doc_retrieval, -# do_tool_retrieval = role2.do_tool_retrieval, -# stop = "\n**Observation:**", -# chat_turn=5, -# ), -# ] -# query = Message(role_name="user", role_type="human", -# input_query="确认本地是否存在book_data.csv,并查看它有哪些列和数据类型,分析这份数据的内容,根据这个数据预测未来走势", tools=tools) -# query = Message(role_name="user", role_type="human", -# input_query="确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图", tools=tools) -# chain = BaseChain(chain_configs["executorChain"], agents, do_code_exec=False, chat_turn=1, do_checker=False) +role1 = role_configs["general_planner"] +role2 = role_configs["executor"] +agent_module = importlib.import_module("coagent.connector.agents") +agents = [ + getattr(agent_module, role1.role.agent_type)( + role=role1.role, + prompt_config = role1.prompt_config, + prompt_manager_type=role1.prompt_manager_type, + chat_turn=role1.chat_turn, + focus_agents=role1.focus_agents, + focus_message_keys=role1.focus_message_keys, + llm_config=llm_config, + embed_config=embed_config, + jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + ), + getattr(agent_module, role2.role.agent_type)( + role=role2.role, + prompt_config = role2.prompt_config, + prompt_manager_type=role2.prompt_manager_type, + chat_turn=role2.chat_turn, + focus_agents=role2.focus_agents, + focus_message_keys=role2.focus_message_keys, + llm_config=llm_config, + embed_config=embed_config, + jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + ), + ] -# output_message, local_memory = chain.step(query) -# print(output_message.role_content) +chain = BaseChain( + agents, + chat_turn=1, + jupyter_work_path=JUPYTER_WORK_PATH, + kb_root_path=KB_ROOT_PATH, + llm_config=llm_config, + embed_config=embed_config, + ) -# print("\n".join("\n".join([": ".join(j) for j in i]) for i in chain.get_agents_memory())) -# print("\n".join(": ".join(i) for i in chain.get_memory())) -# print( chain.get_agents_memory_str()) -# print( chain.get_memory_str()) +# round-1 +query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, output_memory = chain.step(query) +print(output_memory.to_str_messages(content_key="parsed_output_list")) # 测试 phase -phase_name = "toolReactPhase" -phase_name = "codeReactPhase" +# phase_name = "toolReactPhase" +# phase_name = "codeReactPhase" # phase_name = "chatPhase" # phase = BasePhase(phase_name, @@ -133,34 +164,34 @@ phase_name = "codeReactPhase" # input_query="确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型,并选择合适的数值列画出折线图") -phase_name = "baseTaskPhase" -phase = BasePhase(phase_name, - task = None, - phase_config = PHASE_CONFIGS, - chain_config = CHAIN_CONFIGS, - role_config = AGETN_CONFIGS, - do_summary=False, - do_code_retrieval=False, - do_doc_retrieval=True, - do_search=False, - ) +# phase_name = "baseTaskPhase" +# phase = BasePhase(phase_name, +# task = None, +# phase_config = PHASE_CONFIGS, +# chain_config = CHAIN_CONFIGS, +# role_config = AGETN_CONFIGS, +# do_summary=False, +# do_code_retrieval=False, +# do_doc_retrieval=True, +# do_search=False, +# ) -query_content = "查询贵州茅台的股票代码,并查询截止到当前日期(2023年11月8日)的最近10天的每日时序数据,然后对时序数据画出折线图并分析" -query_content = "判断下127.0.0.1这个服务器的在10点的监控数据,是否存在异常" -query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +# query_content = "查询贵州茅台的股票代码,并查询截止到当前日期(2023年11月8日)的最近10天的每日时序数据,然后对时序数据画出折线图并分析" +# query_content = "判断下127.0.0.1这个服务器的在10点的监控数据,是否存在异常" +# query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" -query = Message(role_name="user", role_type="human", role_content=query_content, input_query=query_content, origin_query=query_content, tools=tools) +# query = Message(role_name="user", role_type="human", role_content=query_content, input_query=query_content, origin_query=query_content, tools=tools) -query = Message(role_name="human", role_type="human", input_query=query_content, role_content=query_content, origin_query=query_content) +# query = Message(role_name="human", role_type="human", input_query=query_content, role_content=query_content, origin_query=query_content) -output_message = phase.step(query) +# output_message = phase.step(query) # print(phase.get_chains_memory(content_key="step_content")) # print(phase.get_chains_memory_str(content_key="step_content")) # print(output_message.to_tuple_message(return_all=True)) -from dev_opsgpt.tools import DDGSTool, CodeRetrieval +# from coagent.tools import DDGSTool, CodeRetrieval # print(DDGSTool.run("langchain是什么", 3)) # print(CodeRetrieval.run("dsadsadsa", query.role_content, code_limit=3, history_node_list=[])) diff --git a/tests/db_create_test.py b/tests/db_create_test.py index 5236396..2ad9a05 100644 --- a/tests/db_create_test.py +++ b/tests/db_create_test.py @@ -7,6 +7,6 @@ src_dir = os.path.join( sys.path.append(src_dir) -from dev_opsgpt.orm import create_tables +from coagent.orm import create_tables create_tables() \ No newline at end of file diff --git a/tests/sandbox_test.py b/tests/sandbox_test.py index 93584ea..6e12bc9 100644 --- a/tests/sandbox_test.py +++ b/tests/sandbox_test.py @@ -13,45 +13,59 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.service.sdfile_api import sd_upload_file -from dev_opsgpt.sandbox.pycodebox import PyCodeBox -from pathlib import Path - - -# python = Path(sys.executable).absolute() -# print(Path(python).as_posix()) -# print(python) -# print(sys.executable) - +from configs.model_config import JUPYTER_WORK_PATH +from coagent.sandbox.pycodebox import PyCodeBox import requests -# # 设置Jupyter Notebook服务器的URL -# url = 'http://172.25.0.3:5050' # 或者是你自己的Jupyter服务器的URL - -# # 发送GET请求来获取Jupyter Notebook的登录页面 -# response = requests.get(url) - -# # 检查响应状态码 -# if response.status_code == 200: -# # 打印响应内容 -# print('connect success') -# else: -# print('connect fail') # import subprocess -# jupyter = subprocess.Popen( -# [ -# "juptyer", "notebnook", -# "--NotebookApp.token=mytoken", -# f"--port=4949", -# "--no-browser", -# "--ServerApp.disable_check_xsrf=True", -# ], -# stderr=subprocess.PIPE, -# stdin=subprocess.PIPE, -# stdout=subprocess.PIPE, -# ) +# import time +# from loguru import logger + +# class PyCodeBox: + +# enter_status: bool = False + +# def __init__( +# self, +# remote_url: str = "", +# remote_ip: str = "localhost", +# remote_port: str = "5050", +# token: str = "mytoken", +# do_code_exe: bool = False, +# do_remote: bool = False, +# do_check_net: bool = True, +# ): +# self.enter_status = True +# self.do_check_net = do_check_net +# self.start() + + +# def start(self, ): +# logger.debug("start!") +# jupyter = subprocess.Popen( +# [ +# "jupyter", "notebook", +# "--NotebookApp.token=mytoken", +# "--port=5050", +# "--no-browser", +# "--ServerApp.disable_check_xsrf=True", +# ], +# stderr=subprocess.PIPE, +# # stdin=subprocess.PIPE, +# stdout=subprocess.PIPE, +# ) +# logger.debug("start over") + +# pycodebox = PyCodeBox() + +# logger.debug("start to sleep") +# idx = 0 +# while True: +# time.sleep(5) +# logger.debug(f"sleep {idx}") +# idx+=1 # # 测试1 # import time, psutil @@ -61,56 +75,28 @@ pycodebox = PyCodeBox(remote_url="http://localhost:5050", remote_ip="http://localhost", remote_port="5050", token="mytoken", + jupyter_work_path=JUPYTER_WORK_PATH, do_code_exe=True, - do_remote=False) - -# pycodebox.list_files() -# file = "./torch_test.py" -# upload_file = st_load_file(file, filename="torch_test.py") - -# file_content = upload_file.read() # 读取上传文件的内容 -# print(upload_file, file_content) -# pycodebox.upload("torch_test.py", upload_file) - -# asyncio.run(pycodebox.alist_files()) + do_remote=False, + use_stop=True, + do_check_net=False + ) -reuslt = pycodebox.chat("```'hello world!'```", do_code_exe=True) +reuslt = pycodebox.chat("```import os\nos.getcwd()```", do_code_exe=True) print(reuslt) -# reuslt = pycodebox.chat("print('hello world!')", do_code_exe=False) -# print(reuslt) - -# for process in psutil.process_iter(["pid", "name", "cmdline"]): -# # 检查进程名是否包含"jupyter" -# if 'port=5050' in str(process.info["cmdline"]).lower() and \ -# "jupyter" in process.info['name'].lower(): - -# logger.warning(f'port=5050, {process.info}') -# # 关闭进程 -# process.terminate() - +reuslt = pycodebox.chat("print('hello world!')", do_code_exe=False) +print(reuslt) + # 测试2 # with PyCodeBox(remote_url="http://localhost:5050", # remote_ip="http://localhost", # remote_port="5050", # token="mytoken", # do_code_exe=True, -# do_remote=True) as codebox: +# do_remote=False) as codebox: -# result = codebox.run("print('hello world!')") -# print(result) - - - -# with PyCodeBox( -# remote_ip="http://localhost", -# remote_port="5555", -# token="mytoken", -# do_code_exe=True, -# do_remote=False -# ) as codebox: - -# result = codebox.run("print('hello world!')") - # print(result) +# result = codebox.run("'hello world!'") +# print(result) \ No newline at end of file diff --git a/tests/search_test.py b/tests/search_test.py index b89c707..f5fbc3d 100644 --- a/tests/search_test.py +++ b/tests/search_test.py @@ -1,5 +1,6 @@ from duckduckgo_search import DDGS -with DDGS(proxies="socks5://127.0.0.1:13659", timeout=20) as ddgs: +with DDGS(proxies="socks5h://127.0.0.1:13659", timeout=20) as ddgs: + ddgs._session.headers["Referer"] = "" for r in ddgs.text("马克龙、冯德莱恩访华"): print(r) diff --git a/tests/table_test.py b/tests/table_test.py index 2ca8d84..fc02b91 100644 --- a/tests/table_test.py +++ b/tests/table_test.py @@ -5,6 +5,6 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.orm import create_tables +from coagent.orm import create_tables create_tables() \ No newline at end of file diff --git a/tests/text_splitter_test.py b/tests/text_splitter_test.py index e310834..571a47a 100644 --- a/tests/text_splitter_test.py +++ b/tests/text_splitter_test.py @@ -5,7 +5,7 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.text_splitter import LCTextSplitter +from coagent.text_splitter import LCTextSplitter filepath = "" lc_textSplitter = LCTextSplitter(filepath) diff --git a/tests/tool_test.py b/tests/tool_test.py index 6ec19ad..3b2a72c 100644 --- a/tests/tool_test.py +++ b/tests/tool_test.py @@ -15,7 +15,7 @@ src_dir = os.path.join( ) sys.path.append(src_dir) -from dev_opsgpt.tools import ( +from coagent.tools import ( WeatherInfo, WorldTimeGetTimezoneByArea, Multiplier, KSigmaDetector, toLangchainTools, get_tool_schema, TOOL_DICT, TOOL_SETS @@ -86,7 +86,7 @@ agent = initialize_agent( # from dev_opsgpt.utils.common_utils import read_json_file # stock_name = read_json_file("../sources/stock.json") -from dev_opsgpt.tools.ocr_tool import BaiduOcrTool +from coagent.tools.ocr_tool import BaiduOcrTool print(BaiduOcrTool.run("D:/chromeDownloads/devopschat-bot/ocr_figure.png"))