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

25 KiB
Executable File
Raw Permalink Blame History

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 为每个工具创建子 runrun.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 容器操作

# 构建并启动所有服务
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

数据库操作

# 运行迁移
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

测试

# 运行所有测试
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

代码质量

# 运行 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

# 安装依赖
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

队列与任务

# 查看 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

开发调试

# 启动 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 生成器

# 生成 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.phpapp/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 创建模型数据
  • 运行测试:修改代码后必须运行相关测试确保通过
    # 运行所有测试
    docker compose exec app php artisan test
    
    # 运行特定测试方法
    docker compose exec app php artisan test --filter=testAppendMessageWithDedupe
    

队列配置

  • 开发环境:可使用同步队列,.env 中设置 QUEUE_CONNECTION=sync
    • 优点:调试方便,错误堆栈清晰
    • 缺点:阻塞 HTTP 请求
  • 生产环境:使用 Redis 队列 + 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 检测自动回补

环境变量关键配置

# 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 超时时间(秒)

初始化新环境

# 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 连接:
    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 的消息:
    SELECT message_id, session_id, content, payload
    FROM messages
    WHERE type = 'error'
    ORDER BY created_at DESC
    LIMIT 10;
    
  • 检查 payload.error_typepayload.providerpayload.retryablepayload.details
  • 检查 Provider 配置:
    docker compose exec app php artisan config:show agent
    
  • 查看实时日志:
    docker compose exec app php artisan pail
    # 或查看容器日志
    docker compose logs -f app
    
  • 测试 OpenAI API Key
    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); }
    

工具调用问题

  • 检查工具是否注册:
    docker compose exec app php artisan tinker
    > $registry = app(App\Services\Tool\ToolRegistry::class);
    > dump($registry->openAiToolsSpec());
    
  • 查看 tool.call 和 tool.result 消息:
    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
  • 检查数据库连接:
    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 REPLdocker compose exec app php artisan tinker 交互式调试
  • 查看配置php artisan config:show agent
  • 查看路由php artisan route:list
  • 数据库查询日志:在 .env 中设置 DB_LOG_QUERIES=true