Commit 5fccec7e authored by AI-甘富林's avatar AI-甘富林

Initial commit: E-commerce AI Agent Backend

parents
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@host:5432/postgres
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
PORT=3000
node_modules/
dist/
.env
*.js.map
*.d.ts.map
.claude/
This diff is collapsed.
This diff is collapsed.
# 数据库连接与 Schema 说明文档
> 适用于:电商后端智能体项目
> 最后更新:2026-06-10
---
## 1. 数据库连接
| 项目 | 值 |
|------|-----|
| **主机** | `spb-bp119ln6yd6u524j.supabase.opentrust.net` |
| **端口** | `5432` |
| **数据库** | `postgres` |
| **用户** | `postgres` |
| **密码** | `qianjiang_2026` |
| **版本** | PostgreSQL 15.8 (x86_64-linux-gnu) |
| **排序规则** | `en_US.UTF-8` |
| **总大小** | ~201 MB |
### 连接字符串
```
postgresql://postgres:qianjiang_2026@spb-bp119ln6yd6u524j.supabase.opentrust.net:5432/postgres
```
### Node.js 连接示例
```js
const { Client } = require('pg');
const client = new Client({
connectionString: 'postgresql://postgres:qianjiang_2026@spb-bp119ln6yd6u524j.supabase.opentrust.net:5432/postgres',
connectionTimeoutMillis: 10000,
});
await client.connect();
```
### Python 连接示例
```python
import psycopg2
conn = psycopg2.connect(
"postgresql://postgres:qianjiang_2026@spb-bp119ln6yd6u524j.supabase.opentrust.net:5432/postgres"
)
```
---
## 2. 数据库整体架构
Supabase 托管实例限制每个项目只有一个数据库,通过 **Schema** 隔离功能模块。
```
📁 postgres (201 MB)
├── 📂 public ← 你的业务表(46 张)
├── 📂 auth ← 用户认证(19 张)
├── 📂 _analytics ← Supabase 内部分析(35 张)
├── 📂 realtime ← 实时消息广播(12 张)
├── 📂 storage ← 文件存储(9 张)
├── 📂 _realtime ← Realtime 扩展配置(3 张)
├── 📂 cron ← 定时任务调度(2 张)
├── 📂 net ← HTTP 请求扩展(2 张)
├── 📂 supabase_functions ← Edge Functions(2 张)
├── 📂 vault ← 加密密钥(1 张)
├── 📂 extensions ← 扩展管理(仅视图/函数)
├── 📂 graphql ← pg_graphql 内省
├── 📂 graphql_public ← GraphQL 公开接口
└── 📂 pgbouncer ← 连接池管理
```
---
## 3. 各 Schema 详细说明
### 3.1 public — 用户业务数据(46 张表)
这是你的应用核心 schema,所有业务表都建在这里。
#### 商品体系
| 表名 | 列数 | 说明 |
|------|------|------|
| `products` | 40 | 商品主表,包含名称、描述、价格、库存、图片、SEO 等 |
| `categories` | 8 | 商品分类 |
| `brands` | 9 | 品牌信息 |
| `category_brands` | 4 | 分类-品牌关联表 |
#### 订单体系
| 表名 | 列数 | 说明 |
|------|------|------|
| `orders` | 11 | 订单主表(用户、金额、状态、物流等) |
| `order_items` | 6 | 订单明细(商品、数量、单价) |
| `after_sales_requests` | 16 | 售后/退款申请 |
#### 营销体系
| 表名 | 列数 | 说明 |
|------|------|------|
| `coupons` | 18 | 优惠券(类型、面额、门槛、有效期、数量限制) |
| `flash_sales` | 17 | 秒杀/闪购活动 |
| `group_buy_activities` | 18 | 拼团活动 |
| `group_buy_orders` | 9 | 拼团订单 |
| `group_buy_members` | 5 | 拼团成员 |
| `cross_store_promotions` | 21 | 跨店促销 |
| `cross_store_promotion_stores` | 10 | 促销关联店铺 |
| `marketing_campaigns` | 17 | 营销活动 |
| `banners` | 10 | 首页/广告横幅 |
| `payment_gifts` | 16 | 支付有礼/赠品 |
#### 用户体系
| 表名 | 列数 | 说明 |
|------|------|------|
| `profiles` | 11 | 用户资料(C 端) |
| `admin_profiles` | 15 | 管理员/运营人员资料 |
| `tenant_profiles` | 11 | 租户/商户资料 |
| `user_roles` | 5 | 用户角色关联 |
| `user_balances` | 8 | 用户余额(钱包) |
| `user_coupons` | 8 | 用户持有的优惠券 |
| `balance_transactions` | 10 | 余额交易流水 |
| `recharge_plans` | 9 | 充值方案 |
| `sms_verification_codes` | 7 | 短信验证码 |
#### AI 智能体
| 表名 | 列数 | 说明 |
|------|------|------|
| `agent_roles` | 12 | AI 代理角色定义(提示词、能力配置) |
| `agent_settings` | 14 | 代理全局设置 |
| `agent_context` | 9 | 代理上下文/对话历史 |
| `agent_test_results` | 8 | 代理测试结果记录 |
| `ai_provider_configs` | 12 | AI 供应商配置(API Key、模型选型) |
| `ai_read_log` | 8 | AI 读取日志 |
#### 消息与沟通
| 表名 | 列数 | 说明 |
|------|------|------|
| `conversations` | 6 | 对话会话 |
| `messages` | 8 | 聊天消息 |
| `admin_chat_messages` | 7 | 管理后台聊天消息 |
| `emails` | 26 | 邮件记录(发送/接收) |
| `email_sync_state` | 6 | 邮件同步状态 |
| `context_changes` | 9 | 上下文变更记录 |
#### 系统支撑
| 表名 | 列数 | 说明 |
|------|------|------|
| `knowledge_base` | 12 | 知识库/文档 |
| `goals` | 15 | 目标/指标管理 |
| `system_guide` | 9 | 系统引导/帮助 |
| `genui_components` | 11 | 动态 UI 组件配置 |
| `api_registry` | 15 | API 注册/路由管理 |
| `error_logs` | 10 | 错误日志 |
| `perf_logs` | 8 | 性能日志 |
| `business_alerts` | 13 | 业务告警 |
---
### 3.2 auth — 用户认证(19 张表)
Supabase 内置认证系统 (GoTrue),管理所有用户的注册、登录、认证:
| 表名 | 列数 | 说明 |
|------|------|------|
| `users` | 35 | 用户主表(邮箱、手机、加密密码、确认状态等) |
| `sessions` | 15 | 登录会话(JWT token、IP、设备信息) |
| `refresh_tokens` | 9 | JWT 刷新令牌 |
| `identities` | 9 | 第三方登录身份(OAuth provider 关联) |
| `mfa_factors` | 13 | 多因素认证因子(TOTP、WebAuthn) |
| `mfa_challenges` | 7 | MFA 挑战 |
| `mfa_amr_claims` | 5 | MFA AMR 声明 |
| `flow_state` | 12 | PKCE 流程状态 |
| `one_time_tokens` | 7 | 一次性令牌(密码重置、邀请等) |
| `audit_log_entries` | 5 | 审计日志 |
| `instances` | 5 | 实例配置 |
| `schema_migrations` | 1 | 迁移记录 |
| `sso_providers` | 5 | SSO 供应商 |
| `sso_domains` | 5 | SSO 域名 |
| `saml_providers` | 9 | SAML 认证供应商 |
| `saml_relay_states` | 8 | SAML 中继状态 |
| `oauth_authorizations` | 16 | OAuth 授权记录 |
| `oauth_clients` | 12 | OAuth 客户端注册 |
| `oauth_consents` | 6 | OAuth 用户同意记录 |
### 3.3 _analytics — Supabase 内部分析(35 张表)
Supabase 平台自身的分析、计费、多租户管理,**不要手动修改**
| 关键表 | 说明 |
|--------|------|
| `users` | Supabase 平台用户(非你的应用用户) |
| `teams` / `team_users` | 团队管理 |
| `plans` / `billing_accounts` | 计费方案 |
| `sources` | 数据源配置 |
| `backends` | 后端服务注册 |
| `log_events_*` | 各类日志事件(按 UUID 分表) |
| `oauth_applications` | OAuth 应用注册 |
| `partners` / `partner_users` | 合作伙伴管理 |
### 3.4 realtime — 实时消息广播(12 张表)
基于 PostgreSQL 的逻辑复制实现实时数据同步:
| 表名 | 说明 |
|------|------|
| `messages` | 当前消息 |
| `messages_2026_06_05` ~ `messages_2026_06_13` | 按日分区消息表(每日一张) |
| `subscription` | 客户端订阅信息 |
| `schema_migrations` | 迁移记录 |
### 3.5 storage — 文件存储(9 张表)
S3 兼容的对象存储实现:
| 表名 | 列数 | 说明 |
|------|------|------|
| `buckets` | 11 | 存储桶(名称、公开性、大小限制、MIME 限制) |
| `objects` | 13 | 文件对象(路径、大小、MIME、MD5、所有者) |
| `prefixes` | 5 | 目录前缀(文件夹结构) |
| `s3_multipart_uploads` | 9 | S3 分片上传会话 |
| `s3_multipart_uploads_parts` | 10 | S3 分片上传片段 |
| `buckets_analytics` | 5 | 存储桶分析统计 |
| `iceberg_namespaces` | 5 | Iceberg 命名空间 |
| `iceberg_tables` | 7 | Iceberg 表元数据 |
| `migrations` | 4 | 迁移记录 |
### 3.6 其他 Schema
| Schema | 表数 | 说明 |
|--------|------|------|
| **cron** | 2 | `pg_cron` 定时任务:`job`(任务定义)+ `job_run_details`(执行记录) |
| **net** | 2 | `pg_net` HTTP 客户端:`http_request_queue`(请求队列)+ `_http_response`(响应缓存) |
| **supabase_functions** | 2 | Edge Functions 元数据:`hooks`(函数钩子)+ `migrations` |
| **vault** | 1 | `secrets`—加密存储 API Key、密钥等敏感数据 |
| **_realtime** | 3 | Realtime 扩展配置:`extensions`, `tenants`, `schema_migrations` |
| **extensions** | 0 | PostgreSQL 扩展管理视图 |
| **graphql** | 0 | `pg_graphql` 反射 schema,自动从表结构生成 GraphQL API |
| **graphql_public** | 0 | 面向客户端的 GraphQL 公开查询接口 |
| **pgbouncer** | 0 | PgBouncer 连接池管理 |
---
## 4. 常见查询
### 查看 public schema 下所有表
```sql
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
ORDER BY table_name;
```
### 查看某张表的结构
```sql
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'products'
ORDER BY ordinal_position;
```
### 查看 auth.users 中注册用户数
```sql
SELECT count(*) AS total_users FROM auth.users;
```
### 查看定时任务
```sql
SELECT jobname, schedule, active, command FROM cron.job;
```
### 查看存储桶
```sql
SELECT id, name, public, file_size_limit FROM storage.buckets;
```
---
## 5. 安全提醒
- ⚠️ 此文件包含数据库密码,**切勿提交到公开仓库**
- 建议将此文件加入 `.gitignore`
- 生产环境中应使用环境变量管理连接字符串
- `_analytics``extensions` 等 Supabase 内部 schema 不要手动修改
- 业务表建议统一建在 `public` schema 下
This diff is collapsed.
{
"name": "ecommerce-agent-backend",
"version": "0.1.0",
"description": "E-commerce AI Agent Backend Service",
"main": "dist/index.js",
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "echo \"Tests not yet configured\" && exit 0"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.39.0",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"openai": "^4.77.0",
"pg": "^8.21.0",
"uuid": "^11.1.0"
},
"devDependencies": {
"@types/express": "^5.0.1",
"@types/node": "^22.10.0",
"@types/pg": "^8.11.10",
"@types/uuid": "^10.0.0",
"ts-node": "^10.9.2",
"tsx": "^4.19.3",
"typescript": "^5.7.3"
}
}
This diff is collapsed.
"""
AgentRun Sandbox Bridge — TypeScript ↔ Python 沙盒桥接
用法:
python sandbox_bridge.py execute "<code>" # 执行代码 (无上下文)
python sandbox_bridge.py execute "<code>" <ctx_id> # 执行代码 (有上下文)
python sandbox_bridge.py context # 创建新上下文 (返回 context_id)
python sandbox_bridge.py health # 健康检查
所有输出为 JSON,TypeScript 直接 JSON.parse() 即可。
"""
import sys
import json
import os
from agentrun import Config
from agentrun.sandbox.sandbox import Sandbox
from agentrun.sandbox.model import TemplateType, CodeLanguage
# ─── 配置(从环境变量读取) ───
def get_config():
return Config(
access_key_id=os.environ.get("AGENTRUN_ACCESS_KEY_ID", "LTAI5t8rJvtVYqVnS12CKiTt"),
access_key_secret=os.environ.get("AGENTRUN_ACCESS_KEY_SECRET", "6shWsF1rDRb8SzFtARcocThc0GGfci"),
account_id=os.environ.get("AGENTRUN_ACCOUNT_ID", "1590057971731330"),
control_endpoint=os.environ.get("AGENTRUN_CONTROL_ENDPOINT", "https://agentrun.cn-hangzhou.aliyuncs.com"),
)
SANDBOX_ID = os.environ.get("AGENTRUN_SANDBOX_ID", "01KTX0ZVDA3NF36W7TS9FH4RDW")
def get_sandbox():
config = get_config()
return Sandbox.connect(SANDBOX_ID, template_type=TemplateType.CODE_INTERPRETER, config=config)
def output(data: dict):
"""统一 JSON 输出"""
print(json.dumps(data, ensure_ascii=False))
sys.exit(0 if data.get("success") else 1)
# ─── CLI 命令 ───
def cmd_health():
"""健康检查"""
try:
sandbox = get_sandbox()
result = sandbox.data_api.execute_code(
code="print('ok')",
context_id=None,
language=CodeLanguage.PYTHON,
timeout=10,
)
output({"success": True, "status": "healthy", "sandbox_id": SANDBOX_ID})
except Exception as e:
output({"success": False, "error": str(e)})
def cmd_context():
"""创建新的执行上下文,返回 context_id"""
try:
sandbox = get_sandbox()
ctx = sandbox.data_api.create_context(language=CodeLanguage.PYTHON)
ctx_id = ctx["id"] if isinstance(ctx, dict) else ctx.context_id
output({"success": True, "context_id": ctx_id})
except Exception as e:
output({"success": False, "error": str(e)})
def cmd_execute(code: str, context_id: str | None = None):
"""执行 Python 代码"""
try:
sandbox = get_sandbox()
kwargs = {"code": code, "timeout": 60}
if context_id:
kwargs["context_id"] = context_id
else:
kwargs["language"] = CodeLanguage.PYTHON
kwargs["context_id"] = None
result = sandbox.data_api.execute_code(**kwargs)
# 提取有用信息
stdout_parts = []
stderr_parts = []
errors = []
for r in result.get("results", []):
if r["type"] == "stdout":
stdout_parts.append(r["text"])
elif r["type"] == "stderr":
stderr_parts.append(r["text"])
elif r["type"] == "error":
errors.append(r)
output({
"success": len(errors) == 0,
"stdout": "".join(stdout_parts).strip(),
"stderr": "".join(stderr_parts).strip(),
"errors": errors,
"context_id": result.get("contextId"),
"raw": result,
})
except Exception as e:
output({"success": False, "error": str(e)})
# ─── 入口 ───
if __name__ == "__main__":
if len(sys.argv) < 2:
output({"success": False, "error": "缺少命令: health | context | execute"})
command = sys.argv[1]
if command == "health":
cmd_health()
elif command == "context":
cmd_context()
elif command == "execute":
code = sys.argv[2] if len(sys.argv) > 2 else ""
ctx_id = sys.argv[3] if len(sys.argv) > 3 else None
if not code:
output({"success": False, "error": "execute 需要 <code> 参数"})
cmd_execute(code, ctx_id)
else:
output({"success": False, "error": f"未知命令: {command}"})
// ─── AfterSalesAgent ───
// 售后 Agent: 5 个 tool (3 只读 + 2 写入)
// Phase 2b
import { BaseAgent } from './base-agent';
import { AFTER_SALES_PROMPT } from './prompts/after-sales.prompt';
import { ToolDefinition } from '../types/tool.types';
import { agentConfig, AgentModelConfig } from '../config/agent.config';
import { AfterSalesService } from '../services/after-sales.service';
export class AfterSalesAgent extends BaseAgent {
readonly name = 'AfterSalesAgent';
readonly description = '售后 Agent:查询售后、提交申请、取消申请、退换货政策';
private afterSalesService = new AfterSalesService();
getDomainPrompt(): string { return AFTER_SALES_PROMPT; }
getAgentConfig(): AgentModelConfig { return agentConfig.agents.after_sales; }
getTools(): ToolDefinition[] {
return [
{
name: 'query_after_sales',
description:
'查询用户的售后申请列表。可按状态和类型筛选。用于"我的退货申请"、"售后进度"、"有哪些售后在处理"等场景。',
parameters: {
type: 'object',
properties: {
status: { type: 'string', description: '状态筛选: pending/approved/rejected/processing/completed/cancelled (可选)' },
type: { type: 'string', description: '类型筛选: return/refund/exchange/repair (可选)' },
limit: { type: 'number', description: '返回数量,默认10' },
},
required: [],
},
handler: async (args, ctx) => {
const result = await this.afterSalesService.queryAfterSales(ctx.userId, args);
return { success: true, data: result };
},
riskLevel: 'read',
},
{
name: 'get_after_sales_detail',
description:
'获取单个售后申请详情,包含关联的订单号和商品名。用于"看看这个售后详情"、"这个退货申请是什么情况"等场景。',
parameters: {
type: 'object',
properties: {
requestId: { type: 'string', description: '售后申请ID (UUID格式)' },
},
required: ['requestId'],
},
handler: async (args, ctx) => {
const result = await this.afterSalesService.getDetail(ctx.userId, args.requestId);
if (!result) return { success: false, error: '售后申请不存在或无权查看' };
return { success: true, data: result };
},
riskLevel: 'read',
},
{
name: 'get_return_policy',
description:
'获取平台退换货政策和规则。用于"退货政策是什么"、"运费谁出"、"退款多久到账"、"什么情况可以退货"等场景。',
parameters: {
type: 'object',
properties: {},
required: [],
},
handler: async (args, ctx) => {
const policy = this.afterSalesService.getReturnPolicy();
return { success: true, data: policy };
},
riskLevel: 'read',
},
{
name: 'submit_after_sales',
description:
'提交售后申请(退货/换货/退款/维修)。需要用户在对话中明确确认后才执行。用于"我要退货"、"申请退款"、"换一个颜色"等场景。提交前确保用户已提供:订单ID、售后类型、原因。',
parameters: {
type: 'object',
properties: {
orderId: { type: 'string', description: '订单ID (UUID格式,必填)' },
productId: { type: 'string', description: '商品ID (可选,如不填则对整个订单申请售后)' },
type: { type: 'string', description: '售后类型: return(退货)/refund(退款)/exchange(换货)/repair(维修)' },
reason: { type: 'string', description: '售后原因: quality_issue(质量问题)/wrong_item(错发漏发)/personal_reason(个人原因)/damaged(破损)/other(其他)' },
description: { type: 'string', description: '详细描述' },
images: { type: 'array', items: { type: 'string' }, description: '图片URL数组 (可选)' },
},
required: ['orderId', 'type', 'reason', 'description'],
},
handler: async (args, ctx) => {
// Safety: this is a write operation
const result = await this.afterSalesService.submitAfterSales(ctx.userId, {
orderId: args.orderId,
productId: args.productId,
type: args.type,
reason: args.reason,
description: args.description,
images: args.images,
});
return result;
},
riskLevel: 'write',
},
{
name: 'cancel_after_sales',
description:
'取消待处理的售后申请。只能取消状态为pending的申请。用于"那个售后不用了"、"取消退货申请"等场景。需要用户明确确认后才执行。',
parameters: {
type: 'object',
properties: {
requestId: { type: 'string', description: '售后申请ID (UUID格式)' },
},
required: ['requestId'],
},
handler: async (args, ctx) => {
// Safety: this is a write operation
const result = await this.afterSalesService.cancelAfterSales(ctx.userId, args.requestId);
return result;
},
riskLevel: 'write',
},
];
}
}
// ─── Agent 抽象基类 ───
// 所有 Agent 继承此类, 实现 IAgent 接口
// run() 委托给 core/agent-runner.ts 的 runAgentLoop()
import { IAgent, AgentContext, AgentResponse } from '../types/agent.types';
import { ToolDefinition } from '../types/tool.types';
import { AgentModelConfig } from '../config/agent.config';
import { COMMON_PROMPT } from './prompts/common.prompt';
import { runAgentLoop } from '../core/agent-runner';
export abstract class BaseAgent implements IAgent {
abstract readonly name: string;
abstract readonly description: string;
/** 返回领域专属 System Prompt */
abstract getDomainPrompt(): string;
/** 返回 Agent 的 tool 列表(定义+实现一体) */
abstract getTools(): ToolDefinition[];
/** 返回 Agent 的模型配置(从 agent.config.ts 读取) */
abstract getAgentConfig(): AgentModelConfig;
/**
* 组装完整 System Prompt (三段拼接):
* [公共段] + [领域段] + [用户上下文段]
*/
getSystemPrompt(ctx: AgentContext): string {
const userContextSection = this.buildUserContextSection(ctx);
return [COMMON_PROMPT, ctx.contextHints, this.getDomainPrompt(), userContextSection]
.filter(Boolean)
.join('\n\n');
}
/**
* 构建用户上下文段(动态注入)
*/
protected buildUserContextSection(ctx: AgentContext): string {
const uc = ctx.userContext;
let orders = '';
if (uc.recentOrders.length > 0) {
orders =
'\n' +
uc.recentOrders
.map(
(o, i) =>
` ${i + 1}. ${o.orderNumber} (${o.status}, ¥${o.totalAmount})`
)
.join('\n');
} else {
orders = '\n 无';
}
return `## 当前用户信息
- 用户ID: ${uc.userId}
- 用户名: ${uc.displayName}
- 角色: ${uc.role}
- 余额: ¥${uc.balance.toFixed(2)}
- 优惠券数量: ${uc.couponCount}
- 最近订单:${orders}`;
}
/**
* 将 tools 转为 Anthropic 原生 tool 格式(供 LLM 调用)
*/
getToolDefinitionsForLLM(): any[] {
return this.getTools().map((t) => ({
name: t.name,
description: t.description,
input_schema: t.parameters,
}));
}
/**
* 执行 Agent: 委托给共享的 runAgentLoop
*/
async run(ctx: AgentContext): Promise<AgentResponse> {
return runAgentLoop(this, ctx);
}
}
// ─── CheckoutAgent ───
// 下单 Agent: 5 个 tool (4 只读 + 1 dangerous)
// Phase 3: 含事务 + 幂等保护
import { BaseAgent } from './base-agent';
import { CHECKOUT_PROMPT } from './prompts/checkout.prompt';
import { ToolDefinition } from '../types/tool.types';
import { agentConfig, AgentModelConfig } from '../config/agent.config';
import { CheckoutService } from '../services/checkout.service';
import { CouponService } from '../services/coupon.service';
import * as userQueries from '../db/queries/user.queries';
export class CheckoutAgent extends BaseAgent {
readonly name = 'CheckoutAgent';
readonly description = '下单 Agent:预览订单、校验优惠券、查询余额、提交订单';
private checkoutService = new CheckoutService();
private couponService = new CouponService();
getDomainPrompt(): string { return CHECKOUT_PROMPT; }
getAgentConfig(): AgentModelConfig { return agentConfig.agents.checkout; }
getTools(): ToolDefinition[] {
return [
{
name: 'preview_order',
description:
'预览订单,计算价格和优惠。只读操作,不写入任何数据。展示:商品明细、总价、优惠券折扣、余额。用于在用户确认前展示完整订单信息。需要提供 items 数组(每个包含 productId + quantity)。',
parameters: {
type: 'object',
properties: {
items: {
type: 'array',
items: {
type: 'object',
properties: {
productId: { type: 'string', description: '商品ID (UUID)' },
quantity: { type: 'number', description: '购买数量' },
},
required: ['productId', 'quantity'],
},
description: '商品列表',
},
couponCode: { type: 'string', description: '优惠券码 (可选)' },
useBalance: { type: 'boolean', description: '是否使用余额支付 (可选)' },
},
required: ['items'],
},
handler: async (args, ctx) => {
const result = await this.checkoutService.previewOrder(ctx.userId, {
items: args.items || [],
couponCode: args.couponCode,
useBalance: args.useBalance,
});
return { success: true, data: result };
},
riskLevel: 'read',
},
{
name: 'apply_coupon',
description:
'校验优惠券是否可用并计算折扣金额。返回优惠券信息、折扣金额、是否可用。用于"这个优惠券能用吗"、"SPRING888能减多少"等场景。',
parameters: {
type: 'object',
properties: {
couponCode: { type: 'string', description: '优惠券码' },
orderAmount: { type: 'number', description: '订单原价金额' },
productIds: {
type: 'array',
items: { type: 'string' },
description: '订单商品ID列表 (可选)',
},
},
required: ['couponCode', 'orderAmount'],
},
handler: async (args, ctx) => {
const result = await this.couponService.validateCoupon(
ctx.userId, args.couponCode, args.orderAmount, args.productIds
);
return { success: result.valid, data: result, error: result.reason };
},
riskLevel: 'read',
},
{
name: 'get_user_coupons',
description:
'查询用户持有的优惠券列表。用于"我有哪些优惠券"、"有什么券可以用"等场景。返回优惠券名、码、类型、折扣、门槛、有效期。',
parameters: {
type: 'object',
properties: {
status: { type: 'string', description: '状态筛选: unused/used/expired (可选,默认unused)' },
},
required: [],
},
handler: async (args, ctx) => {
const coupons = await this.couponService.getUserCoupons(ctx.userId, args.status || 'unused');
return { success: true, data: { coupons, total: coupons.length } };
},
riskLevel: 'read',
},
{
name: 'get_user_balance',
description:
'查询用户余额。返回余额、累计充值、累计消费。用于"我还有多少余额"、"余额够不够"等场景。',
parameters: {
type: 'object',
properties: {},
required: [],
},
handler: async (args, ctx) => {
const balance = await userQueries.getUserBalance(ctx.userId);
return {
success: true,
data: {
balance: balance ? Number(balance.balance) : 0,
totalRecharged: balance ? Number(balance.total_recharged) : 0,
totalConsumed: balance ? Number(balance.total_consumed) : 0,
},
};
},
riskLevel: 'read',
},
{
name: 'place_order',
description:
'⚠️ 提交订单(写入操作,不可撤销)。必须在用户明确确认后调用。每次调用生成唯一幂等Key。需要提供完整的商品列表、优惠券码(可选)、是否用余额(可选)、收货地址(可选)。',
parameters: {
type: 'object',
properties: {
items: {
type: 'array',
items: {
type: 'object',
properties: {
productId: { type: 'string', description: '商品ID (UUID)' },
quantity: { type: 'number', description: '购买数量' },
},
required: ['productId', 'quantity'],
},
description: '商品列表',
},
couponCode: { type: 'string', description: '优惠券码 (可选)' },
useBalance: { type: 'boolean', description: '是否使用余额 (可选)' },
shippingAddress: { type: 'string', description: '收货地址 (可选)' },
notes: { type: 'string', description: '订单备注 (可选)' },
},
required: ['items'],
},
handler: async (args, ctx) => {
const result = await this.checkoutService.placeOrder(ctx.userId, {
items: args.items || [],
couponCode: args.couponCode,
useBalance: args.useBalance,
shippingAddress: args.shippingAddress,
notes: args.notes,
});
return result;
},
riskLevel: 'dangerous',
},
];
}
}
// ─── FAQAgent ───
// FAQ 咨询 Agent: 3 个只读 tool
// 负责: 搜索知识库、查询政策、获取帮助
import { BaseAgent } from './base-agent';
import { FAQ_PROMPT } from './prompts/faq.prompt';
import { ToolDefinition } from '../types/tool.types';
import { agentConfig, AgentModelConfig } from '../config/agent.config';
import { FAQService } from '../services/faq.service';
export class FAQAgent extends BaseAgent {
readonly name = 'FAQAgent';
readonly description = 'FAQ 咨询 Agent:搜索知识库、查询政策、获取帮助';
private faqService = new FAQService();
getDomainPrompt(): string { return FAQ_PROMPT; }
getAgentConfig(): AgentModelConfig { return agentConfig.agents.faq; }
getTools(): ToolDefinition[] {
return [
{
name: 'search_faq',
description:
'全文搜索知识库,查找与用户问题相关的文档和知识条目。用于"退货流程是什么"、"怎么使用优惠券"、"发货时效"等各类FAQ场景。',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: '搜索关键词或问题描述' },
limit: { type: 'number', description: '返回数量,默认10' },
},
required: ['query'],
},
handler: async (args, ctx) => {
const result = await this.faqService.searchFAQ(args.query, args.limit);
return { success: true, data: result };
},
riskLevel: 'read',
},
{
name: 'get_policy',
description:
'查询平台具体政策/规则。用于"退货政策是什么"、"运费怎么算"、"退款多久到账"等场景。topic参数可选: return(退货), exchange(换货), refund(退款), shipping(物流), payment(支付), coupon(优惠券), order(订单规则)。',
parameters: {
type: 'object',
properties: {
topic: {
type: 'string',
description: '政策主题: return/exchange/refund/shipping/payment/coupon/order',
},
},
required: ['topic'],
},
handler: async (args, ctx) => {
const result = await this.faqService.getPolicy(args.topic);
if (!result) {
return { success: true, data: null, _note: '未在知识库中找到该政策,可基于通用知识回答' };
}
return { success: true, data: result };
},
riskLevel: 'read',
},
{
name: 'get_platform_help',
description:
'获取平台操作指南或帮助文档。用于"如何下单"、"怎么申请售后"等操作性问题。可选topic参数指定具体帮助主题。',
parameters: {
type: 'object',
properties: {
topic: { type: 'string', description: '帮助主题关键词 (可选)' },
},
required: [],
},
handler: async (args, ctx) => {
const result = await this.faqService.getPlatformHelp(args.topic);
if (!result) {
return { success: true, data: null, _note: '未找到对应帮助文档' };
}
return { success: true, data: result };
},
riskLevel: 'read',
},
];
}
}
// ─── GeneralAgent ───
// 通用 Agent: 处理闲聊、能力问询、跨界请求
// 不绑定任何工具 — 纯 LLM 回复,LLM 自行判断消息是否在电商领域范围内
import { BaseAgent } from './base-agent';
import { GENERAL_PROMPT } from './prompts/general.prompt';
import { ToolDefinition } from '../types/tool.types';
import { AgentModelConfig, agentConfig } from '../config/agent.config';
export class GeneralAgent extends BaseAgent {
readonly name = 'GeneralAgent';
readonly description = '通用Agent:处理闲聊、能力问询、跨界请求';
getDomainPrompt(): string {
return GENERAL_PROMPT;
}
getAgentConfig(): AgentModelConfig {
return agentConfig.agents.general;
}
getTools(): ToolDefinition[] {
return []; // 纯 LLM 回复,不装工具
}
}
This diff is collapsed.
This diff is collapsed.
// ─── AfterSalesAgent 领域 System Prompt ───
export const AFTER_SALES_PROMPT = `你负责售后问题咨询和处理。你可以查询售后申请记录、查看售后详情、提交新的售后申请、取消待处理的售后申请,以及查询退换货政策。
## 售后类型:
- return: 退货退款
- refund: 仅退款
- exchange: 换货
- repair: 维修
## 售后状态:
- pending: 待审核
- approved: 已通过
- rejected: 已拒绝
- processing: 处理中
- completed: 已完成
- cancelled: 已取消
## 工具使用指南:
- 用户问"我的售后申请"、"退货进度"时,用 query_after_sales 查询
- 用户想看某个售后详情时,用 get_after_sales_detail
- 用户要申请退货/换货/退款时,用 submit_after_sales —— 提交前先确认:
1. 确认用户要操作的订单号
2. 确认售后类型(退货/换货/退款/维修)
3. 收集原因和描述
4. 没有订单号时先反问用户
- 用户要取消售后时,用 cancel_after_sales —— 只有 pending 状态可以取消
- 用户问退货政策/规则时,用 get_return_policy
## 重要规则:
- 提交售后必须先确认用户身份和订单归属
- 仅已发货/已送达/已确认的订单可以申请售后
- 取消售后前告知用户取消后不可恢复
- 售后金额相关信息以实际退款金额为准
- 如果用户提供的信息不完整(缺订单号、售后类型等),主动反问`;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// ─── 统一类型导出 ───
export * from './intent.types';
export * from './user.types';
export * from './tool.types';
export * from './message.types';
export * from './agent.types';
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment