feat: 集成Hermes智能体系统,增强聊天和差旅报销功能
This commit is contained in:
61
hermes/skills/domain/x-financial-callback/SKILL.md
Normal file
61
hermes/skills/domain/x-financial-callback/SKILL.md
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: x-financial-callback
|
||||
description: Use when a Hermes task for X-Financial must report progress or completion back to the backend through the single generic callback endpoint.
|
||||
---
|
||||
|
||||
# X-Financial Callback
|
||||
|
||||
Use this skill for every X-Financial task that must notify the backend after Hermes finishes work.
|
||||
|
||||
## Callback contract
|
||||
|
||||
Send exactly one HTTP `POST` request to the callback URL provided in the task payload.
|
||||
|
||||
Use:
|
||||
|
||||
- Header: `Authorization: Bearer <callback_token>`
|
||||
- Header: `Content-Type: application/json`
|
||||
- Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "task_type_from_input",
|
||||
"run_id": "agent_run_id_from_input",
|
||||
"status": "succeeded",
|
||||
"summary": "short human-readable summary",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- Always preserve the incoming `type` and `run_id`.
|
||||
- Execute the callback directly for server-dispatched tasks; do not ask the user for a second confirmation.
|
||||
- Use `scripts/send_callback.py` for the actual HTTP request so large JSON bodies, quotes, and multilingual text are encoded safely.
|
||||
- For normal tasks, prefer letting the script build the generic envelope for you via
|
||||
`--type`, `--run-id`, `--status`, and `--summary`; then the JSON file on stdin should contain only the
|
||||
task-specific business payload.
|
||||
- Prefer sending a validated JSON file for non-trivial payloads:
|
||||
`python3 ~/.hermes/skills/domain/x-financial-callback/scripts/send_callback.py --url "$CALLBACK_URL" --token "$CALLBACK_TOKEN" < /tmp/x-financial-callback.json`
|
||||
- Before sending a large payload, validate it with `python3 -m json.tool /tmp/x-financial-callback.json >/dev/null`.
|
||||
- For large multilingual payloads, write `/tmp/x-financial-callback.json` with the `write_file` tool first.
|
||||
Do not generate helper Python source files or shell heredocs merely to build JSON.
|
||||
- Use `status: "running"` only for optional progress updates.
|
||||
- Use `status: "succeeded"` once when the task is complete.
|
||||
- Use `status: "failed"` once when the task cannot be completed, and include an `error` string.
|
||||
- Put task-specific business data only inside `payload`.
|
||||
- Do not invent extra callback endpoints. X-Financial accepts Hermes callbacks through one shared endpoint only.
|
||||
- If the callback fails, retry the same request up to 3 times before returning failure.
|
||||
|
||||
## Preferred command
|
||||
|
||||
```bash
|
||||
python3 ~/.hermes/skills/domain/x-financial-callback/scripts/send_callback.py \
|
||||
--url "$CALLBACK_URL" \
|
||||
--token "$CALLBACK_TOKEN" \
|
||||
--type "$TASK_TYPE" \
|
||||
--run-id "$RUN_ID" \
|
||||
--status succeeded \
|
||||
--summary "short human-readable summary" \
|
||||
< /tmp/x-financial-callback.json
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Send one generic X-Financial Hermes callback.")
|
||||
parser.add_argument("--url", required=True)
|
||||
parser.add_argument("--token", required=True)
|
||||
parser.add_argument("--retries", type=int, default=3)
|
||||
parser.add_argument("--type", dest="task_type", default="")
|
||||
parser.add_argument("--run-id", default="")
|
||||
parser.add_argument("--status", choices=("running", "succeeded", "failed"), default="")
|
||||
parser.add_argument("--summary", default="")
|
||||
parser.add_argument("--error", default="")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
payload = json.load(sys.stdin)
|
||||
if args.task_type and args.run_id and args.status:
|
||||
payload = {
|
||||
"type": args.task_type,
|
||||
"run_id": args.run_id,
|
||||
"status": args.status,
|
||||
"summary": args.summary,
|
||||
"payload": payload,
|
||||
**({"error": args.error} if args.error else {}),
|
||||
}
|
||||
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
||||
last_error = ""
|
||||
|
||||
for attempt in range(1, max(1, args.retries) + 1):
|
||||
request = Request(
|
||||
args.url,
|
||||
data=body,
|
||||
headers={
|
||||
"Authorization": f"Bearer {args.token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
try:
|
||||
with urlopen(request, timeout=30) as response:
|
||||
response_body = response.read().decode("utf-8")
|
||||
if 200 <= response.status < 300:
|
||||
print(response_body)
|
||||
return 0
|
||||
last_error = f"HTTP {response.status}: {response_body}"
|
||||
except HTTPError as exc:
|
||||
last_error = f"HTTP {exc.code}: {exc.read().decode('utf-8', errors='replace')}"
|
||||
except URLError as exc:
|
||||
last_error = str(exc.reason)
|
||||
|
||||
if attempt < max(1, args.retries):
|
||||
time.sleep(min(attempt, 3))
|
||||
|
||||
print(last_error or "callback failed", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user