# AI 工作流使用指南

> 知识图谱（Knowledge Graph）工作流引擎：用节点+链接构建数据处理流水线，支持 AI Agent、Python 代码、条件分支、API 调用等 7 种节点类型。

---

## 目录

1. [快速开始](#1-快速开始)
2. [LLM 配置（Agent 节点必读）](#2-llm-配置agent-节点必读)
3. [节点类型速查](#3-节点类型速查)
4. [数据流链接机制](#4-数据流链接机制)
5. [长文本属性与 code 编辑](#5-长文本属性与-code-编辑)
6. [使用案例](#6-使用案例)
   - [案例 1：简单计算链](#案例-1简单计算链)
   - [案例 2：多分支数据处理](#案例-2多分支数据处理)
   - [案例 3：条件分支](#案例-3条件分支)
   - [案例 4：API 数据接入](#案例-4api-数据接入)
   - [案例 5：AI Agent 智能分析](#案例-5ai-agent-智能分析)
7. [代码树项目隔离](#7-代码树项目隔离)
8. [如何创建可导入图谱的 Agent 工作流项目](#8-如何创建可导入图谱的-agent-工作流项目)
9. [关键约定与常见问题](#9-关键约定与常见问题)

---

## 1. 快速开始

1. 打开知识图谱页面 → 工具栏「添加/搜索」创建节点
2. 编辑节点：在节点详情面板中修改名称、类型、标签、属性
3. 用「长文本属性」给节点添加 `code`（代码/数据/prompt）
4. 拖拽节点之间的链接，设置关系类型为 `data-flow`
5. 点击「▶️ 执行工作流」或节点面板的「▶️ 执行此节点」

---

## 2. LLM 配置（Agent 节点必读）

**Agent 节点使用用户自己的 LLM 配置独立调用。执行 Agent 节点前会弹出确认框，告知将消耗您的 API 额度。**

### 配置入口

点击工具栏 **「⚙️ LLM 配置」** 按钮，在弹出的配置面板中填写：

| 配置项 | 说明 | 示例 |
|--------|------|------|
| Base URL | LLM API 地址（兼容 OpenAI 接口） | `https://api.deepseek.com` |
| API Key | API 密钥 | `sk-...` |
| 模型 ID | 模型名称（请查看服务商文档获取最新模型） | `deepseek-chat`、`qwen-plus`、`glm-4` 等 |

配置保存到浏览器 localStorage，下次打开页面自动加载。

### 支持的 LLM 服务

任何兼容 OpenAI `/v1/chat/completions` 接口的服务均可使用：

| 服务商 | Base URL | 模型 ID 示例 |
|--------|----------|-------------|
| DeepSeek | `https://api.deepseek.com` | `deepseek-chat`、`deepseek-v4-flash` |
| 豆包 | `https://ark.cn-beijing.volces.com/api/v3` | `doubao-seed-lite` |
| OpenAI | `https://api.openai.com` | `gpt-4o`、`gpt-4.1` 等 |
| 通义千问 | `https://dashscope.aliyuncs.com/compatible-mode` | `qwen-plus`、`qwen-max` |
| 智谱 GLM | `https://open.bigmodel.cn/api/paas/v4` | `glm-4`、`glm-4-flash` |

---

## 3. 节点类型速查

| 类型 | 图标 | 颜色 | 用途 | 执行方式 |
|------|------|------|------|---------|
| （普通） | ● | 蓝 | 通用知识节点，仅作为结构存在 | ❌ 不执行 |
| 🤖 Agent | 🤖 | 红 | AI 智能体，code 作为 prompt 发给用户配置的 LLM | 用户 LLM 独立调用 |
| 💻 代码 | 💻 | 绿 | Python 脚本，支持函数定义、控制流 | Python exec + AST |
| 📊 数据源 | 📊 | 橙 | 数据载荷（JSON/列表/数字/字符串） | 直接传递 |
| ❓ 条件 | ❓ | 紫 | 条件判断，返回 true/false 分支 | Python 表达式求值 |
| 📤 输出 | 📤 | 青 | 汇总上游结果，支持 `{{result_xxx}}` 模板 | 模板替换 |
| 🔗 API | 🔗 | 灰 | 调用外部 HTTP API | urllib GET |

---

## 4. 数据流链接机制

### 链接类型

| 关系类型 | 语义 | 是否传递数据 |
|---------|------|------------|
| `data-flow` / `数据流` | 上游结果注入下游 | ✅ |
| `call` / `调用` | 上游结果注入下游 | ✅ |
| 其它（如 `关联`、`属于`） | 仅结构关系 | ❌ |

### 上游结果引用

下游节点的 code 中用 `result_<节点名>` 引用上游结果。

```
# 上游节点名为「数据源」，code = "42"
# 下游节点中：
result_数据源    # = 42
```

### 数据流传递过程

```
[数据源] --data-flow--> [代码节点] --data-flow--> [输出]
   ↓                        ↓                        ↓
 执行返回 42             result_数据源 = 42          {{result_代码}} → 渲染
                        42**2 + 10 = 1764
```

**单节点执行**也会自动收集上游结果：点击某个节点的「▶️ 执行此代码」时，系统自动按拓扑顺序执行所有上游节点后再执行当前节点。

---

## 5. 长文本属性与 code 编辑

### 添加属性

节点详情面板 → 「📝 长文本属性」按钮 → 输入属性名和值

- 常用属性名：`code`、`prompt`、`指令`、`描述`
- 后端执行时按顺序读取第一个非空值：`code` → `prompt` → `指令` → `代码`
- 支持 Markdown 语法，可在预览区查看渲染效果
- **空值移除**：属性值留空保存 = 删除该属性

### 代码节点语法

- 支持 `def` 函数定义、`if/for/while` 控制流
- 最后一行如果是表达式，其值作为返回值传给下游
- 仅开放安全内置函数：`print`、`len`、`range`、`sum`、`max`、`min`、`sorted`、`math.*` 等
- 禁止 `import`（除 `math`）、`open`、`subprocess` 等危险操作

### Agent 节点 prompt

Agent 节点的 `code` 属性直接作为 prompt 发给用户配置的 LLM：

```python
请基于以下信息给出分析：
数据: {{result_数据源}}
```

---

## 6. 使用案例

### 案例 1：简单计算链

**目标**：输入数字 → 平方 → 加偏移 → 输出

#### 图谱结构
```
[数据源] --data-flow--> [代码] --data-flow--> [输出]
```

#### 节点配置

| 节点 | 类型 | code 属性 |
|------|------|----------|
| 数据源 | 📊 数据源 | `42` |
| 计算 | 💻 代码 | `result_数据源 ** 2 + 10` |
| 输出 | 📤 输出 | `计算结果: {{result_计算}}` |

#### 执行结果
```
数据源 → 42
计算 → 1774
输出 → 计算结果: 1774
```

---

### 案例 2：多分支数据处理

**目标**：两个数据源各自统计 → 汇总比较 → 输出结论

#### 图谱结构
```
[数据A] --data-flow--> [统计A] --data-flow--> [汇总] --data-flow--> [输出]
[数据B] --data-flow--> [统计B] --data-flow---/
```

#### 节点配置

- **数据A**（📊 数据源）：`[5, 12, 8, 3, 9]`
- **数据B**（📊 数据源）：`[7, 4, 15, 2, 10]`
- **统计A**（💻 代码）：
  ```python
  def stats(data):
      return {'total': sum(data), 'avg': sum(data)/len(data), 'max': max(data)}
  stats(result_数据A)
  ```
- **统计B**（💻 代码）：同上，改为 `result_数据B`
- **汇总**（💻 代码）：
  ```python
  a, b = result_统计A, result_统计B
  print('A组:', a, 'B组:', b)
  'A更高' if a['avg'] > b['avg'] else 'B更高'
  ```
- **输出**（📤 输出）：`结论: {{result_汇总}}`

---

### 案例 3：条件分支

**目标**：根据分数判断是否及格

#### 图谱结构
```
[分数] --data-flow--> [条件] --data-flow--> [结果输出]
```

#### 节点配置

| 节点 | 类型 | code |
|------|------|------|
| 分数 | 📊 数据源 | `85` |
| 条件 | ❓ 条件 | `result_分数 >= 60` |
| 结果 | 📤 输出 | `判断: {{result_条件}}` |

条件返回 `branch: true` 表示条件成立。

---

### 案例 4：API 数据接入

**目标**：调用 GitHub API 获取仓库信息 → 解析 → 输出

#### 图谱结构
```
[API] --data-flow--> [解析] --data-flow--> [输出]
```

#### 节点配置

- **API**（🔗 API）：属性中添加 `url` = `https://api.github.com/repos/python/cpython`、`method` = `GET`
- **解析**（💻 代码）：
  ```python
  import json as _json
  data = _json.loads(result_API)
  {'stars': data['stargazers_count'], 'forks': data['forks_count']}
  ```
- **输出**（📤 输出）：`⭐ {{result_解析}}`

> **注意**：API 节点超时 8 秒，响应截取 4096 字节。GET 请求自动将上游结果作为 query 参数。

---

### 案例 5：AI Agent 智能分析

**目标**：数据源 → AI Agent 分析 → 输出建议

#### 图谱结构
```
[销售数据] --data-flow--> [AI 分析] --data-flow--> [输出]
```

#### 节点配置

- **销售数据**（📊 数据源）：`Q2销售额：A产品120万，B产品85万，C产品200万`
- **AI 分析**（🤖 Agent）：
  ```
  请分析以下销售数据，指出表现最好的产品和需要关注的产品：
  {{result_销售数据}}
  用一段话简要回答。
  ```
- **输出**（📤 输出）：`AI 分析结论: {{result_AI_分析}}`

#### 执行前

确保已配置 LLM（工具栏「⚙️ LLM 配置」→ 填写 base_url / api_key / model）。

#### 执行结果

```
AI 分析 → "C产品以200万领先，A产品120万表现稳健，B产品85万需关注..."
输出 → AI 分析结论: C产品以200万领先...
```

---

## 7. 代码树项目隔离

> 代码树（Code Tree）支持按「项目」隔离脚本。不同项目的脚本存放在独立的子目录，互不干扰，且知识图谱可分别导入不同项目。项目分为**内置只读项目**和**用户临时项目**两类。

### 项目类型

| 类型 | 说明 | 是否可修改 | 有效期 |
|------|------|-----------|--------|
| 🔒 内置项目 | 平台预置，如 `default`（含 Agent 工作流示例） | ❌ 只读，不可直接修改/删除 | 永久 |
| ⏳ 用户项目 | 用户新建或复制副本所得 | ✅ 可编辑、同步到图谱 | 最新脚本修改后 30 分钟自动删除 |

> **内置项目只读**：要修改内置项目，必须先点击 **「📋 复制副本」** 创建可编辑副本，再在副本上编辑。
> **用户项目 30 分钟过期**：用户项目（含副本）在**最新一个脚本被修改后的 30 分钟**内有效，超时自动删除。请及时「💾 同步到图谱」，并在知识图谱中导入。

### 在代码树中管理项目

打开 `code-tree.html`，工具栏提供：

- **项目下拉框**：查看/切换当前项目（🔒 内置只读 / ⏳ 临时 30 分钟）
- **🔄 切换**：切换项目，自动从服务器加载该项目脚本
- **📋 复制副本**：把当前项目复制为可编辑副本（内置项目需先复制副本才能修改）
- **＋ 项目**：新建用户项目（30 分钟过期）

> 不同项目的脚本即使同名也互不干扰（存放在不同目录）。

### 同步到图谱

点击 **「💾 同步到图谱」** 会把**当前用户项目**的所有脚本保存到 `data/code_tree_scripts/<当前项目>/`，返回结果会显示项目名。

> 内置只读项目下「同步到图谱」按钮会被禁用，需先复制副本。

### 在知识图谱中按项目导入

点击工具栏 **「📂 从代码树导入」**：

1. 系统先列出所有代码树项目（标注 🔒 内置 / ⏳ 临时）
2. 选择要导入的项目（默认第一个）
3. 从该项目加载脚本，自动生成工作流节点

导入后的节点 `code` 属性格式为 **`项目名/脚本名`**（如 `default/kg_parser`），后端据此从对应项目目录加载脚本。

### code 节点引用跨项目脚本

手动添加代码节点时，code 属性支持两种格式：

- `脚本名` → 从默认项目加载（如 `kg_parser`）
- `项目名/脚本名` → 从指定项目加载（如 `default/kg_parser`）

### 脚本预览与跳转编辑

- 节点面板的脚本源码标题会跳转到代码树，并**自动携带项目参数**（`?script=xxx&project=yyy`），代码树会自动切换到该项目并加载脚本。
- 节点面板的 **✏️ 直接编辑** 保存时也会写回正确项目（内置项目保存会被拒绝，需先复制副本）。

---

## 8. 如何创建可导入图谱的 Agent 工作流项目

代码树项目 + 知识图谱可以编排**任意范式**的 Agent 工作流（ReAct、CoT 思维链、工具调用、Plan-and-Execute 等）。脚本怎么命名、怎么连，由你决定；系统只根据**文件名语义关键词**推断节点类型。下面以「ReAct Agent」作为演示例子，说明通用规则。

### 第 1 步：创建/复制项目

- **复用内置示例**：在代码树切换到 `default` 项目，点击 **「📋 复制副本」**，命名一个用户项目（如 `react-agent`）。副本是可编辑的用户项目。
- **从零开始**：点击 **「＋ 项目」** 新建，如 `data-pipeline`。

### 第 2 步：按「节点」拆分脚本

把工作流的每个环节写成一个独立 `.py` 脚本，**脚本名即节点名**。命名两条约定：

1. **`kg_` 前缀**（重要）：凡是"控制流"脚本（条件判断、输出、工具执行等**没有 `import` 依赖**的节点），脚本名**必须以 `kg_` 开头**。因为系统导入时会自动跳过"孤立脚本"（既不 import 别人、也不被别人 import），而控制流节点的连接靠图谱的 `call`/`control` 边表达、并不靠 Python import，所以必须用 `kg_` 前缀标记，才不会被当成孤立脚本跳过。数据/配置类脚本（`question`/`tools` 等）即使孤立也会作为数据节点保留，可不加前缀。

2. **语义关键词**（用于自动推断节点类型）：脚本名中含以下关键词，导入后自动映射为对应节点类型；不含任何关键词的默认归为「代码」节点：

| 文件名含有关键词 | 自动映射节点类型 |
|----------------|----------------|
| `condition` | ❓ 条件 |
| `output` / `final` | 📤 输出 |
| `agent` / `llm_call` / `llmcall` | 🤖 Agent |
| `api` | 🌐 API |
| `question` / `config` / `client` / `template` / `tools` | 📊 数据源 |
| 其他 | 💻 代码 |

> 前缀与关键词可以自由组合，**不绑定任何特定范式**。例如一个工具调用工作流可以命名 `kg_condition`、`kg_output`、`kg_tool_executor`、`tool_registry`；一个纯代码流水线可以全部命名为 `step1`、`step2`（默认代码节点）。

ReAct 演示项目脚本划分如下（数据类脚本可不加前缀，控制流脚本加 `kg_` 前缀）：

| 脚本文件 | 对应图谱节点类型 | 作用 |
|---------|----------------|------|
| `question.py` | 📊 数据源 | 问题输入，`print(question)` 输出问题 |
| `tools.py` | 📊 数据源 | 工具注册，`print(工具描述)` |
| `prompt_template.py` | 📊 数据源 | 提示词模板 |
| `llm_client.py` | 📊 数据源 | LLM 客户端配置 |
| `kg_prompt_builder.py` | 💻 代码 | 拼接完整提示词 |
| `kg_llm_call.py` | 🤖 Agent | 调用 LLM（输出 prompt 给前端） |
| `kg_parser.py` | 💻 代码 | 解析 LLM 输出的 Thought/Action |
| `kg_condition.py` | ❓ 条件 | 判断是否 Finish |
| `kg_tool_executor.py` | 💻 代码 | 执行工具 |
| `kg_output.py` | 📤 输出 | 输出最终答案 |

### 第 3 步：脚本间数据流通

脚本通过 `import` 引用同项目其他脚本（如 `kg_prompt_builder` 里 `import tools`）。图谱执行时，上游节点的结果以 `result_<脚本名>` 注入，脚本用 `globals().get('result_xxx')` 获取。

> 脚本作为 data/code 节点执行时，`print(...)` 的内容即节点结果，会传给下游。

### 第 4 步：同步到图谱

点击 **「💾 同步到图谱」**，脚本保存到 `data/code_tree_scripts/<项目名>/`。

### 第 5 步：在知识图谱导入

打开 `knowledge_graph.html` → 工具栏 **「📂 从代码树导入」** → 选择你的项目：

- 自动为每个脚本生成节点（按文件名关键词推断类型/颜色）
- 自动按 `import` 依赖生成 `data-flow`/`call` 连接
- `kg_` 前缀的孤立控制流节点不会被跳过（它们的 `condition`→`output`、`condition`→`tool_executor` 等 `control`/`call` 连接，需要在图谱中手动补连，或按工作流语义编排）

> **注意**：`import` 只能自动生成"数据依赖"连接（data-flow / call）。控制流连接（如"条件为真时走输出"）无法用 import 表达，需在知识图谱中把 `condition` 节点指向对应分支节点，并设置链接类型为 `control`/`call`。

### 第 6 步：配置并执行

1. 工具栏 **「⚙️ LLM 配置」** 填 base_url / api_key / model（Agent 节点需要）
2. 点 **「▶️ 执行工作流」**，工作流按拓扑顺序执行；含循环时最多迭代 10 轮，`condition` 判断 `Finish`（true）后退出循环并触发输出节点

### 注意事项

- **及时同步**：用户项目 30 分钟过期，请编辑后立即「💾 同步到图谱」并在图谱导入。
- **内置项目只读**：不要直接改 `default`，用「📋 复制副本」。
- **文件命名**：控制流脚本（无 import 依赖的条件/输出/执行器等）必须以 `kg_` 前缀命名，否则导入时会被当作孤立脚本跳过；节点类型由文件名中的语义关键词（`condition`/`output`/`agent`/`llm_call`/`question`/`tools` 等）自动推断，与任何特定 AI 范式无关。

---

## 9. 关键约定与常见问题

### 关键约定

1. **返回值**：代码最后一行表达式 → 返回值；纯语句 → "执行完成，无返回值"
2. **上游引用**：`result_<节点名>`，节点名中特殊字符替换为下划线
3. **链接类型**：只有 `data-flow`/`数据流`/`call`/`调用` 才传递数据
4. **LLM 调用确认**：Agent 节点执行前会弹出确认框，告知将消耗您的 API 额度
5. **安全限制**：代码节点仅开放安全内置函数，Agent 节点的 prompt 直接发给外部 LLM
6. **代码树脚本引用**：code 属性填 `脚本名`（默认项目）或 `项目名/脚本名`（指定项目），后端从 `data/code_tree_scripts/` 对应目录加载执行
7. **项目隔离**：不同代码树项目的脚本存放在不同子目录，互不干扰；知识图谱导入时按项目选择

### 常见问题

**Q：执行 Agent 节点时报「需要 LLM 配置」？**
A：点击工具栏「⚙️ LLM 配置」设置 base_url、api_key 和 model。配置保存在浏览器中。

**Q：数据流不生效？**
A：检查链接 type 是否为 `data-flow`/`数据流`/`call`/`调用`。普通「关联」不传递数据。

**Q：`name 'result_xxx' is not defined`？**
A：确认上游节点名称正确。单节点执行会自动执行所有上游节点，不需要手动先执行上游。

**Q：LLM 调用报错？**
A：检查 base_url 格式（不要末尾 `/v1`）、api_key 是否正确、model ID 是否被该服务支持。

**Q：代码节点报 `name 'print' is not defined`？**
A：历史 bug 已修复。如果复现请确认部署的是最新代码。

**Q：输出节点模板不替换？**
A：模板格式为 `{{result_节点名}}`，注意大小写和特殊字符。节点名中的空格等特殊字符会被替换为下划线。
