diff --git a/docs/.vitepress/theme/router/index.ts b/docs/.vitepress/theme/router/index.ts index 2001a6f..3e4274a 100644 --- a/docs/.vitepress/theme/router/index.ts +++ b/docs/.vitepress/theme/router/index.ts @@ -57,6 +57,7 @@ export const routers = [ text: '🤖 AI', items: [ { text: '基于Langchain的Agent开发', link: '/Web/AI/Langchain' }, + { text: 'Text To SQL开发', link: '/Web/AI/TextToSQL' }, ] }, { diff --git a/docs/Web/AI/Langchain.md b/docs/Web/AI/Langchain.md index d2ddcf0..c25ca21 100644 --- a/docs/Web/AI/Langchain.md +++ b/docs/Web/AI/Langchain.md @@ -171,4 +171,72 @@ agent.stream({"messages": [message]}, ): ``` -  调用模型的时候需要传递`thread_id`。 \ No newline at end of file +  调用模型的时候需要传递`thread_id`。 +::: tip +通常还需要构建一个`用户-问题标题`的数据库,方便查询用户的所有历史回答。 +问题标题可以再调一次模型总结。 +::: + +# 七、实战 +## 7.1 后端 +```python +async def query_agent(request: ChatRequest): + try: + message = HumanMessage(content=request.message) + + # 流式调用Agent + for chunk, metadata in agent.stream( + {"messages": [message]}, + {"configurable": {"thread_id": request.thread_id}}, + stream_mode="messages" + ): + if isinstance(chunk, AIMessageChunk) and chunk.content: + yield chunk.content + + # 总结对话标题并保存 + if not exists_session(request.thread_id, request.username): + title = generate_title(get_messages(request.thread_id)) + save_session(request.thread_id, request.username, title) + + except Exception as e: + print(f"\n[错误]: {str(e)}") + yield "信息检索失败,请重新输入问题提问" + + +@router.post("/chat/stream") +async def chat_endpoint(request: ChatRequest): + """流式对话""" + return StreamingResponse( + query_agent(request), + media_type="text/event-stream" + ) +``` + +## 7.2 前端 +```typescript +const sendMessage = async () => { + try { + const response = await fetch( + "/chief-agent-api/chat/stream", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(message) + } + ); + + const reader = response.body!.getReader(); + const decoder = new TextDecoder("utf-8"); + let aiText = ""; + + while (true) { + const { value, done } = await reader.read(); + if (done) break; + + aiText += decoder.decode(value, { stream: true }); + } + } finally { + loading = false; + } +}; +``` diff --git a/docs/Web/AI/TextToSql.md b/docs/Web/AI/TextToSql.md new file mode 100644 index 0000000..e2b54f5 --- /dev/null +++ b/docs/Web/AI/TextToSql.md @@ -0,0 +1,103 @@ +--- + title: Text To SQL开发 + date: 2026-06-09 +--- + +# 一、工作流程 +```plain +用户自然语言问题 + ↓ +【大模型】生成 SQL + ↓ +【数据库】执行 SQL → 得到结构化结果 + ↓ +【大模型】分析 / 解读 / 总结 / 可视化建议 + ↓ +最终返回给用户(文字、结论、图表说明) +``` + +# 二、实战 +## 2.1 获取数据库结构 +```python +db = SQLDatabase.from_uri(os.getenv("DB_URL"), include_tables=['table_1', 'table_2']) +table_info = db.get_table_info() +``` + +::: warning +这里的数据库账号只能为只读权限。 +::: + +## 2.2 SQL提示词 +```python +from langchain_core.prompts import PromptTemplate + +SQL_PROMPT = PromptTemplate.from_template( + """ +你是一个 MySQL 专家。 +请根据用户问题生成一条可执行的 MySQL 查询语句。 +只返回 SQL,不要解释,不要加 ```。 + +数据库结构: +{schema} + +数据库补充: +{option} + +用户问题: +{question} +""" +) +``` + +  数据库结构就是刚才生成的`table_info`,数据库补充就是对一些关系表的说明和字段含义的解释等。 + +## 2.3 生成SQL +```python +sql = chat_model.invoke( + SQL_PROMPT.format(schema=table_info, option=option, question=request.message) +).content.strip() +``` + +  这里的`chat_model`为`init_chat_model`构建的大模型。 + +## 2.4 执行SQL +```python +from sqlalchemy import create_engine +from sqlalchemy import text + +engine = create_engine( + os.getenv("DB_URL"), + pool_pre_ping=True, +) + +conn = engine.connect() + + +def is_select(sql: str) -> bool: + return sql.lower().lstrip().startswith("select") + +def execute_sql(sql: str): + if not is_select(sql): + raise ValueError("只允许 SELECT 查询") + + result = conn.execute(text(sql)) + rows = result.fetchall() + columns = result.keys() + return { + "columns": list(columns), + "rows": rows + } +``` +  使用sqlalchemy执行SQL,并加入了安全校验。 + +## 2.5 分析数据 +```python +agent = create_agent( + model=chat_model, + system_prompt=SYSTEM_PROMPT.format(question=request.message, result=result), +) + +response = agent.invoke({"message": HumanMessage(content=request.message)}) +``` + +  这里的系统提示词根据情况编写。 \ No newline at end of file