diff --git a/docs/architecture-v5.md b/docs/architecture-v5.md index f59ee11..98064b5 100644 --- a/docs/architecture-v5.md +++ b/docs/architecture-v5.md @@ -13,7 +13,7 @@ > - 定时调度:使用 Claude Code 原生 CronCreate + cron.json 持久化 > - **OKR 目标系统**:`data/goals/` 集中管理,点分层级 ID,全局可追溯 > - **FDE 流水线**:Hunt→Digest→Evolving 三层训练架构,已经端到端验证通过 -> - **双路径产出**:元 Agent 产出同时写入 workspace/ + agents/ 两个位置 +> - **双路径产出**:元 Agent 产出同时写入 train/ + agents/ 两个位置 > - **Skills 扫描注入**:buildSystemPrompt 扫描所有 .claude/skills/ 子目录 --- @@ -448,7 +448,7 @@ Task 进入 → 海马体检索 → 命中? → 小脑快速执行 - [x] 每个元 Agent 的 SKILL.md 泛化定义 (不硬编码领域知识) - [x] L1/L2 依赖技能复制到各元 Agent 的 `.claude/skills/` - [x] buildSystemPrompt 扫描所有 skills 子目录 (关键修复) -- [x] 双路径产出 (workspace/ + agents/) +- [x] 双路径产出 (train/ + agents/) - [x] CEO Agent 端到端训练: Hunt(1461行) → Digest(1593行) → Evolve(92/100) - [x] retrain-pipeline.js 对齐新 agent 名 - 详见: `docs/system-design/21-fde-pipeline-architecture.md` @@ -501,7 +501,7 @@ Task 进入 → 海马体检索 → 命中? → 小脑快速执行 | 7 | OKR 数据结构 | 扁平 nodes Map (O(1) 查找) | 03-24 | | 8 | OKR 进度算法 | 叶子节点完成率 | 03-24 | | 9 | plan.md 更新 | 行级匹配 + 列拆分 (兼容粗体等格式) | 03-24 | -| 10 | FDE 双路径产出 | workspace/(共享) + agents/(资产) 同时写入 | 03-24 | +| 10 | FDE 双路径产出 | train/(共享) + agents/(资产) 同时写入 | 03-24 | | 11 | 元 Agent 依赖技能加载 | buildSystemPrompt 扫描所有 .claude/skills/ 子目录 | 03-24 | | 12 | retrain-pipeline 对齐 | l3-hunt→hunter, l2-digest→digester, innovator-evolver→evolver | 03-25 | | 13 | Thinking 控制 | `--effort low` 禁用 extended thinking,config.json `cli.effort` 按 Agent 配置 | 03-27 | diff --git a/docs/open-api-manual.md b/docs/open-api-manual.md new file mode 100644 index 0000000..a3fe7f2 --- /dev/null +++ b/docs/open-api-manual.md @@ -0,0 +1,143 @@ +# ArenaSK Open API 外部调用操作手册 + +本手册详细说明了如何通过外部系统(如第三方业务系统、自动化流、外部脚本)跨平台调用 LemonBoss 平台内部已经部署的任意 Agent。 + +--- + +## 1. 认证配置 (Authentication) + +调用系统的任何开放接口前,必须在 HTTP 标头 (Headers) 中携带你申请/配置的 API Key 密钥。 + +**系统配置位置**: +当前系统支持的 API Key 配置位于项目根目录的 `.env` 文件内: +```ini +OPEN_API_KEYS=sk-test-key-001,sk-prod-key-... +``` + +**请求方式**: +无论你用什么语言发送请求,请确保 HTTP Headers 包含以下任意一种格式: +- `Authorization: Bearer ` (推荐) +- `X-API-Key: ` + +--- + +## 2. 核心调用流程 + +因为我们的 Agent 需要进行「思考与执行 (Plan & Execute)」,单次任务通常耗时数分钟。为了防止网络超时断连,我们的接口设计完全**异步化 (Asynchronous)**: + +> **完整调用链路:** +> 👉 外部发送请求 `invoke` ➔ 系统接管并**返回单号 jobId** ➔ 外部每隔几秒根据 jobId去查进度 `poll` ➔ 遇到 **`completed`** 则提取输出。 + +### 接口 ①:指派任务并获取 JobId (Invoke) + +**🔹 端点**:`POST /api/open/companies/:companyId/agents/:agentId/invoke` +**🔹 作用**:唤起指定公司和指定名称的 Agent 执行任务。 + +#### 请求参数 (Body) + +| 参数名 | 类型 | 必填 | 说明 |示例| +|---------|-------|------|------|------| +| `task` | String | ✅ | 需要 Agent 帮您执行的自然语言任务。 | `"帮我爬取并总结最新的 AI agent 趋势"`| +| `contextFiles` | Array | 选填 | 外部传递前置文件,Agent可以据此进行**记忆连接或文档问答**。|见下文详述| +| `webhookUrl` | String | 选填 | 回调地址。当任务完结时主动推送结果至该URL。| `"https://your-app/webhook"`| + +#### 代码示例 (cURL) +```bash +curl -X POST http://YOUR_SERVER_IP:3010/api/open/companies/test/agents/claude-youtube-skills/invoke \ + -H "Authorization: Bearer sk-test-key-001" \ + -H "Content-Type: application/json" \ + -d '{ + "task": "基于上次总结的结果,帮我提取其中所有的产品名称并生成CSV格式", + "contextFiles": [ + { + "name": "previous_result.md", + "content": "这是一段之前的调查结果......" + } + ] + }' +``` + +#### 返回值 +接口会光速返回 HTTP 202 的响应该结果: +```json +{ + "jobId": "job-1712461931-abcdef12", + "status": "queued", + "message": "Agent invocation job queued. Poll GET /api/open/jobs/job-1712461931-abcdef12 for status." +} +``` +此时你必须要把 `jobId` 记录下来! + +--- + +### 接口 ②:轮询任务执行结果 (Status Polling) + +我们推荐外部系统(例如你的脚本中)使用 `while` 循环或定时任务,**每隔 3~5 秒钟**调用一次该接口查询。 + +**🔹 端点**:`GET /api/open/jobs/:jobId` + +#### 代码示例 (cURL) +```bash +curl "http://YOUR_SERVER_IP:3010/api/open/jobs/job-1712461931-abcdef12" \ + -H "Authorization: Bearer sk-test-key-001" +``` + +#### 返回值 +你可以通过判断返回体中的 `status` 字段来得知进度: + +**1. 正在执行时(Running / Queued)** +```json +{ + "jobId": "job-1712461931-abcdef12", + "status": "running", + "progress": "Executing agent task...", + "createdAt": "2026-04-07T12:00:00Z" +} +``` + +**2. 任务已完成时(Completed)** 🌟 此刻可以提取最终结果: +```json +{ + "jobId": "job-1712461931-abcdef12", + "status": "completed", + "progress": "Invocation completed", + "result": { + "output": "你好!我已经完成了产品名称提取。这里是转换后的 CSV 格式内容...\n..." + } +} +``` + +**3. 任务失败异常时(Failed)** +```json +{ + "jobId": "job-1712461931-abcdef12", + "status": "failed", + "error": "Agent execution failed: process timeout." +} +``` + +--- + +## 3. 高级用法指南 + +### 如何保持“上下文记忆”或者“持续对话”? + +Agent 是没有自动长久记忆跨系统调用流的。你在下一次调用相同的这个 Agent(比如第二天再调一次)时,如果想让它知道前面发生了什么,或者传递新的数据报告给它,**一定要使用 `contextFiles` 数组结构!**。 + +外部系统提取了上次的 `result.output` 后,在下次调用的 Body 中: +```json +{ + "task": "基于我附加给你的日志总结,开展新一轮的数据清理", + "contextFiles": [ + { + "name": "logs-from-yesterday.txt", + "content": "....提取的昨日任务输出大段文本...." + }, + { + "name": "data.csv", + "content": "id,name\n1,apple" + } + ] +} +``` +系统底座会自动将这 2 个对象落地成文件,以文本阅读的视角送入 Agent 的大脑供其深度理解(支持上万字)。通过这种 “外部系统充当记忆保管员 + API 动态投喂上下文文件” 的组合拳,就可以把这些 Agent 变成可控、随时插拔的智能化函数库! diff --git a/docs/system-design/15-innovator-meta-agent.md b/docs/system-design/15-innovator-meta-agent.md index 032b55e..f324b77 100644 --- a/docs/system-design/15-innovator-meta-agent.md +++ b/docs/system-design/15-innovator-meta-agent.md @@ -95,12 +95,12 @@ Innovator 不在四层组织架构(Master/CXO/Role/Skill)内。它是四层ä → [CALL: meta_hunt] → 搜索社交媒体爬虫的最佳实践、方法论 → 爬取案例、API 文档 - → 写入 data/agents/social_crawler/workspace/ + → 写入 data/agents/social_crawler/train/ │ ▼ 4️⃣ Digest (Claude CLI 执行型 Agent) → [CALL: meta_digest] - → 读取 workspace/ 调研数据 + → 读取 train/ 调研数据 → 提炼 knowledge/knowhow.md + knowledge/benchmark.md → 生成 .claude/skills/main_skill/SKILL.md │ @@ -238,7 +238,8 @@ data/agents/{new-agent-id}/ │ ├── meta.json │ ├── history.json │ └── {msgId}/messages.log -├── workspace/ +├── workspace/ ← Agent 工作区 +├── train/ ← 训练过程数据 (Hunt/Digest/Evolve) ├── logs/ └── config.json ← status: "ready" ``` diff --git a/docs/system-design/20-scaffold-first-agent-creation.md b/docs/system-design/20-scaffold-first-agent-creation.md index 83ae765..61fc387 100644 --- a/docs/system-design/20-scaffold-first-agent-creation.md +++ b/docs/system-design/20-scaffold-first-agent-creation.md @@ -40,10 +40,11 @@ data/agents/{agent_name}/ │ ├── sessions/ │ └── daily/ ├── knowledge/ ← Digest 阶段写入 knowhow/benchmark -├── workspace/ ← Hunt 阶段写入调研数据 +├── workspace/ ← 自治工作区 │ ├── code/ │ ├── data/ │ └── assets/ +├── train/ ← Hunt 阶段写入的调研和训练数据 └── logs/ ``` @@ -51,10 +52,10 @@ data/agents/{agent_name}/ **执行型子 Agent,有完整工具权限。** -meta_hunt 在新建 Agent 的目录下运行,将调研结果写入该 Agent 的 `workspace/` 目录: +meta_hunt 在新建 Agent 的目录下运行,将调研结果写入该 Agent 的 `train/` 目录: ``` -data/agents/{agent_name}/workspace/ +data/agents/{agent_name}/train/ ├── knowhow.md ← 方法论调研 ├── toplist.md ← 标杆案例 ├── agent_profile.md ← Agent 人格画像(思维、风格、工具、心跳、用户关系) @@ -65,10 +66,10 @@ data/agents/{agent_name}/workspace/ ### 阶段 3: Digest(知识消化) -**执行型子 Agent,读取 workspace/ 的调研数据,提炼输出到 knowledge/ 和 SKILL.md,并基于 agent_profile 生成定制化 Bootstrap 文件。** +**执行型子 Agent,读取 train/ 的调研数据,提炼输出到 knowledge/ 和 SKILL.md,并基于 agent_profile 生成定制化 Bootstrap 文件。** ``` -输入: workspace/knowhow.md + workspace/toplist.md + workspace/agent_profile.md + workspace/crawl/ +输入: train/knowhow.md + train/toplist.md + train/agent_profile.md + train/crawl/ 输出: ├── knowledge/knowhow.md ← 精炼后的方法论 ├── knowledge/benchmark.md ← 评分标准(≥5个维度) @@ -160,9 +161,9 @@ const effectiveWorkDir = agent.workDir || this.workDir; 所有指向其它 Agent 的路径都必须使用 **`../{agent_name}/`** 格式(上一级的兄弟目录): ``` -正确: ../{agent_name}/workspace/knowhow.md -错误: data/agents/{agent_name}/workspace/knowhow.md ← 会变成嵌套路径! -错误: workspace/knowhow.md ← 会写到 innovator-001 自己的目录 +正确: ../{agent_name}/train/knowhow.md +错误: data/agents/{agent_name}/train/knowhow.md ← 会变成嵌套路径! +错误: train/knowhow.md ← 会写到 innovator-001 自己的目录 ``` 目录结构关系: @@ -172,7 +173,7 @@ data/agents/ │ ├── .claude/skills/ ← l1/l2 知识文件 │ └── workspace/ ← innovator 自己的工作区 ├── poetry_writer/ ← ../ poetry_writer/ 可以访问到 -│ ├── workspace/ ← Hunt 写入这里 +│ ├── train/ ← Hunt 写入这里 │ ├── knowledge/ ← Digest 写入这里 │ └── .claude/skills/ ← Digest 生成 SKILL.md 到这里 └── other_agent/ @@ -224,11 +225,11 @@ Innovator (--tools "", 纯文本编排) ├── [CALL: meta_hunt] ──▶ Claude CLI (cwd: innovator-001/) │ 读取 .claude/skills/l1-*/l2-* 知识 │ 用 Bash/curl 搜索调研 - │ 写入 data/agents/poetry_writer/workspace/ + │ 写入 data/agents/poetry_writer/train/ │ └── knowhow.md + toplist.md + agent_profile.md │ ├── [CALL: meta_digest] ──▶ Claude CLI (cwd: innovator-001/) - │ 读取 workspace/ (knowhow + toplist + agent_profile) + │ 读取 train/ (knowhow + toplist + agent_profile) │ 提炼写入 knowledge/knowhow.md + benchmark.md │ 生成 .claude/skills/main_skill/SKILL.md │ 生成 Bootstrap: agent.md, soul.md, tools.md, diff --git a/docs/system-design/21-fde-pipeline-architecture.md b/docs/system-design/21-fde-pipeline-architecture.md index d3a0d90..bf29c38 100644 --- a/docs/system-design/21-fde-pipeline-architecture.md +++ b/docs/system-design/21-fde-pipeline-architecture.md @@ -61,12 +61,12 @@ Hunter ────→ l1-search-engine, l1-web-fetch, l2-web-research, | 路径 | 用途 | 示例 | |------|------|------| -| `data/workspace/{targetAgentId}/{phase}/` | 共享工作区,元 Agent 间共享 | `workspace/ceo/hunt/` | +| `data/train/{targetAgentId}/{phase}/` | 综合训练区,元 Agent 间共享 | `train/ceo/hunt/` | | `data/agents/{targetAgentId}/{phase}/` | Agent 资产,归属目标 Agent | `agents/ceo/hunt/` | ``` data/ -├── {companyId}/workspace/{targetAgentId}/ ← 公司共享工作区 (元 Agent 间共享) +├── {companyId}/train/{targetAgentId}/ ← 训练数据区 (元 Agent 间共享) │ ├── hunt/ │ ├── digest/ │ └── evolve/ @@ -80,7 +80,7 @@ data/ > **注意**: 全局元 Agent (如 Hunter/Digester/Evolver) 位于 `system/agents/`。 > 对于全局训练任务,路径前缀为 `system/`。 -**原则**: 先写到 workspace/,再用 `cp -r` 复制到 agents/。 +**原则**: 先写到 train/,再用 `cp -r` 复制到 agents/。 --- @@ -157,7 +157,7 @@ curl -s -X POST http://localhost:3000/api/agent/task/dispatch \ -d '{ "from": "innovator", "to": "hunter", - "task": "对 CEO 核心能力进行知识猎取。skillName: ceo_leadership, targetAgentId: ceo, domain: CEO战略管理与领导力, keywords: [CEO战略规划, OKR目标管理]。按 l3-hunt skill 执行。产出同时写入 ../../workspace/ceo/hunt/ 和 ../ceo/hunt/。", + "task": "对 CEO 核心能力进行知识猎取。skillName: ceo_leadership, targetAgentId: ceo, domain: CEO战略管理与领导力, keywords: [CEO战略规划, OKR目标管理]。按 l3-hunt skill 执行。产出同时写入 ../../train/ceo/hunt/ 和 ../ceo/hunt/。", "priority": "P0", "autoExecute": true }' @@ -171,7 +171,7 @@ curl -s -X POST http://localhost:3000/api/agent/task/dispatch \ -d '{ "from": "innovator", "to": "digester", - "task": "消化 CEO 调研数据。skillName: ceo_leadership, targetAgentId: ceo。输入: ../../workspace/ceo/hunt/。按 l2-digest skill 执行。产出同时写入 ../../workspace/ceo/digest/ 和 ../ceo/digest/。", + "task": "消化 CEO 调研数据。skillName: ceo_leadership, targetAgentId: ceo。输入: ../../train/ceo/hunt/。按 l2-digest skill 执行。产出同时写入 ../../train/ceo/digest/ 和 ../ceo/digest/。", "priority": "P0", "autoExecute": true }' @@ -185,7 +185,7 @@ curl -s -X POST http://localhost:3000/api/agent/task/dispatch \ -d '{ "from": "innovator", "to": "evolver", - "task": "训练 CEO 的 ceo_leadership 技能。targetAgentId: ceo。输入: ../../workspace/ceo/digest/。按 evolving skill 执行 Small Loop。passThreshold: 90, maxIterations: 5。产出同时写入 ../../workspace/ceo/evolve/ 和 ../ceo/evolve/。训练完成后安装到 ../ceo/.claude/skills/main_skill/SKILL.md。", + "task": "训练 CEO 的 ceo_leadership 技能。targetAgentId: ceo。输入: ../../train/ceo/digest/。按 evolving skill 执行 Small Loop。passThreshold: 90, maxIterations: 5。产出同时写入 ../../train/ceo/evolve/ 和 ../ceo/evolve/。训练完成后安装到 ../ceo/.claude/skills/main_skill/SKILL.md。", "priority": "P0", "autoExecute": true }' @@ -264,9 +264,9 @@ benchmark_score: 92 | # | 决策 | 原因 | 日期 | |---|------|------|------| -| 1 | 双路径产出 (workspace + agents) | 元 Agent 间共享 + 目标 Agent 资产归属 | 03-24 | +| 1 | 双路径产出 (train + agents) | 元 Agent 间共享 + 目标 Agent 资产归属 | 03-24 | | 2 | 依赖技能放 `.claude/skills/` | Claude CLI 自动发现,buildSystemPrompt 自动注入 | 03-24 | -| 3 | 先写 workspace 再 cp agents | workspace 为主源,agents 为副本 | 03-24 | +| 3 | 先写 train 再 cp agents | train 为主源,agents 为副本 | 03-24 | | 4 | Evolver 安装最终 SKILL.md | 训练完成后直接更新目标 Agent 的核心技能 | 03-24 | | 5 | retrain-pipeline 对齐 agent 名 | `l3-hunt`→`hunter`, `l2-digest`→`digester`, `innovator-evolver`→`evolver` | 03-25 | diff --git a/server/routes/open-api.js b/server/routes/open-api.js index 1733b95..12fbefc 100644 --- a/server/routes/open-api.js +++ b/server/routes/open-api.js @@ -278,6 +278,40 @@ router.get('/skills/:name/updates', h((req, res) => { res.json({ skillName: req.params.name, latestVersion: skill.version, currentScore: skill.currentScore, hasUpdate: !!hasUpdate, changelog: hasUpdate ? changelog : undefined, downloadCount: skill.downloadCount || 0, updateCount: skill.updateCount || 0 }); })); +// ── Company Agent Invocation ── +router.post('/companies/:companyId/agents/:agentId/invoke', h(async (req, res) => { + const { companyId, agentId } = req.params; + const { task, contextFiles, webhookUrl } = req.body; + + if (!task || typeof task !== 'string') { + return res.status(400).json({ error: 'Bad Request', message: 'The "task" field is required and must be a string.' }); + } + + // Create an async job + const job = jobManager.createJob({ + description: `Invoke Agent ${agentId} in Company ${companyId}`, + webhookUrl, + apiKey: req.apiKey, + user_id: req.user?.id + }); + + // Custom tracking fields + job.companyId = companyId; + job.agentId = agentId; + jobManager.updateJob(job.jobId, { status: 'queued', progress: 'Task is waiting for execution queue...' }); + + // Run in background without blocking + _runAgentInvocation(job, { companyId, agentId, task, contextFiles }).catch(err => { + jobManager.updateJob(job.jobId, { status: 'failed', error: err.message, progress: `Invocation failed: ${err.message}` }); + }); + + res.status(202).json({ + jobId: job.jobId, + status: job.status, + message: `Agent invocation job queued. Poll GET /api/open/jobs/${job.jobId} for status.` + }); +})); + // ── Pipelines (简化版) ── // 读取 .data/pipelines.json let pipelinesData = { pipelines: [] }; @@ -497,7 +531,95 @@ async function _runSkillCreation(job, resumeFromStep = null) { } } +async function _runAgentInvocation(job, payload) { + const { companyId, agentId, task, contextFiles } = payload; + const path = require('path'); + const fs = require('fs'); + const { loadConfigFromSkills } = require('../src/core/skill-config-loader'); + const AgentManager = require('../src/core/agent-manager'); + const config = require('../config'); + + // Directory for company agent: data/companies/{companyId}/agents/{agentId} + const agentDir = path.resolve(__dirname, `../../data/companies/${companyId}/agents/${agentId}`); + if (!fs.existsSync(agentDir)) { + throw new Error(`Agent directory not found: companies/${companyId}/agents/${agentId}`); + } + + jobManager.updateJob(job.jobId, { status: 'running', progress: 'Initializing agent session...' }); + + let enrichedTask = task; + + // Process Context Files + if (Array.isArray(contextFiles) && contextFiles.length > 0) { + jobManager.updateJob(job.jobId, { progress: `Processing ${contextFiles.length} context files...` }); + + let contextBlock = '\n\n---\n📎 **附件上下文** (外部系统传入的数据):\n\n'; + const uploadDir = path.join(agentDir, 'uploads', job.jobId); + fs.mkdirSync(uploadDir, { recursive: true }); + + for (const file of contextFiles) { + if (file.name && file.content) { + // Sanitize file name + const safeName = file.name.replace(/[\\/:*?"<>|]/g, '_').slice(0, 100); + const filePath = path.join(uploadDir, safeName); + fs.writeFileSync(filePath, String(file.content), 'utf-8'); + + // Truncate for prompt injection if massive + const fileContent = String(file.content); + const displayContent = fileContent.length > 50000 + ? fileContent.substring(0, 50000) + '\\n... (截断)' + : fileContent; + + contextBlock += `### 📄 ${safeName}\n\`\`\`\n${displayContent}\n\`\`\`\n\n`; + } + } + + enrichedTask = contextBlock + '\n\n---\n' + enrichedTask; + } + + // Set up AgentManager + // We initialize the base config and instantiate manager + const backendsDir = path.join(__dirname, '../config/backends.json'); + const backends = fs.existsSync(backendsDir) ? JSON.parse(fs.readFileSync(backendsDir, 'utf-8')) : {}; + const skillsDir = config.skillsCodeDir; + const rootDir = path.resolve(__dirname, '../../'); + + const fullSystemConfig = loadConfigFromSkills(skillsDir, backends); + const manager = new AgentManager(fullSystemConfig, rootDir); + + const namespacedId = `${companyId}:${agentId}`; + + // Execute via AgentManager + jobManager.updateJob(job.jobId, { status: 'running', progress: 'Executing agent task...' }); + + let fullOutput = ''; + const onData = (agent) => (chunk) => { + if (chunk?.trim()) { + fullOutput += chunk; + } + }; + + try { + const result = await manager.callAgent(namespacedId, enrichedTask, { onData: onData(namespacedId) }); + + jobManager.updateJob(job.jobId, { + status: 'completed', + progress: 'Invocation completed', + result: { + output: result.output || fullOutput || '(Empty Output)' + } + }); + + try { await manager.closeAllSessions(); } catch {} + } catch (err) { + jobManager.updateJob(job.jobId, { status: 'failed', error: err.message, progress: `Agent execution failed: ${err.message}` }); + try { await manager.closeAllSessions(); } catch {} + throw err; + } +} + module.exports = router; module.exports._jobManager = jobManager; module.exports._runSkillCreation = _runSkillCreation; module.exports._runFeedbackIteration = _runFeedbackIteration; +module.exports._runAgentInvocation = _runAgentInvocation; diff --git a/system/agents/master/.claude/scheduled_tasks.json b/system/agents/master/.claude/scheduled_tasks.json index ccf160f..63c0abb 100644 --- a/system/agents/master/.claude/scheduled_tasks.json +++ b/system/agents/master/.claude/scheduled_tasks.json @@ -5,7 +5,7 @@ "cron": "*/5 * * * *", "prompt": "执行系统巡检:读取 plan.md 和 todo.md,检查所有任务状态。已完成的更新标记,卡住的分析原因后自主处理(重新分发或修复),未开始的按优先级立即分发。不要请示用户,直接行动。", "createdAt": 1775231859266, - "lastFiredAt": 1775237249762, + "lastFiredAt": 1775237849327, "recurring": true }, { @@ -13,7 +13,7 @@ "cron": "*/30 * * * *", "prompt": "生成系统状态日报,统计所有 Agent 任务完成率和异常情况,写入 memory/daily/ 今日文件。", "createdAt": 1775231859272, - "lastFiredAt": 1775236092651, + "lastFiredAt": 1775237892371, "recurring": true }, { @@ -21,7 +21,7 @@ "cron": "*/5 * * * *", "prompt": "执行系统巡检:读取 plan.md 和 todo.md,检查所有任务状态。已完成的更新标记,卡住的分析原因后自主处理(重新分发或修复),未开始的按优先级立即分发。不要请示用户,直接行动。", "createdAt": 1775235820776, - "lastFiredAt": 1775237170680, + "lastFiredAt": 1775237771254, "recurring": true }, { @@ -29,6 +29,7 @@ "cron": "*/30 * * * *", "prompt": "生成系统状态日报,统计所有 Agent 任务完成率和异常情况,写入 memory/daily/ 今日文件。", "createdAt": 1775235825837, + "lastFiredAt": 1775237771254, "recurring": true } ] diff --git a/system/templates/claude-youtube-skills/config.json b/system/templates/claude-youtube-skills/config.json index 9a0cfe4..de4a5a3 100644 --- a/system/templates/claude-youtube-skills/config.json +++ b/system/templates/claude-youtube-skills/config.json @@ -22,6 +22,7 @@ "maxDiskMB": 1024, "maxProcesses": 5 }, + "companyId": "test", "heartbeat": { "intervalMinutes": 15, "enabled": false diff --git a/web/src/views/AgentChat.vue b/web/src/views/AgentChat.vue index 9f53c1f..08f6cde 100644 --- a/web/src/views/AgentChat.vue +++ b/web/src/views/AgentChat.vue @@ -265,6 +265,14 @@
+
API 外部调用 Endpoint
+
+ {{ getAgentApiUrl(agentId) }} +
+ +
+ +
统计
{{ fileTree?.totalFiles || 0 }} 文件
@@ -766,6 +774,28 @@ async function deleteConversation(convId) { } catch (e) { console.error('Delete conv error:', e) } } +function getAgentApiUrl(fullAgentId) { + if (!fullAgentId) return ''; + let companyId = "system"; + let targetAgentId = fullAgentId; + const parts = fullAgentId.split(':'); + if (parts.length > 1) { + companyId = parts[0]; + targetAgentId = parts.slice(1).join(':'); + } + const host = window.location.hostname; + return `http://${host}:3010/api/open/companies/${companyId}/agents/${targetAgentId}/invoke`; +} + +async function copyText(text) { + try { + await navigator.clipboard.writeText(text); + alert('✅ URL 已复制到剪贴板'); + } catch (e) { + console.error(e); + } +} + // ─── Chat (SSE) ─── async function handleSend() { diff --git a/web/src/views/AgentList.vue b/web/src/views/AgentList.vue index 2cce281..eee6dbe 100644 --- a/web/src/views/AgentList.vue +++ b/web/src/views/AgentList.vue @@ -61,6 +61,14 @@
+
+
API 外部调用 Endpoint
+
+ {{ getAgentApiUrl(detail.id) }} +
+ +
+
@@ -165,6 +173,28 @@ onMounted(async () => { await selectAgent(selectedAgent.value) } }) + +function getAgentApiUrl(agentId) { + if (!agentId) return ''; + let companyId = "system"; + let targetAgentId = agentId; + const parts = agentId.split(':'); + if (parts.length > 1) { + companyId = parts[0]; + targetAgentId = parts.slice(1).join(':'); + } + const host = window.location.hostname; + return `http://${host}:3010/api/open/companies/${companyId}/agents/${targetAgentId}/invoke`; +} + +async function copyText(text) { + try { + await navigator.clipboard.writeText(text); + alert('✅ URL 已复制到剪贴板'); + } catch (e) { + console.error(e); + } +} diff --git a/web/src/views/CompanyDashboard.vue b/web/src/views/CompanyDashboard.vue index d2424b2..e7c2841 100644 --- a/web/src/views/CompanyDashboard.vue +++ b/web/src/views/CompanyDashboard.vue @@ -190,6 +190,16 @@
⚡ {{ skill }}
暂无技能(可通过 Innovator 训练获得)
+ +
+
🔗 外部调用 API Endpoint
+
+ + {{ getAgentApiUrl(selectedCompany.id, agent.id) }} + + +
+
@@ -1170,21 +1180,43 @@ function goToAgentChat(agentId) { router.push(`/${selectedCompany.value.id}/agents/${agentId}`) } -async function toggleAgentSkills(agentId) { +function toggleAgentSkills(agentId) { if (expandedAgent.value === agentId) { expandedAgent.value = null return } // Fetch skills from backend try { - const { data } = await axios.get(`/api/company/${selectedCompany.value.id}/agents/${agentId}/skills`) - agentSkills.value = data + const { data } = axios.get(`/api/company/${selectedCompany.value.id}/agents/${agentId}/skills`).then(res => { + agentSkills.value = res.data + }).catch(e => { + agentSkills.value = [] + }); } catch { agentSkills.value = [] } expandedAgent.value = agentId } +function getAgentApiUrl(companyId, agentId) { + if (!agentId || !companyId) return ''; + let targetAgentId = agentId; + if (targetAgentId.startsWith(companyId + ':')) { + targetAgentId = targetAgentId.substring(companyId.length + 1); + } + const host = window.location.hostname; + return `http://${host}:3010/api/open/companies/${companyId}/agents/${targetAgentId}/invoke`; +} + +async function copyText(text) { + try { + await navigator.clipboard.writeText(text); + alert('✅ URL 已复制到剪贴板'); + } catch (e) { + console.error(e); + } +} + function triggerTrain(agentId) { if (!selectedCompany.value) return targetTrainAgent.value = agentId