first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# Custom OpenAI-Compatible Providers in Hermes
|
||||
|
||||
When a provider isn't built-in but offers an OpenAI-compatible `/v1` endpoint, add it manually to `config.yaml`.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Find the base URL and API key env var** — usually `/v1` at the provider's domain.
|
||||
|
||||
2. **List available models:**
|
||||
```bash
|
||||
curl -s "https://<provider>/v1/models" \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
--max-time 15 | python3 -m json.tool
|
||||
```
|
||||
Note model `id`, `input_modalities`, `context_length`, `supported_features`.
|
||||
|
||||
3. **Add provider to config.yaml via Python** (don't hand-edit YAML — indentation errors break everything):
|
||||
```python
|
||||
import yaml, json, os
|
||||
|
||||
config_path = os.path.expanduser("~/.hermes/config.yaml")
|
||||
with open(config_path) as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
# Read API key from .env
|
||||
api_key = ""
|
||||
with open(os.path.expanduser("~/.hermes/.env")) as f:
|
||||
for line in f:
|
||||
if line.startswith("YOUR_KEY_PREFIX="):
|
||||
api_key = line.strip().split("=", 1)[1]
|
||||
break
|
||||
|
||||
config.setdefault("providers", {})["your-provider"] = {
|
||||
"api_key": api_key,
|
||||
"base_url": "https://provider.example.com/v1",
|
||||
"available_models_json": json.dumps([
|
||||
{"id": "model-id", "name": "Display Name"},
|
||||
]),
|
||||
"model": "default-model-id",
|
||||
"model_display_name": "Default Display Name"
|
||||
}
|
||||
|
||||
with open(config_path, "w") as f:
|
||||
yaml.dump(config, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
||||
```
|
||||
|
||||
4. **Verify with a test call:**
|
||||
```bash
|
||||
curl -s "https://provider.example.com/v1/chat/completions" \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"model-id","messages":[{"role":"user","content":"hi"}],"max_tokens":100}'
|
||||
```
|
||||
|
||||
5. **Use the model:** `hermes -m your-provider/model-id` or via `hermes model` picker.
|
||||
|
||||
## Provider Config Fields
|
||||
|
||||
| Field | Required | Notes |
|
||||
|-------|----------|-------|
|
||||
| `api_key` | Yes | Actual key value, not env var reference |
|
||||
| `base_url` | Yes | Must end with `/v1` (or `/v1/`) |
|
||||
| `available_models_json` | Yes | JSON string of `[{id, name}]` array |
|
||||
| `model` | No | Default model ID |
|
||||
| `model_display_name` | No | Human-readable default model name |
|
||||
| `api_mode` | No | Only set if non-standard (e.g. `anthropic-messages` for MiniMax). Omit for OpenAI-compatible. |
|
||||
| `protocol` | No | Usually leave as `''` |
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Don't hand-edit YAML** — use Python `yaml.safe_load` + `yaml.dump` to avoid indentation corruption.
|
||||
- **`api_key` must be the actual value**, not `$ENV_VAR` — Hermes doesn't resolve env vars inside provider config (only in `.env`).
|
||||
- **No `api_mode` needed for OpenAI-compatible** — only set this for providers with custom protocols (Anthropic Messages, etc.).
|
||||
- **`reasoning` field in responses** — some providers (SenseNova, DeepSeek) return a `reasoning` field in the message object. Hermes handles this natively for reasoning-capable models.
|
||||
- **Model discovery** — always call `/v1/models` first; don't guess model IDs from documentation (they change).
|
||||
|
||||
## Known Custom Providers
|
||||
|
||||
| Provider | Base URL | Key Env Var | Models |
|
||||
|----------|----------|-------------|--------|
|
||||
| SenseNova | `https://token.sensenova.cn/v1` | `SN_API_KEY` | sensenova-6.7-flash-lite, deepseek-v4-flash, sensenova-u1-fast |
|
||||
@@ -0,0 +1,68 @@
|
||||
# Dashboard Remote Access
|
||||
|
||||
## Problem
|
||||
Dashboard binds to 127.0.0.1:9119 by default. Accessing from a different machine (e.g., local laptop → cloud VPS) requires either SSH tunnel or insecure bind.
|
||||
|
||||
## Recommended: SSH Port Forwarding
|
||||
```bash
|
||||
# On your local machine
|
||||
ssh -L 9119:127.0.0.1:9119 user@server-ip
|
||||
# Then open http://127.0.0.1:9119 in browser
|
||||
```
|
||||
**Pitfall (Windows):** `ssh: connect to host ... port 22: Connection timed out` — almost always a cloud security group issue. Check your cloud provider's security group / firewall rules to allow inbound TCP 22. SSH socket activation (`ssh.socket`) is enabled by default on Ubuntu; the service itself may show `inactive (dead)` — that's normal, socket activation triggers it on connection.
|
||||
|
||||
## Password Protection (Reverse Proxy)
|
||||
|
||||
Dashboard has **no built-in password auth**. Options:
|
||||
|
||||
### Nginx + Basic Auth
|
||||
```bash
|
||||
sudo apt install nginx apache2-utils
|
||||
sudo htpasswd -c /etc/nginx/.htpasswd your-username
|
||||
```
|
||||
```nginx
|
||||
server {
|
||||
listen 8080;
|
||||
location / {
|
||||
auth_basic "Hermes Dashboard";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
proxy_pass http://127.0.0.1:9119;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
# WebSocket support for Chat TUI
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Caddy (simpler config)
|
||||
```
|
||||
:8080 {
|
||||
basicauth * {
|
||||
username $hashed_password
|
||||
}
|
||||
reverse_proxy localhost:9119
|
||||
}
|
||||
```
|
||||
Generate hash: `caddy hash-password --plaintext 'your-password'`
|
||||
|
||||
## Alternative: Insecure Bind (⚠️ exposes API keys)
|
||||
```bash
|
||||
hermes dashboard --insecure --port 9119
|
||||
# Access via http://server-ip:9119
|
||||
```
|
||||
Only use on trusted/private networks. The dashboard exposes `.env` contents including API keys. The `--insecure` flag exists because there's no built-in auth — the warning is intentional.
|
||||
|
||||
## TUI Mode (Embedded Chat)
|
||||
```bash
|
||||
hermes dashboard --tui --no-open
|
||||
```
|
||||
Adds a Chat tab to the web UI — a browser-based `hermes --tui` via PTY/WebSocket. Useful when CLI access is inconvenient.
|
||||
|
||||
## Common Issues
|
||||
- Multiple dashboard processes: `hermes dashboard --stop` kills all
|
||||
- Port conflict: change port with `--port 8080`
|
||||
- Gateway must be running for Kanban dispatch to work (`hermes gateway status`)
|
||||
- SSH connection timeout from Windows: check cloud security group allows inbound TCP 22
|
||||
@@ -0,0 +1,110 @@
|
||||
# Hermes Dashboard Reverse Proxy with Nginx
|
||||
|
||||
## Quick Setup (Nginx + Basic Auth)
|
||||
|
||||
### 1. Install Dependencies
|
||||
```bash
|
||||
sudo apt update && sudo apt install -y nginx apache2-utils
|
||||
```
|
||||
|
||||
### 2. Create Password File
|
||||
```bash
|
||||
# Generate password (will prompt for password twice)
|
||||
sudo htpasswd -c /etc/nginx/.htpasswd <username>
|
||||
|
||||
# Or non-interactive:
|
||||
echo -n '<username>:' | sudo tee /etc/nginx/.htpasswd
|
||||
openssl passwd -apr1 '<password>' | sudo tee -a /etc/nginx/.htpasswd
|
||||
```
|
||||
|
||||
### 3. Nginx Config (`/etc/nginx/sites-available/hermes-dashboard`)
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name <your-domain-or-ip>; # e.g., 111.230.53.30 or hermes.example.com
|
||||
|
||||
location / {
|
||||
auth_basic "Hermes Dashboard";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
|
||||
proxy_pass http://127.0.0.1:9119;
|
||||
# IMPORTANT: Use "localhost" for Host header, NOT $host
|
||||
# Dashboard validates Host header and rejects non-localhost values
|
||||
# This causes "Invalid Host header" error if set to $host
|
||||
proxy_set_header Host localhost;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# WebSocket support (required for Chat TUI)
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Enable & Reload
|
||||
```bash
|
||||
sudo ln -sf /etc/nginx/sites-available/hermes-dashboard /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 5. Ensure Dashboard Running
|
||||
```bash
|
||||
hermes dashboard --no-open --port 9119
|
||||
```
|
||||
|
||||
## Access
|
||||
- URL: `http://<domain-or-ip>`
|
||||
- Auth: Browser popup for username/password
|
||||
|
||||
## Commands
|
||||
```bash
|
||||
sudo systemctl status nginx
|
||||
sudo systemctl restart nginx
|
||||
hermes dashboard --status
|
||||
hermes dashboard --stop
|
||||
```
|
||||
|
||||
## Cleanup (Remove Reverse Proxy)
|
||||
```bash
|
||||
# Stop services
|
||||
hermes dashboard --stop
|
||||
sudo systemctl stop nginx
|
||||
sudo systemctl disable nginx
|
||||
|
||||
# Remove config files
|
||||
sudo rm -f /etc/nginx/sites-available/hermes-dashboard
|
||||
sudo rm -f /etc/nginx/sites-enabled/hermes-dashboard
|
||||
sudo rm -f /etc/nginx/.htpasswd
|
||||
```
|
||||
|
||||
## HTTPS (Optional)
|
||||
Use Certbot for Let's Encrypt:
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d hermes.example.com
|
||||
```
|
||||
|
||||
## Pitfalls
|
||||
|
||||
### Invalid Host Header Error
|
||||
If you see `{"detail":"Invalid Host header. Dashboard requests must use the hostname the server was bound to."}`:
|
||||
- **Cause**: Nginx is passing `$host` (the public domain/IP) but Dashboard only accepts `localhost`
|
||||
- **Fix**: Change `proxy_set_header Host $host;` to `proxy_set_header Host localhost;`
|
||||
|
||||
### Domain Requires ICP Filing (China)
|
||||
If accessing via domain in China triggers ICP filing requirement:
|
||||
- **Solution**: Use IP address directly instead of domain
|
||||
- Update `server_name` to the server's public IP
|
||||
|
||||
### Security Notes
|
||||
- Dashboard has NO built-in password auth
|
||||
- Without reverse proxy, anyone with network access can see API keys
|
||||
- Always use reverse proxy + basic auth for remote access
|
||||
- Consider SSH port forwarding as a more secure alternative
|
||||
@@ -0,0 +1,76 @@
|
||||
# Hermes Update Autostash Triage
|
||||
|
||||
`hermes update` auto-stashes local changes before pulling. These accumulate as `stash@{N}` with the naming pattern:
|
||||
|
||||
```
|
||||
hermes-update-autostash-YYYYMMDD-HHMMSS
|
||||
```
|
||||
|
||||
## Triage Workflow
|
||||
|
||||
### Step 1: List all stashes
|
||||
|
||||
```bash
|
||||
git stash list
|
||||
```
|
||||
|
||||
### Step 2: Quick scan each stash — file types matter
|
||||
|
||||
```bash
|
||||
git stash show stash@{N} --stat
|
||||
```
|
||||
|
||||
**Lock-file-only stashes** (only `package.json`, `package-lock.json`, `ui-tui/package-lock.json`):
|
||||
- Usually npm dependency resolution artifacts (registry mirror switches, peer dependency reclassification)
|
||||
- Safe to drop: `git stash drop stash@{N}`
|
||||
|
||||
**Source-code stashes** (`.py`, `.ts`, `.tsx` files changed):
|
||||
- Need detailed analysis — these may contain valuable local features
|
||||
|
||||
### Step 3: For source-code stashes — compare against current code
|
||||
|
||||
Don't just `git stash pop`. First check if the features were already merged upstream:
|
||||
|
||||
```bash
|
||||
# Get the full diff
|
||||
git stash show -p stash@{N}
|
||||
|
||||
# For each key feature, search current code:
|
||||
grep -n "feature_keyword" path/to/file.py
|
||||
```
|
||||
|
||||
**Classification:**
|
||||
- ✅ Already in current code → safe to drop
|
||||
- ❌ Missing from current code → candidate for restoration
|
||||
|
||||
### Step 4: Decision matrix
|
||||
|
||||
| Stash type | Action |
|
||||
|------------|--------|
|
||||
| Lock files only | Drop immediately |
|
||||
| Source code, all features merged | Drop |
|
||||
| Source code, some features missing | Selective restore (cherry-pick specific hunks) or apply + resolve conflicts |
|
||||
| Source code, all features missing | `git stash apply stash@{N}` then test |
|
||||
|
||||
### Pitfalls
|
||||
|
||||
- **Don't blindly pop stashes on an active branch** — always `apply` first (preserves stash), test, then `drop` if good.
|
||||
- **Registry mirror changes in lock files** (npmmirror.com, mirrors.tencentyun.com) are local environment artifacts, not valuable code. Drop them.
|
||||
- **`peer: true` removal** in lock files = npm re-resolved peer deps as direct deps. Not meaningful.
|
||||
- **5+ day old stashes** with source changes are likely abandoned experiments. Check if the user still needs them before restoring.
|
||||
- **Merge conflicts** are common after 5+ days — upstream moves fast. Expect to resolve manually.
|
||||
|
||||
### Restoring selectively
|
||||
|
||||
If only some hunks from a stash are needed:
|
||||
|
||||
```bash
|
||||
# Apply but don't drop
|
||||
git stash apply stash@{N}
|
||||
|
||||
# Review conflicts
|
||||
git diff
|
||||
|
||||
# Or use interactive checkout for specific files
|
||||
git checkout stash@{N} -- path/to/specific/file.py
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
# QQ Bot Rich Media API Reference
|
||||
|
||||
Source: https://bot.q.qq.com/wiki/develop/api-v2/server-inter/message/send-receive/rich-media.html
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Scope | Endpoint | Method |
|
||||
|-------|----------|--------|
|
||||
| 单聊 | `/v2/users/{openid}/files` | POST |
|
||||
| 群聊 | `/v2/groups/{group_openid}/files` | POST |
|
||||
|
||||
## Parameters
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| file_type | int | Yes | 1=图片, 2=视频, 3=语音, 4=文件 |
|
||||
| url | string | Yes* | 媒体资源 URL(*url 或 file_data 二选一) |
|
||||
| file_data | string | No | base64 二进制数据 |
|
||||
| srv_send_msg | bool | Yes | true=直接发送(占用主动消息频次),false=仅上传获取 file_info |
|
||||
| file_name | string | No | 文件名(file_type=4 时建议传) |
|
||||
|
||||
## Supported Formats
|
||||
|
||||
- 图片: png/jpg
|
||||
- 视频: mp4
|
||||
- 语音: silk/wav/mp3/flac
|
||||
- 文件: 无限制(群场景暂不开放 file_type=4)
|
||||
|
||||
## Response
|
||||
|
||||
```json
|
||||
{
|
||||
"file_uuid": "...",
|
||||
"file_info": "...", // 用于发送消息接口的 media 字段
|
||||
"ttl": 3600 // 剩余秒数,0=长期有效
|
||||
}
|
||||
```
|
||||
|
||||
## Sending with file_info
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_type": 7,
|
||||
"media": {"file_info": "<file_info_from_upload>"},
|
||||
"msg_seq": 1
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- `file_info` 不受目标端影响,可复用到多个群/用户
|
||||
- 用 `/v2/users/{openid}/files` 上传的仅能发单聊,群上传的仅能发群聊
|
||||
- 建议 `srv_send_msg=false`,先获取 file_info 再发送
|
||||
- 文件大小限制:~100MB(分块上传),~10MB(inline base64)
|
||||
|
||||
## Hermes Adapter Implementation
|
||||
|
||||
- Gateway adapter: `gateway/platforms/qqbot/adapter.py` → `_send_media()` (line ~2690)
|
||||
- Chunked upload: `gateway/platforms/qqbot/chunked_upload.py`
|
||||
- Media types defined in `gateway/platforms/qqbot/constants.py` (MEDIA_TYPE_IMAGE=1, VIDEO=2, VOICE=3, FILE=4)
|
||||
- send_message tool gap: `_send_qqbot()` in `tools/send_message_tool.py:1677` is text-only
|
||||
96
autonomous-ai-agents/hermes-agent/references/search-tools.md
Normal file
96
autonomous-ai-agents/hermes-agent/references/search-tools.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Search Tools for Hermes Agent
|
||||
|
||||
## Current Environment
|
||||
|
||||
| Tool | Type | Usage |
|
||||
|------|------|-------|
|
||||
| **web** (built-in) | Web Search & Scraping | `web_search`, `web_extract` tools |
|
||||
| **session_search** | Session history | `session_search(query="...")` |
|
||||
| **mmx search** | MiniMax CLI | `mmx search query "关键词"` |
|
||||
|
||||
## Popular Search APIs for AI Agents
|
||||
|
||||
| Tool | Best For | Pricing | MCP Server |
|
||||
|------|----------|---------|------------|
|
||||
| **Tavily Search** | AI-native search, structured results | Free tier available | `tavily-mcp` |
|
||||
| **SerpAPI** | Google results scraping | Paid (100 free/month) | `serpapi-mcp` |
|
||||
| **Brave Search** | Privacy-focused, no tracking | Free tier (2000/month) | `brave-search-mcp` |
|
||||
| **Perplexity API** | AI search with citations | Paid | API only |
|
||||
| **Firecrawl** | Web scraping + extraction | Free tier | `firecrawl-mcp` |
|
||||
| **Jina Reader** | URL to Markdown conversion | Free tier | API only |
|
||||
| **SearXNG** | Self-hosted meta search | Free (self-hosted) | `searxng-mcp` |
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Tavily Search (Recommended for AI Agents)
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
mcp_servers:
|
||||
tavily:
|
||||
command: npx
|
||||
args: ["-y", "tavily-mcp@latest"]
|
||||
env:
|
||||
TAVILY_API_KEY: "tvly-xxxxx"
|
||||
```
|
||||
|
||||
Get API key: https://tavily.com (free tier: 1000 searches/month)
|
||||
|
||||
### Brave Search
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
brave-search:
|
||||
command: npx
|
||||
args: ["-y", "@anthropic/brave-search-mcp@latest"]
|
||||
env:
|
||||
BRAVE_API_KEY: "BSAxxxxx"
|
||||
```
|
||||
|
||||
Get API key: https://brave.com/search/api/ (free tier: 2000 queries/month)
|
||||
|
||||
### Firecrawl (for Web Scraping)
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
firecrawl:
|
||||
command: npx
|
||||
args: ["-y", "firecrawl-mcp"]
|
||||
env:
|
||||
FIRECRAWL_API_KEY: "fc-xxxxx"
|
||||
```
|
||||
|
||||
## When to Use Which
|
||||
|
||||
| Scenario | Recommended Tool |
|
||||
|----------|------------------|
|
||||
| Quick factual lookup | `mmx search` or built-in `web_search` |
|
||||
| Structured data extraction | Tavily Search |
|
||||
| Google-specific results | SerpAPI |
|
||||
| Privacy-sensitive search | Brave Search |
|
||||
| Deep web scraping | Firecrawl |
|
||||
| Convert page to markdown | Jina Reader |
|
||||
| Previous conversation context | `session_search` |
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **mcp_servers: {}** is currently empty in this environment
|
||||
- Tool changes require `/reset` (new session) to take effect
|
||||
- Free tiers have rate limits; monitor usage for production use
|
||||
- Some MCP servers require Node.js (`npx` command)
|
||||
- **mmx search Token Plan 限制**:`mmx search query` 需要 MiniMax Token Plan 支持 `coding-plan-search` 模型。报错 "your current token plan not support model" 表示 plan 不含搜索能力,需升级或换替代方案
|
||||
- **服务器出站网络受限**:某些云服务器/容器的出站网络被限制,`urllib` 报 `Network is unreachable` (errno 101)。此时 curl、Python requests、mimo_search.py 均无法访问外部。排查:`curl -s -o /dev/null -w "%{http_code}" https://www.baidu.com`
|
||||
- **Playwright/Chrome 沙盒问题**:服务器上 Chrome 需要 `--no-sandbox` 参数才能启动。Hermes 的 browser 工具可能因沙盒限制失败。排查:检查 Chrome stderr 中是否有 "No usable sandbox" 错误
|
||||
- **mimo_search.py 环境变量**:脚本依赖 `XIAOMI_API_KEY` 环境变量,但 Hermes config.yaml 中的 key 不会自动 export。需手动 `export XIAOMI_API_KEY=...` 或从 config 提取
|
||||
|
||||
## 搜索降级策略(按优先级)
|
||||
|
||||
当用户请求搜索时,按以下顺序尝试:
|
||||
|
||||
1. **mmx search** — 最简单,但需 Token Plan 支持
|
||||
2. **mimo_search.py** — MiMo 原生搜索,需 XIAOMI_API_KEY 环境变量 + 网络可达
|
||||
3. **web_search 工具** — Hermes 内置,需 web toolset 启用
|
||||
4. **Playwright 浏览器** — 通用兜底,但需 Chrome 可启动
|
||||
5. **curl + 搜索引擎** — 最基础,需服务器出站网络可达
|
||||
|
||||
全部失败时,告知用户具体失败原因(Token Plan 限制 / 网络不通 / 浏览器沙盒问题),并建议用户在本地自行搜索。
|
||||
@@ -0,0 +1,64 @@
|
||||
# Skill Management Pitfalls
|
||||
|
||||
Learned from attempting to optimize the skill library based on SkillRouter paper findings.
|
||||
|
||||
## Pitfall 1: "Same Output" ≠ "Functionally Overlapping"
|
||||
|
||||
**Wrong:** Deleted `pptx-generator` (python-pptx) because `powerpoint` (pptxgenjs) also makes .pptx files.
|
||||
**Right:** Different tech stacks = different fallback options. python-pptx is pure Python, pptxgenjs needs Node.js. Keep both.
|
||||
|
||||
**Rule:** Two skills overlap only when they use the same tools AND serve the same user intent. Same output format is not enough.
|
||||
|
||||
## Pitfall 2: Don't Cross-Reference in Descriptions
|
||||
|
||||
**Wrong:** In arxiv's description: "需要多源学术搜索优先用 sn-search-academic"
|
||||
**Right:** Each skill describes itself only. No competitive recommendations.
|
||||
|
||||
**Why:** Creates circular dependencies. If skill A recommends B, and B recommends A, the LLM loops.
|
||||
|
||||
## Pitfall 3: Don't Expose Implementation Details
|
||||
|
||||
**Wrong:** In sn-infographic description: "需要 SN_API_KEY"
|
||||
**Right:** "需要 SenseNova API"
|
||||
|
||||
**Rule:** Descriptions should express user-facing capabilities, not internal tool/API names.
|
||||
|
||||
## Pitfall 4: Check Hard Dependencies Before Deleting
|
||||
|
||||
**Wrong:** Marked sn-research-planning for deletion because it was "never called."
|
||||
**Right:** sn-deep-research calls it via `skill_view("sn-research-planning")` at runtime. Deleting breaks the pipeline.
|
||||
|
||||
**How to check:**
|
||||
```python
|
||||
# Search all SKILL.md files for references to the target skill name
|
||||
# Only HARD dependencies count: skill_view("target-name") or "读取 target-name"
|
||||
# "Related skills" mentions are SOFT and don't block deletion
|
||||
```
|
||||
|
||||
## Pitfall 5: "Never skill_view'd" ≠ "Unused"
|
||||
|
||||
Skills can be auto-loaded via the system prompt's "MUST load" instruction without explicit `skill_view()` calls. Session data only shows explicit tool calls.
|
||||
|
||||
**Better metric:** Check if the skill is referenced as a runtime dependency by other skills.
|
||||
|
||||
## Pitfall 6: Don't Batch Recommendations Without Verification
|
||||
|
||||
**Wrong:** Generated all 87 recommendations at once, sent to user, then had to fix multiple errors.
|
||||
**Right:** Verify each category before sending. Check dependencies. Then send once.
|
||||
|
||||
**User feedback:** "你这建议就不能确认好之后再发给我吗" (Can you verify before sending?)
|
||||
|
||||
## Description Quality Formula
|
||||
|
||||
Good skill description = **What it does** + **Trigger words** + **Negative boundary**
|
||||
|
||||
Example:
|
||||
```
|
||||
"深度调研全流程编排器(入口 skill)。自动完成:规划→分维度取证→综合→成稿。
|
||||
触发词:深度研究/调研/全面研究/调研报告/deep research。
|
||||
不用于:单点事实问答、一句话摘要。"
|
||||
```
|
||||
|
||||
- What: 深度调研全流程编排器
|
||||
- Triggers: 深度研究/调研/全面研究
|
||||
- Boundary: 不用于单点事实问答
|
||||
@@ -0,0 +1,51 @@
|
||||
# 服务器环境网络搜索指南
|
||||
|
||||
## Playwright 浏览器启动(服务器/容器环境)
|
||||
|
||||
服务器环境(Ubuntu 23.10+、容器、VM)需要 `--no-sandbox` 参数:
|
||||
|
||||
```python
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(
|
||||
headless=True,
|
||||
args=['--no-sandbox', '--disable-setuid-sandbox']
|
||||
)
|
||||
page = browser.new_page()
|
||||
page.goto('https://example.com', timeout=30000)
|
||||
# ... 操作页面
|
||||
browser.close()
|
||||
```
|
||||
|
||||
注意:Hermes 内置的 `browser_navigate` 工具不支持传递启动参数,必须直接用 Playwright API。
|
||||
|
||||
## 搜索引擎反爬情况(2026年测试)
|
||||
|
||||
| 搜索引擎 | 状态 | 备注 |
|
||||
|----------|------|------|
|
||||
| 百度 | ❌ 验证码 | 滑块验证,无法绕过 |
|
||||
| 搜狗 | ❌ 验证码 | 图片点选验证 |
|
||||
| 必应(cn.bing.com) | ⚠️ 可用但质量差 | 中文搜索结果常偏离关键词 |
|
||||
| Google | ❌ 超时 | 服务器网络不可达 |
|
||||
| DuckDuckGo | ❌ 超时 | 服务器网络不可达 |
|
||||
|
||||
**结论**:服务器环境下,主流搜索引擎基本不可用。必应是唯一能返回结果的,但质量不稳定。
|
||||
|
||||
## mmx search 限制
|
||||
|
||||
`mmx search` 需要 Token Plan 支持 `coding-plan-search` 模型。如果报错:
|
||||
```
|
||||
your current token plan not support model, coding-plan-search
|
||||
```
|
||||
说明当前计划不支持搜索功能,需要升级或使用其他方式。
|
||||
|
||||
## MiMo 模型搜索能力
|
||||
|
||||
MiMo (mimo-v2.5-pro) 可以声明 `web_search` 工具调用,但**实际上不会真正联网搜索**。它只能基于训练数据回答,无法获取实时信息。
|
||||
|
||||
## 替代方案优先级
|
||||
|
||||
1. Playwright + 必应(唯一可行的浏览器方案)
|
||||
2. 直接访问目标网站(如培训机构官网)
|
||||
3. 用户自行搜索后提供信息
|
||||
Reference in New Issue
Block a user