349 lines
12 KiB
Markdown
349 lines
12 KiB
Markdown
# gori-agent
|
||
|
||
通过 QQ 等 IM 调用 ACP Coding Agent。当前默认后端是 Kimi Code:
|
||
|
||
```text
|
||
用户 → QQ → gori-agent Gateway → ACP → Kimi Code → 工具/脚本 → QQ
|
||
```
|
||
|
||
## 五分钟上手
|
||
|
||
### 1. 准备 Kimi Code
|
||
|
||
确认 Kimi Code 已登录且 ACP 可用:
|
||
|
||
```bash
|
||
kimi --version
|
||
kimi doctor
|
||
kimi acp --help
|
||
```
|
||
|
||
ACP 使用 `~/.kimi-code/config.toml` 中的 `default_model`。模型切换应先在 Kimi Code 中完成,再重启 gori-agent;已有聊天要使用新模型时,在 QQ 中发送 `/new` 创建新 session。
|
||
|
||
### 2. 构建并迁移配置
|
||
|
||
```bash
|
||
cd /home/ubuntu/gori-space/gori-agent
|
||
npm install
|
||
npm run build
|
||
./gori-agent.sh setup --config ./config.json
|
||
```
|
||
|
||
建议向导选择:
|
||
|
||
```text
|
||
Assistant workspace: /home/ubuntu/gori-space/gori-agent
|
||
Include ops role with the existing gori-update skill: yes
|
||
Write config: yes
|
||
```
|
||
|
||
迁移会把旧的 `kimi -p` 改为 `kimi acp`,并完整保留已有 QQ appId、secret 和连接模式。
|
||
|
||
### 3. 检查配置
|
||
|
||
```bash
|
||
./gori-agent.sh discover-backends --config ./config.json
|
||
./gori-agent.sh doctor --config ./config.json
|
||
```
|
||
|
||
预期看到 Kimi ACP `ready`,并支持 session `load/resume`。
|
||
|
||
### 4. 前台启动并从 QQ 验收
|
||
|
||
```bash
|
||
./gori-agent.sh start --config ./config.json
|
||
```
|
||
|
||
启动日志应包含:
|
||
|
||
```text
|
||
QQ websocket connected
|
||
QQ websocket ready
|
||
```
|
||
|
||
在 QQ 群中 @bot 并发送:
|
||
|
||
```text
|
||
/status
|
||
/roles
|
||
/role assistant
|
||
只读回答当前工作目录,不要修改文件
|
||
```
|
||
|
||
运维角色先做只读测试:
|
||
|
||
```text
|
||
/role ops
|
||
/status
|
||
请只读取并概述 gori-update skill,不要 pull、构建或部署
|
||
```
|
||
|
||
常用聊天命令:
|
||
|
||
```text
|
||
/roles 列出角色
|
||
/role <id> 切换角色
|
||
/status 查看当前 role/workspace/session 状态
|
||
/cancel 取消正在执行的任务
|
||
/new 为当前角色创建新的 Agent session
|
||
```
|
||
|
||
同一个 `平台 + chat + role` 会持续使用同一个 Kimi session;Gateway 或 ACP 子进程重启后会恢复。不同 role 的 session 相互独立。
|
||
|
||
### 5. 后台运行
|
||
|
||
前台验收通过后:
|
||
|
||
```bash
|
||
export GORI_AGENT_HOME=/home/ubuntu/.gori-agent
|
||
./gori-agent.sh start-daemon --config ./config.json
|
||
./gori-agent.sh status --config ./config.json
|
||
./gori-agent.sh logs
|
||
./gori-agent.sh stop
|
||
```
|
||
|
||
运行状态和日志位于:
|
||
|
||
```text
|
||
/home/ubuntu/.gori-agent/state/
|
||
/home/ubuntu/.gori-agent/logs/gori-agent.log
|
||
```
|
||
|
||
`config.json` 包含 IM secret,不要提交,建议执行:
|
||
|
||
```bash
|
||
chmod 600 config.json
|
||
```
|
||
|
||
## 多机器与多 QQ Bot
|
||
|
||
仓库只维护一个无密钥模板:`config.example.json`。在每台机器、每个 Bot 实例中复制后单独修改:
|
||
|
||
```bash
|
||
cp config.example.json config.json
|
||
chmod 600 config.json
|
||
```
|
||
|
||
每个进程只能登录一个 QQ Bot。为每个实例配置唯一的 QQ `appId`、`clientSecret`、`botNames` 和 `server.port`,并使用不同的 `GORI_AGENT_HOME`,避免日志、PID 和 ACP session 状态相互覆盖:
|
||
|
||
```bash
|
||
GORI_AGENT_HOME=/home/USER/.gori-agent-ROLE_ID \
|
||
./gori-agent.sh start-daemon --config ./config.json
|
||
```
|
||
|
||
建议每个 Bot 的 `roles[]` 只保留它自己的一个 role,同时修改 `ROLE_ID`、`workspace`、`persona` 和 `policy`。需要 skill 时,再向顶层 `skills[]` 添加对应 SKILL.md,并在 role 的 `skills` 中引用。各机器的 `config.json` 不要提交到仓库。
|
||
|
||
## 配置角色
|
||
|
||
角色定义在 `config.json` 的 `roles[]` 中:
|
||
|
||
```json
|
||
{
|
||
"id": "ops",
|
||
"backend": "kimi",
|
||
"workspace": "/home/ubuntu/gori-space",
|
||
"persona": "你是 Gori 团队运维角色,严格遵循 gori-update skill。",
|
||
"skills": ["gori-update"],
|
||
"policy": {
|
||
"permissionMode": "allowlist",
|
||
"allowedTools": ["read", "grep", "glob", "bash"],
|
||
"allowedCommandPatterns": ["允许的命令正则"]
|
||
}
|
||
}
|
||
```
|
||
|
||
- `backend`:ACP 后端 ID,默认 `kimi`。
|
||
- `workspace`:该角色的工作目录,必须是绝对路径。
|
||
- `persona`:角色职责。
|
||
- `skills`:引用顶层 `skills[]` 中的 SKILL.md。
|
||
- `permissionMode`:`deny` 全拒绝;`allowlist` 按工具和命令规则放行;`auto` 全放行(高风险)。
|
||
|
||
修改 persona、workspace、skill 内容或 policy 后,角色 fingerprint 会变化;下一条消息会创建新的 native session,避免沿用旧角色上下文。
|
||
|
||
## 配置 QQ
|
||
|
||
`config.json` 的最小 QQ websocket 配置:
|
||
|
||
```json
|
||
{
|
||
"policy": {
|
||
"allowedUsers": [],
|
||
"allowedChats": [],
|
||
"requireMentionInGroup": true
|
||
},
|
||
"platforms": {
|
||
"qq": {
|
||
"enabled": true,
|
||
"connectionMode": "websocket",
|
||
"appId": "你的 AppID",
|
||
"clientSecret": "你的 ClientSecret",
|
||
"botSecret": "",
|
||
"verifySignature": true,
|
||
"botNames": ["你的机器人名称"],
|
||
"intents": 33554432,
|
||
"shard": [0, 1]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
初次测试可让 `allowedUsers` 和 `allowedChats` 为空;正式运维 bot 应限制到指定用户或群。群聊 chat ID 的格式为 `group:<group_openid>`。
|
||
|
||
---
|
||
|
||
## Detailed reference
|
||
|
||
Multi-IM gateway that drives coding agents through the official Agent Client Protocol (ACP):
|
||
|
||
```text
|
||
IM Adapter -> Gateway -> Role/Session Manager -> ACP Client -> kimi acp
|
||
```
|
||
|
||
The MVP backend is Kimi Code ACP. Gateway code contains no Kimi CLI prompt fallback and no Pi/Codex private protocol. Future agents must enter through an ACP adapter such as `codex-acp` or a Pi ACP adapter.
|
||
|
||
## Requirements and quick start
|
||
|
||
Requires Node.js 20+ and an authenticated Kimi Code installation.
|
||
|
||
```bash
|
||
npm install
|
||
npm run build
|
||
./install.sh
|
||
gori-agent setup
|
||
gori-agent doctor --config ./config.json
|
||
gori-agent start --config ./config.json
|
||
```
|
||
|
||
`setup` probes ACP backends, creates an assistant role, optionally creates the `ops` role, preserves all existing platform settings, prints a migration summary, and only writes after confirmation. A v1 config is accepted at runtime only when its default agent is Kimi; other CLI agents require an explicit ACP backend.
|
||
|
||
Convenience wrapper commands:
|
||
|
||
```bash
|
||
./gori-agent.sh start
|
||
./gori-agent.sh start-daemon
|
||
./gori-agent.sh status
|
||
./gori-agent.sh logs
|
||
./gori-agent.sh stop
|
||
```
|
||
|
||
`stop` sends SIGTERM and waits up to 10 seconds for graceful shutdown. For production, use systemd with `Restart=always` and `KillMode=control-group` so an unexpected gateway crash also cleans up ACP children.
|
||
|
||
## CLI
|
||
|
||
```bash
|
||
gori-agent setup [--config path]
|
||
gori-agent discover-backends [--json]
|
||
gori-agent start [--config path]
|
||
gori-agent status [--config path]
|
||
gori-agent doctor [--config path]
|
||
gori-agent print feishu [--config path]
|
||
```
|
||
|
||
`discover-agents` remains a deprecated alias for `discover-backends`. Discovery does not send a prompt. Kimi is ready when `kimi acp` is available; Codex and Pi report `needs-adapter` unless their ACP adapter executable exists.
|
||
|
||
## Roles, sessions, and commands
|
||
|
||
A conversation binding is keyed by `platform + chatId + role`. Each binding stores the ACP backend, native session ID, workspace, and role fingerprint. The selected role and bindings survive gateway restart.
|
||
|
||
- A new session receives a hidden bootstrap prompt containing persona, workspace, skill content, and policy. The binding is saved only after bootstrap succeeds.
|
||
- A role persona, workspace, policy, or skill-content change changes its fingerprint and causes a new native session.
|
||
- Idle workers are stopped and later cold-resumed with `session/resume` (or `session/load` when resume is unavailable).
|
||
- Failed prompts are never replayed automatically.
|
||
- `/new` cancels the current turn, unbinds the current chat/role, and creates a native session on the next prompt. It does not delete Kimi history.
|
||
- `/cancel` bypasses the per-chat lock and sends ACP `session/cancel`; an unresponsive worker is force-recycled after the grace period.
|
||
|
||
Chat commands:
|
||
|
||
- `/help`
|
||
- `/roles`
|
||
- `/role <id>`
|
||
- `/status`
|
||
- `/cancel`
|
||
- `/new`
|
||
- `/agents` and `/agent <id>` (deprecated aliases)
|
||
|
||
Messages in one chat are serialized; different chats run concurrently.
|
||
|
||
## Configuration v2
|
||
|
||
See `config.example.json`. Important sections:
|
||
|
||
- `backends[]`: ACP spawn command and arguments. The default is `/home/ubuntu/.kimi-code/bin/kimi acp`.
|
||
- `roles[]`: backend, absolute workspace, persona, skills, and permission policy.
|
||
- `skills[]`: readable skill files with a byte limit. Skill contents participate in the role fingerprint.
|
||
- `acp.stateFile`: defaults to `$GORI_AGENT_HOME/state/acp-sessions.json`.
|
||
- `acp.initializeTimeoutMs`, `promptTimeoutMs`, `cancelGraceMs`: request lifecycle limits.
|
||
- `acp.idleTimeoutMs`, `sweepIntervalMs`, `maxProcesses`: worker pool limits.
|
||
- `policy.allowedUsers`, `allowedChats`, `requireMentionInGroup`: inbound IM policy.
|
||
- `platforms`: Feishu, WeCom, QQ, generic webhook, and Weixin settings. Migration preserves this object, including credentials.
|
||
|
||
State writes use a serial promise queue, temporary file, fsync, and atomic rename. A single-instance lock protects the state file. Corrupt state is preserved and startup fails explicitly instead of overwriting it.
|
||
|
||
## Permission policy
|
||
|
||
ACP permission requests are decided by role policy, not by persona:
|
||
|
||
- `deny`: reject every permission request.
|
||
- `allowlist`: require both an allowed tool name and, for bash/terminal, a matching raw command pattern. If the ACP request lacks enough command detail, it is denied.
|
||
- `auto`: approve everything; `doctor` prints a high-risk warning.
|
||
|
||
The example `ops` role loads the existing read-only skill file at `/home/ubuntu/gori-space/gori-deploy/.kimi-code/skills/gori-update/SKILL.md`. Its allowlist covers selected `git`, build/deploy entry scripts, and read-only Docker status/log commands. `sudo`, force push, and arbitrary deletion are not allowed. Script-internal operations cannot be inspected by ACP once an explicitly allowed deployment script starts, so script paths must remain trusted.
|
||
|
||
Kimi 0.36.1 does not advertise a generic model config option during ACP initialize; this MVP uses the model configured as Kimi's default and `doctor` reports that limitation.
|
||
|
||
## Platform behavior
|
||
|
||
| Platform | Inbound | Outbound | Notes |
|
||
| --- | --- | --- | --- |
|
||
| Feishu/Lark | Implemented | Implemented | `im.message.receive_v1` and message reply API. |
|
||
| WeCom | 501 scaffold | Implemented | Inbound verification/encryption is not implemented. |
|
||
| Weixin | External webhook scaffold | Synchronous | Native personal WeChat is not included. |
|
||
| QQ | Implemented | Implemented | WebSocket gateway and HTTP callback modes. |
|
||
| Generic webhook | Implemented | Synchronous JSON | HMAC-SHA256 signed JSON. |
|
||
|
||
QQ WebSocket mode uses intent `33554432` for `GROUP_AT_MESSAGE_CREATE` and `C2C_MESSAGE_CREATE`. The adapter supports top-level and nested `author.user_openid` fields, preserves callback ACK `{ "op": 12 }` even if ACP work later fails, and logs receive/send routing. No external QQ message is sent by the automated tests.
|
||
|
||
Endpoints:
|
||
|
||
- `GET /health` — aggregate ACP counters only; native session IDs are not exposed.
|
||
- `GET /platforms`
|
||
- `POST /webhook/feishu`
|
||
- `POST /webhook/wecom`
|
||
- `POST /webhook/qq`
|
||
- `POST /webhook/generic`
|
||
- `POST /webhook/weixin`
|
||
|
||
Generic webhook payload:
|
||
|
||
```json
|
||
{
|
||
"chat_id": "demo-chat",
|
||
"user_id": "demo-user",
|
||
"text": "/status",
|
||
"message_id": "optional",
|
||
"is_group": false,
|
||
"mentions_bot": true
|
||
}
|
||
```
|
||
|
||
When `platforms.webhook.secret` is set, provide `X-Gori-Signature: sha256=<HMAC-SHA256 hex>` over the exact JSON body.
|
||
|
||
## Lifecycle and diagnostics
|
||
|
||
Shutdown order is QQ WebSocket stop, active ACP cancellation, worker termination, state flush/unlock. SIGINT and SIGTERM share the same idempotent shutdown path. Health statistics include active workers, in-flight turns, worker crashes, persisted bindings, and locked chats.
|
||
|
||
`doctor` checks role/workspace/skill validity, ACP initialize and restore capabilities, state directory access, permission warnings, configured platforms, and QQ requirements. It performs no prompt and no external QQ test.
|
||
|
||
## Development
|
||
|
||
```bash
|
||
npm run typecheck
|
||
npm test
|
||
npm run build
|
||
git diff --check
|
||
```
|
||
|
||
Tests use Node `node:test` through `tsx`. The fake ACP subprocess covers initialize/new/resume/prompt/cancel, persistence, idle cold resume, Gateway commands, and QQ normalization/ACK behavior.
|
||
|
||
Legacy `src/agents/*` source remains only for compatibility/reference and is not imported by Gateway or Server. There is no runtime `CliAgent` fallback.
|