feat:增加TextToSql文档
This commit is contained in:
@@ -57,6 +57,7 @@ export const routers = [
|
|||||||
text: '🤖 AI',
|
text: '🤖 AI',
|
||||||
items: [
|
items: [
|
||||||
{ text: '基于Langchain的Agent开发', link: '/Web/AI/Langchain' },
|
{ text: '基于Langchain的Agent开发', link: '/Web/AI/Langchain' },
|
||||||
|
{ text: 'Text To SQL开发', link: '/Web/AI/TextToSQL' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -172,3 +172,71 @@ agent.stream({"messages": [message]},
|
|||||||
```
|
```
|
||||||
|
|
||||||
  调用模型的时候需要传递`thread_id`。
|
  调用模型的时候需要传递`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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|||||||
103
docs/Web/AI/TextToSql.md
Normal file
103
docs/Web/AI/TextToSql.md
Normal file
@@ -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)})
|
||||||
|
```
|
||||||
|
|
||||||
|
  这里的系统提示词根据情况编写。
|
||||||
Reference in New Issue
Block a user