// Package contract 是 Gateway / Dispatcher / MCP 之间的共享契约: // Task 数据结构与 NATS subject 命名约定。 package contract import "encoding/json" // NATS subject / stream 约定(与 README、各服务 config 保持一致)。 const ( StreamTasks = "SUNDYNIX_TASKS" // JetStream stream 名 SubjectTasks = "sundynix.tasks" // 任务发布主题前缀;实际为 sundynix.tasks. SubjectTasksAll = "sundynix.tasks.>" // stream 捕获的通配 SubjectStream = "sundynix.streams" // Token 回流前缀;实际 sundynix.streams. ConsumerDurable = "dispatchers" // Dispatcher 持久消费者(队列组负载均衡) // HeaderStreamEnd 是 Token 流的结束信号(core NATS 消息头)。 // 置为 "1" 的消息体为空,表示该 task 的 Token 流结束。 HeaderStreamEnd = "X-Stream-End" // MCP 工具调用约定(第 4 层 Dispatcher → 第 5 层 MCP Tools)。 // 用 core NATS request-reply:同步拿结果,队列组内负载均衡。 SubjectToolsGo = "sundynix.tools.go" // Go I/O 型工具前缀;实际 sundynix.tools.go. SubjectToolsGoAll = "sundynix.tools.go.>" // mcp-go 通配订阅 SubjectToolsPy = "sundynix.tools.py" // Python 算法型工具前缀;实际 sundynix.tools.py. SubjectToolsPyAll = "sundynix.tools.py.>" // mcp-py 通配订阅 QueueToolsGo = "mcp-go-workers" // mcp-go 队列组(多副本负载均衡) QueueToolsPy = "mcp-py-workers" // mcp-py 队列组 // 服务探活:dispatcher 既无 HTTP 端点也不挂工具,单独用一个 core NATS // request-reply 心跳主题让控制面(管理端「服务状态」)能判定它在不在线。 SubjectHealthDispatcher = "sundynix.health.dispatcher" // 任务生命周期状态回写:dispatcher 开跑/跑完/出错经此主题广播,网关订阅落 PG 并推 UI。 // core NATS pub-sub(状态是幂等覆盖,丢一条由下一条纠正,无需持久化)。 // 注意:必须在 sundynix.tasks.> 之外,否则会被任务流捕获成"幽灵任务"自我放大。 SubjectTaskStatus = "sundynix.status.task" // 人工审批(HITL)决定回传前缀:实际 sundynix.approval.。 // core NATS pub-sub:UI 点批准/拒绝 → 网关发到此 → dispatcher 解除审批节点的阻塞。 SubjectApproval = "sundynix.approval" // 自动化评测结果回写:dispatcher 评完经此广播,网关订阅落 PG 并供 UI 查询。core NATS pub-sub。 SubjectEval = "sundynix.eval.task" ) // 评测质量分级(据综合分 + 忠实度阈值,闭环门控/告警用)。 const ( EvalOK = "ok" // 综合 ≥ 0.75 且无忠实度风险 EvalWarn = "warn" // 0.5 ≤ 综合 < 0.75,或忠实度偏低 EvalPoor = "poor" // 综合 < 0.5 ) // EvalEvent 是一次自动化评测的结果(经 SubjectEval 回流给网关落库)。 type EvalEvent struct { TaskID string `json:"task_id"` Overall float64 `json:"overall"` // 综合分 [0,1] Rule float64 `json:"rule"` // 规则分 LLM float64 `json:"llm"` // LLM 质量分 Faithful float64 `json:"faithful"` // RAG 忠实度分(0=无来源未评) Level string `json:"level"` // ok / warn / poor Flags []string `json:"flags,omitempty"` // 命中问题(规则 + 未被来源支持) Reason string `json:"reason,omitempty"` // 评语 Sources int `json:"sources,omitempty"` // 检索来源数 TS int64 `json:"ts"` // unix 毫秒 } // 任务生命周期状态机:submitted(网关建任务)→ running(dispatcher 开跑) // → done / failed / timeout(dispatcher 收尾)。 // HITL:执行到审批节点 → waiting(等人工决定)→ 批准回 running / 拒绝→rejected。 const ( TaskSubmitted = "submitted" TaskRunning = "running" TaskDone = "done" TaskFailed = "failed" TaskTimeout = "timeout" TaskWaiting = "waiting" // 等待人工审批 TaskRejected = "rejected" // 人工拒绝(或审批超时,安全默认拒绝) ) // TaskStatusEvent 是一次任务状态流转事件(经 SubjectTaskStatus 回流给网关)。 type TaskStatusEvent struct { TaskID string `json:"task_id"` Status string `json:"status"` // running / done / failed / timeout / waiting / rejected Detail string `json:"detail,omitempty"` // 失败原因 / 审批摘要等 TS int64 `json:"ts"` // unix 毫秒 } // ApprovalSubject 返回某任务的人工审批决定回传主题。 func ApprovalSubject(id string) string { return SubjectApproval + "." + id } // ApprovalDecision 是一次人工审批的结果(UI → 网关 → dispatcher)。 type ApprovalDecision struct { TaskID string `json:"task_id"` Node string `json:"node,omitempty"` // 审批节点 id(可空:按 task 维度兜底匹配) Approved bool `json:"approved"` // true=批准放行,false=拒绝中止 Note string `json:"note,omitempty"` // 审批人备注 By string `json:"by,omitempty"` // 审批人(用户 id) TS int64 `json:"ts"` // unix 毫秒 } const ( // MetaUserID 是 Task.Meta 中承载已登录用户标识的键(用于偏好记忆召回)。 MetaUserID = "user_id" // MetaSessionID 是 Task.Meta 中承载会话标识的键(用于短期多轮历史)。 MetaSessionID = "session_id" // 配置控制面按 kind 寻址:sundynix.config..get / .updated。 // Gateway 持有配置,消费方(Dispatcher/mcp-go)经 NATS 取用/订阅变更。 ConfigKindChat = "chat" // 对话模型(Dispatcher 用) ConfigKindEmbedding = "embedding" // 向量模型(mcp-go RAG 用) // 报告生成:Task.Meta[MetaIntent]==IntentReport 时,Dispatcher 走专用多步编排 // (规划大纲 → 各章节并行检索+撰写 → 汇聚 → 渲染 Word),而非通用对话图。 MetaIntent = "intent" IntentReport = "report" MetaTopic = "topic" // 报告主题 MetaKB = "kb" // 报告依据的知识库(可空,则不挂检索) ) // ConfigGetSubject / ConfigUpdatedSubject 返回某类配置的 request / 广播主题。 func ConfigGetSubject(kind string) string { return "sundynix.config." + kind + ".get" } func ConfigUpdatedSubject(kind string) string { return "sundynix.config." + kind + ".updated" } // SubjectExec 是执行可视化事件的回流前缀;实际 sundynix.exec.。 // 与 Token 流(sundynix.streams.)分流:Token 是零拷贝字节,Exec 是结构化节点事件。 const SubjectExec = "sundynix.exec" // ExecSubject 返回某任务的执行事件回流主题。 func ExecSubject(id string) string { return SubjectExec + "." + id } // ExecEvent 是一次任务执行中某节点/阶段的生命周期事件(经 sundynix.exec. 回流给 UI, // 用于"运行·观测"的实时轨迹:节点点亮、工具调用入参/产出、各阶段耗时)。 type ExecEvent struct { Seq int `json:"seq"` // 任务内自增序号(保序) TS int64 `json:"ts"` // unix 毫秒 Node string `json:"node"` // 稳定节点 id:init / tool:wiki_search / prompt / model / plan / section:0 / render Kind string `json:"kind"` // 归类着色:memory|tool|prompt|model|plan|section|render|system Phase string `json:"phase"` // start|end|error|info Label string `json:"label"` // 人读标题 Detail string `json:"detail,omitempty"` // 入参/产出/计数预览 MS int64 `json:"ms,omitempty"` // end 事件的耗时(毫秒) } // TripleView 是回流给 UI 的一条知识三元组(主体-关系-客体),用于实时展示抽取过程与图谱。 type TripleView struct { S string `json:"s"` P string `json:"p"` O string `json:"o"` } // IngestEvent 是入库流水线的实时进度事件(经 sundynix.streams. 回流给 UI)。 type IngestEvent struct { Stage string `json:"stage"` // 解析/解析完成/切块/向量化/写Milvus/写Bleve/抽实体/写Neo4j/完成/失败 Msg string `json:"msg,omitempty"` // 文案 Done int `json:"done,omitempty"` // 进度(如已向量化块数) Total int `json:"total,omitempty"` // 总数 Chunks []string `json:"chunks,omitempty"` // 切块预览(切块阶段发一次) Preview string `json:"preview,omitempty"` // 解析阶段:解析出的文本片段预览 Triples []TripleView `json:"triples,omitempty"` // 抽实体阶段:LLM 抽出的知识三元组(实时浮现 + 喂图谱) Error string `json:"error,omitempty"` } // ModelConfig 是一个模型后端的连接配置(provider 抽象,chat 与 embedding 同形)。 // 开发期指向第三方在线 API(OpenAI 兼容);生产期可换自部署或其它在线模型。 type ModelConfig struct { Provider string `json:"provider"` // openai-compatible / vllm / ... BaseURL string `json:"base_url"` // 如 https://api.deepseek.com APIKey string `json:"api_key,omitempty"` Model string `json:"model"` // 如 deepseek-chat / text-embedding-v3 } // Ready 报告该配置是否足以发起真实推理。 func (m *ModelConfig) Ready() bool { return m != nil && m.BaseURL != "" && m.Model != "" } // Task 是 DSL 解析组装后的可调度任务,在 NATS 上以 JSON 传输。 type Task struct { ID string `json:"id"` Graph json.RawMessage `json:"graph"` // React Flow 导出的 Agent 编排图 Meta map[string]any `json:"meta,omitempty"` } // TaskSubject 返回某任务的发布主题。 func TaskSubject(id string) string { return SubjectTasks + "." + id } // StreamSubject 返回某任务的 Token 回流主题。 func StreamSubject(id string) string { return SubjectStream + "." + id } // ToolSubjectGo / ToolSubjectPy 返回某工具的调用主题。 func ToolSubjectGo(tool string) string { return SubjectToolsGo + "." + tool } func ToolSubjectPy(tool string) string { return SubjectToolsPy + "." + tool } // ToolCall 是 Dispatcher 对一个 MCP 工具的调用请求(NATS request 体)。 type ToolCall struct { Tool string `json:"tool"` // 工具名,如 wiki_search Args map[string]any `json:"args,omitempty"` // 工具参数 TaskID string `json:"task_id,omitempty"` // 触发该调用的任务(便于追踪) } // ToolResult 是 MCP 工具的应答(NATS reply 体)。 type ToolResult struct { OK bool `json:"ok"` Content string `json:"content,omitempty"` // 工具产出(如检索结果文本) Error string `json:"error,omitempty"` // 非空表示工具内部出错 } // Marshal / Unmarshal 便捷方法。 func (t *Task) Marshal() ([]byte, error) { return json.Marshal(t) } func Unmarshal(b []byte) (*Task, error) { var t Task if err := json.Unmarshal(b, &t); err != nil { return nil, err } return &t, nil }