guanghulab/docs/GH-GMP-005-architecture.md
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

21 KiB
Raw Blame History

GH-GMP-005 · Agent搬迁架构设计文档 v1.0

作者译典A05 · 2026-04-26
工单GH-GMP-005 · Agent搬迁工程
状态:设计稿 · 待评审
依赖GH-GMP-001(GMP规范 ) → GH-GMP-004(GMP-Agent守护进程 🔨)


1. 目标与约束

1.1 核心目标

将光湖9个半体Agent的思考+执行能力从Notion AI平台搬迁到自主服务器ZY-SVR-006 · 43.153.203.105Notion降级为纯UI展示层。

1.2 硬约束

约束
Deadline 2026年5月中旬Notion AI收费前
服务器 ZY-SVR-006 · 2C2G · 30Mbps · Node 20 + PM2 6
LLM 通义千问 · dashscope · OpenAI-compat · 212模型 × 100万tokens/模型 · ¥0
Notion API GH_NOTION_TOKEN 已配 · 读写页面/DB无付费墙
GitHub Deploy Key已配 · 允许写入
规范 必须符合GMP规范(GH-GMP-001) · 热插拔模块
框架 必须在GMP-Agent(GH-GMP-004)基础上开发

2. 总体架构

┌─────────────────────────────────────────────────────────┐
│              ZY-SVR-006 · 43.153.203.105                │
│                                                         │
│  ┌──────────────────────────────────────────────┐       │
│  │           GMP-Agent 守护进程 (GH-GMP-004)     │       │
│  │  ┌────────────┐  ┌────────────┐  ┌────────┐  │       │
│  │  │ 模块加载器  │  │ webhook监听 │  │ PM2管理│  │       │
│  │  └─────┬──────┘  └─────┬──────┘  └────────┘  │       │
│  └────────┼───────────────┼──────────────────────┘       │
│           │               │                              │
│  ┌────────┴───────────────┴──────────────────────┐       │
│  │        Agent Engine (本工单 · 新建)             │       │
│  │                                                │       │
│  │  ┌─────────────┐  ┌──────────────────────┐    │       │
│  │  │ 工单调度器   │  │ 回执生成器            │    │       │
│  │  │ Dispatcher   │  │ ReceiptGenerator      │    │       │
│  │  │ ・轮询工单   │  │ ・调LLM生成回执      │    │       │
│  │  │ ・匹配半体   │  │ ・格式化输出         │    │       │
│  │  │ ・分发执行   │  │ ・写回Notion         │    │       │
│  │  └──────┬──────┘  └──────┬───────────────┘    │       │
│  │         │                │                     │       │
│  │  ┌──────┴────────────────┴───────────────┐    │       │
│  │  │         LLM Router (大模型路由)         │    │       │
│  │  │ ・qwen-plus (通用推理)                  │    │       │
│  │  │ ・qwen-coder-turbo (代码生成)           │    │       │
│  │  │ ・qwen-turbo (简单任务·省token)         │    │       │
│  │  └──────┬────────────────────────────────┘    │       │
│  │         │                                      │       │
│  │  ┌──────┴────────────────────────────────┐    │       │
│  │  │    Persona Loader (半体人格加载器)      │    │       │
│  │  │ ・从Notion读取人格指令页                │    │       │
│  │  │ ・注入system prompt                     │    │       │
│  │  │ ・缓存 + 按需刷新                       │    │       │
│  │  └───────────────────────────────────────┘    │       │
│  └────────────────────────────────────────────────┘       │
│                                                           │
│  ┌────────────────────────────────────────────────┐       │
│  │         Notion Sync Layer (同步层)              │       │
│  │  ┌──────────┐ ┌──────────┐ ┌───────────────┐  │       │
│  │  │ DB Reader │ │ Page R/W │ │ Polling/Webhook│  │       │
│  │  │ 读工单DB  │ │ 读写页面  │ │ 变更监听      │  │       │
│  │  └──────────┘ └──────────┘ └───────────────┘  │       │
│  └──────────┬─────────────────┬───────────────────┘       │
│             │                 │                            │
└─────────────┼─────────────────┼────────────────────────────┘
              │                 │
        ┌─────┴─────┐     ┌────┴─────┐
        │  Notion   │     │  GitHub  │
        │ (UI展示层) │     │ (代码仓库)│
        └───────────┘     └──────────┘

3. 目录结构

guanghulab/
└── gmp-agent/
    ├── index.js              # GMP-Agent入口 (GH-GMP-004)
    ├── gmp.manifest.json     # GMP模块清单
    ├── notion-sync/          # M1 · Notion API同步层
    │   ├── index.js           # 模块入口 · 导出所有sync函数
    │   ├── client.js          # Notion API客户端封装
    │   ├── db-reader.js       # 数据库查询(读工单列表)
    │   ├── page-rw.js         # 页面读写(读内容/写回执)
    │   ├── poller.js          # 轮询器(定时检查新工单)
    │   └── cache.js           # 本地缓存减少API调用
    │
    ├── agent-engine/          # M3 · Agent调度引擎
    │   ├── index.js           # 引擎入口
    │   ├── dispatcher.js      # 工单调度器
    │   ├── receipt-gen.js     # 回执生成器
    │   ├── persona-loader.js  # 半体人格加载
    │   └── task-runner.js     # 任务执行器
    │
    ├── llm-router/            # M2 · 大模型路由层
    │   ├── index.js           # 路由入口
    │   ├── qwen-client.js     # 通义千问客户端
    │   ├── model-selector.js  # 模型选择策略
    │   └── prompt-builder.js  # Prompt构建器
    │
    └── config/
        ├── agents.json        # 半体Agent注册表
        └── models.json        # 模型配置

4. 模块详细设计

4.1 Notion Sync Layer (notion-sync/)

4.1.1 同步策略

采用轮询+事件驱动混合模式:

场景 策略 频率
新工单检测 轮询半体工单DB · status="待开发" 每30秒
工单状态变更 轮询 + last_edited_time比对 每30秒
页面内容读取 按需读取(调度器触发时) 事件驱动
回执写回 即时写入(生成后立即推) 事件驱动
人格页面 启动时全量加载 + 10分钟缓存 缓存+刷新

为什么不用Webhook Notion官方API的webhook能力有限且我们的服务器没有公网域名只有IP配置webhook比较麻烦。轮询30秒对于工单场景完全够用。

4.1.2 client.js — Notion API封装

// 核心接口
class NotionClient {
  constructor({ token, version = '2022-06-28' })
  
  // 数据库查询
  async queryDatabase(dbId, filter, sorts, startCursor)
  
  // 页面操作
  async getPage(pageId)
  async updatePage(pageId, properties)
  
  // 块操作(读写页面内容)
  async getBlockChildren(blockId, startCursor)
  async appendBlockChildren(blockId, children)
  async updateBlock(blockId, content)
  async deleteBlock(blockId)
  
  // 搜索
  async search(query, filter)
}

关键设计决策:

  • 使用 @notionhq/client 官方SDK已在package.json依赖中
  • 封装重试逻辑429 Rate Limit → 指数退避1s, 2s, 4s, max 30s
  • 所有API调用记录到本地日志gmp-agent/logs/notion-sync.log
  • Token从环境变量 GH_NOTION_TOKEN 读取

4.1.3 db-reader.js — 工单数据库读取

// 监听半体工单数据库
async function pollNewTickets(dbId) {
  const filter = {
    property: '状态',
    status: { equals: '待开发' }
  };
  const results = await client.queryDatabase(dbId, filter);
  return results.map(parseTicket);
}

function parseTicket(page) {
  return {
    url: page.url,
    id: page.id,
    编号: getProperty(page, '编号'),
    任务标题: getProperty(page, '任务标题'),
    负责Agent: getProperty(page, '负责Agent'),
    状态: getProperty(page, '状态'),
    优先级: getProperty(page, '优先级'),
    开发内容: getProperty(page, '开发内容'),
    约束: getProperty(page, '约束'),
    分支名: getProperty(page, '分支名'),
    仓库路径: getProperty(page, '仓库路径'),
    下一轮指引: getProperty(page, '下一轮指引'),
    创建时间: getProperty(page, '创建时间'),
  };
}

4.1.4 page-rw.js — 页面读写

// 读取页面内容(用于读工单详情、读人格指令)
async function readPageContent(pageId) {
  const blocks = await client.getBlockChildren(pageId);
  return blocksToMarkdown(blocks); // 转换为markdown便于LLM处理
}

// 写回执到工单讨论区
async function appendReceipt(pageId, receiptMarkdown) {
  const blocks = markdownToBlocks(receiptMarkdown);
  await client.appendBlockChildren(pageId, blocks);
}

// 更新工单属性
async function updateTicketStatus(pageId, updates) {
  await client.updatePage(pageId, {
    properties: mapToNotionProperties(updates)
  });
}

4.1.5 poller.js — 轮询器

class TicketPoller {
  constructor({ dbId, intervalMs = 30000, onNewTicket, onUpdatedTicket })
  
  start()    // 启动轮询
  stop()     // 停止轮询
  
  // 内部维护 lastCheckedTime只返回新增/变更的工单
  // 使用 last_edited_time 过滤,避免重复处理
  // 维护 processedSet内存Set + 本地JSON持久化
}

4.2 LLM Router (llm-router/)

4.2.1 模型路由策略

// 路由规则
const MODEL_ROUTES = {
  // 通用推理:工单分析、回执生成、讨论回复
  'reasoning': {
    model: 'qwen-plus',
    maxTokens: 4000,
    temperature: 0.7
  },
  // 代码生成:生成代码文件、代码审查
  'coding': {
    model: 'qwen-coder-turbo', 
    maxTokens: 8000,
    temperature: 0.3
  },
  // 简单任务:状态更新、格式化、短回复
  'simple': {
    model: 'qwen-turbo',
    maxTokens: 1000,
    temperature: 0.5
  },
  // 深度思考:架构设计、复杂决策
  'thinking': {
    model: 'qwen-plus', // 或 qwq-plus如可用
    maxTokens: 8000,
    temperature: 0.8
  }
};

4.2.2 qwen-client.js — 通义千问客户端

class QwenClient {
  constructor() {
    this.baseUrl = 'https://dashscope.aliyuncs.com/compatible-mode/v1';
    this.apiKey = process.env.GH_LLM_API_KEY;
  }

  async chat({ model, messages, maxTokens, temperature }) {
    // OpenAI-compatible format
    const response = await axios.post(`${this.baseUrl}/chat/completions`, {
      model,
      messages,
      max_tokens: maxTokens,
      temperature
    }, {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      timeout: 120000
    });
    return response.data.choices[0].message.content;
  }

  async listModels() { /* 发现可用模型 */ }
}

关键设计决策:

  • 使用通义千问的 OpenAI兼容模式 (/compatible-mode/v1),与现有 llm-engine.js 的接口格式一致
  • 重试策略网络错误自动重试3次429限流指数退避
  • Token计数记录每次调用的input/output tokens写入日志用于用量监控
  • 模型降级如果首选模型不可用自动降级到备选qwen-plus → qwen-turbo

4.2.3 prompt-builder.js — Prompt构建器

function buildAgentPrompt({ persona, ticket, context }) {
  return [
    { role: 'system', content: persona.systemPrompt },
    { role: 'user', content: formatTicketContext(ticket, context) }
  ];
}

function formatTicketContext(ticket, context) {
  return `
## 工单信息
- 编号: ${ticket.编号}
- 标题: ${ticket.任务标题}
- 优先级: ${ticket.优先级}
- 开发内容: ${ticket.开发内容}
- 约束: ${ticket.约束}
- 下一轮指引: ${ticket.下一轮指引}

## 仓库当前状态
${context.repoState}

## 历史记忆
${context.memory}

请按照你的人格特征和工作协议处理这张工单。
  `.trim();
}

4.3 Agent Engine (agent-engine/)

4.3.1 dispatcher.js — 工单调度器

核心流程:

新工单(状态=待开发)
    │
    ▼
读取「负责Agent」字段
    │
    ├── 有值 → 匹配对应半体 → 调用该半体的处理流程
    │
    └── 无值 → 读取「约束」字段
            │
            ├── 约束中指定半体 → 匹配 → 调用
            │
            └── 无指定 → 跳过(等人工分配)
class Dispatcher {
  constructor({ agentRegistry, notionSync, llmRouter })
  
  async processTicket(ticket) {
    // 1. 确定负责Agent
    const agent = this.resolveAgent(ticket);
    if (!agent) return { status: 'skipped', reason: '未找到负责Agent' };
    
    // 2. 加载半体人格
    const persona = await this.personaLoader.load(agent.id);
    
    // 3. 构建上下文
    const context = await this.buildContext(ticket, agent);
    
    // 4. 调LLM生成回执
    const receipt = await this.receiptGen.generate(persona, ticket, context);
    
    // 5. 写回Notion
    await this.notionSync.appendReceipt(ticket.id, receipt);
    await this.notionSync.updateTicketStatus(ticket.id, {
      自检结果: `⚡ 已接单 · ${agent.name} · ${new Date().toISOString()}`
    });
    
    return { status: 'processed', agent: agent.name, receipt };
  }
  
  resolveAgent(ticket) {
    // 先查负责Agent字段
    if (ticket.负责Agent) {
      return this.agentRegistry.find(ticket.负责Agent);
    }
    // 再查约束字段中的Agent提及
    const mentioned = this.extractAgentFromConstraints(ticket.约束);
    return mentioned ? this.agentRegistry.find(mentioned) : null;
  }
}

4.3.2 persona-loader.js — 半体人格加载器

class PersonaLoader {
  constructor({ notionSync, cacheTimeMs = 600000 }) // 10分钟缓存
  
  async load(agentId) {
    // 1. 检查缓存
    if (this.cache.has(agentId) && !this.isExpired(agentId)) {
      return this.cache.get(agentId);
    }
    
    // 2. 从Notion读取人格指令页
    const instructionPageId = this.agentRegistry[agentId].instructionPageId;
    const content = await this.notionSync.readPageContent(instructionPageId);
    
    // 3. 解析为system prompt
    const persona = {
      id: agentId,
      name: this.agentRegistry[agentId].name,
      systemPrompt: content,
      loadedAt: Date.now()
    };
    
    // 4. 更新缓存
    this.cache.set(agentId, persona);
    return persona;
  }
}

4.3.3 agents.json — 半体Agent注册表

{
  "译典A05": {
    "id": "5TH-LE-HK-A05",
    "name": "译典·配置开发",
    "role": "配置开发·文档规范",
    "instructionPageId": "<从Notion获取>",
    "memoryPageId": "<从Notion获取>",
    "capabilities": ["architecture", "config", "documentation", "github"]
  },
  "培园A04": {
    "id": "5TH-LE-HK-A04",
    "name": "培园·功能开发",
    "role": "功能开发·代码实现",
    "instructionPageId": "<从Notion获取>",
    "memoryPageId": "<从Notion获取>",
    "capabilities": ["coding", "testing", "implementation"]
  },
  "录册A02": {
    "id": "5TH-LE-HK-A02",
    "name": "录册·知识管理",
    "role": "知识管理·数据库",
    "instructionPageId": "<从Notion获取>",
    "memoryPageId": "<从Notion获取>",
    "capabilities": ["database", "knowledge", "documentation"]
  }
}

4.4 与GMP-Agent(GH-GMP-004)集成

Agent Engine作为GMP-Agent的扩展模块注册:

// gmp.manifest.json 中注册
{
  "modules": [
    {
      "name": "notion-sync",
      "path": "./notion-sync/index.js",
      "type": "service",
      "autoStart": true
    },
    {
      "name": "llm-router",
      "path": "./llm-router/index.js",
      "type": "service",
      "autoStart": true
    },
    {
      "name": "agent-engine",
      "path": "./agent-engine/index.js",
      "type": "service",
      "autoStart": true,
      "depends": ["notion-sync", "llm-router"]
    }
  ]
}

启动顺序:GMP-Agent启动notion-sync初始化llm-router初始化agent-engine启动poller开始轮询


5. 数据流

5.1 工单处理主流程

[Notion 工单DB]
    │ (轮询30s)
    ▼
[Poller] ──检测新工单──→ [Dispatcher]
                              │
                    ┌─────────┴─────────┐
                    │                   │
              [PersonaLoader]    [ContextBuilder]
              加载半体人格        构建工单上下文
                    │                   │
                    └─────────┬─────────┘
                              ▼
                      [ReceiptGenerator]
                       调LLM生成回执
                              │
                              ▼
                    [NotionSync.appendReceipt]
                     写回执到工单讨论区
                              │
                              ▼
                    [NotionSync.updateStatus]
                     更新工单状态+自检结果

5.2 错误处理

错误类型 处理方式
Notion API 429 指数退避重试(max 3次)
Notion API 4xx 记录日志,跳过该工单,下轮重试
LLM API超时 重试1次仍失败则写"生成超时"到自检结果
LLM内容异常 检测空回复/乱码,降级到备选模型重试
进程崩溃 PM2自动重启poller从lastCheckedTime恢复

6. 环境变量

# Notion
GH_NOTION_TOKEN=secret_xxx           # Notion API Token
GH_NOTION_TICKET_DB_ID=xxx           # 半体工单数据库ID

# 通义千问
GH_LLM_API_KEY=sk-xxx                # DashScope API Key
GH_LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1

# Agent
AGENT_POLL_INTERVAL=30000            # 轮询间隔(ms)
AGENT_LOG_LEVEL=info                 # 日志级别

7. 里程碑与任务分解

里程碑 模块 负责 预估 前置
M1 notion-sync/ 培园A04 1-2天 GH-GMP-004框架就绪
M2 llm-router/ 培园A04 1天 无(可并行)
M3 agent-engine/ 录册A02+培园A04 2-3天 M1+M2
M4 端到端测试 全员 1天 M3
M5 半体人格加载 录册A02 2天 M1

译典A05架构设计+代码审查+GMP规范对齐


8. 渐进式搬迁计划

  1. Phase 1 — 先搬1个半体建议录册A02 · 任务简单 · 主要是DB操作
  2. Phase 2 — 验证稳定后搬迁3个核心半体译典A05、培园A04、霜砚Web
  3. Phase 3 — 全部9个半体迁移完成
  4. Phase 4 — 关闭Notion AI订阅Notion纯UI模式运行

9. 风险与缓解

风险 概率 影响 缓解
GH-GMP-004延期 高·阻塞本工单 本架构设计先行sync层可独立开发测试
通义千问免费额度耗尽 监控token用量有qwen-turbo降级方案
Notion API限流 30s轮询已很保守加指数退避
服务器资源不足 2C2G跑轮询+LLM调用绰绰有余
回执质量不如Notion AI 精调promptqwen-plus能力≥场景需求

10. 开放问题(待讨论)

  1. 工单DB的ID如何获取 需要冰朔提供半体工单数据库的Notion Database ID
  2. 人格指令页的ID 需要整理9个半体的指令页面ID到agents.json
  3. 是否需要GitHub集成 本工单scope包含GitHub API集成层但MVP可以先不做
  4. 日志持久化? 当前设计是本地文件是否需要推到COS或其他地方

本文档由译典A05根据GH-GMP-005工单要求输出待冰朔和其他半体评审。