feat: v2.3.0 流式输出 + 日志系统 + 会议室架构全面升级

- 流式输出: SSE 逐 token 接收, \\n\n\ 段落缓冲后 mdprint 彩色渲染
- 日志系统: charmbracelet/log v2 双写(stderr + log.yml), yunshu log 命令
- 会议室架构: dialog(main) + weather/profile/note(sub) 多 Agent 编排
- 泛型工具注册: NewTool[T] 反射推导 JSON Schema, 类型安全
- 安全加固: safeMemoryPath 三段校验(EvalSymlinks+Rel), maxToolCalls=2
- 性能优化: sync.Once 延迟加载, note 一步完成, obs/summary 合并
- Prompt 适配: 流式输出原则(先调工具不说话), 单 Agent 查询跳过 obs+summary
- 文档: AGENTS.md + architecture.md + changelog.md 全部同步至 v2.3.0
This commit is contained in:
titor
2026-05-16 17:21:29 +08:00
parent 0898188086
commit c4a0e3ef53
24 changed files with 2769 additions and 338 deletions

View File

@@ -51,6 +51,7 @@ type CatalogSkill struct {
type CatalogAgent struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Path string `yaml:"path"`
Description string `yaml:"description"`
Tools []string `yaml:"tools"`
@@ -84,22 +85,37 @@ func buildToolList() []CatalogTool {
Source: "src/tool.go",
}
// 从 JSON Schema 提取参数
// 从 Schema (map[string]any) 提取参数
ct.Parameters = make(map[string]ParameterField)
for name, prop := range t.Parameters.Properties {
required := false
for _, r := range t.Parameters.Required {
if r == name {
required = true
break
if t.Parameters == nil {
list = append(list, ct)
continue
}
props, _ := t.Parameters["properties"].(map[string]any)
required := make(map[string]bool)
if reqList, ok := t.Parameters["required"].([]any); ok {
for _, r := range reqList {
if s, ok := r.(string); ok {
required[s] = true
}
}
}
for name, propRaw := range props {
prop, ok := propRaw.(map[string]any)
if !ok {
continue
}
typ, _ := prop["type"].(string)
desc, _ := prop["description"].(string)
ct.Parameters[name] = ParameterField{
Type: prop.Type,
Required: required,
Description: prop.Description,
Type: typ,
Required: required[name],
Description: desc,
}
}
list = append(list, ct)
}
return list
@@ -181,8 +197,14 @@ func scanAgents() []CatalogAgent {
continue
}
agentType := "main"
if t, ok := fm["type"]; ok {
agentType = fmt.Sprintf("%v", t)
}
cat := CatalogAgent{
Name: fmt.Sprintf("%v", fm["name"]),
Type: agentType,
Path: fmt.Sprintf("agents/%s", e.Name()),
Status: "active",
}
@@ -244,6 +266,21 @@ func GenerateToolsYAML() {
// 注入目录到 system prompt
// ============================================================
// BuildSubAgentPrompt 生成可用子 Agent 列表,动态注入到主 Agent 的 system prompt
func BuildSubAgentPrompt(subs []*AgentDef) string {
if len(subs) == 0 {
return ""
}
var b strings.Builder
b.WriteString("\n\n## 可用子 Agent\n")
b.WriteString("\n以下子 Agent 可通过 task 工具调度:\n")
for _, s := range subs {
b.WriteString(fmt.Sprintf("- **%s**: %s\n", s.Name, s.Description))
}
b.WriteString("\n用 `task(\"agent_name\", {args})` 调度。不自己回答领域问题。\n")
return b.String()
}
// BuildInjectPrompt 生成能力边界目录,追加到 system prompt 末尾
func BuildInjectPrompt(toolNames []string) string {
var b strings.Builder