Version: 0.9.52.dev.260428
后端: 1. 工具结果结构化切流继续推进:schedule 读工具改为“父包 adapter + 子包 view builder”,`queue_pop_head` / `queue_skip_head` 脱离 legacy wrapper,`analyze_health` / `analyze_rhythm` 补齐 `schedule.analysis_result` 诊断卡片。 2. 非 schedule 工具补齐专属结果协议:`web_search` / `web_fetch`、`upsert_task_class`、`context_tools_add` / `context_tools_remove` 全部接入结构化 `ResultView`,注册表继续去 legacy wrapper,同时保持原始 `ObservationText` 供模型链路复用。 3. 工具展示细节继续收口:参数本地化补齐 `domain` / `packs` / `mode` / `all`,deliver 阶段补发段落分隔,避免 execute 与总结正文黏连。 前端: 4. `ToolCardRenderer` 升级为多协议通用渲染器,补齐 read / analysis / web / taskclass / context 卡片渲染、参数折叠区、未知协议兜底与操作明细展示。 5. `AssistantPanel` 修正 `tool_result` 结果回填与卡片布局宽度问题,并新增结构化卡片 fixture / mock 调试入口,便于整体验收。 仓库: 6. 更新工具结果结构化交接文档,补记第四批切流范围、当前切流点与后续收尾建议。
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/LoveLosita/smartflow/backend/newAgent/tools/schedule"
|
||||
toolcontextresult "github.com/LoveLosita/smartflow/backend/newAgent/tools/tool_context_result"
|
||||
)
|
||||
|
||||
type contextToolsAddResult struct {
|
||||
@@ -34,22 +35,22 @@ type contextToolsRemoveResult struct {
|
||||
// NewContextToolsAddHandler 创建 context_tools_add 工具。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 仅负责校验 domain/mode/packs 并返回结构化结果,不直接修改流程状态;
|
||||
// 2. 真正的“激活态写回”由 execute 节点根据工具结果回写 CommonState;
|
||||
// 3. schedule 支持可选 packs,taskclass 当前不支持可选 packs。
|
||||
// 1. 这里只负责校验 domain / packs / mode,并产出结构化结果;
|
||||
// 2. 不直接改 CommonState,真正的激活切流仍由 execute 层读取 observation 后更新快照;
|
||||
// 3. 因为这里拿不到 CommonState,所以卡片展示的是“本次工具结果返回的 domain/packs/mode”,不是全局最终快照。
|
||||
func NewContextToolsAddHandler() ToolHandler {
|
||||
return func(state *schedule.ScheduleState, args map[string]any) ToolExecutionResult {
|
||||
_ = state
|
||||
|
||||
domain := NormalizeToolDomain(readContextToolString(args["domain"]))
|
||||
if domain == "" {
|
||||
return LegacyResult(ToolNameContextToolsAdd, args, marshalContextToolsAddResult(contextToolsAddResult{
|
||||
return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
|
||||
Tool: ToolNameContextToolsAdd,
|
||||
Success: false,
|
||||
Action: "reject",
|
||||
Error: "参数非法:domain 仅支持 schedule/taskclass",
|
||||
ErrorCode: "invalid_domain",
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
mode := strings.ToLower(strings.TrimSpace(readContextToolString(args["mode"])))
|
||||
@@ -57,52 +58,54 @@ func NewContextToolsAddHandler() ToolHandler {
|
||||
mode = "replace"
|
||||
}
|
||||
if mode != "replace" && mode != "merge" {
|
||||
return LegacyResult(ToolNameContextToolsAdd, args, marshalContextToolsAddResult(contextToolsAddResult{
|
||||
return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
|
||||
Tool: ToolNameContextToolsAdd,
|
||||
Success: false,
|
||||
Action: "reject",
|
||||
Domain: domain,
|
||||
Error: "参数非法:mode 仅支持 replace/merge",
|
||||
ErrorCode: "invalid_mode",
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
packsRaw := readContextToolStringSlice(args["packs"])
|
||||
packs, errCode, errText := validateContextPacks(domain, packsRaw, false)
|
||||
if errCode != "" {
|
||||
return LegacyResult(ToolNameContextToolsAdd, args, marshalContextToolsAddResult(contextToolsAddResult{
|
||||
return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
|
||||
Tool: ToolNameContextToolsAdd,
|
||||
Success: false,
|
||||
Action: "reject",
|
||||
Domain: domain,
|
||||
Error: errText,
|
||||
ErrorCode: errCode,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// schedule 未显式传 packs 时,默认启用最小可用包(mutation + analyze)。
|
||||
// 1. schedule 未显式传 packs 时,默认激活最小可用包 mutation+analyze。
|
||||
// 2. taskclass 当前没有可选包,所以这里会保持空切片,由 execute 层只保留固定 core。
|
||||
// 3. 这样做可以让 observation 直接表达“本次实际生效的可选包集合”,减少展示层再二次猜测。
|
||||
if domain == ToolDomainSchedule && len(packsRaw) == 0 {
|
||||
packs = ResolveEffectiveToolPacks(domain, nil)
|
||||
}
|
||||
|
||||
return LegacyResult(ToolNameContextToolsAdd, args, marshalContextToolsAddResult(contextToolsAddResult{
|
||||
return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
|
||||
Tool: ToolNameContextToolsAdd,
|
||||
Success: true,
|
||||
Action: "activate",
|
||||
Domain: domain,
|
||||
Packs: packs,
|
||||
Mode: mode,
|
||||
Message: "已激活工具域,可继续调用对应业务工具。",
|
||||
}))
|
||||
Message: "已激活目标工具域,可继续调用对应业务工具。",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// NewContextToolsRemoveHandler 创建 context_tools_remove 工具。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 仅解析 domain/all/packs 语义并返回结构化结果,不直接触碰上下文存储;
|
||||
// 2. all=true 表示清空动态区业务工具,domain+packs 表示移除该域下指定二级包;
|
||||
// 3. 仅 schedule 支持按 packs 移除,且 core 不允许显式移除。
|
||||
// 1. 这里只解释 domain / packs / all 的语义,并返回结构化结果;
|
||||
// 2. all=true 表示清空全部业务工具域,domain+packs 表示移除某域下的可选包;
|
||||
// 3. 实际 CommonState 的域/包更新,仍由 execute 层统一消费 observation 完成。
|
||||
func NewContextToolsRemoveHandler() ToolHandler {
|
||||
return func(state *schedule.ScheduleState, args map[string]any) ToolExecutionResult {
|
||||
_ = state
|
||||
@@ -111,61 +114,160 @@ func NewContextToolsRemoveHandler() ToolHandler {
|
||||
domainRaw := strings.ToLower(strings.TrimSpace(readContextToolString(args["domain"])))
|
||||
packsRaw := readContextToolStringSlice(args["packs"])
|
||||
|
||||
// 兼容写法:domain=all 视为清空全部。
|
||||
// 兼容旧写法:domain=all 也视为清空全部业务工具域。
|
||||
if domainRaw == "all" {
|
||||
all = true
|
||||
}
|
||||
if all {
|
||||
return LegacyResult(ToolNameContextToolsRemove, args, marshalContextToolsRemoveResult(contextToolsRemoveResult{
|
||||
return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
|
||||
Tool: ToolNameContextToolsRemove,
|
||||
Success: true,
|
||||
Action: "clear_all",
|
||||
All: true,
|
||||
Message: "已移除全部业务工具域,仅保留上下文管理工具。",
|
||||
}))
|
||||
Message: "已移除全部业务工具域,仅保留 context 管理工具。",
|
||||
})
|
||||
}
|
||||
|
||||
domain := NormalizeToolDomain(domainRaw)
|
||||
if domain == "" {
|
||||
return LegacyResult(ToolNameContextToolsRemove, args, marshalContextToolsRemoveResult(contextToolsRemoveResult{
|
||||
return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
|
||||
Tool: ToolNameContextToolsRemove,
|
||||
Success: false,
|
||||
Action: "reject",
|
||||
Error: "参数非法:需提供 domain=schedule/taskclass 或 all=true",
|
||||
Error: "参数非法:需要提供 domain=schedule/taskclass 或 all=true",
|
||||
ErrorCode: "invalid_domain",
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
packs, errCode, errText := validateContextPacks(domain, packsRaw, true)
|
||||
if errCode != "" {
|
||||
return LegacyResult(ToolNameContextToolsRemove, args, marshalContextToolsRemoveResult(contextToolsRemoveResult{
|
||||
return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
|
||||
Tool: ToolNameContextToolsRemove,
|
||||
Success: false,
|
||||
Action: "reject",
|
||||
Domain: domain,
|
||||
Error: errText,
|
||||
ErrorCode: errCode,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
if len(packs) > 0 {
|
||||
return LegacyResult(ToolNameContextToolsRemove, args, marshalContextToolsRemoveResult(contextToolsRemoveResult{
|
||||
return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
|
||||
Tool: ToolNameContextToolsRemove,
|
||||
Success: true,
|
||||
Action: "deactivate_packs",
|
||||
Domain: domain,
|
||||
Packs: packs,
|
||||
Message: "已移除指定工具包。",
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
return LegacyResult(ToolNameContextToolsRemove, args, marshalContextToolsRemoveResult(contextToolsRemoveResult{
|
||||
return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
|
||||
Tool: ToolNameContextToolsRemove,
|
||||
Success: true,
|
||||
Action: "deactivate",
|
||||
Domain: domain,
|
||||
Message: "已移除指定工具域。",
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func buildContextToolsAddExecutionResult(args map[string]any, payload contextToolsAddResult) ToolExecutionResult {
|
||||
observation := marshalContextToolsAddResult(payload)
|
||||
legacy := LegacyResult(ToolNameContextToolsAdd, args, observation)
|
||||
view := toolcontextresult.BuildAddView(toContextToolsAddPayload(payload), observation)
|
||||
return buildContextToolExecutionResult(legacy, args, view)
|
||||
}
|
||||
|
||||
func buildContextToolsRemoveExecutionResult(args map[string]any, payload contextToolsRemoveResult) ToolExecutionResult {
|
||||
observation := marshalContextToolsRemoveResult(payload)
|
||||
legacy := LegacyResult(ToolNameContextToolsRemove, args, observation)
|
||||
view := toolcontextresult.BuildRemoveView(toContextToolsRemovePayload(payload), observation)
|
||||
return buildContextToolExecutionResult(legacy, args, view)
|
||||
}
|
||||
|
||||
// buildContextToolExecutionResult 负责把子包纯展示结构包回 ToolExecutionResult。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只做 ContextResultView -> ToolDisplayView 的协议桥接;
|
||||
// 2. 不改写 ObservationText,确保模型侧仍消费原始 observation JSON;
|
||||
// 3. 错误码与错误文案继续复用父包现有 JSON/text 解析逻辑,避免多套失败判定分叉。
|
||||
func buildContextToolExecutionResult(
|
||||
legacy ToolExecutionResult,
|
||||
args map[string]any,
|
||||
view toolcontextresult.ContextResultView,
|
||||
) ToolExecutionResult {
|
||||
result := legacy
|
||||
status := normalizeToolStatus(result.Status)
|
||||
if status == "" {
|
||||
status = ToolStatusDone
|
||||
}
|
||||
|
||||
collapsed := cloneAnyMap(view.Collapsed)
|
||||
if collapsed == nil {
|
||||
collapsed = make(map[string]any)
|
||||
}
|
||||
expanded := cloneAnyMap(view.Expanded)
|
||||
if expanded == nil {
|
||||
expanded = make(map[string]any)
|
||||
}
|
||||
|
||||
collapsed["status"] = status
|
||||
if _, exists := collapsed["status_label"]; !exists {
|
||||
collapsed["status_label"] = resolveToolStatusLabelCN(status)
|
||||
}
|
||||
if _, exists := expanded["raw_text"]; !exists {
|
||||
expanded["raw_text"] = result.ObservationText
|
||||
}
|
||||
|
||||
result.Status = status
|
||||
result.Success = status == ToolStatusDone
|
||||
result.ResultView = &ToolDisplayView{
|
||||
ViewType: strings.TrimSpace(view.ViewType),
|
||||
Version: view.Version,
|
||||
Collapsed: collapsed,
|
||||
Expanded: expanded,
|
||||
}
|
||||
if title, ok := readStringAnyMap(collapsed, "title"); ok {
|
||||
result.Summary = title
|
||||
}
|
||||
if !result.Success {
|
||||
errorCode, errorMessage := extractToolErrorInfo(result.ObservationText, status)
|
||||
if strings.TrimSpace(result.ErrorCode) == "" {
|
||||
result.ErrorCode = strings.TrimSpace(errorCode)
|
||||
}
|
||||
if strings.TrimSpace(result.ErrorMessage) == "" {
|
||||
result.ErrorMessage = strings.TrimSpace(errorMessage)
|
||||
}
|
||||
}
|
||||
return EnsureToolResultDefaults(result, args)
|
||||
}
|
||||
|
||||
func toContextToolsAddPayload(payload contextToolsAddResult) toolcontextresult.ContextToolsAddPayload {
|
||||
return toolcontextresult.ContextToolsAddPayload{
|
||||
Tool: payload.Tool,
|
||||
Success: payload.Success,
|
||||
Action: payload.Action,
|
||||
Domain: payload.Domain,
|
||||
Packs: append([]string(nil), payload.Packs...),
|
||||
Mode: payload.Mode,
|
||||
Message: payload.Message,
|
||||
Error: payload.Error,
|
||||
ErrorCode: payload.ErrorCode,
|
||||
}
|
||||
}
|
||||
|
||||
func toContextToolsRemovePayload(payload contextToolsRemoveResult) toolcontextresult.ContextToolsRemovePayload {
|
||||
return toolcontextresult.ContextToolsRemovePayload{
|
||||
Tool: payload.Tool,
|
||||
Success: payload.Success,
|
||||
Action: payload.Action,
|
||||
Domain: payload.Domain,
|
||||
Packs: append([]string(nil), payload.Packs...),
|
||||
All: payload.All,
|
||||
Message: payload.Message,
|
||||
Error: payload.Error,
|
||||
ErrorCode: payload.ErrorCode,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user