11 Commits

Author SHA1 Message Date
Blizzard 034ee51b0d chore: 更新 go.work.sum(prometheus client 依赖)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:52:08 +08:00
Blizzard 149c35c21b feat(gateway): 真实鉴权片1 —— JWT 注册/登录 + 校验中间件(后端核心)
替掉"裸 X-User-ID 头当身份"的临时方案,落地无状态 JWT 鉴权后端:

- internal/auth:JWT 签发/校验(HS256,密钥 env JWT_SECRET,仅接受 HMAC 防 alg 混淆)
  + bcrypt 密码哈希/校验。纯包,含单测。
- User 模型加 Name + PasswordHash(json:"-" 不外泄);store 加 CreateUser/GetUserByEmail/
  GetUserByID(邮箱唯一冲突 → ErrUserExists)。
- handler/auth:POST /auth/register(建用户+签发)· POST /auth/login(校验+签发,
  用户不存在与密码错同一文案防枚举)· GET /auth/me。
- middleware/auth:解析 Bearer JWT,校验通过把已验证 userID 注入上下文(非阻断)。
- userID(c) 改为优先取 JWT 注入的 uid,兜底 X-User-ID 头(前端尚未接登录,保持可用)。

验证:
- 单测:JWT 签发/解析往返、过期拒绝、篡改/非法拒绝、bcrypt 哈希校验。
- 实跑(nats+pg+gateway):注册→token+user(无密码)、重复注册 409、错密码 401、
  /auth/me 带 token 200 / 无 token 401;owner 隔离改用已验证 uid —— 带 token 建的库
  匿名/伪造 header 都看不到(JWT 用户数据归于雪花 id,header 无法臆测)。

片 2 待做:前端登录页 + 存令牌带 Bearer + 处理 401 + 去掉 header 兜底 + 保护路由。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:14:21 +08:00
Blizzard 1dd6b0cce3 feat(report): 生成只出 Markdown 预览,导出时再渲染 Word/PDF/Markdown
把"渲染"从生成阶段解耦到导出阶段(导出时再处理):
- 生成阶段:报告正文按 Markdown 流式预览(前端 <Markdown> 已渲染),
  dispatcher 不再 eager 渲染 docx,改为经 mcp-go report_store 落盘报告源(title+sections JSON)。
- 导出阶段(按需现渲染):
  - GET /reports/:id/export?format=docx → mcp-go report_export 读源渲染 .docx;
  - ?format=md → 返回 Markdown 文本;
  - PDF → 前端把已渲染的 Markdown 送进打印视图出 PDF(CJK 零字体依赖)。
- 旧 /reports/:id/download 兼容保留(默认 docx)。

改动:
- contract: ReportSourcePath(id) = <id>.json。
- mcp-go: 新增 report_store / report_export 工具(report_render 保留给 Studio render 节点)。
- dispatcher: handleReport 末尾 renderReport → storeReport。
- gateway: DownloadReport → ExportReport(经 NATS 调 report_export)。
- 前端: ReportView 单个「Word」→「导出」组 Word/PDF/Markdown;
  desktop.printReportHtml 客户端打印;api.reportExportUrl。

实测(docker 全栈 + mcp-go + gateway + dispatcher + DeepSeek 真跑):
- 真实生成「绿茶的功效」18s 完成,report_store 落源(5章, 6280B) ✓
- export md 返回正确 Markdown(# 标题/## 小节/正文) ✓
- export docx 为合法「Microsoft Word 2007+」(含 document.xml/Content_Types) ✓
- 前端 tsc 干净 + 生产构建通过 ✓
(注:发现并修复一处环境问题——mcp-go 启动时若 Milvus 未起会阻塞在
 rag 初始化、永不订阅工具,导致所有 mcp-go 工具"no responders";起全栈后正常。
 报告生成在累积大量未完成 DeepSeek 流连接时会偶发卡顿,干净进程下正常。
 前端导出按钮的实时点击因 React 受控输入自动化限制未在预览中走通,非代码缺陷。)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:04:06 +08:00
Blizzard fd145b5852 feat(dispatcher): 编排引擎按图执行(拓扑+连线+分支剪枝),弃用线性拍平
旧 compileFlow 把 DSL 图拍平成线性 init→tool…→prompt→model,连线/分支/
memory/aggregate/render 节点全被忽略——"画得出、跑不全"。改为纯 Go 图解释器
(graph.go),按真实拓扑与连线执行,每种节点 kind 有真实行为:

- input     注入用户输入
- memory    按勾选注入画像/历史(无 memory 节点则沿用默认注入,不回归)
- retriever kb 按 owner 作用域 → kb_search 累计参考资料
- tool      调 MCP 工具,产出进黑板,失败降级不阻断
- agent     据黑板拼消息 → pool 流式回流 token,累计成稿
- aggregate 按策略合并参考资料(拼接/去重合并/摘要)
- render    把成稿经 report_render 渲染 docx
- branch    求值条件 + active-set 剪枝下游(边序约定 [true,false])
- map       占位(fan-out 暂串行,路线图 Phase 2)
- output    终端

全程逐节点点亮"运行·观测",token 流与记忆写回保持不变;报告 intent 走原专用
编排不动。compile.go 精简为只留 RunCtx/buildMessages/previewArgs。

实测(gateway+dispatcher+DeepSeek 实跑):
- input→agent→output 真实流式答复 ✓
- branch 条件 2>1 走分支A、1>2 走分支B(下游真被剪枝)✓
- memory 节点按勾选注入;exec 事件按新节点名(agent:a 等)回流 ✓
- 桌面端 Studio 载示例→运行:4节点3连线校验通过,检索节点 mcp-go 不在时
  优雅降级,agent 据空资料如实作答,输出/轨迹面板正常 ✓

路线图 Phase 2:map 真并行 fan-out + aggregate reduce 接上 report 那套;
前端给 branch 的边打 true/false 标签,使条件分支完全精确(当前靠出边顺序约定)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 11:42:29 +08:00
Blizzard f610d8d2da feat(kb): 大文档正文存 MinIO(PG 只留元数据+预览+对象键)
超过阈值(8000 字)的正文落对象存储,彻底解决十几万字文件塞 PG 的问题。

- internal/blob:minio-go 封装 Store(Open/Put/Get/Delete + Ready 降级);连不上则降级内联。
- docker-compose:milvus-minio 暴露 9000 端口供网关用作文档对象存储(bucket sundynix-docs)。
- main/router/handler:注入 blob.Store(env MINIO_*,默认 localhost:9000 minioadmin)。
- runIngest:size>8000 且 MinIO 可用 → 正文 Put 到 owner/kb/name,PG content 置空仅存
  object_key+preview+size;否则内联。SaveDoc 改为按全文显式传 preview(offload 后内联为空也有预览)。
- KbDoc:object_key 非空时从 MinIO 取回全文。

验证:入 12182 字笔记 → PG content_len=0、object_key=wt/default/超大文件测试、preview 非空、
size=12182;/kb/doc 取回完整 12182 字(来自 MinIO);6321 字的仍内联(object_key 空)。
列表只读元数据+预览。gateway build 通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 17:02:44 +08:00
Blizzard 2291f60380 feat(desktop): 深色 AI 控制台视觉改造 + 工作台仪表盘
桌面端从扁平 dev 工具风改为深色高端 AI 控制台:分层表面 + 紫青强调 + 微光 +
首页仪表盘,提升第一印象与吸引力。

- 设计 tokens: tailwind ink 分层调色板 + glow/card 阴影; index.css 深色基底 +
  品牌渐变 + 深色滚动条
- shell: TopBar(品牌渐变+毛玻璃+发光健康灯) / LeftNav(激活态紫色高亮+左光条) /
  BottomDrawer(深色+状态色)
- 新 views/Home 工作台仪表盘: 渐变 hero + 4 状态卡 + 3 能力卡 + 快速开始(默认首页)
- 画布: TypedNode 深色节点卡; StudioView ReactFlow colorMode=dark + 深色工具栏/面板/
  MiniMap; Inspector 深色表单
- KbView/MemoryView/MemoryPanel/Placeholder 全深色化; 进度条改紫青渐变
- 纯前端改造, npm build✓; 浏览器验证: 仪表盘 + 编排画布深色呈现

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 16:56:04 +08:00
Blizzard 85a5c2c1e7 feat(rag): 混合检索融合 — Milvus 向量 + Bleve 全文 + RRF + DashScope rerank
检索从向量单路升级为混合:向量(Milvus) + 全文(Bleve BM25) → RRF 融合 →
可选 rerank(DashScope gte-rerank)。

- rag/bleve.go: Bleve 全文索引(内存,随 ingest 写入;kb 过滤);ingest 同步写 Milvus+Bleve
- rag/fuse.go: RRF(Reciprocal Rank Fusion, k=60, 按文本去重)融合多路排序
- rag/rerank.go: DashScope gte-rerank 客户端(可选,env 配置,失败降级 RRF)
- rag/rag.go: Search 改混合(向量+全文→RRF→可选rerank→topK);main 读 RERANK_* env
- 验证: 全模块 build✓ + e2e PASS; live——入库写双索引;查'NATS'→全文精确命中#1+向量
  →RRF NATS 排首(向量=4 全文=1);接 DashScope gte-rerank(百炼 key 有权限)→relevance
  score 0.19 真重排;retriever 节点端到端→DeepSeek 答 Milvus
- 边界: Neo4j 图路(GraphRAG,需实体抽取)推迟;Bleve 内存索引重启重建;rerank 走 env
  (TODO 同 embedding 搬控制面 kind=rerank)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 09:53:37 +08:00
Blizzard 84d1a1dd3a feat: RAG 核心链 — embedding(provider) + Milvus 真连 + 入库/检索
mcp-go 接通向量 RAG:embedding(OpenAI 兼容 provider 抽象) + Milvus 真实连接,
kb_ingest 入库、wiki_search 真检索。retriever 节点一行不改即从桩变真。

- mcp-go internal/rag: embed.go(OpenAI 兼容 /embeddings 客户端) + milvus.go(milvus-sdk-go
  真连,集合按首次 embedding 维度懒建+AUTOINDEX/COSINE索引+加载,insert/向量search) +
  rag.go(Engine: 切块→embed→insert / embed query→search;embedding 或 Milvus 缺则降级)
- mcp-go gateway: 新工具 kb_ingest,wiki_search 换真(RAG 向量检索,kb 过滤 topK)
- mcp-go main: rag.Open 读 MILVUS_ADDR/EMBED_BASE_URL/EMBED_API_KEY/EMBED_MODEL 环境变量
- gateway: POST /api/v1/kb/ingest → kb_ingest(供知识库页/脚本)
- scripts/mock_embeddings.py: 确定性词法向量(字+bigram 哈希),无真 key 验证检索
- 开发期 embedding 接在线 API(无真 key 用 mock),见 llm-provider-strategy
- 验证: 全模块 build✓ + e2e PASS; live——入库5条→Milvus;retriever 节点查'向量数据库'
  →召回 Milvus 那条→DeepSeek 答'Milvus';查'知识图谱'→Neo4j(向量检索区分正确)

注: 当前向量单路;Bleve/Neo4j 融合 + rerank + 真实语义 embedding 为后续。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 17:07:36 +08:00
Blizzard cbd130ecae feat: 第一张真实 Eino 图 + 偏好记忆(让模型知道是我)
dispatcher 不再手搓 pool.Stream,改用编译好的 Eino 图驱动;接入用户常驻画像,
推理前召回并注入 system prompt,实现个性化(架构'心脏'首次真跳)。

Eino 图(dispatcher/internal/eino): START→recall→prompt→model→END + 全局 State
- recall(Lambda): 取 Meta[user_id] → 调 MCP memory_get → ProcessState 写画像
- prompt(ChatTemplate): {profile} 注入 system,{query} 作 user
- model: poolModel 适配 LLM Pool 为 model.BaseChatModel(Generate+Stream, schema.Pipe)
- 写回: 流排空后异步 memorize(流式节点走 OnEndWithStreamOutput 非 OnEndFn)

记忆存储(mcp-go owns): GORM Profile→sundynix_user_profile(复合主键, AutoMigrate,
遵守前缀约定), 新工具 memory_get/memory_upsert, 连不上降级
Gateway: SubmitTask 注入 Meta[user_id](X-User-ID 头), PUT /api/v1/memory→memory_upsert
shared: contract.MetaUserID; llm.Pool 拆出 StreamText

验证: 4 模块 build✓ + 3 e2e PASS; live 跑通——PUT 偏好落 sundynix_user_profile,
带 X-User-ID 提交→Eino recall 召回→注入→SSE 流出含画像的个性化回答, writeback 触发

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 14:06:18 +08:00
Blizzard e5fa0ae36c feat: Gateway store 桩换真实 GORM/Postgres + go-redis (含自动迁移与优雅降级)
第 2 层网关持久层落地,遵守 sundynix_ 表名前缀 + AutoMigrate 约定。

- store: GORM(NamingStrategy 前缀 sundynix_/单数) → User=sundynix_user, Task=sundynix_task
  启动 AutoMigrate;go-redis/v9 滑动窗口限流(Incr+Expire,按 IP)
- 优雅降级:连不上库则 warn 继续(不 fatal),保证无 Docker 的 make demo 仍跑通
- handler: SubmitTask 持久化任务(best-effort),Billing 真实读库返回 tasks_submitted
- main: OpenPostgres/OpenRedis 读 POSTGRES_DSN/REDIS_ADDR 环境变量
- 验证: 4 模块 build ✓;e2e 3 测试 PASS;live 双路径(真实库持久化 + 坏DSN降级)实测通过

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 11:43:53 +08:00
Blizzard c7a02c3905 feat: 初始化 sundynix-agentix 分层式 AI Agent 平台脚手架
5 层 + 1 条 NATS 零拷贝消息总线的 monorepo(Monolith First → Microservices Morph B)。
纵向主干(任务流 + Token 流回流)已真实跑通,横向各层能力为带注释的桩。

已贯通(real code):
- sundynix-shared: 共享契约 + JetStream/core NATS 真实收发(bus) + 内嵌 NATS(devnats) + e2e 测试
- sundynix-gateway: Gin 接入 + DSL 解析组装 + NATS Publish + SSE 流式输出
- sundynix-dispatcher: NATS 消费 + Eino Orchestrator 流式回流 + 熔断器 + LLM Pool 占位流式
- 链路: HTTP POST → DSL → sundynix.tasks.* → Dispatcher → Token 经 sundynix.streams.<id> 回流 → SSE
- 基础设施: docker-compose(nats/postgres/redis/neo4j/milvus) + Makefile(make demo/e2e)

待填(桩):
- Eino 图编排 compose.NewGraph、LLM Pool 接 vLLM/Ollama
- Gateway store 换真实 pgx/redis
- sundynix-mcp-go: Bleve+Milvus+Neo4j 混合检索 / UniOffice / 外部 API
- sundynix-mcp-py: gVisor 沙箱 / MinerU(PaddleOCR) / Docker 解释器
- sundynix-desktop: React Flow 画布 → DSL 导出 → SSE 展示
2026-06-10 11:00:29 +08:00