Files
sundynix-agentix/sundynix-shared/contract/task.go
T
Blizzard 8bcb90cdb2 feat(admin): 服务状态面板(基建/服务探活 + MCP 工具注册)+ mcp-go 工具注册表
管理端新增「运维 › 服务状态」:总览横幅 + 摘要数字 + 请求链路拓扑
(客户端→网关→NATS→调度→MCP,按健康三态着色)+ 应用服务卡(含探针
延迟)+ 基建磁贴 + MCP 工具按能力域分组(中文名/作用)。

探活机制(全走 NATS,无 HTTP):
- mcp-go/mcp-py 新增 list_tools 自省工具,能应答=在线 + 上报工具清单
- dispatcher 无端点 → 新增 NATS 心跳主题 sundynix.health.dispatcher
  (ServeHealth 应答 model/ready/uptime),网关用 bus.Ping 探
- 网关 GET /api/v1/admin/status 并发聚合四探针 + 各项延迟

mcp-go 重构:switch → map 注册表(buildRegistry),dispatch 与 list_tools
共用单一事实源,杜绝漂移;每个工具带中文名 + 作用描述。mcp-py 同样补元信息。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:38:10 +08:00

147 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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.<id>
SubjectTasksAll = "sundynix.tasks.>" // stream 捕获的通配
SubjectStream = "sundynix.streams" // Token 回流前缀;实际 sundynix.streams.<id>
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.<tool>
SubjectToolsGoAll = "sundynix.tools.go.>" // mcp-go 通配订阅
SubjectToolsPy = "sundynix.tools.py" // Python 算法型工具前缀;实际 sundynix.tools.py.<tool>
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"
// MetaUserID 是 Task.Meta 中承载已登录用户标识的键(用于偏好记忆召回)。
MetaUserID = "user_id"
// MetaSessionID 是 Task.Meta 中承载会话标识的键(用于短期多轮历史)。
MetaSessionID = "session_id"
// 配置控制面按 kind 寻址:sundynix.config.<kind>.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.<task_id>。
// 与 Token 流(sundynix.streams.<id>)分流:Token 是零拷贝字节,Exec 是结构化节点事件。
const SubjectExec = "sundynix.exec"
// ExecSubject 返回某任务的执行事件回流主题。
func ExecSubject(id string) string { return SubjectExec + "." + id }
// ExecEvent 是一次任务执行中某节点/阶段的生命周期事件(经 sundynix.exec.<id> 回流给 UI
// 用于"运行·观测"的实时轨迹:节点点亮、工具调用入参/产出、各阶段耗时)。
type ExecEvent struct {
Seq int `json:"seq"` // 任务内自增序号(保序)
TS int64 `json:"ts"` // unix 毫秒
Node string `json:"node"` // 稳定节点 idinit / 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.<job_id> 回流给 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
}