diff --git a/bbit_ai/Dockerfile_ai_lab b/bbit_ai/Dockerfile_ai_lab index 4dcf2ca..2bc12e9 100644 --- a/bbit_ai/Dockerfile_ai_lab +++ b/bbit_ai/Dockerfile_ai_lab @@ -1,10 +1,10 @@ -# 使用官方 Python 镜像 -FROM python:3.10-slim +FROM ubuntu:22.04 WORKDIR /app RUN apt-get update && \ apt-get install -y --no-install-recommends \ + ca-certificates \ libpq5 \ unixodbc \ curl \ @@ -20,31 +20,4 @@ RUN apt-get update && \ ACCEPT_EULA=Y apt-get install -y msodbcsql18 && \ rm -rf /var/lib/apt/lists/* -COPY app/requirements.txt . -# 安装 Python 依赖 -RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt -RUN python -m pip uninstall -y opencv-python -RUN python -m pip install opencv-python-headless - - - -# 复制并解压 JRE -COPY docker/OpenJDK17U-jre_x64_linux_hotspot_17.0.16_8.tar.gz /opt/ -RUN tar -xzf /opt/OpenJDK17U-jre_x64_linux_hotspot_17.0.16_8.tar.gz -C /opt/ && \ - rm /opt/OpenJDK17U-jre_x64_linux_hotspot_17.0.16_8.tar.gz - -# 配置 Java 环境 -ENV JAVA_HOME=/opt/jdk-17.0.16+8-jre -ENV PATH="$JAVA_HOME/bin:$PATH" - -# 复制项目代码 -COPY app/ . -# 复制 pyzxing 的 jar 文件到默认路径 -COPY docker/javase-3.4.1-SNAPSHOT-jar-with-dependencies.jar /root/.local/pyzxing/javase-3.4.1-SNAPSHOT-jar-with-dependencies.jar - -EXPOSE 13011 - -# 启动命令(使用 uvicorn 启动 FastAPI) -CMD ["python", "app.py"] - - +COPY app/ /app diff --git a/bbit_ai/app/agent/dataAgent.py b/bbit_ai/app/agent/dataAgent.py index eab1eb9..14d9543 100644 --- a/bbit_ai/app/agent/dataAgent.py +++ b/bbit_ai/app/agent/dataAgent.py @@ -1,41 +1,34 @@ - -from langchain.prompts import PromptTemplate -from config.llm import llm -from config.ssDb import ssDBLC -from typing import Annotated -from typing_extensions import TypedDict -from langgraph.graph import StateGraph, START, END -from langgraph.graph.message import add_messages -from langchain_community.agent_toolkits import create_sql_agent -from langchain.prompts import PromptTemplate -from config.llm import llm -from config.ssDb import ssDBLC -from typing import Annotated -from langgraph.graph.message import add_messages import os -from langchain_tavily import TavilySearch -from langgraph.prebuilt import ToolNode, tools_condition -from llm.chatLLM import get_chat_response from typing import TypedDict + +from langchain_community.agent_toolkits import create_sql_agent +from langchain_core.prompts import PromptTemplate +from langchain_tavily import TavilySearch +from langgraph.graph import START from langgraph.graph import StateGraph, END + +from config.llm import llm +from config.ssDb import ssDBLC +from llm.chatLLM import get_chat_response from llm.summarizeLLM import getSummary # -------- 定义状态 -------- class State(TypedDict): - userInput: str # 用户输入 - source: str # 选择的数据来源:web 或 db 或 chat - infomation: str # 查询到的内容 - aiRole: str # AI 角色 - history: str # 聊天历史 - reply: str # 最终回复 + userInput: str # 用户输入 + source: str # 选择的数据来源:web 或 db 或 chat + infomation: str # 查询到的内容 + aiRole: str # AI 角色 + history: str # 聊天历史 + reply: str # 最终回复 + # -------- 定义节点 -------- # ------------------------------------------------------------------------ 路径选择 -------- pathSelectPrompt = PromptTemplate( input_variables=["aiRole", "history", "userStr", "infomation"], - template = """ + template=""" 你是主干信息科技有限公司的业务员,是一家蚕桑服务公司,现在需要根据用户输入来判断应该使用哪种方式来回答用户的问题。 你有三种选择: 1. 如果用户的问题涉及最新的信息,比如新闻、事件、天气等涉及时间的内容时,请选择 "web @@ -45,19 +38,26 @@ pathSelectPrompt = PromptTemplate( 用户最新输入: {userStr} 请做出你的选择: - """ + """, ) pathSelectChain = pathSelectPrompt | llm + def decide_source(state: State, max_retry=3): print("根据用户输入选择数据来源,用户输入:", state["userInput"]) """根据用户输入选择数据来源""" for _ in range(max_retry): - choice = pathSelectChain.invoke({ - "aiRole": state["aiRole"], - "history": state["history"], - "userStr": state["userInput"], - }).content.strip().lower() + choice = ( + pathSelectChain.invoke( + { + "aiRole": state["aiRole"], + "history": state["history"], + "userStr": state["userInput"], + } + ) + .content.strip() + .lower() + ) if choice in ["web", "db", "chat"]: state["source"] = choice break @@ -72,36 +72,45 @@ def decide_source(state: State, max_retry=3): os.environ["TAVILY_API_KEY"] = "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" tool = TavilySearch(max_results=2) + def fetch_web(state: State): result = tool.invoke(state["userInput"]) - state["infomation"] = result.get("content") or result + state["infomation"] = result.get("content") or result print("调用了联网工具,结果是:", state["infomation"]) return state + # ------------------------------------------------------------------------ 数据库查询 -------- -agent = create_sql_agent( - llm=llm, - db=ssDBLC, - agent_type="tool-calling", - verbose=True -) +agent = create_sql_agent(llm=llm, db=ssDBLC, agent_type="tool-calling", verbose=True) + + def fetch_db(state: State): state["infomation"] = agent.invoke({"input": state["userInput"]})["output"] print("调用了数据库工具,结果是:", state["infomation"]) return state + # ------------------------------------------------------------------------ 整理结果 -------- def summarize_ai(state: State): """AI 总结输出""" - state["reply"] = getSummary(aiRole=state["aiRole"], history=state["history"], userInput= state["userInput"], infomation= state["infomation"]) + state["reply"] = getSummary( + aiRole=state["aiRole"], + history=state["history"], + userInput=state["userInput"], + infomation=state["infomation"], + ) return state + # ------------------------------------------------------------------------ 普通聊天 -------- def chat(state: State): - state["reply"] = get_chat_response(aiRole=state["aiRole"],history=state["history"], userInput= state["userInput"]).content + state["reply"] = get_chat_response( + aiRole=state["aiRole"], history=state["history"], userInput=state["userInput"] + ).content print("直接回复") return state + # ------------------------------------------------------------------------ 构建有向图 -------- workflow = StateGraph(State) workflow.add_node("decide", decide_source) @@ -119,21 +128,20 @@ workflow.add_edge("fetch_db", "summarize") workflow.add_conditional_edges( "decide", lambda state: state["source"], # 返回 state["source"] 的值 - { - "web": "fetch_web", - "chat": "chat", - "db": "fetch_db" - } + {"web": "fetch_web", "chat": "chat", "db": "fetch_db"}, ) workflow.add_edge("summarize", END) workflow.add_edge("chat", END) graph = workflow.compile() + # 执行函数 -def get_graph_output(aiRole:str,history: str, userInput: str) -> str: - final_state = graph.invoke({ - "aiRole":aiRole, - "history": history, - "userInput": userInput, - }) - return final_state["reply"] \ No newline at end of file +def get_graph_output(aiRole: str, history: str, userInput: str) -> str: + final_state = graph.invoke( + { + "aiRole": aiRole, + "history": history, + "userInput": userInput, + } + ) + return final_state["reply"] diff --git a/bbit_ai/app/agent/dbAgent.py b/bbit_ai/app/agent/dbAgent.py index 8310e1c..fc0d490 100644 --- a/bbit_ai/app/agent/dbAgent.py +++ b/bbit_ai/app/agent/dbAgent.py @@ -1,31 +1,12 @@ -from typing import Literal -from langchain_core.messages import AIMessage -from langchain_core.runnables import RunnableConfig -from langgraph.graph import END, START, MessagesState, StateGraph -from langgraph.prebuilt import ToolNode -from langchain_community.agent_toolkits import SQLDatabaseToolkit -from config.llm import llm, llmThink -from langgraph.graph import StateGraph, END -from langchain.prompts import PromptTemplate -from config.llm import llm -from typing import Annotated -from typing_extensions import TypedDict -from langgraph.graph import StateGraph, START, END -from langgraph.graph.message import add_messages -from langchain_community.agent_toolkits import create_sql_agent -from langchain.prompts import PromptTemplate -from config.llm import llm -from typing import Annotated -from langgraph.graph.message import add_messages -import os -from langchain_tavily import TavilySearch -from langgraph.prebuilt import ToolNode, tools_condition -from llm.chatLLM import get_chat_response from typing import TypedDict + +from langchain_core.prompts import PromptTemplate from langgraph.graph import StateGraph, END -from llm.summarizeLLM import getSummary + import db.postgres as pgdb import db.sqlserver as sqlserver +from config.llm import llm +from config.llm import llmThink # -------- 定义状态 -------- @@ -69,7 +50,7 @@ gen_sql_prompt = PromptTemplate( 请直接输出完整可执行的 SQL 语句,不要任何其他文字或格式化,例如反引号或 ```sql。 -""" +""", ) sqlChain = gen_sql_prompt | llm @@ -89,33 +70,34 @@ fix_prompt = PromptTemplate( # 输出要求 只返回修正后的 SQL 语句,不包含任何额外的解释或说明。 -""" +""", ) fixSQLChain = fix_prompt | llm def sql(state: State): if state["isFirstGenSQL"]: - state['sql'] = sql_1(state) + state["sql"] = sql_1(state) else: - state['sql'] = sql_2(state) + state["sql"] = sql_2(state) for attempt in range(2): try: # 执行 SQL - result = sqlserver.executeSQL(state['sql']) - state['sql_result'] = result + result = sqlserver.executeSQL(state["sql"]) + state["sql_result"] = result # print("SQL 执行成功,结果:", result) break except Exception as e: error_msg = str(e) print(f"SQL 执行出错: {error_msg}") # 调用 LLM 修正 SQL - state['sql'] = fixSQLChain.invoke({ - "sql": state['sql'], - "error_msg": error_msg, - "table_info": state['table_info'], - "tenant_id": state['tenant_id'] - } + state["sql"] = fixSQLChain.invoke( + { + "sql": state["sql"], + "error_msg": error_msg, + "table_info": state["table_info"], + "tenant_id": state["tenant_id"], + } ).content # print(f"LLM 生成修正 SQL: {state['sql']}") else: @@ -124,11 +106,13 @@ def sql(state: State): def sql_1(state: State): - return sqlChain.invoke({ - "table_info": state['table_info'], - "userInput": state["userInput"], - "tenant_id": state['tenant_id'] - }).content + return sqlChain.invoke( + { + "table_info": state["table_info"], + "userInput": state["userInput"], + "tenant_id": state["tenant_id"], + } + ).content improve_sql_prompt = PromptTemplate( @@ -151,18 +135,20 @@ improve_sql_prompt = PromptTemplate( 7. 通常来说,不查询对用户来说意义不大的字段,比如主键、外键、id等。 8. 查询的SQL字段要用别名,取名参考描述。 9. 一般情况下,如果能限制租户Id(通常为tenantid 字段),则尽量加上WHERE tenantid = {tenant_id}。 -""" +""", ) improveSqlChain = improve_sql_prompt | llm def sql_2(state: State): - return improveSqlChain.invoke({ - "sql": state['sql'], - "table_info": state['table_info'], - "userInput": state["userInput"], - "tenant_id": state['tenant_id'] - }).content + return improveSqlChain.invoke( + { + "sql": state["sql"], + "table_info": state["table_info"], + "userInput": state["userInput"], + "tenant_id": state["tenant_id"], + } + ).content # ------------------------------------------------------------------------ 路径选择 -------- @@ -199,7 +185,7 @@ chat → 无法直接生成 SQL,需要进一步解释或澄清。 回答内容仅限于db或者chat,请勿输出其他内容。 你的回复: - """ + """, ) pathSelectChain = pathSelectPrompt | llmThink @@ -207,12 +193,18 @@ pathSelectChain = pathSelectPrompt | llmThink def decide_source(state: State, max_retry=3): """根据用户输入选择数据来源""" for _ in range(max_retry): - choice = pathSelectChain.invoke({ - "userInput": state["userInput"], - "table_info": state["table_info"], - "ai_service": state["ai_service"], - "sql": state["sql"] - }).content.strip().lower() + choice = ( + pathSelectChain.invoke( + { + "userInput": state["userInput"], + "table_info": state["table_info"], + "ai_service": state["ai_service"], + "sql": state["sql"], + } + ) + .content.strip() + .lower() + ) print("根据用户输入选择数据来源,路径是:", choice) if choice in ["db", "chat"]: state["path"] = choice @@ -242,17 +234,16 @@ noChatPrompt = PromptTemplate( 3. 引导用户提出与你业务相关的问题。 4. 使用礼貌和友好的语气。 你的回答: - """ + """, ) noChatChain = noChatPrompt | llm def chat(state: State): - state["reply"] = noChatChain.invoke({ - "userInput": state["userInput"], - "ai_service": state["ai_service"] - }).content + state["reply"] = noChatChain.invoke( + {"userInput": state["userInput"], "ai_service": state["ai_service"]} + ).content print("直接回复") return state @@ -291,19 +282,21 @@ summarizePrompt = PromptTemplate( 2. 提供进一步可选的查询示例,基于当前的数据库表结构,引导用户提出更具体需求。 你的回复: - """ + """, ) summarizeChain = summarizePrompt | llm def summarize_ai(state: State): """AI 总结输出""" - state["reply"] = summarizeChain.invoke({ - "ai_role": state["ai_role"], - "sql": state['sql'], - "userInput": state['userInput'], - "table_info": state['table_info'], - }).content + state["reply"] = summarizeChain.invoke( + { + "ai_role": state["ai_role"], + "sql": state["sql"], + "userInput": state["userInput"], + "table_info": state["table_info"], + } + ).content return state @@ -322,7 +315,7 @@ workflow.add_conditional_edges( { "db": "sql_1", "chat": "chat", - } + }, ) workflow.add_edge("summarize", END) workflow.add_edge("chat", END) @@ -334,13 +327,15 @@ def get_db_agent_reply(aiId: str, userInput: str, tenant_id: str, sql: str = "") json = pgdb.get_ai_personality(aiId) ai_service = json["业务"] ai_role = json["性格"] - final_state = graph.invoke({ - "ai_service": ai_service, - "ai_role": ai_role, - "table_info": pgdb.get_available_tables_str(aiId), - "tenant_id": tenant_id, - "userInput": userInput, - "sql": sql, - "isFirstGenSQL": sql == "", - }) + final_state = graph.invoke( + { + "ai_service": ai_service, + "ai_role": ai_role, + "table_info": pgdb.get_available_tables_str(aiId), + "tenant_id": tenant_id, + "userInput": userInput, + "sql": sql, + "isFirstGenSQL": sql == "", + } + ) return final_state diff --git a/bbit_ai/app/agent/serviceAgent.py b/bbit_ai/app/agent/serviceAgent.py index a1e1e9f..2377ce0 100644 --- a/bbit_ai/app/agent/serviceAgent.py +++ b/bbit_ai/app/agent/serviceAgent.py @@ -1,59 +1,39 @@ - -from typing import Literal -from langchain_core.messages import AIMessage -from langchain_core.runnables import RunnableConfig -from langgraph.graph import END, START, MessagesState, StateGraph -from langgraph.prebuilt import ToolNode -from langchain_community.agent_toolkits import SQLDatabaseToolkit -from config.llm import llm,llmThink -from langgraph.graph import StateGraph, END -from langchain.prompts import PromptTemplate -from config.llm import llm -from typing import Annotated -from typing_extensions import TypedDict -from langgraph.graph import StateGraph, START, END -from langgraph.graph.message import add_messages -from langchain_community.agent_toolkits import create_sql_agent -from langchain.prompts import PromptTemplate -from config.llm import llm -from typing import Annotated -from langgraph.graph.message import add_messages -import os -from langchain_tavily import TavilySearch -from langgraph.prebuilt import ToolNode, tools_condition -from llm.chatLLM import get_chat_response +from typing import List from typing import TypedDict + +from langchain_core.prompts import PromptTemplate from langgraph.graph import StateGraph, END -from llm.summarizeLLM import getSummary -import db.postgres as pgdb -import db.sqlserver as sqlserver -from typing import List, Dict + import db.milvus as milvus +import db.postgres as pgdb +from config.llm import llm +from config.llm import llmThink # -------- 定义状态 -------- class State(TypedDict): - path: str # 开始聊天选择的路径 + path: str # 开始聊天选择的路径 - memory:str # 记忆 - knowledge: str # 知识库内容 - history: str # 聊天历史 + memory: str # 记忆 + knowledge: str # 知识库内容 + history: str # 聊天历史 - ai_id : str # AI id - ai_name:str # AI 名称 - ai_service: str # AI 角色 业务 - ai_role: str # AI 角色 性格特点 - kn_bases: List[str] # AI 所使用的知识库 + ai_id: str # AI id + ai_name: str # AI 名称 + ai_service: str # AI 角色 业务 + ai_role: str # AI 角色 性格特点 + kn_bases: List[str] # AI 所使用的知识库 + + userInput: str # 用户输入 + reply: str # 最终回复 - userInput: str # 用户输入 - reply: str # 最终回复 # -------- 定义节点 -------- # ------------------------------------------------------------------------ 向量数据库查询 -------- gen_sql_prompt = PromptTemplate( input_variables=["userInput"], - template = """你的任务是对用户输入进行意图分析,并将其分解成方便进行知识向量数据库搜索的关键词。 + template="""你的任务是对用户输入进行意图分析,并将其分解成方便进行知识向量数据库搜索的关键词。 以下是用户的输入: <用户输入> {userInput} @@ -65,28 +45,33 @@ gen_sql_prompt = PromptTemplate( 4. 确保关键词之间相互独立,不包含其他关键词。 关键词之间用空格分隔。 你的回答是: -""" +""", ) sqlChain = gen_sql_prompt | llm + + def db_search(state: State): - key_words = sqlChain.invoke({ - "userInput": state['userInput'], - }).content + key_words = sqlChain.invoke( + { + "userInput": state["userInput"], + } + ).content print("关键词是:", key_words) - knowledge = milvus.get_knowledge_by_key_words(key_words, state['kn_bases']) + knowledge = milvus.get_knowledge_by_key_words(key_words, state["kn_bases"]) print("知识库内容是:", knowledge) state["knowledge"] = knowledge - ai_ids = [state['ai_id']] + ai_ids = [state["ai_id"]] memory = milvus.get_memory_by_key_words(key_words, ai_ids) print("记忆是:", memory) state["memory"] = memory return state + # ------------------------------------------------------------------------ 意图分析 -------- pathSelectPrompt = PromptTemplate( - input_variables=[ "userInput","ai_service","history"], - template = """ + input_variables=["userInput", "ai_service", "history"], + template=""" 你是一个意图分类器,负责判断用户提问是否与你的工作相关,进而确定是否需要去查知识库。 以下是你负责的工作内容: @@ -103,17 +88,25 @@ pathSelectPrompt = PromptTemplate( 判断规则如下: 如果用户最新回复与你的负责工作相关,需要去查知识库,输出“kn”;如果不相关,则输出“chat”,不要包含任何标点符号以及空格。 你生成的结果: - """ + """, ) pathSelectChain = pathSelectPrompt | llmThink + + def decide_source(state: State, max_retry=3): """根据用户输入选择数据来源""" for _ in range(max_retry): - choice = pathSelectChain.invoke({ - "userInput": state["userInput"], - "ai_service": state["ai_service"], - "history": state["history"], - }).content.strip().lower() + choice = ( + pathSelectChain.invoke( + { + "userInput": state["userInput"], + "ai_service": state["ai_service"], + "history": state["history"], + } + ) + .content.strip() + .lower() + ) print("根据用户输入选择数据来源,路径是:", choice) if choice in ["kn", "chat"]: state["path"] = choice @@ -123,10 +116,11 @@ def decide_source(state: State, max_retry=3): state["path"] = "chat" return state + # ------------------------------------------------------------------------ !普通聊天 -------- noChatPrompt = PromptTemplate( - input_variables=[ "ai_name", "ai_service", "ai_role", "history"], - template = """ + input_variables=["ai_name", "ai_service", "ai_role", "history"], + template=""" 你的名字是:{ai_name},你负责的业务是{ai_service},你具有{ai_role}的性格特点。 这是你和用户的对话历史 @@ -140,26 +134,31 @@ noChatPrompt = PromptTemplate( 4. 回复要简洁明了,避免冗长和复杂的表述。 你的回答: - """ + """, ) noChatChain = noChatPrompt | llm + + def chat(state: State): - state["reply"] = noChatChain.invoke({ - "ai_name": state["ai_name"], - "ai_service": state["ai_service"], - "ai_role": state["ai_role"], - "history": state["history"], - "userStr": state["userInput"] - }).content + state["reply"] = noChatChain.invoke( + { + "ai_name": state["ai_name"], + "ai_service": state["ai_service"], + "ai_role": state["ai_role"], + "history": state["history"], + "userStr": state["userInput"], + } + ).content print("直接回复") return state + # ------------------------------------------------------------------------ 整理结果 -------- summarizePrompt = PromptTemplate( input_variables=["ai_name", "ai_service", "ai_role", "history", "knowledge"], - template = """ + template=""" 你的任务是基于给定的AI名称、AI角色业务、AI角色性格特点和聊天历史来回复用户。请仔细阅读以下信息,并按照指示进行回复。 你的名字是:{ai_name},你负责的业务是{ai_service},你具有{ai_role}的性格特点。 @@ -177,32 +176,40 @@ summarizePrompt = PromptTemplate( 2. 回复的语气要结合你的性格特点。 3. 确保回复内容清晰、简洁、有针对性。 请生成你的回复: - """ + """, ) summarizeChain = summarizePrompt | llm + + def summarize_ai(state: State): """AI 总结输出""" - mem = state['memory'] + mem = state["memory"] if mem != "": - memStr = """ + memStr = ( + """ 这是给你参考的相关历史记忆: %s - """ % mem # 这里用 % 把 mem 填进去 + """ + % mem + ) # 这里用 % 把 mem 填进去 else: memStr = "没有记忆内容" - print("历史记录是:" ,state["history"]) - state["reply"] = summarizeChain.invoke({ - "ai_role":state["ai_role"], - "ai_name":state["ai_name"], - "history":state["history"], - "ai_service":state['ai_service'], - "knowledge": state["knowledge"], - "memory": memStr, - }).content + print("历史记录是:", state["history"]) + state["reply"] = summarizeChain.invoke( + { + "ai_role": state["ai_role"], + "ai_name": state["ai_name"], + "history": state["history"], + "ai_service": state["ai_service"], + "knowledge": state["knowledge"], + "memory": memStr, + } + ).content return state + # ------------------------------------------------------------------------ 构建有向图 -------- workflow = StateGraph(State) workflow.add_node("decide", decide_source) @@ -217,30 +224,35 @@ workflow.add_conditional_edges( { "kn": "db_search", "chat": "chat", - } + }, ) workflow.add_edge("db_search", "summarize") workflow.add_edge("summarize", END) workflow.add_edge("chat", END) graph = workflow.compile() + # 执行函数 -def get_service_agent_reply(aiId:str, userInput: str,history:str, kn_bases:List[str]) : +def get_service_agent_reply( + aiId: str, userInput: str, history: str, kn_bases: List[str] +): json = pgdb.get_ai_personality(aiId) ai_service = json["业务"] - ai_role = json["性格"] + ai_role = json["性格"] ai_name = json["名字"] print("AI Name:", ai_name) print("AI Service:", ai_service) - - final_state = graph.invoke({ - "ai_service":ai_service, - "ai_role":ai_role, - "ai_name":ai_name, - "history":history, - "kn_bases":kn_bases, - "table_info": pgdb.get_available_tables_str(aiId), - "userInput": userInput, - "ai_id": aiId, - }) - return final_state["reply"] \ No newline at end of file + + final_state = graph.invoke( + { + "ai_service": ai_service, + "ai_role": ai_role, + "ai_name": ai_name, + "history": history, + "kn_bases": kn_bases, + "table_info": pgdb.get_available_tables_str(aiId), + "userInput": userInput, + "ai_id": aiId, + } + ) + return final_state["reply"] diff --git a/bbit_ai/app/config/emqx.py b/bbit_ai/app/config/emqx.py index 88f51b8..83dda00 100644 --- a/bbit_ai/app/config/emqx.py +++ b/bbit_ai/app/config/emqx.py @@ -40,17 +40,14 @@ if sys.platform.lower() == "win32" or os.name.lower() == "nt": def get_device_id_simple(): - try: - with open("/etc/machine-id") as f: - mid = f.read().strip() - if mid: - return mid - except Exception: - pass - hostname = socket.gethostname() - mac = uuid.getnode() - mac_str = ":".join(f"{(mac >> ele) & 0xff:02x}" for ele in range(40, -1, -8)) - return f"{hostname}|{mac_str}" + hostname = os.getenv("HOST_NAME") + if not hostname: + hostname = socket.gethostname() + mac = uuid.getnode() + mac_str = ":".join(f"{(mac >> ele) & 0xff:02x}" for ele in range(40, -1, -8)) + return f"{hostname}|{mac_str}" + else: + return hostname # todo 这里需要订阅状态信息 设备发送信息 这里回复 vue前端发送指令 后端发送指令 设备接收指令 diff --git a/bbit_ai/app/db/milvus.py b/bbit_ai/app/db/milvus.py index 82283cc..70a67c4 100644 --- a/bbit_ai/app/db/milvus.py +++ b/bbit_ai/app/db/milvus.py @@ -1,9 +1,10 @@ -from config.milvus import knVectorstore,memVectorstore -from langchain.schema import Document from datetime import datetime from typing import List -from typing import List, Dict, Any +from langchain_core.documents import Document + +from config.milvus import knVectorstore, memVectorstore + def get_knowledge_by_key_words(key_words: str, kn_ids: List[str]) -> str: """ @@ -15,13 +16,11 @@ def get_knowledge_by_key_words(key_words: str, kn_ids: List[str]) -> str: expr = f"({ids_expr})" else: return "未找到相关的知识。" - + result = knVectorstore.similarity_search( - query=key_words, - k=3, # 可调节返回条数 - expr=expr + query=key_words, k=3, expr=expr # 可调节返回条数 ) - + # 整理成字符串 doc_texts = [] for idx, doc in enumerate(result, start=1): @@ -29,14 +28,14 @@ def get_knowledge_by_key_words(key_words: str, kn_ids: List[str]) -> str: if text: # 可以加个编号,便于LLM区分 doc_texts.append(f"[文档{idx}]: {text}") - + # 拼成一个大字符串,用换行隔开 combined_text = "\n\n".join(doc_texts) return combined_text def get_memory_by_key_words(key_words: str, ai_ids: List[str]) -> str: - print("ai_id是:" , ai_ids) + print("ai_id是:", ai_ids) """ 根据关键词和 ai_ids 列表,在知识库中检索相关内容,并返回整理后的文本字符串 """ @@ -46,13 +45,11 @@ def get_memory_by_key_words(key_words: str, ai_ids: List[str]) -> str: expr = f"({ids_expr})" else: expr = "" # 不限制 kn_id todo 实际上应该不反悔任何内容 - + result = memVectorstore.similarity_search( - query=key_words, - k=5, # 可调节返回条数 - expr=expr + query=key_words, k=5, expr=expr # 可调节返回条数 ) - + # 整理成字符串 doc_texts = [] for idx, doc in enumerate(result, start=1): @@ -60,16 +57,16 @@ def get_memory_by_key_words(key_words: str, ai_ids: List[str]) -> str: if text: # 可以加个编号,便于LLM区分 doc_texts.append(f"[记忆{idx}]: {text}") - + # 拼成一个大字符串,用换行隔开 combined_text = "\n\n".join(doc_texts) return combined_text + + def get_knowledge_by_base_id(base_id: str): expr = f'kn_id == "{base_id}"' # base_id 会被替换 - result = knVectorstore.similarity_search( - query="", # 如果只想用过滤条件,可以传空字符串 - k=100, - expr=expr + result = knVectorstore.similarity_search( + query="", k=100, expr=expr # 如果只想用过滤条件,可以传空字符串 ) return [ { @@ -80,6 +77,7 @@ def get_knowledge_by_base_id(base_id: str): for doc in result ] + def add_knowledge(text: str, is_active: bool, base_id: str, user_id: str): docs = [ Document( @@ -89,12 +87,13 @@ def add_knowledge(text: str, is_active: bool, base_id: str, user_id: str): "created_by": str(user_id), "created_at": datetime.now().isoformat(), "is_active": is_active, - } + }, ) ] return knVectorstore.add_documents(docs) -def add_memory(ai_id:str,mem: str, user_id: str,is_active: bool): + +def add_memory(ai_id: str, mem: str, user_id: str, is_active: bool): docs = [ Document( page_content=mem, @@ -103,7 +102,7 @@ def add_memory(ai_id:str,mem: str, user_id: str,is_active: bool): "created_by": str(user_id), "created_at": datetime.now().isoformat(), "is_active": is_active, - } + }, ) ] return memVectorstore.add_documents(docs) diff --git a/bbit_ai/app/llm/chatLLM.py b/bbit_ai/app/llm/chatLLM.py index 925caa3..2c6b43e 100644 --- a/bbit_ai/app/llm/chatLLM.py +++ b/bbit_ai/app/llm/chatLLM.py @@ -1,10 +1,10 @@ +from langchain_core.prompts import PromptTemplate from config.llm import llm -from langchain.prompts import PromptTemplate chatPrompt = PromptTemplate( input_variables=["aiRole", "history", "userInput"], - template = """ + template=""" 你的用户画像为:{aiRole}。 你需要基于你的角色性格,使用中文回答用户。 @@ -15,13 +15,12 @@ chatPrompt = PromptTemplate( {userInput} 最后,请注意,不要编造数据,不知道就说不知道,现在,请生成你的回复: - """ + """, ) chatChain = chatPrompt | llm -def get_chat_response(aiRole: str,history: str, userInput: str) -> str: - return chatChain.invoke({ - "aiRole": aiRole, - "history": history, - "userInput": userInput - }) \ No newline at end of file + +def get_chat_response(aiRole: str, history: str, userInput: str) -> str: + return chatChain.invoke( + {"aiRole": aiRole, "history": history, "userInput": userInput} + ) diff --git a/bbit_ai/app/llm/memLLM.py b/bbit_ai/app/llm/memLLM.py index 8fd4d2b..27e87a3 100644 --- a/bbit_ai/app/llm/memLLM.py +++ b/bbit_ai/app/llm/memLLM.py @@ -1,12 +1,12 @@ -from langchain.prompts import PromptTemplate -from config.llm import llm,llmThink +from langchain_core.prompts import PromptTemplate + import db.milvus as milvus import db.postgres as pg -import json +from config.llm import llmThink memPathPrompt = PromptTemplate( input_variables=["ai_role", "CHAT_RECORD"], - template = """ + template=""" 你是一个记忆筛选器,负责判断最近对话的信息中,用户的回复内容是否对业务具有长期价值或潜在价值,或者可以帮助形成用户画像。 首先,请仔细阅读以下关于你业务的描述: @@ -31,12 +31,12 @@ no:用户最新回复价值有限或几乎不会在未来业务中使用。 回复不要带任何标点符号以及空格、换行符。 请给出你的判断结果: - """ + """, ) memPathChain = memPathPrompt | llmThink memPrompt = PromptTemplate( input_variables=["CHAT_RECORD"], - template = """ + template=""" 你的任务是对给定的聊天记录进行关键信息的记忆总结。请仔细阅读以下聊天记录,并按照要求进行总结: <聊天记录> {CHAT_RECORD} @@ -48,13 +48,15 @@ memPrompt = PromptTemplate( 4. 总结内容应包含时间,并确保时间是准确的。 5. 你需要针对你的业务场景{ai_role},展开对用户最后回复的总结。 请生成你的总结,以用户、时间开头: - """ + """, ) memChain = memPrompt | llmThink -def take_memory(ai_id:str,sessionId: str,user_id:str, max_retry=3): + + +def take_memory(ai_id: str, sessionId: str, user_id: str, max_retry=3): """根据用户输入选择数据来源""" - history = pg.get_history_with_time(sessionId,10) - print("获取的历史记录:",history) + history = pg.get_history_with_time(sessionId, 10) + print("获取的历史记录:", history) ai_service = pg.get_description(ai_id) if ai_service == "": # AI描述没有描述,则取业务字段 @@ -66,17 +68,29 @@ def take_memory(ai_id:str,sessionId: str,user_id:str, max_retry=3): else: ai_service = json["业务"] print("获取的描述是:", ai_service) - choice = memPathChain.invoke({ - "ai_role": ai_service, - "CHAT_RECORD": history, - }).content.strip().lower() + choice = ( + memPathChain.invoke( + { + "ai_role": ai_service, + "CHAT_RECORD": history, + } + ) + .content.strip() + .lower() + ) print("记忆判断器判断的结果是:", choice) if choice == "yes": # 对对话进行总结 - memory = memChain.invoke({ - "CHAT_RECORD": history, - "ai_role": ai_service, - }).content.strip().lower() + memory = ( + memChain.invoke( + { + "CHAT_RECORD": history, + "ai_role": ai_service, + } + ) + .content.strip() + .lower() + ) print("记忆生成结果是:", memory) - milvus.add_memory(mem = memory,user_id = user_id, is_active = True, ai_id = ai_id) - return \ No newline at end of file + milvus.add_memory(mem=memory, user_id=user_id, is_active=True, ai_id=ai_id) + return diff --git a/bbit_ai/app/llm/sqlLLM.py b/bbit_ai/app/llm/sqlLLM.py index 9635710..7cfee16 100644 --- a/bbit_ai/app/llm/sqlLLM.py +++ b/bbit_ai/app/llm/sqlLLM.py @@ -1,13 +1,13 @@ +from langchain_community.agent_toolkits import create_sql_agent +from langchain_core.prompts import PromptTemplate from config.llm import llm -from langchain.prompts import PromptTemplate -from config.ssDb import ssDBLC -from langchain_community.agent_toolkits import create_sql_agent +from config.ssDb import ssDBLC -#______________________________________________________________SQL描述_____________________________________________________________________ +# ______________________________________________________________SQL描述_____________________________________________________________________ sqlDescriptionPrompt = PromptTemplate( input_variables=["sql"], - template = """ + template=""" 你是一个SQL专家,精通SQLServer数据库。请把一下SQL查询语句用通俗易懂的中文进行总结。 SQL语句:{sql} 有以下要求: @@ -16,54 +16,51 @@ sqlDescriptionPrompt = PromptTemplate( 3. 不能有markdown语法 4. 要用业务语言描述,不能有专业语句例如SQL表名等 请生成你认为合适的标题,: - """ + """, ) sqlDescriptionChain = sqlDescriptionPrompt | llm -def get_sql_description_response( sql: str) -> str: - return sqlDescriptionChain.invoke({ - "sql": sql - }) -#______________________________________________________________第一次生成SQL_____________________________________________________________________ +def get_sql_description_response(sql: str) -> str: + return sqlDescriptionChain.invoke({"sql": sql}) + + +# ______________________________________________________________第一次生成SQL_____________________________________________________________________ sqlPrompt = PromptTemplate( input_variables=["userInput"], - template = """ + template=""" 你是一个SQL专家,精通SQLServer数据库。 请根据用户的需求,生成相应的SQL查询语句。 只需要返回SQL语句,不要任何解释。 用户需求:{userInput} 请生成SQL语句: - """ + """, ) sqlChain = sqlPrompt | llm -agent = create_sql_agent( - llm=llm, - db=ssDBLC, - agent_type="tool-calling", - verbose=True -) +agent = create_sql_agent(llm=llm, db=ssDBLC, agent_type="tool-calling", verbose=True) + + # def get_chat_sql_response2( userInput: str) -> str: # return sqlChain.invoke({ # "userInput": userInput # }) -def get_chat_sql_response( userInput: str) -> str: +def get_chat_sql_response(userInput: str) -> str: return agent.invoke({"input": userInput})["output"] -#______________________________________________________________改进SQL_____________________________________________________________________ + +# ______________________________________________________________改进SQL_____________________________________________________________________ sqlImprovePrompt = PromptTemplate( input_variables=["userInput", "sql"], - template = """ + template=""" 你是一个SQL专家,精通SQLServer数据库。 请根据用户的需求,改进已有的SQL查询语句。 只需要返回改进后的SQL语句,不要任何解释。 已有SQL:{sql} 用户需求:{userInput} - """ + """, ) sqlImproveChain = sqlImprovePrompt | llm -def get_chat_sql_improve_response( userInput: str) -> str: - return sqlImproveChain.invoke({ - "userInput": userInput - }) + +def get_chat_sql_improve_response(userInput: str) -> str: + return sqlImproveChain.invoke({"userInput": userInput}) diff --git a/bbit_ai/app/llm/summarizeLLM.py b/bbit_ai/app/llm/summarizeLLM.py index ece422b..9c8c69d 100644 --- a/bbit_ai/app/llm/summarizeLLM.py +++ b/bbit_ai/app/llm/summarizeLLM.py @@ -1,10 +1,10 @@ +from langchain_core.prompts import PromptTemplate -from langchain.prompts import PromptTemplate from config.llm import llm summarizePrompt = PromptTemplate( input_variables=["aiRole", "history", "userStr", "infomation"], - template = """ + template=""" 你是一个主干信息研发的 AI 助手,用户画像为:{aiRole}。 请基于你的角色性格,保持中文简洁回答的,根据下方提示回答用户。 @@ -21,14 +21,17 @@ summarizePrompt = PromptTemplate( {infomation} ··· 如果参考内容明显有问题,你要请用户重新描述问题,现在请生成你的回复: - """ + """, ) summarizeChain = summarizePrompt | llm + def getSummary(aiRole: str, history: str, userInput: str, infomation: str) -> str: - return summarizeChain.invoke({ - "aiRole":aiRole, - "history": history, - "userStr": userInput, - "infomation": infomation - }).content + return summarizeChain.invoke( + { + "aiRole": aiRole, + "history": history, + "userStr": userInput, + "infomation": infomation, + } + ).content diff --git a/bbit_ai/app/llm/ticketLLM.py b/bbit_ai/app/llm/ticketLLM.py index 5c52bc9..586147d 100644 --- a/bbit_ai/app/llm/ticketLLM.py +++ b/bbit_ai/app/llm/ticketLLM.py @@ -1,7 +1,7 @@ import json import re -from langchain.schema import HumanMessage +from langchain_core.messages import HumanMessage from config.llm import * diff --git a/bbit_ai/app/llm/ticketLLMv2.py b/bbit_ai/app/llm/ticketLLMv2.py index d18f5ba..8e8587b 100644 --- a/bbit_ai/app/llm/ticketLLMv2.py +++ b/bbit_ai/app/llm/ticketLLMv2.py @@ -1,7 +1,7 @@ import json import re -from langchain.schema import HumanMessage +from langchain_core.messages import HumanMessage from config.llm import * from llm.ticketLLM import decode_barcode diff --git a/bbit_ai/app/llm/titleChain.py b/bbit_ai/app/llm/titleChain.py index 71ad9c6..78a44bb 100644 --- a/bbit_ai/app/llm/titleChain.py +++ b/bbit_ai/app/llm/titleChain.py @@ -1,10 +1,10 @@ +from langchain_core.prompts import PromptTemplate -from langchain.prompts import PromptTemplate from config.llm import llm titlePrompt = PromptTemplate( input_variables=["userStr"], - template = """ + template=""" 请将用户的这句话总结成一个简短、精准的对话标题,要求: 1. 不超过10个字(可根据需要调整长度)。 2. 直接概括本次对话的核心内容。 @@ -12,9 +12,10 @@ titlePrompt = PromptTemplate( 4. 保持自然、易懂、专业或有趣(可根据场景调整风格)。 5. 不能出现标点符号。 用户原话:"{userStr}" - """ + """, ) titleChain = titlePrompt | llm + def get_title(userInput: str): - return titleChain.invoke({"userStr": userInput}).content \ No newline at end of file + return titleChain.invoke({"userStr": userInput}).content diff --git a/bbit_ai/app/requirements.txt b/bbit_ai/app/requirements.txt index da159e8..b317011 100644 --- a/bbit_ai/app/requirements.txt +++ b/bbit_ai/app/requirements.txt @@ -23,6 +23,7 @@ python-multipart==0.0.20 aio_pika==9.5.7 ultralytics==8.3.227 redis==7.1.0 +aiomqtt==2.4.0 # MCP服务 python-dotenv>=1.0.0 websockets>=11.0.3