cbd130ecae
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>
66 lines
2.8 KiB
Bash
Executable File
66 lines
2.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 无 Docker 的最小任务流演示:devnats(内嵌NATS) + gateway + dispatcher + mcp-go。
|
||
# 提交一个 DSL 任务,验证 Gateway → NATS → Dispatcher → MCP 工具 全链路。
|
||
set -euo pipefail
|
||
cd "$(dirname "$0")/.."
|
||
|
||
mkdir -p .bin
|
||
echo "== 编译 =="
|
||
( cd sundynix-shared && go build -o ../.bin/devnats ./cmd/devnats )
|
||
( cd sundynix-gateway && go build -o ../.bin/gateway ./cmd/server )
|
||
( cd sundynix-dispatcher && go build -o ../.bin/dispatcher ./cmd/dispatcher )
|
||
( cd sundynix-mcp-go && go build -o ../.bin/mcp-go ./cmd/server )
|
||
|
||
cleanup() { kill "${GW_PID:-}" "${DISP_PID:-}" "${MCP_PID:-}" "${NATS_PID:-}" 2>/dev/null || true; }
|
||
trap cleanup EXIT
|
||
|
||
# 若 :4222 已有 NATS(docker compose 的容器),直接复用;否则起内嵌 devnats。
|
||
if nc -z 127.0.0.1 4222 2>/dev/null; then
|
||
echo "== 检测到已运行的 NATS(:4222),复用之 =="
|
||
else
|
||
echo "== 启动内嵌 devnats =="
|
||
.bin/devnats > .bin/devnats.log 2>&1 & NATS_PID=$!
|
||
for _ in $(seq 1 30); do nc -z 127.0.0.1 4222 2>/dev/null && break || sleep 0.2; done
|
||
fi
|
||
|
||
echo "== 启动 mcp-go / dispatcher / gateway =="
|
||
.bin/mcp-go > .bin/mcp-go.log 2>&1 & MCP_PID=$!
|
||
.bin/dispatcher > .bin/dispatcher.log 2>&1 & DISP_PID=$!
|
||
.bin/gateway > .bin/gateway.log 2>&1 & GW_PID=$!
|
||
|
||
for _ in $(seq 1 30); do
|
||
curl -s -o /dev/null http://127.0.0.1:8080/api/v1/billing && break || sleep 0.3
|
||
done
|
||
|
||
# 等 mcp-go 订阅就绪后再提交,否则工具调用会撞上启动竞态而降级。
|
||
for _ in $(seq 1 30); do
|
||
grep -q "tools ready" .bin/mcp-go.log 2>/dev/null && break || sleep 0.1
|
||
done
|
||
|
||
USER="wt"
|
||
echo "== 登记用户偏好记忆 (→ mcp-go memory_upsert → sundynix_user_profile) =="
|
||
curl -s -X PUT http://127.0.0.1:8080/api/v1/memory \
|
||
-H 'Content-Type: application/json' -H "X-User-ID: $USER" \
|
||
-d '{"key":"称呼","value":"老王"}'; echo
|
||
curl -s -X PUT http://127.0.0.1:8080/api/v1/memory \
|
||
-H 'Content-Type: application/json' -H "X-User-ID: $USER" \
|
||
-d '{"key":"回答偏好","value":"简洁、中文、多给要点"}'; echo
|
||
|
||
echo "== 提交 DSL 任务 (带 X-User-ID,Dispatcher 将召回其画像) =="
|
||
RESP=$(curl -s -X POST http://127.0.0.1:8080/api/v1/tasks \
|
||
-H 'Content-Type: application/json' -H "X-User-ID: $USER" \
|
||
-d '{"nodes":[{"id":"n1","type":"agent","data":{"prompt":"hello"}}],"edges":[]}')
|
||
echo "$RESP"
|
||
TASK_ID=$(echo "$RESP" | sed -n 's/.*"task_id":"\([^"]*\)".*/\1/p')
|
||
|
||
echo "== 订阅 SSE Token 流 (Gateway ← NATS ← Dispatcher) =="
|
||
# 客户端在 TTFT(700ms) 内连上即可收全部 token;--max-time 超时(exit 28) 属正常,不让 set -e 中断
|
||
curl -sN --max-time 10 "http://127.0.0.1:8080/api/v1/tasks/$TASK_ID/stream" || true
|
||
echo
|
||
|
||
echo "== mcp-go 日志 (工具被调用) =="
|
||
cat .bin/mcp-go.log
|
||
|
||
echo "== dispatcher 日志 =="
|
||
cat .bin/dispatcher.log
|