Initial commit: TradeMate 外贸小助手 MVP

项目结构:
- backend/     Python FastAPI 后端
- uni-app/     uni-app跨端前端
- docs/        设计文档
- docker-compose.yml  Docker编排
- nginx/scripts/systemd 运维配置

已完成功能:
- 用户认证 (JWT)
- 智能翻译 + 回复建议
- 营销素材生成
- 客户管理 + 沉默检测
- 报价单管理
- 产品库管理
- 汇率换算
- 推送通知 (uni-push)
- WhatsApp Webhook框架
- Celery定时任务
This commit is contained in:
TradeMate Dev
2026-05-08 18:17:12 +08:00
commit c6206787da
121 changed files with 11743 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
from typing import Dict, Any, Optional
import json
from openai import AsyncOpenAI
from app.ai.base import AIProvider
SYSTEM_PROMPTS = {
"translate": "You are a professional translator specialized in foreign trade and e-commerce. "
"Accurately translate business terms like MOQ, FOB, CIF, lead time, etc. "
"Return ONLY the translated text, no explanations.",
"reply": "You are an experienced foreign trade sales expert. Write professional, "
"clear business replies. Be concise but warm. Include relevant details "
"naturally. Return ONLY the reply text, no explanations.",
"marketing": "You are a creative copywriter for international trade. Write compelling "
"marketing content that drives action. Adapt to the target audience's culture. "
"Return ONLY the copy, no explanations.",
"extract": "You extract structured data from text. Return ONLY valid JSON matching the requested schema.",
}
class OpenAIProvider(AIProvider):
def __init__(self, api_key: str, model: str = "gpt-4o"):
self.client = AsyncOpenAI(api_key=api_key)
self.model = model
self._name = f"openai-{model}"
self._pricing = {
"gpt-4o": {"input": 0.01, "output": 0.03},
"gpt-4o-mini": {"input": 0.0015, "output": 0.006},
}
self._cheap_model = "gpt-4o-mini" if model == "gpt-4o" else model
async def translate(self, text: str, source_lang: Optional[str], target_lang: str, context: Optional[str] = None) -> Dict[str, Any]:
system = SYSTEM_PROMPTS["translate"]
if context:
system += f"\nContext: this is about {context}"
if source_lang and source_lang != "auto":
system += f"\nSource language: {source_lang}"
content = await self._call(system, f"Translate to {target_lang}:\n\n{text}", model=self._cheap_model)
return {"translated_text": content, "provider": self.name, "model": self.model}
async def reply(self, inquiry: str, context: Optional[Dict[str, Any]] = None, tone: str = "professional") -> Dict[str, Any]:
system = SYSTEM_PROMPTS["reply"] + f"\nTone: {tone}"
context_str = ""
if context:
if context.get("product"):
context_str += f"Product: {context['product']}\n"
if context.get("price"):
context_str += f"Price: {context['price']}\n"
if context.get("customer_history"):
context_str += f"Customer history: {context['customer_history']}\n"
if context.get("conversation_history"):
context_str += f"Previous messages: {context['conversation_history']}\n"
prompt = f"{context_str}\nCustomer inquiry:\n{inquiry}\n\nWrite a reply:"
content = await self._call(system, prompt)
return {"reply": content, "provider": self.name, "model": self.model}
async def generate_marketing(self, product_info: Dict[str, Any], target: str, style: str = "professional", language: str = "en") -> Dict[str, Any]:
system = SYSTEM_PROMPTS["marketing"] + f"\nStyle: {style}\nTarget audience: {target}\nLanguage: {language}"
product_str = json.dumps(product_info, ensure_ascii=False, indent=2)
prompt = f"Product information:\n{product_str}\n\nGenerate marketing copy:"
content = await self._call(system, prompt)
return {"content": content, "provider": self.name, "model": self.model}
async def extract_info(self, text: str, schema: Dict[str, Any]) -> Dict[str, Any]:
system = SYSTEM_PROMPTS["extract"]
schema_str = json.dumps(schema, indent=2)
prompt = f"Schema:\n{schema_str}\n\nText:\n{text}\n\nExtracted JSON:"
content = await self._call(system, prompt, response_format={"type": "json_object"})
try:
data = json.loads(content)
return {"data": data, "confidence": 0.9, "provider": self.name}
except json.JSONDecodeError:
return {"data": {}, "confidence": 0.0, "provider": self.name, "error": "parse_failed"}
async def _call(self, system: str, prompt: str, max_tokens: int = 1000, response_format: Optional[Dict] = None, model: Optional[str] = None) -> str:
kwargs = {
"model": model or self.model,
"messages": [
{"role": "system", "content": system},
{"role": "user", "content": prompt},
],
"max_tokens": max_tokens,
"temperature": 0.7,
}
if response_format:
kwargs["response_format"] = response_format
resp = await self.client.chat.completions.create(**kwargs)
return resp.choices[0].message.content
@property
def name(self) -> str:
return self._name
@property
def cost_per_1k_tokens(self) -> float:
p = self._pricing.get(self.model, {"input": 0.01, "output": 0.03})
return (p["input"] + p["output"]) / 2