506 lines
14 KiB
Markdown
506 lines
14 KiB
Markdown
|
|
# Dev-Assistant
|
|||
|
|
|
|||
|
|
嵌入式 AI 开发助手 — 专为 admin-xy 项目设计,支持 Claude / GLM / MiniMax 多模型运行时切换。
|
|||
|
|
|
|||
|
|
## 架构概览
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ admin-xy/web (React) │
|
|||
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ DevAssistantMount (侧栏组件) │ │
|
|||
|
|
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
|
|||
|
|
│ │ │ @dev-assistant/web-sdk │ │ │
|
|||
|
|
│ │ │ - WebSocket 通信 │ │ │
|
|||
|
|
│ │ │ - 页面上下文捕获 (选区/控制台/网络请求) │ │ │
|
|||
|
|
│ │ │ - 流式响应渲染 │ │ │
|
|||
|
|
│ │ │ - 工具执行确认 │ │ │
|
|||
|
|
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
|||
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────────────────┘
|
|||
|
|
│ WebSocket + HTTP
|
|||
|
|
▼
|
|||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ @dev-assistant/bridge (Node.js) │
|
|||
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ WebSocket 会话管理 │ │
|
|||
|
|
│ │ - JWT 认证校验 (支持 HTTP 校验 / 本地解析) │ │
|
|||
|
|
│ │ - 速率限制 │ │
|
|||
|
|
│ │ - 上下文收集 (日志/文件/代码) │ │
|
|||
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|||
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ 模型路由 & Agent SDK │ │
|
|||
|
|
│ │ - 多模型协议适配 (Anthropic 兼容) │ │
|
|||
|
|
│ │ - 工具执行 (Read/Edit/Bash/Glob/Grep) │ │
|
|||
|
|
│ │ - 流式响应处理 │ │
|
|||
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────────────────┘
|
|||
|
|
│ HTTP
|
|||
|
|
▼
|
|||
|
|
┌────────────┴────────────┐
|
|||
|
|
│ │
|
|||
|
|
GLM / MiniMax Claude API
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 快速开始
|
|||
|
|
|
|||
|
|
### 前置要求
|
|||
|
|
|
|||
|
|
- Node.js >= 20
|
|||
|
|
- admin-xy 后端服务运行在 http://localhost:9010
|
|||
|
|
- AI 模型 API Key(配置后存储在 keychain 或加密文件中)
|
|||
|
|
|
|||
|
|
### 安装依赖
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd dev-assistant
|
|||
|
|
npm install
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 启动开发环境
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 启动桥接服务
|
|||
|
|
npm run bridge:dev
|
|||
|
|
|
|||
|
|
# 构建前端 SDK(admin-xy/web 集成时需要)
|
|||
|
|
npm run web-sdk:build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
桥接服务默认监听 `http://127.0.0.1:9020`,WebSocket 路径为 `/copilot`。
|
|||
|
|
|
|||
|
|
## 配置说明
|
|||
|
|
|
|||
|
|
配置文件位于 `config/dev-assistant.yaml`,启动时自动加载,支持热更新。
|
|||
|
|
|
|||
|
|
### 核心配置项
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# 项目信息 — 驱动 AI 系统提示
|
|||
|
|
project:
|
|||
|
|
name: admin-xy
|
|||
|
|
description: 企业管理系统 — 用户/角色/菜单/权限管理
|
|||
|
|
techStack:
|
|||
|
|
- Spring Boot 3
|
|||
|
|
- Java 21
|
|||
|
|
- React 18
|
|||
|
|
- Vite
|
|||
|
|
- TypeScript
|
|||
|
|
conventions:
|
|||
|
|
- 分层架构(Controller → Service → Repository → Entity → DTO)
|
|||
|
|
- JWT 无状态认证
|
|||
|
|
- Flyway 数据库迁移
|
|||
|
|
|
|||
|
|
# 工作目录配置
|
|||
|
|
workspace:
|
|||
|
|
root: ./ # 相对于仓库根
|
|||
|
|
allowWrite:
|
|||
|
|
- admin-xy/** # 允许写入的路径
|
|||
|
|
- dev-assistant/**
|
|||
|
|
denyWrite:
|
|||
|
|
- .claude/** # 禁止写入的路径
|
|||
|
|
- '**/.env*'
|
|||
|
|
- '**/target/**'
|
|||
|
|
- '**/node_modules/**'
|
|||
|
|
|
|||
|
|
# 日志源配置(用于 bugfix 场景自动附加)
|
|||
|
|
logs:
|
|||
|
|
- id: admin-server
|
|||
|
|
label: admin-xy Spring Boot
|
|||
|
|
path: admin-xy/server/logs/app.log
|
|||
|
|
format: logback-json # 支持 logback-json / plain / spring-default
|
|||
|
|
tailLines: 500
|
|||
|
|
followDurationSec: 30
|
|||
|
|
|
|||
|
|
# 上下文预算控制
|
|||
|
|
context:
|
|||
|
|
maxPromptTokens: 16000
|
|||
|
|
attachAsFileThreshold: 4000 # 超过此大小作为文件附件而非内联
|
|||
|
|
cacheDir: dev-assistant/.cache
|
|||
|
|
console:
|
|||
|
|
keepLines: 200 # 控制台保留行数
|
|||
|
|
network:
|
|||
|
|
keepRequests: 50 # 网络请求保留数量
|
|||
|
|
captureBody: true
|
|||
|
|
bodyMaxBytes: 4096
|
|||
|
|
|
|||
|
|
# 场景配置
|
|||
|
|
scenarios:
|
|||
|
|
modify:
|
|||
|
|
label: 调整现有页面
|
|||
|
|
requireSelection: true # 要求用户选择文本
|
|||
|
|
bugfix:
|
|||
|
|
label: 排查页面问题
|
|||
|
|
attachErrorLogs: true # 自动附加错误日志
|
|||
|
|
followJavaLogs: true
|
|||
|
|
|
|||
|
|
# 桥接服务配置
|
|||
|
|
bridge:
|
|||
|
|
port: 9020
|
|||
|
|
host: 127.0.0.1 # 容器环境改为 0.0.0.0
|
|||
|
|
wsPath: /copilot
|
|||
|
|
origin: https://oa.xiangyuetech.cn # CORS 允许的前端 Origin
|
|||
|
|
wsIdleTimeoutMs: 1800000 # 30 分钟空闲超时
|
|||
|
|
maxConcurrentChats: 1
|
|||
|
|
auth:
|
|||
|
|
type: http # http / jwt-local / disabled
|
|||
|
|
verifyUrl: http://server:9010/api/auth/me # HTTP 校验端点
|
|||
|
|
requiredRole: ROLE_ADMIN
|
|||
|
|
cacheTtlSec: 60
|
|||
|
|
rateLimit:
|
|||
|
|
chatPerMinute: 20
|
|||
|
|
httpPerMinute: 120
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 环境变量覆盖
|
|||
|
|
|
|||
|
|
生产环境可通过环境变量覆盖配置:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
BRIDGE_HOST=0.0.0.0 # 监听地址
|
|||
|
|
BRIDGE_AUTH_VERIFY_URL=http://... # 认证校验端点
|
|||
|
|
BRIDGE_AUTH_DISABLED=true # 跳过认证(仅本机开发)
|
|||
|
|
BRIDGE_ORIGIN=https://... # CORS 允许的 Origin
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 认证机制
|
|||
|
|
|
|||
|
|
支持三种认证模式:
|
|||
|
|
|
|||
|
|
### 1. HTTP 远程校验(默认)
|
|||
|
|
|
|||
|
|
桥接服务调用后端 `/api/auth/me` 接口验证 JWT 有效性。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
bridge:
|
|||
|
|
auth:
|
|||
|
|
type: http
|
|||
|
|
verifyUrl: http://server:9010/api/auth/me
|
|||
|
|
requiredRole: ROLE_ADMIN
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. JWT 本地解析
|
|||
|
|
|
|||
|
|
无需网络调用,本地解析 JWT 验证用户和角色。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
bridge:
|
|||
|
|
auth:
|
|||
|
|
type: jwt-local
|
|||
|
|
jwtSecret: ${JWT_SECRET} # 从环境变量读取
|
|||
|
|
jwtIssuer: admin-xy
|
|||
|
|
jwtAudience: admin-xy-web
|
|||
|
|
jwtUsernameClaim: sub
|
|||
|
|
jwtRolesClaim: roles
|
|||
|
|
requiredRole: ROLE_ADMIN
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 禁用认证(仅开发环境)
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
bridge:
|
|||
|
|
auth:
|
|||
|
|
type: disabled
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
或通过环境变量:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
BRIDGE_AUTH_DISABLED=true npm run bridge:dev
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 模型管理
|
|||
|
|
|
|||
|
|
模型配置和 API Key 通过 Web SDK 动态管理,存储在 `dev-assistant/.cache/` 目录(gitignore)。
|
|||
|
|
|
|||
|
|
### 支持的模型
|
|||
|
|
|
|||
|
|
- **Claude** (Anthropic) - 原生支持
|
|||
|
|
- **GLM** (智谱 AI) - 通过协议适配器
|
|||
|
|
- **MiniMax** - 通过协议适配器
|
|||
|
|
|
|||
|
|
### 管理接口
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
# 获取模型列表
|
|||
|
|
GET /api/models
|
|||
|
|
|
|||
|
|
# 设置活跃模型
|
|||
|
|
PUT /api/models/active
|
|||
|
|
Body: { "provider": "claude", "model": "claude-sonnet-4-6" }
|
|||
|
|
|
|||
|
|
# 更新 API Key
|
|||
|
|
PUT /api/models/:provider/key
|
|||
|
|
Body: { "apiKey": "sk-..." }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## WebSocket 协议
|
|||
|
|
|
|||
|
|
### 客户端 → 服务端
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 发起对话
|
|||
|
|
{
|
|||
|
|
type: 'chat',
|
|||
|
|
requestId: 'uuid',
|
|||
|
|
scenario: 'bugfix',
|
|||
|
|
prompt: '修复用户登录失败的问题',
|
|||
|
|
context: {
|
|||
|
|
url: '/login',
|
|||
|
|
selection: '选中的代码',
|
|||
|
|
console: [...],
|
|||
|
|
network: [...]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 中止对话
|
|||
|
|
{ type: 'abort', requestId: 'uuid' }
|
|||
|
|
|
|||
|
|
// 工具执行决策
|
|||
|
|
{
|
|||
|
|
type: 'tool_decision',
|
|||
|
|
requestId: 'uuid',
|
|||
|
|
toolUseId: 'tool-xxx',
|
|||
|
|
decision: 'allow', // 或 'deny'
|
|||
|
|
message: '允许此操作'
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 服务端 → 客户端
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 会话开始
|
|||
|
|
{
|
|||
|
|
type: 'session_started',
|
|||
|
|
serverTime: 1714567890123,
|
|||
|
|
active: { name: 'Claude', model: 'claude-sonnet-4-6' }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 消息开始
|
|||
|
|
{
|
|||
|
|
type: 'message_started',
|
|||
|
|
requestId: 'uuid',
|
|||
|
|
provider: 'claude',
|
|||
|
|
model: 'claude-sonnet-4-6'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文本流式输出
|
|||
|
|
{
|
|||
|
|
type: 'text_delta',
|
|||
|
|
requestId: 'uuid',
|
|||
|
|
text: '根据日志分析...'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 工具使用开始
|
|||
|
|
{
|
|||
|
|
type: 'tool_use_started',
|
|||
|
|
requestId: 'uuid',
|
|||
|
|
toolUseId: 'tool-xxx',
|
|||
|
|
name: 'Read'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 工具执行请求(需用户确认)
|
|||
|
|
{
|
|||
|
|
type: 'tool_request',
|
|||
|
|
requestId: 'uuid',
|
|||
|
|
toolUseId: 'tool-xxx',
|
|||
|
|
name: 'Bash',
|
|||
|
|
input: { command: 'npm install' },
|
|||
|
|
destructive: false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 消息完成
|
|||
|
|
{
|
|||
|
|
type: 'message_done',
|
|||
|
|
requestId: 'uuid',
|
|||
|
|
stopReason: 'end_turn',
|
|||
|
|
outputTokens: 1234
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 集成到 admin-xy
|
|||
|
|
|
|||
|
|
### 前端集成
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
// admin-xy/web/src/integrations/dev-assistant.tsx
|
|||
|
|
import { useEffect, useRef } from 'react';
|
|||
|
|
import {
|
|||
|
|
ADMIN_ROLE,
|
|||
|
|
mountDevAssistant,
|
|||
|
|
type DevAssistantHandle,
|
|||
|
|
} from '@dev-assistant/web-sdk';
|
|||
|
|
import '@dev-assistant/web-sdk/styles.css';
|
|||
|
|
import { useAuthStore } from '@/stores/auth-store';
|
|||
|
|
|
|||
|
|
export function DevAssistantMount(): null {
|
|||
|
|
const user = useAuthStore((s) => s.user);
|
|||
|
|
const handleRef = useRef<DevAssistantHandle | null>(null);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
const isAdmin = !!user?.roles?.includes(ADMIN_ROLE);
|
|||
|
|
if (!isAdmin) {
|
|||
|
|
handleRef.current?.unmount();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (handleRef.current) return;
|
|||
|
|
|
|||
|
|
const token = useAuthStore.getState().token ?? undefined;
|
|||
|
|
handleRef.current = mountDevAssistant({
|
|||
|
|
bridgeHttpUrl: 'http://localhost:9020',
|
|||
|
|
bridgeWsUrl: 'ws://localhost:9020/copilot',
|
|||
|
|
roles: user?.roles ?? [],
|
|||
|
|
userIdentifier: user?.username,
|
|||
|
|
token,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
handleRef.current?.unmount();
|
|||
|
|
handleRef.current = null;
|
|||
|
|
};
|
|||
|
|
}, [user]);
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 后端日志集成
|
|||
|
|
|
|||
|
|
在 Spring Boot 中添加 `RequestIdFilter` 和 `MdcUserFilter`,将请求 ID 和用户信息写入 MDC:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Component
|
|||
|
|
public class RequestIdFilter implements Filter {
|
|||
|
|
@Override
|
|||
|
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
|
|||
|
|
String requestId = ((HttpServletRequest) request).getHeader("X-Request-Id");
|
|||
|
|
MDC.put("requestId", requestId != null ? requestId : UUID.randomUUID().toString());
|
|||
|
|
try {
|
|||
|
|
chain.doFilter(request, response);
|
|||
|
|
} finally {
|
|||
|
|
MDC.remove("requestId");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
配置 logback JSON 输出,使日志包含 MDC 字段,便于 bridge 关联查询。
|
|||
|
|
|
|||
|
|
## 日志查询 API
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
# 获取日志源列表
|
|||
|
|
GET /api/logs/sources
|
|||
|
|
|
|||
|
|
# 读取单个日志源尾部
|
|||
|
|
GET /api/logs/admin-server?tailLines=100&minLevel=WARN&contains=ERROR
|
|||
|
|
|
|||
|
|
# 聚合读取所有日志源
|
|||
|
|
GET /api/logs?priority=high&minLevel=ERROR
|
|||
|
|
|
|||
|
|
# 按 RequestId 关联查询
|
|||
|
|
GET /api/logs?requestId=abc-123-def
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 编程式 API
|
|||
|
|
|
|||
|
|
### 启动桥接服务
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import { startBridge } from '@dev-assistant/bridge';
|
|||
|
|
|
|||
|
|
const handle = await startBridge({
|
|||
|
|
configPath: './my-config.yaml',
|
|||
|
|
configOverrides: {
|
|||
|
|
bridge: { port: 9021 }
|
|||
|
|
},
|
|||
|
|
// 自定义认证验证器
|
|||
|
|
verifier: {
|
|||
|
|
async verify(token) {
|
|||
|
|
// 返回 { username, roles, isAdmin } 或 null
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
onReady: ({ port, host }) => {
|
|||
|
|
console.log(`Bridge listening on ${host}:${port}`);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 关闭服务
|
|||
|
|
await handle.close();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### CLI 启动
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npx dev-assistant-bridge --config ./dev-assistant.yaml
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Docker 部署
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# docker-compose.yml
|
|||
|
|
services:
|
|||
|
|
dev-assistant:
|
|||
|
|
image: dev-assistant:latest
|
|||
|
|
ports:
|
|||
|
|
- "9020:9020"
|
|||
|
|
environment:
|
|||
|
|
- BRIDGE_HOST=0.0.0.0
|
|||
|
|
- BRIDGE_AUTH_VERIFY_URL=http://admin-server:9010/api/auth/me
|
|||
|
|
- BRIDGE_ORIGIN=https://oa.xiangyuetech.cn
|
|||
|
|
volumes:
|
|||
|
|
- ./config/dev-assistant.yaml:/app/dev-assistant.yaml
|
|||
|
|
- ./dev-assistant-data:/app/dev-assistant-data
|
|||
|
|
depends_on:
|
|||
|
|
- admin-server
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 安全注意事项
|
|||
|
|
|
|||
|
|
1. **API Key 管理**:禁止将 API Key 写入配置文件或代码,使用 keychain 或加密文件存储
|
|||
|
|
2. **认证配置**:生产环境必须启用认证(type: http 或 jwt-local)
|
|||
|
|
3. **路径限制**:配置 `workspace.denyWrite` 保护敏感目录(.env、node_modules、.claude)
|
|||
|
|
4. **速率限制**:根据实际使用调整 `rateLimit` 防止滥用
|
|||
|
|
5. **CORS 配置**:`bridge.origin` 严格限制允许的前端来源
|
|||
|
|
|
|||
|
|
## 故障排查
|
|||
|
|
|
|||
|
|
### 连接失败
|
|||
|
|
|
|||
|
|
1. 检查桥接服务是否启动:`curl http://localhost:9020/health`
|
|||
|
|
2. 验证 CORS 配置:`bridge.origin` 是否包含前端域名
|
|||
|
|
3. 确认认证配置是否正确
|
|||
|
|
|
|||
|
|
### 认证失败
|
|||
|
|
|
|||
|
|
1. 检查 JWT 是否有效:通过后端 `/api/auth/me` 验证
|
|||
|
|
2. 确认用户角色包含 `requiredRole`(默认 ROLE_ADMIN)
|
|||
|
|
3. 查看桥接服务日志中的认证错误
|
|||
|
|
|
|||
|
|
### 模型调用失败
|
|||
|
|
|
|||
|
|
1. 验证 API Key 是否配置:通过 `/api/models` 查看
|
|||
|
|
2. 检查模型协议适配器日志
|
|||
|
|
3. 确认网络可访问模型 API 端点
|
|||
|
|
|
|||
|
|
## 开发
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 安装依赖
|
|||
|
|
npm install
|
|||
|
|
|
|||
|
|
# 开发模式(热重载)
|
|||
|
|
npm run bridge:dev
|
|||
|
|
|
|||
|
|
# 构建
|
|||
|
|
npm run build
|
|||
|
|
|
|||
|
|
# 测试
|
|||
|
|
npm test
|
|||
|
|
npm run test:watch
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 许可证
|
|||
|
|
|
|||
|
|
MIT
|