feat:更新智能体架构
This commit is contained in:
6
.env
6
.env
@@ -1,5 +1 @@
|
||||
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"
|
||||
TAVILY_API_KEY=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
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -65,7 +65,4 @@ media/
|
||||
.DS_Store
|
||||
|
||||
logs/
|
||||
packages/
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
packages/
|
||||
3
.idea/sweet-hut-agent.iml
generated
3
.idea/sweet-hut-agent.iml
generated
@@ -3,8 +3,9 @@
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (sweet-hut-agent) (2)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.12 (sweet-hut-agent) (2)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
64
.idea/workspace.xml
generated
64
.idea/workspace.xml
generated
@@ -5,23 +5,18 @@
|
||||
</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$/.env" beforeDir="false" afterPath="$PROJECT_DIR$/.env" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/agent.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config/db.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/model.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/prompt/sql.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/prompt/system.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/prompt/table.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/router.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/services/agent_service.py" beforeDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -44,26 +39,27 @@
|
||||
"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 name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"FastAPI.sweet-hut-agent.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.typescript.service.memoryLimit.init": "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>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="D:\Cxx\PythonProjects\sweet-hut-agent\prompt" />
|
||||
@@ -83,6 +79,7 @@
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="RUN_TOOL" value="" />
|
||||
<option name="launchJavascriptDebuger" value="false" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
@@ -90,8 +87,8 @@
|
||||
<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" />
|
||||
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PY-253.29346.142" />
|
||||
<option value="bundled-python-sdk-f2b7a9f6281b-6e1f45a539f7-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-253.29346.142" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
@@ -106,6 +103,7 @@
|
||||
<workItem from="1780474024998" duration="4337000" />
|
||||
<workItem from="1780488022005" duration="10000" />
|
||||
<workItem from="1780971608037" duration="7293000" />
|
||||
<workItem from="1783001713796" duration="2000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo 'Asia/Shanghai' > /etc/timezone
|
||||
COPY ./packages /app/packages
|
||||
COPY requirements.txt /app/
|
||||
RUN pip install --no-cache-dir --no-index --find-links=/app/packages -r requirements.txt
|
||||
COPY . /app/
|
||||
EXPOSE 8000
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
# pip download -r requirements.txt -d ./packages --only-binary=:all: --platform manylinux2014_x86_64 -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
11
agent.py
11
agent.py
@@ -1,11 +0,0 @@
|
||||
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,
|
||||
)
|
||||
11
agent/bill.py
Normal file
11
agent/bill.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from langchain.agents import create_agent
|
||||
|
||||
from agent.model import model
|
||||
from prompt.bill import bill_prompt
|
||||
from tools.bill import bill_search
|
||||
|
||||
bill_agent = create_agent(
|
||||
model=model,
|
||||
tools=[bill_search],
|
||||
system_prompt=bill_prompt
|
||||
)
|
||||
22
agent/model.py
Normal file
22
agent/model.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
# model = init_chat_model(
|
||||
# model="glm-5.1",
|
||||
# model_provider="openai",
|
||||
# base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
# api_key="sk-52bcd98e9c1d45908437c4e8706eefff"
|
||||
# )
|
||||
|
||||
# model = init_chat_model(
|
||||
# model="GLM-5-Turbo",
|
||||
# model_provider="openai",
|
||||
# base_url="https://open.bigmodel.cn/api/coding/paas/v4",
|
||||
# api_key="ea3f96fd542f4bab805a719cf3063cd6.HCRhCV7nqwkDsVKm",
|
||||
# )
|
||||
|
||||
model = init_chat_model(
|
||||
model="deepseek-v4-flash",
|
||||
model_provider="openai",
|
||||
base_url="https://api.deepseek.com",
|
||||
api_key="sk-0b237d41f6bc44fc9732ea66bd7eade0"
|
||||
)
|
||||
31
config/db.py
31
config/db.py
@@ -1,31 +0,0 @@
|
||||
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
|
||||
}
|
||||
25
main.py
25
main.py
@@ -1,22 +1,25 @@
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from router import router
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
# 初始化FastAPI
|
||||
app = FastAPI(
|
||||
title="Sweet Hut Agent",
|
||||
description="智能体",
|
||||
version="0.1.0"
|
||||
)
|
||||
from modes.agent import QueryRequest
|
||||
from services import query_bill_agent
|
||||
|
||||
app = FastAPI(title="Integrate Agent API")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
app.include_router(router)
|
||||
@app.post("/query/bill")
|
||||
async def query_agent(query: QueryRequest):
|
||||
"""流式对话"""
|
||||
return StreamingResponse(
|
||||
query_bill_agent(query),
|
||||
media_type="text/event-stream"
|
||||
)
|
||||
|
||||
5
model.py
5
model.py
@@ -1,5 +0,0 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
type: str
|
||||
message: str
|
||||
4
modes/agent.py
Normal file
4
modes/agent.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
class QueryRequest(BaseModel):
|
||||
message: str
|
||||
49
prompt/bill.py
Normal file
49
prompt/bill.py
Normal file
@@ -0,0 +1,49 @@
|
||||
bill_prompt = """
|
||||
你是一个个人账单查询助手,负责帮助用户查询账单记录。
|
||||
|
||||
# 可调用的工具
|
||||
- bill_search(start_date, end_date)
|
||||
- 用于查询指定日期范围内的所有账单
|
||||
- 日期格式:YYYY-MM-DD
|
||||
- 该接口不区分收入和支出,会一并返回
|
||||
|
||||
# 日期推断规则(非常重要)
|
||||
当用户未明确给出日期时,你必须先按以下规则推断 start_date 和 end_date,再调用工具:
|
||||
|
||||
- 近期 / 最近:今天之前的 30 天
|
||||
- 本月:当前自然月(如 2026-08-01 ~ 2026-08-31)
|
||||
- 上个月:上一个自然月
|
||||
- 本季度:当前季度(Q1: 01-01~03-31,依此类推)
|
||||
- 上半年:01-01 ~ 06-30
|
||||
- 下半年:07-01 ~ 12-31
|
||||
- 今年 / 本年:当前自然年
|
||||
- 去年:上一年自然年
|
||||
- 无时间描述:默认最近 90 天
|
||||
|
||||
# 示例
|
||||
- 用户:帮我看看最近的账单
|
||||
→ 推断为最近 30 天 → 调用 bill_search
|
||||
|
||||
- 用户:查一下这个月的支出
|
||||
→ 推断为本月日期范围 → 调用 bill_search,后续再用文本过滤支出
|
||||
|
||||
- 用户:上半年收入怎么样
|
||||
→ 推断为 01-01 ~ 06-30 → 调用 bill_search,再自行判断 type = income
|
||||
|
||||
# 返回字段说明(JSON)
|
||||
- id: 账单ID
|
||||
- bookName: 账本名称
|
||||
- type: 账单类型(income / expense)
|
||||
- category: 账单类别
|
||||
- location: 账单地点
|
||||
- payAccount: 支付账户
|
||||
- amount: 金额
|
||||
- content: 账单内容
|
||||
- remark: 备注
|
||||
- date: 账单日期(yyyy-MM-dd)
|
||||
|
||||
# 行为准则
|
||||
- 用户未指定日期时,必须先按上述规则推断日期范围
|
||||
- 查询前应在回复中说明使用的查询条件,例如:“为您查询本月(2026-08-01 ~ 2026-08-31)的支出账单:”
|
||||
- 查询完成后,对账单进行解读(收入 / 支出 / 分类 / 汇总)
|
||||
"""
|
||||
@@ -1,18 +0,0 @@
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
SQL_PROMPT = PromptTemplate.from_template(
|
||||
"""
|
||||
你是一个 MySQL 专家。
|
||||
请根据用户问题生成一条可执行的 MySQL 查询语句。
|
||||
只返回 SQL,不要解释,不要加 ```。
|
||||
|
||||
数据库结构:
|
||||
{schema}
|
||||
|
||||
数据库补充:
|
||||
{option}
|
||||
|
||||
用户问题:
|
||||
{question}
|
||||
"""
|
||||
)
|
||||
@@ -1,23 +0,0 @@
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
BILL_PROMPT = PromptTemplate.from_template(
|
||||
"""
|
||||
你是一位家庭账单助手,负责用清晰、专业、易懂的中文回答用户的问题。
|
||||
|
||||
用户问题:
|
||||
{question}
|
||||
|
||||
查询到的数据:
|
||||
{result}
|
||||
|
||||
回答要求:
|
||||
1. 仅基于上方数据进行回答,不要编造内容。
|
||||
2. 如果数据为空,直接回答:「没有找到相关数据」。
|
||||
3. 金额统一保留两位小数,并带上“元”。
|
||||
4. 优先给出结论和总结,再选择性补充关键明细。
|
||||
5. 不要输出 SQL、字段名、JSON 或技术细节。
|
||||
6. 语气自然,像在和家人沟通。
|
||||
|
||||
现在请根据上述内容回答问题。
|
||||
"""
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
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'=收入
|
||||
"""
|
||||
@@ -1,12 +1,11 @@
|
||||
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
|
||||
pymysql~=1.1.2
|
||||
requests~=2.32.5
|
||||
fastapi~=0.135.1
|
||||
langchain-community~=0.4.1
|
||||
pydantic~=2.12.5
|
||||
langchain-openai~=1.1.10
|
||||
uvicorn~=0.23.0
|
||||
SQLAlchemy~=2.0.48
|
||||
langchain-core~=1.2.17
|
||||
langchain~=1.2.10
|
||||
starlette~=0.52.1
|
||||
11
router.py
11
router.py
@@ -1,11 +0,0 @@
|
||||
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)
|
||||
46
services.py
Normal file
46
services.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from datetime import datetime
|
||||
|
||||
from langchain_core.messages import HumanMessage, AIMessageChunk
|
||||
|
||||
from agent.bill import bill_agent
|
||||
from modes.agent import QueryRequest
|
||||
|
||||
|
||||
async def query_bill_agent(query: QueryRequest):
|
||||
try:
|
||||
print(query)
|
||||
today = datetime.today().strftime("%Y-%m-%d")
|
||||
|
||||
message_content = (
|
||||
f"[当前系统日期: {today}]\n\n"
|
||||
f"{query.message}"
|
||||
)
|
||||
|
||||
message = HumanMessage(content=message_content)
|
||||
|
||||
tool_called = False
|
||||
content_generate = False
|
||||
|
||||
# 流式调用Agent
|
||||
for chunk, metadata in bill_agent.stream(
|
||||
{"messages": [message]},
|
||||
stream_mode="messages"
|
||||
):
|
||||
# print(chunk)
|
||||
if isinstance(chunk, AIMessageChunk):
|
||||
if chunk.content:
|
||||
if not content_generate:
|
||||
content_generate = True
|
||||
yield "\n 📋 **分析结果**:\n"
|
||||
yield chunk.content
|
||||
|
||||
for tc in chunk.tool_call_chunks:
|
||||
if tc.get("args"):
|
||||
if not tool_called:
|
||||
tool_called = True
|
||||
yield "🛠️ **开始查询数据**:查询日期:"
|
||||
yield tc["args"]
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n[错误]: {str(e)}")
|
||||
yield "信息检索失败,请重新输入问题提问"
|
||||
@@ -1,44 +0,0 @@
|
||||
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)}")
|
||||
67
tools/bill.py
Normal file
67
tools/bill.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from langchain_core.tools import tool
|
||||
import requests
|
||||
|
||||
BASE_URL = "https://cxx0822.s.3q.hair/home-api/"
|
||||
USERNAME = "Cxx0822"
|
||||
PASSWORD = "19940822Cxx"
|
||||
|
||||
|
||||
def get_token() -> str:
|
||||
"""
|
||||
调用登录接口,返回 saToken.tokenValue
|
||||
"""
|
||||
params = {
|
||||
"name": USERNAME,
|
||||
"password": PASSWORD
|
||||
}
|
||||
|
||||
try:
|
||||
resp = requests.post(f"{BASE_URL}session", params=params, timeout=10)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
# 按你给的返回结构取值
|
||||
token_value = data["saToken"]["tokenValue"]
|
||||
return token_value
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise RuntimeError(f"登录失败,无法获取 token: {e}")
|
||||
except KeyError as e:
|
||||
raise RuntimeError(f"登录响应结构异常,未找到 tokenValue: {e}")
|
||||
|
||||
|
||||
@tool
|
||||
def bill_search(start_date: str, end_date: str) -> str:
|
||||
"""
|
||||
查询账单列表。
|
||||
|
||||
参数说明:
|
||||
- start_date (必填):
|
||||
- 开始日期,格式:YYYY-MM-DD
|
||||
- 示例:2026-06-01
|
||||
- end_date (必填):
|
||||
- 结束日期,格式:YYYY-MM-DD
|
||||
- 示例:2026-07-01
|
||||
"""
|
||||
|
||||
print(f"start_date: {start_date}, end_date: {end_date}")
|
||||
|
||||
params = {
|
||||
"bookName": "",
|
||||
"type": "",
|
||||
"startDate": start_date,
|
||||
"endDate": end_date
|
||||
}
|
||||
|
||||
token = get_token()
|
||||
|
||||
headers = {
|
||||
"satoken": token
|
||||
}
|
||||
|
||||
try:
|
||||
resp = requests.get(f"{BASE_URL}bill/summary", params=params, headers=headers, timeout=10)
|
||||
resp.raise_for_status()
|
||||
return resp.text
|
||||
except Exception as e:
|
||||
return f"查询账单失败: {e}"
|
||||
Reference in New Issue
Block a user