diff --git a/AGENTS.md b/AGENTS.md index 7dbfce2..41ef1e1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -75,120 +75,41 @@ 开始任务前先定义完成标准。交付前依此验证,发现问题就修好再测,不把未完成的工作交回给使用者。只有确认完成,或遇到真正需要使用者介入的障碍时,才回报。 -## 当前范围 +## 当前范围与权威入口 -- 后续Agent必须先读 **`docs/plan-dispatcher-state-v0.1.md`** 和 **`docs/thirds/v0.4.md`**;历史 F 工作包索引见 `docs/plan-config-read-v0.1.md`:按 F 工作包/文档索引读取需求与契约,确认 C/L/M 和 I/M/G 前置、授权及写入边界后实施;并行时按该计划§9认领,子Agent只写独占模块/证据,§8本轮总台账、公共文件及合并状态由集成负责人单写。已恢复原内容的 `docs/archive/plan-0918.md` 仅保留旧 W00–W16 历史,不是现行计划入口。本地 Dispatcher v0.4 任务发现、MQ 控制及外呼入站规则另读 `docs/plan-dispatcher-state-v0.1.md`、`docs/thirds/v0.4.md`;`docs/plan-0926.md`、`docs/thirds/v0.3.md` 及 v0.2 状态仅作历史。**v0.4 的 Schema、隔离 Mock 与代码已在项目内实施,真实 SaaS/management 尚未签收或联调;仅显式条件清理已终止任务的任务配置副本,不自动清除现有 SQLite 数据。没有 SaaS 应用收讫的执行、幂等及 outbox 记录继续保留。日志不得记录密钥、完整音频或对话。** 计划不替代权威Schema或验收,不重复审批已确认方向,不自动授权真实云/付费/拨号。 -- 当前MQ-only/OSS调整目标已获用户明确批准由当前Agent独立执行,**不启动子Agent**,不得再以子Agent模型/fast环境阻塞本目标;此前按任务类型自动分工规则在本目标不适用。若未来另获启动授权,仍遵守下条模型要求。 -- 用户指定:若启动开发及配套审查子Agent,固定 **gpt-5.6-luna、max思考、fast模式**。启动前查询精确provider/model和runner支持并显式配置(当前工具用模型`:max`后缀及`fast: true`,不继承默认);不可用/不支持/无法核验则报告阻塞,不静默换模型、降思考档、关fast或换CLI。此为后续执行约束,本轮仅修复计划,未启动开发子Agent。 -- 并行开发须先有获授权的可追溯Git/契约基线、一lane一工作区/测试资源、无交叠写集合及每批合并后回归;当前子项目尚未跟踪的文件不能假定存在于HEAD/worktree。不得自动提交、暂存或清理父项目无关改动;详情见 `docs/plan-config-read-v0.1.md` §9。 -- 当前已获授权进行本项目开发:W01 项目内契约基线和 W02 Proto/stubs 已建立;仍不能把设计、Mock、Proto或88项测试清单写成真实供应商/生产验收已通过。入口和权威依据仍为 `docs/architecture/Go重写方案_v0.3.md`、`docs/acceptance/验证与切换验收_v0.3.md`、`docs/contracts/通信与事件数据交互_v0.1.md`、`docs/references/OpenAPI与MQ字段索引_v0.1.md`、`docs/dependencies/开源组件选型与复用清单_v0.2.md`。 -- 开发准备见 `docs/architecture/G0开发准备与契约冻结提案_v0.1.md`:D01–D10的方案方向、双模式/许可/恢复机制及内部PoC初始profile已获用户确认;本项目已自行交付 W01/W02 开发基线,但外部权威发布、真实预算、供应商签收和 G0/PoC 仍需分别验证,不能混写为生产合同。缺失字段细节、实际预算及方案变更另行确认。本轮验收基线收敛为单节点/单 Agent/单 Cell/单租户;双节点、第二 Cell、第二租户及其公平/故障矩阵不在本轮开发或验收范围,跨 Cell/多租户能力保留为后续阶段。 -- 用户已确认完整 Go Agent、分阶段替换:调度、Cell 执行、ARI/RTP/录音、AI 流及 Cell 配置接收。最终没有 Python 运行依赖;不重写 Asterisk、不实现第二套管理后台。 -- 本项目已独立拆仓运营,源码、文档、依赖、配置、迁移、测试、部署、发布入口全部留在本目录。普通构建/测试/运行不读取父目录,不使用其它项目内部模块、环境文件或夹具,不共享其业务数据库。 -- 当前已初始化独立 Git 仓库并绑定公开远程 `git.ipao.vip/rogee/go-sip`;未经授权不要重新 `git init`、改为 submodule、移动历史或更改父仓库跟踪关系。 +- 当前工作由使用者批准按 P01–P08 实施,K01–K16 已确认,不重复审批;本目标由当前 Agent 独立执行,**不启动子 Agent**。只有未来重新获得授权,才按使用者指定的 `gpt-5.6-luna`、`max` 思考和 `fast: true` 逐项核验并显式配置,不静默换模型、降档或关闭 fast。 +- 唯一现行 SaaS↔Dispatcher 业务说明是 [`docs/thirds/saas-dispatcher.md`](docs/thirds/saas-dispatcher.md);当前项目内 Schema、拓扑、正反例及来源/hash 在 [`contracts/local/`](contracts/local/);内部 Agent RPC 在 [`proto/agent/agent.proto`](proto/agent/agent.proto)。进度与缺口见 [`docs/plan-saas-dispatcher.md`](docs/plan-saas-dispatcher.md) 和 [`docs/evidence/saas-dispatcher-implementation.md`](docs/evidence/saas-dispatcher-implementation.md)。文档不能代替机器合同或外部签收。 +- [`docs/plan-saas-dispatcher-v05-v0.1.md`](docs/plan-saas-dispatcher-v05-v0.1.md) 的原路径和原 SHA-256 已由 `contracts/local/manifest.json` 固定,只作**不可变历史来源**;旧 F/W 工作包、v0.1–v0.4 本地提案、旧 MQ-only 合同及 `docs/archive/` 不作为当前运行入口。固定 MQ `v1` 名称、HTTP `/internal/v1/dispatcher/...` 路径和有业务意义的 revision 是通信规则,不是应删除的自有实现代次。不得为历史路径新建兼容层或配置回退。 +- 当前本地范围仅**单节点、单 Dispatcher、单 Agent、单 Cell、单租户和隔离 Mock**。根命令只接受显式 `agent`/`dispatcher` 业务子命令,mixed/real 启动即拒绝;没有真实 SaaS、management、RabbitMQ、OSS、AI 供应商、Asterisk、SIP 线路、ECS 或生产签收。生产发布包仍为 `production_approval=false`。本机 Mock HTTPS、双向 TLS、RabbitMQ 和哈希通过均不授权真实呼叫。 +- 开发按 TDD 分批,小步提交;不得覆盖使用者现存修改/未跟踪文件,不自动清理、迁移或覆盖任何现存 SQLite、spool、outbox 和 Agent 恢复文件。旧 `.executions` 及恢复根目录中旧 `.uploads`、`.upload-locks`、逐执行 `state.json` 的发现须只读失败关闭,现存未交付事实由使用者确认处置。真实云账号、EIP、线路、拨号、生产部署和共享数据操作分别需要明确授权。 -## SIP 接入信息 +## SaaS、Dispatcher 与 Agent 的现行边界 -以下为用户提供的 SIP 参数,服务商名称待补充;已记录不代表已完成真实线路验证。 +- SaaS→Dispatcher 的**五类只读配置**为 `GET /internal/v1/dispatcher/sip`、`/task/:task_id`、`/tasks`、`/tenant/:tenant_id/quota`、`/ai-providers`;路径前缀固定,均须校验 Dispatcher UUID/资源归属、数字 `tenant_id`、完整快照、来源、有效授权和版本。配置读失败、过期、矛盾或不确定时关新准入;没有旧 MQ 配置回退、通用业务 HTTP、ETag 兜底或偷偷启用旧执行字段。已接纳任务持久绑定原快照。 +- 呼叫、控制、必要回执和**每通话唯一最终结果**经固定 `v1` RabbitMQ Topic/队列,任务与控制队列由 SaaS 预建,Dispatcher 仅消费、不可自行建/删/绑定;结果进入指定共享 durable 队列。独立 D UUID 和接收队列不能广播后正文过滤;`tenant_key` 原值保留,任务只能由归属 D 执行。入站先校验和持久 inbox 后 ACK,状态/outbox 同事务;persistent、mandatory、无 return、publisher confirm 成功才记交付,confirm **不是** SaaS 应用收讫。失败/确认丢失与重启只重发同身份消息,不重复拨号或捏造结果。 +- `call.execute` 只带获批 `task_id/callee`,调用线路、主叫、AI 和时限由该任务快照固定;`task.control` 的 pause/resume/stop 无旧 CAS/版本字段,控制回 task/action/status 处理结果,stop 不可恢复。发现分页同快照先全部校验后提交,MQ 控制持久状态高于偶发 HTTP 状态;冲突关准入、resume 须重新核验。历史命令不因消息年龄过期,但实际呼出前仍检查任务/白名单/时段/授权/额度和有效通话上限;任务结束不删未确认结果、恢复、幂等或未知占用。 +- 独立 Dispatcher 的 SQLite 是任务、额度、inbox/outbox 的权威数据;Agent 无业务数据库,录音、执行与上传恢复只写受控私有文件。额度包含未知占用,新 boot/租约到期不得自动清除未知执行;不实现双活数据库、自动跨机热备、多 D 共享额度或第二租户公平。本轮不借本机 D1/D2 隔离夹具宣称多 D 运行。不得建立旧表/旧消息/旧 HTTP 执行兼容通道。 +- Dispatcher↔Agent 复用 Unary gRPC 和受控 Endpoint;Agent 预绑定 D UUID 与服务端证书指纹,激活/会话代际、peer mTLS/SAN/SNI 和已签发期限须核对,新 boot 不清未知占用。Agent 不自行向 SaaS 取任务/AI/OSS 授权;Dispatcher 只用已经核验的 Agent `GetLoadedSIP` revision 开执行准入。本机 Mock 的加载回报不证明 Asterisk 已实际加载,SIP 配置的唯一编辑/审批面仍是 management。 +- AI 使用任务内不可变授权快照:仅经获批准百炼/火山 ASR、OpenAI 兼容 LLM、火山 TTS 能表达的参数进入每通话实例;ASR-only 不启动 LLM/TTS,完整 AI 不借旧语音测试的 LLM/TTS。只有最终用户 ASR 文本的明确字面关键词可触发拒联/挂断;不由 SDK 默认值、环境、CLI、metadata 或宽松 Schema 改写业务参数,不因 SDK 重试产生第二次发起/收费或重播。日志只存脱敏版本/摘要/计数,不存密钥、prompt、完整对话或音频。 +- Agent 录音经受控双向 TLS 向 D 领取短期 OSS 上传授权,每次尝试只作**一次 HTTPS PUT**;正常上传成功不写录音/结果业务文件。首次明确失败须先完整保存录音与结果两份恢复文件,才从该时刻启动 48 小时重试;按 1、2、4、8、16、32、60 分钟及其后每 60 分钟的固定节奏显式重新申请授权,同一 OSS 目标、同一消息身份。PUT 结果未知不得盲目重传;48 小时届满仍失败时保留文件待人工,**不伪造最终结果或自动清理**。D 不转发文件,已确认结束的通话及时释放执行占用;未知执行仍占用。只有真实终结后才通过唯一 `call.execute.result` 回报录音路径、最终转写和拒联事实;无录音或录音生成失败以空 `recording={}` 和真实结果收口,生成失败须说明原因。不能恢复的录音不声称零丢失,也不伪造 OSS/SaaS 应用回执。凭据/TOKEN/签名 URL 不写入样例、日志、源码或证据。 -| 服务商 | SIP 服务端 | 主叫号码/标识 | 被叫前缀 | +## SIP 与真实呼叫限制 + +| 线路 | SIP 服务端(历史登记,非已验证线路) | 主叫标识 | 被叫前缀 | | --- | --- | --- | --- | | 数企 | `61.132.228.221:5060` | `BD93205882` | `7089` | | 中鼎 | `60.171.24.90:5060` | `mbkq` | 无 | | 百应 | `160.202.254.79:5060` | `KQ91526` | `mka755` | -## SIP 全局共用定义:外呼号码白名单 +- 全线路**只允许原始号码** `15003164745`、`15830461047`,但白名单和上述登记绝非真实拨号授权。已授权的真实/旧路径仅在 Asia/Shanghai 每日 `09:00`(含)至 `20:00`(不含)放行,每条 trunk 对每个原始号码每天最多 3 次,窗口外直接拒绝,不等候/自动延迟/自动重试/静默换线。每次真实试拨仍须使用者明确安排并受现行程序 Mock-only 启动屏障约束;不能拿 Mock 时段测试宣称 real 放行。 +- 任务按周一至周日多个时段与指定排除日期配置,线路只有每周允许时段(**没有线路排除日期**),Asia/Shanghai 左闭右开、跨日拆分;缺失或不确定 fail-closed,不自动重拨。由 Dispatcher 在持久接纳与实际发出指令前判定,并取任务/获批 AI 较小通话时限;Agent 仅校验会话和签发期限,不重算外呼策略。本地策略 Mock 与固定真实门禁必须分别报告。 +- `BD` 等主叫原值不得清洗或当作 Digest 用户名;业务原始被叫号码不变,仅被选定数企 trunk 按规则构造 `7089<原号>`(其它线路使用自己的前缀),不重复加前缀。三条 trunk 独立,不能把同地址伪造为备用线路或换线重拨;服务商反馈 PCMA,对应 Asterisk `allow=alaw`,传输/注册/鉴权/并发仍待真实签收。不以 sipgo/diago 另造 Asterisk 替代架构。 -- 外呼号码白名单:`15003164745`、`15830461047`。 -- 所有 SIP 线路仅允许在此列表范围内发起外呼;不在列表内的号码必须拒绝。该列表仅用于已授权的 Mock/明确安排的测试;不得因写入此处而自动发起真实呼叫,原始号码保持不变。 -- **现行真实/旧路径运行限制**仍为 Asia/Shanghai 每日 `09:00`(含)至 `20:00`(不含);窗口外 Dispatcher/Agent 必须 fail-closed,禁止等待、自动延迟、重试或换线。下述非生产和真实验证条款中的固定时间仍是**当前真实运行限制**。新增的 V3 执行入口只启用隔离 Mock,mixed/real 启动即拒绝;Mock 测试不能宣称 real 放行。 -- **F04 项目内 Mock 已实现、外部未签收的后续目标**:仅 Dispatcher 在持久接纳与实际发出呼出指令前判断任务×已选 SIP 线路的允许时段、白名单、命令/授权/租户额度期限及任务/AI 较小通话时限;Agent 只校验会话及 Dispatcher 签发期限,不重算外呼业务策略。任务按周一至周日分别配置多个允许时段,可排除多个指定日期;线路只配置每周允许时段,**没有线路排除日期**。均按 Asia/Shanghai 判断,跨日拆分、左闭右开,任务排除日期全天拒绝;缺失/不确定时段拒绝并持久记录,不等下一窗口、不自动重试或换线。任务和线路时段**尚不能授权真实试拨**;F08 最终结果/额度收口、F09 完整验收及真实合同/部署授权分别未完成,不能据此放开固定真实门禁。 -- 主叫标识保留原值(包括 `BD`),不能按纯数字手机号清洗,也不能直接当成 Digest 认证用户名;具体 From/PAI 等字段映射仍需确认。 -- 业务原始被叫号码保持不变;使用该线路时按其规则构造 `7089<被叫号码>`,避免重复添加或把该前缀带到其他供应商线路。 -- 传输协议、IP/Digest 鉴权、是否注册及并发限制仍需供应商确认;当前供应商已反馈需使用 PCMA,Asterisk 配置以 `allow=alaw` 表示,仍需真实线路验证。 -- 每条线路目前只提供一个服务端地址,未提供独立备用地址。不能把同一地址重复填写成主备并宣称具备容灾;三条已登记线路应作为独立 trunk 配置,不为凑主备虚构供应商。 +## 运行环境、诊断与开发门禁 -## ECS 部署环境与 Asterisk 运行约束 - -- ECS 部署环境固定优先使用 Debian 13(Trixie)minimal;仅当阿里云北京区域没有可用的 Debian 13 镜像时,才允许使用 Ubuntu 24.04 LTS。不得擅自切换到其他操作系统。 -- Asterisk 必须直接部署在实际承载它的 ECS 主机上,由 systemd service 统一管理并启用开机自启动;部署验收必须确认 service 已 enabled 且 active,不得以手工前台进程或容器入口替代生产启动方式。 - -## 开发与非生产环境强制部署/诊断步骤 - -- 开发、Mock、mixed、real 的**非生产环境**必须默认开启环境部署与诊断步骤;这些步骤是必需的,不得因“只是开发”“环境已存在”“时间紧”或调用方参数而跳过、关闭、静默降级或默认禁用。任何显式关闭均视为配置错误,应失败并阻止验收。 -- 每次新主机、新版本或新 Cell 验证至少执行并留存脱敏事实:ECS/EIP/网络资源只读核验、Debian/架构/磁盘/权限核验、`rogee` SSH 与 SSH 加固核验、发布包及依赖 SHA-256、Asterisk/systemd `enabled+active`、ARI/PJSIP endpoint/contact 状态、媒体 profile/监听端口和运行版本。 -- 每次非生产 `mixed`/`real` 外呼验证,必须先通过 Asia/Shanghai `09:00`–`20:00`(左闭右开)时间门禁,再在拨号前启动受限 SIP/RTP 抓包和 Asterisk PJSIP logger,并在结束后采集 SIP 响应码、INVITE/180/183/200/4xx/5xx/BYE 或 CANCEL 时间线、SDP codec/媒体地址端口、RTP 包/字节计数、录音与 ASR/LLM/TTS 事实及 SHA-256;无拨号前抓包、状态快照或时间门禁不得宣称验证通过。失败呼叫同样必须保留状态和抓包证据,不能只报告一个 hangup cause。 -- 抓包、日志、录音和识别文本只写入受限的非生产证据目录,聊天、源码、配置样例、提交和长期证据不得保存密钥、完整用户音频或完整用户对话;交付证据默认保存脱敏摘要、计数、状态码和哈希。若 tcpdump/CAP_NET_RAW、PJSIP logger、ARI/PJSIP 状态采集任一不可用,必须 fail-closed 报告阻塞,不得静默改成无抓包流程。 -- 上述步骤由统一部署/验收入口自动执行;`mock`、`mixed`、`real` 只替换适配器,不能绕开同一套部署、诊断、状态和证据门禁。生产环境仍须另行授权和通过生产安全屏障,非生产默认强制开启不等于生产放行。 - -## 本次上线目标与分期(用户已确认) - -- P1以稳定快速内测上线为目标:1个节点、1个Agent、1套Asterisk、1个单活Dispatcher/SQLite、1个启用租户;本轮不开发、不验收双节点、第二 Cell/第二 Asterisk或第二租户。用户已确认后续多 Dispatcher 目标为每 D 独立 ID/接收队列、独占 Agent/Asterisk 执行资源;同一租户可有多任务,但每个任务只能有一个 D 归属,SaaS 持久绑定租户+任务→D,呼叫/控制不得分发到其它 D,不能暗中迁移。单任务并发可由归属 D 本地判断;跨 D 租户/供应商总上限仍须先冻结有界额度份额,不能各 D 各按全局上限放行。本轮不把 D1/D2 隔离 fixture 冒充多 D 业务运行。 -- 至少3家独立SIP trunk 的静态配置、路由/主叫/前缀/codec/额度约束和协议 Mock/mixed 覆盖仍需保持;真实供应商外呼和 ECS 仅作为第二阶段联调,不是本轮前置。 -- ASR-only和ASR+LLM+TTS均按批准的不可变AI配置在本地/隔离链路验收;不擅自加MQ模式字段,不复用旧LLM/TTS。真实供应商未联调时必须明确标记为第二阶段,不能把 Mock 写成真实供应商通过。 -- P1使用管理平台批准的静态单 Cell 快照和受控维护窗口,不做在线发布/回滚编排;静态配置必须关准入、排空、核验实际加载,旧直写通道不得并行。 -- P1保留 tenant_key 原值、租户独立队列、复合幂等键、有界窗口及单租户配额/控制边界;不开发或验收双租户公平、第二 Cell 汇总配额和多实例协调。 -- 本阶段上传按固定15分钟授权、单次PUT、原事实持久化及recording.uploaded可靠入队进行本地隔离验收;不申请SaaS上传会话、不等待complete/verified或OSS ID。RabbitMQ ACL/TLS和application receipt仅按适用版本化契约/隔离测试验证,不将通知入队称为SaaS已处理;真实SaaS/MQ联调延期第二阶段。 -- 88项验收为跨阶段基线,当前只签收单节点/单 Cell/单租户适用子场景;双节点、第二 Cell、第二租户、真实 ECS/生产联调、容量/N+1及切换均不作为本轮门禁。 - -## 语言与工程 - -- 工具链基线为 Go 1.27.1。实施时锁定 CI/构建镜像和依赖,并校验实际工具链;不得自动修改其它项目的 Go 基线。 -- 标准库优先、单Go module/二进制,用Cobra显式提供agent、dispatcher两个业务子命令,无默认双角色启动;同一制品分进程/权限/目录,升级受版本兼容和排空约束,不另造CLI框架。 -- JSON v2、UUID 和新测试 API 的采用以契约兼容和实测为前提;既有标识、哈希规范不得随 API 更换。实验性 SIMD 不在当前范围。 -- 用户已确认不接PG:独立Dispatcher持有SQLite权威任务/配额/outbox,Agent无业务DB,文本/录音及执行/上传恢复信息落文件。禁止NFS共享SQLite/两份DB双活发额度,自动跨机热备不在已实现承诺内。 -- 已确认Unary gRPC,Dispatcher预配置Agent Endpoint;Agent业务只需D Endpoint,证书/监听/ARI/持久目录由部署提供。SDK复用连接,不增内部MQ或双向流,不因RPC超时重拨。 -- 所有Agent共用mTLS证书,D身份独立;必须通过受控Endpoint主动激活/节点会话授权,不信自报身份/地址。共享私钥泄露影响整组,轮换/撤销和风险要签收,不关闭SAN/SNI校验。 -- D感知健康/负载/软件协议/能力及供应商applied配置版本;样本过期/缺失为unknown,低CPU不突破租户/供应商/Cell/AI配额,新boot不清旧未知占用。 -- 实施后至少执行本模块的格式化检查、`go vet ./...`、`go test -race ./...` 和构建;当前代码已有 W01/W02/本地 RPC/AI Mock 入口,真实 DB/MQ、媒体、供应商、容量及切换验收仍分别报告,不能用本地通过记录代签。 - -## 开源复用硬约束(用户已确认) - -- SIP 及其它组件有适用开源库/官方 SDK 必须复用,禁止从零手写替代协议栈或客户端。优先标准库、Asterisk 原生能力、现成 SDK;自有代码限业务状态机、事务、权限、配额和薄适配。 -- 不自写 SIP/ARI、RTP/RTCP 编解包、G.711、WS/AMQP/数据库驱动、OSS 签名、已有 SDK 覆盖的 AI 协议或 Schema 解析。库不满足先选替代、修上游或报告阻塞;例外须用户另行批准。 -- 采用前核验 module/tag/commit、Go1.27.1、许可证/NOTICE、传递依赖/漏洞及真实协议兼容,留存 PoC;不得将 main README、未归档或可下载等同于生产通过。 -- SDK 自动重试不得造成二次 originate、旧音频重播或重复收费;不因 SIP 库存在而用 sipgo/diago 替换已选定 Asterisk 架构。 - -## AI配置与参数(用户已确认) - -- P1采用百炼/火山ASR、OpenAI兼容LLM、火山TTS;SDK首选及未通过门禁见组件清单§1.3/§4.3。基础栈方向确定不等于精确版本、许可证或参数能力已验收;不为补字段改为自写协议。 -- **现行合同(尚未切换)**:Dispatcher按MQ任务agent_version_id,经RabbitMQ专用Topic向SaaS取得不可变AI配置/授权,响应回原Dispatcher;校验租户/源Schema/不可变摘要/能力并持久绑定后向Agent交付执行快照。过去已废弃的独立AI版本GET不复活;新获批准的方向是**任务配置只读接口内含智能体**,须另冻合同而非旧GET兼容层。Agent不直连SaaS,不从CLI/env/源码常量或SDK默认覆盖AI业务值,不新增任务MQ模式字段或调参后台。 -- 已有model/prompt/voice/speed/ASR输入与识别/temperature/max_tokens/timeout及对话控制必须实际传入SDK或控制器;热词/VAD/top_p/音量/阶段时限等所需扩展先在上游补GAP-09,再生成校验。严格additionalProperties不放宽,不借metadata/raw_request透传。 -- **现行 AI 合同**:SaaS新版本供新任务引用,无需改代码/重启D/A;在途/原排队任务固定快照,同版本异内容拒绝。缓存按租户+版本隔离,断SaaS无有效授权缓存拒新准入;显式0/false与未提供保真,并发通话不得共享可变SDK参数。 -- **项目内配置与执行(已发布外部 v1 不变)**:项目内 v0.4 任务发现、控制及外呼入站依据 `docs/thirds/v0.4.md`;其它必要边界见 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md`。拟定 SaaS 只读路径为 `/internal/v1/dispatcher/sip`、`/internal/v1/dispatcher/task/:task_id`、`/internal/v1/dispatcher/tasks` 和 `/internal/v1/dispatcher/tenant/:tenant_id/quota`;均为项目内字段/路径,实际 SaaS 兼容性未验证。D 用 `X-DISPATCHER-id`/`X-DISPATCHER-SECRET-KEY` 按需读取,不在源码、配置样例或日志暴露凭据。SIP 启动先取获批全量并核验 Agent/Asterisk 加载后才准入;任务按租户+任务缓存约 60 秒,到期重新读取完整 200,无 ETag、过期缓存或 MQ 配置回退;已接纳执行固定原快照,暂停/停止控制不等待缓存。项目内 v0.4 `call.execute` 入站只有 `task_id/callee`,调用路由、主叫、AI 和时限绑定已授权任务快照;历史 MQ 命令不因年龄过期,但拨号前门禁不放宽。任务终止仅允许**显式、条件式**清理任务配置副本,不删执行恢复、幂等或 outbox。SIP 改动须关准入、排空及核验实际加载;外部 opt-out 现行 MQ 即时语义不可擅自改称已切换为最终 `call.result`。F01/F07 本地 Schema、正反例、来源/hash 与 Mock C 已通过,外部签收/连通仍未验证。 -- 凭据/供应商端点来自受控引用且有授权校验;不再定义独立出口池标识或出口池授权名单,Dispatcher 仍校验任务允许线路、全局号码白名单、时段和额度,不能因可调参数绕过安全硬限额或启用不安全重试。OpenAI默认自动重试显式关闭;日志只留脱敏版本/摘要/有效参数,不打印prompt/变量/密钥。 -- 静态发布只约束SIP/节点制品,不将AI配置硬编码;GAP-08/09及SDK参数PoC为P1门禁,验证入口见验收§5.1(现有E/L项子场景,不新增虚假通过数)。 - -## 契约与可靠性 - -- 本项目设计/运行/验收文档只在自身 `docs/` 维护。上游共享接口有唯一权威来源;导入带版本、来源和哈希的不可变契约包,再生成类型/校验,不维护重复手写 Schema。 -- 新内部消息/许可/fencing 协议需先获批;不擅自改变 SaaS 路径、字段、状态、路由或控制语义。 -- **现行已发布合同(新版本生效前必须遵守)**:SaaS↔Dispatcher的全部交互唯一经RabbitMQ专用Topic订阅,双方无HTTP请求/回调/兼容通道或故障回退,包括执行、控制、查询、整体补传、AI配置/授权及recording.uploaded上传事实通知;上传不申请SaaS会话或等待verified/OSS ID回复。OSS配置/TOKEN不来自SaaS:Agent领取及显式重申请TOKEN只经D↔A Unary;本规则不禁止Agent→OSS、ARI、AI供应商HTTP(S)或gRPC的HTTP/2。 -- **本轮项目内目标(尚未替换真实外部运行)**:任务(含智能体)、SIP、任务发现和按 tenant_id 的租户额度走四条只读 HTTP;呼叫、控制及其必要回执/单份最终结果走 MQ,取消对外业务查询/补传和分散通话事件。不提供配置HTTP→MQ回退,也不恢复其它业务HTTP通道。SaaS须分发 management 已批准的唯一 SIP 版本,management 仍为唯一编辑/审批面。该本地语义按第三方契约及版本化 Schema/示例/hash 冻结,Mock C 是本地门禁,不等待外部签收;真实切换仍需另行授权。 -- 新HTTP配置字段项目内 SIP 新版见 `docs/contracts/config-read-fields-v0.3-proposal.md`、`config-read-v0.3.schema.json`/mock示例;AI 授权和静态 Cell 制品的项目内新版见同一提案;旧 v1/v0.2 保留历史,不作为当前运行契约;任务/额度仍参照 v0.1;截图只证实UI含义,英文响应键为项目自定义,绝非SaaS现网接口已确认字段。用户新增任务排除日期、线路时段等未见截图项按项目需求设计;SIP传输/鉴权/注册及额度未知不能猜默认值。Schema/Mock 校验可满足项目内 C,但不代表 SaaS/management 已发布或真实兼容;真实响应、审批来源和 Agent/Asterisk 实际加载仍须单独验证。当前唯一权威运行契约不因草案变化。 -- **每个Dispatcher必须有独立、全局唯一且不重复的ID及独立接收Topic/队列**;指定D的任务/现行MQ配置结果/上传结果不能由其它D抢收,也不能广播后仅靠正文过滤;新目标只读HTTP配置由该D UUID+SECRETKEY获取且须核对任务归属。身份与tenant/Agent/Cell ID、dispatcher_epoch分开;现行合同保留租户独立队列及原值tenant_key,完整新路由长度预算须重验。具体ID生成/持久化、Topic/绑定、消息字段/关联/错误/期限须随W01新版本冻结,不凭本文给旧严格Schema添加字段。 -- **v0.4 任务发现与队列所有权硬边界**:SaaS 独占创建、维护、退役每 D 的独立控制队列与每任务任务队列及绑定;Dispatcher 仅消费,不能自行建队、绑定或删除。本地 RabbitMQ 无 `configure` 权限 Mock 已通过,真实兼容性未验证。新加入或重启 D 先 `GET /internal/v1/dispatcher/tasks?mode=snapshot` 逐页取得同一 `snapshot_id`/`watermark` 的完整清单,全部校验后一次 SQLite 事务提交;独立控制队列积压处理完成前不开任务准入。运行期 `GET .../tasks?after=<内存游标>` 仅发现归属变更,每页持久提交后推进内存游标;重启不恢复旧 v0.3 持久事件游标,分页或持久化失败关新准入、停任务消费,MQ 控制及结果恢复照常处理。HTTP 的偶发状态与 MQ 已应用状态冲突则关准入,不让发现页覆盖已持久的 pause/stop;只有 MQ resume 经单任务新鲜状态确认才恢复原积压。stop 挂断、排空后回自身控制回执,未接纳旧外呼静默 ACK、不拨号、不回逐条结果,也不删 SaaS 任务队列。历史 `task.control` 与 `call.execute` 不因消息年龄过期,外呼仍在接纳和拨号前独立检查任务/白名单/时段/授权/额度及通话时限,未来 `issued_at` 不提前接纳。保留原值 tenant_key、租户额度和未知占用;跨 D 份额仍待外部冻结。v0.1–v0.3 发现证据仅作历史,v0.4 本地证据见 `docs/evidence/dispatcher-v04-local-acceptance.md`;不能证明真实 SaaS/management、多 D 或生产可用。 -- **OSS相关配置存于Dispatcher配置文件,Agent向Dispatcher领取临时上传TOKEN后直传OSS,不保存长期凭据;SaaS不再下发OSS配置/TOKEN。** D复用官方SDK提供受限TOKEN/目标信息,配置缺失/无效明确失败;不在样例、源码、日志或证据中保存实际密钥/完整TOKEN。过期只允许A显式向D重新申请,不自动续期或向SaaS申请TOKEN;精确配置格式/TOKEN形态/UploadGrant映射另行核验,不猜字段。 -- **本轮项目内目标:上传仅负责 Agent 直传及 D 可靠通知 MQ,SaaS 后续处理不属本项目职责;外部 v2 的现行上传事实不因本地目标自动改变。** D保留签发能力、不转发文件;R13持久保存事实与recording.uploaded outbox,消息为persistent、进入指定durable队列/绑定、mandatory无return且publisher confirm成功后才记交付完成。只写本地outbox不算入队;不申请SaaS会话、不等待verified/OSS ID、不新增VERIFYING、不伪造SaaS结果。 -- 新版recording.uploaded取代本项目recording.ready,字段为call_id/recording_id/upload_id/bucket/object_key/format/channels/sample_rate_hz/duration_ms/size_bytes/checksum_sha256;不含TOKEN/密钥/签名URL。MQ失败/确认丢失/重启只恢复原消息身份的交付,不重新PUT或新建资产。现行AI/控制等必要请求响应不受此收缩影响;新目标AI改用任务只读HTTP获取,控制仍走MQ。 -- 前一轮MQ-only文档纠正已结束;以下是**现行 MQ-only v1 合同的历史基线**,不得误当作新只读配置HTTP方案的限制或验收通过:现行机器依据见 `contracts/upstream/v1/mq-topology.json`、`mq.schema.json` 和 `event-payloads.schema.json`,第三方顺序说明见 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md`:纯Topic精确绑定、拒绝独立通配词段、tenant_key预算196 UTF-8字节、稳定UUID v4、旧版严格JSON配置及15分钟SDK预签名PUT;新目标保留业务MQ及OSS规则,仅替换配置获取,须另发版本化合同,不原地复用旧Schema。当前仅保留现有代码需要的契约包;未发生契约迁移前不新增契约,旧快照不作为当前工作树输入。新版须完成Schema/正反例/哈希验证后发布,不把方案确认当实现完成。历史 W01/W02/W04/W05/W07/W08/W11/W12/W13/W14 范围见归档 `docs/archive/plan-0918.md` §8.2;项目内 v0.4 配置/控制路径以 `docs/plan-dispatcher-state-v0.1.md` 和本地验收证据为当前入口;外部合同另行签收。全局唯一D身份/专用Topic及本地D1/D2隔离fixture为当前合同要求,不授权双D业务运行、HA或共享额度。 -- **现行已发布合同**中的实时文字事件名为transcript.updated,不新增call.transcript别名;OSS文本归档不能冒充当前实时文字/opt-out,缺少专用资产授权接口时明确未启用,不能伪装recording.ready。 -- **用户已批准的项目内精简契约目标**见 `docs/plan-config-read-v0.1.md` §3.5/§4 和第三方对接契约:SaaS↔D 移除对外查询/补传命令及分散通话/转写/拒联/录音事件,保留呼叫/控制命令和必要回执;录音仍上传 OSS,D 每通话只回传一份含最终转写、拒联事实与 OSS 路径的 `call.result`。本地实现以 F01/F07 Schema、正反例、来源/hash 和 Mock C 为门禁,无需额外外部/业务签收;本地验收更新后不要求通话前实时文字或拒联。无录音按 `not_created` 收口;预期录音明确失败立即 `unavailable`,未知最迟于 `call.ended_at + 15m` 收口。通话确认终结释放执行资源后释放占用,不等待 OSS 或 MQ confirm;未知仍占用。现行外部 Schema/代码保持原样,真实兼容性未验证。 -- 现有MQ信封command_type/command_id、event_type/aggregate_*与正文已对齐;事件payload专属约束尚需补齐,不把通用object校验当完整验收。字段索引只读生成,不手改成第二套Schema。 -- `tenant_key` 原值一对一绑定,不清洗、编码或截断;旧布局224个UTF-8字节预算不能在加入D身份后直接照搬,W01须冻结并验证完整routing key/queue预算及分隔符/通配符边界;超限停止发布并保留源任务。 -- 持久 inbox 后 ACK;状态与 outbox 同事务;confirm 不等于 SaaS 应用收讫。重复投递、未知执行和恢复不能触发重复拨号。 -- 配额覆盖所有 Cell/实例及未知占用;租约过期不自动释放不明通话。**现行 v2** 控制 CAS 为 expected_task_revision,pause 与 stop 的 drain/hangup 区分;paused 可按新授权恢复,stopped 不可恢复。现行整体补传仅 call_id/source_command_id;下一版对外查询/补传目标取消但严格合同/代码未改。最后发起许可、权限和屏障须故障注入。 -- **用户已批准的项目内精简契约目标(不能混写现行 v2;外部签收不阻塞本地 C)**:`call.execute.payload` 仅 `task_id/callee`,外呼信封身份仍用于防止重拨;路由/主叫/智能体版本与 `ring_timeout_ms/max_call_duration_ms` 均从已批准的任务配置取得并持久绑定。`task.control` 的 pause/resume/stop 均不带 `command_id` 或 `expected_task_revision`,不设计控制去重,但 D 必须回 task/action/status 处理结果;乱序、控制重投/回执丢失与 stop 后 resume 的本地判定写入第三方契约并由 Mock 测试;外部签收不是本地阻塞项,不偷偷重引入 CAS/去重掩盖。任务配置新增明确 `caller_profile_id`,按allowed_trunk_ids顺序选首个时段/额度/加载/主叫均匹配的线路,选后固定、不自动换线重拨;有效通话上限取任务与已授权AI两者较小值。旧running配置/发现不得解除已持久的paused/stopped,resume须强制最新任务核验,stopped同ID不可逆。拟定配置响应不再包含 `agent.content_sha256`;录音 checksum 另有用途,不误删;SIP SaaS 响应不再提供 snapshot_sha256 或静态制品。参见 `docs/plan-config-read-v0.1.md` 和 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md`。 -- management是SIP配置唯一编辑/审批面。**现行 P1**通过批准的版本化静态制品和受控部署入口交付,D核验目标/准入屏障,Agent加载并报告;不要求在线发布控制面。静态交接合同须批准,旧直接写Agent面不能同时启用;成功必须证明精确快照已被Asterisk加载。 -- **本轮项目内 SIP 配置读取目标(现行外部运行未切换)**:management 仍是 SIP 唯一编辑/审批方,SaaS 仅经只读 HTTP 提供该 D 资源分区的获批全量快照;新加入/重启 D 先查询、核验所属 Agent/Asterisk 实际加载的 SIP 线路 revision 后才消费执行队列;Agent/Asterisk 的部署配置由受控部署确定,不来自 SaaS。后续约每60秒核对版本,有变化就关执行准入、排空旧活动通话并确认新版已加载;控制MQ照常处理。SaaS/management 分属不同配置系统时须证明SaaS分发的是同一份获批线路配置版本;不引入共享业务DB或MQ配置回退。项目内 SIP 读取及准入链路已用隔离 Mock 验证;真实 Agent/Asterisk 加载与 SaaS/management 切换尚未验证。真实 SaaS/management 兼容性及多 D 资源配额份额仍未验证、不属本轮验收,详见 `docs/architecture/Dispatcher有界接纳与控制通道改造计划_v0.1.md` §3.2。 - -## 安全和真实验证 - -- mock/mixed/real 明确隔离;Mock 默认隔离真实外网,正式模式拒绝 Mock/测试凭据,不静默回退。 -- 只允许复用既有 ASR 协议;禁止复用 `voice_test` 的 LLM/TTS。P1必须完成新LLM/TTS规范、SDK适配和本地/协议隔离验收;真实供应商联调延期第二阶段,不能以Mock冒充真实供应商通过,也不能把真实联调延期误写成当前 P1 已实测。 -- 真实外呼只允许原始号码 `15003164745`、`15830461047`,但白名单和文档不是拨号授权;每次真实验证仍需明确安排,且仅可在 Asia/Shanghai `09:00`–`20:00`(左闭右开)执行。每条 SIP trunk 对每个原始手机号每天最多 3 次;某条线路失败时可在额度内经当前会话确认后改测另一条线路,但不得在窗口外等待、自动延迟、在同一条线路/号码上超额、自动重试或静默换线。 -- 不把 SIP 白名单出口 IP 当作 SIP 服务端,不虚构备用线路,不逐呼重写共享配置,不自动重拨已接通/未知的执行。 -- 生产使用多机器、多 EIP 直连;1000 路指完整 ASR/LLM/TTS 已接通通话,N+1 与供应商能力须实测,不采用单 EIP+NAT。 -- 独立开发和测试不要求云账号。云创建、EIP 改绑、网络放行、供应商消费和测试资源清理必须另获明确授权;不得触碰无关资源。 -- 不在源码、配置样例、文档、日志或证据中保存密钥、密码、私钥、完整用户音频/对话;注入受控凭据,诊断端点仅管理网可达。 -- 旧 Python 与新 Go 不得同时写同一资源或各自发放共享额度。没有可验证的所有权/状态回迁方案就暂停切换,不以回滚镜像冒险重拨。 +- 项目是独立 Go module,工具链 Go **1.27.1**;普通构建、测试、运行不读取父项目业务模块、数据库、env 或夹具。标准库和成熟官方 SDK 优先,Cobra 显式 `agent`/`dispatcher`,单制品分角色/权限/目录。禁止自行重写 SIP/ARI、RTP/RTCP/G.711、WebSocket、AMQP、SQLite 驱动、OSS 签名及 SDK 已覆盖的 AI 协议;核验现有依赖能力后再新增库。生产原生 Asterisk 仍由独立 Cell 的 systemd 统一管理,不声称当前 Mock 已完成真实媒体或 1000 路容量验收。 +- ECS 优先 Debian 13(Trixie)minimal;只有阿里云北京无可用镜像时允许 Ubuntu 24.04 LTS。Asterisk 直接安装在承载 ECS 主机,由 systemd 启用自启动并核对 `enabled+active`;不得以前台进程或容器入口代替。 +- 开发、Mock、mixed、real 的**非生产主机**部署与诊断步骤默认强制,不因时间/旧环境/调用方参数跳过或静默降级;显式关闭即失败。每次新主机/版本/Cell 至少留存脱敏 ECS/EIP/网络只读核验、Debian/架构/磁盘/权限、`rogee` SSH 与加固、发布包/依赖 SHA-256、Asterisk/systemd `enabled+active`、ARI/PJSIP endpoint/contact、媒体 profile/端口及运行版本。 +- 非生产 mixed/real 呼叫必须先通过上述真实时间门禁;**拨号前**启动受限 SIP/RTP 抓包和 Asterisk PJSIP logger,结束后采集 SIP 响应/INVITE–BYE 时间线、SDP codec/媒体地址端口、RTP 包/字节、录音与 ASR/LLM/TTS 事实及 SHA-256。失败通话也保存状态和抓包;tcpdump/CAP_NET_RAW、PJSIP logger 或 ARI/PJSIP 状态任一不可用须失败关闭。原始抓包/日志/录音只写受限证据目录,聊天、提交与长期证据只存脱敏摘要/计数/状态码/hash,不含完整用户音频/对话或凭据。统一入口见 [`deploys/test/nonprod-call-evidence.sh`](deploys/test/nonprod-call-evidence.sh);本地 `make check` 与 `make release-check-local` **不能代签主机诊断或生产门禁**。 +- 当前完成前至少检查格式、当前合同和 Proto 来源/hash、`go vet ./...`、`go test -race ./...`、构建、确实运行的隔离 RabbitMQ/HTTPS/双向 TLS 端到端测试、业务单元覆盖率 ≥65% 及 A01–A12/K01–K16 对照。真实 SaaS/management/OSS/AI/Asterisk/ECS、第二节点/Cell/租户、多 D 额度、容量/N+1及生产切换必须另有事实与授权,任何本机 Mock 通过不得写成其签收。 +- 不自动提交/暂存/清理使用者在父项目或本项目的无关修改;并行开发仍须有可追溯 Git/合同基线、一 lane 一工作区/测试资源、无交叠写集合、合并后回归。本目标明确禁用子 Agent,不能以模型、fast 环境或外部服务不可用阻塞本地 Mock 目标。问题根因不明时补可观测性并诚实报告,不能用静默兜底伪装修复。 diff --git a/README.md b/README.md index 0945162..6480345 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,28 @@ # go-sip -Go SIP 调度与执行项目:同一 module/二进制通过 Cobra 提供 `dispatcher`、`agent` 两个业务子命令,分阶段替换 Python。现行**外部** SaaS↔Dispatcher 合同仍为 MQ-only;本轮**项目内本地 Mock** 目标为四条只读 HTTP GET、v3 MQ 及单份最终结果,其中只有任务发现改按 [`docs/thirds/v0.2.md`](docs/thirds/v0.2.md) §2.5,其他本地 SaaS 协议仍按 v0.1。项目内提案和 Mock 不代表 SaaS/management 已签收或上线。 +独立 Go SIP 调度与执行项目。单一 Go 1.27.1 module/制品通过 Cobra 显式提供 `dispatcher`、`agent` 两个业务子命令;当前业务启动只允许**隔离 Mock**,mixed/real 直接拒绝。目标是分阶段以 Go 替换 Agent,不重写 Asterisk,也不建立第二套 SaaS 管理后台。项目的源码、文档、依赖、构建、测试、配置和发布入口均在本仓库内。 -> **当前状态:F00–F04、F08、F09 与项目内 Mock C 已完成本地适用验证;F06 的本地制品/拒绝门禁通过,真实部署和切换未执行。** P1 业务模块覆盖率均 ≥65%(MQ 须带 RabbitMQ 集成测试);CLI 和历史包未达线单独列为技术债,不宣称全仓达标。默认 Mock 不生成真实录音,上传成功/失败分支由隔离测试注入合成事实。真实 SaaS/MQ、供应商/ECS、Asterisk 媒体、生产切换及第二 Cell/第二租户仍属未验证或后续阶段;缺少 Asterisk/tcpdump 时部署 preflight 按要求 fail-closed,未冒充 mixed/real 通过。 -> 本项目已独立拆仓运营;源码、配置、依赖、迁移、测试、部署和文档均在此目录内维护。远程仓库为 `git.ipao.vip/rogee/go-sip`,本地 Git 默认分支为 `main`;真实外呼仍受逐次授权、capture-first、白名单和时间门禁约束。 +> **范围与进度:** [当前实施与验收入口](docs/plan-saas-dispatcher.md)记录 P01–P06 本地已验证、P07 文档/残留清理进行中、P08 最终 A01–A12/K01–K16 与 ≥65% 业务覆盖率对照待完成。隔离 RabbitMQ、双向 TLS/HTTPS、OSS PUT 与 AI Mock 的通过,不代表真实 SaaS、管理平台、MQ 应用收讫、OSS/AI 供应商、Asterisk/SIP/ECS、真实拨号或生产切换已验证;当前发布清单 `production_approval=false`。真实试拨另需逐次授权,并遵守白名单、Asia/Shanghai `09:00`–`20:00` 及拨号前诊断抓包门禁。 -## 已确认的范围 +## 唯一当前接口 -- 使用 **Go 1.27.1**、Cobra单项目双命令和同一发布制品;角色分进程、权限/目录分离。 -- 不接PG:Dispatcher独立SQLite统一任务/配额/outbox;Agent无业务数据库,文本/录音/必要恢复信息落文件。 -- 内部 **Unary gRPC**;Dispatcher配置Agent Endpoint列表,Agent业务启动只需Dispatcher Endpoint,证书/监听/本地资源通过部署提供。 -- 所有Agent共用mTLS证书、Dispatcher身份独立;需Endpoint主动激活/受限节点会话,不能把群组证书当单节点身份。 -- 本次以稳定、快速单节点上线为目标:**1 个 Agent、1 套 Asterisk、1 个单活 Dispatcher、单 Cell、至少 3 家独立 SIP trunk 的契约/协议 fixture、1 个内测商务租户**;双节点、第二 Cell、双租户延期第二阶段。 -- SIP 使用管理平台批准的不可变静态快照;维护窗口发布、排空和实际加载确认保留,在线动态发布/回滚编排暂缓。**现行真实路径**仍受 Asia/Shanghai 每日 `09:00`(含)至 `20:00`(不含)及白名单门禁约束;任务×线路时段与任务排除日期仅在本地 Mock V3 验证,不放开真实试拨。 -- 有适用开源库/官方 SDK 时强制复用,不从零手写 SIP/ARI、RTP、MQ、数据库、OSS 或 AI 协议客户端;选库失败先报告阻塞,不静默转自研。 -- 重写完整 Agent:调度控制面、Cell 外呼执行、ARI/RTP/录音、AI 流式适配、Cell 配置接收。 -- 分阶段迁移,最终构建、测试和运行不依赖 Python、父仓库目录或其它业务项目内部代码。 -- Asterisk 继续负责 SIP;独立 SIP 管理平台继续拥有配置管理权,均不纳入重写。 -- **外部运行基线与本地目标分开**:当前真实 SaaS↔Dispatcher 仍按已发布 MQ-only 合同工作。P1 本地目标按项目内契约采用四条只读 HTTP GET(SIP 全量、单任务含智能体、归属任务清单、按 `tenant_id` 取额度)和 v3 MQ 命令/控制/回执/唯一最终 `call.result`,移除对外 query/replay 及拆分通话事件。F01/F07 Schema、正反例、来源/hash 与 Mock C 已通过,F02–F04/F08/F09 本地验证见下文;外部兼容仍未验证,不扩为多 D 协调/HA。 -- **OSS配置存于Dispatcher配置文件,Agent向Dispatcher领取临时上传TOKEN后直传OSS,不保存长期凭据。** SaaS不下发OSS配置/TOKEN;D保留SDK签发能力,不转发文件。上传不申请SaaS会话、不等待verified/OSS ID或业务处理回复;D仅在原上传事实可靠进入指定持久MQ队列后记录交付完成。实时文字仍为 `transcript.updated`,归档不能替代实时事件。 -- 本次必须支持 **ASR-only** 与 **ASR + LLM + TTS** 两种模式;本阶段已完成本地/协议隔离双模式验收。百炼/火山ASR、OpenAI兼容LLM、火山TTS的真实供应商能力和生产参数联调仍标第二阶段/未启用,不能将 Mock 写成真实供应商通过;禁止复用旧LLM/TTS。 -- **现行 AI 配置**按任务 agent_version_id 经 MQ 向 SaaS 取得并持久绑定;过去独立的 AI GET 已废弃。**新目标**将获授权的智能体配置放入任务只读 HTTP 响应,不复活旧 GET 或保留 MQ 配置回退。 模型、提示词、音色/语速、识别、超时/打断等参数不写死;新版本用于新任务,无需重启,在途通话固定快照。静态SIP发布不代表AI配置静态硬编码。 -- 本轮验收范围收敛为单节点、单 Agent、单 Cell、单租户;保留 tenant_key 原值、独立队列、复合幂等和有界窗口。双节点、第二 Cell、双租户公平/背压/恢复不在本轮开发或验收范围,作为后续阶段。 -- 当前外部上传合同仍是 R12 临时授权、Agent 直传、R13 报告及 `recording.uploaded` 可靠入队。P1 本地目标改为每通话一份最终 `call.result`(含 `uploaded/unavailable/not_created`),不再发送拆分录音/实时文字/拒联事件;15 分钟收口、8 MiB 消息体上限及超限 outbox 阻塞已通过本地 Mock 验证,但未完成真实媒体、OSS 或 SaaS 应用签收。 +- [SaaS↔Dispatcher 通信说明](docs/thirds/saas-dispatcher.md);机器合同为 [`contracts/local/`](contracts/local/) 的 Schema、拓扑、正反例和来源/hash。SaaS 的 SIP、任务、归属任务清单、租户额度和 AI 供应商配置只经五类只读 HTTP;呼叫、控制、必要回执与每通话唯一结果只经固定 `v1` RabbitMQ 通信名。固定 HTTP `/internal/v1/dispatcher/...`、MQ `v1` 及业务 revision 是有效合同,不是旧实现代次。 +- Dispatcher 独立 UUID/资源归属、SaaS 预建的控制/任务队列、共享持久结果队列及本地 SQLite 权威任务/额度/inbox/outbox;未知通话不释放占用、不重拨、重启继续原消息身份。Agent 与 Dispatcher 通过双向 TLS Unary gRPC 和预绑定身份/会话代际交互,业务恢复落私有文件。内部联系见 [`proto/agent/agent.proto`](proto/agent/agent.proto)。 +- 任务内不可变获批 AI 参数支持 ASR-only 与 ASR+LLM+TTS;录音经短期授权由 Agent **一次** HTTPS PUT 至 OSS,明确失败才按受控文件恢复,结果未知不自动再传,最终事实进入 MQ。批准 Mock 的 SIP revision 回报不证明 Asterisk 已实际加载。 +- [`docs/plan-saas-dispatcher-v05-v0.1.md`](docs/plan-saas-dispatcher-v05-v0.1.md) 保持原字节和来源 hash,**只作为当前机器合同的历史来源**,不是另一个运行计划;更早的 F/W/v0.1–v0.4、旧 MQ-only 与部署示例在历史文档或 [`docs/archive/`](docs/archive/) 中追溯,不作为运行回退。 -## 当前本地实现 - -旧 MQ-only 基线的身份隔离、配置、回执、控制、AI授权及上传通知有 loopback RabbitMQ 本地证据,见 `docs/evidence/20260922-mq-only-local-final.md`;该证据不代签新目标。本轮项目内结果分别见 [F03 v0.3/F09](docs/evidence/f09-local-acceptance-v0.3.md)、[F04](docs/evidence/f04-local-dial-policy-v0.1.md)、[F08](docs/evidence/f08-local-final-result-v0.1.md) 和 [F06 本地发布阻断](docs/evidence/f06-local-release-gates-v0.3.md);[F03 v0.2](docs/evidence/f03-local-task-discovery-v0.2.md) 只留作历史。部署 preflight 在缺失 Asterisk/tcpdump 时明确失败并保留日志;本地测试不等同真实 SaaS、供应商或生产验收。 - -- `contracts/upstream/` 嵌入项目内自包含 W01 基线,记录父源 commit、dirty 继承和 SHA-256;运行时代码读取该包,不读取父目录。 -- `internal/store/` 提供 SQLite inbox、任务、租户/跨 Cell 配额、控制 CAS、replay 和 outbox 事务。 -- `internal/agent/` 只使用文件保存执行状态、transcript、录音/资产和崩溃恢复信息;boot 不释放未知占用;录音通过 Dispatcher 授权的短期 Alibaba OSS presigned PUT 直传,上传尝试和通知恢复分别持久记录,失败/过期保留源文件,不自动续期或重新PUT。 -- `internal/ai/` 对固定 AI Schema 做不可变快照校验,支持项目内显式 `full_ai`/`asr_only` 分支;`mock_pipeline` 只用于隔离协议/取消/参数测试,不宣称真实供应商已启用。 -- 旧 `internal/control/` HTTP业务实现、启动入口、CLI参数及环境配置已删除,不保留兼容通道。 -- `internal/rpc/` 提供 `agent.v1` Unary handlers、mTLS TLS1.3 配置、会话世代/fencing、CAS、permit/fact/upload metadata 边界;Agent CLI 可选启动受证书保护的 gRPC listener,并校验静态 Cell 制品与 AI 授权边界。`internal/calllog/` 提供按手机号 HMAC 关联、掩码和 allow-list JSONL 外呼业务日志,不写原始号码、凭据、音频、转写或 prompt。 -- `internal/health/` 使用 gopsutil 采样主机/进程资源;媒体端口和 AI 配额未接入时明确报告 unknown。`internal/mq/` 默认 bounded prefetch=1、per-tenant DLQ 和 publisher confirm;`make mq-integration-local` 只启动 disposable RabbitMQ。 -- `make acceptance-local` 校验当前项目内合同、格式、race/vet/build 和受限 RabbitMQ 本地矩阵;不访问真实服务。`make release-check-local` 构建并校验一次性本地制品与归档包、v0.1 业务/v0.3 发现/v3 队列草案和 Proto 的版本/hash,验证已有制品、打包目录、归档及校验文件均不会被覆盖,且 CLI 旧开关不存在、mixed/real 制品在打开资源前拒绝。`make release` 仅允许在 `dist/` 下指定**尚不存在**的目标目录,记录 dirty-source 与版本/hash,始终标记 `production_approval=false`;两者均不部署或拨号。真实 OSS 授权测试仍须另用受控环境显式开启,不能从 Mock 或上述制品检查推定通过。 -- 直接依赖版本/本地许可证清单见 [`docs/evidence/20260918-dependencies.md`](docs/evidence/20260918-dependencies.md);门禁结论见 [`docs/evidence/20260918-acceptance-matrix.md`](docs/evidence/20260918-acceptance-matrix.md);SIP 注册/认证/From/PAI/前缀/选路对比见 [`docs/evidence/20260919-sip-routing-implementation-comparison.md`](docs/evidence/20260919-sip-routing-implementation-comparison.md),手机号业务日志边界见 [`docs/evidence/20260919-phone-call-business-log.md`](docs/evidence/20260919-phone-call-business-log.md)。 - -以上 20260918–20260922 的历史证据只覆盖旧基线;本轮新合同、Mock C 和 F03–F09/F06 的本地证据单独列在前文。两组证据都不等于真实供应商、生产 SaaS/MQ receipt、实际切换或第二阶段拓扑通过。范围和延期项见 [`docs/evidence/20260920-scope-amendment.md`](docs/evidence/20260920-scope-amendment.md)。 - -## 显式重新申请上传授权 - -仅对已有失败或结果未知的上传使用: +## 本地验证(不触碰真实线路) ```sh -sip-go-agent agent upload-retry --spool /path/to/agent-spool \ - --upload-id '<原上传ID>' --request-id '<新的小写UUID v4>' \ - --file '/path/to/retained-recording.wav' +make check # 格式、Proto、当前合同/历史来源、race、vet、构建、三项实际运行的隔离 MQ 集成测试 +make release-check-local # 本地制品、hash、当前拓扑、Mock-only/不覆盖既有文件的包检查 ``` -该命令使用既有Dispatcher端点/mTLS部署配置,不发起呼叫。请求ID必须由调用方显式提供;每个ID最多一次PUT,已成功上传的文件只恢复通知,不能再次PUT。重复旧请求不会获得续期TOKEN。`completed` 只表示指定持久MQ队列已接收原通知,不表示SaaS已处理。使用前仍需满足对应环境的统一部署与诊断要求。 +结果、重启与故障场景及未验证项见 [`docs/evidence/saas-dispatcher-implementation.md`](docs/evidence/saas-dispatcher-implementation.md)。本地检查不等于非生产主机验收:任何实际新主机/版本/Cell 验证须执行 [`deploys/test/nonprod-call-evidence.sh`](deploys/test/nonprod-call-evidence.sh) 规定的资源、Asterisk/systemd、ARI/PJSIP 与媒体诊断;mixed/real 外呼还须在拨号**之前**启动受限 SIP/RTP 抓包和 PJSIP logger。当前 Mock-only 制品不能启用 mixed/real,更不能因白名单和样例自动发起真实呼叫。 -## 文档 +## 导航 -文档目录总览见 [`docs/README.md`](docs/README.md)。后续 Agent 先读[本轮配置读取与有界外呼计划](docs/plan-config-read-v0.1.md),按 F 工作包确认 C/L/M 和 I/M/G 前置及权威合同;原 [plan-0918](docs/archive/plan-0918.md) 恢复原文归档,仅供追溯。并行开发按新计划§9登记单写范围、隔离工作区/测试资源和合并回归,由集成负责人统一维护§8总台账;开发子Agent固定使用 **gpt-5.6-luna+max+fast**,不可用时报告阻塞,不静默降级。本轮未启动开发子Agent。 +- [当前实施与 A01–A12 验收](docs/plan-saas-dispatcher.md) · [当前通信合同](docs/thirds/saas-dispatcher.md) · [当前机器合同](contracts/local/manifest.json) · [可复核本地证据](docs/evidence/saas-dispatcher-implementation.md) +- [文档索引](docs/README.md) · [物理部署及独立 Asterisk 约束](deploys/README.md) · [项目开发与安全约束](AGENTS.md) -| 文档 | 内容 | -| --- | --- | -| [本轮配置读取与有界外呼计划](docs/plan-config-read-v0.1.md) | 首读入口:F00–F09、C/L/M 与 I/M/G 门禁、单写/验证状态;四条只读 GET 的外部合同仍待签收 | -| [任务发现事件游标计划](docs/plan-0926.md) | v0.3 项目内合同、运行与本地 Mock 范围;外部顺序、墓碑容量和切换另行签收 | -| [旧 plan-0918 归档原文](docs/archive/plan-0918.md) | W00–W16 历史状态及旧 MQ-only 阶段证据;非本轮执行入口 | -| [Go重写方案 v0.3](docs/architecture/Go重写方案_v0.3.md) | 本次单节点/单 Cell/单租户目标、至少3 SIP fixture、双AI模式、P0/P1/第二阶段分期 | -| [通信与事件数据交互 v0.1](docs/contracts/通信与事件数据交互_v0.1.md) | 首发Unary、静态SIP与双模式、§6.1–6.3 SaaS配置来源/参数矩阵/安全边界 | -| [SaaS↔Dispatcher 第三方对接顺序](docs/thirds/第三方对接事件与请求消费顺序_v0.1.md)、[任务发现 v0.3](docs/thirds/v0.3.md) | 仅任务发现按 v0.3 项目内修订;[v0.2](docs/thirds/v0.2.md) 是历史,其余项目内 SaaS 协议仍用 v0.1;外部合同/取消查询补传仍待签收 | -| [现行 MQ 机器契约](contracts/upstream/v1/mq.schema.json)、[Agent Proto](proto/agent/v1/agent.proto) | 现行消息和Unary字段权威;HTTP配置仅有项目提案 | -| [OpenAPI与MQ字段索引 v0.1](docs/references/OpenAPI与MQ字段索引_v0.1.md) | 旧源只读提取42个HTTP操作/115个组件及哈希;不代表新MQ-only入口,不手改生成物 | -| [deploys:物理机 systemd 发布包](deploys/README.md) | 锁定 Debian 13/Go 1.27.1/发布版本,构建并上传不依赖 Docker 的 Agent/Dispatcher 安装包 | -| [验证与切换验收 v0.3](docs/acceptance/验证与切换验收_v0.3.md) | 10个首发汇总门禁、88项基线按阶段适用、P2公平性及独立容量验收 | -| [开源组件选型与复用清单 v0.2](docs/dependencies/开源组件选型与复用清单_v0.2.md) | §1.3首发基础库/AI SDK选择,§4.3参数能力及锁版/许可/PoC门禁 | -| [G0开发准备与契约冻结方案 v0.1](docs/architecture/G0开发准备与契约冻结提案_v0.1.md) | 原D01–D10方向已确认;MQ-only新增GAP-10及受影响G0须重新验证,真实外部发布/生产联调另行安排 | -| [AGENTS.md](AGENTS.md) | 本独立子项目的开发与安全约束 | - -## 独立项目原则 - -- 当前设计文档以本目录 `docs/` 为唯一维护位置,不再向父项目另存一份。 -- 对接协议的现有权威来源仍属于上游;后续在本项目导入带来源、版本和 SHA-256 的不可变契约发布包,不能另写一套同名 Schema。 -- 后续发行包、CI、数据库和运行配置独立。不能靠 `../agent_call`、`../management`、`../sip_mock_server` 或父项目环境文件运行。 -- 业务代码范围仅包括 SIP Agent/Dispatcher 与 Asterisk;RabbitMQ、OSS、AI 供应商及 SaaS API 是 SaaS 提供的基础设施,不在本项目生产包中部署。独立集成测试使用自有隔离数据库、RabbitMQ 和契约 fixture;OSS数据面复用官方SDK/标准HTTP,D按自身配置文件提供临时TOKEN,A直传且不持有长期AK/SK;不申请SaaS上传会话,不等待SaaS校验或业务处理;外部 SIP Mock 只能以固定镜像及版本化协议接入,不导入其源码。 -- 真实外呼、云创建、供应商调用和消费授权均是独立门禁,不能由测试成功或本文档自动授权。 - -## 旧阶段基线与本轮入口 - -以下为旧阶段的原有分期记录;四条只读 HTTP GET(配置、任务发现、租户额度)、有界消费和时段改造的新依赖、状态与门禁以[本轮计划](docs/plan-config-read-v0.1.md)为准。 - -1. **P0:先完成MQ-only新合同(GAP-10)。** 冻结D唯一ID/生命周期、独立Topic/队列/绑定、全部请求响应/关联/错误/期限及Unary异步衔接;旧包原样保留。其余已有基线按受影响范围重新验证,包括8种事件payload、双AI模式(GAP-08)、SaaS任务AI配置读取/调参(GAP-09)、首发Unary职责、单 Cell 静态快照来源/加载回执、D配置文件/临时上传TOKEN及上传通知可靠入队和至少3家SIP trunk的配置/协议 fixture。只核验实际采用的SDK;火山TTS参数覆盖、精确版本/许可证未核验前不宣布锁库,不等待未来动态发布/文本OSS归档合同。 -2. **P1:本次单节点内测上线。** 完成单 Cell、单租户、双模式、幂等/控制/配额/恢复/录音安全的本地/隔离闭环;真实 ECS、真实外呼、生产 SaaS/MQ 联调不作为本阶段前置。 -3. **第二阶段:** 真实 SaaS/MQ 对接、RabbitMQ ACL/TLS及适用的业务回执、真实供应商/ECS 联调,以及双节点、第二 Cell、第二租户公平调度。 -4. **后续另立项:** 在线动态发布、自动跨供应商FALLBACK、多Dispatcher HA/分布式配额、权重借用、文本OSS归档、1000路完整AI/N+1。既有call/command整体补传不是通用回放平台,当前单节点首发仍保留。 - -阶段目标详见主方案§1/§10,首发验收见验收方案§1.1–§1.2;文件名保持不变。旧 MQ-only 本地门禁见[归档计划 §8.2](docs/archive/plan-0918.md)和`docs/evidence/20260922-mq-only-local-final.md`;本轮新目标仅按[新计划 §8](docs/plan-config-read-v0.1.md)记状态;真实依赖、供应商、云/拨号、生产receipt、容量和切换仍属第二阶段,归档旧证据见 `docs/archive/evidence/20260920-local-p1-acceptance.md`。 +现存 SQLite、spool、outbox 与 Agent 恢复文件不自动清空或迁移;发现旧状态拒绝启动并保留原文件,必须先厘清未交付事实。仓库不保存真实密钥、完整用户音频或完整对话。 diff --git a/deploys/physical-deployment.md b/deploys/physical-deployment.md index 841eaa9..b34c639 100644 --- a/deploys/physical-deployment.md +++ b/deploys/physical-deployment.md @@ -1,5 +1,7 @@ # Physical production deployment +This is the **target host layout**, not an approved deployment of the current binary. The current business commands are isolated Mock-only and reject mixed/real before opening resources; the local release manifest has `production_approval=false`. Neither the steps below nor a local package/check authorize real services or calls. Native Asterisk loading and required non-production diagnostics still need separate evidence. + Production is a small systemd installation on Debian 13 amd64: 1. native Asterisk Cell (`asterisk.service`), owned by the approved SIP diff --git a/docs/README.md b/docs/README.md index 9a01231..86e1559 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,47 +1,25 @@ # 文档导航 -`docs/` 是本项目唯一的设计、契约、决策和验证文档目录。 +`docs/` 是本项目设计、合同、决策和验收事实的维护目录。以下入口以**当前项目内隔离 Mock 合同**为准;真实 SaaS/management、供应商、线路、ECS 和生产切换仍未签收。 ## 首读顺序 -1. [`plan-config-read-v0.1.md`](plan-config-read-v0.1.md):四条只读接口、任务发现、有界消费及外呼时段的 F00–F09/F06 执行入口、C/L/M 和 I/M/G 门禁;任务发现 v0.3 另读 [`plan-0926.md`](plan-0926.md)。原 [`plan-0918.md`](archive/plan-0918.md) 按 HEAD 原文归档,仅作旧 W00–W16 历史。 -2. [`architecture/Go重写方案_v0.3.md`](architecture/Go重写方案_v0.3.md):单节点、单 Cell、单租户的总体方案与分期。 -3. [`contracts/`](contracts/):SaaS、Dispatcher、Agent、RabbitMQ 和 OSS 的项目内契约。 -4. [`acceptance/验证与切换验收_v0.3.md`](acceptance/验证与切换验收_v0.3.md):验收门禁、证据要求和第二阶段边界。 -5. [`evidence/f09-local-acceptance-v0.3.md`](evidence/f09-local-acceptance-v0.3.md) 和 [`evidence/f06-local-release-gates-v0.3.md`](evidence/f06-local-release-gates-v0.3.md):任务发现当前项目内 Mock 与发布阻断证据;[`20260922-mq-only-local-final.md`](evidence/20260922-mq-only-local-final.md) 仅作旧 MQ-only 本地基线。 +1. [`plan-saas-dispatcher.md`](plan-saas-dispatcher.md):P01–P08 当前实施进度、A01–A12/K01–K16 验收与未验证边界。 +2. [`thirds/saas-dispatcher.md`](thirds/saas-dispatcher.md):唯一现行 SaaS↔Dispatcher 项目内通信说明,五类只读 HTTP、固定 `v1` MQ、归属、控制、唯一结果与上传恢复。 +3. [`../contracts/local/manifest.json`](../contracts/local/manifest.json) 与 [`../contracts/local/`](../contracts/local/):唯一当前机器 Schema、拓扑、正反例和不可变来源/hash;内部 Agent RPC 见 [`../proto/agent/agent.proto`](../proto/agent/agent.proto)。Markdown 不代替机器校验。 +4. [`evidence/saas-dispatcher-implementation.md`](evidence/saas-dispatcher-implementation.md):本地测试、故障/重启与未验证项的可核查证据。 +5. [`../AGENTS.md`](../AGENTS.md):本独立项目开发、SIP、部署、真实呼叫与诊断约束。 -## 按职责查找 +## 其它文档 -### 方案与范围 - -- [`architecture/Go重写方案_v0.3.md`](architecture/Go重写方案_v0.3.md):产品范围、运行边界和阶段目标。 -- [`contracts/通信与事件数据交互_v0.1.md`](contracts/通信与事件数据交互_v0.1.md):通信、事件、AI 配置和上传边界。 -- [`architecture/G0开发准备与契约冻结提案_v0.1.md`](architecture/G0开发准备与契约冻结提案_v0.1.md):D01–D10 方向及 G0 前置。 -- [`dependencies/开源组件选型与复用清单_v0.2.md`](dependencies/开源组件选型与复用清单_v0.2.md):组件、SDK、版本和 PoC 门禁。 -- [`references/OpenAPI与MQ字段索引_v0.1.md`](references/OpenAPI与MQ字段索引_v0.1.md):旧源只读索引,不是当前 MQ-only Schema。 -- [`references/saas-page-snapshot-analysis.md`](references/saas-page-snapshot-analysis.md):SaaS 页面可见字段盘点;其中旧 MQ 配置建议已被新目标取代,非契约/验收证据。 - -### 契约与决策 - -- [`thirds/第三方对接事件与请求消费顺序_v0.1.md`](thirds/第三方对接事件与请求消费顺序_v0.1.md):SaaS↔Dispatcher 的项目内配置与 MQ 业务契约;仅其中旧 §2.5 任务发现已由 [`thirds/v0.3.md`](thirds/v0.3.md) 项目内合同替换,[`v0.2`](thirds/v0.2.md) 为历史。取消对外查询/补传及分散事件仍待外部签收。 -- [`../contracts/upstream/v1/mq.schema.json`](../contracts/upstream/v1/mq.schema.json)、[`event-payloads.schema.json`](../contracts/upstream/v1/event-payloads.schema.json)、[`mq-topology.json`](../contracts/upstream/v1/mq-topology.json):现行 MQ 信封、事件正文、路由的机器契约。 -- [`contracts/config-read-fields-v0.1-proposal.md`](contracts/config-read-fields-v0.1-proposal.md):两只读接口字段来源、响应结构与项目自拟 Schema/Mock 示例;**不是现网 SaaS JSON 或正式合同**。 -- [`decisions/`](decisions/):已记录的关键技术决策。 - -### 证据 - -- [`evidence/README.md`](evidence/README.md):证据与开发文档的职责边界、保留和归档规则。 -- `evidence/` 保存可复核的运行事实、测试结果和边界说明;历史证据不因当前契约变化而改写。 -- 旧 MQ-only 本地结论见[归档计划 §8.2](archive/plan-0918.md)和 [`20260922-mq-only-local-final.md`](evidence/20260922-mq-only-local-final.md);**本轮新方向的状态只在[新计划 §8](plan-config-read-v0.1.md)登记**。 -- 证据中的 `mock`、`mixed`、`real` 必须按原记录理解;本地或隔离通过不等于真实 SaaS、供应商、生产或容量验收通过。 - -### 归档 - -- [`archive/`](archive/) 保存已被当前基线替代、但仍需保留追溯价值的文档,包括恢复原文的[旧总计划](archive/plan-0918.md),以及无项目归属的旧参考资料。 -- 归档文件不作为当前实现、契约或验收依据;需要引用历史事实时必须使用归档路径。 +- [`architecture/`](architecture/) 保存产品方案、边界与阶段背景;[`decisions/`](decisions/) 保存设计决定;[`contracts/`](contracts/) 是原有合同和提案记录,当前机器约束以 `../contracts/local/` 为准;[`acceptance/`](acceptance/) 保存跨阶段验收基线,须按本轮单节点/单 Cell/单租户范围核对。 +- [`plan-saas-dispatcher-v05-v0.1.md`](plan-saas-dispatcher-v05-v0.1.md) 是**不可变历史来源**:原文件路径和 SHA-256 被当前合同 `manifest.json` 引用,必须保持原字节,不是第二个当前运行计划。旧 [`plan-config-read-v0.1.md`](plan-config-read-v0.1.md)、[`plan-dispatcher-state-v0.1.md`](plan-dispatcher-state-v0.1.md)、[`plan-0926.md`](plan-0926.md)、`thirds/v0.1–v0.4` 和上游 MQ-only v1 只供追溯旧事实,不能覆盖当前项目内合同,也不能作为兼容回退。 +- [`archive/`](archive/) 保存旧 W/F 工作包、旧部署样例和上一版 AGENTS 原文等历史事实;[`archive/plan-0918.md`](archive/plan-0918.md) 是旧 W00–W16 原文,不是现在的执行入口。归档说明不能代签真实验收。 +- [`evidence/README.md`](evidence/README.md) 说明历史证据保留规则;旧本地结果仍按当时环境和版本解读,不能据此推断当前合同或真实供应商已通过。 +- [`../deploys/README.md`](../deploys/README.md) 为 Mock-only 本地发布及物理环境要求,实际新主机/版本/Cell 诊断与拨号前受限抓包见 [`../deploys/test/nonprod-call-evidence.sh`](../deploys/test/nonprod-call-evidence.sh)。本地包检查不替代真实 Asterisk 加载或生产审批。 ## 维护规则 -- 不在 `docs/` 复制上游 Schema;上游契约包、生成物和字段索引保持单一来源。 -- 不把证据摘要改写成新的通过结论;范围、环境和未执行项必须保持可追溯。 -- 新增文档先归入方案、契约、决策、证据或归档中的一个明确类别;根目录 `plan-config-read-v0.1.md` 是总执行入口,用户指定的 `plan-0926.md` 仅说明 v0.3 任务发现分项,不另设第二个总台账;不新增无类别根文件。 +- 不在文档中维护第二套 Schema,不改写已被来源/hash 固定的原文;新增当前字段先更新机器合同及正反例,再更新当前说明与来源记录。 +- 当前状态只在 `plan-saas-dispatcher.md` 与实施证据中维护;历史文件保留真实来源和阶段,不为消除旧名称而重写归档或受保护的使用者修改。 +- 报告须区分本地隔离 Mock、真实非生产和生产,注明哪些测试未执行。源码、样例、日志、证据均不得保存真实凭据、完整用户音频或完整用户对话。 diff --git a/docs/archive/AGENTS-before-current-contract.md b/docs/archive/AGENTS-before-current-contract.md new file mode 100644 index 0000000..7dbfce2 --- /dev/null +++ b/docs/archive/AGENTS-before-current-contract.md @@ -0,0 +1,194 @@ +# go-sip:独立项目约束 + +## 宪法 + +- 任何涉及文件的调研或修改,如果当前是 git 仓库,需要先同步远程提交到本地,避免调研过时问题。 +- 基于 TDD 进行功能的开发与业务变更,单元测试覆盖率要保证 65% 以上 +- 任何时候我提出任何需求均需要理解并**结构化复述后与我进行确认,避免理解偏差**。 +- 不要在代码里藏兜底逻辑来吞掉错误、隐藏问题。出了问题就应该让它爆出来,否则你永远找不到真实问题。 +- 当一个问题出现时,不要用各种 small fix、针对性补丁来掩盖它。**必须定位真实根因,彻底修复**。在 bug 上糊纸只会让系统积累你不知道的危险暗病。 +- 即使问题很难定位,也**绝不要偷懒做表面修复**。应该给项目增加充分的日志和可观测性,保证下次问题再现时你有足够信息去定位。问题无法修复时,只需要诚实告诉我信息不足、需新增日志,不要假装修好了。 +- 始终注意在关键路径上给自己留足排查日志,确保每一个**关键节点都是可追溯**的。 +- 当项目关键技术栈或产品方向发生变更时,同步更新 agents.md。文档必须随代码一起演进,不能让它变成过时的谎言。 +- 大规模重构或实验性改动前,必须先切新分支。 + +- 不以维护向后兼容性为目标。**对于已经废弃的代码路径,应直接移除**,不再通过兼容层、回退机制或迁移方案予以保留。 +- 在充分满足当前需求的前提下,采用**尽可能简单的实现方案**。避免引入缺乏实际需求依据的抽象、配置项和间接层。 +- **采用渐进式、分层的方式构建系统**。首先完成能够端到端运行的最小版本,再基于稳定可用的产品逐步增加功能。不要以尚未成熟的复杂性取代已经可用的产品。 +- **保持组件的模块化**,并明确划分不同职责与关注点。 +- 当成熟且维护良好的库能够降低整体复杂度或提高可靠性时,应优先采用。除非有明确理由,不要重复实现通用功能。 +- 在自行实现功能或新增依赖之前,应优先评估项目现有依赖的能力。应先查阅相关文档和类型定义,不应未经确认就认定某个库不具备所需能力。 +- 架构决策**应着眼于长期演进**。不要采用仅能解决当前问题、且预期需要在后续替换的权宜方案。 +- 在设计解决方案之前,**先研究成熟产品如何解决同类问题**。优先采用经过验证的模式和约定,避免从零开始另行设计一套方案。 + +## 禁止清单(不主动考虑、不主动提议、不实现,遇到只记入 TODO 技术债列表) + +1. 法律合规:商业库授权、开源协议合规、GDPR/个保、隐私政策(法务负责)。 +2. 依赖安全:NPM 及第三方包漏洞、安全补丁、依赖升级策略。 +3. 访问安全:服务只需支持局域网访问(host 绑定 0.0.0.0 即可),不考虑公网暴露、HTTPS、认证/权限体系(登录、RBAC)、限流、防爬、数据加密、审计日志。 + +## 红线清单(快速阶段也不能省,现在便宜、以后极贵) + +1. 数据模型/表结构:认真设计,建表慎重——改表成本远高于写代码。 +2. 目录结构与模块边界:保持简单清晰,不堆一坨代码。 +3. 基础错误日志:出错时至少能看到发生了什么。 +4. Git:小步提交,保持历史清晰。 +5. 基础输入校验:仅防止程序崩溃,不做安全加固。 +6. 环境差异配置(端口、地址等)与代码分离(.env 或配置项)。 + +## 沟通方式 + +- 向使用者回报时,使用清楚直白的语言说明做了什么、结果如何。最终回复禁用术语、技术实现细节与工程腔。写法是:对一个聪明但没在看代码的人解释。 + +- 实际执行过程(思考、规划、写程序、除错、解决问题)保持完整的技术严谨度,这条规范只适用于对使用者的沟通方式。 + +## 回复风格 + +- 只写结论、实际改动、原因、验证结果 +- 不描述推进动作,禁用「我先……再……」等叙述句式 +- 不使用工程汇报腔(「落地」「落到」「推进」等类似用语) +- 直接、专业、去表演化 +- 回复文字永远使用与对方相同的语系,专有名词维持英文 +- 不使用口语化表达,说重点,简单明了 +- 需要时搭配条列式与表格加强输出可读性 + +## 决策规则 + +- 当方案有多个选项时,列出每个选项的优缺点,并明确指出推荐选项与原因,先问我。 +- 有多种实现方式时,选最简单能跑通的。 +- 遇到"禁止清单"中的问题:不展开、不实现,追加到 TODO 技术债列表即可。 + +## Sub-Agent 使用时机 + +当任务符合以下任一条件时,直接 spawn sub-agent 分工执行,无需询问使用者: + +- 任务可拆分为多个**平行且无依赖**的子任务 +- 各子任务职责明确分离,合并执行会造成 context 混杂 +- 大量结构相同的重复性任务(可用 `spawn_agents_on_csv` batch 执行) +- 各子任务需要不同的 model 配置或 sandbox 权限,例如: + + - 探索型任务使用轻量 model + `read-only` sandbox + - 审查型任务使用高推理 model + `read-only` sandbox + - 修改型任务使用执行导向 model + `workspace-write` sandbox + +## 验证标准 + +开始任务前先定义完成标准。交付前依此验证,发现问题就修好再测,不把未完成的工作交回给使用者。只有确认完成,或遇到真正需要使用者介入的障碍时,才回报。 + +## 当前范围 + +- 后续Agent必须先读 **`docs/plan-dispatcher-state-v0.1.md`** 和 **`docs/thirds/v0.4.md`**;历史 F 工作包索引见 `docs/plan-config-read-v0.1.md`:按 F 工作包/文档索引读取需求与契约,确认 C/L/M 和 I/M/G 前置、授权及写入边界后实施;并行时按该计划§9认领,子Agent只写独占模块/证据,§8本轮总台账、公共文件及合并状态由集成负责人单写。已恢复原内容的 `docs/archive/plan-0918.md` 仅保留旧 W00–W16 历史,不是现行计划入口。本地 Dispatcher v0.4 任务发现、MQ 控制及外呼入站规则另读 `docs/plan-dispatcher-state-v0.1.md`、`docs/thirds/v0.4.md`;`docs/plan-0926.md`、`docs/thirds/v0.3.md` 及 v0.2 状态仅作历史。**v0.4 的 Schema、隔离 Mock 与代码已在项目内实施,真实 SaaS/management 尚未签收或联调;仅显式条件清理已终止任务的任务配置副本,不自动清除现有 SQLite 数据。没有 SaaS 应用收讫的执行、幂等及 outbox 记录继续保留。日志不得记录密钥、完整音频或对话。** 计划不替代权威Schema或验收,不重复审批已确认方向,不自动授权真实云/付费/拨号。 +- 当前MQ-only/OSS调整目标已获用户明确批准由当前Agent独立执行,**不启动子Agent**,不得再以子Agent模型/fast环境阻塞本目标;此前按任务类型自动分工规则在本目标不适用。若未来另获启动授权,仍遵守下条模型要求。 +- 用户指定:若启动开发及配套审查子Agent,固定 **gpt-5.6-luna、max思考、fast模式**。启动前查询精确provider/model和runner支持并显式配置(当前工具用模型`:max`后缀及`fast: true`,不继承默认);不可用/不支持/无法核验则报告阻塞,不静默换模型、降思考档、关fast或换CLI。此为后续执行约束,本轮仅修复计划,未启动开发子Agent。 +- 并行开发须先有获授权的可追溯Git/契约基线、一lane一工作区/测试资源、无交叠写集合及每批合并后回归;当前子项目尚未跟踪的文件不能假定存在于HEAD/worktree。不得自动提交、暂存或清理父项目无关改动;详情见 `docs/plan-config-read-v0.1.md` §9。 +- 当前已获授权进行本项目开发:W01 项目内契约基线和 W02 Proto/stubs 已建立;仍不能把设计、Mock、Proto或88项测试清单写成真实供应商/生产验收已通过。入口和权威依据仍为 `docs/architecture/Go重写方案_v0.3.md`、`docs/acceptance/验证与切换验收_v0.3.md`、`docs/contracts/通信与事件数据交互_v0.1.md`、`docs/references/OpenAPI与MQ字段索引_v0.1.md`、`docs/dependencies/开源组件选型与复用清单_v0.2.md`。 +- 开发准备见 `docs/architecture/G0开发准备与契约冻结提案_v0.1.md`:D01–D10的方案方向、双模式/许可/恢复机制及内部PoC初始profile已获用户确认;本项目已自行交付 W01/W02 开发基线,但外部权威发布、真实预算、供应商签收和 G0/PoC 仍需分别验证,不能混写为生产合同。缺失字段细节、实际预算及方案变更另行确认。本轮验收基线收敛为单节点/单 Agent/单 Cell/单租户;双节点、第二 Cell、第二租户及其公平/故障矩阵不在本轮开发或验收范围,跨 Cell/多租户能力保留为后续阶段。 +- 用户已确认完整 Go Agent、分阶段替换:调度、Cell 执行、ARI/RTP/录音、AI 流及 Cell 配置接收。最终没有 Python 运行依赖;不重写 Asterisk、不实现第二套管理后台。 +- 本项目已独立拆仓运营,源码、文档、依赖、配置、迁移、测试、部署、发布入口全部留在本目录。普通构建/测试/运行不读取父目录,不使用其它项目内部模块、环境文件或夹具,不共享其业务数据库。 +- 当前已初始化独立 Git 仓库并绑定公开远程 `git.ipao.vip/rogee/go-sip`;未经授权不要重新 `git init`、改为 submodule、移动历史或更改父仓库跟踪关系。 + +## SIP 接入信息 + +以下为用户提供的 SIP 参数,服务商名称待补充;已记录不代表已完成真实线路验证。 + +| 服务商 | SIP 服务端 | 主叫号码/标识 | 被叫前缀 | +| --- | --- | --- | --- | +| 数企 | `61.132.228.221:5060` | `BD93205882` | `7089` | +| 中鼎 | `60.171.24.90:5060` | `mbkq` | 无 | +| 百应 | `160.202.254.79:5060` | `KQ91526` | `mka755` | + +## SIP 全局共用定义:外呼号码白名单 + +- 外呼号码白名单:`15003164745`、`15830461047`。 +- 所有 SIP 线路仅允许在此列表范围内发起外呼;不在列表内的号码必须拒绝。该列表仅用于已授权的 Mock/明确安排的测试;不得因写入此处而自动发起真实呼叫,原始号码保持不变。 +- **现行真实/旧路径运行限制**仍为 Asia/Shanghai 每日 `09:00`(含)至 `20:00`(不含);窗口外 Dispatcher/Agent 必须 fail-closed,禁止等待、自动延迟、重试或换线。下述非生产和真实验证条款中的固定时间仍是**当前真实运行限制**。新增的 V3 执行入口只启用隔离 Mock,mixed/real 启动即拒绝;Mock 测试不能宣称 real 放行。 +- **F04 项目内 Mock 已实现、外部未签收的后续目标**:仅 Dispatcher 在持久接纳与实际发出呼出指令前判断任务×已选 SIP 线路的允许时段、白名单、命令/授权/租户额度期限及任务/AI 较小通话时限;Agent 只校验会话及 Dispatcher 签发期限,不重算外呼业务策略。任务按周一至周日分别配置多个允许时段,可排除多个指定日期;线路只配置每周允许时段,**没有线路排除日期**。均按 Asia/Shanghai 判断,跨日拆分、左闭右开,任务排除日期全天拒绝;缺失/不确定时段拒绝并持久记录,不等下一窗口、不自动重试或换线。任务和线路时段**尚不能授权真实试拨**;F08 最终结果/额度收口、F09 完整验收及真实合同/部署授权分别未完成,不能据此放开固定真实门禁。 +- 主叫标识保留原值(包括 `BD`),不能按纯数字手机号清洗,也不能直接当成 Digest 认证用户名;具体 From/PAI 等字段映射仍需确认。 +- 业务原始被叫号码保持不变;使用该线路时按其规则构造 `7089<被叫号码>`,避免重复添加或把该前缀带到其他供应商线路。 +- 传输协议、IP/Digest 鉴权、是否注册及并发限制仍需供应商确认;当前供应商已反馈需使用 PCMA,Asterisk 配置以 `allow=alaw` 表示,仍需真实线路验证。 +- 每条线路目前只提供一个服务端地址,未提供独立备用地址。不能把同一地址重复填写成主备并宣称具备容灾;三条已登记线路应作为独立 trunk 配置,不为凑主备虚构供应商。 + +## ECS 部署环境与 Asterisk 运行约束 + +- ECS 部署环境固定优先使用 Debian 13(Trixie)minimal;仅当阿里云北京区域没有可用的 Debian 13 镜像时,才允许使用 Ubuntu 24.04 LTS。不得擅自切换到其他操作系统。 +- Asterisk 必须直接部署在实际承载它的 ECS 主机上,由 systemd service 统一管理并启用开机自启动;部署验收必须确认 service 已 enabled 且 active,不得以手工前台进程或容器入口替代生产启动方式。 + +## 开发与非生产环境强制部署/诊断步骤 + +- 开发、Mock、mixed、real 的**非生产环境**必须默认开启环境部署与诊断步骤;这些步骤是必需的,不得因“只是开发”“环境已存在”“时间紧”或调用方参数而跳过、关闭、静默降级或默认禁用。任何显式关闭均视为配置错误,应失败并阻止验收。 +- 每次新主机、新版本或新 Cell 验证至少执行并留存脱敏事实:ECS/EIP/网络资源只读核验、Debian/架构/磁盘/权限核验、`rogee` SSH 与 SSH 加固核验、发布包及依赖 SHA-256、Asterisk/systemd `enabled+active`、ARI/PJSIP endpoint/contact 状态、媒体 profile/监听端口和运行版本。 +- 每次非生产 `mixed`/`real` 外呼验证,必须先通过 Asia/Shanghai `09:00`–`20:00`(左闭右开)时间门禁,再在拨号前启动受限 SIP/RTP 抓包和 Asterisk PJSIP logger,并在结束后采集 SIP 响应码、INVITE/180/183/200/4xx/5xx/BYE 或 CANCEL 时间线、SDP codec/媒体地址端口、RTP 包/字节计数、录音与 ASR/LLM/TTS 事实及 SHA-256;无拨号前抓包、状态快照或时间门禁不得宣称验证通过。失败呼叫同样必须保留状态和抓包证据,不能只报告一个 hangup cause。 +- 抓包、日志、录音和识别文本只写入受限的非生产证据目录,聊天、源码、配置样例、提交和长期证据不得保存密钥、完整用户音频或完整用户对话;交付证据默认保存脱敏摘要、计数、状态码和哈希。若 tcpdump/CAP_NET_RAW、PJSIP logger、ARI/PJSIP 状态采集任一不可用,必须 fail-closed 报告阻塞,不得静默改成无抓包流程。 +- 上述步骤由统一部署/验收入口自动执行;`mock`、`mixed`、`real` 只替换适配器,不能绕开同一套部署、诊断、状态和证据门禁。生产环境仍须另行授权和通过生产安全屏障,非生产默认强制开启不等于生产放行。 + +## 本次上线目标与分期(用户已确认) + +- P1以稳定快速内测上线为目标:1个节点、1个Agent、1套Asterisk、1个单活Dispatcher/SQLite、1个启用租户;本轮不开发、不验收双节点、第二 Cell/第二 Asterisk或第二租户。用户已确认后续多 Dispatcher 目标为每 D 独立 ID/接收队列、独占 Agent/Asterisk 执行资源;同一租户可有多任务,但每个任务只能有一个 D 归属,SaaS 持久绑定租户+任务→D,呼叫/控制不得分发到其它 D,不能暗中迁移。单任务并发可由归属 D 本地判断;跨 D 租户/供应商总上限仍须先冻结有界额度份额,不能各 D 各按全局上限放行。本轮不把 D1/D2 隔离 fixture 冒充多 D 业务运行。 +- 至少3家独立SIP trunk 的静态配置、路由/主叫/前缀/codec/额度约束和协议 Mock/mixed 覆盖仍需保持;真实供应商外呼和 ECS 仅作为第二阶段联调,不是本轮前置。 +- ASR-only和ASR+LLM+TTS均按批准的不可变AI配置在本地/隔离链路验收;不擅自加MQ模式字段,不复用旧LLM/TTS。真实供应商未联调时必须明确标记为第二阶段,不能把 Mock 写成真实供应商通过。 +- P1使用管理平台批准的静态单 Cell 快照和受控维护窗口,不做在线发布/回滚编排;静态配置必须关准入、排空、核验实际加载,旧直写通道不得并行。 +- P1保留 tenant_key 原值、租户独立队列、复合幂等键、有界窗口及单租户配额/控制边界;不开发或验收双租户公平、第二 Cell 汇总配额和多实例协调。 +- 本阶段上传按固定15分钟授权、单次PUT、原事实持久化及recording.uploaded可靠入队进行本地隔离验收;不申请SaaS上传会话、不等待complete/verified或OSS ID。RabbitMQ ACL/TLS和application receipt仅按适用版本化契约/隔离测试验证,不将通知入队称为SaaS已处理;真实SaaS/MQ联调延期第二阶段。 +- 88项验收为跨阶段基线,当前只签收单节点/单 Cell/单租户适用子场景;双节点、第二 Cell、第二租户、真实 ECS/生产联调、容量/N+1及切换均不作为本轮门禁。 + +## 语言与工程 + +- 工具链基线为 Go 1.27.1。实施时锁定 CI/构建镜像和依赖,并校验实际工具链;不得自动修改其它项目的 Go 基线。 +- 标准库优先、单Go module/二进制,用Cobra显式提供agent、dispatcher两个业务子命令,无默认双角色启动;同一制品分进程/权限/目录,升级受版本兼容和排空约束,不另造CLI框架。 +- JSON v2、UUID 和新测试 API 的采用以契约兼容和实测为前提;既有标识、哈希规范不得随 API 更换。实验性 SIMD 不在当前范围。 +- 用户已确认不接PG:独立Dispatcher持有SQLite权威任务/配额/outbox,Agent无业务DB,文本/录音及执行/上传恢复信息落文件。禁止NFS共享SQLite/两份DB双活发额度,自动跨机热备不在已实现承诺内。 +- 已确认Unary gRPC,Dispatcher预配置Agent Endpoint;Agent业务只需D Endpoint,证书/监听/ARI/持久目录由部署提供。SDK复用连接,不增内部MQ或双向流,不因RPC超时重拨。 +- 所有Agent共用mTLS证书,D身份独立;必须通过受控Endpoint主动激活/节点会话授权,不信自报身份/地址。共享私钥泄露影响整组,轮换/撤销和风险要签收,不关闭SAN/SNI校验。 +- D感知健康/负载/软件协议/能力及供应商applied配置版本;样本过期/缺失为unknown,低CPU不突破租户/供应商/Cell/AI配额,新boot不清旧未知占用。 +- 实施后至少执行本模块的格式化检查、`go vet ./...`、`go test -race ./...` 和构建;当前代码已有 W01/W02/本地 RPC/AI Mock 入口,真实 DB/MQ、媒体、供应商、容量及切换验收仍分别报告,不能用本地通过记录代签。 + +## 开源复用硬约束(用户已确认) + +- SIP 及其它组件有适用开源库/官方 SDK 必须复用,禁止从零手写替代协议栈或客户端。优先标准库、Asterisk 原生能力、现成 SDK;自有代码限业务状态机、事务、权限、配额和薄适配。 +- 不自写 SIP/ARI、RTP/RTCP 编解包、G.711、WS/AMQP/数据库驱动、OSS 签名、已有 SDK 覆盖的 AI 协议或 Schema 解析。库不满足先选替代、修上游或报告阻塞;例外须用户另行批准。 +- 采用前核验 module/tag/commit、Go1.27.1、许可证/NOTICE、传递依赖/漏洞及真实协议兼容,留存 PoC;不得将 main README、未归档或可下载等同于生产通过。 +- SDK 自动重试不得造成二次 originate、旧音频重播或重复收费;不因 SIP 库存在而用 sipgo/diago 替换已选定 Asterisk 架构。 + +## AI配置与参数(用户已确认) + +- P1采用百炼/火山ASR、OpenAI兼容LLM、火山TTS;SDK首选及未通过门禁见组件清单§1.3/§4.3。基础栈方向确定不等于精确版本、许可证或参数能力已验收;不为补字段改为自写协议。 +- **现行合同(尚未切换)**:Dispatcher按MQ任务agent_version_id,经RabbitMQ专用Topic向SaaS取得不可变AI配置/授权,响应回原Dispatcher;校验租户/源Schema/不可变摘要/能力并持久绑定后向Agent交付执行快照。过去已废弃的独立AI版本GET不复活;新获批准的方向是**任务配置只读接口内含智能体**,须另冻合同而非旧GET兼容层。Agent不直连SaaS,不从CLI/env/源码常量或SDK默认覆盖AI业务值,不新增任务MQ模式字段或调参后台。 +- 已有model/prompt/voice/speed/ASR输入与识别/temperature/max_tokens/timeout及对话控制必须实际传入SDK或控制器;热词/VAD/top_p/音量/阶段时限等所需扩展先在上游补GAP-09,再生成校验。严格additionalProperties不放宽,不借metadata/raw_request透传。 +- **现行 AI 合同**:SaaS新版本供新任务引用,无需改代码/重启D/A;在途/原排队任务固定快照,同版本异内容拒绝。缓存按租户+版本隔离,断SaaS无有效授权缓存拒新准入;显式0/false与未提供保真,并发通话不得共享可变SDK参数。 +- **项目内配置与执行(已发布外部 v1 不变)**:项目内 v0.4 任务发现、控制及外呼入站依据 `docs/thirds/v0.4.md`;其它必要边界见 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md`。拟定 SaaS 只读路径为 `/internal/v1/dispatcher/sip`、`/internal/v1/dispatcher/task/:task_id`、`/internal/v1/dispatcher/tasks` 和 `/internal/v1/dispatcher/tenant/:tenant_id/quota`;均为项目内字段/路径,实际 SaaS 兼容性未验证。D 用 `X-DISPATCHER-id`/`X-DISPATCHER-SECRET-KEY` 按需读取,不在源码、配置样例或日志暴露凭据。SIP 启动先取获批全量并核验 Agent/Asterisk 加载后才准入;任务按租户+任务缓存约 60 秒,到期重新读取完整 200,无 ETag、过期缓存或 MQ 配置回退;已接纳执行固定原快照,暂停/停止控制不等待缓存。项目内 v0.4 `call.execute` 入站只有 `task_id/callee`,调用路由、主叫、AI 和时限绑定已授权任务快照;历史 MQ 命令不因年龄过期,但拨号前门禁不放宽。任务终止仅允许**显式、条件式**清理任务配置副本,不删执行恢复、幂等或 outbox。SIP 改动须关准入、排空及核验实际加载;外部 opt-out 现行 MQ 即时语义不可擅自改称已切换为最终 `call.result`。F01/F07 本地 Schema、正反例、来源/hash 与 Mock C 已通过,外部签收/连通仍未验证。 +- 凭据/供应商端点来自受控引用且有授权校验;不再定义独立出口池标识或出口池授权名单,Dispatcher 仍校验任务允许线路、全局号码白名单、时段和额度,不能因可调参数绕过安全硬限额或启用不安全重试。OpenAI默认自动重试显式关闭;日志只留脱敏版本/摘要/有效参数,不打印prompt/变量/密钥。 +- 静态发布只约束SIP/节点制品,不将AI配置硬编码;GAP-08/09及SDK参数PoC为P1门禁,验证入口见验收§5.1(现有E/L项子场景,不新增虚假通过数)。 + +## 契约与可靠性 + +- 本项目设计/运行/验收文档只在自身 `docs/` 维护。上游共享接口有唯一权威来源;导入带版本、来源和哈希的不可变契约包,再生成类型/校验,不维护重复手写 Schema。 +- 新内部消息/许可/fencing 协议需先获批;不擅自改变 SaaS 路径、字段、状态、路由或控制语义。 +- **现行已发布合同(新版本生效前必须遵守)**:SaaS↔Dispatcher的全部交互唯一经RabbitMQ专用Topic订阅,双方无HTTP请求/回调/兼容通道或故障回退,包括执行、控制、查询、整体补传、AI配置/授权及recording.uploaded上传事实通知;上传不申请SaaS会话或等待verified/OSS ID回复。OSS配置/TOKEN不来自SaaS:Agent领取及显式重申请TOKEN只经D↔A Unary;本规则不禁止Agent→OSS、ARI、AI供应商HTTP(S)或gRPC的HTTP/2。 +- **本轮项目内目标(尚未替换真实外部运行)**:任务(含智能体)、SIP、任务发现和按 tenant_id 的租户额度走四条只读 HTTP;呼叫、控制及其必要回执/单份最终结果走 MQ,取消对外业务查询/补传和分散通话事件。不提供配置HTTP→MQ回退,也不恢复其它业务HTTP通道。SaaS须分发 management 已批准的唯一 SIP 版本,management 仍为唯一编辑/审批面。该本地语义按第三方契约及版本化 Schema/示例/hash 冻结,Mock C 是本地门禁,不等待外部签收;真实切换仍需另行授权。 +- 新HTTP配置字段项目内 SIP 新版见 `docs/contracts/config-read-fields-v0.3-proposal.md`、`config-read-v0.3.schema.json`/mock示例;AI 授权和静态 Cell 制品的项目内新版见同一提案;旧 v1/v0.2 保留历史,不作为当前运行契约;任务/额度仍参照 v0.1;截图只证实UI含义,英文响应键为项目自定义,绝非SaaS现网接口已确认字段。用户新增任务排除日期、线路时段等未见截图项按项目需求设计;SIP传输/鉴权/注册及额度未知不能猜默认值。Schema/Mock 校验可满足项目内 C,但不代表 SaaS/management 已发布或真实兼容;真实响应、审批来源和 Agent/Asterisk 实际加载仍须单独验证。当前唯一权威运行契约不因草案变化。 +- **每个Dispatcher必须有独立、全局唯一且不重复的ID及独立接收Topic/队列**;指定D的任务/现行MQ配置结果/上传结果不能由其它D抢收,也不能广播后仅靠正文过滤;新目标只读HTTP配置由该D UUID+SECRETKEY获取且须核对任务归属。身份与tenant/Agent/Cell ID、dispatcher_epoch分开;现行合同保留租户独立队列及原值tenant_key,完整新路由长度预算须重验。具体ID生成/持久化、Topic/绑定、消息字段/关联/错误/期限须随W01新版本冻结,不凭本文给旧严格Schema添加字段。 +- **v0.4 任务发现与队列所有权硬边界**:SaaS 独占创建、维护、退役每 D 的独立控制队列与每任务任务队列及绑定;Dispatcher 仅消费,不能自行建队、绑定或删除。本地 RabbitMQ 无 `configure` 权限 Mock 已通过,真实兼容性未验证。新加入或重启 D 先 `GET /internal/v1/dispatcher/tasks?mode=snapshot` 逐页取得同一 `snapshot_id`/`watermark` 的完整清单,全部校验后一次 SQLite 事务提交;独立控制队列积压处理完成前不开任务准入。运行期 `GET .../tasks?after=<内存游标>` 仅发现归属变更,每页持久提交后推进内存游标;重启不恢复旧 v0.3 持久事件游标,分页或持久化失败关新准入、停任务消费,MQ 控制及结果恢复照常处理。HTTP 的偶发状态与 MQ 已应用状态冲突则关准入,不让发现页覆盖已持久的 pause/stop;只有 MQ resume 经单任务新鲜状态确认才恢复原积压。stop 挂断、排空后回自身控制回执,未接纳旧外呼静默 ACK、不拨号、不回逐条结果,也不删 SaaS 任务队列。历史 `task.control` 与 `call.execute` 不因消息年龄过期,外呼仍在接纳和拨号前独立检查任务/白名单/时段/授权/额度及通话时限,未来 `issued_at` 不提前接纳。保留原值 tenant_key、租户额度和未知占用;跨 D 份额仍待外部冻结。v0.1–v0.3 发现证据仅作历史,v0.4 本地证据见 `docs/evidence/dispatcher-v04-local-acceptance.md`;不能证明真实 SaaS/management、多 D 或生产可用。 +- **OSS相关配置存于Dispatcher配置文件,Agent向Dispatcher领取临时上传TOKEN后直传OSS,不保存长期凭据;SaaS不再下发OSS配置/TOKEN。** D复用官方SDK提供受限TOKEN/目标信息,配置缺失/无效明确失败;不在样例、源码、日志或证据中保存实际密钥/完整TOKEN。过期只允许A显式向D重新申请,不自动续期或向SaaS申请TOKEN;精确配置格式/TOKEN形态/UploadGrant映射另行核验,不猜字段。 +- **本轮项目内目标:上传仅负责 Agent 直传及 D 可靠通知 MQ,SaaS 后续处理不属本项目职责;外部 v2 的现行上传事实不因本地目标自动改变。** D保留签发能力、不转发文件;R13持久保存事实与recording.uploaded outbox,消息为persistent、进入指定durable队列/绑定、mandatory无return且publisher confirm成功后才记交付完成。只写本地outbox不算入队;不申请SaaS会话、不等待verified/OSS ID、不新增VERIFYING、不伪造SaaS结果。 +- 新版recording.uploaded取代本项目recording.ready,字段为call_id/recording_id/upload_id/bucket/object_key/format/channels/sample_rate_hz/duration_ms/size_bytes/checksum_sha256;不含TOKEN/密钥/签名URL。MQ失败/确认丢失/重启只恢复原消息身份的交付,不重新PUT或新建资产。现行AI/控制等必要请求响应不受此收缩影响;新目标AI改用任务只读HTTP获取,控制仍走MQ。 +- 前一轮MQ-only文档纠正已结束;以下是**现行 MQ-only v1 合同的历史基线**,不得误当作新只读配置HTTP方案的限制或验收通过:现行机器依据见 `contracts/upstream/v1/mq-topology.json`、`mq.schema.json` 和 `event-payloads.schema.json`,第三方顺序说明见 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md`:纯Topic精确绑定、拒绝独立通配词段、tenant_key预算196 UTF-8字节、稳定UUID v4、旧版严格JSON配置及15分钟SDK预签名PUT;新目标保留业务MQ及OSS规则,仅替换配置获取,须另发版本化合同,不原地复用旧Schema。当前仅保留现有代码需要的契约包;未发生契约迁移前不新增契约,旧快照不作为当前工作树输入。新版须完成Schema/正反例/哈希验证后发布,不把方案确认当实现完成。历史 W01/W02/W04/W05/W07/W08/W11/W12/W13/W14 范围见归档 `docs/archive/plan-0918.md` §8.2;项目内 v0.4 配置/控制路径以 `docs/plan-dispatcher-state-v0.1.md` 和本地验收证据为当前入口;外部合同另行签收。全局唯一D身份/专用Topic及本地D1/D2隔离fixture为当前合同要求,不授权双D业务运行、HA或共享额度。 +- **现行已发布合同**中的实时文字事件名为transcript.updated,不新增call.transcript别名;OSS文本归档不能冒充当前实时文字/opt-out,缺少专用资产授权接口时明确未启用,不能伪装recording.ready。 +- **用户已批准的项目内精简契约目标**见 `docs/plan-config-read-v0.1.md` §3.5/§4 和第三方对接契约:SaaS↔D 移除对外查询/补传命令及分散通话/转写/拒联/录音事件,保留呼叫/控制命令和必要回执;录音仍上传 OSS,D 每通话只回传一份含最终转写、拒联事实与 OSS 路径的 `call.result`。本地实现以 F01/F07 Schema、正反例、来源/hash 和 Mock C 为门禁,无需额外外部/业务签收;本地验收更新后不要求通话前实时文字或拒联。无录音按 `not_created` 收口;预期录音明确失败立即 `unavailable`,未知最迟于 `call.ended_at + 15m` 收口。通话确认终结释放执行资源后释放占用,不等待 OSS 或 MQ confirm;未知仍占用。现行外部 Schema/代码保持原样,真实兼容性未验证。 +- 现有MQ信封command_type/command_id、event_type/aggregate_*与正文已对齐;事件payload专属约束尚需补齐,不把通用object校验当完整验收。字段索引只读生成,不手改成第二套Schema。 +- `tenant_key` 原值一对一绑定,不清洗、编码或截断;旧布局224个UTF-8字节预算不能在加入D身份后直接照搬,W01须冻结并验证完整routing key/queue预算及分隔符/通配符边界;超限停止发布并保留源任务。 +- 持久 inbox 后 ACK;状态与 outbox 同事务;confirm 不等于 SaaS 应用收讫。重复投递、未知执行和恢复不能触发重复拨号。 +- 配额覆盖所有 Cell/实例及未知占用;租约过期不自动释放不明通话。**现行 v2** 控制 CAS 为 expected_task_revision,pause 与 stop 的 drain/hangup 区分;paused 可按新授权恢复,stopped 不可恢复。现行整体补传仅 call_id/source_command_id;下一版对外查询/补传目标取消但严格合同/代码未改。最后发起许可、权限和屏障须故障注入。 +- **用户已批准的项目内精简契约目标(不能混写现行 v2;外部签收不阻塞本地 C)**:`call.execute.payload` 仅 `task_id/callee`,外呼信封身份仍用于防止重拨;路由/主叫/智能体版本与 `ring_timeout_ms/max_call_duration_ms` 均从已批准的任务配置取得并持久绑定。`task.control` 的 pause/resume/stop 均不带 `command_id` 或 `expected_task_revision`,不设计控制去重,但 D 必须回 task/action/status 处理结果;乱序、控制重投/回执丢失与 stop 后 resume 的本地判定写入第三方契约并由 Mock 测试;外部签收不是本地阻塞项,不偷偷重引入 CAS/去重掩盖。任务配置新增明确 `caller_profile_id`,按allowed_trunk_ids顺序选首个时段/额度/加载/主叫均匹配的线路,选后固定、不自动换线重拨;有效通话上限取任务与已授权AI两者较小值。旧running配置/发现不得解除已持久的paused/stopped,resume须强制最新任务核验,stopped同ID不可逆。拟定配置响应不再包含 `agent.content_sha256`;录音 checksum 另有用途,不误删;SIP SaaS 响应不再提供 snapshot_sha256 或静态制品。参见 `docs/plan-config-read-v0.1.md` 和 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md`。 +- management是SIP配置唯一编辑/审批面。**现行 P1**通过批准的版本化静态制品和受控部署入口交付,D核验目标/准入屏障,Agent加载并报告;不要求在线发布控制面。静态交接合同须批准,旧直接写Agent面不能同时启用;成功必须证明精确快照已被Asterisk加载。 +- **本轮项目内 SIP 配置读取目标(现行外部运行未切换)**:management 仍是 SIP 唯一编辑/审批方,SaaS 仅经只读 HTTP 提供该 D 资源分区的获批全量快照;新加入/重启 D 先查询、核验所属 Agent/Asterisk 实际加载的 SIP 线路 revision 后才消费执行队列;Agent/Asterisk 的部署配置由受控部署确定,不来自 SaaS。后续约每60秒核对版本,有变化就关执行准入、排空旧活动通话并确认新版已加载;控制MQ照常处理。SaaS/management 分属不同配置系统时须证明SaaS分发的是同一份获批线路配置版本;不引入共享业务DB或MQ配置回退。项目内 SIP 读取及准入链路已用隔离 Mock 验证;真实 Agent/Asterisk 加载与 SaaS/management 切换尚未验证。真实 SaaS/management 兼容性及多 D 资源配额份额仍未验证、不属本轮验收,详见 `docs/architecture/Dispatcher有界接纳与控制通道改造计划_v0.1.md` §3.2。 + +## 安全和真实验证 + +- mock/mixed/real 明确隔离;Mock 默认隔离真实外网,正式模式拒绝 Mock/测试凭据,不静默回退。 +- 只允许复用既有 ASR 协议;禁止复用 `voice_test` 的 LLM/TTS。P1必须完成新LLM/TTS规范、SDK适配和本地/协议隔离验收;真实供应商联调延期第二阶段,不能以Mock冒充真实供应商通过,也不能把真实联调延期误写成当前 P1 已实测。 +- 真实外呼只允许原始号码 `15003164745`、`15830461047`,但白名单和文档不是拨号授权;每次真实验证仍需明确安排,且仅可在 Asia/Shanghai `09:00`–`20:00`(左闭右开)执行。每条 SIP trunk 对每个原始手机号每天最多 3 次;某条线路失败时可在额度内经当前会话确认后改测另一条线路,但不得在窗口外等待、自动延迟、在同一条线路/号码上超额、自动重试或静默换线。 +- 不把 SIP 白名单出口 IP 当作 SIP 服务端,不虚构备用线路,不逐呼重写共享配置,不自动重拨已接通/未知的执行。 +- 生产使用多机器、多 EIP 直连;1000 路指完整 ASR/LLM/TTS 已接通通话,N+1 与供应商能力须实测,不采用单 EIP+NAT。 +- 独立开发和测试不要求云账号。云创建、EIP 改绑、网络放行、供应商消费和测试资源清理必须另获明确授权;不得触碰无关资源。 +- 不在源码、配置样例、文档、日志或证据中保存密钥、密码、私钥、完整用户音频/对话;注入受控凭据,诊断端点仅管理网可达。 +- 旧 Python 与新 Go 不得同时写同一资源或各自发放共享额度。没有可验证的所有权/状态回迁方案就暂停切换,不以回滚镜像冒险重拨。 diff --git a/docs/evidence/saas-dispatcher-implementation.md b/docs/evidence/saas-dispatcher-implementation.md index 4feeafd..2fe19ae 100644 --- a/docs/evidence/saas-dispatcher-implementation.md +++ b/docs/evidence/saas-dispatcher-implementation.md @@ -138,6 +138,8 @@ - 历史部署 JSON:部署目录仍放置旧 Dispatcher 文件配置与两份自行编号的静态 Cell 样例,其中审批标记不能证明现行管理平台审批。部署布局测试先复现三份旧样例仍在当前路径,再将原始字节移至 `docs/archive/deployment-examples/`,逐份核验迁移前后 SHA-256 相同并记录于该目录 README;当前 `deploys/config/` 仅保留已由现行 Mock 端点读取测试验证的 Agent 样例。安装包从未包含这三份旧文件,真实 Agent/Asterisk 加载和审批仍未验证。 +- 当前文档单入口:`AGENTS.md`、根 `README.md` 和 `docs/README.md` 不再把旧 MQ-only、F 工作包或 v0.4 发现写作当前执行依据,改指唯一项目内通信说明、机器合同与新的 `docs/plan-saas-dispatcher.md` 状态入口;物理部署说明标明当前制品只支持隔离 Mock、不批准真实安装。原 AGENTS 原字节归档到 `docs/archive/AGENTS-before-current-contract.md`(SHA-256 `8996ce2db909508172f59f39e780bdab2f48b2eba1acc3590dcffc50f8b2f6f0`)。版本化计划 `docs/plan-saas-dispatcher-v05-v0.1.md` 已被当前 `contracts/local/manifest.json` 固定原路径及 SHA-256 `666f39e56ea9f4b55661efcac82edd6f9729848e2d60e5f24cdf5aa3ac97ee87`,因此保留原字节作为合同**历史来源例外**,另建不带代次的当前计划索引,而不伪造来源或另立运行合同。四份当前根文档的相对链接、两份原字节 SHA-256、`go test ./contracts ./internal/config`、`make check`(含 race/vet/build 与三项真实隔离 MQ PASS)及 `make release-check-local` 均已核对通过;当前待办仍有 AI 自有名称及历史夹具残留分类,本批不宣布 P07 完成。真实 SaaS、Asterisk/ECS、OSS、AI 与非生产主机诊断均未运行。 + ## 验收台账 P01–P06 的项目内隔离证据见上;P07 全仓命名与唯一入口尚在清理,A01–A12 和 K01–K16 的最终对照仍待 P08。不得用本地 Mock 冒充外部签收。 diff --git a/docs/plan-saas-dispatcher.md b/docs/plan-saas-dispatcher.md new file mode 100644 index 0000000..e0aa223 --- /dev/null +++ b/docs/plan-saas-dispatcher.md @@ -0,0 +1,31 @@ +# SaaS↔Dispatcher:当前实施与验收入口 + +本项目的**唯一现行业务通信说明**为 [`thirds/saas-dispatcher.md`](thirds/saas-dispatcher.md),唯一项目内机器契约为 [`../contracts/local/`](../contracts/local/) 的 Schema、拓扑、正反例与 `manifest.json`。Agent 内部 RPC 见 [`../proto/agent/agent.proto`](../proto/agent/agent.proto)。本文只记录实施和验收进度,不另立一套字段或队列。 + +## 范围和来源 + +- 用户已确认 K01–K16,并明确批准实施 P01–P08;已取代原计划 §1.2“仅修订计划”的旧范围,不重复审批。固定 MQ `v1` 通信名、HTTP `/internal/v1/dispatcher/...` 路径和有业务含义的 revision 不属于自有实现代次,保持不变。 +- [`plan-saas-dispatcher-v05-v0.1.md`](plan-saas-dispatcher-v05-v0.1.md) 是本轮合同的**不可变历史来源**,其原路径及 SHA-256 已写入当前合同 `manifest.json`;为保护来源事实,不改名、不改字节、不作为另一个当前运行入口。其他旧方案、旧 MQ-only 合同与旧验收数字均为历史。 +- 当前仅验收单节点、单 Dispatcher、单 Agent、单 Cell、单租户的**隔离 Mock**。真实 SaaS/MQ/OSS/AI/Asterisk/SIP/ECS、容量及生产切换没有在本轮验证;样例、Hash 和本机测试均不是拨号或生产授权。 +- 不迁移、清空或自动处置现存 SQLite、Agent 恢复文件、旧 spool 和 outbox;发现旧执行/上传状态时拒绝启动并保留原文件,按事实另行确认处置。 + +## 工作项 + +| 阶段 | 本地状态及核验入口 | +| --- | --- | +| P01 契约 | 已建立当前 HTTP/MQ Schema、拓扑、正反例和来源/hash;见 `contracts/local/manifest.json` 及 [`evidence/saas-dispatcher-implementation.md`](evidence/saas-dispatcher-implementation.md)。 | +| P02 单入口与 Proto | 根命令只注册现行 Mock Agent/Dispatcher;八个 Agent RPC 方法、生成物与 `proto/manifest.json` 已验证。 | +| P03 五类只读 HTTP | SIP、任务、发现、租户额度及供应商配置只读;租户/任务归属和新鲜度均经隔离测试。 | +| P04 MQ/SQLite | SaaS 预建队列、独立 Dispatcher 身份、持久 inbox/outbox、控制屏障及共享结果队列有隔离 MQ 测试。Confirm 不等于 SaaS 已处理。 | +| P05 AI/媒体 | 不可变获批 AI 快照,ASR-only、ASR+LLM+TTS、关键词、16 kHz 媒体及失败边界仅经本地 Mock 验证。 | +| P06 录音/OSS/结果 | 隔离双向 TLS 录音、一次 HTTPS PUT、失败与未知结果恢复、48 小时边界和唯一最终 MQ 结果经本机测试。 | +| P07 唯一当前入口 | 旧执行/配置/MQ/AI/Proto/Schema/脚本路径已分批删除;当前文档和全仓残留例外审计进行中,历史来源及实际数据保留。 | +| P08 验收交付 | A01–A12、K01–K16、业务覆盖率 ≥65%、重启/故障矩阵及诊断门禁的最终对照**尚未完成**。不得将前述分批通过写为最终签收。 | + +每批变更、测试命令、正反例和未验证项详见 [`evidence/saas-dispatcher-implementation.md`](evidence/saas-dispatcher-implementation.md)。发布包 `production_approval=false`;`make check`、`make release-check-local` 仅检验本机隔离制品与行为,不运行真实服务。 + +## 交付门禁 + +- 必须执行格式、当前合同及 Proto 来源/hash 检查,`go vet ./...`、`go test -race ./...`、构建及三项**实际通过**的隔离 RabbitMQ 集成测试;旧测试筛选式匹配零项不得算通过。 +- 对照原计划 A01–A12 与 K01–K16 逐项记录本地证据和缺口,业务代码单元测试覆盖率须达到 65%;故障、重投、重启和约十分钟 Agent 会话过期边界不可遗漏。 +- 实际非生产主机验证另须执行 [`../deploys/test/nonprod-call-evidence.sh`](../deploys/test/nonprod-call-evidence.sh) 规定的资源/状态核查和拨号前受限抓包;本地 Mock、打包、证书与哈希均不能替代真实 Agent/Asterisk 加载、SaaS 应用收讫或供应商验收。未经单独安排不进行云操作或真实呼叫。