Commit Graph

13 Commits

Author SHA1 Message Date
Blizzard aba88193d8 feat(voice): 提升桌面端语音交互与本地任务执行支持 2026-07-24 16:40:25 +08:00
Blizzard 99ce07c1b0 feat(voice): 每用户 JARVIS——自定义名字/人设 + 自带豆包配置回落 + 语音不串主记忆
把 JARVIS 做成每用户独立:名字(你叫JARVIS别人叫星期五)、人设(与主偏好记忆分开)、
可自带豆包配置(齐全则用用户的,否则回落系统)。

- store/user_jarvis.go: sundynix_user_jarvis(user_id唯一;name/persona/火山creds,APIKey密文)
  + Get/Save(upsert) + AutoMigrate 注册
- voice_config.go resolveJarvis(uid): 火山配置用户优先系统兜底 + 取名字/人设
- voice_task.go: voiceSystemPrompt(name,persona)——简短是硬基线,名字/语气由用户定;
  buildVoiceGraph 带 name+persona;voice.go 会话升级时按用户解析
- dispatcher compose_compiler: 语音任务(useVoice)不拉主偏好记忆,改用用户 persona(保留短期历史)
- jarvis.go + 路由: GET/PUT /api/v1/me/jarvis(用户级,api_key 脱敏/留空沿用)

真机验证:设 name=星期五+干净人设→语音答"我是星期五…"(自称新名、无糙话、4.4s),
has_own_voice=false 用系统豆包。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:06:32 +08:00
Blizzard aec7ad949c feat(model): 工作模型与 JARVIS 语音模型分开配置 + 语音任务路由到快模型
模型配置加"用途"维度:工作主力(chat,要强)与 JARVIS 语音(voice,要快/低时延)各配各激活,
语音任务走语音模型池,未配置则透明回落工作模型——不影响现有功能。

- contract: ConfigKindVoice="voice" + Meta[model_profile]=voice(与 intent==report 同类路由)
- gateway: ServeConfig/broadcastActive 循环纳入 voice;submitVoiceTask 打 model_profile=voice 标记
- dispatcher: 第二个 llm.Pool(voicePool)吃 voice 配置热更新;board.useVoice 从 Meta 派生(含快照);
  Orchestrator.agentPool(b) 按黑板选池——语音且语音池就绪→语音池,否则工作池;
  agent 生成路径(graph/react/coordinator/compose)全改走 agentPool(b),报告/护栏/记忆固定工作池
- admin: 模型页三 Tab(工作主力/JARVIS语音/向量化),复用 ModelManager;api Kind 加 voice

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:39:17 +08:00
Blizzard c2811e79a3 fix(desktop,dispatcher): 从编排执行的多 agent 图看不到「团队」tab
现象:编排里并排三个 agent(研究/撰写/审查)跑完,运行页没有团队 tab。

两处卡住,不是一处:
1) isMultiAgent 只认 coordinator: 节点。用户自己在图里并排多个 agent 也是
   团队,却被整个漏掉。改成:有协调者,或 ≥2 个 agent: 节点。
2) 就算放宽 1,deriveTeam 仍按 kind==="agent" 挑工位——而两条产生 agent 的
   路径 kind 并不一致:协调者派发的专家是 kind=agent,图里的 agent 节点是
   kind=model。改成按节点名前缀(agent:/tool:)判,这本来就是后端一直遵守的
   约定;顺带天然把 retriever:/map:/render: 这些同为 kind=tool 的节点挡在
   工位之外(之前它们会混进来当工位)。

连带修一个更要命的:runAgent 把轨迹标签写死成"模型流式推理"、runReactAgent
写死成"ReAct 智能体(自主调工具)",用户在编排里给节点起的名字(研究 Agent /
撰写 Agent / 审查 Agent)整个丢了。后果不止办公室:执行轨迹里三行同名,根本
分不出谁是谁;团队视图只能退回节点 ID,工位显示成 r/w/rev。
改成一律 labelOf(n, 兜底) 由调用方传入,+2 单测钉住。

另:没有协调者时不再凭空画一个"协调者"小人(白板改挂「任务产出」),也不演
递简报那一程(没人可递),✓ 气泡改为收工即冒。

注意:已存的历史轨迹是落库的,仍是旧标签;只有新跑的任务才有节点名。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 17:41:21 +08:00
Blizzard ef6f525a74 refactor(dispatcher): T1.1 退役 graph.go —— 编排引擎收成单一 compose
compose 已默认数天、HITL/多智能体/评测全在其上真实跑过,soak 充分。删掉自研拓扑
解释器这第二套引擎,消灭"双实现 drift"税:

- 删 runGraph(graph.go 的自研解释器)+ composeEnabled/EINO_COMPOSE 逃生舱开关
  + runConversation(仅 runGraph 用的死代码)。
- executeGraph 直接走 runComposeGraph;compose 编译失败兜底改单轮对话(不再回退
  graph.go);清掉仅 runGraph 用的 import(otel attribute/trace/otelx)。
- 保留 board / 各节点执行器(retriever/tool/agent/branch/approval/map/render/aggregate)
  / 工具函数 —— 它们是 compose 各节点 lambda 复用的,非 graph.go 专属。
- 测试:8 处 runGraph→runComposeGraph;等价测试(对照两引擎)转为 compose 正确性测试;
  runConversation 的开关测试转为「无 ChatModel 降级 runAgent」。

go test ./... 全绿 + vet 干净;冒烟 简单 agent/分支图 跑通。此后每个编排改动不再两边
对齐,成本减半。DEPTH_ROADMAP T1.1 。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 08:53:02 +08:00
Blizzard 66caeef35c feat(agent): 多智能体协同 v1 —— coordinator 节点(Eino×Anthropic 融合)
LLM 自主在 agent 间路由/委派:orchestrator=ReAct(Eino 出机器),编排认知按 Anthropic
orchestrator-worker 配方(出脑子)。专家=包成工具的子 agent(agent-as-tool),lead 给
每个专家写定制简报(brief)后并行派发、综合。方案见 MULTI_AGENT.md。

为什么 agent-as-tool 而非 Eino host:host 的 specialist 拿原始输入(preHandler
return state.msgs),传不了 lead 写的定制简报,而定制简报正是 Anthropic 多智能体的
精髓。agent-as-tool 让 orchestrator 自己 emit 工具调用、参数 brief 即简报。
= OpenAI agent.as_tool() / Anthropic 研究系统的 orchestrator-worker。

- coordinator.go: specialistTool(react.Agent/ChatModel 包成 InvokableTool,入参 brief,
  精炼返回) + parseSpecialists/buildSpecialists(带工具→react,不带→ChatModel,MCP 工具
  按 spec.tools 过滤) + runCoordinator(lead 提示词=Anthropic 配方) + leadOrchestratorPrompt。
- 双路接入 execDSLNode(compose)+ runGraph(graph.go)的 case coordinator。
- 护栏:禁套娃(专家是内联叶子)/ MaxStep / 专家 I/O 计入共享 Budget / 降级(无
  ToolCallingModel 或 0 专家 → runAgent)。
- streamAgentReply:抽出 runReactAgent 与 runCoordinator 共用的流式回流尾段。
- 复用即得:evaluator-optimizer=harness 低分纠偏;成本天花板=预算护栏;上下文隔离=
  专家独立 react.Agent;观测=每次派发落 agent 轨迹。

测试:parseSpecialists / agent-as-tool 包装(brief 透传+精炼返回+失败作观察) / 降级。
live 验证(真 deepseek):两专家**并行派发**、lead 给各自写**不同定制简报**、最终
**综合**(非拼接)成稿,评测 1.00。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 15:37:23 +08:00
Blizzard 0378a770ca fix(hitl): resume 记录 KV 键不可含冒号(NATS: invalid key)
live NATS 联调发现:resume 记录键用 "pending:"+taskID,冒号是 NATS JetStream KV
非法字符(仅允许 [-/_=.a-zA-Z0-9])→ 中断时 persistResume 的 Put 静默失败、决定
到达时 loadResume 报 "nats: invalid key",任务永卡 waiting、无法恢复。内存桩接受
任意键,故单测漏过——正是只有 live NATS 才暴露的那类。

- pendingKey: "pending:"+id → "pending_"+id(合法键)。
- persistResume: Put 失败改 log.Printf 大声告警(不止 exec 轨迹),关键失败可见。
- 回归测试 TestPendingKeyIsNATSValid:直接钉键形匹配 NATS KV 字符集,绕开内存桩盲区。

live 验证(devnats + 真链路):提交 HITL 任务→waiting→杀 dispatcher→离线批准→
重启→1s waiting→2s running→3s done,deepseek 真实出稿。证明 checkpoint 抗重启 +
决定经 JetStream 抗离线 + 断点恢复。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:25:47 +08:00
Blizzard 515cf7f87a feat(hitl): 增量3b —— 持久决定投递 + 生产激活(HITL 中断/恢复上线)
把中断/恢复模型在 dispatcher 接线启用,并让审批决定持久化抗离线。至此 HITL
从「阻塞 goroutine 等 5min、core NATS 非持久、不抗重启」升级为「持久化中断 +
决定持久投递 + 从 checkpoint 恢复续跑」。

- contract: 新增审批决定流 StreamApprovals(SUNDYNIX_APPROVALS)/通配
  SubjectApprovalAll/消费者 ConsumerApprovals + checkpoint 桶 BucketCheckpoints。
- bus: EnsureApprovalStream(JetStream 流持久捕获 sundynix.approval.>,MaxAge 24h;
  gateway 现有 nc.Publish 的决定被本流自动捕获,无需改 gateway)+ ConsumeApprovals
  (持久消费者,队列组多副本安全)。
- orchestrator: HandleApprovalDecision(据 task_id 取 resume 记录续跑;无记录则忽略,
  兼容阻塞态任务的决定 + 决定重投幂等)+ finishResumed(收尾对齐 Handle 尾段:
  中断/拒绝/预算/失败/成功+评测落历史)。
- main: 开 checkpoint 存储 + 审批流 + 起决定消费者 → SetCheckpoints 启用中断模型;
  任一步失败优雅降级回阻塞模型;停机 drain 在途 resume。

决定经 JetStream 持久:dispatcher 在决定发出时离线,重连后仍消费到并续跑;任一
dispatcher 副本都能据共享 KV 的 checkpoint+记录恢复(HA)。
测试:决定驱动恢复(续跑下游+判 done+清记录)、无记录忽略(幂等/兼容)。
全模块 go build + go test 绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 13:08:41 +08:00
Blizzard 368938ce89 feat(hitl): 增量3a —— resume 重入闭环(从 checkpoint 续跑审批)
把 HITL 的恢复半边补上:人工决定到达后从 checkpoint 重入图、喂给审批节点续跑。
至此中断→恢复全闭环在 compose 路径打通(in-process 端到端测试钉死)。

- runComposeGraph 重构为统一入口 execComposeGraph(rc):rc==nil 全新执行、rc!=nil
  从 checkpoint resume,两路共用建图/编译,仅三处分叉——①记忆注入仅 fresh(resume
  时黑板由 checkpoint 还原,重注入会覆盖已积累态);②Invoke ctx(resume 经
  ResumeWithData 注入决定);③终态黑板来源。
- live 捕获:resume 时 compose 用 checkpoint 还原的实例作 local state(非闭包 b),
  故节点 ProcessState 内捕获 live 指针,Invoke 后据此读终态(fresh 仍读 b)。
- resume 记录:中断时把 {interruptID, Task} 落 KV(pending:task_id),供决定在另一
  goroutine/重启进程独立重建任务并续跑;终态清记录 + checkpoint(幂等)。
- ResumeApproval(t, dec) 入口:载记录定位中断点 → execComposeGraph resume。续跑
  语义同 fresh:再遇审批→errInterrupted、批准跑完→稿+refs、拒绝→errRejected。

测试:批准(黑板无损还原 + 下游执行 + 状态拨回 running + 清记录/checkpoint)、
拒绝(errRejected + 拒绝语 + 下游不跑 + 清 checkpoint)。这条用例也透过 eino 真实
checkpoint 路径端到端验证了 board 序列化(2a)。全量 go test ./... 绿。

下一步 3b:审批决定改 JetStream 持久投递 + orchestrator 持久消费者触发 ResumeApproval
+ main 接 Bus.Checkpoints 打开开关。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:57:07 +08:00
Blizzard bc7600625d feat(hitl): 增量2b —— 审批节点改 compose.Interrupt + orchestrator 识别中断
接了 checkpoint 后端时,审批节点从「阻塞 goroutine 等 5min」改为持久化中断:
首次执行发待审 + 置 waiting + compose.Interrupt → compose 把整图状态(含 board)
落进 checkpoint store 并返回中断错误 → Handle 释放 goroutine、任务停在 waiting,
不收尾不评测不判 done。抗 dispatcher 重启。

- orchestrator: 新增 errInterrupted 哨兵 + checkpoints 字段 + SetCheckpoints
  setter(沿用 guard/usageSink 的 setter 注入,不动构造签名);Handle 识别
  errInterrupted → 释放 goroutine、保留 waiting、SSE 流不关。
- compose_compiler: 编译挂 WithCheckPointStore + WithGraphName("root"),Invoke
  带 WithCheckPointID(task_id);审批节点接 checkpoint 时改走专用
  approvalInterruptLambda(须把中断错误作节点返回值上抛,泛型 lambda 会吞掉);
  ExtractInterruptInfo 识别中断 → 上抛 errInterrupted。
- graph.go: 抽出 approvalSummary / applyApprovalDecision,阻塞式与中断式审批共用,
  杜绝两路文案/语义漂移。

双路径并存:未接 checkpoint 后端(store=nil)维持阻塞模型,行为不变;main 暂不
接,生产保持阻塞,待增量3 resume 闭环补齐再打开(建在 flag 后)。

测试:中断半边端到端——errInterrupted + 置 waiting + checkpoint 落 KV(key=task_id)
+ 下游 agent 不执行。既有阻塞式审批/等价测试全绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:46:48 +08:00
Blizzard 03225e31a9 refactor(dispatcher): compose 翻默认 —— 补平三缺口后退役 graph.go 在望
EINO_COMPOSE 默认改为开(留 =0 逃生舱回退权威 graph.go)。翻默认前补平
compose 路径三个会静默吃掉治理能力的缺口:

- HITL 审批:execDSLNode 无 approval 分支 → 审批节点被当未识别跳过、下游照跑;
  补 case 调 approvalNode。
- 忠实度评测:executeGraph 把 refs 硬写 nil → 评测静默失效;runComposeGraph
  改签名回传 refsOf(b)。
- 终态传播:rejected/fatalErr 不传播也不阻断下游 → 任务误判 done-空;加节点
  lambda 入口守卫 + branch cond 守卫 + 终态上抛 errRejected/fatalErr,并让
  runComposeConversation 模型失败置 fatalErr(对齐 graph.go 的 break 语义)。

新增等价测试:审批拒绝停下游、refs 回流。go test ./... 全绿。
soak 无回归后即可物理退役 graph.go。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:13:46 +08:00
Blizzard 446b784fc6 feat(harness): RAG 忠实度评测 —— judge 拿检索原文评幻觉
补 Harness 已知洞:此前 LLM-judge 只看 input+output,看不到检索来源,幻觉其实没评。

- Evaluator.Score 增 sources 参数;有来源时走 llmJudgeGrounded:一次调用同时评 quality 质量 +
  faithfulness 忠实度,并列出 unsupported(未被来源支持的说法)→ 进 Flags。
  综合分(有来源)=0.3规则+0.35质量+0.35忠实;无来源时维持原 0.4规则+0.6质量。Result 增 Faithful 字段。
- 透传检索来源:runGraph 返回 (answer, refs, err),executeGraph 同步;Handle→evaluate(input,output,refs);
  refsOf(board)=检索资料+工具产出。compose 路径暂返回 nil refs(不评忠实度)。
- eval 日志增「忠实 X.XX,来源 N」。

测试:单测覆盖 grounded(quality/faithfulness/unsupported 解析 + 加权 + flags)与无来源跳过;
3 处测试 runGraph 三返回值更新。live 实测 RAG 任务忠实 1.00/来源 1,judge 正确判定无编造。
project_analysis Harness 清单勾掉该项。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:03:17 +08:00
Blizzard 2e927eca0a feat(dispatcher): Eino 采纳 Phase C 完成 —— 全图 DSL→compose.Graph 编译器
把整张 DSL 图编译为 Eino compose.Graph 执行(编排归一):
- compose_compiler.go:每节点=Lambda,节点体复用 execDSLNode(全节点类型);
  黑板进 compose 本地状态(WithGenLocalState + ProcessState);branch 走
  AddBranch + 状态感知条件(复用 branchNode);边载荷 flowSignal(注册 no-op
  合并支持 fan-in,真实数据全走黑板)。
- WithNodeTriggerMode(AllPredecessor) DAG 模式:无依赖节点并行调度(效率)。
- Handle→executeGraph 按 EINO_COMPOSE 开关选 compose/graph.go;compose 编译
  失败自动降级回 graph.go(安全网)。默认关,graph.go 仍权威。

等价回归:线性图 + 分支图经解释器与 compose 两路径产出逐字一致(单测);
live 多节点分支图 compose 路径 2800 字答复 eval 1.00、FSM done、0 幽灵;
make test-go 全绿。

过渡期 soak 后翻默认到 compose、退役 graph.go。性能后续:编译图按 DSL-hash 缓存。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 11:02:32 +08:00