zhouhui.jiang

update 动态TOKEN

......@@ -21,6 +21,7 @@ def upload_clearance_file(
pdf_path: str,
file_index: int = 0,
uid: Optional[int] = None,
Authorization: Optional[str] = None,
) -> str:
"""根据运单ID上传清关文件(PDF)。
......@@ -30,6 +31,7 @@ def upload_clearance_file(
pdf_path: 本地 PDF 文件路径(必填)
file_index: 文件索引,默认 0
uid: 文件 uid,可不传,默认使用时间戳生成
Authorization: 授权令牌,如果提供则使用该值,否则使用 API_CONFIG 中的默认值
Returns:
文本:成功/失败信息。
......@@ -71,8 +73,11 @@ def upload_clearance_file(
"fileUpload": (os.path.basename(pdf_path), open(pdf_path, "rb"), "application/pdf"),
}
# 复制 headers,并移除 Content-Type(由 requests 根据 multipart 自动设置)
# 构建 headers,优先使用传入的 Authorization
headers = dict(API_CONFIG.get("headers", {}))
if Authorization:
headers['Authorization'] = Authorization
# 移除 Content-Type(由 requests 根据 multipart 自动设置)
headers.pop("Content-Type", None)
try:
......
......@@ -9,11 +9,12 @@ import json
from .api_config import API_CONFIG
def create_waybill_d(consignmentCode: str) -> str:
def create_waybill_d(consignmentCode: str, Authorization: str = None) -> str:
"""根据运单号创建D类运单
Args:
consignmentCode: 运单号(必填)
Authorization: 授权令牌,如果提供则使用该值,否则使用 API_CONFIG 中的默认值
Returns:
创建结果信息
......@@ -28,10 +29,15 @@ def create_waybill_d(consignmentCode: str) -> str:
"isAIRecognition": 0
}
# 构建 headers,优先使用传入的 Authorization
headers = dict(API_CONFIG['headers'])
if Authorization:
headers['Authorization'] = Authorization
# 发送POST请求
response = requests.post(
url,
headers=API_CONFIG['headers'],
headers=headers,
json=data,
timeout=30
)
......@@ -74,7 +80,7 @@ def create_waybill_d(consignmentCode: str) -> str:
try:
head_resp = requests.post(
head_url,
headers=API_CONFIG["headers"],
headers=headers,
json={"id": waybill_id},
timeout=30,
)
......@@ -97,7 +103,7 @@ def create_waybill_d(consignmentCode: str) -> str:
}
detail_resp = requests.post(
detail_url,
headers=API_CONFIG["headers"],
headers=headers,
json=detail_payload,
timeout=30,
)
......@@ -127,12 +133,13 @@ def create_waybill_d(consignmentCode: str) -> str:
return f"❌ 创建D类运单失败: {str(e)}"
def query_waybill_list(initialize: int = 1, consStatusName: str = "等待录入") -> str:
def query_waybill_list(initialize: int = 1, consStatusName: str = "等待录入", Authorization: str = None) -> str:
"""查询运单列表信息
Args:
initialize: 初始化标志,默认为1
consStatusName: 运单状态名称,默认为"等待录入"
Authorization: 授权令牌,如果提供则使用该值,否则使用 API_CONFIG 中的默认值
Returns:
运单查询结果
......@@ -154,10 +161,15 @@ def query_waybill_list(initialize: int = 1, consStatusName: str = "等待录入"
"total": 0
}
# 构建 headers,优先使用传入的 Authorization
headers = dict(API_CONFIG['headers'])
if Authorization:
headers['Authorization'] = Authorization
# 发送POST请求
response = requests.post(
url,
headers=API_CONFIG['headers'],
headers=headers,
json=data,
timeout=30
)
......@@ -331,11 +343,12 @@ def get_waybill_detail_list(
return f"查询表体明细失败: {str(e)}"
def push_waybill_for_ocr(waybill_id: int) -> str:
def push_waybill_for_ocr(waybill_id: int, Authorization: str = None) -> str:
"""根据运单ID推送OCR进行识别
Args:
waybill_id: 运单ID(必填)
Authorization: 授权令牌,如果提供则使用该值,否则使用 API_CONFIG 中的默认值
Returns:
推送结果信息
......@@ -348,10 +361,15 @@ def push_waybill_for_ocr(waybill_id: int) -> str:
"id": waybill_id
}
# 构建 headers,优先使用传入的 Authorization
headers = dict(API_CONFIG['headers'])
if Authorization:
headers['Authorization'] = Authorization
# 发送POST请求
response = requests.post(
url,
headers=API_CONFIG['headers'],
headers=headers,
json=data,
timeout=30
)
......
......@@ -3,7 +3,10 @@ from langchain_openai import ChatOpenAI
import os
import sys
import json
from typing import Dict, Any
from typing import Dict, Any, List, Optional
from contextvars import ContextVar
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import AnyMessage
# 添加项目根目录到 Python 路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
......@@ -87,17 +90,99 @@ def pre_model_inspect_attachments(state, **kwargs):
traceback.print_exc()
return {}
# 创建 ReAct 智能体
agent = create_react_agent(
model=model,
tools=[query_waybill_list, create_waybill_d, upload_clearance_file, push_waybill_for_ocr],
pre_model_hook=pre_model_inspect_attachments,
prompt="""你是一个专业的出口物流系统智能助手,专门帮助用户处理运单相关的业务操作。
def extract_token(state: Dict[str, Any]) -> str:
"""
从 state 中提取 token
获取最后一个类型为 HumanMessage 或 human 的消息中的 token
Args:
state: LangGraph 状态字典,包含 messages 数组
Returns:
token 字符串,如果未找到则返回空字符串
"""
messages = state.get("messages", [])
if not messages:
return ""
# 找到所有 is_human 类型的消息
human_messages = []
for msg in messages:
# 兼容 dict 或 LangChain 的消息对象
if isinstance(msg, dict):
msg_type = msg.get("type")
else:
msg_type = msg.__class__.__name__
# 检查是否是 human 类型的消息
is_human = (msg_type == "HumanMessage" or msg_type == "human")
if is_human:
human_messages.append(msg)
# 如果没有 human 消息,直接返回
if not human_messages:
return ""
# 直接取最后一个 human 消息(不需要循环判断)
last_human_msg = human_messages[-1]
# 从 content 中提取 token
if isinstance(last_human_msg, dict):
content = last_human_msg.get("content")
else:
content = getattr(last_human_msg, "content", None)
if isinstance(content, list):
# content 是列表,遍历查找包含 token 的 part
for part in content:
if isinstance(part, dict) and "token" in part:
token = part.get("token")
if token:
return token
elif isinstance(content, dict):
# content 是字典,直接获取 token
if "token" in content:
token = content.get("token")
if token:
return token
return ""
def _create_system_prompt(state: Dict[str, Any], config: RunnableConfig) -> List[AnyMessage]:
"""
创建动态系统提示词
Args:
state: LangGraph 状态字典
config: Runnable 配置
Returns:
包含系统消息和原始消息的列表
"""
# 添加调试信息,确认函数被调用
# print("\n=== _create_system_prompt 被调用 ===")
# print(f"state type: {type(state)}")
# print(f"state keys: {list(state.keys()) if isinstance(state, dict) else 'not a dict'}")
# 从 state 中提取动态参数
token = extract_token(state)
# 如果从 state 中提取的 token 为空,则从 api_config.py 中获取 Authorization 作为备选
if not token:
from API.api_config import API_CONFIG
token = API_CONFIG.get("headers", {}).get("Authorization", "")
#
# print(f"提取到的 token: {token[:30] if token else 'None'}...")
# 创建系统提示词(使用 f-string 以便插入 token)
system_msg = f"""你是一个专业的出口物流系统智能助手,专门帮助用户处理运单相关的业务操作。
## 你的主要职责:
1. **运单查询**:根据用户需求查询运单列表,支持按状态、时间等条件筛选,结果以JSON形式展示,AI不用对返回数据JSON进行加工
2. **运单创建**:协助用户创建D类运单,确保信息完整准确
4. **业务咨询**:解答用户关于出口物流流程、运单状态、操作规范等问题
3. **业务咨询**:解答用户关于出口物流流程、运单状态、操作规范等问题
4. **您当前访问工具Authorization的传参为 Authorization= {token}
## 工作原则:
- 上传时文件路径不用确认,路径肯定是完整的,文件肯定是存在的,不用确认文件索引,工具会自动设置索引为0,请直接调用工具接口
......@@ -107,13 +192,13 @@ agent = create_react_agent(
- 如遇到错误,主动分析原因并提供解决方案
- 保持专业、友好的沟通态度
- 严禁改写工具函数返回的文本格式;对工具输出仅直接转述,不得增删前后缀或改写内容。
- 若调用了工具并获得结果,则必须将该工具返回的文本“原样作为最终答复”输出,不允许添加任何解释、建议或额外文字。
- 若调用了工具并获得结果,则必须将该工具返回的文本"原样作为最终答复"输出,不允许添加任何解释、建议或额外文字。
## 可用工具:
- query_waybill_list: 查询运单列表,支持按状态筛选,结果以JSON形式展示,AI不用对返回数据JSON进行加工
- create_waybill_d: 根据运单号创建D类运单,需要提供运单号参数
- upload_clearance_file: 上传清关PDF文件,需要 code、slip_id、pdf_path,上传时文件路径不用确认,路径肯定是完整的,文件肯定是存在的,不用确认文件索引,工具会自动设置索引为0,请直接调用工具接口
- push_waybill_for_ocr: 根据运单ID推送OCR进行识别,需要提供运单ID(waybill_id)参数
- query_waybill_list: 查询运单列表,支持按状态筛选,需提供Authorization,结果以JSON形式展示,AI不用对返回数据JSON进行加工
- create_waybill_d: 根据运单号创建D类运单,需要提供参数(运单号、Authorization)
- upload_clearance_file: 上传清关PDF文件,需要 code、slip_id、pdf_path、Authorization,上传时文件路径不用确认,路径肯定是完整的,文件肯定是存在的,不用确认文件索引,工具会自动设置索引为0,请直接调用工具接口
- push_waybill_for_ocr: 根据运单ID推送OCR进行识别,需要提供运单ID(waybill_id、Authorization)参数
## query_waybill_list数据展示说明:
- 运单查询结果会自动格式化为JSON形式展示,包含:运单号、运单类型、运单状态、发件人、运单日期
......@@ -122,7 +207,22 @@ agent = create_react_agent(
## create_waybill_d数据展示说明:
- 按照数据返回的原本格式进行展示,不得增删前后缀或改写内容。
请根据用户的具体需求,选择合适的工具并提供帮助。"""
# 返回系统消息 + 原始消息
result = [{"role": "system", "content": system_msg}] + state.get("messages", [])
print(f"返回消息数量: {len(result)}")
print("=== _create_system_prompt 执行完成 ===\n")
return result
# 创建 ReAct 智能体
agent = create_react_agent(
model=model,
tools=[query_waybill_list, create_waybill_d, upload_clearance_file, push_waybill_for_ocr],
pre_model_hook=pre_model_inspect_attachments,
prompt=_create_system_prompt,
)
# 如果直接运行此文件
......