diff --git a/README.md b/README.md index 7568217..907cda4 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,11 @@

-本项目是一个开源的 AI 智能助手,专为软件开发的全生命周期而设计,涵盖设计、编码、测试、部署和运维等阶段。通过知识检索、工具使用和沙箱执行,Codefuse-ChatBot 能解答您开发过程中的各种专业问题、问答操作周边独立分散平台。 +本项目是一个开源的 AI 智能助手,专为软件开发的全生命周期而设计,涵盖设计、编码、测试、部署和运维等阶段。通过知识检索、代码检索,工具使用和沙箱执行,Codefuse-ChatBot 能解答您开发过程中的各种专业问题、问答操作周边独立分散平台。 ## 🔔 更新 +- [2023.11.15] 增加基于本地代码库的问答增强模式 - [2023.09.15] 本地/隔离环境的沙盒功能开放,基于爬虫实现指定url知识检索 ## 📜 目录 @@ -29,6 +30,7 @@ 💡 本项目旨在通过检索增强生成(Retrieval Augmented Generation,RAG)、工具学习(Tool Learning)和沙盒环境来构建软件开发全生命周期的AI智能助手,涵盖设计、编码、测试、部署和运维等阶段。 逐渐从各处资料查询、独立分散平台操作的传统开发运维模式转变到大模型问答的智能化开发运维模式,改变人们的开发运维习惯。 - 📚 知识库管理:DevOps专业高质量知识库 + 企业级知识库自助构建 + 对话实现快速检索开源/私有技术文档 +- ⌨️ 代码知识库管理:支持本地代码库导入和代码结构解析 + 对话实现快速检索本地代码 - 🐳 隔离沙盒环境:实现代码的快速编译执行测试 - 🔄 React范式:支撑代码的自我迭代、自动执行 - 🛠️ Prompt管理:实现各种开发、运维任务的prompt管理 @@ -46,10 +48,11 @@ ## 🎥 演示视频 -为了帮助您更直观地了解 Codefuse-ChatBot 的功能和使用方法,我们录制了一个演示视频。您可以通过观看此视频,快速了解本项目的主要特性和操作流程。 +为了帮助您更直观地了解 Codefuse-ChatBot 的功能和使用方法,我们录制了一系列演示视频。您可以通过观看这些视频,快速了解本项目的主要特性和操作流程。 -[演示视频](https://www.youtube.com/watch?v=UGJdTGaVnNY&t=2s&ab_channel=HaotianZhu) +- 知识库导入和问答:[演示视频](https://www.youtube.com/watch?v=UGJdTGaVnNY&t=2s&ab_channel=HaotianZhu) +- 本地代码库导入和问答:[演示视频](https://www.youtube.com/watch?v=ex5sbwGs3Kg) ## 🧭 技术路线 diff --git a/dev_opsgpt/__init__.py b/dev_opsgpt/__init__.py new file mode 100644 index 0000000..67c87c2 --- /dev/null +++ b/dev_opsgpt/__init__.py @@ -0,0 +1,7 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: __init__.py.py +@time: 2023/11/9 下午4:01 +@desc: +''' \ No newline at end of file diff --git a/dev_opsgpt/chat/agent_chat.py b/dev_opsgpt/chat/agent_chat.py index 3fe68e2..2ec4261 100644 --- a/dev_opsgpt/chat/agent_chat.py +++ b/dev_opsgpt/chat/agent_chat.py @@ -74,6 +74,13 @@ class AgentChat: # update configs phase_configs, chain_configs, agent_configs = self.update_configs( custom_phase_configs, custom_chain_configs, custom_role_configs) + + logger.info('phase_configs={}'.format(phase_configs)) + logger.info('chain_configs={}'.format(chain_configs)) + logger.info('agent_configs={}'.format(agent_configs)) + logger.info('phase_name') + logger.info('chain_name') + # choose tools tools = toLangchainTools([TOOL_DICT[i] for i in choose_tools if i in TOOL_DICT]) input_message = Message( diff --git a/dev_opsgpt/chat/code_chat.py b/dev_opsgpt/chat/code_chat.py index fea8e7a..a7df657 100644 --- a/dev_opsgpt/chat/code_chat.py +++ b/dev_opsgpt/chat/code_chat.py @@ -71,9 +71,12 @@ class CodeChat(Chat): node_name, node_type, node_score = node_info[0], node_info[1], node_info[2] source_nodes.append(f'{inum + 1}. 节点名为 {node_name}, 节点类型为 `{node_type}`, 节点得分为 `{node_score}`') + logger.info('history={}'.format(history)) + logger.info('message={}'.format([i.to_msg_tuple() for i in history] + [("human", CODE_PROMPT_TEMPLATE)])) chat_prompt = ChatPromptTemplate.from_messages( [i.to_msg_tuple() for i in history] + [("human", CODE_PROMPT_TEMPLATE)] ) + logger.info('chat_prompt={}'.format(chat_prompt)) chain = LLMChain(prompt=chat_prompt, llm=model) result = {"answer": "", "codes": source_nodes} return chain, context, result @@ -89,7 +92,7 @@ class CodeChat(Chat): code_limit: int = Body(1, examples=['1']), stream: bool = Body(False, description="流式输出"), local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), - request: Request = Body(None), + request: Request = None, **kargs ): self.engine_name = engine_name if isinstance(engine_name, str) else engine_name.default diff --git a/dev_opsgpt/connector/configs/agent_config.py b/dev_opsgpt/connector/configs/agent_config.py index 42432a1..dc52852 100644 --- a/dev_opsgpt/connector/configs/agent_config.py +++ b/dev_opsgpt/connector/configs/agent_config.py @@ -200,7 +200,7 @@ $JSON_BLOB ``` """ -CODE_QA_PROMPT = """【指令】根据已知信息来回答问""" +CODE_QA_PROMPT = """【指令】根据已知信息来回答问题""" AGETN_CONFIGS = { diff --git a/dev_opsgpt/service/api.py b/dev_opsgpt/service/api.py index f9e5b75..0f7194d 100644 --- a/dev_opsgpt/service/api.py +++ b/dev_opsgpt/service/api.py @@ -87,7 +87,6 @@ def create_app(): tags=["Chat"], summary="与代码库对话")(codeChat.chat) - # Tag: Knowledge Base Management app.get("/knowledge_base/list_knowledge_bases", tags=["Knowledge Base Management"], diff --git a/dev_opsgpt/utils/__init__.py b/dev_opsgpt/utils/__init__.py index d0c6e1d..c00d663 100644 --- a/dev_opsgpt/utils/__init__.py +++ b/dev_opsgpt/utils/__init__.py @@ -1,6 +1,7 @@ from .server_utils import BaseResponse, ListResponse from .common_utils import func_timer +from .postprocess import replace_lt_gt __all__ = [ - "BaseResponse", "ListResponse", "func_timer" + "BaseResponse", "ListResponse", "func_timer", 'replace_lt_gt' ] \ No newline at end of file diff --git a/dev_opsgpt/utils/postprocess.py b/dev_opsgpt/utils/postprocess.py new file mode 100644 index 0000000..9929aaa --- /dev/null +++ b/dev_opsgpt/utils/postprocess.py @@ -0,0 +1,12 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: postprocess.py +@time: 2023/11/9 下午4:01 +@desc: +''' + +def replace_lt_gt(text: str): + text = text.replace('<', '<') + text = text.replace('>', '>') + return text \ No newline at end of file diff --git a/dev_opsgpt/webui/code.py b/dev_opsgpt/webui/code.py index 93b2e04..0e6a6c4 100644 --- a/dev_opsgpt/webui/code.py +++ b/dev_opsgpt/webui/code.py @@ -135,6 +135,6 @@ def code_page(api: ApiRequest): ): ret = api.delete_code_base(cb, no_remote_api=True) - st.toast(ret.get("msg", " ")) - time.sleep(1) + st.toast(ret.get("msg", "删除成功")) + time.sleep(0.5) st.experimental_rerun() diff --git a/dev_opsgpt/webui/dialogue.py b/dev_opsgpt/webui/dialogue.py index bb45af7..6258ecf 100644 --- a/dev_opsgpt/webui/dialogue.py +++ b/dev_opsgpt/webui/dialogue.py @@ -248,8 +248,14 @@ def dialogue_page(api: ApiRequest): st.error(error_msg) break text += t["answer"] + + text = replace_lt_gt(text) + chat_box.update_msg(text) logger.debug(f"text: {text}") + + text = replace_lt_gt(text) + chat_box.update_msg(text, streaming=False) # 更新最终的字符串,去除光标 # 判断是否存在代码, 并提高编辑功能,执行功能 code_text = api.codebox.decode_code_from_text(text) @@ -388,8 +394,12 @@ def dialogue_page(api: ApiRequest): st.error(error_msg) text += d["answer"] if idx_count % 10 == 0: + text = replace_lt_gt(text) chat_box.update_msg(text, element_index=0) + # postprocess + text = replace_lt_gt(text) chat_box.update_msg(text, element_index=0, streaming=False) # 更新最终的字符串,去除光标 + logger.debug('text={}'.format(text)) chat_box.update_msg("\n".join(d["codes"]), element_index=1, streaming=False, state="complete") # session state update @@ -467,6 +477,8 @@ def dialogue_page(api: ApiRequest): ): chat_box.reset_history() GLOBAL_EXE_CODE_TEXT = "" + if 'history_node_list' in st.session_state: + st.session_state['history_node_list'] = [] st.experimental_rerun() export_btn.download_button( diff --git a/examples/python.exe.stackdump b/examples/python.exe.stackdump deleted file mode 100644 index 8b6d724..0000000 --- a/examples/python.exe.stackdump +++ /dev/null @@ -1,35 +0,0 @@ -Stack trace: -Frame Function Args -065CA2341B0 00180064365 (001802A267B, 00180266FD1, 00180310720, 065CA234690) -065CA2346E0 001800499D2 (00000000000, 00000000000, 00000000000, 00000000000) -065CA2356F0 00180049A11 (00000000032, 000000018A1, 00180310720, 001803512E5) -065CA235720 0018017C34A (00000000000, 00000000000, 00000000000, 00000000000) -065CA2357C0 00180107A01 (065CA2369E8, 000000018A1, 00180310720, 000FFFFFFFF) -065CA236A50 0018016142F (7FFD86AEB0EB, 001803512E5, 065CA236A88, 00000000000) -065CA236BF0 00180142EBB (7FFD86AEB0EB, 001803512E5, 065CA236A88, 00000000000) -065CA236BF0 004B39318AE (24B346C45C0, 065CA236C98, 24B00A54270, 00000000000) -065CA236CD0 004B3936115 (00000000000, 24B36D861A0, FFC1F2061D5F707D, 065CA236DB0) -065CA236D80 7FFDB39F4541 (0000000000A, 065CA236FD0, 7FFDB39F4262, 00000000000) -065CA236DB0 7FFDB39F4332 (065CA237010, 00000000000, 24B594D6870, 00000000000) -065CA237010 7FFDB39F4212 (7FFD86AE24F7, 7FFD8696D680, 24B77D7BD60, 065CA236FB0) -065CA237010 7FFD8695CABF (004B39319D0, 065CA236FC0, 00000001101, 24B00000000) -065CA237010 7FFD8695D629 (24B00A4D6C0, 00000000000, 00000000000, 06500001101) -24B00A4DEE0 7FFD869577FD (24B00A573C0, 00000000000, 00000000000, 24B00A4FB20) -00000000000 7FFD86AE0256 (065CA237299, 24B00A4FB10, 24B2688BDF8, 00000000003) -065CA237299 7FFD86BC8D88 (24B594D6870, 065CA237299, 00000000000, 24B00A47F70) -065CA237299 7FFD86BC2DF8 (00000000000, 24B00A505B0, 00000000043, 24B00A47F70) -00000000001 7FFD86BC798A (00000000002, 24B00A4E898, 7FFD86B49A3A, 00000000001) -00000000000 7FFD86AE0AAF (065CA237599, 24B008E7BB8, 7FFD86B2B9DA, 24B008E7BA8) -065CA237599 7FFD86BC03F6 (24B75AE03C8, 24B752735CA, 000000000A0, 00000000062) -065CA237599 7FFD86BC8D88 (24B594D6870, 065CA237599, 24B75273340, 7FFD86E45B00) -065CA237599 7FFD86BC517F (00000000000, 24B00A50D40, 00000000040, 24B34AC8A08) -00000000000 7FFD86BC798A (00000000000, 24B00A4F180, 00000000000, 00000000000) -24B00A50D40 7FFD86BC17F4 (7FFD86D82364, 24B00A50D40, 00000000010, 24B00A20B30) -24B00A50D40 7FFD86BBCA4F (24B34AD0810, 24B34AD5478, 24B594D6870, 24B594D6870) -00000000000 7FFD86B26BF5 (24B34AD0810, 24B00A4F240, 7FFDCD9B5BA1, 065CA237840) -00000000000 7FFD86AE03A6 (24B00A52280, 24B00A4F240, 24B00A52298, 24B345B7B32) -24B00A4F240 7FFD86BC8EF0 (24B00A52280, 065CA237949, 24B34AD5470, 00000000038) -065CA237949 7FFD86BC52D8 (00000000000, 24B34AD99D0, 0000000004F, 00000000000) -00000000003 7FFD86BC798A (00000000001, 00000000000, 24B516F49B0, 00000000003) -00000000000 7FFD86AE0AAF (065CA237C49, 24B2687E5C8, 7FFD86B28DEE, 24B2687E5A8) -End of stack trace (more stack frames may be present) diff --git a/examples/start.py b/examples/start.py index 8b3ab56..fbe5fd9 100644 --- a/examples/start.py +++ b/examples/start.py @@ -111,8 +111,10 @@ def start_sandbox_service(): if check_docker(client, SANDBOX_CONTRAINER_NAME, do_stop=True, ): container = start_docker(client, script_shs, ports, SANDBOX_IMAGE_NAME, SANDBOX_CONTRAINER_NAME, mounts=mounts, network=network_name) # 判断notebook是否启动 + time.sleep(5) retry_nums = 3 while retry_nums>0: + logger.info(f"http://localhost:{SANDBOX_SERVER['port']}") response = requests.get(f"http://localhost:{SANDBOX_SERVER['port']}", timeout=270) if response.status_code == 200: logger.info("container & notebook init success") @@ -180,7 +182,6 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): webui_sh = "streamlit run webui.py" if USE_TTY else "streamlit run webui.py" # if not NO_REMOTE_API and check_process("service/api.py"): - logger.info('check 1') subprocess.Popen(api_sh, shell=True) # if USE_FASTCHAT and check_process("service/llm_api.py"): diff --git a/requirements.txt b/requirements.txt index 36bd661..8c5213e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ langchain==0.0.266 -openai +openai==0.28.1 sentence_transformers fschat==0.2.24 transformers>=4.31.0 diff --git a/sources/docs/zhuanlan_zhihu_com_p_80963305_text.jsonl b/sources/docs/zhuanlan_zhihu_com_p_80963305_text.jsonl deleted file mode 100644 index 1d63956..0000000 --- a/sources/docs/zhuanlan_zhihu_com_p_80963305_text.jsonl +++ /dev/null @@ -1 +0,0 @@ -{"url": "https://zhuanlan.zhihu.com/p/80963305", "host_url": "https://zhuanlan.zhihu.com", "title": "【工具类】PyCharm+Anaconda+jupyter notebook +pip环境配置 - 知乎", "all_text": "\n【工具类】PyCharm+Anaconda+jupyter notebook +pip环境配置 - 知乎切换模式写文章登录/注册【工具类】PyCharm+Anaconda+jupyter notebook +pip环境配置Joe.Zhao14 人赞同了该文章Pycharm是一个很好的python的IDE,Anaconda是一个环境管理工具,可以针对不同工作配置不同的环境,如何在Pycharm中调用Anaconda中创建的环境Anaconda环境配置Anaconda 解决了官方 Python 的两大痛点第一:提供了包管理功能,解决安装第三方包经常失败第二:提供环境管理的功能,功能类似 Virtualenv,解决了多版本Python并存、切换的问题。查看Anaconda中所有的Python环境,Window环境下Anaconda Prompt中输入以下命令,其中前面有个‘*’的代表当前环境\n```code\nconda info --env\n\n# conda environments:\n#\nbase * D:\\Anaconda3\ntf D:\\Anaconda3\\envs\\tf\n```\n创建新的Python环境\n```code\nconda create --name python35 python=3.5 #代表创建一个python3.5的环境,我们把它命名为python35\n```\n激活进入创建的环境\n```code\nconda activate python35\n```\n在当前环境中安装package,可以使用pip,还可以用conda\n```code\npip install numpy\nconda install numpy\n```\n退出当前环境,回到base环境\n```code\nconda deactivate\n```\n删除创建的环境,conda创建的环境会在安装目录Anaconda3\\envs\\下面,每一个环境对应一个文件夹,当删除环境的时候,响应的文件夹也会被删除掉\n```code\nconda env remove --name python35 --all\nconda remove --name myenv --all\n```\nconda源头\n```code\nconda config --show channels\nchannels:\n- https://pypi.doubanio.com/simple/\n- defaults\n```\n添加新源\n```code\nconda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/\n```\n删除源\n```code\nconda config --remove channels https://pypi.doubanio.com/simple/\n```\n查看安装package\n```code\nconda list\n\n```\nPycharm 使用Anaconda创建的环境pycharm工程目录中打开/file/settings/Project Interpreter在Project Interpreter中打开Add,左侧边栏目选择Conda Environment,右侧选择Existing environment在文件路径中选择Anaconda安装目录下面的envs目录,下面是该系统安装的所有anaconda环境,进入文件夹,选择python解释器这就就把Pycharm下使用Anconda环境的配置完成了。pip 环境配置conda 环境下也可以用pip来安装包pip安装\n```code\npip install 安装包名\n[...]\nSuccessfully installed SomePackage #安装成功\n```\npip 安装指定源\n```code\npip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple\n```\npip查看是否已安装\n```code\npip show --files 安装包名\n\nName:SomePackage # 包名\nVersion:1.0 # 版本号\nLocation:/my/env/lib/pythonx.x/site-packages # 安装位置\nFiles: # 包含文件等等\n../somepackage/__init__.py\n```\npip检查哪些包需要更新\n```code\npip list --outdated\n```\npip升级包\n```code\npip install --upgrade 要升级的包名\n```\npip卸载包\n```code\npip uninstall 要卸载的包名\n```\npip参数解释\n```code\npip --help\n```\nJupyter notebook使用在利用anaconda创建了tensorflow,pytorch等儿女environment后,想利用jupyter notebook,发现jupyer notebook中只有系统的python3环境,如何把conda创建的环境添加进jupyter notebook中呢,终于解决了这个问题了1. 安装ipykernel\n```code\nconda install ipykernel\n```\n2. 将环境写入notebook的kernel中\n```code\npython -m ipykernel install --user --name your_env_name --display-name your_env_name\n\n//把conda environment pytorch_0.4 add to jupyter notebook kernel display as pytorch_0.4\npython -m ipykernel install --user --name pytorch_0.4 --display-name pytorch_0.4\n```\n3. 打开notebook\n```code\njupyter notebook\n```\n4. magic commands\n```code\n!git clone https://github.com/ultralytics/yolov5\n%ls\n%cd yolov5\n%pip install -qr requirements.txt\n```\n还有一些实用的魔术命令\n```code\n%magic——用来显示所有魔术命令的详细文档\n%time和%timeit——用来测试代码执行时间\n```\n参考文档编辑于 2023-05-21 20:41・IP 属地浙江PyCharmAnacondapip3​赞同 14​​2 条评论​分享​喜欢​收藏​申请转载​"} diff --git a/tests/text_spliter_test.py b/tests/text_spliter_test.py deleted file mode 100644 index 47ce854..0000000 --- a/tests/text_spliter_test.py +++ /dev/null @@ -1,16 +0,0 @@ - -import os, sys - - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - ) -sys.path.append(src_dir) - -from dev_opsgpt.text_splitter import LCTextSplitter - -filepath = "D:/project/gitlab/llm/DevOpsGpt/knowledge_base/SAMPLES/content/test.txt" -lcTextSplitter = LCTextSplitter(filepath) -docs = lcTextSplitter.file2text() - -print(docs[0]) \ No newline at end of file