feat:初始化工程
This commit is contained in:
5
.env
Normal file
5
.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
SQLITE_DB_PATH="sweet-hut.db"
|
||||||
|
DB_URL="mysql+pymysql://root:estun%40medical@localhost/sweet_hut?charset=utf8mb4"
|
||||||
|
MODEL_NAME="qwen3.6-plus"
|
||||||
|
MODEL_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
|
MODEL_API_KEY="sk-52bcd98e9c1d45908437c4e8706eefff"
|
||||||
71
.gitignore
vendored
Normal file
71
.gitignore
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Python 字节码文件
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C 扩展
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# 分发/打包
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# 虚拟环境
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
|
||||||
|
# Django 相关
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
media/
|
||||||
|
|
||||||
|
# PyCharm IDE
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
.vscode/
|
||||||
|
*.code-workspace
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# 其他
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
logs/
|
||||||
|
packages/
|
||||||
|
*.db
|
||||||
|
*.db-shm
|
||||||
|
*.db-wal
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/sweet-hut-agent.iml" filepath="$PROJECT_DIR$/.idea/sweet-hut-agent.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/sweet-hut-agent.iml
generated
Normal file
10
.idea/sweet-hut-agent.iml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.10 (sweet-hut-agent) (2)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
118
.idea/workspace.xml
generated
Normal file
118
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="fca82301-8db7-46f2-bd02-1f76c1dc566b" name="Changes" comment="">
|
||||||
|
<change afterPath="$PROJECT_DIR$/.env" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/sweet-hut-agent.iml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/agent.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/config/db.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/main.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/model.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/prompt/sql.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/prompt/system.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/prompt/table.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/router.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/services/agent_service.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="FastAPI main" />
|
||||||
|
<option value="FastAPI test_main" />
|
||||||
|
<option value="Python Script" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectColorInfo">{
|
||||||
|
"associatedIndex": 0
|
||||||
|
}</component>
|
||||||
|
<component name="ProjectId" id="3EcNiHw4Lf7c97fEacOE86P9DSi" />
|
||||||
|
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
|
"keyToString": {
|
||||||
|
"FastAPI.sweet-hut-agent.executor": "Run",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"RunOnceActivity.git.unshallow": "true",
|
||||||
|
"git-widget-placeholder": "master",
|
||||||
|
"last_opened_file_path": "D:/Cxx/PythonProjects/sweet-hut-agent/prompt",
|
||||||
|
"node.js.detected.package.eslint": "true",
|
||||||
|
"node.js.detected.package.tslint": "true",
|
||||||
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
|
"nodejs_package_manager_path": "npm",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
}
|
||||||
|
}]]></component>
|
||||||
|
<component name="RecentsManager">
|
||||||
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="D:\Cxx\PythonProjects\sweet-hut-agent\prompt" />
|
||||||
|
<recent name="D:\Cxx\PythonProjects\sweet-hut-agent" />
|
||||||
|
</key>
|
||||||
|
</component>
|
||||||
|
<component name="RunManager">
|
||||||
|
<configuration name="sweet-hut-agent" type="Python.FastAPI">
|
||||||
|
<option name="file" value="$PROJECT_DIR$/main.py" />
|
||||||
|
<module name="sweet-hut-agent" />
|
||||||
|
<option name="ENV_FILES" value="" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="PARENT_ENVS" value="true" />
|
||||||
|
<option name="SDK_HOME" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
|
<option name="IS_MODULE_SDK" value="false" />
|
||||||
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||||
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||||
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||||
|
<option name="launchJavascriptDebuger" value="false" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
|
<component name="SharedIndexes">
|
||||||
|
<attachedChunks>
|
||||||
|
<set>
|
||||||
|
<option value="bundled-js-predefined-d6986cc7102b-1632447f56bf-JavaScript-PY-243.26053.29" />
|
||||||
|
<option value="bundled-python-sdk-b1dbf8ef85a6-4df51de95216-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-243.26053.29" />
|
||||||
|
</set>
|
||||||
|
</attachedChunks>
|
||||||
|
</component>
|
||||||
|
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="Default task">
|
||||||
|
<changelist id="fca82301-8db7-46f2-bd02-1f76c1dc566b" name="Changes" comment="" />
|
||||||
|
<created>1780474023959</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1780474023959</updated>
|
||||||
|
<workItem from="1780474024998" duration="4337000" />
|
||||||
|
<workItem from="1780488022005" duration="10000" />
|
||||||
|
<workItem from="1780971608037" duration="7293000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
||||||
|
<SUITE FILE_PATH="coverage/sweet_hut_agent$sweet_hut_agent.coverage" NAME="sweet-hut-agent Coverage Results" MODIFIED="1780976147915" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
agent.py
Normal file
11
agent.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from langchain.chat_models import init_chat_model
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
chat_model = init_chat_model(
|
||||||
|
model=os.getenv("MODEL_NAME"),
|
||||||
|
model_provider="openai",
|
||||||
|
base_url=os.getenv("MODEL_BASE_URL"),
|
||||||
|
api_key=os.getenv("MODEL_API_KEY"),
|
||||||
|
temperature=1.5,
|
||||||
|
)
|
||||||
31
config/db.py
Normal file
31
config/db.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
22
main.py
Normal file
22
main.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from router import router
|
||||||
|
|
||||||
|
# 初始化FastAPI
|
||||||
|
app = FastAPI(
|
||||||
|
title="Sweet Hut Agent",
|
||||||
|
description="智能体",
|
||||||
|
version="0.1.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
app.include_router(router)
|
||||||
5
model.py
Normal file
5
model.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
class ChatRequest(BaseModel):
|
||||||
|
type: str
|
||||||
|
message: str
|
||||||
18
prompt/sql.py
Normal file
18
prompt/sql.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from langchain_core.prompts import PromptTemplate
|
||||||
|
|
||||||
|
SQL_PROMPT = PromptTemplate.from_template(
|
||||||
|
"""
|
||||||
|
你是一个 MySQL 专家。
|
||||||
|
请根据用户问题生成一条可执行的 MySQL 查询语句。
|
||||||
|
只返回 SQL,不要解释,不要加 ```。
|
||||||
|
|
||||||
|
数据库结构:
|
||||||
|
{schema}
|
||||||
|
|
||||||
|
数据库补充:
|
||||||
|
{option}
|
||||||
|
|
||||||
|
用户问题:
|
||||||
|
{question}
|
||||||
|
"""
|
||||||
|
)
|
||||||
23
prompt/system.py
Normal file
23
prompt/system.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from langchain_core.prompts import PromptTemplate
|
||||||
|
|
||||||
|
BILL_PROMPT = PromptTemplate.from_template(
|
||||||
|
"""
|
||||||
|
你是一位家庭账单助手,负责用清晰、专业、易懂的中文回答用户的问题。
|
||||||
|
|
||||||
|
用户问题:
|
||||||
|
{question}
|
||||||
|
|
||||||
|
查询到的数据:
|
||||||
|
{result}
|
||||||
|
|
||||||
|
回答要求:
|
||||||
|
1. 仅基于上方数据进行回答,不要编造内容。
|
||||||
|
2. 如果数据为空,直接回答:「没有找到相关数据」。
|
||||||
|
3. 金额统一保留两位小数,并带上“元”。
|
||||||
|
4. 优先给出结论和总结,再选择性补充关键明细。
|
||||||
|
5. 不要输出 SQL、字段名、JSON 或技术细节。
|
||||||
|
6. 语气自然,像在和家人沟通。
|
||||||
|
|
||||||
|
现在请根据上述内容回答问题。
|
||||||
|
"""
|
||||||
|
)
|
||||||
9
prompt/table.py
Normal file
9
prompt/table.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
BILL_TABLE_SCHEMA = """
|
||||||
|
【表关系】
|
||||||
|
bill_record.book_id → bill_book.id
|
||||||
|
bill_record.category_id → bill_category.id
|
||||||
|
bill_record.pay_id → bill_pay.id
|
||||||
|
|
||||||
|
【字段说明】
|
||||||
|
bill_record.type / bill_category.type 取值:'expensive'=支出,'income'=收入
|
||||||
|
"""
|
||||||
12
requirements.txt
Normal file
12
requirements.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fastapi~=0.136.3
|
||||||
|
uvicorn~=0.48.0
|
||||||
|
langchain~=1.3.2
|
||||||
|
langchain-openai~=1.2.2
|
||||||
|
langchain-tavily~=0.2.18
|
||||||
|
langgraph~=1.2.2
|
||||||
|
langgraph-checkpoint-sqlite~=3.1.0
|
||||||
|
pydantic~=2.13.4
|
||||||
|
langchain-core~=1.4.0
|
||||||
|
python-dotenv~=1.2.2
|
||||||
|
langchain-community~=0.4.2
|
||||||
|
pymysql~=1.2.0
|
||||||
11
router.py
Normal file
11
router.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from model import ChatRequest
|
||||||
|
from services.agent_service import query
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/chat")
|
||||||
|
async def chat_endpoint(request: ChatRequest):
|
||||||
|
return query(request)
|
||||||
44
services/agent_service.py
Normal file
44
services/agent_service.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from langchain.agents import create_agent
|
||||||
|
from langchain_community.utilities import SQLDatabase
|
||||||
|
from langchain_core.messages import HumanMessage
|
||||||
|
|
||||||
|
from agent import chat_model
|
||||||
|
from config.db import execute_sql
|
||||||
|
from model import ChatRequest
|
||||||
|
from prompt.sql import SQL_PROMPT
|
||||||
|
from prompt.system import BILL_PROMPT
|
||||||
|
from prompt.table import BILL_TABLE_SCHEMA
|
||||||
|
|
||||||
|
tables = {
|
||||||
|
"bill": ["bill_book", "bill_category", "bill_pay", "bill_record"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def query(request: ChatRequest):
|
||||||
|
try:
|
||||||
|
db = SQLDatabase.from_uri(os.getenv("DB_URL"), include_tables=tables[request.type])
|
||||||
|
table_info = db.get_table_info()
|
||||||
|
|
||||||
|
sql = chat_model.invoke(
|
||||||
|
SQL_PROMPT.format(schema=table_info, option=BILL_TABLE_SCHEMA, question=request.message)
|
||||||
|
).content.strip()
|
||||||
|
|
||||||
|
print("sql: ", sql)
|
||||||
|
|
||||||
|
result = execute_sql(sql)
|
||||||
|
|
||||||
|
print("result: ", result)
|
||||||
|
|
||||||
|
agent = create_agent(
|
||||||
|
model=chat_model,
|
||||||
|
system_prompt=BILL_PROMPT.format(question=request.message, result=result),
|
||||||
|
)
|
||||||
|
|
||||||
|
response = agent.invoke({"message": HumanMessage(content=request.message)})
|
||||||
|
print("response: ", response)
|
||||||
|
|
||||||
|
return response["messages"][-1].content
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[错误]: {str(e)}")
|
||||||
Reference in New Issue
Block a user