Files
ars-backend/CLAUDE.md
ROOG e956df9daa main: 增强工具功能与消息处理
- 添加 `FileReadTool`,支持文件内容读取与安全验证
- 引入 `hasToolMessages` 逻辑,优化工具历史上下文处理
- 修改工具选项逻辑,支持禁用工具时的动态调整
- 增加消息序列化逻辑,优化 Redis 序列管理与数据同步
- 扩展测试覆盖,验证序列化与工具调用场景
- 增强 Docker Compose 脚本,支持应用重置与日志清理
- 调整工具调用超时设置,提升运行时用户体验
2025-12-24 00:55:54 +08:00

680 lines
25 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
自然语言使用中文。
## 项目概述
这是一个基于 Laravel 12 + Octane + Docker 的 Agent Runtime Server (ARS),用于提供可部署的 Agent 运行时服务。核心特性包括:
- 兼容多种 Agent 模型
- Web 终端实时交互,支持断线重连
- 后台任务持续执行,重连后可续传会话
## 技术栈
- **PHP**: 8.2+
- **Laravel Framework**: 12.x
- **Laravel Octane**: 2.x (FrankenPHP)
- **Laravel Horizon**: 5.x (队列监控)
- **Laravel Telescope**: 5.x (调试工具)
- **数据库**: PostgreSQL 16
- **缓存/队列**: Redis 7
- **认证**: JWT (php-open-source-saver/jwt-auth)
- **容器化**: Docker Compose
## 核心架构
### 数据模型
#### ChatSession (会话)
- `session_id` (UUID, 主键): 会话唯一标识
- `session_name`: 会话名称
- `status`: 会话状态 (OPEN/LOCKED/CLOSED)
- `last_seq`: 最后消息序号
- `last_message_id`: 最后消息ID
#### Message (消息)
- `message_id` (UUID, 主键): 消息唯一标识
- `session_id`: 所属会话ID
- `role`: 消息角色 (USER/AGENT/TOOL/SYSTEM)
- `type`: 消息类型 (user.prompt/agent.message/message.delta/tool.call/tool.result/run.status/error等)
- `content`: 消息内容 (text)
- `payload`: 附加数据 (jsonb),包含 run_id、tool_call_id、error_type 等元数据
- `seq`: 会话内序号 (单调递增)
- `reply_to`: 回复的消息ID
- `dedupe_key`: 幂等去重键
- **约束**: `unique(session_id, seq)``unique(session_id, dedupe_key)`
#### 消息类型完整列表
- `user.prompt` (USER): 用户提示
- `agent.message` (AGENT): Agent 完整回复
- `message.delta` (AGENT): 流式文本增量
- `tool.call` (AGENT): 工具调用请求
- `tool.result` (TOOL): 工具执行结果
- `run.status` (SYSTEM): Run 状态RUNNING/DONE/FAILED/CANCELED
- `error` (SYSTEM): 错误信息
- `run.cancel.request` (USER): 取消请求
### 会话状态与门禁规则
- **OPEN**: 正常追加所有消息
- **LOCKED**: 拒绝 `role=USER && type=user.prompt`
- **CLOSED**: 拒绝追加,例外允许 `role=SYSTEM && type in [run.status, error]`
- **状态变更规则**: CLOSED 状态的会话不能重新打开
### Agent Run 执行流程
1. 用户发送 `user.prompt` 消息后自动触发,或通过 API 手动触发
2. `RunDispatcher` 检查幂等性(基于 `trigger_message_id`
3. 检查会话是否已有 RUNNING 状态的 run单会话单任务限制
4. 创建 `run.status=RUNNING` 消息并派发 `AgentRunJob`
5. `RunLoop` 执行 Agent 调用流程:
- `ContextBuilder` 构建上下文(加载最近 20 条相关消息)
- `AgentProviderInterface::stream()` 流式调用 Agent
- 消费 `Generator<ProviderEvent>` 流:
- `MessageDelta`: 流式文本,写入 `message.delta` 消息
- `ToolCall`: 工具调用,累积后写入 `tool.call` 并分发 `ToolRunJob`
- `Done`: 流结束,写入最终 `agent.message` + `DONE` 状态
- `Error`: 错误,写入 `error` + `FAILED` 状态
- `CancelChecker` 定期检查取消信号
- 工具调用完成后,等待 `tool.result`,继续下一轮 Provider 调用
- `OutputSink` 统一写入消息,保证幂等性
6. 完成后写入 `run.status=DONE/FAILED/CANCELED`
**Provider 选择逻辑**(在 `AppServiceProvider` 中绑定):
- `HttpAgentProvider` 会检查 `AGENT_OPENAI_API_KEY` 环境变量
- 若配置了 OpenAI Key则使用 `OpenAiChatCompletionsAdapter`
- 否则回退到 `DummyAgentProvider`(返回模拟响应)
### 工具系统架构
项目支持 Agent 调用工具Tools采用子 Run 模式:
- **Tool** (`app/Services/Tool/Tool.php`): 工具接口,定义 name、description、parameters、execute 方法
- **ToolRegistry** (`app/Services/Tool/ToolRegistry.php`): 管理已注册工具,生成 OpenAI 兼容工具声明
- **ToolExecutor** (`app/Services/Tool/ToolExecutor.php`): 执行工具,处理超时和结果截断
- **ToolRunDispatcher** (`app/Services/Tool/ToolRunDispatcher.php`): 为每个工具调用创建子 run 并投递 `ToolRunJob`
- **ToolRunJob** (`app/Jobs/ToolRunJob.php`): 队列任务,执行工具并写入 `tool.result` 消息
工具调用流程:
1. Agent 返回 ToolCall 事件
2. RunLoop 累积工具调用,写入 `tool.call` 消息
3. ToolRunDispatcher 为每个工具创建子 run`run.status=RUNNING`
4. ToolRunJob 执行工具,写入 `tool.result` 消息
5. RunLoop 轮询等待所有 `tool.result`(支持超时)
6. 收集工具结果后,继续下一轮 Provider 调用
配置项(`config/agent.php`
- `agent.tools.max_calls_per_run`: 单 run 最多工具调用次数(默认 1
- `agent.tools.wait_timeout_ms`: 等待工具结果超时(默认 15000ms
- `agent.tools.wait_poll_interval_ms`: 轮询间隔(默认 200ms
- `agent.tools.timeout_seconds`: 工具执行超时(默认 15s
- `agent.tools.result_max_bytes`: 结果最大字节数(默认 4096
### 实时消息推送 (SSE)
- **端点**: `GET /api/sessions/{id}/sse?after_seq=123`
- **机制**:
1. 先从数据库补发历史消息seq > after_seq
2. 订阅 Redis 频道 `session:{id}:messages` 监听新消息
3. 支持 `Last-Event-ID` 自动续传
4. 检测 seq gap 自动回补
5. 15 秒心跳保活
- **事件格式**: SSE event id 为消息 seq
### 服务层架构
- **ChatService** (`app/Services/ChatService.php`): 会话和消息的核心业务逻辑
- 使用行锁 (`lockForUpdate`) + 事务保证消息 seq 单调递增
- 通过 `dedupe_key` 实现幂等性
- 消息追加后发布 Redis 事件用于 SSE 推送
- 提供 `appendMessage()``listMessagesBySeq()``updateSession()` 等方法
- **RunDispatcher** (`app/Services/RunDispatcher.php`): Agent Run 调度器
- 检查 trigger_message_id 幂等性
- 确保同会话只有一个 RUNNING 状态的 run
- **RunLoop** (`app/Services/RunLoop.php`): Agent 执行循环
- 协调 ContextBuilder、AgentProvider、OutputSink、CancelChecker、ToolRunDispatcher
- 处理工具调用上限(`max_calls_per_run`
- 达到上限后强制 `tool_choice=none` 防止再次触发
- **OutputSink** (`app/Services/OutputSink.php`): 统一的消息写入接口
- `appendAgentMessage()`: 写入 agent 回复
- `appendAgentDelta()`: 写入流式文本增量
- `appendRunStatus()`: 写入 run 状态
- `appendError()`: 写入错误信息
- `appendToolCall()`: 写入工具调用
- `appendToolResult()`: 写入工具结果
- **ContextBuilder** (`app/Services/ContextBuilder.php`): 构建 Agent 上下文
- 加载最近 20 条相关消息USER/AGENT/TOOL 角色)
- 按 seq 排序并转换为 AgentContext
- **CancelChecker** (`app/Services/CancelChecker.php`): 检查 run 是否被取消
- 查询 `type='run.cancel.request'` 消息
## 常用开发命令
### Docker 容器操作
```bash
# 构建并启动所有服务
docker compose build
docker compose up -d app horizon pgsql redis
# 停止服务
docker compose stop
# 查看日志
docker compose logs -f app
docker compose logs -f horizon
# 进入容器 shell
docker compose exec app bash
```
### 数据库操作
```bash
# 运行迁移
docker compose exec app php artisan migrate
# 回滚迁移
docker compose exec app php artisan migrate:rollback
# 刷新数据库(危险:删除所有表并重新迁移)
docker compose exec app php artisan migrate:fresh
# 运行 seeder
docker compose exec app php artisan db:seed
```
### 测试
```bash
# 运行所有测试
docker compose exec app php artisan test
# 运行特定测试套件
docker compose exec app php artisan test --testsuite=Feature
docker compose exec app php artisan test --testsuite=Unit
# 运行特定测试文件
docker compose exec app php artisan test tests/Feature/ChatSessionTest.php
# 运行特定测试方法(使用 filter
docker compose exec app php artisan test --filter=testCreateSession
docker compose exec app php artisan test --filter=testAppendMessageWithDedupe
# 显示测试覆盖率
docker compose exec app php artisan test --coverage
```
### 代码质量
```bash
# 运行 Laravel Pint 格式化代码
docker compose exec app vendor/bin/pint
# 只检查格式问题(不修改)
docker compose exec app vendor/bin/pint --test
# 格式化脏文件git 变更的文件)
docker compose exec app vendor/bin/pint --dirty
```
### 本地开发(不使用 Docker
如果你想在本地直接运行(需要 PHP 8.2+、PostgreSQL、Redis
```bash
# 安装依赖
composer install
# 启动 Octane 开发服务器
php artisan octane:start --host=0.0.0.0 --port=8000
# 启动队列 worker
php artisan queue:work
# 或启动 Horizon
php artisan horizon
# 查看实时日志
php artisan pail
```
### 队列与任务
```bash
# 查看 Horizon 仪表板
# 访问 http://localhost:8000/horizon
# 手动运行队列 worker开发调试用
docker compose exec app php artisan queue:work
# 查看失败的任务
docker compose exec app php artisan queue:failed
# 重试失败的任务
docker compose exec app php artisan queue:retry all
```
### 开发调试
```bash
# 启动 Tinker REPL
docker compose exec app php artisan tinker
# 查看路由列表
docker compose exec app php artisan route:list
# 清除缓存
docker compose exec app php artisan cache:clear
docker compose exec app php artisan config:clear
docker compose exec app php artisan route:clear
# 查看实时日志(使用 Laravel Pail
docker compose exec app php artisan pail
```
### Artisan 生成器
```bash
# 生成 Model推荐带选项一次性生成相关文件
docker compose exec app php artisan make:model ChatSession -mfs
# -m: migration, -f: factory, -s: seeder
# 生成 Controller
docker compose exec app php artisan make:controller ChatSessionController
# 生成 Form Request用于验证
docker compose exec app php artisan make:request AppendMessageRequest
# 生成 ResourceAPI 响应格式化)
docker compose exec app php artisan make:resource ChatSessionResource
# 生成 Job
docker compose exec app php artisan make:job AgentRunJob
# 生成 Service 类
docker compose exec app php artisan make:class Services/ChatService
# 生成测试
docker compose exec app php artisan make:test ChatSessionTest --phpunit
docker compose exec app php artisan make:test ChatServiceTest --unit --phpunit
```
## API 路由结构
所有 API 路由均在 `/api/*` 下,除登录和健康检查外均需 JWT 认证:
- `POST /api/login`: 用户登录
- `GET /api/health`: 健康检查
- `GET /api/me`: 获取当前用户信息
### 用户管理
- `GET /api/users`: 用户列表
- `POST /api/users`: 创建用户
- `PUT /api/users/{user}`: 更新用户
- `POST /api/users/{user}/deactivate`: 停用用户
- `POST /api/users/{user}/activate`: 激活用户
### 会话管理
- `POST /api/sessions`: 创建会话
- `GET /api/sessions`: 会话列表(支持分页、状态过滤、关键词搜索)
- `GET /api/sessions/{session_id}`: 获取会话详情
- `PATCH /api/sessions/{session_id}`: 更新会话(重命名、状态变更)
- `POST /api/sessions/{session_id}/archive`: 归档会话(幂等,设为 CLOSED
### 消息管理
- `POST /api/sessions/{session_id}/messages`: 追加消息(支持幂等 dedupe_key
- `GET /api/sessions/{session_id}/messages`: 获取消息列表(支持 after_seq 增量拉取)
- `GET /api/sessions/{session_id}/messages/{message_id}`: 获取单条消息
### 实时与 Agent Run
- `GET /api/sessions/{session_id}/sse`: SSE 实时消息流
- `POST /api/sessions/{session_id}/runs`: 手动触发 Agent Run
## 项目结构
```
app/
├── Enums/ # 枚举类ChatSessionStatus 等)
├── Exceptions/ # 自定义异常
├── Http/
│ ├── Controllers/ # API 控制器
│ │ ├── ChatSessionController.php # 会话和消息 API
│ │ ├── ChatSessionSseController.php # SSE 实时推送
│ │ ├── RunController.php # Agent Run 手动触发
│ │ ├── AuthController.php # 用户认证
│ │ └── UserController.php # 用户管理
│ ├── Requests/ # Form Request 验证
│ └── Resources/ # API 响应格式化
├── Jobs/ # 队列任务
│ ├── AgentRunJob.php # Agent Run 队列任务
│ └── ToolRunJob.php # 工具执行队列任务
├── Models/ # Eloquent 模型
│ ├── ChatSession.php
│ ├── Message.php
│ └── User.php
├── Providers/ # 服务提供者
│ └── AppServiceProvider.php # 绑定 AgentProviderInterface
└── Services/ # 业务逻辑服务
├── Agent/ # Agent Provider 实现
│ ├── OpenAi/ # OpenAI 适配器
│ │ ├── OpenAiChatCompletionsAdapter.php
│ │ ├── ChatCompletionsRequestBuilder.php
│ │ ├── OpenAiApiClient.php
│ │ ├── OpenAiStreamParser.php
│ │ └── OpenAiEventNormalizer.php
│ ├── AgentProviderInterface.php
│ ├── AgentContext.php
│ ├── ProviderEvent.php
│ ├── ProviderEventType.php
│ ├── ProviderException.php
│ ├── HttpAgentProvider.php
│ └── DummyAgentProvider.php
├── Tool/ # 工具系统
│ ├── Tool.php # 工具接口
│ ├── ToolRegistry.php # 工具注册表
│ ├── ToolExecutor.php # 工具执行器
│ ├── ToolRunDispatcher.php # 工具 Run 分发器
│ ├── ToolCall.php # 工具调用对象
│ ├── ToolResult.php # 工具结果对象
│ └── Tools/ # 具体工具实现
│ └── GetTimeTool.php # 获取时间工具(示例)
├── ChatService.php # 会话和消息核心服务
├── RunDispatcher.php # Run 调度器
├── RunLoop.php # Run 执行循环
├── ContextBuilder.php # 上下文构建器
├── OutputSink.php # 消息写入器
└── CancelChecker.php # 取消检查器
database/
├── migrations/
│ └── 2025_02_14_000003_create_chat_tables.php # 核心表结构
└── factories/ # 测试数据工厂
tests/
├── Feature/
│ ├── ChatSessionTest.php # 会话和消息测试
│ └── AgentRunTest.php # Agent Run 流程测试
└── Unit/
└── OpenAiAdapterTest.php # OpenAI 适配器单元测试
config/
├── agent.php # Agent Provider 和工具配置
├── auth.php # JWT 认证配置
├── queue.php # 队列配置
└── horizon.php # Horizon 队列监控配置
bootstrap/
├── app.php # Laravel 12 应用引导(中间件、路由、异常)
└── providers.php # 服务提供者注册
```
**关键设计原则**
- 所有 Agent Provider 实现 `AgentProviderInterface::stream()` 接口
- 使用 `Generator` 模式流式返回 `ProviderEvent`
- 统一通过 `OutputSink` 写入消息,保证事务性和幂等性
- 工具系统采用子 Run 模式,每个工具调用创建独立 run
- 所有异步操作通过队列AgentRunJob、ToolRunJob执行
## 开发注意事项
### Laravel 12 新特性
-`app/Console/Kernel.php``app/Http/Kernel.php`
- 中间件、路由、异常处理在 `bootstrap/app.php` 配置
- 服务提供者在 `bootstrap/providers.php` 注册
- Commands 自动注册(无需手动注册)
- JWT 中间件别名在 `bootstrap/app.php` 中配置为 `auth.jwt`
### 数据库操作规范
- **消息追加**:必须使用 `ChatService::appendMessage()`,不要直接操作 Message 模型
- 原因:需要行锁 + 事务保证 seq 单调递增,并发布 Redis 事件
- 所有 `OutputSink` 方法最终都调用 `ChatService::appendMessage()`
- **会话状态变更**:必须通过 `ChatService::updateSession()`
- 会自动校验 CLOSED 状态不可重新打开
- **所有涉及 seq 递增的操作**:必须在事务 + 行锁中完成
### Provider 与 Event Stream 开发
- 实现自定义 Provider 时必须实现 `AgentProviderInterface::stream()` 接口
- 使用 `Generator` 模式 yield `ProviderEvent` 对象
- 事件类型:
- `ProviderEvent::messageDelta($content)`: 流式文本增量
- `ProviderEvent::toolCall($toolCallId, $name, $arguments)`: 工具调用
- `ProviderEvent::done($finishReason)`: 流结束
- `ProviderEvent::error($errorCode, $message, $retryable)`: 错误
- 错误处理:抛出 `ProviderException` 包含 errorCode、retryable、httpStatus
- `RunLoop` 会自动处理重试、取消检查、工具调用分发
### 工具开发规范
- 创建新工具:继承 `Tool` 抽象类,实现 `name()``description()``parameters()``execute()` 方法
- 注册工具:在 `AppServiceProvider` 中调用 `ToolRegistry::register($tool)`
- 工具执行:
- `execute()` 方法接收 `array $args`,返回字符串结果
- 超时控制:通过 `AGENT_TOOL_TIMEOUT_SECONDS` 配置
- 结果截断:超过 `AGENT_TOOL_RESULT_MAX_BYTES` 会自动截断并标记
- 工具参数:使用 JSON Schema 格式定义,会自动传递给 OpenAI API
### 测试规范
- **框架**:所有测试使用 PHPUnit非 Pest
- **Feature 测试**:必须测试完整的 HTTP 请求流程
- 使用 `RefreshDatabase` trait 在测试间刷新数据库
- 使用 `Queue::fake()` 模拟队列
- 使用 `Redis::shouldReceive()` 模拟 Redis 发布
- **测试数据**:使用 Factory 创建模型数据
- **运行测试**:修改代码后必须运行相关测试确保通过
```bash
# 运行所有测试
docker compose exec app php artisan test
# 运行特定测试方法
docker compose exec app php artisan test --filter=testAppendMessageWithDedupe
```
### 队列配置
- **开发环境**:可使用同步队列,`.env` 中设置 `QUEUE_CONNECTION=sync`
- 优点:调试方便,错误堆栈清晰
- 缺点:阻塞 HTTP 请求
- **生产环境**:使用 Redis 队列 + Horizon 监控
- `AgentRunJob` 和 `ToolRunJob` 在队列中异步执行
- Horizon 仪表板http://localhost:8000/horizon
- **Job 配置**:通过 `config/agent.php` 控制重试次数、退避时间、超时
### 幂等性设计
- **dedupe_key 机制**:所有可能重复调用的操作都使用 `dedupe_key`
- 基于 UNIQUE 约束 `unique(session_id, dedupe_key)` 自动去重
- 重复请求返回已有消息(相同 message_id 和 seq
- **Run 幂等**`RunDispatcher` 通过 `trigger_message_id` 的 dedupe_key 确保不会为同一 prompt 重复创建 run
- **SSE 续传**:通过 `Last-Event-ID` / `after_seq` 支持断线续传
- **消息幂等模式**
- `run:{runId}:agent:message` - Agent 最终回复
- `run:{runId}:agent:delta:{index}` - 流式增量
- `run:{runId}:status:{status}` - Run 状态
- `run:{runId}:tool:call:{toolCallId}` - 工具调用
- `run:{runId}:tool:result:{toolCallId}` - 工具结果
### 性能优化建议
- **上下文加载**`ContextBuilder` 只加载最近 20 条消息,可通过配置调整
- **消息分页**`listMessagesBySeq()` 使用 `after_seq` + `limit` 增量拉取
- **索引优化**`(session_id, seq)` 和 `(session_id, dedupe_key)` 复合索引加速查询
- **Redis 发布**:消息追加后异步发布,使用 `DB::afterCommit()` 保证顺序
- **SSE 优化**backlog 限制 200 条,心跳 15 秒gap 检测自动回补
## 环境变量关键配置
```bash
# Octane 服务器
OCTANE_SERVER=frankenphp
# 数据库PostgreSQL
DB_CONNECTION=pgsql
DB_HOST=pgsql
DB_PORT=5432
DB_DATABASE=ars_backend
DB_USERNAME=ars
DB_PASSWORD=secret
# Redis
REDIS_CLIENT=phpredis
REDIS_HOST=redis
REDIS_PORT=6379
CACHE_STORE=redis
# 队列
QUEUE_CONNECTION=redis # 或 sync开发用
# JWT 认证
JWT_SECRET=<生成的密钥>
AUTH_GUARD=api
# CORS
CORS_ALLOWED_ORIGINS=http://localhost:5173
# OpenAI 兼容 API 配置
AGENT_OPENAI_BASE_URL=https://api.openai.com/v1 # 支持任何 OpenAI 兼容端点
AGENT_OPENAI_API_KEY= # 为空时使用 DummyProvider
AGENT_OPENAI_ORGANIZATION= # 可选
AGENT_OPENAI_PROJECT= # 可选
AGENT_OPENAI_MODEL=gpt-4o-mini
AGENT_OPENAI_TEMPERATURE=0.7
AGENT_OPENAI_TOP_P=1.0
AGENT_OPENAI_INCLUDE_USAGE=false
# Agent Provider HTTP 配置(重试机制)
AGENT_PROVIDER_TIMEOUT=30 # HTTP 请求超时(秒)
AGENT_PROVIDER_CONNECT_TIMEOUT=5 # 连接超时(秒)
AGENT_PROVIDER_RETRY_TIMES=1 # 建立流前重试次数(仅连接失败/429/5xx 且未产出事件时)
AGENT_PROVIDER_RETRY_BACKOFF_MS=500 # 重试退避毫秒(指数退避)
# Agent Run Job 配置
AGENT_RUN_JOB_TRIES=1 # 队列重试次数
AGENT_RUN_JOB_BACKOFF=3 # 重试退避秒数
AGENT_RUN_JOB_TIMEOUT=600 # Job 超时时间(秒)
# 工具系统配置
AGENT_TOOL_MAX_CALLS_PER_RUN=1 # 单个父 Run 允许的工具调用次数
AGENT_TOOL_WAIT_TIMEOUT_MS=15000 # 等待 tool.result 的超时时间(毫秒)
AGENT_TOOL_WAIT_POLL_MS=200 # 等待工具结果轮询间隔(毫秒)
AGENT_TOOL_TIMEOUT_SECONDS=15 # 单个工具执行超时(秒,超出记为 TIMEOUT
AGENT_TOOL_RESULT_MAX_BYTES=4096 # 工具结果最大保存字节数(截断后仍会写入)
AGENT_TOOL_CHOICE=auto # OpenAI tool_choice 选项auto/required 等)
AGENT_TOOL_JOB_TRIES=1 # ToolRunJob 重试次数
AGENT_TOOL_JOB_BACKOFF=3 # ToolRunJob 重试退避秒数
AGENT_TOOL_JOB_TIMEOUT=120 # ToolRunJob 超时时间(秒)
```
## 初始化新环境
```bash
# 1. 复制环境配置
cp .env.example .env
# 2. 生成应用密钥
docker compose exec app php artisan key:generate
# 3. 生成 JWT 密钥
docker compose exec app php artisan jwt:secret
# 4. 运行迁移
docker compose exec app php artisan migrate
# 5. (可选)创建测试用户
docker compose exec app php artisan db:seed
```
## 相关文档
- API 详细文档:`docs/ChatSession/chat-session-api.md`
- OpenAPI 规范:`docs/ChatSession/chat-session-openapi.yaml`
- 用户管理文档:`docs/User/user-api.md`
## 常见问题排查
### 队列任务不执行
- 检查 Horizon 是否运行:`docker compose ps horizon`
- 查看 Horizon 日志:`docker compose logs -f horizon`
- 检查 Redis 连接:
```bash
docker compose exec app php artisan tinker
> Redis::ping() # 应返回 "PONG"
```
- 查看失败的任务:`docker compose exec app php artisan queue:failed`
- 重试失败任务:`docker compose exec app php artisan queue:retry all`
### SSE 连接断开
- 检查 Nginx/代理是否支持 SSE需要禁用缓冲
- 确认客户端正确处理 `Last-Event-ID` 续传
- 查看 Redis 发布日志
- 测试环境下 SSE 会自动回退到仅返回 backlog无实时推送
### Agent Run 失败
- 查看 `messages` 表中 `type=error` 的消息:
```sql
SELECT message_id, session_id, content, payload
FROM messages
WHERE type = 'error'
ORDER BY created_at DESC
LIMIT 10;
```
- 检查 `payload.error_type`、`payload.provider`、`payload.retryable`、`payload.details`
- 检查 Provider 配置:
```bash
docker compose exec app php artisan config:show agent
```
- 查看实时日志:
```bash
docker compose exec app php artisan pail
# 或查看容器日志
docker compose logs -f app
```
- 测试 OpenAI API Key
```bash
docker compose exec app php artisan tinker
> $provider = app(App\Services\Agent\AgentProviderInterface::class);
> $context = new App\Services\Agent\AgentContext('test', []);
> foreach ($provider->stream($context) as $event) { dump($event); }
```
### 工具调用问题
- 检查工具是否注册:
```bash
docker compose exec app php artisan tinker
> $registry = app(App\Services\Tool\ToolRegistry::class);
> dump($registry->openAiToolsSpec());
```
- 查看 tool.call 和 tool.result 消息:
```sql
SELECT message_id, type, content, payload
FROM messages
WHERE session_id = 'xxx' AND type IN ('tool.call', 'tool.result')
ORDER BY seq;
```
- 检查工具调用上限:配置 `AGENT_TOOL_MAX_CALLS_PER_RUN`
- 工具执行超时:检查 `payload.status` 是否为 `TIMEOUT`
### 数据库迁移问题
- 确保 PostgreSQL 已启动:`docker compose ps pgsql`
- 查看迁移状态:`docker compose exec app php artisan migrate:status`
- 检查数据库连接:
```bash
docker compose exec app php artisan tinker
> DB::connection()->getPdo()
```
- 查看数据库日志:`docker compose logs -f pgsql`
### 消息 seq 不连续或重复
- 检查是否有并发追加消息(应使用行锁 + 事务)
- 确认所有消息追加都通过 `ChatService::appendMessage()`
- 查看 unique 约束冲突日志
### 调试技巧
- **实时日志**`docker compose exec app php artisan pail`
- **Telescope**:访问 http://localhost:8000/telescope 查看请求、查询、队列
- **Tinker REPL**`docker compose exec app php artisan tinker` 交互式调试
- **查看配置**`php artisan config:show agent`
- **查看路由**`php artisan route:list`
- **数据库查询日志**:在 `.env` 中设置 `DB_LOG_QUERIES=true`