Define the core contracts: domain model

Details (original message):
Define the core contracts: domain model, stream events, HITL, profiles, tools, plugins, UI vocabulary, ingest IR, retrieval and memory; golden-sample conformance tests; TS mirror.
This commit is contained in:
weichen
2026-09-29 17:34:58 +00:00
parent f2fcc2f62e
commit 9929d8f8ad
25 changed files with 1585 additions and 2 deletions
+22 -2
View File
@@ -1,5 +1,25 @@
# Agent-System-Core
系统核心契约仓:领域模型、流式事件协议、HITL 卡片协议、插件 API、设置与 UI 扩展词汇、文档解析/检索/记忆契约。Go 模块路径 agent-system-core;同时发布 JSON Schema 与 TS 类型。不含实现。
系统核心契约:领域模型(Project/Conversation/Message/Run/Artifact/Plan)、流式事件信封、
HITL 卡片协议、插件 API 与 manifest、AgentProfile 画像、设置 schema、UI 扩展词汇、
文档解析(ParsedDocument IR)与检索/记忆契约。Go 模块路径 `agent-system-core`。
契约与架构见 Agent-System-Documents 设计方案与架构文档;开发规范见 development-guidelines(Dev 仓 `development/guidelines`)。本仓为 Agent-System 多仓组合的组成部分。
## 三态一致
同一契约以三种形态发布,变更须保持一致:
1. **Go 类型**(`*.go`,camelCase JSON 标签)——后端与代码插件使用。
2. **黄金样本**(`testdata/golden/*.json`)——线格式权威,`go test ./...` 强校验;变更显式重生成(`go test ./... -update`)。
3. **TS 类型**(`ts/types.ts`)——前端镜像,前端测试按黄金样本做形状断言。
## 原则
- 只含契约与纯函数,不含实现、不含 I/O、不依赖生态仓。
- 前向兼容:字段只增不删;消费方对未知 `type`/词汇静默忽略(`v` 字段协商)。
- 摄取 IR(ParsedDocument)是解析器与下游的隔离层;检索/记忆契约先行、实现后置。
## 验证
```bash
go test ./...
```
+179
View File
@@ -0,0 +1,179 @@
package core
import (
"encoding/json"
"flag"
"os"
"path/filepath"
"testing"
)
// 黄金样本一致性:Go 序列化(线格式契约)与 testdata/golden/*.json 逐字节一致。
// ts/types.ts 镜像同一契约;前端测试按样本做形状断言。变更契约须显式更新样本。
var update = flag.Bool("update", false, "rewrite golden files")
func golden(t *testing.T, name string, v any) {
t.Helper()
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
t.Fatal(err)
}
path := filepath.Join("testdata", "golden", name+".json")
if *update {
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, append(data, '\n'), 0o644); err != nil {
t.Fatal(err)
}
return
}
want, err := os.ReadFile(path)
if err != nil {
t.Fatalf("golden missing (run go test ./... -update): %v", err)
}
if string(want) != string(append(data, '\n')) {
t.Errorf("%s: golden mismatch:\n got: %s\nwant: %s", name, data, want)
}
}
func sampleMessage() Message {
return Message{
ID: "msg_abc123def456", ConversationID: "cnv_abc123def456", RunID: "run_abc123def456",
Role: RoleAssistant,
Parts: []Part{
{Type: PartThinking, Text: "考虑先检索…", Process: true},
{Type: PartToolCall, Process: true, ToolCall: &ToolCallPart{ID: "call_1", Name: "history_search", Arguments: `{"q":"部署"}`, Duration: 12.5}},
{Type: PartToolResult, Process: true, ToolResult: &ToolResultPart{CallID: "call_1", Status: "ok", Summary: "3 条命中"}},
{Type: PartText, Text: "根据历史记录……"},
{Type: PartArtifactRef, Artifact: &ArtifactRef{ArtifactID: "art_abc123def456", Name: "报告.md", Kind: "document"}},
},
}
}
func sampleRun() Run {
return Run{
ID: "run_abc123def456", ConversationID: "cnv_abc123def456", ProjectID: "prj_abc123def456",
Kind: RunConversation, ProfileID: "default", Status: RunCompleted, Phase: PhaseDone,
Usage: &Usage{PromptTokens: 1200, CompletionTokens: 300, TotalTokens: 1500, DurationMs: 4200.5, ToolCalls: 2},
}
}
func TestGoldenDomain(t *testing.T) {
golden(t, "message", sampleMessage())
golden(t, "run", sampleRun())
golden(t, "project", Project{ID: "prj_abc123def456", Name: "示例项目"})
golden(t, "conversation", Conversation{ID: "cnv_abc123def456", ProjectID: "prj_abc123def456", Title: "新会话"})
golden(t, "artifact", Artifact{ID: "art_abc123def456", ProjectID: "prj_abc123def456", Name: "报告.md", Kind: "document", MIME: "text/markdown", Size: 128})
}
func TestGoldenEvents(t *testing.T) {
golden(t, "event_message_delta", StreamEvent{Seq: 42, RunID: "run_1", ConversationID: "cnv_1", TS: "2026-09-29T10:00:00Z", V: 1, Type: EvMessageDelta, Payload: json.RawMessage(`{"index":0,"text":"你"}`)})
golden(t, "event_agent_spawn", StreamEvent{Seq: 43, RunID: "run_1", ConversationID: "cnv_1", TS: "2026-09-29T10:00:01Z", V: 1, Type: EvAgentSpawn, Payload: json.RawMessage(`{"agentRun":"run_sub1","name":"检索员","task":"查法规"}`)})
golden(t, "event_run_completed", StreamEvent{Seq: 44, RunID: "run_1", ConversationID: "cnv_1", TS: "2026-09-29T10:00:02Z", V: 1, Type: EvRunCompleted, Payload: json.RawMessage(`{"usage":{"promptTokens":100,"completionTokens":20,"totalTokens":120,"durationMs":1500,"toolCalls":1}}`)})
}
func TestGoldenHITL(t *testing.T) {
golden(t, "hitl_card", HITLCard{
ID: "hitl_abc123def456", RunID: "run_1", ConversationID: "cnv_1",
Question: "采用哪种输出格式?", Kind: HITLKindChoice,
Choices: []HITLChoice{
{ID: "md", Label: "Markdown 文档", Description: "可版本管理的轻量文档", Impact: "写入 artifacts,可在右栏查看", Recommended: true},
{ID: "docx", Label: "Word 文档", Description: "正式交付格式", Impact: "生成 docx 工件"},
},
AllowFreeInput: true, FreeInputHint: "也可以直接输入其他格式要求",
})
}
func TestGoldenProfileAndTools(t *testing.T) {
maxT := 40
golden(t, "profile", AgentProfile{
ID: "default", Name: "通用助手", Builtin: true, Source: "builtin",
SystemPrompt: "你是一个通用智能体助手。",
Model: ModelRoute{Chat: "gpt-default", Lightweight: "gpt-lite"},
Tools: ToolPolicy{Allow: []string{"*"}, Deny: []string{"http.admin."}, Confirm: []string{"http."}},
Context: ContextPolicy{MaxHistoryMessages: 40, MaxToolResultChars: 20000},
HITL: HITLPolicy{AskOnAmbiguity: true},
Budget: RunBudget{MaxToolCalls: &maxT},
})
golden(t, "tool_http", ToolDesc{
Name: "http_translate", Description: "调用翻译 API", Source: SourceUser,
Parameters: json.RawMessage(`{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]}`),
HTTP: &HTTPToolSpec{Method: "POST", URL: "https://api.example.com/translate", BodyTemplate: `{"q":"{{param.text}}"}`, TimeoutMs: 10000, ResultPath: "data.0"},
})
}
func TestGoldenIngest(t *testing.T) {
golden(t, "parsed_document", ParsedDocument{
Meta: DocMeta{Filename: "报告.docx", MIME: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", Parser: "builtin:docx", Confidence: 0.92},
Blocks: []DocBlock{
{Type: BlockHeading, Level: 1, Text: "概述"},
{Type: BlockPara, Text: "本文档说明……"},
{Type: BlockList, Items: []string{"要点一", "要点二"}},
{Type: BlockTable, Rows: [][]string{{"项", "值"}, {"版本", "1.0"}}},
},
})
}
func TestGoldenPlugin(t *testing.T) {
golden(t, "plugin_manifest", PluginManifest{
ID: "example", Name: "示例插件", Version: "0.1.0", APIVersion: PluginAPIVersion,
Requires: PluginRequires{Network: []string{"api.example.com"}},
Provides: PluginProvides{Tools: []string{"http_echo"}, Intents: []string{"demo"}, Panels: []string{"demo"}, Cards: []string{"note"}, Settings: []string{"greeting"}},
})
}
// ---- 纯函数行为 ----
func TestToolPolicy(t *testing.T) {
p := ToolPolicy{Allow: []string{"*"}, Deny: []string{"http.admin."}, Confirm: []string{"http."}}
if !p.ToolAllowed("history_search") {
t.Error("allow * should permit builtin tools")
}
if p.ToolAllowed("http.admin.reset") {
t.Error("deny prefix should win over allow *")
}
if !p.ToolAllowed("http.fetch") || !p.NeedsConfirm("http.fetch") {
t.Error("http prefix tools allowed but need confirm")
}
if p.NeedsConfirm("history_search") {
t.Error("builtin tool should not need confirm")
}
}
func TestPlainText(t *testing.T) {
d := ParsedDocument{Blocks: []DocBlock{
{Type: BlockHeading, Level: 2, Text: "标题"},
{Type: BlockPara, Text: "段落"},
{Type: BlockTable, Rows: [][]string{{"a", "b"}}},
}}
got := d.PlainText()
for _, want := range []string{"## 标题", "段落", "a\tb"} {
if !contains(got, want) {
t.Errorf("PlainText missing %q in %q", want, got)
}
}
}
func TestSettingsMergeAndPath(t *testing.T) {
base := map[string]any{"models": map[string]any{"chat": "a", "temperature": 0.7}, "ui": map[string]any{"theme": "light"}}
over := map[string]any{"models": map[string]any{"chat": "b"}, "ui": map[string]any{"theme": "dark"}}
eff := EffectiveSettings(MergeSettings(base, over))
if eff.GetString("models.chat", "") != "b" || eff.GetNumber("models.temperature", 0) != 0.7 {
t.Error("deep merge failed")
}
if eff.GetString("models.missing", "def") != "def" {
t.Error("missing path should return default")
}
}
func contains(s, sub string) bool {
return len(s) >= len(sub) && (func() bool {
for i := 0; i+len(sub) <= len(s); i++ {
if s[i:i+len(sub)] == sub {
return true
}
}
return false
})()
}
+146
View File
@@ -0,0 +1,146 @@
package core
import "time"
// ---- 文档摄取与解析(4.11)----
// ParsedDocument 是解析中间表示:所有下游(附件上下文/工件预览/检索索引/全文检索)
// 只消费 IR,不关心解析器——解析器可替换、可组合、可交叉验证。
type ParsedDocument struct {
Meta DocMeta `json:"meta"`
Blocks []DocBlock `json:"blocks"`
Warnings []string `json:"warnings,omitempty"`
}
type DocMeta struct {
Filename string `json:"filename"`
MIME string `json:"mime,omitempty"`
Parser string `json:"parser"` // builtin:md / builtin:docx / mineru / agent
Confidence float64 `json:"confidence,omitempty"` // 0–1;交叉验证差异块拉低
CrossCheck *CrossCheck `json:"crossCheck,omitempty"`
ParsedAt time.Time `json:"parsedAt"`
}
type CrossCheck struct {
With string `json:"with"` // 交叉验证的另一解析器
DiffBlocks int `json:"diffBlocks"`
Verdict string `json:"verdict"` // agree | differ
}
type DocBlockType string
const (
BlockHeading DocBlockType = "heading"
BlockPara DocBlockType = "para"
BlockList DocBlockType = "list"
BlockTable DocBlockType = "table"
BlockCode DocBlockType = "code"
BlockImageRef DocBlockType = "image-ref"
)
type DocBlock struct {
Type DocBlockType `json:"type"`
Level int `json:"level,omitempty"` // heading
Text string `json:"text,omitempty"` // para/heading/list 项拼接/code
Items []string `json:"items,omitempty"` // list
Rows [][]string `json:"rows,omitempty"` // table(首行为表头)
Ref string `json:"ref,omitempty"` // image-ref 的对象键
Confidence float64 `json:"confidence,omitempty"`
}
// PlainText 把文档压成纯文本(附件注入与全文索引用);表格按行拼接。
func (d ParsedDocument) PlainText() string {
out := make([]byte, 0, 1024)
for _, b := range d.Blocks {
switch b.Type {
case BlockHeading:
out = append(out, '\n')
for i := 0; i < b.Level; i++ {
out = append(out, '#')
}
out = append(out, ' ')
out = append(out, b.Text...)
out = append(out, '\n')
case BlockPara, BlockCode:
out = append(out, b.Text...)
out = append(out, '\n')
case BlockList:
for _, it := range b.Items {
out = append(out, "- "...)
out = append(out, it...)
out = append(out, '\n')
}
case BlockTable:
for _, row := range b.Rows {
for i, cell := range row {
if i > 0 {
out = append(out, '\t')
}
out = append(out, cell...)
}
out = append(out, '\n')
}
}
out = append(out, '\n')
}
return string(out)
}
// ParserDesc 注册表条目(来源标记:builtin/plugin/...;一切可禁用)。
type ParserDesc struct {
ID string `json:"id"`
Formats []string `json:"formats"` // 支持的扩展名
Source SourceKind `json:"source"`
Note string `json:"note,omitempty"`
}
// ---- 检索(4.12)----
// KnowledgeDoc 是检索语料的一条文档(来自 ingest 或插件知识库,带来源标记)。
type KnowledgeDoc struct {
ID ID `json:"id"`
ProjectID ID `json:"projectId,omitempty"` // 空 = 插件级知识库
Source string `json:"source"` // upload | artifact | plugin:<id>
Title string `json:"title"`
Ref string `json:"ref,omitempty"` // 工件/文件引用
Chunk string `json:"chunk"`
CreatedAt time.Time `json:"createdAt"`
}
type RetrievalHit struct {
Doc KnowledgeDoc `json:"doc"`
Score float64 `json:"score"`
Kind string `json:"kind"` // fulltext | vector | plugin
}
type RetrievalQuery struct {
ProjectID ID `json:"projectId,omitempty"`
Text string `json:"text"`
TopK int `json:"topK,omitempty"`
Sources []string `json:"sources,omitempty"` // 过滤来源
}
// ---- 记忆(4.13 契约;实现远期)----
type MemoryEntry struct {
ID string `json:"id"`
Scope string `json:"scope"` // user | project
ProjectID ID `json:"projectId,omitempty"`
Type string `json:"type"` // fact | preference | decision | episode | procedure
Content string `json:"content"`
Source string `json:"source"` // run:<id> | manual
Confidence float64 `json:"confidence,omitempty"`
CreatedAt time.Time `json:"createdAt"`
LastAccessed time.Time `json:"lastAccessed"`
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
}
// ---- 上传文件 ----
type UploadInfo struct {
ArtifactID ID `json:"artifactId"`
Filename string `json:"filename"`
MIME string `json:"mime,omitempty"`
Size int64 `json:"size"`
Parsed *ParsedDocument `json:"parsed,omitempty"`
}
+122
View File
@@ -0,0 +1,122 @@
package core
import "encoding/json"
// 事件信封:后端 → 前端的唯一增量通道(SSE),持久化后转发,Seq 在会话内单调。
// 消费方对未知 Type 静默忽略(原则 6 前向兼容)。
type StreamEvent struct {
Seq int64 `json:"seq"`
RunID ID `json:"runId,omitempty"`
ConversationID ID `json:"conversationId"`
TS string `json:"ts"` // RFC3339
V int `json:"v"`
Type StreamEventType `json:"type"`
Payload json.RawMessage `json:"payload,omitempty"`
}
type StreamEventType string
const (
EvMessageDelta StreamEventType = "message.delta"
EvThinkingDelta StreamEventType = "thinking.delta"
EvToolCall StreamEventType = "tool.call"
EvToolResult StreamEventType = "tool.result"
EvRunPhase StreamEventType = "run.phase"
EvHITLRequest StreamEventType = "hitl.request"
EvHITLResolved StreamEventType = "hitl.resolved"
EvPlanUpdate StreamEventType = "plan.update"
EvTodoUpdate StreamEventType = "todo.update"
EvAgentSpawn StreamEventType = "agent.spawn"
EvAgentProgress StreamEventType = "agent.progress"
EvAgentResult StreamEventType = "agent.result"
EvArtifactCreated StreamEventType = "artifact.created"
EvArtifactUpdated StreamEventType = "artifact.updated"
EvRunCompleted StreamEventType = "run.completed"
EvRunFailed StreamEventType = "run.failed"
EvError StreamEventType = "error"
)
// ProtocolVersion 是事件信封与 payload 词汇的当前版本。
const ProtocolVersion = 1
// ---- 各事件 payload(新增字段只增不删;消费方忽略未知字段)----
// MessageDeltaPayload:正文增量。Index 标识目标 Part 序号(流式聚合用)。
type MessageDeltaPayload struct {
Index int `json:"index"`
Text string `json:"text"`
}
type ThinkingDeltaPayload struct {
Index int `json:"index"`
Text string `json:"text"`
}
type ToolCallPayload struct {
CallID string `json:"callId"`
Name string `json:"name"`
Arguments string `json:"arguments,omitempty"`
}
type ToolResultPayload struct {
CallID string `json:"callId"`
Status string `json:"status"`
Summary string `json:"summary,omitempty"`
Truncated bool `json:"truncated,omitempty"`
}
type RunPhasePayload struct {
Phase RunPhase `json:"phase"`
}
type HITLResolvedPayload struct {
CardID ID `json:"cardId"`
Answer string `json:"answer,omitempty"`
Kind string `json:"kind"` // choice | free | timeout | cancelled
}
type PlanUpdatePayload struct {
Plan PlanSnapshot `json:"plan"`
}
type TodoUpdatePayload struct {
List TodoList `json:"list"`
}
// AgentSpawnPayload:子代理派生;AgentRun 即子 Run(父子关系即任务树)。
type AgentSpawnPayload struct {
AgentRun ID `json:"agentRun"`
Name string `json:"name"`
Task string `json:"task"`
}
type AgentProgressPayload struct {
AgentRun ID `json:"agentRun"`
Note string `json:"note,omitempty"`
Phase RunPhase `json:"phase,omitempty"`
}
type AgentResultPayload struct {
AgentRun ID `json:"agentRun"`
Status RunStatus `json:"status"`
Summary string `json:"summary,omitempty"`
}
type ArtifactPayload struct {
Artifact Artifact `json:"artifact"`
}
type RunCompletedPayload struct {
Usage Usage `json:"usage"`
}
type RunFailedPayload struct {
Code string `json:"code"`
Message string `json:"message"`
}
type ErrorPayload struct {
Code string `json:"code"`
Message string `json:"message"`
RunID ID `json:"runId,omitempty"`
}
+3
View File
@@ -0,0 +1,3 @@
module agent-system-core
go 1.27
+45
View File
@@ -0,0 +1,45 @@
package core
import "encoding/json"
// HITL 卡片协议(4.5):choice | input | confirm | form 四种;
// 发起方必须给 Choices 提供 description 与 impact——用户在决策点需要上下文。
type HITLCard struct {
ID ID `json:"id"`
RunID ID `json:"runId"`
ConversationID ID `json:"conversationId"`
Question string `json:"question"`
Kind HITLKind `json:"kind"`
Choices []HITLChoice `json:"choices,omitempty"`
AllowFreeInput bool `json:"allowFreeInput"`
FreeInputHint string `json:"freeInputHint,omitempty"`
TimeoutSec *int `json:"timeoutSec,omitempty"`
Form []UINode `json:"form,omitempty"` // form kind 的字段(UI 词汇)
CreatedAt string `json:"createdAt"`
Resolved bool `json:"resolved"`
}
type HITLKind string
const (
HITLKindChoice HITLKind = "choice"
HITLKindInput HITLKind = "input"
HITLKindConfirm HITLKind = "confirm"
HITLKindForm HITLKind = "form"
)
type HITLChoice struct {
ID string `json:"id"`
Label string `json:"label"`
Description string `json:"description"`
Impact string `json:"impact"` // 该选择可能造成的影响(必填)
Recommended bool `json:"recommended,omitempty"`
}
// HITLResponse 是用户对卡片的响应;自由输入时 ChoiceID 为空。
type HITLResponse struct {
CardID ID `json:"cardId"`
ChoiceID string `json:"choiceId,omitempty"`
FreeText string `json:"freeText,omitempty"`
FormData json.RawMessage `json:"formData,omitempty"`
}
+217
View File
@@ -0,0 +1,217 @@
// Package core 定义 Agent-System 的核心契约:领域模型、流式事件、HITL 卡片、
// 插件 API、设置与 UI 词汇。本仓只含契约与少量纯函数,不含实现、不含 I/O。
//
// JSON 标签(camelCase)是前后端与插件共同的线格式契约;TS 类型镜像见 ts/types.ts,
// 黄金样本见 testdata/golden/(conformance 测试保证 Go 序列化与样本一致)。
package core
import "time"
// ---- 标识 ----
// ID 形态为 <kind>_<hex12>;生成由后端负责,契约只约束非空与唯一。
type ID = string
// ---- 项目与会话 ----
type Project struct {
ID ID `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Settings map[string]any `json:"settings,omitempty"` // 项目层设置覆盖(4.9 覆盖链)
}
type Conversation struct {
ID ID `json:"id"`
ProjectID ID `json:"projectId"`
Title string `json:"title"`
ProfileID ID `json:"profileId,omitempty"` // 空为默认画像
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// ---- 消息与部件 ----
type Role string
const (
RoleUser Role = "user"
RoleAssistant Role = "assistant"
RoleSystem Role = "system"
)
// PartType 区分消息成分;前端按类型查渲染器,未知类型静默忽略(原则 6)。
type PartType string
const (
PartText PartType = "text"
PartThinking PartType = "thinking"
PartToolCall PartType = "tool_call"
PartToolResult PartType = "tool_result"
PartArtifactRef PartType = "artifact_ref"
PartHITL PartType = "hitl"
PartPlan PartType = "plan"
PartError PartType = "error"
PartCard PartType = "card" // 插件自定义卡片(Data 为 UI schema)
)
type Message struct {
ID ID `json:"id"`
ConversationID ID `json:"conversationId"`
RunID ID `json:"runId,omitempty"`
Role Role `json:"role"`
Parts []Part `json:"parts"`
CreatedAt time.Time `json:"createdAt"`
}
// Part 是判别联合:Type 决定哪些字段有意义;未用字段为零值。
type Part struct {
Type PartType `json:"type"`
Text string `json:"text,omitempty"` // text / thinking(流式增量聚合后)
ToolCall *ToolCallPart `json:"toolCall,omitempty"`
ToolResult *ToolResultPart `json:"toolResult,omitempty"`
Artifact *ArtifactRef `json:"artifact,omitempty"`
Hitl *HITLSummary `json:"hitl,omitempty"`
Plan *PlanSnapshot `json:"plan,omitempty"`
Card *CardPart `json:"card,omitempty"`
Err *ErrorPart `json:"error,omitempty"`
// 过程标记:true 的部件在 run 终态后由前端折叠为摘要行(4.2)。
Process bool `json:"process,omitempty"`
}
type ToolCallPart struct {
ID string `json:"id"`
Name string `json:"name"`
Arguments string `json:"arguments,omitempty"` // 原始 JSON
Duration float64 `json:"durationMs,omitempty"`
}
type ToolResultPart struct {
CallID string `json:"callId"`
Status string `json:"status"` // ok | error
Summary string `json:"summary,omitempty"`
Truncated bool `json:"truncated,omitempty"` // 超长截断并转工件引用(4.10)
}
type ArtifactRef struct {
ArtifactID ID `json:"artifactId"`
Name string `json:"name"`
Kind string `json:"kind,omitempty"` // document | image | data | ...
}
type HITLSummary struct {
CardID ID `json:"cardId"`
Question string `json:"question"`
Resolved bool `json:"resolved"`
Answer string `json:"answer,omitempty"` // 选中项 label 或回填文本
}
type ErrorPart struct {
Code string `json:"code"`
Message string `json:"message"`
}
// CardPart.Data 是 UI schema 节点(4.6 有限词汇)。
type CardPart struct {
Kind string `json:"kind"`
Data []UINode `json:"data"`
}
// ---- Run(一切运行皆 Run)----
type RunKind string
const (
RunConversation RunKind = "conversation"
RunSubagent RunKind = "subagent"
RunTool RunKind = "tool"
)
type RunStatus string
const (
RunPending RunStatus = "pending"
RunRunning RunStatus = "running"
RunWaitingHITL RunStatus = "waiting_hitl"
RunCompleted RunStatus = "completed"
RunFailed RunStatus = "failed"
RunCancelled RunStatus = "cancelled"
RunInterrupted RunStatus = "interrupted"
)
type RunPhase string
const (
PhaseThinking RunPhase = "thinking"
PhaseActing RunPhase = "acting"
PhaseResponding RunPhase = "responding"
PhaseDone RunPhase = "done"
)
type Run struct {
ID ID `json:"id"`
ParentRunID ID `json:"parentRunId,omitempty"`
ConversationID ID `json:"conversationId"`
ProjectID ID `json:"projectId"`
Kind RunKind `json:"kind"`
ProfileID ID `json:"profileId,omitempty"`
Status RunStatus `json:"status"`
Phase RunPhase `json:"phase,omitempty"`
AgentName string `json:"agentName,omitempty"` // subagent 显示名
CreatedAt time.Time `json:"createdAt"`
EndedAt *time.Time `json:"endedAt,omitempty"`
Usage *Usage `json:"usage,omitempty"`
Error *ErrorPart `json:"error,omitempty"`
}
type Usage struct {
PromptTokens int `json:"promptTokens"`
CompletionTokens int `json:"completionTokens"`
TotalTokens int `json:"totalTokens"`
DurationMs float64 `json:"durationMs"`
ToolCalls int `json:"toolCalls"`
}
// ---- 工件 ----
type Artifact struct {
ID ID `json:"id"`
ProjectID ID `json:"projectId"`
Name string `json:"name"`
Kind string `json:"kind"`
MIME string `json:"mime,omitempty"`
Size int64 `json:"size"`
SourceRun ID `json:"sourceRun,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// ---- 计划与待办(右栏 Plan / Todos)----
type PlanSnapshot struct {
PlanID ID `json:"planId"`
Title string `json:"title"`
Steps []PlanStep `json:"steps"`
}
type PlanStep struct {
ID string `json:"id"`
Text string `json:"text"`
Status string `json:"status"` // pending | in_progress | done | skipped
AgentRun ID `json:"agentRun,omitempty"`
}
type TodoList struct {
ConversationID ID `json:"conversationId"`
Items []TodoItem `json:"items"`
}
type TodoItem struct {
ID string `json:"id"`
Text string `json:"text"`
Status string `json:"status"` // pending | in_progress | done
}
+69
View File
@@ -0,0 +1,69 @@
package core
import "encoding/json"
// 插件 manifest(4.6):插件 = manifest + 声明资产 + 可选代码。
// 校验失败整批拒绝(fail-closed);卸载注销注册项,在途引用按定义缺失稳定报错。
const PluginAPIVersion = "1.0"
type PluginManifest struct {
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
APIVersion string `json:"apiVersion"`
Requires PluginRequires `json:"requires,omitempty"`
Provides PluginProvides `json:"provides"`
}
type PluginRequires struct {
Network []string `json:"network,omitempty"` // 允许访问的域名(展示用,v1 自装自审)
}
type PluginProvides struct {
Tools []string `json:"tools,omitempty"` // tools/ 目录下的资产名
Workflows []string `json:"workflows,omitempty"`
Intents []string `json:"intents,omitempty"`
Prompts []string `json:"prompts,omitempty"`
Panels []string `json:"panels,omitempty"`
Cards []string `json:"cards,omitempty"`
Settings []string `json:"settings,omitempty"`
}
// PluginPanel:右栏面板声明(UI schema 静态骨架 + 可选数据提供者)。
type PluginPanel struct {
ID string `json:"id"`
Title string `json:"title"`
Icon string `json:"icon,omitempty"`
Body []UINode `json:"body"`
// Tool 绑定:面板挂载时调用该工具刷新 Body 中的动态区(v1 简化为整体刷新)。
RefreshTool string `json:"refreshTool,omitempty"`
}
// WorkflowDef:工作流即数据(4.6)。Step 可产 HITL 卡片、调工具、产出工件。
type WorkflowDef struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
IntentID string `json:"intentId,omitempty"` // 绑定意图(命中即启动)
Steps []WorkflowStep `json:"steps"`
}
type WorkflowStep struct {
ID string `json:"id"`
Title string `json:"title"`
Kind string `json:"kind"` // prompt | tool | hitl | artifact
Prompt string `json:"prompt,omitempty"` // prompt: 发给模型的指令模板
Tool string `json:"tool,omitempty"` // tool: 工具名
Args json.RawMessage `json:"args,omitempty"` // tool: 参数模板
Hitl *HITLCardTemplate `json:"hitl,omitempty"` // hitl: 卡片模板
Artifact string `json:"artifact,omitempty"` // artifact: 产出工件名模板
}
type HITLCardTemplate struct {
Question string `json:"question"`
Kind HITLKind `json:"kind"`
Choices []HITLChoice `json:"choices,omitempty"`
AllowFreeInput bool `json:"allowFreeInput"`
FreeInputHint string `json:"freeInputHint,omitempty"`
}
+205
View File
@@ -0,0 +1,205 @@
package core
import "encoding/json"
// 智能体画像(4.7):核心定制机制,全部数据化、可导入导出。
type AgentProfile struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Builtin bool `json:"builtin,omitempty"`
Source string `json:"source,omitempty"` // builtin | plugin | user
SystemPrompt string `json:"systemPrompt"`
Model ModelRoute `json:"model"`
Tools ToolPolicy `json:"tools"`
Context ContextPolicy `json:"context"`
HITL HITLPolicy `json:"hitl"`
Budget RunBudget `json:"budget"`
}
// ModelRoute 按用途选择模型(4.7 kind 路由):空串回落到 chat 默认。
type ModelRoute struct {
Chat string `json:"chat,omitempty"`
Lightweight string `json:"lightweight,omitempty"`
Embedding string `json:"embedding,omitempty"`
Rerank string `json:"rerank,omitempty"`
}
// ToolPolicy 三清单:allow/deny 决定可见工具,confirm 强制执行前 HITL 确认。
// 匹配语义:精确名或前缀通配("http.*" 表示 http 前缀);deny 优先于 allow。
type ToolPolicy struct {
Allow []string `json:"allow"` // ["*"] 全部
Deny []string `json:"deny,omitempty"`
Confirm []string `json:"confirm,omitempty"`
}
// ContextPolicy(4.10):ctxasm 的输入;逐版本增加策略项不动组装器接口。
type ContextPolicy struct {
MaxHistoryMessages int `json:"maxHistoryMessages"` // 0 = 默认 40
SummarizeBeyond bool `json:"summarizeBeyond,omitempty"` // 远期;先存标志
MemoryBudgetPct int `json:"memoryBudgetPct,omitempty"` // 注入槽份额上限
KnowledgeBudgetPct int `json:"knowledgeBudgetPct,omitempty"`
MaxToolResultChars int `json:"maxToolResultChars,omitempty"` // 0 = 默认 20000
}
type HITLPolicy struct {
AskOnAmbiguity bool `json:"askOnAmbiguity"`
TimeoutSec *int `json:"timeoutSec,omitempty"`
}
type RunBudget struct {
MaxTokens *int `json:"maxTokens,omitempty"`
MaxToolCalls *int `json:"maxToolCalls,omitempty"`
MaxTurns *int `json:"maxTurns,omitempty"` // LLM 循环轮上限
}
// ToolAllows 判定:deny 优先;allow 含 "*" 或精确/前缀命中。
func (p ToolPolicy) ToolAllowed(name string) bool {
if matchAny(p.Deny, name) {
return false
}
if len(p.Allow) == 0 {
return true
}
return matchAny(p.Allow, name)
}
func (p ToolPolicy) NeedsConfirm(name string) bool {
return matchAny(p.Confirm, name)
}
// matchAny 语义:pattern 精确等于 name,或以 "xxx." 开头表示前缀族(http. → http.*)。
func matchAny(patterns []string, name string) bool {
for _, p := range patterns {
if p == "*" || p == name {
return true
}
if n := len(p); n > 1 && p[n-1] == '.' {
if len(name) > n-1 && name[:n-1] == p[:n-1] {
return true
}
}
}
return false
}
// ---- 设置(4.9)----
type SettingType string
const (
SettingString SettingType = "string"
SettingNumber SettingType = "number"
SettingBool SettingType = "boolean"
SettingEnum SettingType = "enum"
SettingSecret SettingType = "secret"
)
type SettingEffect string
const (
EffectHot SettingEffect = "hot" // 立即生效
EffectNewRun SettingEffect = "new-run" // 仅影响新 run
EffectRestart SettingEffect = "restart" // 需重启
)
// SettingDesc 描述一个设置项;设置页按此渲染并标注生效时机(schema 驱动)。
type SettingDesc struct {
Key string `json:"key"`
Group string `json:"group"` // 分区:models | profiles | tools | knowledge | ui | plugin:<id>
Label string `json:"label"`
Description string `json:"description,omitempty"`
Type SettingType `json:"type"`
Default any `json:"default,omitempty"`
Options []string `json:"options,omitempty"` // enum
Effect SettingEffect `json:"effect"`
Secret bool `json:"secret,omitempty"`
}
// EffectiveSettings 是覆盖链(system → user → project → conversation)深合并结果。
type EffectiveSettings map[string]any
// MergeSettings 深合并:后者覆盖前者;map 递归合并,其它类型直接覆盖(4.9 数组显式覆盖)。
func MergeSettings(base, override map[string]any) map[string]any {
out := make(map[string]any, len(base)+len(override))
for k, v := range base {
out[k] = v
}
for k, v := range override {
if bm, ok := base[k].(map[string]any); ok {
if vm, ok2 := v.(map[string]any); ok2 {
out[k] = MergeSettings(bm, vm)
continue
}
}
out[k] = v
}
return out
}
// GetString/GetBool/GetNumber 读取点路径("models.chat");缺省返回 def。
func (s EffectiveSettings) GetString(path, def string) string {
v, ok := lookupPath(s, path)
if !ok {
return def
}
if str, ok2 := v.(string); ok2 {
return str
}
return def
}
func (s EffectiveSettings) GetBool(path string, def bool) bool {
v, ok := lookupPath(s, path)
if !ok {
return def
}
if b, ok2 := v.(bool); ok2 {
return b
}
return def
}
func (s EffectiveSettings) GetNumber(path string, def float64) float64 {
v, ok := lookupPath(s, path)
if !ok {
return def
}
if f, ok2 := v.(float64); ok2 {
return f
}
return def
}
func lookupPath(m map[string]any, path string) (any, bool) {
parts := splitPath(path)
var cur any = m
for _, p := range parts {
cm, ok := cur.(map[string]any)
if !ok {
return nil, false
}
cur, ok = cm[p]
if !ok {
return nil, false
}
}
return cur, true
}
func splitPath(path string) []string {
var out []string
cur := ""
for i := 0; i < len(path); i++ {
switch path[i] {
case '.':
out = append(out, cur)
cur = ""
default:
cur += string(path[i])
}
}
return append(out, cur)
}
var _ = json.Marshal // 保持 encoding/json 引用(RawMessage 一致性)
+10
View File
@@ -0,0 +1,10 @@
{
"id": "art_abc123def456",
"projectId": "prj_abc123def456",
"name": "报告.md",
"kind": "document",
"mime": "text/markdown",
"size": 128,
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z"
}
+7
View File
@@ -0,0 +1,7 @@
{
"id": "cnv_abc123def456",
"projectId": "prj_abc123def456",
"title": "新会话",
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z"
}
+13
View File
@@ -0,0 +1,13 @@
{
"seq": 43,
"runId": "run_1",
"conversationId": "cnv_1",
"ts": "2026-09-29T10:00:01Z",
"v": 1,
"type": "agent.spawn",
"payload": {
"agentRun": "run_sub1",
"name": "检索员",
"task": "查法规"
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"seq": 42,
"runId": "run_1",
"conversationId": "cnv_1",
"ts": "2026-09-29T10:00:00Z",
"v": 1,
"type": "message.delta",
"payload": {
"index": 0,
"text": "你"
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"seq": 44,
"runId": "run_1",
"conversationId": "cnv_1",
"ts": "2026-09-29T10:00:02Z",
"v": 1,
"type": "run.completed",
"payload": {
"usage": {
"promptTokens": 100,
"completionTokens": 20,
"totalTokens": 120,
"durationMs": 1500,
"toolCalls": 1
}
}
}
+26
View File
@@ -0,0 +1,26 @@
{
"id": "hitl_abc123def456",
"runId": "run_1",
"conversationId": "cnv_1",
"question": "采用哪种输出格式?",
"kind": "choice",
"choices": [
{
"id": "md",
"label": "Markdown 文档",
"description": "可版本管理的轻量文档",
"impact": "写入 artifacts,可在右栏查看",
"recommended": true
},
{
"id": "docx",
"label": "Word 文档",
"description": "正式交付格式",
"impact": "生成 docx 工件"
}
],
"allowFreeInput": true,
"freeInputHint": "也可以直接输入其他格式要求",
"createdAt": "",
"resolved": false
}
+45
View File
@@ -0,0 +1,45 @@
{
"id": "msg_abc123def456",
"conversationId": "cnv_abc123def456",
"runId": "run_abc123def456",
"role": "assistant",
"parts": [
{
"type": "thinking",
"text": "考虑先检索…",
"process": true
},
{
"type": "tool_call",
"toolCall": {
"id": "call_1",
"name": "history_search",
"arguments": "{\"q\":\"部署\"}",
"durationMs": 12.5
},
"process": true
},
{
"type": "tool_result",
"toolResult": {
"callId": "call_1",
"status": "ok",
"summary": "3 条命中"
},
"process": true
},
{
"type": "text",
"text": "根据历史记录……"
},
{
"type": "artifact_ref",
"artifact": {
"artifactId": "art_abc123def456",
"name": "报告.md",
"kind": "document"
}
}
],
"createdAt": "0001-01-01T00:00:00Z"
}
+40
View File
@@ -0,0 +1,40 @@
{
"meta": {
"filename": "报告.docx",
"mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"parser": "builtin:docx",
"confidence": 0.92,
"parsedAt": "0001-01-01T00:00:00Z"
},
"blocks": [
{
"type": "heading",
"level": 1,
"text": "概述"
},
{
"type": "para",
"text": "本文档说明……"
},
{
"type": "list",
"items": [
"要点一",
"要点二"
]
},
{
"type": "table",
"rows": [
[
"项",
"值"
],
[
"版本",
"1.0"
]
]
}
]
}
+28
View File
@@ -0,0 +1,28 @@
{
"id": "example",
"name": "示例插件",
"version": "0.1.0",
"apiVersion": "1.0",
"requires": {
"network": [
"api.example.com"
]
},
"provides": {
"tools": [
"http_echo"
],
"intents": [
"demo"
],
"panels": [
"demo"
],
"cards": [
"note"
],
"settings": [
"greeting"
]
}
}
+32
View File
@@ -0,0 +1,32 @@
{
"id": "default",
"name": "通用助手",
"builtin": true,
"source": "builtin",
"systemPrompt": "你是一个通用智能体助手。",
"model": {
"chat": "gpt-default",
"lightweight": "gpt-lite"
},
"tools": {
"allow": [
"*"
],
"deny": [
"http.admin."
],
"confirm": [
"http."
]
},
"context": {
"maxHistoryMessages": 40,
"maxToolResultChars": 20000
},
"hitl": {
"askOnAmbiguity": true
},
"budget": {
"maxToolCalls": 40
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"id": "prj_abc123def456",
"name": "示例项目",
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z"
}
+17
View File
@@ -0,0 +1,17 @@
{
"id": "run_abc123def456",
"conversationId": "cnv_abc123def456",
"projectId": "prj_abc123def456",
"kind": "conversation",
"profileId": "default",
"status": "completed",
"phase": "done",
"createdAt": "0001-01-01T00:00:00Z",
"usage": {
"promptTokens": 1200,
"completionTokens": 300,
"totalTokens": 1500,
"durationMs": 4200.5,
"toolCalls": 2
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"name": "http_translate",
"description": "调用翻译 API",
"parameters": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": [
"text"
]
},
"source": "user",
"http": {
"method": "POST",
"url": "https://api.example.com/translate",
"bodyTemplate": "{\"q\":\"{{param.text}}\"}",
"timeoutMs": 10000,
"resultPath": "data.0"
}
}
+49
View File
@@ -0,0 +1,49 @@
package core
import "encoding/json"
// 工具契约(4.8):内置、声明式 HTTP、插件工具统一走 ToolProvider 形态。
// Parameters 是 JSON Schema(draft-07 子集即可),调用参数按其校验。
type SourceKind string
const (
SourceBuiltin SourceKind = "builtin"
SourcePlugin SourceKind = "plugin"
SourceUser SourceKind = "user"
)
type ToolDesc struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters json.RawMessage `json:"parameters,omitempty"`
Source SourceKind `json:"source"`
PluginID string `json:"pluginId,omitempty"`
HTTP *HTTPToolSpec `json:"http,omitempty"` // 声明式 HTTP 工具(无代码)
}
// HTTPToolSpec:无代码自定义工具(4.8)。模板变量 {{param.x}} 从调用参数注入。
type HTTPToolSpec struct {
Method string `json:"method"`
URL string `json:"url"`
Headers map[string]string `json:"headers,omitempty"`
BodyTemplate string `json:"bodyTemplate,omitempty"` // JSON 模板
AuthRef string `json:"authRef,omitempty"` // 设置中的密钥引用(settings key)
TimeoutMs int `json:"timeoutMs,omitempty"`
// ResultPath 从响应 JSON 中裁剪(jq 点路径);空取整个响应。
ResultPath string `json:"resultPath,omitempty"`
}
type ToolCallRequest struct {
CallID string `json:"callId"`
Name string `json:"name"`
Arguments json.RawMessage `json:"arguments,omitempty"`
}
type ToolCallResult struct {
CallID string `json:"callId"`
Status string `json:"status"` // ok | error
Output string `json:"output,omitempty"`
Truncated bool `json:"truncated,omitempty"`
Artifact *Artifact `json:"artifact,omitempty"` // 超长转工件
}
+198
View File
@@ -0,0 +1,198 @@
// Agent-System-Core 契约的 TypeScript 镜像。
// 线格式契约以 Go 类型(camelCase JSON 标签)与 testdata/golden/*.json 黄金样本为准;
// 本文件手工镜像同一契约,前端按未知 type/字段静默忽略(前向兼容原则)。
export type ID = string;
// ---- 领域模型 ----
export interface Project {
id: ID; name: string; createdAt: string; updatedAt: string;
settings?: Record<string, unknown>;
}
export interface Conversation {
id: ID; projectId: ID; title: string; profileId?: ID;
createdAt: string; updatedAt: string;
}
export type Role = 'user' | 'assistant' | 'system';
export type PartType =
| 'text' | 'thinking' | 'tool_call' | 'tool_result' | 'artifact_ref'
| 'hitl' | 'plan' | 'error' | 'card';
export interface Part {
type: PartType;
text?: string;
toolCall?: { id: string; name: string; arguments?: string; durationMs?: number };
toolResult?: { callId: string; status: 'ok' | 'error'; summary?: string; truncated?: boolean };
artifact?: { artifactId: ID; name: string; kind?: string };
hitl?: { cardId: ID; question: string; resolved: boolean; answer?: string };
plan?: PlanSnapshot;
card?: { kind: string; data: UINode[] };
error?: { code: string; message: string };
process?: boolean; // run 终态后折叠为摘要行
}
export interface Message {
id: ID; conversationId: ID; runId?: ID; role: Role;
parts: Part[]; createdAt: string;
}
export type RunKind = 'conversation' | 'subagent' | 'tool';
export type RunStatus =
| 'pending' | 'running' | 'waiting_hitl' | 'completed'
| 'failed' | 'cancelled' | 'interrupted';
export type RunPhase = 'thinking' | 'acting' | 'responding' | 'done';
export interface Usage {
promptTokens: number; completionTokens: number; totalTokens: number;
durationMs: number; toolCalls: number;
}
export interface Run {
id: ID; parentRunId?: ID; conversationId: ID; projectId: ID;
kind: RunKind; profileId?: ID; status: RunStatus; phase?: RunPhase;
agentName?: string; createdAt: string; endedAt?: string;
usage?: Usage; error?: { code: string; message: string };
}
export interface Artifact {
id: ID; projectId: ID; name: string; kind: string; mime?: string;
size: number; sourceRun?: ID; createdAt: string; updatedAt: string;
}
export interface PlanSnapshot {
planId: ID; title: string;
steps: { id: string; text: string; status: 'pending' | 'in_progress' | 'done' | 'skipped'; agentRun?: ID }[];
}
export interface TodoList {
conversationId: ID;
items: { id: string; text: string; status: 'pending' | 'in_progress' | 'done' }[];
}
// ---- 流式事件 ----
export type StreamEventType =
| 'message.delta' | 'thinking.delta' | 'tool.call' | 'tool.result'
| 'run.phase' | 'hitl.request' | 'hitl.resolved' | 'plan.update' | 'todo.update'
| 'agent.spawn' | 'agent.progress' | 'agent.result'
| 'artifact.created' | 'artifact.updated'
| 'run.completed' | 'run.failed' | 'error';
export interface StreamEvent<P = unknown> {
seq: number; runId?: ID; conversationId: ID; ts: string; v: number;
type: StreamEventType; payload?: P;
}
// payload 形状(按 type 判别;未知字段忽略)
export interface MessageDeltaPayload { index: number; text: string }
export interface AgentSpawnPayload { agentRun: ID; name: string; task: string }
export interface AgentProgressPayload { agentRun: ID; note?: string; phase?: RunPhase }
export interface AgentResultPayload { agentRun: ID; status: RunStatus; summary?: string }
export interface HITLResolvedPayload { cardId: ID; answer?: string; kind: 'choice' | 'free' | 'timeout' | 'cancelled' }
export interface RunCompletedPayload { usage: Usage }
export interface RunFailedPayload { code: string; message: string }
// ---- HITL ----
export type HITLKind = 'choice' | 'input' | 'confirm' | 'form';
export interface HITLChoice {
id: string; label: string; description: string; impact: string; recommended?: boolean;
}
export interface HITLCard {
id: ID; runId: ID; conversationId: ID; question: string; kind: HITLKind;
choices?: HITLChoice[]; allowFreeInput: boolean; freeInputHint?: string;
timeoutSec?: number | null; form?: UINode[]; createdAt: string; resolved: boolean;
}
export interface HITLResponse {
cardId: ID; choiceId?: string; freeText?: string; formData?: unknown;
}
// ---- UI 词汇 ----
export type UINodeType =
| 'section' | 'list' | 'kv' | 'form' | 'markdown' | 'doc-ref' | 'actions' | 'chart-lite';
export interface UINode {
type: UINodeType;
title?: string;
items?: string[];
kv?: [string, string][];
fields?: UIField[];
markdown?: string;
docId?: ID;
actions?: { id: string; label: string; style?: 'primary' | 'default' | 'danger' }[];
chart?: { kind: 'bar' | 'line'; labels: string[]; series: number[] };
children?: UINode[];
}
export interface UIField {
key: string; label: string;
type: 'text' | 'textarea' | 'number' | 'boolean' | 'select';
options?: string[]; required?: boolean; placeholder?: string; help?: string; default?: string;
}
// ---- 画像 / 设置 ----
export interface AgentProfile {
id: string; name: string; description?: string; builtin?: boolean; source?: string;
systemPrompt: string;
model: { chat?: string; lightweight?: string; embedding?: string; rerank?: string };
tools: { allow: string[]; deny?: string[]; confirm?: string[] };
context: {
maxHistoryMessages: number; summarizeBeyond?: boolean;
memoryBudgetPct?: number; knowledgeBudgetPct?: number; maxToolResultChars?: number;
};
hitl: { askOnAmbiguity: boolean; timeoutSec?: number | null };
budget: { maxTokens?: number | null; maxToolCalls?: number | null; maxTurns?: number | null };
}
export interface SettingDesc {
key: string; group: string; label: string; description?: string;
type: 'string' | 'number' | 'boolean' | 'enum' | 'secret';
default?: unknown; options?: string[];
effect: 'hot' | 'new-run' | 'restart';
secret?: boolean;
}
// ---- 插件 ----
export interface PluginManifest {
id: string; name: string; version: string; apiVersion: string;
requires?: { network?: string[] };
provides: {
tools?: string[]; workflows?: string[]; intents?: string[];
prompts?: string[]; panels?: string[]; cards?: string[]; settings?: string[];
};
}
export interface PluginPanel {
id: string; title: string; icon?: string; body: UINode[]; refreshTool?: string;
}
// ---- 摄取 / 检索 ----
export interface ParsedDocument {
meta: {
filename: string; mime?: string; parser: string; confidence?: number;
crossCheck?: { with: string; diffBlocks: number; verdict: 'agree' | 'differ' };
parsedAt: string;
};
blocks: {
type: 'heading' | 'para' | 'list' | 'table' | 'code' | 'image-ref';
level?: number; text?: string; items?: string[];
rows?: string[][]; ref?: string; confidence?: number;
}[];
warnings?: string[];
}
export interface RetrievalHit {
doc: { id: ID; projectId?: ID; source: string; title: string; ref?: string; chunk: string; createdAt: string };
score: number; kind: 'fulltext' | 'vector' | 'plugin';
}
+54
View File
@@ -0,0 +1,54 @@
package core
// UI schema 词汇(4.6 原则 3):设置界面、插件面板、HITL 表单、插件卡片共用
// 这套有限词汇,由前端通用渲染器渲染。插件不注入 React 组件;表达不了的高级
// 交互走 Core 契约扩展流程新增词汇,而不是开洞。
type UINodeType string
const (
UISection UINodeType = "section" // 标题 + 子节点
UIList UINodeType = "list" // 字符串列表
UIKV UINodeType = "kv" // 键值对列表
UIForm UINodeType = "form" // 字段表单(HITL form / 设置分区复用)
UIMarkdown UINodeType = "markdown"
UIDocRef UINodeType = "doc-ref" // 工件引用(右栏查看器打开)
UIActions UINodeType = "actions" // 可点动作(发命令/发消息)
UIChartLite UINodeType = "chart-lite"
)
type UINode struct {
Type UINodeType `json:"type"`
Title string `json:"title,omitempty"`
Items []string `json:"items,omitempty"` // list
KV [][2]string `json:"kv,omitempty"` // kv
Fields []UIField `json:"fields,omitempty"` // form
Markdown string `json:"markdown,omitempty"`
DocID ID `json:"docId,omitempty"` // doc-ref
Actions []UIAction `json:"actions,omitempty"`
Chart *UIChart `json:"chart,omitempty"`
Children []UINode `json:"children,omitempty"` // section
}
type UIField struct {
Key string `json:"key"`
Label string `json:"label"`
Type string `json:"type"` // text | textarea | number | boolean | select
Options []string `json:"options,omitempty"`
Required bool `json:"required,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
Help string `json:"help,omitempty"`
Default string `json:"default,omitempty"`
}
type UIAction struct {
ID string `json:"id"`
Label string `json:"label"`
Style string `json:"style,omitempty"` // primary | default | danger
}
type UIChart struct {
Kind string `json:"kind"` // bar | line
Labels []string `json:"labels"`
Series []float64 `json:"series"`
}