内容
# 商业交换
商业交换是一个B2B集成平台,合作伙伴可以在这里注册、互相发现、订阅数据流,并跨多种格式(如JSON、XML、CSV和EDI)交换商业消息。
该平台使用人工智能驱动的映射引擎,将合作伙伴的有效载荷标准化为规范数据模型(CDM),并将其重塑为每个接收者的首选格式。
合作伙伴可以通过两种方式连接到平台:
- **人工门户路径** — 合作伙伴通过网页界面自行注册,配置模式和网络钩,手动管理集成
- **代理到代理路径** — 合作伙伴的自主人工智能代理(连接到其内部ERP、CRM或WMS)可以使用[A2A协议](https://google.github.io/A2A)和[模型上下文协议(MCP)](https://modelcontextprotocol.io)自主发现、协商并建立与平台的完整集成。双方无需人工干预。
## 本仓库包含的内容
这是一个使用Turborepo的单体仓库,包含:
- `apps/`下的后端微服务
- `apps/partner-portal`下的Next.js合作伙伴/管理员门户
- `packages/`下的共享包
- `infra/`下的本地基础设施和部署资产
## 目录
- [架构](#架构)
- [核心消息流](#核心消息流)
- [合作伙伴集成路径](#合作伙伴集成路径)
- [服务和端口](#服务和端口)
- [仓库布局](#仓库布局)
- [快速入门](#快速入门)
- [环境配置](#环境配置)
- [开发工作流](#开发工作流)
- [代理协调器](#代理协调器)
- [代理到代理集成](#代理到代理集成)
- [人工智能映射和可见性模型](#人工智能映射和可见性模型)
- [部署](#部署)
- [演示模式](#演示模式)
- [操作说明](#操作说明)
- [测试A2A/MCP集成](#测试A2A/MCP集成)
- [故障排除](#故障排除)
## 架构
所有外部流量都通过API网关进入。网关验证JWT,应用速率限制,并将请求反向代理到下游服务。
```mermaid
graph TB
subgraph Clients["客户端"]
PP["🖥️ 合作伙伴门户<br/>:3100"]
PA["🤖 合作伙伴AI代理<br/>(外部)"]
end
subgraph Gateway["API网关 :3000"]
GW["网关<br/>JWT认证 · 速率限制 · 反向代理"]
end
subgraph CoreServices["核心服务"]
AUTH["认证服务<br/>:3001"]
PS["合作伙伴服务<br/>:3002"]
SS["订阅服务<br/>:3003"]
IS["集成服务<br/>:3004"]
ME["映射引擎<br/>:3005"]
AO["代理协调器<br/>:3006"]
BS["计费服务<br/>:3007"]
EA["交换代理<br/>:3008"]
end
subgraph Data["数据"]
PG[("PostgreSQL<br/>:5432")]
LLM["☁️ LLM提供商<br/>Azure / OpenAI / 兼容"]
end
PP -->|JWT / HTTPS| GW
PA -->|A2A · MCP · x-api-key| GW
GW --> AUTH
GW --> PS
GW --> SS
GW --> IS
GW --> ME
GW --> AO
GW --> BS
GW -->|"/.well-known /a2a /mcp"| EA
EA -->|注册合作伙伴| PS
EA -->|推断模式| ME
EA -->|订阅| SS
EA -->|配置网络钩| IS
AUTH & PS & SS & IS & ME & AO & BS & EA --> PG
ME -->|模式推断| LLM
EA -->|协商| LLM
```
平台围绕几个主要思想设计:
- 合作伙伴自行注册并维护自己的集成设置
- 订阅定义哪些合作伙伴可以交换消息
- 集成服务处理消息路由和网络钩交付
- 映射引擎通过内部CDM转换合作伙伴特定的有效载荷
- 代理协调器执行重试、监控、漂移检测和警报任务
- 交换代理使合作伙伴AI代理能够自主协商并通过A2A和MCP建立集成,无需人工干预
## 核心消息流
当合作伙伴发送消息时,高级路径如下:
```mermaid
sequenceDiagram
participant S as 发送方合作伙伴
participant GW as API网关
participant IS as 集成服务
participant ME as 映射引擎
participant R as 接收方网络钩
participant AO as 代理协调器
S->>GW: POST /api/integrations/send<br/>(JWT + 有效载荷)
GW->>GW: 验证JWT,注入x-partner-id
GW->>IS: 转发请求
IS->>IS: 验证活动订阅<br/>发送方 ↔ 接收方
IS->>ME: 转换有效载荷<br/>发送方格式 → CDM → 接收方格式
ME-->>IS: 映射有效载荷(或回退)
IS->>IS: 存储消息(状态:处理中)
IS->>R: POST到接收方网络钩
alt 交付成功
R-->>IS: 2xx
IS->>IS: 状态 → 已交付
else 交付失败
R-->>IS: 错误 / 超时
IS->>IS: 状态 → 失败
AO->>IS: 重试代理接管(每2分钟)
AO->>R: 重试并退避
end
```
### 公共路由
这些路由不需要JWT认证:
- `GET /api/partners/platform-branding`
- `POST /api/partners`
- `POST /api/auth/login`
- `POST /api/auth/refresh`
- `POST /api/auth/token`
- `GET /.well-known/agent.json` — 交换代理发现卡(A2A,无需认证)
A2A任务执行(`POST /a2a/tasks`,`/mcp`)需要一个API密钥作为`x-api-key`传递。
## 合作伙伴集成路径
合作伙伴可以通过两条独立的路径连接到平台。两条路径都完全受支持,可以共存。
### 人工门户路径
```mermaid
flowchart LR
A["合作伙伴<br/>在门户注册"] --> B["管理员<br/>审查和批准"]
B --> C["合作伙伴配置<br/>模式和网络钩<br/>在设置中"]
C --> D["合作伙伴发送和<br/>接收消息<br/>通过网关API"]
```
1. 合作伙伴导航到门户并自行注册(`POST /api/partners`)。
2. 管理员审查和批准申请。
3. 合作伙伴通过UI配置模式、映射规则和网络钩交付设置。
4. 合作伙伴通过网关API发送和接收消息。
### 代理到代理路径
```mermaid
sequenceDiagram
participant PA as 合作伙伴AI代理
participant GW as 网关
participant EA as 交换代理
participant ME as 映射引擎
participant PS as 合作伙伴服务
PA->>GW: GET /.well-known/agent.json
GW-->>PA: 代理卡(技能,认证,端点)
PA->>GW: POST /a2a/tasks<br/>x-api-key · "我们生成JSON订单..."
GW->>EA: 转发
EA->>EA: 创建会话(状态:发现)
EA-->>PA: 任务已接受 · 状态:工作中<br/>"请发送示例有效载荷"
PA->>GW: POST /a2a/tasks/:id/send<br/>{示例有效载荷}
GW->>EA: 转发
EA->>ME: 推断模式 + 映射规则
ME-->>EA: 映射规则(置信度分数)
EA-->>PA: 状态:输入所需<br/>"提议映射。确认并提供网络钩URL"
PA->>GW: POST /a2a/tasks/:id/send<br/>{接受:真,网络钩URL}
GW->>EA: 转发
EA->>PS: 注册合作伙伴
PS-->>EA: 合作伙伴ID + 凭证
EA-->>PA: 状态:已完成<br/>{合作伙伴ID,API密钥,订阅}
```
合作伙伴的AI代理(在其自己的基础设施中运行,连接到其ERP、CRM或WMS)可以通过机器对机器的协议自主完成相同的上线步骤:
1. 合作伙伴代理通过`GET /.well-known/agent.json`发现交换代理。
2. 合作伙伴代理发送一个任务到`POST /a2a/tasks`,描述集成意图。
3. 交换代理(由LLM驱动的协商引擎支持)请求示例有效载荷。
4. 交换代理运行AI模式推断,提议映射规则和可用订阅。
5. 合作伙伴代理接受或反提议条款。
6. 交换代理配置合作伙伴账户,配置网络钩,并颁发凭证。
7. 集成上线,无需双方人工介入。
代理路径调用与人工门户相同的内部服务API。现有行为未更改。
## 服务和端口
| 组件 | 端口 | 责任 |
| --- | --- | --- |
| 网关 | `3000` | 单一入口点,JWT认证,速率限制,反向代理 |
| 认证服务 | `3001` | 登录,刷新令牌,OAuth2,API密钥 |
| 合作伙伴服务 | `3002` | 合作伙伴注册,配置文件,KYB审批,品牌 |
| 订阅服务 | `3003` | 发现和订阅生命周期 |
| 集成服务 | `3004` | 消息路由,存储,交付,状态跟踪 |
| 映射引擎 | `3005` | AI模式推断和转换 |
| 代理协调器 | `3006` | 监控,重试,模式更改和警报代理 |
| 计费服务 | `3007` | 使用情况跟踪和计费 |
| 交换代理 | `3008` | A2A + MCP服务器;合作伙伴代理协商和自主上线 |
| 合作伙伴门户 | `3100` | Next.js UI,用于合作伙伴和管理人员 |
| PostgreSQL | `5432` | 主要数据库 |
## 仓库布局
```mermaid
graph LR
subgraph apps["apps/ — 可部署服务"]
GW2["网关<br/>:3000"]
AU["认证服务<br/>:3001"]
PA2["合作伙伴服务<br/>:3002"]
SU["订阅服务<br/>:3003"]
IN["集成服务<br/>:3004"]
MA["映射引擎<br/>:3005"]
AG["代理协调器<br/>:3006"]
EX["交换代理<br/>:3008 ★ A2A/MCP"]
BI["计费服务<br/>:3007"]
PO["合作伙伴门户<br/>:3100 Next.js"]
end
subgraph packages["packages/ — 共享库"]
ST["@bx/shared-types"]
SU2["@bx/shared-utils"]
DB["@bx/database"]
SDK["@bx/a2a-sdk ★ 新"]
LG["@bx/logger"]
end
GW2 & AU & PA2 & SU & IN & MA & AG & EX & BI --> ST
GW2 & AU & PA2 & SU & IN & MA & AG & EX & BI --> LG
PA2 & SU & IN & MA & AG & EX & BI & AU --> DB
EX --> SDK
```
```text
business-exchange/
├── apps/
│ ├── gateway/
│ ├── auth-service/
│ ├── partner-service/
│ ├── subscription-service/
│ ├── integration-service/
│ ├── mapping-engine/
│ ├── agent-orchestrator/
│ ├── exchange-agent/ ← A2A + MCP服务器,用于合作伙伴代理上线
│ ├── billing-service/
│ └── partner-portal/
├── packages/
│ ├── shared-types/
│ ├── shared-utils/
│ ├── database/
│ ├── a2a-sdk/ ← A2A和MCP协议类型(共享)
│ └── logger/
├── infra/
├── docker-compose.yml
├── turbo.json
└── package.json
```
### 共享包
- `@bx/shared-types`:共享TypeScript合约,如`ApiResponse<T>`、`Partner`、`Message`和订阅模型
- `@bx/shared-utils`:ID、网络钩签名、哈希、退避和其他共享帮助程序
- `@bx/database`:PostgreSQL连接和迁移
- `@bx/a2a-sdk`:A2A和MCP协议类型 — `AgentCard`、`Task`、`TaskState`、`MCPTool`、`MCPToolResult`
- `@bx/logger`:Pino日志工厂
## 快速入门
### 先决条件
- Node.js 20+
- npm 11+
- 用于容器化本地开发的Docker Desktop
- 一个`ENCRYPTION_KEY`,以便存储的LLM凭证可以加密和解密
### 使用Docker Compose快速启动
这是将整个堆栈在本地快速启动的最快方法。
```bash
git clone https://github.com/sprintly-exchange/business-exchange.git
cd business-exchange
cp .env.example .env
# 使用至少JWT_SECRET、WEBHOOK_SECRET和ENCRYPTION_KEY编辑.env
docker compose up -d --build
```
然后打开:
- 合作伙伴门户:`http://localhost:3100`
- 网关:`http://localhost:3000`
要停止所有内容:
```bash
docker compose down
```
### 工作区开发快速启动
如果您想直接从单体仓库进行热重载:
```bash
npm install
cp .env.example .env
npm run dev
```
有用变体:
```bash
npm run build
npm run typecheck
npm run lint
cd apps/gateway && npm run dev
cd apps/partner-portal && npm run dev
cd packages/database && npm run db:migrate
cd packages/database && npm run db:migrate:down
```
### 健康检查
每个后端服务都遵循相同的服务模式,并公开一个`/health`端点。最重要的本地检查是:
- 网关:`http://localhost:3000/health`
- 合作伙伴门户:`http://localhost:3100`
## 环境配置
从`.env.example`开始。
### 本地开发所需
```bash
JWT_SECRET=change-me
WEBHOOK_SECRET=change-me-too
ENCRYPTION_KEY=<64-char-hex-key>
```
### 平台默认LLM设置
平台默认LLM在应用程序中配置,而不是通过`.env`。
1. 启动堆栈。
2. 以管理员身份登录。
3. 打开`管理员设置`。
4. 配置平台默认LLM提供商、端点/基准URL、模型/部署和API密钥。
合作伙伴特定的BYO-LLM覆盖也通过门户UI配置。
### 其他有用变量
来自`.env.example`:
- `CORS_ORIGIN`
- `LOG_LEVEL`
- `RATE_LIMIT_MAX`
- `MAPPING_ENGINE_URL`
- `ENCRYPTION_KEY`
### 合作伙伴BYOLLM
平台在合作伙伴级别支持**BYOLLM**("自带LLM")。
默认情况下,合作伙伴使用平台配置的LLM。合作伙伴可以在门户的**设置**下,通过保存以下内容切换到自己的提供商:
- `llm_use_platform=false`
- `llm_provider`
- `llm_model`
- `llm_endpoint` for `azure` and `openai-compatible`
- `llm_api_key`
支持的合作伙伴级提供商与平台提供商集匹配:
- `azure`
- `openai`
- `openai-compatible`
重要行为:
- 合作伙伴API密钥存储加密,不通过公共配置文件API返回
- 集成服务按请求加载发送方和接收方的LLM配置
- 映射引擎可以使用不同LLM配置进行转换的第1阶段和第2阶段
- 如果不存在合作伙伴级配置,则自动使用平台LLM
实际上,这意味着:
- 发送方可以使用自己的LLM进行`发送方格式 -> CDM`
- 接收方可以使用自己的LLM进行`CDM -> 接收方格式`
- 双方不需要使用相同的提供商或模型
## 开发工作流
### 常用命令
| 命令 | 执行操作 |
| --- | --- |
| `npm install` | 安装所有工作区依赖 |
| `npm run dev` | 通过Turbo运行所有服务进行热重载 |
| `npm run build` | 构建每个工作区 |
| `npm run typecheck` | 类型检查每个工作区 |
| `npm run lint` | 在工作区运行lint |
| `npm run clean` | 清洁工作区工件和根`node_modules` |
### 单服务开发
示例:
```bash
cd apps/gateway && npm run dev
cd apps/integration-service && npm run dev
cd apps/partner-portal && npm run dev
```
### 数据库迁移
Docker设置将`packages/database/migrations`挂载到Postgres初始化。对于手动迁移工作:
```bash
cd packages/database && npm run db:migrate
cd packages/database && npm run db:migrate:down
```
### 当前测试姿态
单体仓库公开了一个根`npm run test`,但目前在大多数服务中没有有效的自动化测试套件。目前,`typecheck`是主要的仓库范围验证路径。
## 代理协调器
`agent-orchestrator`服务是一个轻量级Node.js工作器服务,使用`node-cron`在进程内运行调度操作作业。
### 其结构
在启动时,服务为每个代理类创建一个内存实例:
- `MonitorAgent`
- `RetryAgent`
- `SchemaChangeAgent`
- `AlertAgent`
这些是`agent-orchestrator` Node.js进程中运行的普通类实例。它们不是单独的容器,也不是单独的操作系统进程。
`node-cron`保留调度并调用每个实例的`.run()`方法,按定义的间隔。
### 当前调度
| 代理 | Cron | 间隔 | 执行操作 |
| --- | --- | --- | --- |
| 监控代理 | `* * * * *` | 每分钟 | 将卡住的`processing`消息标记为`failed`,并记录高错误率情况 |
| 重试代理 | `*/2 * * * *` | 每2分钟 | 重试失败的网络钩交付,并带有回退和增加重试次数 |
| 模式变更代理 | `*/30 * * *` | 每30分钟 | 如果最近的故障率高,则将活动模式标记为`drift_suspected` |
| 警报代理 | `*/5 * * * *` | 每5分钟 | 为最近的死信和模式审查条件发出警报事件 |
### 代理如何做出决定
当前代理基于规则,而不是LLM驱动。
它们根据以下内容做出决定:
- 平台数据的SQL查询
- 诸如故障率百分比等阈值
- 重试计数器
- 最近的时间窗口,例如“过去5分钟”或“过去2小时”
示例:
- 监控代理检查超过5分钟卡住的`processing`消息
- 重试代理仅重试具有少于3次重试的失败消息
- 模式变更代理查找具有超过5条最近消息且故障率超过30%的合作伙伴
- 警报代理查找新死信消息和最近标记为`drift_suspected`的模式
### LLM行为
当前调度的代理不使用LLM进行决策。
这意味着:
- 它们不调用Azure OpenAI、OpenAI或合作伙伴BYOLLM端点
- 它们不使用合作伙伴特定的LLM设置从门户
- 它们也不使用平台/全局LLM配置
LLM选择当前适用于:
- 消息映射转换
- 模式生成
对于这些路径,可以在配置时使用合作伙伴的BYOLLM;否则,将自动使用平台LLM。
### 运行时跟踪和监控
代理协调器现在在内存中跟踪每个调度代理的运行时状态,包括:
- 它是否正在运行
- 自服务启动以来的总运行次数
- 上次启动时间
- 上次完成时间
- 上次持续时间
- 上次成功或失败
- 上次错误消息
这些运行时快照通过以下方式公开:
- `GET /api/agents/overview`
通过以下方式公开最近的已持久化代理事件:
- `GET /api/agents/events`
- `GET /api/agents/events/:entityId`
合作伙伴门户使用这些端点为以下内容提供支持:
- **代理监视器**页面
- 主仪表盘的技术可见性面板
### 事件日志
代理将操作事件记录到 `agent_events` 表中。
这些事件用于:
- 操作员可见性
- 仪表盘摘要
- 最近事件馈送
- 事后调试代理操作
典型事件示例:
- `mark_stuck_failed`
- `retry_delivery`
- `schema_drift_detected`
- `dead_letter_alert`
- `schema_review_alert`
### 配置和环境
代理编排器本身目前不需要单独的代理特定cron配置;调度目前直接在 `apps/agent-orchestrator/src/index.ts` 的源代码中定义。
重要的运行时配置仍然来自正常的平台环境变量,例如:
- `WEBHOOK_SECRET` 用于重试Webhook签名
- `DATABASE_URL` 用于数据库访问
- 在Docker或分布式环境中运行的标准服务URL变量
### 操作注意事项
由于运行时状态保存在内存中:
- “当前运行”和“自启动以来总运行次数”在`agent-orchestrator`服务重启时重置
- 持久化的`agent_events`保留在PostgreSQL中,并在重启后存活
## 代理到代理集成
`exchange-agent` 服务为平台提供了它自己的人工智能代理身份。合作伙伴拥有的代理(连接到内部ERP、CRM、WMS或任何系统)可以使用标准代理协议来发现、协商和集成到平台中,完全自主。
### 支持的协议
| 协议 | 传输 | 目的 |
| --- | --- | --- |
| A2A(谷歌代理到代理) | HTTPS + SSE | 代理之间的多轮任务协商 |
| MCP(模型上下文协议) | 流式HTTP | 面向平台能力的LLM工具调用 |
### 端点
| 路由 | 身份验证 | 描述 |
| --- | --- | --- |
| `GET /.well-known/agent.json` | 无 | 代理卡 - 合作伙伴代理通过此卡发现技能、身份验证要求和端点 |
| `POST /a2a/tasks` | API密钥 | 提交新协商任务(例如“我想要集成”) |
| `GET /a2a/tasks/:id` | API密钥 | 轮询任务状态和读取代理的最新响应 |
| `POST /a2a/tasks/:id/send` | API密钥 | 继续多轮协商(发送示例有效载荷、接受提案等) |
| `POST /mcp` | API密钥 | MCP工具调用端点(流式HTTP,2025-03-26规范) |
### 合作伙伴代理可用的MCP工具
| 工具 | 功能 |
| --- | --- |
| `get_platform_info` | 返回平台能力和支持的格式 |
| `register_partner` | 创建合作伙伴账户 |
| `discover_subscriptions` | 列出可用数据馈送 |
| `submit_schema_sample` | 发送示例有效载荷并接收AI生成的映射提案 |
| `confirm_mapping` | 接受或拒绝提议的映射规则集 |
| `request_subscription` | 订阅数据馈送 |
| `configure_webhook` | 设置入站消息的交付端点 |
| `get_onboarding_status` | 返回当前注册状态和任何待处理步骤 |
| `get_integration_health` | 返回消息交付统计信息 |
### 协商状态机
每个A2A会话都经过以下状态:
```mermaid
stateDiagram-v2
[*] --> discovery : 合作伙伴代理发送第一个任务
discovery --> schema_submitted : 合作伙伴发送示例有效载荷
schema_submitted --> mapping_proposed : AI推断模式和规则
mapping_proposed --> terms_negotiated : 合作伙伴接受映射
mapping_proposed --> counter_offered : 合作伙伴拒绝/提出更改
counter_offered --> mapping_proposed : 交换代理修订提案
terms_negotiated --> provisioning : 合作伙伴提供Webhook URL
provisioning --> active : 合作伙伴账户已配置✅
mapping_proposed --> abandoned : 无响应(超时)
terms_negotiated --> abandoned : 无响应(超时)
provisioning --> failed : 注册或Webhook错误
active --> [*]
failed --> [*]
abandoned --> [*]
```
会话状态存储在 `agent_sessions` 表中。停滞的会话在门户的 **A2A Sessions** 下升级到管理员审查队列。
### LLM用法
协商引擎使用平台配置的LLM(与`Admin Settings`中设置的相同提供商和凭证)。交换代理不需要单独的LLM配置。
### 合作伙伴代理身份验证
- **代理卡发现** (`/.well-known/agent.json`) 是公开的 - 不需要凭证
- **任务和MCP端点** 需要在 `x-api-key` 请求头中提供API密钥
- API密钥可以预先颁发给受信任的合作伙伴代理或在协商过程中生成
### 它与现有架构的关系
交换代理是一种纯粹的附加服务。它调用与合作伙伴门户相同的内部API:
| 交换代理操作 | 内部调用 |
| --- | --- |
| 注册合作伙伴 | `POST partner-service/api/partners` |
| 推断模式 | `POST mapping-engine/api/mappings/schemas/register` |
| 列出订阅 | `GET subscription-service/api/subscriptions` |
| 订阅 | `POST subscription-service/api/subscriptions` |
| 配置Webhook | `PUT integration-service/api/integrations/...` |
没有现有的服务、数据库模式或API被修改。
### 在门户中查看A2A会话
管理员和合作伙伴可以在合作伙伴门户的 **A2A Sessions** 下查看协商会话。该页面显示:
- 当前协商状态
- 合作伙伴代理和交换代理之间的完整对话记录
- 提议的映射规则和订阅
- 一个按钮,用于手动批准或拒绝停滞的会话
## AI映射和可见性模型
### 规范数据模型(CDM)
映射引擎使用内部CDM作为发送者和接收者格式之间的中间表示。
典型转换路径:
```text
发送者有效载荷 -> CDM -> 接收者有效载荷
```
### 映射阶段和合作伙伴LLM选择
映射引擎支持两个独立的LLM阶段:
1. 发送者有效载荷 -> CDM
2. CDM -> 接收者有效载荷
每个阶段可以使用:
- 平台LLM,或
- 合作伙伴自己的BYOLLM配置
这允许混合流程,例如:
- 发送者使用平台LLM,接收者使用BYOLLM
- 发送者使用Azure OpenAI,接收者使用OpenAI兼容端点
- 双方合作伙伴都使用平台LLM
平台LLM使用情况的计费和跟踪与外部合作伙伴管理的使用情况区分开来。
### 集成UI中的可见性规则
该项目目前在消息详情体验中使用查看者感知有效载荷可见性:
- 发送者看到原始有效载荷和CDM
- 接收者看到CDM和以接收者格式交付的有效载荷
- 管理员看到所有有效载荷变体
这保持发送者的原始有效载荷对接收者私密,同时允许接收者检查中间标准化表示。
### 映射回退行为
如果映射失败或超时:
- 消息仍可成功交付
- UI单独显示交付状态和映射状态
- 映射回退作为警告显示,而不是交付失败
- 发送者可以从消息详情UI重新发送原始有效载荷
## 部署
### 本地Docker Compose
```bash
docker compose up -d
docker compose up -d --build
docker compose logs -f gateway
docker compose logs -f partner-portal
docker compose down
```
### Fly.io
支持两种部署拓扑。选择一个并使用匹配的环境文件和脚本。
#### 单主机(一个Fly应用)
所有服务都在单个Fly应用中运行在nginx后面。适用于低流量环境、演示或成本敏感的部署。
```bash
# 1. 从示例创建环境文件
cp infra/.env.fly.single.example infra/.env.fly.single
# 编辑infra/.env.fly.single — 填写FLY_API_TOKEN、APP_NAME和机密
# 2. 准备应用和Postgres(运行一次)
bash infra/fly/single/setup-single.sh
# 3. 部署
bash infra/fly/single/deploy-single.sh
```
单主机关键变量:
| 变量 | 描述 |
| --- | --- |
| `APP_NAME` | 全局唯一的Fly.io应用名称(例如`bx-mycompany`) |
| `FLY_API_TOKEN` | `flyctl auth token` |
| `JWT_SECRET` | `openssl rand -hex 32` |
| `WEBHOOK_SECRET` | `openssl rand -hex 32` |
| `ENCRYPTION_KEY` | `openssl rand -hex 32` |
| `PG_NAME` | Postgres集群名称,默认为`${APP_NAME}-db` |
| `DATABASE_URL` | 由`setup-single.sh`自动配置 - 首次运行时留空 |
部署后,平台位于 `https://<APP_NAME>.fly.dev`。
#### 多应用(每个服务一个Fly应用)
每个服务都部署为其自己的Fly应用(`bx-gateway`、`bx-auth-service`等)。适用于生产环境,其中独立缩放和滚动部署很重要。
```bash
# 1. 从示例创建环境文件
cp infra/.env.fly.multi.example infra/.env.fly.multi
# 编辑infra/.env.fly.multi — 填写FLY_API_TOKEN和机密
# 2. 准备所有应用和Postgres(运行一次)
bash infra/fly/multi/setup.sh
# 3. 部署所有服务
bash infra/fly/multi/deploy.sh
# 重新部署单个服务
bash infra/fly/multi/deploy.sh --app bx-gateway
```
多应用关键变量:
| 变量 | 描述 |
| --- | --- |
| `FLY_API_TOKEN` | `flyctl auth token` |
| `JWT_SECRET` | `openssl rand -hex 32` |
| `WEBHOOK_SECRET` | `openssl rand -hex 32` |
| `ENCRYPTION_KEY` | `openssl rand -hex 32` |
| `GATEWAY_URL` | 公共网关URL,用作门户构建时的`NEXT_PUBLIC_API_URL` |
| `DATABASE_URL` | 由`setup.sh`自动配置 - 首次运行时留空 |
部署后:
- 门户:`https://bx-partner-portal.fly.dev`
- 网关:`https://bx-gateway.fly.dev`
#### 环境文件参考
| 文件 | 模式 | 已提交 |
| --- | --- | --- |
| `infra/.env.fly.single.example` | 单主机模板 | ✅ 是 |
| `infra/.env.fly.single` | 单主机机密 | ❌ git-ignored |
| `infra/.env.fly.multi.example` | 多应用模板 | ✅ 是 |
| `infra/.env.fly.multi` | 多应用机密 | ❌ git-ignored |
### Azure容器应用
`infra/bicep/`下有Azure的基础设施模板。
当外部部署合作伙伴门户时,需要将网关URL作为构建时输入。
## 演示模式
演示模式为演示和手动测试提供一个现实的工作环境。
默认场景现在模拟一个医药配送线:
- CareBridge Pharmacy Network下订单
- MediCore Systems制造和开具OTC药店产品发票
- GlobalTrade Logistics处理运输和货物可见性
- NexusPay Finance推进和结算MediCore应收账款
### 它提供的内容
- 预配置的合作伙伴公司
- 种子订阅
- JSON、XML、CSV、X12和EDIFACT之间的种子端到端消息
- 演示用的可预测登录凭证
### 演示账户
所有演示合作伙伴都使用密码`Demo@1234`。
| 公司 | 电子邮件 | 焦点 |
| --- | --- | --- |
| CareBridge Pharmacy Network | `edi@carebridge-demo.io` | 药店零售和补货 |
| MediCore Systems | `integration@medicore-demo.io` | 制药制造 |
| GlobalTrade Logistics | `ops@globaltrade-demo.io` | 运输和冷链物流 |
| NexusPay Finance | `treasury@nexuspay-demo.io` | 应收账款融资和结算 |
### 建议的端到端演示流程
1. 以CareBridge身份登录并查看种子X12 850采购订单到MediCore。
2. 切换到MediCore以显示入站订单、出站XML发票和EDIFACT IFTMIN发货指令。
3. 切换到GlobalTrade以显示入站物流预订和出站DESADV发货通知。
4. 返回CareBridge以显示链接的发货可见性和发票对账。
5. 在MediCore和NexusPay完成融资请求和结算/支付轨迹。
参见`DEMO_GUIDE.md`以获取完整的演练、谈话要点和示例用例。
### 管理账户
在首次启动时会自动创建一个管理员用户。
| 字段 | 默认值 |
| --- | --- |
| 用户名 | `admin` |
| 密码 | `changeme` |
在将平台用于本地开发以外的任何地方之前,请更改默认密码。
## 操作说明
### 自主代理
代理编排器运行四个计划代理:
| 代理 | 责任 |
| --- | --- |
| 监控 | 检测卡住的消息和错误率升高 |
| 重试 | 重试失败的Webhook交付 |
| 模式变更 | 检测相对于注册模式的负载漂移 |
| 警报 | 显示死信和模式漂移问题 |
交换代理作为单独的服务运行,独立于上述基于cron的代理处理按需的代理到代理协商会话。
### API响应形状
服务返回共享的响应类型来自`@bx/shared-types`。
标准响应形状:
```ts
{ success: boolean; data?: T; error?: string; message?: string }
```
分页端点扩展此功能,包括诸如`total`、`page`和`pageSize`等元数据。
### 包导入约定
跨包导入应始终使用工作区别名:
```ts
import { createLogger } from '@bx/logger';
import { generateId } from '@bx/shared-utils';
import type { ApiResponse } from '@bx/shared-types';
```
## 测试A2A / MCP集成
### 端口参考
开发和Docker Compose模式下的端口有所不同。请在运行以下命令之前设置这些变量。
**开发模式(`npm run dev` + `docker compose up -d postgres`)或Docker Compose(`docker compose up -d --build`)**
```bash
export GW="http://localhost:3000"
export PORTAL="http://localhost:3100"
```
### 先决条件
1. **启动堆栈**
*开发模式* — Postgres必须在Docker中运行;所有其他服务通过Turbo运行:
```bash
docker compose up -d postgres
npm install # 拾取新包(a2a-sdk,exchange-agent)
npm run build -w packages/a2a-sdk # 构建共享协议类型
npm run dev # 启动所有服务,包括exchange-agent
```
*Docker Compose* — 构建并启动所有内容:
```bash
docker compose up -d --build
```
2. **应用agent_sessions迁移**(仅需一次,如果您的数据库已经存在于添加此迁移之前)
*开发模式:*
```bash
DATABASE_URL=postgresql://bx_user:bx_password@localhost:5432/business_exchange \
npm run db:migrate -w packages/database
```
*Docker Compose:*
```bash
docker exec -i bx-postgres psql -U bx_user -d business_exchange \
< packages/database/migrations/007_agent_sessions.sql
```
验证:
```bash
docker exec bx-postgres psql -U bx_user -d business_exchange -c "\dt agent_sessions"
```
3. **验证exchange-agent健康状况**
```bash
curl $GW/health
# gateway → {"status":"ok","service":"gateway"}
curl http://localhost:3008/health
# exchange-agent direct → {"status":"ok","service":"exchange-agent"}
```
4. **配置平台LLM** 在门户下 **Admin Settings**。 没有它,协商引擎会回退到静态提示。
### 步骤1 - 发现代理卡(无身份验证)
```bash
curl $GW/.well-known/agent.json | jq .
```
预期:代理卡JSON列表三个技能 - `partner_onboarding`、`schema_mapping` 和 `subscription_discovery`。
### 步骤2 - 获取API密钥
1. 打开 `$PORTAL` 浏览器并以任何演示合作伙伴身份登录(例如 `edi@carebridge-demo.io` / `Demo@1234`)
2. 转到 **Settings** 在侧边栏并滚动到 **API Keys** 部分底部
3. 点击 **Generate New API Key**
4. 复制密钥 - 它以 `bx_` 开头并仅显示一次
```bash
export API_KEY="bx_<粘贴您的密钥此处>"
```
> 如果丢失密钥,请从相同的设置页面撤销它并生成新的密钥。
### 步骤3 - 启动A2A协商任务
```bash
curl -X POST $GW/a2a/tasks \
-H "Content-Type: application/json" \
-H "x-api-key: $API_KEY" \
-d '{
"skill": "partner_onboarding",
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "Hi, we are Acme Corp. We produce purchase orders in JSON from our ERP and want to receive invoice confirmations. Can you onboard us?" }]
}
}' | jq .
```
注意响应中的 `id`(任务ID)。
### 步骤4 - 继续协商
```bash
curl -X POST $GW/a2a/tasks/$TASK_ID/send \
-H "Content-Type: application/json" \
-H "x-api-key: $API_KEY" \
-d '{
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "Here is our sample: {\"poNumber\":\"PO-1001\",\"vendor\":\"MediCore\",\"items\":[{\"sku\":\"IBUP-200\",\"qty\":500,\"price\":2.50}],\"deliveryDate\":\"2026-04-01\"}" }]
}
}' | jq .
# 轮询状态随时
curl $GW/a2a/tasks/$TASK_ID \
-H "x-api-key: $API_KEY" | jq .result.status
```
会话在状态之间切换:`discovery → schema_submitted → mapping_proposed → terms_negotiated → provisioning → active`。
### 步骤5 - 直接测试MCP工具
```bash
# 初始化 - 无身份验证
curl -X POST $GW/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | jq .
# 列出可用工具
curl -X POST $GW/mcp \
-H "Content-Type: application/json" \
-H "x-api-key: $API_KEY" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | jq '.result.tools[].name'
# 直接调用工具
curl -X POST $GW/mcp \
-H "Content-Type: application/json" \
-H "x-api-key: $API_KEY" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": { "name": "get_platform_info", "arguments": {} }
}' | jq .
```
### 步骤6 - 在门户中查看会话
打开 `$PORTAL` 浏览器,登录,点击侧边栏中的 **A2A Sessions**。每个协商会话都列出其当前状态、合作伙伴详细信息和合作伙伴代理与交换代理之间的完整对话记录。
### 关键检查的预期响应
| 请求 | 预期 |
| --- | --- |
| `GET /.well-known/agent.json` | Agent Card JSON,3 个技能,无需身份验证 |
| `POST /a2a/tasks`(不带 `x-api-key`)| `401 API 密钥必填` |
| `POST /a2a/tasks`(带有效密钥)| `202` + 任务,状态为 `working` |
| `GET /a2a/tasks/:id` | 当前状态 + 协商历史数组 |
| `POST /mcp` `initialize` | `200` + 协议版本,无需身份验证 |
| `POST /mcp` `tools/list`(不带密钥)| `401 API 密钥必填` |
| 门户 A2A 会话页面 | 会话列表,包含记录和状态徽章 |
## 故障排除
### A2A 协商会话卡住
如果合作伙伴代理会话没有进展:
- 检查平台 LLM 是否在 `管理员设置` 中配置(协商引擎使用它)
- 检查交换代理日志:`docker compose logs -f exchange-agent`(Docker)或 Turbo 开发输出(开发模式)
- 在门户中查看 **A2A 会话** 下的会话记录
- 如果映射和订阅提案看起来正确,则使用管理员手动批准操作来解阻塞会话
### 门户加载但 API 调用失败
检查:
- 网关正在运行(`http://localhost:3000`)
- `NEXT_PUBLIC_API_URL` 指向网关
- 您的身份验证令牌存在且有效
### Docker 服务启动但映射不工作
检查:
- 平台默认 LLM 在 `管理员设置` 中配置
- `ENCRYPTION_KEY` 已设置,以便可以解密存储的 LLM 凭证
- `MAPPING_ENGINE_URL` 在您使用的环境中正确解析
### 消息已传递但映射显示回退
这通常意味着:
- 映射引擎超时
- 没有可用的适用模式
- 平台默认 LLM 在 `管理员设置` 中丢失或配置不正确
交付状态和映射状态是故意分开跟踪的。
### Lint 行为与预期不同
存储库有一个根 lint 命令,但前端生态系统的某些部分可能仍会根据您的本地环境触发首次运行的工具设置。如果 lint 提示交互式配置,请完成该设置一次,然后重新运行命令。
## 建议的首次探索路径
如果您是此代码库的新手,以下顺序效果良好:
1. 阅读此 README
2. 使用 Docker Compose 启动堆栈
3. 登录合作伙伴门户
4. 探索 `apps/gateway`、`apps/integration-service` 和 `apps/mapping-engine`
5. 检查 `packages/shared-types` 和 `packages/a2a-sdk` 中的共享合约
6. 查看合作伙伴门户中的种子/演示流程
7. 探索 `apps/exchange-agent` 以获取 A2A/MCP 代理集成
## 许可证 / 使用
本项目为专有软件。所有权利由存储库所有者保留。在没有明确书面许可的情况下,不得使用、发布或修改。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器