# Loomflow-demo **Repository Path**: yaomd/Loomflow-demo ## Basic Information - **Project Name**: Loomflow-demo - **Description**: Loomflow-demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-19 - **Last Updated**: 2026-10-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Loomflow 一个 Dify 风格的工作流编排 MVP。用 JSON 描述节点和边,发布修订,再编译成 Spring AI Alibaba **StateGraph** 执行。LLM 走 AgentScope Java 的 OpenAI 兼容客户端;没有 API Key 时自动使用 mock,不会访问外网。 设计说明见 [DESIGN.md](DESIGN.md)。 ## English JSON graph in, `CompiledGraph` out. Stack: Java 17, Spring Boot 3.5, `spring-ai-alibaba-graph-core` `1.1.2.4-security-fix`, AgentScope Java `2.0.4` (`agentscope-extensions-model-openai`). H2 by default. Mock LLM when `OPENAI_API_KEY` is empty. ```bash mvn test mvn -pl loomflow-app -am package -DskipTests java -jar loomflow-app/target/loomflow-app.jar ``` Open http://127.0.0.1:47123 `spring-boot:run` has to target the app module alone. The parent POM is an aggregator and has no main class: ```bash mvn -pl loomflow-app -am install -DskipTests mvn -pl loomflow-app spring-boot:run ``` ## 环境 - JDK 17+(本仓库用 17 字节码编译,21 也可以跑) - Maven 3.8+ ## 启动 ```bash mvn -pl loomflow-app -am package -DskipTests java -jar loomflow-app/target/loomflow-app.jar ``` 浏览器打开 。首次启动会导入 `samples/` 里的三张已发布示例图。数据在启动目录下的 `data/`(H2 文件库),删掉该目录即可重置。 也可以只对 app 模块使用插件(先 install,避免父 POM 被 `spring-boot:run` 扫到): ```bash mvn -pl loomflow-app -am install -DskipTests mvn -pl loomflow-app spring-boot:run ``` 只跑测试(含图编译、if-else 路由、API): ```bash mvn test ``` 测试强制 `loomflow.llm.mode=mock`,不需要密钥。 ### 环境变量 | 变量 | 默认 | 作用 | | --- | --- | --- | | `LOOMFLOW_LLM_MODE` | `auto` | `auto` / `mock` / `agentscope` | | `OPENAI_API_KEY` | 空 | 空则 auto 使用 mock | | `OPENAI_BASE_URL` | `https://api.openai.com/v1` | OpenAI 兼容端点,例如 DeepSeek、vLLM | | `OPENAI_MODEL` | `gpt-4o-mini` | 节点未写 `model` 时的默认模型 | | `LOOMFLOW_LLM_TIMEOUT_SECONDS` | `60` | AgentScope 调用超时 | 真实模型示例: ```bash export OPENAI_API_KEY=sk-... export OPENAI_BASE_URL=https://api.openai.com/v1 export OPENAI_MODEL=gpt-4o-mini mvn -pl loomflow-app -am package -DskipTests java -jar loomflow-app/target/loomflow-app.jar ``` `GET /api/health` 的 `llmProvider` 为 `mock` 或 `agentscope-openai`。 ### Docker(可选) ```bash docker compose up --build ``` 容器默认 mock 模式。要接真实模型,在 `docker-compose.yml` 里加上 `OPENAI_API_KEY`。 ### MySQL(可选) ```bash mvn -pl loomflow-app -am package -DskipTests java -jar loomflow-app/target/loomflow-app.jar --spring.profiles.active=mysql ``` 对应配置在 `loomflow-app/src/main/resources/application-mysql.yml`。变量:`MYSQL_HOST`、`MYSQL_PORT`、`MYSQL_DATABASE`、`MYSQL_USER`、`MYSQL_PASSWORD`。 H2 控制台: ,JDBC URL `jdbc:h2:file:./data/loomflow`,用户 `sa`,密码空。 ## 示例图 | 文件 | 做什么 | | --- | --- | | `samples/sequential-llm-chain.json` | start → 改写 LLM → 回答 LLM → answer → end | | `samples/if-else-branch.json` | 含「退款」或 `refund` 走退款节点,否则走通用节点 | | `samples/knowledge-chatflow.json` | 知识检索 stub + iteration stub + LLM | ## curl 下面用 Python 取 id,避免依赖 jq。服务地址按实际修改。 ```bash BASE=http://127.0.0.1:47123 curl -s $BASE/api/health # 找到已发布的条件分支示例 ID=$(python3 - <<'PY' import json,urllib.request rows=json.load(urllib.request.urlopen("http://127.0.0.1:47123/api/workflows")) print(next(row["id"] for row in rows if row["sampleKey"]=="if-else-branch")) PY ) # 创建一次运行(立即返回 QUEUED) RUN=$(curl -s -X POST $BASE/api/workflows/$ID/runs \ -H 'content-type: application/json' \ -d '{"inputs":{"query":"我要退款"}}') echo "$RUN" RUN_ID=$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["id"])' "$RUN") # 轮询直到结束 curl -s $BASE/api/runs/$RUN_ID # 或者看 SSE(结束后连接会关闭) curl -N $BASE/api/runs/$RUN_ID/events ``` 新建、发布、再跑一条顺序链: ```bash cat > /tmp/loomflow-body.json <<'JSON' { "name": "我的顺序链", "description": "本地试跑", "mode": "workflow", "graph": { "schemaVersion": "1.0", "mode": "workflow", "nodes": [ {"id":"start","type":"start","title":"开始","data":{"inputs":[{"name":"query","type":"string","required":true}]}}, {"id":"rewrite","type":"llm","title":"改写","data":{"systemPrompt":"Rewrite the user question into one clear sentence.","userPrompt":"{{query}}","outputKey":"rewritten"}}, {"id":"answer","type":"answer","title":"回复","data":{"template":"{{rewritten}}"}}, {"id":"end","type":"end","title":"结束","data":{"outputs":[{"name":"answer","value":"{{answer}}"}]}} ], "edges": [ {"id":"e1","source":"start","target":"rewrite"}, {"id":"e2","source":"rewrite","target":"answer"}, {"id":"e3","source":"answer","target":"end"} ] } } JSON curl -s -X POST $BASE/api/workflows -H 'content-type: application/json' --data-binary @/tmp/loomflow-body.json ``` 响应里的 `id` 和 `revisionId` 接着用: ```bash curl -s -X POST $BASE/api/workflows/$WF/revisions/$REV/publish curl -s -X POST $BASE/api/workflows/$WF/runs \ -H 'content-type: application/json' \ -d '{"inputs":{"query":"运费怎么算"}}' ``` Chatflow: ```bash curl -s -X POST $BASE/api/workflows/$ID/chat \ -H 'content-type: application/json' \ -d '{"query":"运费怎么算"}' ``` ## API 一览 | 方法 | 路径 | 作用 | | --- | --- | --- | | GET | `/api/health` | 引擎与 LLM 模式 | | GET/POST | `/api/workflows` | 列表 / 创建 | | GET/PATCH/DELETE | `/api/workflows/{id}` | 详情 / 改名称 / 删除 | | POST | `/api/workflows/{id}/revisions` | 新草稿 | | POST | `/api/workflows/{id}/revisions/{revisionId}/publish` | 发布(旧的已发布修订会回到草稿) | | POST | `/api/workflows/{id}/revisions/{revisionId}/compile` | 只编译,不执行 | | POST | `/api/workflows/{id}/runs` | 异步运行 | | GET | `/api/workflows/{id}/runs` | 最近运行 | | GET | `/api/runs/{id}` | 轮询状态、输出、节点轨迹 | | GET | `/api/runs/{id}/events` | SSE | | POST | `/api/workflows/{id}/chat` | Chatflow 一轮 | | GET | `/api/conversations/{id}` | 会话消息 | 未指定 `revisionId` 时,运行使用最新已发布修订;如果还没有发布,则用最新草稿。 ## 仓库结构 ``` pom.xml DESIGN.md samples/ 示例图 loomflow-definition/ 图模型与校验 loomflow-engine/ StateGraph 编译器、节点、LLM loomflow-app/ Spring Boot、H2、静态页 ``` 依赖坐标(与父 POM 一致): - `com.alibaba.cloud.ai:spring-ai-alibaba-graph-core:1.1.2.4-security-fix` - `io.agentscope:agentscope-extensions-model-openai:2.0.4` - Spring Boot `3.5.8` ## 实现与 stub 已实现:定义 CRUD、修订发布、JSON → StateGraph、start / llm / if-else / answer / end、运行轨迹、SSE 与轮询、Chatflow 会话、mock 与 AgentScope 两种 LLM。 Stub: - `knowledge-retrieval`:内存关键词重叠,不是向量检索 - `iteration`:按条渲染模板,不是嵌套子图 - `http-request`:真实 HTTP,但默认拒绝内网地址;`mock://` 不走网络 没有回边、并行扇出、人工节点、代码节点。细节和替换 AgentScope 1.x `asNode()` 的方式写在 DESIGN.md。