项目实现架构
s3下载器
flowchart TD
controller["NAS 下载主控循环"]
schedule["根据云服务器执行端空闲槽位请求切片"]
taskStore[("SQLite 任务切片库<br/>任务 ID、字节范围、状态、租约、下载路径、服务器 ID、S3 官方校验值")]
claim["SQLite 写事务原子领取切片<br/>查询 状态=等待 或已过期切片<br/>写入任务 ID、服务器 ID、最后检测时间"]
launch["NAS 启动该切片的独立 SSH 会话"]
cloud["云服务器执行端"]
cloudScript["启动云端下载脚本"]
inspect{"检查云端临时切片文件"}
complete["已有完整切片"]
partial["存在部分下载文件"]
fresh["没有可用下载进度"]
resume["读取已有进度<br/>计算下一个 Range 起点"]
requestRange["发起 S3 Range 下载"]
s3["S3 对象存储"]
response["返回指定字节范围"]
remoteFiles["云端临时切片文件"]
remoteCheck{"切片是否达到预期大小"}
remoteComplete["云端切片完整"]
rsync["NAS 使用 rsync 把云端切片拉回 NAS"]
nasTemp["NAS 临时文件<br/>xxx.download"]
verify["检查文件大小是否等于准确字节数"]
publish["临时文件原子改名<br/>xxx.download → xxx"]
verified["更新 SQLite<br/>状态为本地已校验"]
assembly["所有切片完成后合成文件"]
expectedChecksum["读取任务保存的 S3 官方 ChecksumSHA256"]
compareChecksum{"完整文件 SHA-256 校验是否一致"}
completeTask["更新 SQLite<br/>任务状态为已完成"]
hashFail["删除不可信合成文件<br/>相关切片重新标记为等待状态"]
fail["SSH、S3、下载或 rsync 失败"]
requeue["保留可复用进度<br/>切片标记为等待状态"]
renewScan["主控循环每约 15 秒扫描活动切片"]
renewDB["校验任务 ID、字节范围、服务器 ID、租约标识<br/>更新最后检测时间 = 当前时间 + 60 秒"]
expireScan["检查是否存在过期租约"]
expire["超过 60 秒未续租<br/>状态更改:下载/回传 → 等待"]
keepalive["SSH 保活<br/>每 10 秒探测<br/>连续 3 次无响应则断开"]
stalled["约 45 秒没有下载进度"]
stop["终止卡住的下载进程"]
lifecycleTimer["Agent 工作流定时器<br/>当前配置:每 3 小时触发"]
lifecycleLock[获取订购生命周期全局锁先恢复未完成的退订/重购事务]
pendingTx{"是否存在未完成的生命周期事务"}
resumeTx["按事务当前阶段继续执行<br/>禁止重复退款或重复下单"]
txStep["执行当前阶段<br/>退款确认、退款核验、购买、初始化、测速或接入"]
healthCheck[巡检云服务器节点读取实时工作状态、下载指标和固定 S3 测速]
retireDecision{"是否满足连续两轮不合格条件"}
keepActive["节点继续保留<br/>维持下载服务"]
drain["节点标记为 draining<br/>停止分配新切片并等待活动切片归零"]
retire["Agent 工作流通过网页工单手动提交退订/退款"]
refundCheck{目标实例是否已从服务器列表消失}
purchase["按实时余额与实例配额重购替代节点"]
init["发现新实例并完成初始化<br/>SSH、curl、rsync、procps、配置"]
benchmark["执行公网双源、三段 S3 和 NAS 回传测速"]
benchmarkDecision{"新节点验收是否合格"}
enable[原子写入云服务器配置重启控制器并恢复下载]
replacementFail["新节点验收不合格<br/>进入下一轮退订重购事务"]
lifecycleWait["记录当前阻塞阶段<br/>下一次定时器继续恢复同一事务"]
lifecycleDone["记录订单、实例、测速和状态变化<br/>释放生命周期全局锁"]
controller --> schedule
schedule --> claim
claim -.-> taskStore
claim --> launch
launch --> cloud
cloud --> cloudScript
cloudScript --> inspect
inspect -->|已有完整文件| complete
inspect -->|存在部分文件| partial
inspect -->|没有文件| fresh
partial --> resume
fresh --> requestRange
resume --> requestRange
requestRange --> s3
s3 --> response
response --> remoteFiles
remoteFiles --> remoteCheck
remoteCheck -->|未完成| resume
remoteCheck -->|已完成| remoteComplete
complete --> remoteComplete
remoteComplete --> rsync
rsync --> nasTemp
nasTemp --> verify
verify -->|大小一致| publish
publish --> verified
verified --> assembly
verify -->|大小不一致| requeue
assembly --> compareChecksum
taskStore -.-> expectedChecksum
expectedChecksum --> compareChecksum
compareChecksum -->|一致| completeTask
compareChecksum -->|不一致| hashFail
hashFail --> claim
cloudScript -->|命令失败| fail
rsync -->|传输失败| fail
keepalive -->|连接断开| fail
fail --> requeue
requeue --> claim
controller -.-> renewScan
renewScan --> renewDB
renewDB -.-> controller
controller -.-> expireScan
expireScan -->|发现过期租约| expire
expire --> claim
launch -.-> keepalive
launch -.-> stalled
stalled --> stop
stop --> requeue
controller -.-> lifecycleTimer
lifecycleTimer --> lifecycleLock
lifecycleLock --> pendingTx
pendingTx -->|有| resumeTx
resumeTx --> txStep
txStep --> lifecycleDone
pendingTx -->|无| healthCheck
healthCheck --> retireDecision
retireDecision -->|否| keepActive
keepActive --> lifecycleDone
retireDecision -->|是| drain
drain -.-> schedule
drain --> retire
retire --> refundCheck
refundCheck -->|未生效| lifecycleWait
refundCheck -->|已从服务器列表消失| purchase
purchase --> init
init --> benchmark
benchmark --> benchmarkDecision
benchmarkDecision -->|合格| enable
enable --> lifecycleDone
benchmarkDecision -->|不合格| replacementFail
replacementFail --> lifecycleWait
lifecycleWait --> lifecycleDone
api聚合系统网关链路
flowchart TD
client["客户端<br/>控制台页面或模型 API 调用"]
subgraph console["用户面板"]
web["前端页面"]
nginx{"Nginx 按请求路径分流"}
web --> nginx
end
client --> web
subgraph business["前置网关服务"]
bizEntry[前置网关 API 入口]
bizRoute{"接口类型"}
loginProxy["注册、登录接口<br/>原样代理给 New API"]
bizAuth["读取 Authorization"]
tokenLookup{"在共享 MySQL 中<br/>查询 users.access_token"}
jwtVerify["调用 New API /api/user/self<br/>验证 Dashboard JWT"]
bizExecute["执行业务接口<br/>套餐、订阅、订单、支付、发票、代理商、价格等"]
bizEntry --> bizRoute
bizRoute -->|注册或登录| loginProxy
bizRoute -->|受保护的业务接口| bizAuth
bizAuth --> tokenLookup
tokenLookup -->|找到 Access Token| bizExecute
tokenLookup -->|未找到,可能是 Dashboard JWT| jwtVerify
jwtVerify -->|验证成功| bizExecute
jwtVerify -->|验证失败| bizUnauthorized["返回未登录或无权限"]
bizExecute --> bizResponse["返回业务结果"]
end
subgraph newapi["New API"]
newEntry["New API 请求入口"]
newRoute{"请求类型"}
dashboardAuth["控制台身份认证<br/>登录、注册、JWT、刷新会话"]
apiTokenAuth["校验用户 API Token"]
userPolicy["读取用户、分组、模型权限和额度"]
modelRateLimit{"检查用户或模型请求频率"}
rateReject["超过限制<br/>返回 429"]
channelSelect["根据模型、分组、优先级和渠道状态<br/>选择可用渠道"]
userQuota["请求前额度检查或预扣"]
channelType{"选中的渠道"}
buildMaxxRequest["按渠道类型构造上游请求<br/>携带 网关 内部渠道凭证"]
newResponse["接收上游响应"]
usageParse["解析输入、输出 Token 用量"]
userBilling["按用户模型倍率结算<br/>补扣或退还预扣额度"]
apiResponse["向客户端返回模型响应"]
newEntry --> newRoute
newRoute -->|控制台登录、注册、自身信息| dashboardAuth
newRoute -->|模型调用 /v1/*| apiTokenAuth
apiTokenAuth -->|无效| newUnauthorized["返回 401"]
apiTokenAuth -->|有效| userPolicy
userPolicy -->|无权限或额度不足| newForbidden["拒绝请求"]
userPolicy -->|通过| modelRateLimit
modelRateLimit -->|超过限制| rateReject
modelRateLimit -->|通过| channelSelect
channelSelect --> userQuota
userQuota -->|额度不足| newForbidden
userQuota -->|通过| channelType
channelType --> buildMaxxRequest
newResponse --> usageParse
usageParse --> userBilling
userBilling --> apiResponse
end
nginx -->|前置网关<br/>支付、订阅、发票、价格等| bizEntry
nginx -->|/v1/* 和其他 New API 路径| newEntry
loginProxy --> dashboardAuth
jwtVerify -.-> dashboardAuth
dashboardAuth --> dashboardResponse["签发或验证 Dashboard JWT"]
dashboardResponse --> bizResponse
subgraph maxx["多上游代理网关"]
maxxEntry[请求入口]
internalToken[校验 New API 使用的网关内部 API Token]
tokenConcurrency{"检查该 Token 的<br/>单实例并发上限"}
tokenReject["超过 Token 并发限制<br/>返回 429"]
parseRequest["识别客户端协议和模型<br/>提取 Session ID"]
sessionSources["Session ID 来源<br/>Session_id 请求头<br/>previous_response_id<br/>prompt_cache_key<br/>metadata.session_id / user_id<br/>X-Session-Id<br/>均不存在时生成稳定摘要"]
routeLoad["加载启用的 Route、Provider<br/>路由策略、模型映射和重试配置"]
routeFilter["生成并筛选候选 Provider"]
concurrencyPrefilter{"Provider 是否已达到<br/>配置的并发上限"}
cooldownCheck{"逐级检查冷却状态<br/>Provider<br/>Provider + 协议<br/>Provider + Model<br/>Provider + 协议 + Model"}
modelSupport{"Provider 是否支持<br/>当前模型或协议转换"}
candidateList["得到有序候选 Provider 列表"]
stickyEnabled{"是否启用会话亲和"}
stickyGet["从 Redis 查询会话绑定<br/>协议 + 分组+ API Token / Session ID"]
stickyUsable{"原绑定 Provider<br/>是否仍在候选列表"}
promote["将原绑定 Provider<br/>提升为第一候选"]
normalOrder["使用优先级或<br/>加权随机顺序"]
choose["选择当前候选 Provider"]
mapModel["把客户端模型名<br/>映射为上游模型名"]
convertCheck{"Provider 是否原生支持<br/>客户端请求协议"}
requestConvert["转换请求体、URI 和字段<br/>客户端协议 → Provider 协议"]
keepOriginal["保留原请求协议"]
acquireSlot{"原子占用 Provider 并发槽位"}
nextOnBusy["当前 Provider 已满<br/>直接尝试下一候选<br/>不写入错误冷却"]
adapterExecute["调用对应 Provider Adapter<br/>向具体上游发送请求"]
upstreamResult{"上游执行结果"}
releaseSlot["释放 Provider 并发槽位"]
responseConvert["必要时转换响应<br/>Provider 协议 → 客户端协议"]
maxxCost["记录上游 Token、耗时和成本"]
exactSuccess["记录成功<br/>清除精确的 Provider + 协议 + 模型冷却<br/>重置这一层失败次数"]
stickySet["将成功 Provider 写回 Redis<br/>失败不影响本次响应"]
maxxResponse["响应返回 New API"]
maxxEntry --> internalToken
internalToken -->|无效| maxxUnauthorized["返回 401"]
internalToken -->|有效| tokenConcurrency
tokenConcurrency -->|已满| tokenReject
tokenConcurrency -->|可用| parseRequest
sessionSources -.-> parseRequest
parseRequest --> routeLoad
routeLoad --> routeFilter
routeFilter --> concurrencyPrefilter
concurrencyPrefilter -->|已满| routeFilter
concurrencyPrefilter -->|未满| cooldownCheck
cooldownCheck -->|仍在冷却| routeFilter
cooldownCheck -->|没有冷却或已经到期| modelSupport
modelSupport -->|不支持| routeFilter
modelSupport -->|支持或可以转换| candidateList
candidateList --> stickyEnabled
stickyEnabled -->|是| stickyGet
stickyGet --> stickyUsable
stickyUsable -->|可用| promote
stickyUsable -->|已冷却、已满或已下线| normalOrder
stickyEnabled -->|否| normalOrder
promote --> choose
normalOrder --> choose
choose --> mapModel
mapModel --> convertCheck
convertCheck -->|不原生支持| requestConvert
convertCheck -->|原生支持| keepOriginal
requestConvert --> acquireSlot
keepOriginal --> acquireSlot
acquireSlot -->|占用失败| nextOnBusy
nextOnBusy --> choose
acquireSlot -->|占用成功| adapterExecute
adapterExecute --> upstreamResult
upstreamResult --> releaseSlot
releaseSlot -->|成功| responseConvert
responseConvert --> maxxCost
maxxCost --> exactSuccess
exactSuccess --> stickySet
stickySet --> maxxResponse
end
buildMaxxRequest --> maxxEntry
maxxResponse --> newResponse
subgraph cooldownFlow["多上游代理网关 错误分类、冷却、重试与降级"]
classify["Provider Adapter 解析<br/>HTTP 状态码、响应体和网络错误"]
committed{"响应是否已经<br/>写给客户端"}
committedStop["记录失败并结束<br/>避免切换上游后重复响应"]
scope{判断错误影响范围}
requestScope["当前请求级<br/>参数错误、请求过大、内容过滤等"]
modelScope["模型级<br/>某个模型不存在或暂时不可用<br/>冷却键:Provider + 协议 + Model"]
keyScope["密钥级<br/>额度耗尽、Key 限流、认证失败<br/>冷却键:Provider + 协议"]
endpointScope["端点级<br/>某种协议对应的上游端点异常<br/>冷却键:Provider + 协议"]
providerScope["Provider 级<br/>DNS、连接超时、整个上游不可达<br/>冷却键:Provider"]
noCooldown["不写入冷却"]
duration{"确定冷却结束时间"}
explicitTime["优先使用上游返回的<br/>明确恢复时间 CooldownUntil"]
retryAfter["其次使用<br/>当前时间 + Retry-After"]
localPolicy["没有上游时间时<br/>按错误原因和连续失败次数计算"]
policies["默认冷却策略<br/>5xx:5、10、15 秒递增,最长 10 分钟<br/>网络错误:5、10、20、40 秒递增,最长 30 分钟<br/>额度耗尽:1 小时<br/>限流或上游并发限制:5 秒<br/>认证失败:1 小时<br/>模型不可用:5 分钟<br/>未知错误:线性递增,最长 5 分钟"]
saveCooldown["写入冷却状态<br/>相同键保留更晚的结束时间"]
currentCandidates["当前请求继续使用请求开始时<br/>已经生成的候选 Provider 列表<br/>不会重新执行冷却筛选"]
requestRetryable{"当前请求级错误<br/>是否允许重试"}
requestFailed["直接返回当前请求错误<br/>不冷却,也不切换 Provider"]
retryable{"当前 Provider 的错误<br/>是否允许重试"}
attemptsLeft{"当前 Provider 是否还有<br/>同渠道重试次数"}
waitSource{"本次重试等待时间来源"}
retryAfterWait["上游返回 Retry-After<br/>按该时长等待<br/>不受 RetryConfig 最大间隔限制"]
retryConfigWait["上游未返回 Retry-After<br/>按 RetryConfig 计算退避时间<br/>不超过配置的最大间隔"]
waitState{"等待期间请求是否<br/>仍然有效"}
requestStopped["客户端断开或请求超时<br/>停止等待并结束当前请求"]
retrySame["等待结束<br/>再次请求同一个 Provider"]
nextProvider{"是否还有下一个<br/>候选 Provider"}
failover["切换到下一候选 Provider<br/>该 Provider 使用自己的重试配置"]
allFailed["所有候选均失败<br/>返回最终错误"]
classify --> committed
committed -->|已经写出| committedStop
committed -->|尚未写出| scope
scope -->|Request| requestScope
scope -->|Model| modelScope
scope -->|Key| keyScope
scope -->|Endpoint| endpointScope
scope -->|Provider| providerScope
requestScope --> noCooldown
modelScope --> duration
keyScope --> duration
endpointScope --> duration
providerScope --> duration
duration -->|上游返回存在冷却时间| explicitTime
duration -->|否则存在 至少冷却时间| retryAfter
duration -->|都不存在| localPolicy
localPolicy -.-> policies
explicitTime --> saveCooldown
retryAfter --> saveCooldown
localPolicy --> saveCooldown
noCooldown --> requestRetryable
requestRetryable -->|否| requestFailed
requestRetryable -->|是| currentCandidates
saveCooldown --> currentCandidates
saveCooldown -.->|只影响后续新请求| cooldownCheck
currentCandidates --> retryable
retryable -->|是| attemptsLeft
retryable -->|否,不再重试当前 Provider| nextProvider
attemptsLeft -->|有| waitSource
waitSource -->|存在 Retry-After| retryAfterWait
waitSource -->|不存在 Retry-After| retryConfigWait
retryAfterWait --> waitState
retryConfigWait --> waitState
waitState -->|仍然有效| retrySame
waitState -->|已取消或超时| requestStopped
attemptsLeft -->|没有,当前 Provider 重试结束| nextProvider
nextProvider -->|有| failover
nextProvider -->|没有| allFailed
end
releaseSlot -->|失败| classify
retrySame --> adapterExecute
failover --> choose
committedStop --> maxxResponse
requestFailed --> maxxResponse
requestStopped --> maxxResponse
allFailed --> maxxResponse
subgraph data["数据与分布式状态"]
sharedMySQL[(共享 MySQLNew API 用户、Token、渠道、套餐、订单、订阅、支付、发票等)]
maxxMySQL[("Maxx MySQL<br/>Provider、Route、模型映射、重试配置<br/>请求记录、成本、冷却持久化")]
redis[("Redis<br/>会话亲和绑定<br/>冷却状态和冷却版本号<br/>发布冷却变更通知")]
localCache["各网关实例的本地内存<br/>用于快速查询冷却状态"]
redisWrite[写入 Redis 冷却值]
localWrite["更新当前网关实例的<br/>本地冷却状态"]
mysqlWrite["将冷却状态持久化到 MySQL"]
versionBump["在 Redis 中原子递增<br/>该 Provider 的冷却版本号"]
publishEvent["发布冷却变更通知<br/>Provider ID + 最新版本号"]
instanceSync["其他网关实例<br/>接收变更通知或主动检查版本号"]
versionCompare{"本地已知版本号<br/>是否落后于 Redis"}
reloadCooldown["从 Redis 重新加载<br/>该 Provider 的全部冷却状态"]
ignoreEvent["版本未变化<br/>忽略重复或过期通知"]
saveCooldown --> redisWrite
redisWrite --> redis
redisWrite --> localWrite
localWrite --> localCache
localWrite --> mysqlWrite
mysqlWrite --> maxxMySQL
mysqlWrite --> versionBump
versionBump --> redis
versionBump --> publishEvent
publishEvent --> instanceSync
redis -.->|主动检查最新版本号| instanceSync
instanceSync --> versionCompare
versionCompare -->|是| reloadCooldown
versionCompare -->|否| ignoreEvent
redis --> reloadCooldown
reloadCooldown --> localCache
localCache -.-> cooldownCheck
stickyGet <--> redis
stickySet --> redis
routeLoad <--> maxxMySQL
maxxCost --> maxxMySQL
dashboardAuth <--> sharedMySQL
apiTokenAuth <--> sharedMySQL
userPolicy <--> sharedMySQL
channelSelect <--> sharedMySQL
userBilling --> sharedMySQL
tokenLookup <--> sharedMySQL
bizExecute <--> sharedMySQL
end
subgraph upstreams["具体模型上游"]
upstreamA["Provider A"]
upstreamB["Provider B"]
upstreamC["Provider C"]
end
adapterExecute --> upstreamA
adapterExecute --> upstreamB
adapterExecute --> upstreamC
eks 交付系统
flowchart TD
developer["开发者"]
%% =========================
%% 外部镜像来源
%% =========================
subgraph imageSource["外部镜像来源"]
release["业务系统发布流程<br/>构建并发布容器镜像"]
imageRegistry[("镜像仓库<br/>保存容器镜像")]
writeImageTag["自动更新 IaC 仓库中的镜像版本"]
release --> imageRegistry
release --> writeImageTag
end
%% =========================
%% IaC 仓库
%% =========================
subgraph iacRepo["基础设施与部署配置仓库(IaC 仓库)"]
appConfig["修改应用或平台配置<br/>apps/** 或 platform/**"]
appPR["创建 IaC 仓库合并请求"]
manifestCheck["Kubernetes 资源清单预检查<br/>生成 Kustomize 配置<br/>校验资源格式和字段<br/>检查开发与生产环境的密钥隔离"]
appReview["人工评审应用或平台配置"]
appMerge["合并应用或平台配置"]
infraConfig["修改云基础设施配置<br/>infra/**"]
infraPR["创建 IaC 仓库合并请求"]
tofuPlan["OpenTofu 变更预演<br/>展示将新增、修改或删除的云资源"]
infraReview["人工评审基础设施变更"]
infraMerge["合并基础设施配置"]
mainBranch[("IaC 仓库 main 分支<br/>保存应用、平台和云基础设施配置")]
appConfig -->|创建合并请求| appPR
appPR --> manifestCheck
manifestCheck --> appReview
appReview --> appMerge
appMerge --> mainBranch
appConfig -->|直接提交<br/>跳过合并请求预检查| mainBranch
infraConfig -->|创建合并请求| infraPR
infraPR --> tofuPlan
tofuPlan --> infraReview
infraReview --> infraMerge
infraMerge --> mainBranch
infraConfig -->|直接提交<br/>跳过变更预演和人工评审| mainBranch
end
developer --> appConfig
developer --> infraConfig
writeImageTag -->|写入新镜像版本| mainBranch
%% =========================
%% Argo CD 同步
%% =========================
subgraph gitops["Argo CD 应用部署链路"]
argoPoll["Argo CD 每约 180 秒<br/>读取 IaC 仓库 main 分支"]
renderManifest["读取 apps/** 和 platform/**<br/>生成 Kubernetes 资源清单"]
compareState{"仓库配置与集群当前状态<br/>是否存在差异?"}
unchanged["没有差异<br/>保持当前运行状态"]
sync["发现差异后按仓库配置同步<br/>创建或更新发生变化的资源<br/>删除仓库中已移除的受管资源<br/>同步后集群状态与仓库配置保持一致"]
rollingUpdate["多副本服务滚动更新<br/>先创建新 Pod<br/>新 Pod 通过就绪检查后接收流量<br/>再逐个停止旧 Pod"]
recreateUpdate["单副本服务重新创建<br/>先停止旧 Pod,再创建新 Pod<br/>更新期间可能短暂中断"]
configResources["其他 Kubernetes 资源<br/>Service、Ingress、Secret<br/>ExternalSecret、PodDisruptionBudget"]
pod["运行中的 Pod"]
clusterChange["集群资源被手动修改<br/>或运行状态发生意外变化"]
mainBranch -->|读取应用和平台配置| argoPoll
argoPoll --> renderManifest
renderManifest --> compareState
compareState -->|没有差异| unchanged
compareState -->|存在差异| sync
sync -->|多副本服务| rollingUpdate
sync -->|单副本服务| recreateUpdate
sync --> configResources
rollingUpdate --> pod
recreateUpdate --> pod
clusterChange -->|下次检查发现差异| compareState
end
imageRegistry -->|创建 Pod 时拉取镜像| pod
%% =========================
%% 用户请求
%% =========================
subgraph requestFlow["用户请求链路"]
user["用户请求"]
ingress["Ingress<br/>根据域名和路径转发请求"]
service["Service<br/>提供稳定访问地址<br/>并把请求分配给可用 Pod"]
user --> ingress
ingress --> service
service -->|只转发给通过就绪检查的 Pod| pod
end
configResources -.-> ingress
configResources -.-> service
%% =========================
%% Pod 状态检查
%% =========================
subgraph healthCheck["Pod 状态检查"]
startup["启动检查<br/>判断应用是否完成初始化"]
readiness["就绪检查<br/>失败时停止接收新请求"]
liveness["存活检查<br/>持续失败时重新启动容器"]
pod --> startup
pod --> readiness
pod --> liveness
liveness -->|持续失败| pod
end
%% =========================
%% OpenTofu
%% =========================
subgraph infrastructure["OpenTofu 云基础设施链路"]
applyWorkflow["GitHub Actions<br/>检测到 main 分支中的 infra/** 发生变化"]
acquireLock["获取状态锁<br/>防止多个 OpenTofu 任务同时执行"]
lockTable[("AWS DynamoDB 状态锁表<br/>code0-iac-tofu-locks<br/>us-west-2")]
readState["读取 OpenTofu 状态文件"]
stateBucket[("AWS S3 状态文件存储<br/>记录 OpenTofu 已管理的云资源<br/>启用版本控制和 KMS 加密")]
tofuApply["OpenTofu 执行基础设施变更"]
cloudAPI["调用 AWS 接口"]
cloudResources["AWS 云资源<br/>网络、Kubernetes 集群<br/>数据库、权限及其他基础设施"]
saveState["变更完成后<br/>把最新状态写回 S3"]
releaseLock["释放 DynamoDB 状态锁"]
mainBranch -->|infra/** 发生变化时触发| applyWorkflow
applyWorkflow --> acquireLock
acquireLock -.-> lockTable
acquireLock --> readState
stateBucket -.-> readState
readState --> tofuApply
tofuApply --> cloudAPI
cloudAPI --> cloudResources
cloudResources -->|变更完成| saveState
saveState --> stateBucket
saveState --> releaseLock
releaseLock -.-> lockTable
end
cloudResources -.->|提供运行环境| pod
%% =========================
%% 监控与日志
%% =========================
subgraph observability["监控与日志"]
prometheus["Prometheus<br/>采集运行指标"]
logCollector["日志采集组件<br/>收集容器日志"]
loki["Loki<br/>保存和查询日志"]
grafana["Grafana<br/>查看指标、日志和告警"]
pod --> prometheus
pod --> logCollector
logCollector --> loki
prometheus --> grafana
loki --> grafana
end
支付系统
flowchart TD
client["客户端<br/>选择支付方式并发起支付"]
subgraph create["一、创建预约订单和支付订单"]
preview["价格预览<br/>计算课程价格、优惠和应付金额"]
booking["创建预约订单<br/>锁定教师时间 15 分钟"]
payment["创建支付订单<br/>锁定价格并预留优惠券"]
channel{"本次选择的支付渠道"}
preview --> booking
booking --> payment
payment --> channel
end
client --> preview
subgraph attempt["二、创建或复用支付尝试"]
sameChannel["继续使用原支付渠道"]
reusable{"原支付尝试和支付链接<br/>是否仍然有效"}
reuse["复用原支付尝试和支付链接"]
newAttempt["创建新的支付尝试"]
paypalOrder["创建新的 PayPal 订单"]
checkout["返回支付链接"]
channel --> sameChannel
sameChannel --> reusable
reusable -->|有效| reuse
reusable -->|无效| newAttempt
channel -->|切换支付渠道| newAttempt
newAttempt --> paypalOrder
paypalOrder --> checkout
reuse --> checkout
end
subgraph paymentFlow["三、用户授权、扣款和主动查询"]
authorize["用户打开支付链接<br/>完成 PayPal、Apple Pay 或 Google Pay 授权"]
clientQuery["客户端通知平台支付完成<br/>并查询支付结果"]
queryOrder["平台主动查询 PayPal 订单"]
orderState{"PayPal 订单状态"}
approved["订单已批准"]
capture["平台向 PayPal 发起正式扣款"]
queryCapture["重新查询 PayPal 订单详情<br/>读取最新真实扣款记录"]
captureState{"真实扣款状态"}
authorize --> clientQuery
clientQuery --> queryOrder
queryOrder --> orderState
orderState -->|已批准| approved
approved --> capture
capture --> queryCapture
orderState -->|已完成| queryCapture
queryCapture --> captureState
end
checkout --> authorize
orderState -->|处理中| processing
orderState -->|失败| failed
captureState -->|扣款已完成| settle
captureState -->|扣款处理中| processing
captureState -->|暂无扣款记录| noCapture
captureState -->|扣款被拒绝| declined
captureState -->|扣款失败| failed
noCapture["暂无真实扣款记录<br/>暂不判定支付成功"]
declined["扣款被支付渠道拒绝"]
noCapture --> processing
declined --> failed
subgraph processingFlow["四、支付处理中和异步恢复"]
processing["保持支付处理中<br/>不提前标记为支付成功"]
waitEvent["等待客户端再次查询<br/>或接收 PayPal 异步通知"]
webhook["接收 PayPal 异步通知"]
reconcile["主动查询 PayPal 最新订单状态<br/>不直接相信客户端或通知中的结果"]
processing --> waitEvent
waitEvent -->|客户端再次查询| reconcile
waitEvent -->|收到异步通知| webhook
webhook --> reconcile
reconcile --> queryOrder
end
subgraph settlement["五、统一幂等结算"]
settle["进入统一结算入口"]
lockPayment["在数据库事务中<br/>锁定支付订单和支付尝试"]
checkSettled{"支付订单是否已经结算"}
settled["直接返回已有结算结果"]
markSuccess["支付订单标记为已支付<br/>支付尝试标记为成功"]
confirmBooking["确认预约订单<br/>正式占用教师时间"]
consumeCoupon["核销预留的优惠券"]
commitSuccess["提交结算事务"]
notify["发送学生和导师付款确认通知"]
settle --> lockPayment
lockPayment --> checkSettled
checkSettled -->|已经结算| settled
checkSettled -->|尚未结算| markSuccess
markSuccess --> confirmBooking
confirmBooking --> consumeCoupon
consumeCoupon --> commitSuccess
commitSuccess --> notify
end
subgraph failureFlow["六、正常支付失败"]
failed["确认支付失败"]
markAttemptFailed["当前支付尝试标记为失败"]
retryChoice{"用户是否重新支付"}
retrySame["继续使用同一支付渠道"]
changeChannel["选择其他支付渠道"]
abandon["放弃支付<br/>等待预约订单到期"]
failed --> markAttemptFailed
markAttemptFailed --> retryChoice
retryChoice -->|同一渠道| retrySame
retryChoice -->|其他渠道| changeChannel
retryChoice -->|暂不支付| abandon
retrySame --> newAttempt
changeChannel --> newAttempt
end
subgraph timeout["七、超时扫描和最终支付核验"]
timeoutJob["后台定时任务<br/>扫描未确认的预约订单"]
expired{"15 分钟预约锁定时间<br/>是否已经到期"}
nextScan["暂不处理<br/>等待下一轮扫描"]
lockExpired["开启数据库事务<br/>锁定预约订单和支付订单"]
reread["重新读取最新状态<br/>防止与主动查询或异步通知并发冲突"]
currentState{"重新读取后的支付状态"}
alreadyPaid["已经支付成功"]
keepBooking["保持预约订单有效<br/>不释放教师时间和优惠券"]
noAttempt["没有有效的支付尝试"]
clearlyFailed["支付已经明确失败"]
needVerify["支付处理中、状态未知<br/>或存在尚未确认的支付尝试"]
startVerify["记录最终核验截止时间<br/>预约到期时间 + 1 分钟"]
queryLatest["主动查询 PayPal 订单<br/>并读取最新真实扣款记录"]
verifyResult{"最终核验结果"}
verifyApproved["订单已经批准"]
verifyCompleted["真实扣款已完成"]
verifyProcessing["真实扣款仍在处理中"]
verifyNoCapture["暂无真实扣款记录"]
verifyDeclined["真实扣款被拒绝"]
verifyFailed["真实扣款失败"]
verifyUnpaid["确认用户尚未完成支付"]
verifyUnavailable["PayPal 查询超时、网络异常<br/>或服务暂时不可用"]
withinWindow{"是否仍在 1 分钟<br/>最终核验期内"}
waitQuery["短暂等待<br/>进入下一次主动查询"]
abnormal["标记为支付核验异常<br/>不能直接判定为未支付"]
preserve["继续保留预约订单<br/>教师时间和优惠券"]
adminAlert["向管理员发送异常告警邮件"]
autoRecheck["后台继续自动重查"]
manualReview["管理员在后台人工核对"]
timeoutJob --> expired
expired -->|没有到期| nextScan
expired -->|已经到期| lockExpired
lockExpired --> reread
reread --> currentState
currentState -->|已经支付| alreadyPaid
alreadyPaid --> keepBooking
currentState -->|没有有效支付尝试| noAttempt
currentState -->|明确支付失败| clearlyFailed
noAttempt --> releaseTx
clearlyFailed --> releaseTx
currentState -->|处理中或状态未知| needVerify
needVerify --> startVerify
startVerify --> queryLatest
queryLatest --> verifyResult
verifyResult -->|订单已批准| verifyApproved
verifyApproved --> capture
verifyResult -->|扣款已完成| verifyCompleted
verifyCompleted --> settle
verifyResult -->|扣款处理中| verifyProcessing
verifyResult -->|暂无扣款记录| verifyNoCapture
verifyResult -->|暂时无法查询| verifyUnavailable
verifyProcessing --> withinWindow
verifyNoCapture --> withinWindow
verifyUnavailable --> withinWindow
withinWindow -->|仍在核验期内| waitQuery
waitQuery --> queryLatest
withinWindow -->|核验期已结束| abnormal
verifyResult -->|扣款被拒绝| verifyDeclined
verifyResult -->|扣款失败| verifyFailed
verifyResult -->|确认未支付| verifyUnpaid
verifyDeclined --> releaseTx
verifyFailed --> releaseTx
verifyUnpaid --> releaseTx
abnormal --> preserve
abnormal --> adminAlert
abnormal --> autoRecheck
abnormal --> manualReview
autoRecheck --> queryLatest
manualReview --> queryLatest
end
payment -.-> timeoutJob
subgraph releaseFlow["八、取消预约和释放资源"]
releaseTx["在数据库事务中<br/>再次锁定并复核订单状态"]
releaseCheck{"复核时是否已经支付成功"}
stopRelease["停止释放<br/>转入统一结算"]
cancelPayment["支付订单标记为已过期"]
closeAttempt["未完成的支付尝试标记为已结束"]
cancelBooking["取消预约订单<br/>释放教师时间"]
releaseCoupon["释放预留的优惠券"]
commitRelease["提交事务"]
expiredResult["客户端再次查询时<br/>返回预约和支付已过期"]
releaseTx --> releaseCheck
releaseCheck -->|已经支付| stopRelease
stopRelease --> settle
releaseCheck -->|仍未支付| cancelPayment
cancelPayment --> closeAttempt
closeAttempt --> cancelBooking
cancelBooking --> releaseCoupon
releaseCoupon --> commitRelease
commitRelease --> expiredResult
end
agent 系统
flowchart TD
user["操作者"]
web["Web 工作台"]
client["接口访问层"]
user --> web
web --> client
subgraph core_group["核心服务入口"]
direction TB
http_in["1. 接收 HTTP 请求"]
request_ctx["生成请求编号和调用链编号"]
middleware["执行访问日志、超时、限流<br/>跨域和请求体大小检查"]
request_route{"识别请求类型"}
http_in --> request_ctx
request_ctx --> middleware
middleware --> request_route
end
client --> http_in
subgraph management_group["系统管理"]
direction LR
manage_agent["AI 助手配置"]
manage_model["模型接入配置"]
manage_skill["技能配置"]
manage_module["可插拔模块配置"]
manage_schedule["定时计划配置"]
manage_system["系统运行配置"]
manage_agent --> manage_model
manage_model --> manage_skill
manage_skill --> manage_module
manage_module --> manage_schedule
manage_schedule --> manage_system
end
request_route -->|系统管理请求| manage_agent
subgraph call_group["统一功能调用"]
direction TB
call_entry["根据功能名称查找实现"]
call_find["读取当前已启用的功能实现"]
call_actor{"是否由 AI 助手发起"}
call_permission["执行权限检查"]
call_execute["调用对应功能模块"]
call_result{"执行结果"}
call_success["记录成功并触发后续处理"]
call_failure["记录失败原因"]
call_return["返回调用结果"]
call_entry --> call_find
call_find --> call_actor
call_actor -->|是| call_permission
call_actor -->|否| call_execute
call_execute --> call_result
call_result -->|成功| call_success
call_result -->|失败| call_failure
call_success --> call_return
call_failure --> call_return
end
request_route -->|业务功能请求| call_entry
subgraph module_group["可插拔模块运行层"]
direction TB
module_config["读取模块声明<br/>功能、订阅、页面、设置和模型能力"]
module_snapshot["生成当前可用模块清单<br/>配置变化时递增版本"]
module_lookup["根据功能名称找到目标模块"]
module_kind{"模块运行方式"}
module_process["独立进程运行<br/>通过标准输入输出交换 JSON 消息"]
module_wasm["Wasm 运行<br/>限制内存、时间和计算量"]
module_response["返回模块执行结果"]
module_diagnostic["模块异常时记录诊断<br/>失效进程按需重建"]
module_config --> module_snapshot
module_snapshot --> module_lookup
module_lookup --> module_kind
module_kind -->|独立进程| module_process
module_kind -->|Wasm| module_wasm
module_process --> module_response
module_wasm --> module_response
module_process -->|异常| module_diagnostic
module_wasm -->|异常| module_diagnostic
end
call_find -.-> module_snapshot
call_execute --> module_lookup
module_response --> call_result
subgraph conversation_group["会话管理"]
direction TB
msg_append["2. 追加用户消息"]
msg_save["3. 保存会话、线路、分支和原始消息"]
msg_identify["生成消息编号和分支内顺序"]
conv_db[("会话数据库<br/>原始消息唯一保存位置")]
msg_return["4. 立即返回消息写入结果"]
msg_notify["通知前端显示新消息"]
reply_background["5. 后台启动回复流程<br/>不阻塞消息写入接口"]
msg_append --> msg_save
msg_save --> msg_identify
msg_identify --> conv_db
msg_identify --> msg_return
msg_identify --> msg_notify
msg_identify --> reply_background
end
call_entry -->|追加消息| msg_append
msg_return --> call_return
msg_notify --> web
subgraph selector_group["回复处理选择"]
direction TB
reply_received["6. 收到待回复的用户消息"]
reply_find["查找可以处理会话回复的模块"]
reply_order["按优先级稳定排序"]
reply_ask["依次询问是否处理本次回复"]
reply_choice{"处理结果"}
reply_continue["跳过、失败或不适用<br/>继续下一个模块"]
reply_claimed["确定本次回复处理模块"]
reply_none["没有模块接管<br/>记录未处理状态"]
reply_received --> reply_find
reply_find --> reply_order
reply_order --> reply_ask
reply_ask --> reply_choice
reply_choice -->|不接管| reply_continue
reply_continue --> reply_ask
reply_choice -->|接管或完成| reply_claimed
reply_order -->|没有可用模块| reply_none
end
reply_background --> reply_received
subgraph assistant_group["AI 助手运行模块"]
direction TB
assistant_default["7. 默认回复模块"]
assistant_dedup["领取消息处理记录<br/>避免同一消息重复回复"]
assistant_load["加载 AI 助手、模型、技能和生成参数"]
assistant_source["读取当前用户消息"]
assistant_history["按当前分支分页读取历史"]
assistant_skills["加载已启用技能"]
assistant_tools["准备命令和技能调用能力"]
assistant_context["开始组装模型上下文"]
runtime_db[("AI 助手运行数据库<br/>处理记录、执行状态和历史摘要")]
assistant_default --> assistant_dedup
assistant_dedup --> runtime_db
assistant_dedup --> assistant_load
assistant_load --> assistant_source
assistant_source --> assistant_history
assistant_history --> assistant_skills
assistant_skills --> assistant_tools
assistant_tools --> assistant_context
end
reply_claimed --> assistant_default
assistant_source -.->|读取单条消息| call_entry
assistant_history -.->|分页读取历史| call_entry
subgraph context_group["上下文管理"]
direction TB
context_fixed["加入系统要求、AI 助手信息<br/>技能说明和工具定义"]
context_budget["计算可用输入容量<br/>模型窗口减去输出预留和安全余量"]
context_request["请求记忆等模块补充上下文"]
context_accept["按优先级和来源上限<br/>接纳补充内容"]
context_history["将剩余容量分配给会话历史"]
context_check{"历史是否达到压缩阈值"}
context_find_summary["查找已有历史摘要"]
context_validate{"摘要是否仍属于<br/>当前分支的有效历史"}
context_summarize["按完整对话回合生成摘要"]
context_save["保存摘要边界、内容版本和历史指纹"]
context_final{"调用模型前再次检查总容量"}
context_error["停止生成并返回容量错误<br/>不静默删除消息"]
context_ready["形成最终模型输入"]
assistant_context --> context_fixed
context_fixed --> context_budget
context_budget --> context_request
context_request --> context_accept
context_accept --> context_history
context_history --> context_check
context_check -->|未达到阈值| context_final
context_check -->|达到阈值| context_find_summary
context_find_summary --> context_validate
context_validate -->|有效| context_final
context_validate -->|无效或不存在| context_summarize
context_summarize --> context_save
context_save --> runtime_db
context_save --> context_final
context_final -->|超出容量| context_error
context_final -->|通过| context_ready
end
subgraph memory_group["记忆系统"]
direction TB
memory_prepare["收到补充上下文请求"]
memory_scope["根据当前 AI 助手和记忆分类<br/>确定检索范围"]
memory_search["检索相关记忆"]
memory_db[("记忆数据库<br/>提炼内容、来源引用、向量和关系")]
memory_return["返回预算内的记忆内容和来源"]
memory_evidence{"是否需要核对原始依据"}
memory_get_source["根据消息编号读取原文"]
memory_manage["显式新增、查询和审查记忆"]
memory_future["自动从会话提炼记忆<br/>当前尚未接入"]
memory_prepare --> memory_scope
memory_scope --> memory_search
memory_search --> memory_db
memory_search --> memory_return
memory_return --> memory_evidence
memory_evidence -->|需要| memory_get_source
memory_manage --> memory_db
memory_future -.-> memory_db
end
context_request --> memory_prepare
memory_return --> context_accept
memory_get_source -.-> call_entry
msg_identify -.->|未来只传消息编号| memory_future
subgraph model_group["模型调用与工具执行循环"]
direction TB
model_call["8. 请求模型服务"]
model_permission["检查模型调用权限"]
model_endpoint["读取模型地址、凭证和能力"]
model_convert["转换统一请求为目标模型格式"]
model_external["外部模型服务"]
model_parse["解析模型响应"]
model_result{"模型返回类型"}
model_final["得到最终回复"]
model_tool["模型要求调用工具"]
model_continue["将工具结果加入上下文<br/>继续请求模型"]
context_ready --> model_call
model_call --> model_permission
model_permission --> model_endpoint
model_endpoint --> model_convert
model_convert --> model_external
model_external --> model_parse
model_parse --> model_result
model_result -->|最终内容| model_final
model_result -->|工具调用| model_tool
model_continue --> model_call
end
subgraph tool_group["命令与技能执行"]
direction TB
tool_type{"工具类型"}
tool_command["解析本地命令、参数<br/>工作目录和超时"]
tool_skill["读取已启用技能的入口和配置"]
tool_permission["检查工具操作权限"]
tool_run_command["启动本地命令"]
tool_run_skill["执行技能脚本"]
tool_state{"执行状态"}
tool_done["返回执行结果"]
tool_pending["保存等待审批或异步执行状态"]
tool_resume["审批或异步操作完成后恢复"]
model_tool --> tool_type
tool_type -->|命令| tool_command
tool_type -->|技能| tool_skill
tool_command --> tool_permission
tool_skill --> tool_permission
tool_permission --> tool_run_command
tool_permission --> tool_run_skill
tool_run_command --> tool_state
tool_run_skill --> tool_state
tool_state -->|完成| tool_done
tool_state -->|等待| tool_pending
tool_pending --> tool_resume
tool_resume --> tool_done
tool_done --> model_continue
end
subgraph permission_group["权限和审批"]
direction TB
permission_input["根据 AI 助手、操作、目标<br/>会话和任务生成权限请求"]
permission_policy["读取权限策略和命令匹配规则"]
permission_decision{"权限结果"}
permission_allow["允许执行"]
permission_deny["拒绝执行并记录原因"]
permission_approval["创建待审批事项"]
permission_user{"操作者审批结果"}
permission_grant["生成临时许可<br/>单次、当前回复或当前会话"]
permission_db[("权限数据库<br/>权限事件、审批和临时许可")]
permission_input --> permission_policy
permission_policy --> permission_decision
permission_decision -->|允许| permission_allow
permission_decision -->|拒绝| permission_deny
permission_decision -->|需要确认| permission_approval
permission_approval --> permission_user
permission_user -->|批准| permission_grant
permission_user -->|拒绝| permission_deny
permission_grant --> permission_allow
permission_policy --> permission_db
permission_approval --> permission_db
permission_grant --> permission_db
end
call_permission --> permission_input
model_permission --> permission_input
tool_permission --> permission_input
permission_allow --> call_execute
permission_allow --> model_endpoint
permission_allow --> tool_run_command
permission_allow --> tool_run_skill
permission_approval --> tool_pending
permission_deny --> call_failure
subgraph reply_write_group["回复写回"]
direction TB
reply_prepare["9. 整理最终回复格式"]
reply_append["调用追加消息功能"]
reply_save["保存 AI 助手回复"]
reply_db[("会话数据库")]
reply_notify["通知前端更新会话"]
model_final --> reply_prepare
reply_prepare --> reply_append
reply_append --> reply_save
reply_save --> reply_db
reply_save --> reply_notify
end
reply_notify --> web
subgraph event_group["事件记录与订阅"]
direction TB
event_publish["记录已经发生的事实<br/>只携带编号和必要信息"]
event_db[("事件数据库<br/>事件内容和投递状态")]
event_find["查找订阅该事件的模块"]
event_create["为每个订阅者创建投递任务"]
event_loop["后台执行投递"]
event_order["按优先级和稳定顺序排列"]
event_invoke["调用订阅模块"]
event_result{"投递结果"}
event_success["标记投递成功"]
event_failure["记录失败并保留恢复依据"]
event_publish --> event_db
event_db --> event_find
event_find --> event_create
event_create --> event_db
event_create --> event_loop
event_loop --> event_order
event_order --> event_invoke
event_invoke --> event_result
event_result -->|成功| event_success
event_result -->|失败| event_failure
event_success --> event_db
event_failure --> event_db
end
msg_identify -->|消息已创建| event_publish
call_success -->|功能执行成功| event_publish
tool_pending -->|操作状态变化| event_publish
event_invoke --> module_lookup
subgraph subscription_group["主要订阅关系"]
direction TB
sub_operation["异步操作状态变化"]
sub_restore["恢复等待中的 AI 助手执行"]
sub_run["编排任务已创建"]
sub_workflow_start["启动任务编排"]
sub_stage["编排阶段变化"]
sub_workflow_update["更新编排状态"]
sub_artifact["任务产物已创建"]
sub_artifact_record["登记任务产物"]
sub_schedule["定时计划到期"]
sub_schedule_run["启动定时任务"]
sub_operation --> sub_restore
sub_run --> sub_workflow_start
sub_stage --> sub_workflow_update
sub_artifact --> sub_artifact_record
sub_schedule --> sub_schedule_run
end
event_invoke --> sub_operation
event_invoke --> sub_run
event_invoke --> sub_stage
event_invoke --> sub_artifact
event_invoke --> sub_schedule
sub_restore --> tool_resume
subgraph workflow_group["任务编排"]
direction TB
workflow_entry["显式创建任务<br/>或由定时计划触发"]
workflow_create["创建一次任务运行"]
workflow_plan["生成结构化执行方案"]
workflow_steps["从方案生成任务步骤"]
workflow_run_agent["调用 AI 助手执行步骤"]
workflow_check{"检查步骤结果"}
workflow_approval["等待确认或审批"]
workflow_retry["重试、调整方案或结束"]
workflow_artifact["保存文件或结构化产物"]
workflow_accept["根据完成标准验收"]
workflow_finish["生成最终任务结果"]
workflow_db[("任务编排数据库<br/>运行、方案、步骤、尝试<br/>审批、产物和任务交接")]
workflow_entry --> workflow_create
workflow_create --> workflow_db
workflow_create --> workflow_plan
workflow_plan --> workflow_db
workflow_plan --> workflow_steps
workflow_steps --> workflow_db
workflow_steps --> workflow_run_agent
workflow_run_agent --> workflow_check
workflow_check -->|需要确认| workflow_approval
workflow_check -->|失败可重试| workflow_retry
workflow_check -->|成功| workflow_artifact
workflow_approval --> workflow_steps
workflow_retry --> workflow_steps
workflow_artifact --> workflow_db
workflow_artifact --> workflow_accept
workflow_accept -->|通过| workflow_finish
workflow_accept -->|未通过| workflow_retry
end
sub_workflow_start --> workflow_entry
sub_workflow_update --> workflow_check
sub_artifact_record --> workflow_db
sub_schedule_run --> workflow_entry
workflow_run_agent -.-> assistant_default
workflow_artifact -->|产物已创建| event_publish
workflow_finish -->|编排阶段变化| event_publish
workflow_note["普通会话默认不自动进入任务编排<br/>当前主要通过显式创建或定时计划进入"]
workflow_note -.-> workflow_entry
subgraph schedule_group["定时计划"]
direction TB
schedule_db[("定时计划文件")]
schedule_scan["扫描到期计划"]
schedule_trigger{"触发方式<br/>单次、固定间隔或 Cron"}
schedule_executor{"执行方式"}
schedule_command["执行本地命令"]
schedule_workflow["启动任务编排"]
schedule_retry["失败重试和退避"]
schedule_history["保存最近运行历史"]
schedule_delivery["可选:将全文、摘要或结论<br/>投递到指定会话"]
schedule_db --> schedule_scan
schedule_scan --> schedule_trigger
schedule_trigger --> schedule_executor
schedule_executor -->|命令| schedule_command
schedule_executor -->|AI 任务| schedule_workflow
schedule_command --> schedule_retry
schedule_workflow --> workflow_entry
workflow_finish --> schedule_retry
schedule_retry --> schedule_history
schedule_history --> schedule_delivery
schedule_delivery --> msg_append
end
subgraph trace_group["调用链追踪与日志"]
direction TB
trace_root["HTTP 请求入口记录<br/>请求编号和调用链编号"]
trace_feature["功能调用记录"]
trace_module["模块调用记录"]
trace_model["模型或工具调用记录"]
trace_event["事件发布记录"]
trace_async["建立原请求与后台任务的异步关联"]
trace_delivery["事件订阅投递记录"]
trace_reply["回复处理模块选择结果"]
trace_db[("日志数据库<br/>日志、调用节点和异步关联")]
trace_text["核心服务和模块文本日志"]
trace_stream["实时日志通知"]
trace_root --> trace_feature
trace_feature --> trace_module
trace_module --> trace_model
trace_feature --> trace_event
trace_event --> trace_async
trace_async --> trace_delivery
trace_root --> trace_db
trace_feature --> trace_db
trace_module --> trace_db
trace_model --> trace_db
trace_event --> trace_db
trace_async --> trace_db
trace_delivery --> trace_db
trace_reply --> trace_db
trace_module --> trace_text
trace_db --> trace_stream
end
request_ctx -.-> trace_root
call_execute -.-> trace_feature
module_lookup -.-> trace_module
model_call -.-> trace_model
tool_type -.-> trace_model
event_publish -.-> trace_event
event_invoke -.-> trace_delivery
reply_choice -.-> trace_reply
subgraph live_group["前端实时更新"]
direction TB
live_center["实时状态分发中心"]
live_conversation["会话消息变化"]
live_operation["AI 执行和工具状态变化"]
live_permission["权限和审批变化"]
live_module["模块状态变化"]
live_schedule["定时计划变化"]
live_logs["日志变化"]
live_center --> live_conversation
live_center --> live_operation
live_center --> live_permission
live_center --> live_module
live_center --> live_schedule
live_center --> live_logs
end
msg_identify --> live_center
reply_save --> live_center
tool_pending --> live_center
permission_approval --> live_center
module_snapshot --> live_center
schedule_history --> live_center
trace_db --> live_center
live_conversation --> web
live_operation --> web
live_permission --> web
live_module --> web
live_schedule --> web
live_logs --> web