update
This commit is contained in:
parent
72182b32f5
commit
dfe436d178
25
README.md
25
README.md
@ -4,28 +4,19 @@
|
||||
> 简体中文 < | <a href="https://www.google.com/search?q=best+website+to+learn+chinese&newwindow=1&sca_esv=a76695392a9980a7&rlz=1C1CHBD_zh-HKHK1072HK1072&sxsrf=ADLYWIKib09skMzGw8JqpTv2AWB6Xk8uZQ%3A1716986674152&ei=MiNXZsP3COrk2roPt-SrgA0&oq=best+website+to+learn+chin&gs_lp=Egxnd3Mtd2l6LXNlcnAiGmJlc3Qgd2Vic2l0ZSB0byBsZWFybiBjaGluKgIIADIIEAAYgAQYywEyCBAAGIAEGMsBMggQABiABBiiBDIIEAAYgAQYogQyCBAAGIAEGKIEMggQABiABBiiBDIIEAAYgAQYogRItDNQtgNY3C5wAXgAkAEFmAHMBKABlzaqAQwwLjE4LjUuNC4wLjK4AQPIAQD4AQGYAhigAosmwgINECMYgAQYsAMYJxiKBcICDBAAGIAEGLADGAoYDMICERAuGIAEGLADGMcBGMsBGK8BwgIHEAAYsAMYHsICCRAAGLADGAgYHsICBBAjGCfCAhAQLhiABBjRAxhDGMcBGIoFwgIFEAAYgATCAgsQABiABBixAxiDAcICExAuGIAEGNEDGMcBGMkDGAoYywHCAgsQABiABBiSAxiKBcICBhAAGAgYHsICIhAuGIAEGNEDGMcBGMkDGAoYywEYlwUY3AQY3gQY4ATYAQHCAhcQLhiABBjHARiYBRiZBRjLARieBRivAcICERAuGIAEGNEDGMcBGMkDGMsBwgImEC4YgAQYxwEYmAUYmQUYywEYngUYrwEYlwUY3AQY3gQY4ATYAQHCAgYQABgeGA-YAwCIBgGQBgq6BgYIARABGBSSBwoxLjE2LjUuMS4xoAf2rwE&sclient=gws-wiz-serp" target="_blank">English</a>
|
||||
</div>
|
||||
|
||||
## 结构
|
||||
|
||||
## 环境搭建
|
||||
每一个服务都放在 ./node 中,根据各自的 readme 分别配置。
|
||||
|
||||
你需要准备的环境变量:
|
||||
|
||||
```bash
|
||||
git clone xxxxxx
|
||||
npm install pm2 -g
|
||||
yarn
|
||||
pip install -r requirements.txt
|
||||
export DEEPSEEK_API_TOKEN=xxx
|
||||
export OMCP_DISCORD_SERVER_IP=xxx
|
||||
export OMCP_DISCORD_SERVER_PORT=xxx
|
||||
export OMCP_DISCORD_TOKEN=xxx
|
||||
```
|
||||
|
||||
下载最新版本的 Lagrange.core ,并完成初始化。完成后根目录为
|
||||
|
||||
```
|
||||
📁bot
|
||||
📁rag
|
||||
📁app
|
||||
├─📁publish
|
||||
│ ├─🏷️...
|
||||
│ ├─🏷️appsettings.json
|
||||
│ └─🪛Lagrange.OneBot
|
||||
└─🎗️...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
51
config.yaml
51
config.yaml
@ -1,4 +1,47 @@
|
||||
mcp-server:
|
||||
port: 25565
|
||||
bot:
|
||||
port: 25564
|
||||
towardsdatascience:
|
||||
route: /get-news-from-towards-science
|
||||
prompt:
|
||||
请通过 headless 的方式帮我搜集 https://towardsdatascience.com/tag/editors-pick/ 最热门的前三条信息,这些信息大概率在 main 元素的 ul li 下面。
|
||||
|
||||
请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。
|
||||
|
||||
然后帮我进入这些网站后总结相关信息,文章通常也在 <main> 中,返回翻译好的简体中文,并且整理成一个简单的资讯。
|
||||
按以下格式输出(严格禁止添加任何额外引导语或总结句):
|
||||
|
||||
[用一句吸引人的话开头,例如:"⌨️ 今日份 AI & CS 技术文章分享"]
|
||||
|
||||
📌 [翻译成简体中文的文章标题]
|
||||
摘要:[文章的核心观点,100字以内]
|
||||
作者:[若存在]
|
||||
发布时间:[若存在]
|
||||
链接:[文章URL]
|
||||
|
||||
mcp:
|
||||
command: node ~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js
|
||||
|
||||
settings:
|
||||
systemPrompt: 'you are a clever bot'
|
||||
|
||||
hacknews:
|
||||
route: /get-news-from-hack-news
|
||||
prompt:
|
||||
请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。
|
||||
|
||||
请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。
|
||||
|
||||
然后帮我进入这些网站后总结相关信息,返回翻译好的简体中文,并且整理成一个简单的资讯。
|
||||
按以下格式输出(严格禁止添加任何额外引导语或总结句):
|
||||
|
||||
[用一句吸引人的话开头,例如:"⌨️ 今日份 AI & CS 技术文章分享"]
|
||||
|
||||
📌 [翻译成简体中文的文章标题]
|
||||
摘要:[文章的核心观点,100字以内]
|
||||
作者:[若存在]
|
||||
发布时间:[若存在]
|
||||
链接:[文章URL]`.
|
||||
|
||||
mcp:
|
||||
command: node ~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js
|
||||
|
||||
settings:
|
||||
systemPrompt: 'you are a clever bot'
|
0
node/.gitignore
vendored
Normal file
0
node/.gitignore
vendored
Normal file
3
node/Lagrange.Core/.gitignore
vendored
3
node/Lagrange.Core/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
*.json
|
||||
*.db
|
||||
*.png
|
||||
*.OneBot
|
||||
*.OneBot
|
||||
lagrange-0-db
|
Binary file not shown.
Binary file not shown.
106
node/bot/package-lock.json
generated
106
node/bot/package-lock.json
generated
@ -12,12 +12,15 @@
|
||||
"@types/node": "^20.12.12",
|
||||
"axios": "^1.7.2",
|
||||
"fs": "^0.0.1-security",
|
||||
"lagrange.onebot": "^1.0.2",
|
||||
"json-bigint": "^1.0.0",
|
||||
"lagrange.onebot": "^1.0.8",
|
||||
"node-schedule": "^2.1.1",
|
||||
"ws": "^8.17.0",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sinonjs/referee-sinon": "^12.0.0",
|
||||
"@types/node-schedule": "^2.1.7",
|
||||
"@types/ws": "^8.5.10",
|
||||
"chai": "4.3.4",
|
||||
"jsverify": "^0.8.4",
|
||||
@ -439,6 +442,16 @@
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node-schedule": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node-schedule/-/node-schedule-2.1.7.tgz",
|
||||
"integrity": "sha512-G7Z3R9H7r3TowoH6D2pkzUHPhcJrDF4Jz1JOQ80AX0K2DWTHoN9VC94XzFAPNMdbW9TBzMZ3LjpFi7RYdbxtXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
|
||||
@ -706,6 +719,15 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bignumber.js": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.0.tgz",
|
||||
"integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
@ -1211,6 +1233,18 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cron-parser": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-4.9.0.tgz",
|
||||
"integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"luxon": "^3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/croner": {
|
||||
"version": "4.1.97",
|
||||
"resolved": "https://registry.npmmirror.com/croner/-/croner-4.1.97.tgz",
|
||||
@ -2400,6 +2434,15 @@
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
@ -2457,18 +2500,34 @@
|
||||
"license": "(MIT OR Apache-2.0)"
|
||||
},
|
||||
"node_modules/lagrange.onebot": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/lagrange.onebot/-/lagrange.onebot-1.0.4.tgz",
|
||||
"integrity": "sha512-Ll/8d7H3Sea/Ajy5lj4ReBf8JmfR3ptgCWbF/7UYZ/lTtmA8Lms18p1XDR8El90AsARRIqYDmlk92vfOGcNewg==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/lagrange.onebot/-/lagrange.onebot-1.0.8.tgz",
|
||||
"integrity": "sha512-x2IdveKDW5GLm6NJRYjV+/6lAds94AQN3Ow00bFBqFTtZDAED6AKBCnIrrkyfns1OXpXJrZXbdGfny11zgtdmA==",
|
||||
"dependencies": {
|
||||
"@ptkdev/logger": "^1.8.0",
|
||||
"@types/node": "^20.12.12",
|
||||
"axios": "^1.7.2",
|
||||
"@types/node": "22.15.16",
|
||||
"axios": "^1.9.0",
|
||||
"node-schedule": "^2.1.1",
|
||||
"pm2": "^5.4.0",
|
||||
"ws": "^8.17.0",
|
||||
"yaml": "^2.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lagrange.onebot/node_modules/@types/node": {
|
||||
"version": "22.15.16",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-22.15.16.tgz",
|
||||
"integrity": "sha512-3pr+KjwpVujqWqOKT8mNR+rd09FqhBLwg+5L/4t0cNYBzm/yEiYGCxWttjaPBsLtAo+WFNoXzGJfolM1JuRXoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lagrange.onebot/node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lazy": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/lazy/-/lazy-1.0.11.tgz",
|
||||
@ -2542,6 +2601,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/long-timeout": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/long-timeout/-/long-timeout-0.1.1.tgz",
|
||||
"integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lowdb": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/lowdb/-/lowdb-1.0.0.tgz",
|
||||
@ -2567,6 +2632,15 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.6.1.tgz",
|
||||
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@ -2805,6 +2879,20 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/node-schedule": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/node-schedule/-/node-schedule-2.1.1.tgz",
|
||||
"integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron-parser": "^4.2.0",
|
||||
"long-timeout": "0.1.1",
|
||||
"sorted-array-functions": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@ -3872,6 +3960,12 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/sorted-array-functions": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
|
||||
"integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
|
||||
|
@ -18,12 +18,15 @@
|
||||
"@types/node": "^20.12.12",
|
||||
"axios": "^1.7.2",
|
||||
"fs": "^0.0.1-security",
|
||||
"lagrange.onebot": "^1.0.2",
|
||||
"json-bigint": "^1.0.0",
|
||||
"lagrange.onebot": "^1.0.8",
|
||||
"node-schedule": "^2.1.1",
|
||||
"ws": "^8.17.0",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sinonjs/referee-sinon": "^12.0.0",
|
||||
"@types/node-schedule": "^2.1.7",
|
||||
"@types/ws": "^8.5.10",
|
||||
"chai": "4.3.4",
|
||||
"jsverify": "^0.8.4",
|
||||
|
18
node/bot/src/global.ts
Normal file
18
node/bot/src/global.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export const qq_groups = {
|
||||
/**
|
||||
* OpenMCP正式级技术组
|
||||
*/
|
||||
OPENMCP_DEV: 782833642,
|
||||
|
||||
/**
|
||||
* Tiphereth 测试频道
|
||||
*/
|
||||
TEST_CHANNEL: 1046693162
|
||||
};
|
||||
|
||||
export const qq_users = {
|
||||
/**
|
||||
* 锦恢 dayo !
|
||||
*/
|
||||
JIN_HUI: 1193466151
|
||||
};
|
@ -1,14 +1,23 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { server } from 'lagrange.onebot';
|
||||
import './services/test';
|
||||
|
||||
import { TestChannel } from './test-channel/test-channel.controller';
|
||||
import { OpenMcpChannel } from './openmcp-dev/openmcp-dev.controller';
|
||||
import { qq_users } from './global';
|
||||
|
||||
// 注册的模块
|
||||
export const InstallChannels = [
|
||||
TestChannel,
|
||||
OpenMcpChannel
|
||||
];
|
||||
|
||||
server.onMounted(c => {
|
||||
c.sendPrivateMsg(1193466151, '成功上线');
|
||||
c.sendPrivateMsg(qq_users.JIN_HUI, '成功上线');
|
||||
});
|
||||
|
||||
server.onUnmounted(c => {
|
||||
c.sendPrivateMsg(1193466151, '成功下线');
|
||||
c.sendPrivateMsg(qq_users.JIN_HUI, '成功下线');
|
||||
});
|
||||
|
||||
|
||||
@ -21,4 +30,8 @@ server.run({
|
||||
port: impl.Port,
|
||||
path: impl.Suffix,
|
||||
qq: 1542544558
|
||||
});
|
||||
|
||||
InstallChannels.forEach(channel => {
|
||||
console.log('上线模块:' + channel.name);
|
||||
});
|
97
node/bot/src/openmcp-dev/openmcp-dev.controller.ts
Normal file
97
node/bot/src/openmcp-dev/openmcp-dev.controller.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import '../plugins/image';
|
||||
import * as path from 'path';
|
||||
|
||||
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger, ApproveMessage, Message } from 'lagrange.onebot';
|
||||
import { getNews, publishOpenMCP } from './openmcp-dev.service';
|
||||
import { qq_groups, qq_users } from '../global';
|
||||
import { parseCommand, sendMessageToDiscord } from '../util';
|
||||
import axios from 'axios';
|
||||
|
||||
console.log('activate ' + path.basename(__filename));
|
||||
|
||||
const visitCache = new Map<string, number>();
|
||||
|
||||
export class OpenMcpChannel {
|
||||
|
||||
@mapper.onGroup(qq_groups.OPENMCP_DEV, { onlyAdmin: true })
|
||||
async handleTestChannel(c: LagrangeContext<GroupMessage>) {
|
||||
const text = c.getRawText();
|
||||
|
||||
const commandResult = parseCommand(text);
|
||||
if (commandResult !== undefined) {
|
||||
// 校验身份
|
||||
|
||||
if (c.message.user_id === qq_users.JIN_HUI) {
|
||||
const now = Date.now();
|
||||
const lastVisit = visitCache.get(c.message.user_id.toString());
|
||||
|
||||
const info = await c.getGroupMemberInfo(c.message.group_id, c.message.user_id);
|
||||
const role = info['data'].role;
|
||||
const name = info['data'].nickname;
|
||||
|
||||
if (!lastVisit || (now - lastVisit) > 10 * 60 * 1000) {
|
||||
c.sendMessage('检测到超级管理员,TIP 系统允许访问,正在执行 ' + JSON.stringify(commandResult));
|
||||
visitCache.set(c.message.user_id.toString(), now);
|
||||
}
|
||||
}
|
||||
|
||||
const { command, args } = commandResult;
|
||||
|
||||
switch (command) {
|
||||
case 'news':
|
||||
await getNews(c);
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
c.sendMessage('ping');
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
if (args.length === 0) {
|
||||
c.sendMessage('usage\n:file filename');
|
||||
} else {
|
||||
const filename = args[0];
|
||||
const file = path.join('/home/kirigaya/download/', filename);
|
||||
c.uploadGroupFile(c.message.group_id, file, filename);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'notice':
|
||||
c.sendGroupNotice(c.message.group_id, 'hello world');
|
||||
break;
|
||||
|
||||
case 'get-notice':
|
||||
const notice = await c.getGroupNotice(c.message.group_id);
|
||||
c.sendMessage('当前的公告内容为:' + JSON.stringify(notice, null, 2));
|
||||
break;
|
||||
|
||||
case 'pub':
|
||||
await publishOpenMCP(c);
|
||||
break;
|
||||
|
||||
case 'discord':
|
||||
await sendMessageToDiscord('hello from qq');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@mapper.onGroupIncrease(qq_groups.OPENMCP_DEV)
|
||||
async handleTestChannelIncrease(c: LagrangeContext<ApproveMessage>) {
|
||||
console.log(c.message);
|
||||
// c.setGroupAddRequest('', c.message.sub_type, true, '');
|
||||
}
|
||||
|
||||
@mapper.createTimeSchedule('0 0 10 * * *')
|
||||
async handleTestChannelSchedule(c: LagrangeContext<Message>) {
|
||||
const res = await axios.post('http://localhost:3000/get-news-from-hack-news');
|
||||
const data = res.data;
|
||||
const message = data.msg;
|
||||
c.sendGroupMsg(qq_groups.OPENMCP_DEV, message);
|
||||
}
|
||||
}
|
32
node/bot/src/openmcp-dev/openmcp-dev.service.ts
Normal file
32
node/bot/src/openmcp-dev/openmcp-dev.service.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import '../plugins/image';
|
||||
import axios from 'axios';
|
||||
|
||||
import { LagrangeContext, PrivateMessage, GroupMessage } from 'lagrange.onebot'
|
||||
import path from 'path';
|
||||
import { sendMessageToDiscord, wait } from '../util';
|
||||
|
||||
export async function getNews(c: LagrangeContext<PrivateMessage | GroupMessage>) {
|
||||
const res = await axios.post('http://localhost:3000/get-news-from-hack-news');
|
||||
const data = res.data;
|
||||
const message = data.msg;
|
||||
console.log('message', message);
|
||||
|
||||
c.sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
export async function publishOpenMCP(c: LagrangeContext<GroupMessage>) {
|
||||
const res = await axios.post('http://localhost:3000/publish-openmcp-client');
|
||||
const data = res.data;
|
||||
const message = data.msg;
|
||||
console.log('message', message);
|
||||
|
||||
const { vsix, content } = message;
|
||||
|
||||
await c.uploadGroupFile(c.message.group_id, vsix, path.basename(vsix));
|
||||
await wait(2000);
|
||||
|
||||
await c.sendGroupNotice(c.message.group_id, content);
|
||||
|
||||
await sendMessageToDiscord(content);
|
||||
}
|
@ -24,4 +24,5 @@ export class Impl {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger } from 'lagrange.onebot';
|
||||
|
||||
console.log('activate ' + __filename);
|
||||
|
||||
export class Impl {
|
||||
@mapper.onGroup(782833642)
|
||||
@plugins.use('echo')
|
||||
@plugins.use('pm')
|
||||
@plugins.use('wget-image')
|
||||
async handleOpenMCP(c: LagrangeContext<GroupMessage>) {
|
||||
const text = c.getRawText();
|
||||
console.log('[receive] ' + text);
|
||||
|
||||
if (c.qq) {
|
||||
|
||||
}
|
||||
|
||||
if (text.startsWith(':')) {
|
||||
const command = text.substring(1);
|
||||
switch (command) {
|
||||
case 'news':
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// c.sendMessage([{
|
||||
// type: 'image',
|
||||
// data: {
|
||||
// file: 'file:///data/zhelonghuang/project/rag-llm/images/bird.png',
|
||||
// timeout: 10000
|
||||
// }
|
||||
// }])
|
||||
c.finishSession();
|
||||
}
|
||||
}
|
@ -3,10 +3,6 @@ import axios from 'axios';
|
||||
|
||||
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger } from 'lagrange.onebot'
|
||||
|
||||
import { apiGetIntentRecogition, apiQueryVecdb } from '../api/vecdb';
|
||||
|
||||
import { handleGroupIntent } from './intent';
|
||||
|
||||
console.log('activate ' + __filename);
|
||||
|
||||
export class Impl {
|
||||
@ -84,4 +80,27 @@ async function getNews(c: LagrangeContext<PrivateMessage>) {
|
||||
console.log('message', message);
|
||||
|
||||
c.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getFile(c: LagrangeContext<PrivateMessage>) {
|
||||
c.sendMessage([
|
||||
// {
|
||||
// type: 'text',
|
||||
// data: {
|
||||
// file: 'file:///data/zhelonghuang/project/rag-llm/images/bird.png',
|
||||
// timeout: 10000
|
||||
// }
|
||||
// }
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
async function publishOpenMCP(c: LagrangeContext<PrivateMessage>) {
|
||||
const res = await axios.post('http://localhost:3000/get-news-from-towards-science');
|
||||
const data = res.data;
|
||||
const message = data.msg;
|
||||
console.log('message', message);
|
||||
|
||||
c.sendMessage(message);
|
||||
}
|
||||
|
121
node/bot/src/test-channel/test-channel.controller.ts
Normal file
121
node/bot/src/test-channel/test-channel.controller.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import '../plugins/image';
|
||||
import * as path from 'path';
|
||||
|
||||
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger, ApproveMessage, Message } from 'lagrange.onebot';
|
||||
import { getNews, publishOpenMCP } from './test-channel.service';
|
||||
import { qq_groups, qq_users } from '../global';
|
||||
import { parseCommand, sendMessageToDiscord } from '../util';
|
||||
|
||||
console.log('activate ' + path.basename(__filename));
|
||||
|
||||
const visitCache = new Map<string, number>();
|
||||
|
||||
export class TestChannel {
|
||||
|
||||
@mapper.onPrivateUser(qq_users.JIN_HUI)
|
||||
@plugins.use('echo')
|
||||
@plugins.use('pm')
|
||||
@plugins.use('wget-image')
|
||||
async handleJinhui(c: LagrangeContext<PrivateMessage>) {
|
||||
|
||||
const text = c.getRawText();
|
||||
console.log('[receive] ' + text);
|
||||
|
||||
if (text.startsWith(':')) {
|
||||
const command = text.substring(1);
|
||||
switch (command) {
|
||||
case 'news':
|
||||
await getNews(c);
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
c.sendMessage('ping');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c.finishSession();
|
||||
}
|
||||
|
||||
@mapper.onGroup(qq_groups.TEST_CHANNEL, { onlyAdmin: true })
|
||||
async handleTestChannel(c: LagrangeContext<GroupMessage>) {
|
||||
const text = c.getRawText();
|
||||
|
||||
const commandResult = parseCommand(text);
|
||||
if (commandResult !== undefined) {
|
||||
// 校验身份
|
||||
|
||||
if (c.message.user_id === qq_users.JIN_HUI) {
|
||||
const now = Date.now();
|
||||
const lastVisit = visitCache.get(c.message.user_id.toString());
|
||||
|
||||
const info = await c.getGroupMemberInfo(c.message.group_id, c.message.user_id);
|
||||
const role = info['data'].role;
|
||||
const name = info['data'].nickname;
|
||||
|
||||
if (!lastVisit || (now - lastVisit) > 10 * 60 * 1000) {
|
||||
c.sendMessage('检测到超级管理员,TIP 系统允许访问,正在执行 ' + JSON.stringify(commandResult));
|
||||
visitCache.set(c.message.user_id.toString(), now);
|
||||
}
|
||||
}
|
||||
|
||||
const { command, args } = commandResult;
|
||||
|
||||
switch (command) {
|
||||
case 'news':
|
||||
await getNews(c);
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
c.sendMessage('ping');
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
if (args.length === 0) {
|
||||
c.sendMessage('usage\n:file filename');
|
||||
} else {
|
||||
const filename = args[0];
|
||||
const file = path.join('/home/kirigaya/download/', filename);
|
||||
c.uploadGroupFile(c.message.group_id, file, filename);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'notice':
|
||||
c.sendGroupNotice(c.message.group_id, 'hello world');
|
||||
break;
|
||||
|
||||
case 'get-notice':
|
||||
const notice = await c.getGroupNotice(c.message.group_id);
|
||||
c.sendMessage('当前的公告内容为:' + JSON.stringify(notice, null, 2));
|
||||
break;
|
||||
|
||||
case 'pub':
|
||||
await publishOpenMCP(c);
|
||||
break;
|
||||
|
||||
case 'discord':
|
||||
await sendMessageToDiscord('hello from qq');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@mapper.onGroupIncrease(qq_groups.TEST_CHANNEL)
|
||||
async handleTestChannelIncrease(c: LagrangeContext<ApproveMessage>) {
|
||||
console.log(c.message);
|
||||
c.setGroupAddRequest('', c.message.sub_type, true, '');
|
||||
}
|
||||
|
||||
@mapper.createTimeSchedule('0 11 20 * * *')
|
||||
async handleTestChannelSchedule(c: LagrangeContext<Message>) {
|
||||
c.sendPrivateMsg(qq_users.JIN_HUI, 'hello');
|
||||
}
|
||||
}
|
32
node/bot/src/test-channel/test-channel.service.ts
Normal file
32
node/bot/src/test-channel/test-channel.service.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import '../plugins/image';
|
||||
import axios from 'axios';
|
||||
|
||||
import { LagrangeContext, PrivateMessage, GroupMessage } from 'lagrange.onebot'
|
||||
import path from 'path';
|
||||
import { sendMessageToDiscord, wait } from '../util';
|
||||
|
||||
export async function getNews(c: LagrangeContext<PrivateMessage | GroupMessage>) {
|
||||
const res = await axios.post('http://localhost:3000/get-news-from-hack-news');
|
||||
const data = res.data;
|
||||
const message = data.msg;
|
||||
console.log('message', message);
|
||||
|
||||
c.sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
export async function publishOpenMCP(c: LagrangeContext<GroupMessage>) {
|
||||
const res = await axios.post('http://localhost:3000/publish-openmcp-client');
|
||||
const data = res.data;
|
||||
const message = data.msg;
|
||||
console.log('message', message);
|
||||
|
||||
const { vsix, content } = message;
|
||||
|
||||
await c.uploadGroupFile(c.message.group_id, vsix, path.basename(vsix));
|
||||
await wait(2000);
|
||||
|
||||
await c.sendGroupNotice(c.message.group_id, content);
|
||||
|
||||
await sendMessageToDiscord(content);
|
||||
}
|
53
node/bot/src/util.ts
Normal file
53
node/bot/src/util.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import axios from "axios";
|
||||
|
||||
export function parseCommand(text: string) {
|
||||
if (text.startsWith(':')) {
|
||||
text = text.substring(1);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const command = text.split(' ')[0];
|
||||
const args = text.split(' ').slice(1);
|
||||
return {
|
||||
command,
|
||||
args
|
||||
}
|
||||
}
|
||||
|
||||
export function wait(timeout: number) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(void 0);
|
||||
}, timeout);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export async function sendMessageToDiscord(message: string) {
|
||||
const ip = process.env['OMCP_DISCORD_SERVER_IP'];
|
||||
const port = process.env['OMCP_DISCORD_SERVER_PORT'];
|
||||
const token = process.env['OMCP_DISCORD_TOKEN'];
|
||||
try {
|
||||
const response = await axios({
|
||||
method: 'post',
|
||||
url: `http://${ip}:${port}/api/message`,
|
||||
headers: {
|
||||
'Authorization': token,
|
||||
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': '*/*',
|
||||
'Host': `${ip}:${port}`,
|
||||
'Connection': 'keep-alive'
|
||||
},
|
||||
data: {
|
||||
content: message,
|
||||
channel: '1368185363549651107'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Response:', response.data);
|
||||
} catch (error) {
|
||||
console.error('Error:', error.response ? error.response.data : error.message);
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "mcp-server",
|
||||
"name": "faas",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mcp-server",
|
||||
"name": "faas",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@ -13,12 +13,14 @@
|
||||
"bson": "^6.10.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"morgan": "^1.10.0",
|
||||
"openmcp-sdk": "^0.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.18",
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/morgan": "^1.9.9",
|
||||
"@types/node": "^22.15.17",
|
||||
"ts-node": "^10.9.2",
|
||||
@ -239,6 +241,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/js-yaml": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmmirror.com/@types/js-yaml/-/js-yaml-4.0.9.tgz",
|
||||
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
|
||||
@ -486,6 +495,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@ -1370,6 +1385,18 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/lie/-/lie-3.1.1.tgz",
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "mcp-server",
|
||||
"name": "faas",
|
||||
"version": "1.0.0",
|
||||
"description": "执行 mcp 部分的模块",
|
||||
"main": "index.js",
|
||||
@ -13,12 +13,14 @@
|
||||
"bson": "^6.10.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"morgan": "^1.10.0",
|
||||
"openmcp-sdk": "^0.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.18",
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/morgan": "^1.9.9",
|
||||
"@types/node": "^22.15.17",
|
||||
"ts-node": "^10.9.2",
|
@ -0,0 +1,287 @@
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。\n\n请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。\n\n然后帮我进入这些网站后总结相关信息,返回翻译好的简体中文,并且整理成一个简单的资讯。\n按以下格式输出(严格禁止添加任何额外引导语或总结句):\n\n[用一句吸引人的话开头,例如:\"⌨️ 今日份 AI & CS 技术文章分享\"]\n\n📌 [翻译成简体中文的文章标题]\n摘要:[文章的核心观点,100字以内]\n作者:[若存在]\n发布时间:[若存在]\n链接:[文章URL]",
|
||||
"extraInfo": {
|
||||
"created": 1747073959697,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_a0f34b9b-d93a-437d-af28-bf96d68555f3",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747073967399,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_a0f34b9b-d93a-437d-af28-bf96d68555f3",
|
||||
"content": [
|
||||
{
|
||||
"type": "error",
|
||||
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747073997731,
|
||||
"state": "tool call failed",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1071,
|
||||
"completion_tokens": 36,
|
||||
"total_tokens": 1107,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1024
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1024,
|
||||
"prompt_cache_miss_tokens": 47
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_0e73ade3-e100-4f12-b3cd-505cf31a3e26",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true,\"args\":[\"--no-sandbox\"]},\"allowDangerous\":true}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747074003265,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_0e73ade3-e100-4f12-b3cd-505cf31a3e26",
|
||||
"content": [
|
||||
{
|
||||
"type": "error",
|
||||
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747074034143,
|
||||
"state": "tool call failed",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1141,
|
||||
"completion_tokens": 54,
|
||||
"total_tokens": 1195,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1088
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1088,
|
||||
"prompt_cache_miss_tokens": 53
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "很抱歉,我无法直接访问该网站以获取信息。您可以尝试手动访问 [Hacker News](https://news.ycombinator.com/) 并查看最热门的前三条信息,然后我可以帮助您整理和翻译相关内容。",
|
||||
"extraInfo": {
|
||||
"created": 1747074039413,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1229,
|
||||
"completion_tokens": 49,
|
||||
"total_tokens": 1278,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1216
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1216,
|
||||
"prompt_cache_miss_tokens": 13
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"temperature": 0.7,
|
||||
"enableTools": [
|
||||
{
|
||||
"name": "k_navigate",
|
||||
"description": "Navigate to a URL",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "URL to navigate to"
|
||||
},
|
||||
"launchOptions": {
|
||||
"type": "object",
|
||||
"description": "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }"
|
||||
},
|
||||
"allowDangerous": {
|
||||
"type": "boolean",
|
||||
"description": "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_screenshot",
|
||||
"description": "Take a screenshot of the current page or a specific element",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name for the screenshot"
|
||||
},
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to screenshot"
|
||||
},
|
||||
"width": {
|
||||
"type": "number",
|
||||
"description": "Width in pixels (default: 800)"
|
||||
},
|
||||
"height": {
|
||||
"type": "number",
|
||||
"description": "Height in pixels (default: 600)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_click",
|
||||
"description": "Click an element on the page",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to click"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_fill",
|
||||
"description": "Fill out an input field",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for input field"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Value to fill"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_select",
|
||||
"description": "Select an element on the page with Select tag",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to select"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Value to select"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_hover",
|
||||
"description": "Hover an element on the page",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to hover"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_evaluate",
|
||||
"description": "Execute JavaScript in the browser console",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "JavaScript code to execute"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"systemPrompt": "",
|
||||
"contextLength": 20
|
||||
},
|
||||
"mcp": {
|
||||
"connectionType": "STDIO",
|
||||
"command": "node",
|
||||
"args": [
|
||||
"~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,287 @@
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。\n\n请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。\n\n然后帮我进入这些网站后总结相关信息,返回翻译好的简体中文,并且整理成一个简单的资讯。\n按以下格式输出(严格禁止添加任何额外引导语或总结句):\n\n[用一句吸引人的话开头,例如:\"⌨️ 今日份 AI & CS 技术文章分享\"]\n\n📌 [翻译成简体中文的文章标题]\n摘要:[文章的核心观点,100字以内]\n作者:[若存在]\n发布时间:[若存在]\n链接:[文章URL]",
|
||||
"extraInfo": {
|
||||
"created": 1747074265643,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_27a3e2af-e707-4eaf-909c-2fa9f1075667",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747074272702,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_27a3e2af-e707-4eaf-909c-2fa9f1075667",
|
||||
"content": [
|
||||
{
|
||||
"type": "error",
|
||||
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747074303017,
|
||||
"state": "tool call failed",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1071,
|
||||
"completion_tokens": 36,
|
||||
"total_tokens": 1107,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1024
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1024,
|
||||
"prompt_cache_miss_tokens": 47
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_273aedd5-6187-403d-bb53-404b293360b7",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true,\"args\":[\"--no-sandbox\"]},\"allowDangerous\":true}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747074308989,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_273aedd5-6187-403d-bb53-404b293360b7",
|
||||
"content": [
|
||||
{
|
||||
"type": "error",
|
||||
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747074339889,
|
||||
"state": "tool call failed",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1141,
|
||||
"completion_tokens": 54,
|
||||
"total_tokens": 1195,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1088
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1088,
|
||||
"prompt_cache_miss_tokens": 53
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "很抱歉,我无法访问该网站以获取所需的信息。可能是由于网站的反爬机制或其他技术限制导致无法加载页面。如果您有其他需求或可以尝试其他方法,请告诉我!",
|
||||
"extraInfo": {
|
||||
"created": 1747074347931,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1229,
|
||||
"completion_tokens": 39,
|
||||
"total_tokens": 1268,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1216
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1216,
|
||||
"prompt_cache_miss_tokens": 13
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"temperature": 0.7,
|
||||
"enableTools": [
|
||||
{
|
||||
"name": "k_navigate",
|
||||
"description": "Navigate to a URL",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "URL to navigate to"
|
||||
},
|
||||
"launchOptions": {
|
||||
"type": "object",
|
||||
"description": "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }"
|
||||
},
|
||||
"allowDangerous": {
|
||||
"type": "boolean",
|
||||
"description": "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_screenshot",
|
||||
"description": "Take a screenshot of the current page or a specific element",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name for the screenshot"
|
||||
},
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to screenshot"
|
||||
},
|
||||
"width": {
|
||||
"type": "number",
|
||||
"description": "Width in pixels (default: 800)"
|
||||
},
|
||||
"height": {
|
||||
"type": "number",
|
||||
"description": "Height in pixels (default: 600)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_click",
|
||||
"description": "Click an element on the page",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to click"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_fill",
|
||||
"description": "Fill out an input field",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for input field"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Value to fill"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_select",
|
||||
"description": "Select an element on the page with Select tag",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to select"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Value to select"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_hover",
|
||||
"description": "Hover an element on the page",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to hover"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_evaluate",
|
||||
"description": "Execute JavaScript in the browser console",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "JavaScript code to execute"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"systemPrompt": "",
|
||||
"contextLength": 20
|
||||
},
|
||||
"mcp": {
|
||||
"connectionType": "STDIO",
|
||||
"command": "node",
|
||||
"args": [
|
||||
"~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js"
|
||||
]
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,563 @@
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。\n\n请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。\n\n然后帮我进入这些网站后总结相关信息,返回翻译好的简体中文,并且整理成一个简单的资讯。\n按以下格式输出(严格禁止添加任何额外引导语或总结句):\n\n[用一句吸引人的话开头,例如:\"⌨️ 今日份 AI & CS 技术文章分享\"]\n\n📌 [翻译成简体中文的文章标题]\n摘要:[文章的核心观点,100字以内]\n作者:[若存在]\n发布时间:[若存在]\n链接:[文章URL]",
|
||||
"extraInfo": {
|
||||
"created": 1747101600320,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_d1b875c1-5a16-4136-a2f4-53648127051f",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101606670,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_d1b875c1-5a16-4136-a2f4-53648127051f",
|
||||
"content": [
|
||||
{
|
||||
"type": "error",
|
||||
"text": "McpError: MCP error -32603: net::ERR_CONNECTION_RESET at https://news.ycombinator.com/"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101616953,
|
||||
"state": "tool call failed",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1071,
|
||||
"completion_tokens": 36,
|
||||
"total_tokens": 1107,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1024
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1024,
|
||||
"prompt_cache_miss_tokens": 47
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_16d05805-8a72-4fb2-b002-6acc27237b94",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true,\"args\":[\"--no-sandbox\"]},\"allowDangerous\":true}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101623731,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_16d05805-8a72-4fb2-b002-6acc27237b94",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Navigated to https://news.ycombinator.com/"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101628493,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1150,
|
||||
"completion_tokens": 54,
|
||||
"total_tokens": 1204,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1088
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1088,
|
||||
"prompt_cache_miss_tokens": 62
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_c1b1ae98-17cd-4ef6-b4a7-5209e80c7695",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.documentElement.outerHTML\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101633965,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_c1b1ae98-17cd-4ef6-b4a7-5209e80c7695",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"<html><head></head><body></body></html>\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101634989,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1230,
|
||||
"completion_tokens": 25,
|
||||
"total_tokens": 1255,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1152
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1152,
|
||||
"prompt_cache_miss_tokens": 78
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_4d783557-5cfc-428b-ae7c-8535ce181d57",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.body.outerHTML\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101640718,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_4d783557-5cfc-428b-ae7c-8535ce181d57",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"<body></body>\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101640724,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1296,
|
||||
"completion_tokens": 23,
|
||||
"total_tokens": 1319,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1216
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1216,
|
||||
"prompt_cache_miss_tokens": 80
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_53fcd540-014c-4bc3-9ba6-15a9d5b65b3b",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.querySelectorAll('a.storylink').length\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101646038,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_53fcd540-014c-4bc3-9ba6-15a9d5b65b3b",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n0\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101646042,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1352,
|
||||
"completion_tokens": 28,
|
||||
"total_tokens": 1380,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1280
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1280,
|
||||
"prompt_cache_miss_tokens": 72
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_7ec588aa-9a81-41ad-9d5e-1cab03e5e7ef",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.querySelectorAll('a').length\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101651231,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_7ec588aa-9a81-41ad-9d5e-1cab03e5e7ef",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n0\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101651236,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1406,
|
||||
"completion_tokens": 25,
|
||||
"total_tokens": 1431,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1344
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1344,
|
||||
"prompt_cache_miss_tokens": 62
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_77caa460-fd9a-41a9-9db6-98c7d65bd59f",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.querySelectorAll('*').length\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101656646,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_77caa460-fd9a-41a9-9db6-98c7d65bd59f",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n3\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101656651,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1457,
|
||||
"completion_tokens": 25,
|
||||
"total_tokens": 1482,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1408
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1408,
|
||||
"prompt_cache_miss_tokens": 49
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_379c9e3f-c525-4cde-965c-dea7635f116b",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"Array.from(document.querySelectorAll('*')).map(el => el.outerHTML).join('\\\\n')\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101663316,
|
||||
"state": "success",
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_0_379c9e3f-c525-4cde-965c-dea7635f116b",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"<html><head></head><body></body></html>\\n<head></head>\\n<body></body>\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747101663320,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1508,
|
||||
"completion_tokens": 39,
|
||||
"total_tokens": 1547,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1472
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1472,
|
||||
"prompt_cache_miss_tokens": 36
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "由于页面加载失败或内容未被正确渲染,无法获取 Hacker News 的热门信息。建议稍后重试或检查网络设置。",
|
||||
"extraInfo": {
|
||||
"created": 1747101669791,
|
||||
"state": "success",
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 1600,
|
||||
"completion_tokens": 30,
|
||||
"total_tokens": 1630,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 1536
|
||||
},
|
||||
"prompt_cache_hit_tokens": 1536,
|
||||
"prompt_cache_miss_tokens": 64
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"temperature": 0.7,
|
||||
"enableTools": [
|
||||
{
|
||||
"name": "k_navigate",
|
||||
"description": "Navigate to a URL",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "URL to navigate to"
|
||||
},
|
||||
"launchOptions": {
|
||||
"type": "object",
|
||||
"description": "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }"
|
||||
},
|
||||
"allowDangerous": {
|
||||
"type": "boolean",
|
||||
"description": "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_screenshot",
|
||||
"description": "Take a screenshot of the current page or a specific element",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name for the screenshot"
|
||||
},
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to screenshot"
|
||||
},
|
||||
"width": {
|
||||
"type": "number",
|
||||
"description": "Width in pixels (default: 800)"
|
||||
},
|
||||
"height": {
|
||||
"type": "number",
|
||||
"description": "Height in pixels (default: 600)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_click",
|
||||
"description": "Click an element on the page",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to click"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_fill",
|
||||
"description": "Fill out an input field",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for input field"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Value to fill"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_select",
|
||||
"description": "Select an element on the page with Select tag",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to select"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Value to select"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_hover",
|
||||
"description": "Hover an element on the page",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"description": "CSS selector for element to hover"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"selector"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_evaluate",
|
||||
"description": "Execute JavaScript in the browser console",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "JavaScript code to execute"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"systemPrompt": "",
|
||||
"contextLength": 20
|
||||
},
|
||||
"mcp": {
|
||||
"connectionType": "STDIO",
|
||||
"command": "node",
|
||||
"args": [
|
||||
"~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js"
|
||||
]
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -8,6 +8,9 @@ const corsOptions = {
|
||||
};
|
||||
|
||||
export const app = express();
|
||||
export const testRouter = express.Router();
|
||||
|
||||
app.use(express.json());
|
||||
app.use(cors(corsOptions));
|
||||
app.use(morgan('dev'));
|
||||
app.use(morgan('dev'));
|
||||
app.use('/test', testRouter);
|
@ -1,23 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { app } from './common';
|
||||
import express, { Request, Response } from 'express';
|
||||
import { app, testRouter } from './common';
|
||||
|
||||
import "./service/news";
|
||||
import "./openmcp/openmcp";
|
||||
|
||||
// 路由为 localhost:3000
|
||||
app.get('/', (req: Request, res: Response) => {
|
||||
res.send('<h1>Hello, World!</h1><br><img src="https://picx.zhimg.com/v2-b4251de7d2499e942c7ebf447a90d2eb_l.jpg"/>');
|
||||
});
|
||||
|
||||
app.post('/hello', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.send('message');
|
||||
} catch (error) {
|
||||
console.log('error happen in /save-view, ' + error);
|
||||
res.send('error')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 运行在 3000 端口
|
||||
const PORT = process.env.PORT || 3000;
|
||||
app.listen(PORT, () => {
|
54
node/faas/src/openmcp/openmcp.ts
Normal file
54
node/faas/src/openmcp/openmcp.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { Request, Response } from "express";
|
||||
import { app } from "../common";
|
||||
import { executeCommand } from "../util";
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { execSync, spawnSync } from 'child_process';
|
||||
import os from 'os';
|
||||
|
||||
export const OPENMCP_CLIENT = os.homedir() + '/project/openmcp-client';
|
||||
|
||||
async function publishOpenMCP() {
|
||||
|
||||
const updateResult = execSync('git pull origin main', { cwd: OPENMCP_CLIENT, env: process.env });
|
||||
console.log(updateResult.toString());
|
||||
|
||||
const rmResult = execSync('rm *.vsix', { cwd: OPENMCP_CLIENT });
|
||||
console.log(rmResult.toString());
|
||||
|
||||
const packageResult = execSync('vsce package', { cwd: OPENMCP_CLIENT });
|
||||
console.log(packageResult.toString());
|
||||
|
||||
// 找到 OPENMCP_CLIENT 下的第一个 vsix 文件,返回绝对路径
|
||||
const vsixFile = fs.readdirSync(OPENMCP_CLIENT).find(file => file.endsWith('.vsix'));
|
||||
const vsixPath = path.join(OPENMCP_CLIENT, vsixFile);
|
||||
|
||||
return vsixPath;
|
||||
}
|
||||
|
||||
function getLastChangeLog() {
|
||||
const changelog = fs.readFileSync(path.join(OPENMCP_CLIENT, 'CHANGELOG.md'), { encoding: 'utf-8' });
|
||||
const newContent = changelog.split('## [main]')[1];
|
||||
const version = newContent.split('\n')[0];
|
||||
const updateContent = newContent.split('\n').slice(1).join('\n');
|
||||
const content = `✴️ openmcp client ${version} 更新内容\n\n` + updateContent.trim() + '\n\n在 vscode/trae/cursor 等编辑器的插件商城搜索【openmcp】就可以下载最新的版本了!';
|
||||
return content
|
||||
}
|
||||
|
||||
|
||||
app.post('/publish-openmcp-client', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const filePath = await publishOpenMCP();
|
||||
const updateContent = getLastChangeLog();
|
||||
res.send({
|
||||
code: 200,
|
||||
msg: {
|
||||
vsix: filePath,
|
||||
content: updateContent
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.send(error.toString());
|
||||
}
|
||||
})
|
32
node/faas/src/service/free-mcp.ts
Normal file
32
node/faas/src/service/free-mcp.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import * as fs from 'fs';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as path from 'path';
|
||||
|
||||
const CONFIG_PATH = path.join(__dirname, '../../../config.yaml');
|
||||
|
||||
|
||||
interface McpConfig {
|
||||
prompt: string;
|
||||
mcp: {
|
||||
command?: string;
|
||||
url?: string;
|
||||
oauth?: string;
|
||||
};
|
||||
settings: {
|
||||
systemPrompt?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function getConfig() {
|
||||
try {
|
||||
const fileContent = fs.readFileSync(CONFIG_PATH, 'utf8');
|
||||
return yaml.load(fileContent);
|
||||
} catch (error) {
|
||||
console.error('Error reading config file:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
// const config = getConfig();
|
||||
// console.log(config.towardsdatascience.prompt);
|
195
node/faas/src/service/news.ts
Normal file
195
node/faas/src/service/news.ts
Normal file
@ -0,0 +1,195 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { app, testRouter } from "../common";
|
||||
import { createTaskContext } from "./common";
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { markdownToPlainText, getFormatedTime } from '../util';
|
||||
|
||||
export async function getNewsFromTowardsScience() {
|
||||
const { taskLoop, adapter } = await createTaskContext();
|
||||
|
||||
const tools = await adapter.listTools();
|
||||
|
||||
// 创建当前事件循环对应的上下文,并且配置当前上下文的设置
|
||||
const storage = {
|
||||
messages: [],
|
||||
settings: {
|
||||
temperature: 0.7,
|
||||
enableTools: tools,
|
||||
systemPrompt: '',
|
||||
contextLength: 20
|
||||
}
|
||||
};
|
||||
|
||||
// 本次发出的问题
|
||||
const message = `
|
||||
请通过 headless 的方式帮我搜集 https://towardsdatascience.com/tag/editors-pick/ 最热门的前三条信息,这些信息大概率在 main 元素的 ul li 下面。
|
||||
|
||||
请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。
|
||||
|
||||
然后帮我进入这些网站后总结相关信息,文章通常也在 <main> 中,返回翻译好的简体中文,并且整理成一个简单的资讯。
|
||||
按以下格式输出(严格禁止添加任何额外引导语或总结句):
|
||||
|
||||
[用一句吸引人的话开头,例如:"⌨️ 今日份 AI & CS 技术文章分享"]
|
||||
|
||||
📌 [翻译成简体中文的文章标题]
|
||||
摘要:[文章的核心观点,100字以内]
|
||||
作者:[若存在]
|
||||
发布时间:[若存在]
|
||||
链接:[文章URL]`.trim();
|
||||
|
||||
|
||||
// 开启事件循环
|
||||
await taskLoop.start(storage, message);
|
||||
|
||||
// 保存数据到本地
|
||||
const filename = getFormatedTime();
|
||||
const savePath = path.join(__dirname, '..', '..', 'runtime-data', 'get-news-from-towards-science', `${filename}.json`);
|
||||
if (!fs.existsSync(path.dirname(savePath))) {
|
||||
fs.mkdirSync(path.dirname(savePath));
|
||||
}
|
||||
fs.writeFileSync(savePath, JSON.stringify(
|
||||
{
|
||||
messages: storage.messages,
|
||||
settings: storage.settings,
|
||||
mcp: {
|
||||
connectionType: 'STDIO',
|
||||
command: 'node',
|
||||
args: ['~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js']
|
||||
}
|
||||
},
|
||||
null, 2
|
||||
));
|
||||
|
||||
|
||||
const lastMessage = storage.messages.at(-1);
|
||||
|
||||
console.log('lastMessage', lastMessage);
|
||||
|
||||
if (lastMessage?.role === 'assistant') {
|
||||
return markdownToPlainText(lastMessage.content);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
export async function getNewsFromHackNews() {
|
||||
const { taskLoop, adapter } = await createTaskContext();
|
||||
|
||||
const tools = await adapter.listTools();
|
||||
|
||||
// 创建当前事件循环对应的上下文,并且配置当前上下文的设置
|
||||
const storage = {
|
||||
messages: [],
|
||||
settings: {
|
||||
temperature: 0.7,
|
||||
enableTools: tools,
|
||||
systemPrompt: '',
|
||||
contextLength: 20
|
||||
}
|
||||
};
|
||||
|
||||
// 本次发出的问题
|
||||
const message = `
|
||||
请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。
|
||||
|
||||
请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。
|
||||
|
||||
然后帮我进入这些网站后总结相关信息,返回翻译好的简体中文,并且整理成一个简单的资讯。
|
||||
按以下格式输出(严格禁止添加任何额外引导语或总结句):
|
||||
|
||||
[用一句吸引人的话开头,例如:"⌨️ 今日份 AI & CS 技术文章分享"]
|
||||
|
||||
📌 [翻译成简体中文的文章标题]
|
||||
摘要:[文章的核心观点,100字以内]
|
||||
作者:[若存在]
|
||||
发布时间:[若存在]
|
||||
链接:[文章URL]`.trim();
|
||||
|
||||
|
||||
// 开启事件循环
|
||||
await taskLoop.start(storage, message);
|
||||
|
||||
// 保存数据到本地
|
||||
const filename = getFormatedTime();
|
||||
const savePath = path.join(__dirname, '..', '..', 'runtime-data', 'get-news-from-towards-science', `${filename}.json`);
|
||||
|
||||
console.log('savePath', savePath);
|
||||
|
||||
if (!fs.existsSync(path.dirname(savePath))) {
|
||||
fs.mkdirSync(path.dirname(savePath), { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(savePath, JSON.stringify(
|
||||
{
|
||||
messages: storage.messages,
|
||||
settings: storage.settings,
|
||||
mcp: {
|
||||
connectionType: 'STDIO',
|
||||
command: 'node',
|
||||
args: ['~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js']
|
||||
}
|
||||
},
|
||||
null, 2
|
||||
));
|
||||
|
||||
console.log('write data to ' + savePath);
|
||||
|
||||
const lastMessage = storage.messages.at(-1);
|
||||
|
||||
console.log('lastMessage', lastMessage);
|
||||
|
||||
if (lastMessage?.role === 'assistant') {
|
||||
return markdownToPlainText(lastMessage.content);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
app.post('/get-news-from-towards-science', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const news = await getNewsFromTowardsScience();
|
||||
res.send({
|
||||
code: 200,
|
||||
msg: news
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.send(error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.post('/get-news-from-hack-news', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const news = await getNewsFromHackNews();
|
||||
res.send({
|
||||
code: 200,
|
||||
msg: news
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.send(error);
|
||||
}
|
||||
});
|
||||
|
||||
testRouter.post('/get-news-from-towards-science', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.send({
|
||||
code: 200,
|
||||
msg: `以下是整理好的咨询信息,并已翻译为简体中文:
|
||||
|
||||
咨询1:Uh-Uh, Not Guilty
|
||||
简介:暂无简介(未能成功获取内容)。
|
||||
原文链接:Uh-Uh, Not Guilty
|
||||
|
||||
咨询2:Regression Discontinuity Design: How It Works and When to Use It
|
||||
简介:从核心概念到实际分析——回归不连续设计(RDD)因果推断的工作原理、如何运行以及如何正确使用。
|
||||
原文链接:Regression Discontinuity Design
|
||||
|
||||
由于技术原因,未能成功获取第一条咨询的详细内容。如果需要进一步帮助,请告诉我!`
|
||||
});
|
||||
} catch (error) {
|
||||
res.send(error);
|
||||
}
|
||||
});
|
94
node/faas/src/util.ts
Normal file
94
node/faas/src/util.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { spawn, SpawnOptions, execSync } from 'child_process';
|
||||
|
||||
export function executeCommand(
|
||||
command: string,
|
||||
cwd?: string
|
||||
): Promise<{ stdout: string; stderr: string; code: number | null }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 自动拆分命令和参数(支持带引号的参数)
|
||||
const [cmd, ...args] = command.match(/(?:[^\s"']+|['"][^'"]*['"])+/g) || [];
|
||||
|
||||
if (!cmd) {
|
||||
reject(new Error('Empty command'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除参数中的引号(如果存在)
|
||||
const processedArgs = args.map(arg =>
|
||||
arg.replace(/^['"](.*)['"]$/, '$1')
|
||||
);
|
||||
|
||||
// 将 cwd 的 ~ 替换为用户的 home 目录
|
||||
if (cwd && cwd.startsWith('~')) {
|
||||
cwd = cwd.replace('~', process.env.HOME || '');
|
||||
}
|
||||
|
||||
const options: SpawnOptions = {
|
||||
cwd,
|
||||
stdio: 'pipe',
|
||||
shell: process.platform === 'win32',
|
||||
env: {
|
||||
...process.env, // 继承父进程所有环境变量
|
||||
}
|
||||
};
|
||||
|
||||
const child = spawn(cmd, processedArgs, options);
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
child.stdout?.on('data', (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
child.stderr?.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
child.on('close', (code) => {
|
||||
resolve({
|
||||
stdout: stdout.trim(),
|
||||
stderr: stderr.trim(),
|
||||
code,
|
||||
});
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function markdownToPlainText(markdown: string): string {
|
||||
// 移除 Markdown 标题符号
|
||||
let plainText = markdown.replace(/^#+\s*/gm, '');
|
||||
|
||||
// 移除 Markdown 链接格式
|
||||
plainText = plainText.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
||||
|
||||
// 移除 Markdown 加粗和斜体符号
|
||||
plainText = plainText.replace(/\*\*([^*]+)\*\*/g, '$1');
|
||||
plainText = plainText.replace(/\*([^*]+)\*/g, '$1');
|
||||
|
||||
// 移除 Markdown 代码块符号
|
||||
plainText = plainText.replace(/`([^`]+)`/g, '$1');
|
||||
|
||||
// 移除 Markdown 分割线
|
||||
plainText = plainText.replace(/^-{3,}/gm, '');
|
||||
|
||||
// 移除多余的空行
|
||||
plainText = plainText.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
return plainText.trim();
|
||||
}
|
||||
|
||||
export function getFormatedTime() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
return `${year}_${month}_${day}_${hours}_${minutes}_${seconds}`;
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { app } from "../common";
|
||||
import { createTaskContext } from "./common";
|
||||
|
||||
export async function getNewsFromTowardsScience() {
|
||||
const { taskLoop, adapter } = await createTaskContext();
|
||||
|
||||
const tools = await adapter.listTools();
|
||||
|
||||
// 创建当前事件循环对应的上下文,并且配置当前上下文的设置
|
||||
const storage = {
|
||||
messages: [],
|
||||
settings: {
|
||||
temperature: 0.7,
|
||||
enableTools: tools,
|
||||
systemPrompt: 'you are a clever bot',
|
||||
contextLength: 20
|
||||
}
|
||||
};
|
||||
|
||||
// 本次发出的问题
|
||||
const message = `
|
||||
请通过 headless 的方式帮我搜集 https://towardsdatascience.com/tag/editors-pick/ 最热门的前三条信息,这些信息大概率在 <main> 元素的 ul li 下面。然后帮我进入这些网站后总结相关信息,文章通常也在 <main> 中,并且整理成一个简单的咨询,返回翻译好的简体中文。比如
|
||||
|
||||
K1 标题
|
||||
简介 {简介}
|
||||
原文链接 {原文链接}
|
||||
|
||||
简介请按照科技推广文的标准进行撰写,尽量能够体现出文章的亮点。`.trim();
|
||||
|
||||
|
||||
// 开启事件循环
|
||||
await taskLoop.start(storage, message);
|
||||
|
||||
// 打印上下文,最终的回答在 messages.at(-1) 中
|
||||
console.log(storage.messages);
|
||||
|
||||
const lastMessage = storage.messages.at(-1);
|
||||
if (lastMessage?.role === 'assistant') {
|
||||
return lastMessage.content;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
app.post('/get-news-from-towards-science', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const news = await getNewsFromTowardsScience();
|
||||
res.send({
|
||||
code: 200,
|
||||
msg: news
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.send(error);
|
||||
}
|
||||
});
|
@ -161,7 +161,10 @@ async function ensureBrowser({ launchOptions, allowDangerous }: any) {
|
||||
previousLaunchOptions = launchOptions;
|
||||
|
||||
if (!browser) {
|
||||
const npx_args = { headless: false }
|
||||
const npx_args = {
|
||||
headless: false,
|
||||
args: [ '--proxy-server=127.0.0.1:7890' ]
|
||||
}
|
||||
const docker_args = { headless: true, args: ["--no-sandbox", "--single-process", "--no-zygote"] }
|
||||
browser = await puppeteer.launch(deepMerge(
|
||||
process.env.DOCKER_CONTAINER ? docker_args : npx_args,
|
||||
@ -221,7 +224,9 @@ export async function handleToolCall(name: string, args: any): Promise<CallToolR
|
||||
|
||||
switch (name) {
|
||||
case "k_navigate":
|
||||
await page.goto(args.url);
|
||||
await page.goto(args.url, {
|
||||
waitUntil: 'networkidle2'
|
||||
});
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
|
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Lagrange.RagBot",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
70
scripts/qq-text-format.js
Normal file
70
scripts/qq-text-format.js
Normal file
@ -0,0 +1,70 @@
|
||||
function markdownToPlainText(markdown) {
|
||||
// 移除 Markdown 标题符号
|
||||
let plainText = markdown.replace(/^#+\s*/gm, '');
|
||||
|
||||
// 移除 Markdown 链接格式
|
||||
plainText = plainText.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
||||
|
||||
// 移除 Markdown 加粗和斜体符号
|
||||
plainText = plainText.replace(/\*\*([^*]+)\*\*/g, '$1');
|
||||
plainText = plainText.replace(/\*([^*]+)\*/g, '$1');
|
||||
|
||||
// 移除 Markdown 代码块符号
|
||||
plainText = plainText.replace(/`([^`]+)`/g, '$1');
|
||||
|
||||
// 移除 Markdown 分割线
|
||||
plainText = plainText.replace(/^-{3,}/gm, '');
|
||||
|
||||
// 移除多余的空行
|
||||
plainText = plainText.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
return plainText.trim();
|
||||
}
|
||||
|
||||
// 示例用法
|
||||
const markdown = `以下是整理好的热门文章资讯,已翻译为简体中文:
|
||||
|
||||
---
|
||||
|
||||
### 1. **《我的GPT造型师教会我如何更好地提示》**
|
||||
**文章介绍**
|
||||
本文探讨了大型语言模型(LLM)的奇怪行为,并通过作者与GPT造型师的互动,揭示了如何通过优化提示来更好地利用这些模型。文章深入分析了LLM的工作原理和局限性。
|
||||
|
||||
**文章链接**
|
||||
\`https://towardsdatascience.com/what-my-gpt-stylist-taught-me-about-prompting-better-inside-the-strange-behavior-of-llms/\`
|
||||
**作者**
|
||||
Arielle Caron
|
||||
**发布时间**
|
||||
2025年5月9日
|
||||
|
||||
---
|
||||
|
||||
### 2. **《AccentFold论文回顾:非洲ASR领域最重要的研究之一》**
|
||||
**文章介绍**
|
||||
文章回顾了AccentFold论文,该研究解决了当前自动语音识别(ASR)系统在处理非洲口音时的局限性。作者详细分析了论文的创新点和实际应用价值。
|
||||
|
||||
**文章链接**
|
||||
\`https://towardsdatascience.com/a-review-of-accentfold-one-of-the-most-important-papers-on-african-asr/\`
|
||||
**作者**
|
||||
Zaynab Awofeso
|
||||
**发布时间**
|
||||
2025年5月9日
|
||||
|
||||
---
|
||||
|
||||
### 3. **《欺骗性数据的危险(第二部分):基础比例与糟糕的统计》》**
|
||||
**文章介绍**
|
||||
本文是作者关于欺骗性数据系列的第二部分,重点讨论了相关性、基础比例、摘要统计和不确定性如何误导人们。文章通过生动的例子和图表,帮助读者理解统计陷阱。
|
||||
|
||||
**文章链接**
|
||||
\`https://towardsdatascience.com/the-dangers-of-deceptive-data-part-2-base-proportions-and-bad-statistics/\`
|
||||
**作者**
|
||||
Murtaza Ali
|
||||
**发布时间**
|
||||
2025年5月8日
|
||||
|
||||
---
|
||||
|
||||
希望这些资讯对您有帮助!如果需要进一步了解某篇文章,请随时告诉我。`;
|
||||
|
||||
console.log(markdownToPlainText(markdown));
|
Loading…
x
Reference in New Issue
Block a user