Files
Losita d7184b776b Version: 0.9.75.dev.260505
后端:
1.收口阶段 6 agent 结构迁移,将 newAgent 内核与 agentsvc 编排层迁入 services/agent
- 切换 Agent 启动装配与 HTTP handler 直连 agent sv,移除旧 service agent bridge
- 补齐 Agent 对 memory、task、task-class、schedule 的 RPC 适配与契约字段
- 扩展 schedule、task、task-class RPC/contract 支撑 Agent 查询、写入与 provider 切流
- 更新迁移文档、README 与相关注释,明确 agent 当前切流点和剩余 memory 迁移面
2026-05-05 16:00:57 +08:00

654 lines
18 KiB
Go
Raw Permalink 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 toolcontextresult
import (
"fmt"
"strings"
)
const (
ViewTypeContextResult = "tool.context_result"
ViewVersionContextResult = 1
StatusDone = "done"
StatusFailed = "failed"
)
// ContextResultView 仅承载 context 工具卡片的纯展示数据。
//
// 职责边界:
// 1. 负责输出 view_type / version / collapsed / expanded 四段展示结构;
// 2. 不依赖父包 ToolExecutionResult避免形成反向 import
// 3. 不改写 ObservationText原始文本由父包原样挂到 expanded.raw_text。
type ContextResultView struct {
ViewType string `json:"view_type"`
Version int `json:"version"`
Collapsed map[string]any `json:"collapsed"`
Expanded map[string]any `json:"expanded"`
}
type MetricField struct {
Label string `json:"label"`
Value string `json:"value"`
}
type KVField struct {
Label string `json:"label"`
Value string `json:"value"`
}
type ItemView struct {
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Tags []string `json:"tags"`
DetailLines []string `json:"detail_lines"`
Meta map[string]any `json:"meta,omitempty"`
}
// ContextToolsAddPayload 对齐 context_tools_add observation 的机器字段。
type ContextToolsAddPayload struct {
Tool string `json:"tool"`
Success bool `json:"success"`
Action string `json:"action"`
Domain string `json:"domain,omitempty"`
Packs []string `json:"packs,omitempty"`
Mode string `json:"mode,omitempty"`
Message string `json:"message,omitempty"`
Error string `json:"error,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
}
// ContextToolsRemovePayload 对齐 context_tools_remove observation 的机器字段。
type ContextToolsRemovePayload struct {
Tool string `json:"tool"`
Success bool `json:"success"`
Action string `json:"action"`
Domain string `json:"domain,omitempty"`
Packs []string `json:"packs,omitempty"`
All bool `json:"all,omitempty"`
Message string `json:"message,omitempty"`
Error string `json:"error,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
}
// BuildAddView 生成 context_tools_add 的结构化卡片。
func BuildAddView(payload ContextToolsAddPayload, observation string) ContextResultView {
status := statusFromSuccess(payload.Success)
summary := buildAddSummary(payload)
detailLines := buildAddDetailLines(payload)
item := BuildItem(
fallbackText(ResolveDomainLabelCN(payload.Domain), "工具域"),
summary,
buildAddTags(payload),
detailLines,
map[string]any{
"action": payload.Action,
"domain": strings.TrimSpace(payload.Domain),
"mode": strings.TrimSpace(payload.Mode),
},
)
sections := []map[string]any{
buildContextCalloutSection(
fallbackText(buildAddCalloutTitle(payload), "工具域变更"),
summary,
toneFromSuccess(payload.Success),
detailLines,
),
BuildKVSection("当前工具区参数", []KVField{
BuildKVField("工具域", fallbackText(ResolveDomainLabelCN(payload.Domain), "未指定")),
BuildKVField("工具包", buildAddPackField(payload)),
BuildKVField("注入模式", fallbackText(ResolveModeLabelCN(payload.Mode), "未指定")),
BuildKVField("清空全部", "否"),
BuildKVField("动作", fallbackText(ResolveActionLabelCN(payload.Action), strings.TrimSpace(payload.Action))),
}),
BuildItemsSection("变更摘要", "", []ItemView{item}),
}
if !payload.Success {
appendErrorSection(&sections, payload.Error, payload.ErrorCode)
}
return ContextResultView{
ViewType: ViewTypeContextResult,
Version: ViewVersionContextResult,
Collapsed: map[string]any{
"title": buildAddCollapsedTitle(payload),
"subtitle": summary,
"status": status,
"status_label": resolveStatusLabelCN(status),
"metrics": buildMetrics(
BuildMetric("域", fallbackText(shortDomainLabel(payload.Domain), "未指定")),
BuildMetric("包", fmt.Sprintf("%d 个", len(payload.Packs))),
BuildMetric("模式", fallbackText(ResolveModeLabelCN(payload.Mode), "未指定")),
),
},
Expanded: map[string]any{
"items": []map[string]any{item.Map()},
"sections": sections,
"raw_text": observation,
"machine_payload": buildAddMachinePayload(payload),
},
}
}
// BuildRemoveView 生成 context_tools_remove 的结构化卡片。
func BuildRemoveView(payload ContextToolsRemovePayload, observation string) ContextResultView {
status := statusFromSuccess(payload.Success)
summary := buildRemoveSummary(payload)
detailLines := buildRemoveDetailLines(payload)
item := BuildItem(
buildRemoveItemTitle(payload),
summary,
buildRemoveTags(payload),
detailLines,
map[string]any{
"action": payload.Action,
"domain": strings.TrimSpace(payload.Domain),
"all": payload.All,
},
)
sections := []map[string]any{
buildContextCalloutSection(
fallbackText(buildRemoveCalloutTitle(payload), "工具域变更"),
summary,
toneFromSuccess(payload.Success),
detailLines,
),
BuildKVSection("当前工具区参数", []KVField{
BuildKVField("工具域", buildRemoveDomainField(payload)),
BuildKVField("工具包", buildRemovePackField(payload)),
BuildKVField("注入模式", "不适用"),
BuildKVField("清空全部", formatBoolCN(payload.All)),
BuildKVField("动作", fallbackText(ResolveActionLabelCN(payload.Action), strings.TrimSpace(payload.Action))),
}),
BuildItemsSection("变更摘要", "", []ItemView{item}),
}
if !payload.Success {
appendErrorSection(&sections, payload.Error, payload.ErrorCode)
}
return ContextResultView{
ViewType: ViewTypeContextResult,
Version: ViewVersionContextResult,
Collapsed: map[string]any{
"title": buildRemoveCollapsedTitle(payload),
"subtitle": summary,
"status": status,
"status_label": resolveStatusLabelCN(status),
"metrics": buildMetrics(
BuildMetric("范围", buildRemoveMetricScope(payload)),
BuildMetric("包", fmt.Sprintf("%d 个", len(payload.Packs))),
BuildMetric("动作", fallbackText(ResolveActionLabelCN(payload.Action), strings.TrimSpace(payload.Action))),
),
},
Expanded: map[string]any{
"items": []map[string]any{item.Map()},
"sections": sections,
"raw_text": observation,
"machine_payload": buildRemoveMachinePayload(payload),
},
}
}
func ResolveDomainLabelCN(domain string) string {
switch strings.ToLower(strings.TrimSpace(domain)) {
case "schedule":
return "排程工具域"
case "taskclass":
return "任务类工具域"
default:
return ""
}
}
func ResolvePackLabelCN(pack string) string {
switch strings.ToLower(strings.TrimSpace(pack)) {
case "core":
return "固定 core"
case "mutation":
return "排程改写"
case "analyze":
return "健康分析"
case "detail_read":
return "明细读取"
case "deep_analyze":
return "深度分析"
case "queue":
return "队列微调"
case "web":
return "网页能力"
default:
return strings.TrimSpace(pack)
}
}
func FormatPacksCN(packs []string) string {
if len(packs) == 0 {
return "无"
}
parts := make([]string, 0, len(packs))
for _, pack := range packs {
label := strings.TrimSpace(ResolvePackLabelCN(pack))
if label == "" {
continue
}
parts = append(parts, label)
}
if len(parts) == 0 {
return "无"
}
return strings.Join(parts, "、")
}
func ResolveModeLabelCN(mode string) string {
switch strings.ToLower(strings.TrimSpace(mode)) {
case "replace":
return "替换"
case "merge":
return "合并"
default:
return strings.TrimSpace(mode)
}
}
func ResolveActionLabelCN(action string) string {
switch strings.ToLower(strings.TrimSpace(action)) {
case "activate":
return "激活"
case "deactivate":
return "移除域"
case "deactivate_packs":
return "移除包"
case "clear_all":
return "清空全部"
case "reject":
return "拒绝"
default:
return strings.TrimSpace(action)
}
}
func BuildMetric(label string, value string) MetricField {
return MetricField{
Label: strings.TrimSpace(label),
Value: strings.TrimSpace(value),
}
}
func BuildKVField(label string, value string) KVField {
return KVField{
Label: strings.TrimSpace(label),
Value: strings.TrimSpace(value),
}
}
func BuildItem(title string, subtitle string, tags []string, detailLines []string, meta map[string]any) ItemView {
return ItemView{
Title: strings.TrimSpace(title),
Subtitle: strings.TrimSpace(subtitle),
Tags: normalizeStringSlice(tags),
DetailLines: normalizeStringSlice(detailLines),
Meta: cloneAnyMap(meta),
}
}
func BuildItemsSection(title string, summary string, items []ItemView) map[string]any {
normalized := make([]map[string]any, 0, len(items))
for _, item := range items {
normalized = append(normalized, item.Map())
}
return map[string]any{
"type": "items",
"title": strings.TrimSpace(title),
"summary": strings.TrimSpace(summary),
"items": normalized,
}
}
func BuildKVSection(title string, fields []KVField) map[string]any {
normalized := make([]map[string]any, 0, len(fields))
for _, field := range fields {
label := strings.TrimSpace(field.Label)
value := strings.TrimSpace(field.Value)
if label == "" || value == "" {
continue
}
normalized = append(normalized, map[string]any{
"label": label,
"value": value,
})
}
return map[string]any{
"type": "kv",
"title": strings.TrimSpace(title),
"fields": normalized,
}
}
func buildContextCalloutSection(title string, summary string, tone string, detailLines []string) map[string]any {
return map[string]any{
"type": "callout",
"title": strings.TrimSpace(title),
"summary": strings.TrimSpace(summary),
"tone": strings.TrimSpace(tone),
"detail_lines": normalizeStringSlice(detailLines),
}
}
func appendErrorSection(target *[]map[string]any, reason string, errorCode string) {
lines := make([]string, 0, 2)
if strings.TrimSpace(reason) != "" {
lines = append(lines, strings.TrimSpace(reason))
}
if strings.TrimSpace(errorCode) != "" {
lines = append(lines, fmt.Sprintf("错误码:%s", strings.TrimSpace(errorCode)))
}
*target = append(*target, buildContextCalloutSection("失败原因", fallbackText(reason, "工具调用失败"), "danger", lines))
}
func buildAddCollapsedTitle(payload ContextToolsAddPayload) string {
if !payload.Success {
return "激活工具域失败"
}
label := ResolveDomainLabelCN(payload.Domain)
if label == "" {
return "已激活工具域"
}
return fmt.Sprintf("已激活%s", label)
}
func buildRemoveCollapsedTitle(payload ContextToolsRemovePayload) string {
if !payload.Success {
return "移除工具域失败"
}
if payload.All {
return "已清空业务工具域"
}
if len(payload.Packs) > 0 {
return "已移除工具包"
}
label := ResolveDomainLabelCN(payload.Domain)
if label == "" {
return "已移除工具域"
}
return fmt.Sprintf("已移除%s", label)
}
func buildAddCalloutTitle(payload ContextToolsAddPayload) string {
if payload.Success {
return "动态工具区已更新"
}
return "激活失败"
}
func buildRemoveCalloutTitle(payload ContextToolsRemovePayload) string {
if payload.Success {
return "动态工具区已更新"
}
return "移除失败"
}
func buildAddSummary(payload ContextToolsAddPayload) string {
if !payload.Success {
return fallbackText(payload.Error, "激活工具域失败")
}
domainLabel := fallbackText(ResolveDomainLabelCN(payload.Domain), "工具域")
packsText := formatPackFieldText(payload.Packs)
modeLabel := fallbackText(ResolveModeLabelCN(payload.Mode), "替换")
if len(payload.Packs) == 0 {
return fmt.Sprintf("%s已激活模式=%s仅保留固定 core。", domainLabel, modeLabel)
}
return fmt.Sprintf("%s已激活模式=%s启用 %s。", domainLabel, modeLabel, packsText)
}
func buildRemoveSummary(payload ContextToolsRemovePayload) string {
if !payload.Success {
return fallbackText(payload.Error, "移除工具域失败")
}
if payload.All {
return "已清空全部业务工具域,仅保留 context 管理工具。"
}
domainLabel := fallbackText(ResolveDomainLabelCN(payload.Domain), "工具域")
if len(payload.Packs) > 0 {
return fmt.Sprintf("已从%s移除 %s。", domainLabel, FormatPacksCN(payload.Packs))
}
return fmt.Sprintf("已移除%s。", domainLabel)
}
func buildAddDetailLines(payload ContextToolsAddPayload) []string {
lines := []string{
fmt.Sprintf("工具域:%s", fallbackText(ResolveDomainLabelCN(payload.Domain), "未指定")),
fmt.Sprintf("工具包:%s", buildAddPackField(payload)),
fmt.Sprintf("注入模式:%s", fallbackText(ResolveModeLabelCN(payload.Mode), "未指定")),
}
if strings.TrimSpace(payload.Message) != "" {
lines = append(lines, strings.TrimSpace(payload.Message))
}
if !payload.Success && strings.TrimSpace(payload.Error) != "" {
lines = append(lines, fmt.Sprintf("失败原因:%s", strings.TrimSpace(payload.Error)))
}
return lines
}
func buildRemoveDetailLines(payload ContextToolsRemovePayload) []string {
lines := []string{
fmt.Sprintf("工具域:%s", buildRemoveDomainField(payload)),
fmt.Sprintf("工具包:%s", buildRemovePackField(payload)),
fmt.Sprintf("清空全部:%s", formatBoolCN(payload.All)),
}
if strings.TrimSpace(payload.Message) != "" {
lines = append(lines, strings.TrimSpace(payload.Message))
}
if !payload.Success && strings.TrimSpace(payload.Error) != "" {
lines = append(lines, fmt.Sprintf("失败原因:%s", strings.TrimSpace(payload.Error)))
}
return lines
}
func buildAddTags(payload ContextToolsAddPayload) []string {
tags := []string{
fallbackText(ResolveActionLabelCN(payload.Action), "激活"),
}
if modeLabel := strings.TrimSpace(ResolveModeLabelCN(payload.Mode)); modeLabel != "" {
tags = append(tags, modeLabel)
}
if len(payload.Packs) > 0 {
tags = append(tags, fmt.Sprintf("%d 个包", len(payload.Packs)))
}
return normalizeStringSlice(tags)
}
func buildRemoveTags(payload ContextToolsRemovePayload) []string {
tags := []string{
fallbackText(ResolveActionLabelCN(payload.Action), "移除"),
}
if payload.All {
tags = append(tags, "全部")
}
if len(payload.Packs) > 0 {
tags = append(tags, fmt.Sprintf("%d 个包", len(payload.Packs)))
}
return normalizeStringSlice(tags)
}
func buildRemoveItemTitle(payload ContextToolsRemovePayload) string {
if payload.All {
return "全部业务工具域"
}
return fallbackText(ResolveDomainLabelCN(payload.Domain), "工具域")
}
func buildRemoveDomainField(payload ContextToolsRemovePayload) string {
if payload.All {
return "全部业务工具域"
}
return fallbackText(ResolveDomainLabelCN(payload.Domain), "未指定")
}
func buildRemoveMetricScope(payload ContextToolsRemovePayload) string {
if payload.All {
return "全部"
}
return fallbackText(shortDomainLabel(payload.Domain), "单域")
}
func buildAddMachinePayload(payload ContextToolsAddPayload) map[string]any {
return map[string]any{
"tool": payload.Tool,
"success": payload.Success,
"action": strings.TrimSpace(payload.Action),
"domain": strings.TrimSpace(payload.Domain),
"packs": append([]string(nil), payload.Packs...),
"mode": strings.TrimSpace(payload.Mode),
"message": strings.TrimSpace(payload.Message),
"error": strings.TrimSpace(payload.Error),
"error_code": strings.TrimSpace(payload.ErrorCode),
}
}
func buildRemoveMachinePayload(payload ContextToolsRemovePayload) map[string]any {
return map[string]any{
"tool": payload.Tool,
"success": payload.Success,
"action": strings.TrimSpace(payload.Action),
"domain": strings.TrimSpace(payload.Domain),
"packs": append([]string(nil), payload.Packs...),
"all": payload.All,
"message": strings.TrimSpace(payload.Message),
"error": strings.TrimSpace(payload.Error),
"error_code": strings.TrimSpace(payload.ErrorCode),
}
}
func buildMetrics(metrics ...MetricField) []map[string]any {
normalized := make([]map[string]any, 0, len(metrics))
for _, metric := range metrics {
label := strings.TrimSpace(metric.Label)
value := strings.TrimSpace(metric.Value)
if label == "" || value == "" {
continue
}
normalized = append(normalized, map[string]any{
"label": label,
"value": value,
})
}
return normalized
}
func toneFromSuccess(success bool) string {
if success {
return "info"
}
return "danger"
}
func statusFromSuccess(success bool) string {
if success {
return StatusDone
}
return StatusFailed
}
func resolveStatusLabelCN(status string) string {
switch strings.ToLower(strings.TrimSpace(status)) {
case StatusDone:
return "已完成"
default:
return "失败"
}
}
func shortDomainLabel(domain string) string {
switch strings.ToLower(strings.TrimSpace(domain)) {
case "schedule":
return "排程"
case "taskclass":
return "任务类"
default:
return strings.TrimSpace(domain)
}
}
func formatPackFieldText(packs []string) string {
if len(packs) == 0 {
return "无"
}
return FormatPacksCN(packs)
}
func buildAddPackField(payload ContextToolsAddPayload) string {
if len(payload.Packs) == 0 {
return "仅固定 core"
}
return FormatPacksCN(payload.Packs)
}
func buildRemovePackField(payload ContextToolsRemovePayload) string {
if len(payload.Packs) == 0 {
if payload.All {
return "全部清空"
}
if strings.EqualFold(strings.TrimSpace(payload.Action), "deactivate") {
return "未指定(按整域处理)"
}
return "无"
}
return FormatPacksCN(payload.Packs)
}
func formatBoolCN(value bool) string {
if value {
return "是"
}
return "否"
}
func fallbackText(text string, fallback string) string {
if strings.TrimSpace(text) == "" {
return strings.TrimSpace(fallback)
}
return strings.TrimSpace(text)
}
func normalizeStringSlice(values []string) []string {
if len(values) == 0 {
return nil
}
out := make([]string, 0, len(values))
for _, value := range values {
text := strings.TrimSpace(value)
if text == "" {
continue
}
out = append(out, text)
}
if len(out) == 0 {
return nil
}
return out
}
func cloneAnyMap(input map[string]any) map[string]any {
if len(input) == 0 {
return nil
}
out := make(map[string]any, len(input))
for key, value := range input {
out[key] = value
}
return out
}
func (view ItemView) Map() map[string]any {
item := map[string]any{
"title": strings.TrimSpace(view.Title),
"subtitle": strings.TrimSpace(view.Subtitle),
"tags": normalizeStringSlice(view.Tags),
"detail_lines": normalizeStringSlice(view.DetailLines),
}
if len(view.Meta) > 0 {
item["meta"] = cloneAnyMap(view.Meta)
}
return item
}