Files
supply-intelligence/tech/BASELINE_TECHLEAD_V2.md
2026-05-07 10:16:46 +08:00

610 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> 真源索引:当前文档受 `/home/long/project/立交桥/projects/supply-intelligence/tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md` 纳管。
> 若阅读顺序、真源优先级或跨文档冲突存在疑问,先看该索引,再回到本基线文档。
1. 设计范围:本次覆盖 / 明确不做 / 与 PRD 对应关系
1.1 本次覆盖
- 覆盖模块 A 供应商品质探针,但收敛为“账号健康探测 + 状态写回 + 审计 + gateway 可消费状态快照”。
- 覆盖模块 B 全网模型发现,但收敛为“已接入供应商的模型列表扫描 + candidate 生成 + 下架告警”,不做广义全网情报平台。
- 覆盖模块 C 模型准入测试,但收敛为“对 discovered candidate 做标准兼容性验证 + 生成 supply_package 草稿 + 发布闭环”。
- 模块 E 仅覆盖与主链路直接相关的最小运营干预:手动触发探针、忽略 candidate、确认上架、查看失败原因、查看审计。
- 覆盖与立交桥主项目的优先集成运行方案。
- 覆盖对 NewAPI / Sub2API 的最小适配边界:状态读取、模型列表消费、可选发布回调;不反向侵入其内部实现。
- 覆盖五个 QA 阻塞的显式修复:
1) 设计范围蔓延
2) 探针误判规则冲突
3) candidate 状态机不闭环
4) 模块关闭一致性缺失
5) gateway 消费链路未闭环
1.2 明确不做
- 不做独立平台化、多服务拆分、专用 API Gateway、专用消息总线、专用控制台集群。
- 不做 vector / embedding 检索 / 向量库。
- 不做 pricing 数据库、模型比价主链路、自动定价、家族回退定价。
- 不做 predictions / 预测分析 / 广义开放平台 / 社区情报源。
- 不做 WebSocket 实时推送作为本期前提;工作台可先走普通 HTTP 拉取。
- 不做 Playwright 浏览器自动化注册主路径;本期仅保留受控自动补给的最小边界:白名单供应商、阈值触发、任务化补给、待验证/待启用,不把浏览器自动化注册链路作为首期上线门槛。
- 不要求独立 Redis/Temporal/Milvus/Qdrant 等新增重基础设施;优先复用立交桥现有 DB、现有 scheduler、现有审计、现有配置热更新机制。
- 不自动直接操作 gateway 路由细节表;只提供 package 发布后的内部契约,由 gateway/supply-api 按既有主链路消费。
1.3 与 PRD 对应关系
- AC-01/02/03保留落在探针执行、判定、状态迁移、审计与降级策略。
- AC-04/05保留落在扫描、去重、新增 candidate、下架告警。
- AC-06/07保留落在 admission runner、candidate 流转、draft package 生成。
- AC-08/09本期不做深自动注册链路但保留“受控自动补给”的最小产品/技术边界:仅允许白名单供应商、仅允许阈值触发、仅允许生成待补给任务或进入待验证/待启用,不允许把注册浏览器自动化、验证码编排、自动激活作为首期硬门槛。
- AC-10/11/12保留但只保留支撑主链路的最小实现不扩展成独立大盘平台。
- PRD 中与商业化、SFI、预测分析、比价报表相关内容不作为本次技术控制面主路径。
2. 架构与模块:模块划分、文件/目录落点、关键调用链路、关键依赖与降级边界
2.1 总体架构
基线采用“立交桥主项目内集成模块”模式,而不是独立平台。推荐以 supply-api 内部模块形式落地,原因:
- 直接复用 supply_accounts / supply_packages / audit / verify / config / scheduler。
- 避免再造服务间调用、鉴权、部署、监控、迁移复杂度。
- 更符合立交桥现有 net/http + pgx + PostgreSQL 的简洁架构。
独立运行能力保留为简单可选形态:
- 仅在确有外部项目需要时,封装为同仓内单进程启动入口。
- 独立运行不得要求新增专用基础设施;仍使用 PostgreSQL + 现有 scheduler 抽象。
- 不额外设计独立控制台、独立 worker 集群、独立 API 网关。
2.2 模块划分
建议收敛为 6 个模块,均为最小必要:
A. probe
- 读取待探测账号
- 执行标准探针
- 依据统一判定规则生成 outcome
- 驱动 account 状态迁移
- 写审计与探针日志
B. discovery
- 读取已接入供应商适配器
- 拉取模型列表
- 与现有 supply_packages / candidate 去重
- 创建 candidate
- 生成模型下架告警待办
C. admission
- 消费 discovered / retry_pending candidate
- 执行标准测试集
- 更新 candidate 状态
- 生成或更新 supply_package draft
D. publish
- 运营确认 package draft
- 将 package 切到 active
- 将 candidate 切到 published
- 写入 gateway 可消费的发布事件/变更记录
E. integration
- 立交桥内部直接集成接口
- gateway / supply-api 内部契约
- NewAPI / Sub2API 适配边界
F. control
- 模块开关、停机、运行中任务收敛、配置热更新、幂等、审计
2.3 文件/目录落点
以下为建议落点,优先放入立交桥主项目既有模块内;若 supply-intelligence 仓先行设计,可按同名目录组织:
- /home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md
- 建议实现落点参考:
- supply-api/internal/supplyintelligence/module.go
- supply-api/internal/supplyintelligence/probe/service.go
- supply-api/internal/supplyintelligence/probe/evaluator.go
- supply-api/internal/supplyintelligence/probe/state_machine.go
- supply-api/internal/supplyintelligence/discovery/service.go
- supply-api/internal/supplyintelligence/discovery/adapter_registry.go
- supply-api/internal/supplyintelligence/admission/service.go
- supply-api/internal/supplyintelligence/admission/runner.go
- supply-api/internal/supplyintelligence/publish/service.go
- supply-api/internal/supplyintelligence/integration/http_internal.go
- supply-api/internal/supplyintelligence/integration/newapi_adapter.go
- supply-api/internal/supplyintelligence/integration/sub2api_adapter.go
- supply-api/internal/supplyintelligence/control/shutdown.go
- supply-api/internal/supplyintelligence/repository/*.go
- supply-api/sql/*supply_intelligence*.sql
2.4 关键调用链路
链路 1探针 -> 状态写回 -> gateway 消费闭环
1) scheduler 触发 ProbeTick(platform/account batch)
2) probe.Service.LoadProbeTargets()
3) probe.Service.RunProbe(accountID)
4) probe.Evaluator.Classify(response/error) => success / explicit_failure / inconclusive
5) probe.StateMachine.Apply(account.current_status, recent_probe_window)
6) repository.UpdateAccountHealthAndStatusTx(...)
7) repository.AppendAuditLog(...)
8) repository.UpsertGatewayAccountSnapshot(...)
9) gateway 通过内部契约读取 snapshot 或随 package/account 查询一起读取可用状态
链路 2扫描 -> candidate -> admission
1) scheduler 触发 DiscoveryTick(platform)
2) discovery.Adapter.FetchModels()
3) discovery.Service.DiffAgainstPackagesAndCandidates()
4) repository.UpsertModelCandidate(status=discovered)
5) scheduler enqueue AdmissionRun(candidateID)
6) admission.Runner.Execute(candidateID)
7) repository.UpdateCandidateStatus(...)
8) repository.UpsertDraftPackage(...)
9) repository.AppendAuditLog(...)
链路 3运营确认上架 -> gateway 消费闭环
1) ops POST confirm publish
2) publish.Service.PublishDraft(candidateID, actor)
3) tx: lock candidate + package draft
4) package draft -> active
5) candidate test_passed -> published
6) append internal event supply_package_published
7) append audit
8) gateway/supply-api 既有主链路消费 active package 或发布事件刷新内存路由
链路 4模块关闭闭环
1) operator/config 将 module.enabled=false
2) control.ModuleGate.MarkClosing(module)
3) 新任务拒绝入队/拒绝手动触发
4) 运行中任务继续到安全提交点或超时中断
5) 写 module_state=closed when inflight=0
6) 后续 scheduler tick 直接跳过
2.5 关键依赖与降级边界
- PostgreSQL强依赖。不可用时所有自动写操作 fail-closed不做假成功。
- scheduler中强依赖。不可用时自动任务暂停但手动接口可保留。记录告警。
- supplier adapter弱依赖。单供应商异常不影响其他供应商。
- gateway首期默认事件型消费方。发布链路不等待 gateway 成功回调才提交 package active但必须通过 package change + ack 保留可追踪消费记录,且必须存在真实消费入口。
- NewAPI/Sub2API可选适配依赖。未配置时不影响立交桥内部主链路。
降级原则
- 探针外部错误、429、5xx、DNS/TCP 异常inconclusive不推进惩罚性状态迁移。
- admission 外部超时candidate 转 retry_pending 或 test_failed不能生成 active package。
- gateway 消费延迟package 可 active但需要“未消费/待同步”状态位和审计,不可假定已生效。
- 模块关闭中:新任务一律拒绝,运行中任务只允许安全收尾。
3. 接口与数据模型API/RPC/事件、数据模型/schema、错误码、安全/鉴权契约
3.1 接口分类
3.1.1 立交桥内部直接集成接口
用途:供立交桥主项目内其他模块直接调用,优先 Go 接口,不先暴露额外网络跳。
interface SupplyIntelligenceModule {
RunProbe(ctx context.Context, accountID int64, trigger string) (*ProbeOutcome, error)
ScanPlatform(ctx context.Context, platform string, trigger string) (*ScanOutcome, error)
RunAdmission(ctx context.Context, candidateID int64, trigger string) (*AdmissionOutcome, error)
PublishCandidate(ctx context.Context, candidateID int64, actor string) (*PublishOutcome, error)
GetAccountRoutingState(ctx context.Context, accountID int64) (*AccountRoutingState, error)
}
3.1.2 给 gateway / supply-api 使用的内部契约
用途:形成真实消费闭环,避免“文档说 gateway 会用,但无真实契约”。
HTTP internal 契约,前缀建议:/internal/supply-intelligence
1) GET /internal/supply-intelligence/accounts/{account_id}/routing-state
响应:
{
"account_id": 123,
"platform": "openai",
"account_status": "active",
"routing_enabled": true,
"risk_score": 20,
"reason_code": "ok",
"last_probe_at": "2026-05-06T15:00:00Z",
"version": 17
}
2) GET /internal/supply-intelligence/models/{platform}/{model}/admission-state
响应:
{
"platform": "openai",
"model": "gpt-4.1-mini",
"candidate_status": "published",
"package_id": 456,
"package_status": "active",
"gateway_sync_status": "pending|applied|failed|not_required",
"version": 9
}
3) GET /internal/supply-intelligence/gateway/package-changes?cursor=...
响应:
{
"items": [
{
"event_id": "evt_001",
"event_type": "supply_package_published",
"package_id": 456,
"platform": "openai",
"model": "gpt-4.1-mini",
"occurred_at": "2026-05-06T15:00:00Z",
"version": 9
}
],
"next_cursor": "..."
}
4) POST /internal/supply-intelligence/gateway/package-changes/{event_id}/ack
请求:
{
"consumer": "gateway",
"result": "applied|failed",
"detail": "optional"
}
响应204
闭环定义
- 发布成功 != gateway 已消费。
- 只有 gateway ack event_id 后gateway_sync_status 才能从 pending -> applied/failed。
- QA 必须验证 publish -> list changes -> ack 的真实链路。
3.1.3 面向 NewAPI/Sub2API 的适配边界
原则:只暴露最小必要只读/回调能力,不把本系统设计成它们的管理平台。
适配边界 A状态拉取
- GET /adapter/v1/supply-status/accounts/{account_id}
- 字段与 routing-state 对齐,但去掉内部实现细节。
适配边界 B模型拉取
- GET /adapter/v1/models?status=published
响应只返回已 published 且 package active 的模型。
适配边界 C可选发布回调下发
- POST /adapter/v1/package-events
仅在对方需要 webhook 模式时启用;默认不要求。
适配边界约束
- 不暴露审计明细。
- 不暴露原始探针日志。
- 不暴露账号凭证、测试账号信息、内部风险算法细节。
- 仅允许配置白名单来源访问。
3.2 数据模型/schema
3.2.1 probe_execution_logs
- id bigint pk
- account_id bigint not null
- platform varchar(64) not null
- probe_result varchar(32) not null 取值: success | explicit_failure | inconclusive
- failure_class varchar(64) null 取值: auth_invalid | quota_empty | timeout | tcp_error | dns_error | rate_limited | upstream_5xx | parse_error
- http_status int null
- latency_ms int null
- risk_score int not null
- evaluated_transition varchar(64) not null 取值: no_change | active_to_suspended | suspended_to_disabled | suspended_to_active
- executed_at timestamptz not null
- request_id varchar(64) not null
- index(account_id, executed_at desc)
3.2.2 model_candidates
- id bigint pk
- platform varchar(64) not null
- model varchar(128) not null
- status varchar(32) not null
- discovery_source varchar(32) not null 取值: official_api | official_doc | manual_seed
- last_scan_at timestamptz not null
- discovered_at timestamptz not null
- last_test_at timestamptz null
- failure_reason_code varchar(64) null
- failure_summary text null
- ignored_until timestamptz null
- package_id bigint null
- version int not null default 1
- unique(platform, model)
candidate 最终闭环状态机
- discovered扫描新发现可入测试
- testing测试执行中
- test_passed测试通过已存在 draft package
- test_failed测试失败允许人工重试或自动进入 retry_pending
- retry_pending等待下次重试
- ignored运营临时忽略到 ignored_until 后自动回 discovered
- published运营已确认上架package active
- deprecated供应商侧已消失已产生运营待办但历史保留
- closed不再处理的终态仅用于模型被明确弃用/手工关闭
合法迁移
- discovered -> testing
- testing -> test_passed | test_failed | retry_pending
- test_failed -> retry_pending | closed
- retry_pending -> testing | closed
- discovered | test_failed | retry_pending -> ignored
- ignored -> discovered
- test_passed -> published | closed
- published -> deprecated | closed
- deprecated -> closed
闭环修复点
- 任何非终态都存在后继处理路径。
- ignored 有自动回流。
- published/deprecated 最终可归档到 closed。
- 不再存在“只定义中间态、无出口”的 QA 阻塞。
3.2.3 gateway_package_events
- event_id varchar(64) pk
- event_type varchar(64) not null
- package_id bigint not null
- candidate_id bigint null
- payload jsonb not null
- consumer varchar(64) null
- consumer_status varchar(32) not null default 'pending'
- consumer_detail text null
- occurred_at timestamptz not null
- acked_at timestamptz null
- retry_count int not null default 0
3.2.4 module_runtime_state
- module_name varchar(64) pk
- desired_state varchar(16) not null 取值: enabled | disabled
- runtime_state varchar(16) not null 取值: starting | running | closing | closed
- inflight_count int not null
- updated_at timestamptz not null
3.3 探针判定统一规则
这是本轮必须修的 QA 阻塞之一,统一如下:
明确失败 explicit_failure
- HTTP 401/403
- 供应商明确返回 key invalid / account suspended / quota exhausted 且可稳定识别
不可判定 inconclusive
- HTTP 429
- HTTP 5xx
- DNS 失败
- TCP 连接失败
- 超时
- 响应体为空或格式突变
成功 success
- 返回 2xx 且最小校验通过
状态迁移规则
- active + 1 次 explicit_failure -> suspended
- suspended + 最近连续 3 次 explicit_failure -> disabled
- suspended + 1 次 success -> active
- disabled 不自动恢复,只能人工恢复到 active 或 closed
- inconclusive 永不计入 explicit failure 连续次数
说明
- 将 timeout/TCP/DNS 从“失败导致降级”统一修正为 inconclusive消除 PRD/HLD 冲突。
- 若未来某供应商能明确证明 timeout 即余额停用,也必须走供应商级覆盖配置,不改全局默认。
3.4 错误码
- SUP_INT_PROBE_NOT_FOUND 404
- SUP_INT_PROBE_MODULE_DISABLED 409
- SUP_INT_CANDIDATE_NOT_FOUND 404
- SUP_INT_CANDIDATE_STATE_INVALID 409
- SUP_INT_PUBLISH_PACKAGE_MISSING 409
- SUP_INT_GATEWAY_ACK_CONFLICT 409
- SUP_INT_ADAPTER_UNSUPPORTED 400
- SUP_INT_AUTH_FORBIDDEN 403
- SUP_INT_CONFIG_INVALID 400
- SUP_INT_UPSTREAM_TEMPORARY 503
3.5 安全/鉴权契约
- 内部接口只允许立交桥内部服务身份访问,走现有 internal auth middleware。
- NewAPI/Sub2API 适配接口必须使用独立 access key 或签名校验,按来源白名单限制。
- 审计字段必须包含 object_type/object_id/action/result_code/before_state/after_state/request_id/actor。
- 任何日志不得输出明文 API key、cookie、token、测试账号凭证。
- 手动发布、手动恢复 disabled 账号、关闭 candidate 必须要求 operator 身份并审计。
4. 任务拆解:每个任务必须有具体文件路径和函数名,粒度 2-5 分钟
说明:以下为 Engineer 最小实现任务单,按设计拆到文件级与函数级。路径以优先集成到 supply-api 为准。
4.1 模块骨架
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/module.go :: func RegisterModule(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/module.go :: func MountInternalRoutes(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/module.go :: func RegisterSchedulers(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/control/shutdown.go :: func BeginModuleClose(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/control/shutdown.go :: func FinishInflightTask(...) error
4.2 probe
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/service.go :: func LoadProbeTargets(ctx context.Context, limit int) ([]Account, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/service.go :: func RunProbe(ctx context.Context, accountID int64, trigger string) (*ProbeOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/evaluator.go :: func ClassifyProbeResult(resp *http.Response, err error) ProbeClass
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/evaluator.go :: func CalculateRiskScore(class ProbeClass) int
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/state_machine.go :: func ApplyAccountTransition(current string, recent []ProbeClass) (next string, transition string)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/state_machine.go :: func CountRecentExplicitFailures(recent []ProbeClass) int
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/worker.go :: func HandleProbeTick(ctx context.Context) error
4.3 discovery
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/adapter_registry.go :: func ResolveModelAdapter(platform string) (ModelAdapter, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func ScanPlatform(ctx context.Context, platform string, trigger string) (*ScanOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func DiffModels(current []string, packages []string, candidates []string) DiffResult
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func UpsertDiscoveredCandidates(ctx context.Context, platform string, models []string) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func MarkDeprecatedAlerts(ctx context.Context, platform string, missing []string) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/worker.go :: func HandleDiscoveryTick(ctx context.Context) error
4.4 admission
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/service.go :: func EnqueueAdmission(ctx context.Context, candidateID int64) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/service.go :: func RunAdmission(ctx context.Context, candidateID int64, trigger string) (*AdmissionOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func LoadCandidateForTesting(ctx context.Context, candidateID int64) (*Candidate, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func ExecuteTestSuite(ctx context.Context, c *Candidate) (*SuiteResult, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func DecideCandidateNextState(result *SuiteResult) (string, string)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func UpsertDraftPackage(ctx context.Context, c *Candidate, result *SuiteResult) (int64, error)
4.5 publish
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service.go :: func PublishCandidate(ctx context.Context, candidateID int64, actor string) (*PublishOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service.go :: func ValidatePublishable(ctx context.Context, candidateID int64) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service.go :: func AppendGatewayPackageEvent(ctx context.Context, packageID int64, candidateID int64) error
4.6 integration
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func GetAccountRoutingState(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func GetModelAdmissionState(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func ListGatewayPackageChanges(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func AckGatewayPackageChange(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/newapi_adapter.go :: func ListPublishedModels(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/newapi_adapter.go :: func GetExternalAccountStatus(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/sub2api_adapter.go :: func ListPublishedModels(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/sub2api_adapter.go :: func GetExternalAccountStatus(w http.ResponseWriter, r *http.Request)
4.7 repository / sql
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/probe_repo.go :: func InsertProbeExecutionLog(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/probe_repo.go :: func UpdateAccountHealthAndStatusTx(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/candidate_repo.go :: func UpsertModelCandidate(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/candidate_repo.go :: func UpdateCandidateStateTx(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/package_repo.go :: func UpsertDraftPackageTx(...) (int64, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/gateway_repo.go :: func InsertGatewayPackageEventTx(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/gateway_repo.go :: func AckGatewayPackageEventTx(...) error
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_probe_logs.sql :: migration create table
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_candidates.sql :: migration create table
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_gateway_events.sql :: migration create table
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_module_runtime.sql :: migration create table
4.8 测试与校验
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/state_machine_test.go :: func TestApplyAccountTransition()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/evaluator_test.go :: func TestClassifyProbeResult()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner_test.go :: func TestDecideCandidateNextState()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service_test.go :: func TestPublishCandidate_AppendsGatewayEvent()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal_test.go :: func TestGatewayEventAckFlow()
5. 风险与保护:风险清单(概率/影响/缓解)、降级策略、威胁建模结果
5.1 风险清单
1) 探针误判导致错误下线
- 概率:中
- 影响:高
- 缓解:只允许 explicit_failure 触发惩罚状态429/超时/网络错误全部 inconclusivedisabled 仅连续 3 次明确失败;生产初期可只告警不自动 disabled。
2) candidate 重复创建或状态乱序
- 概率:中
- 影响:中
- 缓解unique(platform, model)version 乐观锁;状态迁移函数集中校验;测试任务拿行锁。
3) gateway 未真实消费已上架 package
- 概率:高
- 影响:高
- 缓解:新增 gateway_package_events + ack 契约;区分 published 与 gateway applied监控 pending backlog。
4) 模块关闭时仍有脏写入
- 概率:中
- 影响:中
- 缓解runtime_state=closing 时拒绝新任务inflight 计数;安全提交点;超时取消 ctx。
5) 适配器变更影响扫描质量
- 概率:中
- 影响:中
- 缓解:按供应商隔离;单平台失败不扩散;保留 last_successful_scan 基线;失败仅告警不删数据。
6) NewAPI/Sub2API 适配越权暴露内部数据
- 概率:低
- 影响:高
- 缓解:适配接口单独 DTO白名单认证不复用内部 debug 输出。
5.2 降级策略
- probe 模块关闭gateway 继续依赖现有 account/package 状态;新鲜度下降但主链路可运行。
- discovery 模块关闭:不再发现新模型;已上架模型不受影响。
- admission 模块关闭candidate 可积压,但不会误上架。
- publish 后 gateway 未消费:保留 pending运营可见不回滚 package active但不得宣称“已进路由”。
- NewAPI/Sub2API 未配置:直接关闭适配路由,不影响内部主链路。
5.3 威胁建模结果
输入边界
- 供应商返回体属于不可信输入必须限长、schema 校验、错误脱敏。
- 运营手动接口属于高权限输入:必须鉴权、审计、幂等。
- gateway ack 请求属于内部写接口:必须鉴权并校验 event_id/consumer 一致性。
数据流
- supplier -> adapter -> evaluator -> db
- db -> internal route -> gateway
- db -> adapter route -> NewAPI/Sub2API
主要威胁与处置
- 凭证泄漏:本期不纳入自动注册主路径;现有账号密钥仅走既有安全存储,不在本模块新增明文链路。
- 重放/重复发布publish 接口需幂等published candidate 再次 publish 返回 409。
- 伪造 gateway ack只接受内部服务身份event consumer 固定枚举。
- 大响应体压垮解析adapter 限制 body size超限视为 inconclusive/scan_failed。
- SQL 并发覆盖:关键状态表使用 version 或 select for update。
6. QA 交接与实施约束:编码前设计审查要点、编码后漂移检查点、必查真实调用链路、禁止偏离的边界
6.1 编码前设计审查要点
- 是否明确“集成运行优先,独立运行可选且轻量”。
- 是否删除 pricing/vector/predictions/开放平台化内容。
- 探针默认规则是否统一为 explicit_failure 才触发状态惩罚。
- candidate 状态机是否存在完整入口、出口、终态与回流。
- gateway 是否存在 list change + ack 的真实闭环,而非只有查询接口。
- 模块关闭是否存在 closing -> closed 收敛语义。
- NewAPI/Sub2API 是否仅作为适配边界,而非反向牵引主架构。
6.2 编码后漂移检查点
- 是否出现新增 Redis/Temporal/Kafka/MQ/向量库等重基础设施前置依赖。
- 是否出现额外独立服务、额外 API gateway、复杂事件总线。
- 是否把自动注册重新抬回本期主路径。
- 是否把 gateway 路由刷新实现成跨系统强耦合同步 RPC 必须成功。
- 是否新增未在本基线定义的中间状态。
- 是否把 timeout/TCP/DNS 再次当成 explicit_failure。
6.3 QA 必查真实调用链路
- probe tick -> evaluator -> state machine -> supply_accounts 写回 -> audit 写入
- discovery tick -> candidate discovered -> admission run -> draft package
- publish confirm -> package active -> candidate published -> gateway change event -> gateway ack
- module disable -> closing -> reject new task -> inflight drain -> closed
- adapter route -> NewAPI/Sub2API 只读返回,字段不泄露内部敏感信息
6.4 禁止偏离的边界
- 禁止把本期做成独立平台化部署前提。
- 禁止把比价、预测、向量检索恢复为主链路。
- 禁止未定义契约就声称“gateway 会消费”。
- 禁止 candidate 状态直接跳 published绕过 test_passed + draft package。
- 禁止 disabled 自动恢复。
- 禁止模块关闭时直接 kill 运行中事务而无收敛策略。
7. Engineer 实施说明:文件级落点、最小验证项、需 PM 澄清项
7.1 文件级落点
优先实施目录:
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/
- /home/long/project/立交桥/supply-api/sql/
- /home/long/project/立交桥/supply-api/internal/http/internal/
若 supply-intelligence 项目仓仅承载设计文档,则本文件作为交付基线,后续代码并入 supply-api 主仓。
7.2 最小验证项
- 单测探针分类、账号状态迁移、candidate 状态迁移、publish 幂等。
- 集成测publish 后产生 gateway eventgateway ack 后状态更新 applied。
- 集成测module closing 时手动触发探针返回 409 module disabled/closing。
- E2E 最小链路:
1) 一个 active 账号 401 -> suspended
2) 一个新模型 discovered -> test_passed -> draft -> published
3) gateway 拉取 package change 并 ack
7.3 需 PM 澄清项
- 本期是否允许 production 初期仅启用 active->suspended暂不自动 disabled。
- candidate ignored 的默认恢复期是否固定 7 天,或允许按供应商配置。
- gateway 首期默认采用 pull package-changes + ack 作为事件型消费闭环;若后续证明已有内部刷新入口可复用,也必须保留等价 ack 语义与可审计消费状态。
- NewAPI/Sub2API 本期需要只读拉取,还是还需要 webhook 模式;默认只做只读拉取。
8. 阶段门控结论:可进入 QA 设计审查 / 需返回 PM / 需继续补设计
结论:可进入 QA 设计审查
理由
- 五个 QA 阻塞已在本基线中逐项补洞并收敛。
- 架构已回到立交桥一致的简洁集成模式。
- 对立交桥 / NewAPI / Sub2API 的边界已最小化并显式分类。
- 已删除明显超范围和重基础设施设计。
附带条件
- 不代表可直接开发放行。
- 进入开发前仍需确认 PM 澄清项中的 gateway 消费方式与 production 初期自动 disabled 策略。
9. 下游执行约束摘要:
- Engineer 禁止偏离:不得新增独立平台化部署前提、不得恢复 pricing/vector/predictions 主路径、不得绕过 gateway event ack 闭环、不得新增未定义 candidate 状态。
- QA 必查调用链路probe->状态写回discovery->candidate->admission->draftpublish->gateway event->ackmodule disable->closing->drain->closedNewAPI/Sub2API 只读适配边界。
- XL 若继续推进需补的门控:确认 gateway 实际消费方式;确认生产首期自动 disabled 策略;确认代码最终并入 supply-api 主仓而非另起独立重部署。
自检清单
- [x] 架构设计覆盖 PRD 所有 AC
- [x] 接口定义完整(请求/响应/错误)
- [x] 每个任务 < 5分钟有明确文件路径
- [x] 依赖关系无循环
- [x] 考虑了扩展点(未来可能的变化)
- [x] 风险评估完整,有关键风险的缓解方案
- [x] 符合项目现有技术栈和编码规范
- [x] 降级策略已设计(熔断/限流/兜底)
- [x] 威胁建模已完成(输入边界/鉴权/数据流)
- [x] 实施漂移检测点已定义(可与 QA checklist 对接)
- [x] 已明确标记是否可进入 QA 设计审查
- [x] 已提供 QA 编码前审查与编码后漂移检测所需交接物
- [x] 已给出 Engineer / QA / XL 的下游执行约束摘要
- [x] 已纳入立交桥简洁架构与立交桥/NewAPI/Sub2API 集成边界