跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

部署运维

最近更新:

运行、配置并维护自托管实例。

本章面向负责安装和维护 Server 的用户。首次部署可从单机开始;只有需要拆开管理与执行服务时,才需要阅读分离部署。

1 - 单机与 Docker 部署

最近更新:

在本机运行包含 Web 的 Standalone Server。

Standalone 在一个 Server 中提供管理页面、对话执行和定时任务,适合本机试用或单台主机部署。默认使用 SQLite 保存数据,由当前进程运行任务,无需另行部署数据库服务。

可以使用 Docker 镜像,或自行构建与操作系统及处理器架构匹配的 Portable Server。两种方式都包含 Web 界面;下面先完成本机访问,再说明目录挂载和远程访问。

Docker 本机试用

docker run -d --name agw \
  --restart unless-stopped \
  -p 127.0.0.1:30816:8080 \
  -v agw-data:/data \
  ghcr.io/zxyao145/agw:latest

打开 http://localhost:30816/setup 完成初始化。通过 Docker 端口映射访问时,容器看到的请求来源不是回环地址,Setup 页面会要求填写一次性 Setup Code;用 docker logs agw 在启动日志中查找 “Agw remote setup code”。镜像包含静态 Web,浏览器直接访问 Server 即可。示例的 latest 适用于试用;持续运行的部署应换成 Releases 对应的固定版本标签。

要让 Agent 访问主机项目,额外添加显式 bind mount,再将容器内路径配置为 Project Workspace。不要将主机路径误填成容器可见路径。

Portable Server

Portable Server 不随 Release 提供,需要在仓库根目录构建,例如:

PUBLISH_MODE=portable APP_VERSION=0.1.0 RIDS=linux-x64 ./publish.sh

产物位于 artifacts/publish/portable/agw-server-<版本>-<RID>/,同时生成对应的压缩包。在产物目录中启动:

./agw-server serve

Windows 使用 agw-server.exe serve。默认监听 http://127.0.0.1:30816,需要调整时使用 ASPNETCORE_URLS。未设置 ASPNETCORE_URLS 且 30816 端口已被占用时,Server 会改用一个随机的本机空闲端口,并把实际地址记录在 <AgwDataDir>/runtime/server.json 中。先验证本机 Setup、Web 和一次对话,再配置远程访问。

持久化与网络

Docker 数据目录为 /data,日志默认独立写入工作目录下的 logs;持久化文件日志需要另外挂载配置的日志路径。Project Workspace 也独立于数据卷。

远程部署需要正确配置 AllowedHosts、受信代理、HTTPS 和 WebSocket 转发。仓库 Compose 是带域名与代理配置的示例,使用前替换成真实环境值。完整持久化范围见备份与升级。

实现与参考

2 - Control/Data Plane 分离部署

最近更新:

共享 PostgreSQL、密钥和工作目录,按角色路由请求。

分离部署把管理和调度放在 Control Plane(控制面),把任务执行放在 Data Plane(数据面)。需要单独维护执行环境或增加执行节点时,可以采用这种方式;只在一台主机试用时,Standalone更容易配置。

本页面向熟悉容器、数据库和反向代理的部署者。开始前,准备共享 PostgreSQL、用于解密凭据的 Data Protection 密钥,以及各执行节点都能访问的工作目录。入口代理还需要支持 WebSocket,以便持续传输对话事件。

角色

Host职责
Control PlaneSetup、Web、管理 API、Jobs 调度
Data PlaneSignalR Execution、A2A、持久化执行 workers
Standalone合并两种职责,适合单机

分离部署使用 Distributed 执行模式。在这种模式下,Chat 中直接运行 External Agent(Claude Code、Codex、Pi)的回合会报错 “Distributed execution currently supports System Agents only.”;需要直接使用这些外部 Agent 时,应选择 InProcess 执行的 Standalone。

分离部署要求两端使用 PostgreSQL 数据库、Distributed 执行和 PostgreSQL 锁。不能使用 SQLite 或内存锁替代跨节点协调。

Database__Provider: postgres
Database__ConnectionString: "${AGW_DATABASE_CONNECTION_STRING}"
Execution__Provider: Distributed
DistributedLock__Provider: postgres
DistributedLock__ConnectionString: ""

这是注入两端的环境配置片段。连接字符串为空的锁配置复用数据库连接;实际数据库连接字符串通过 Secrets 提供。

启动与路由

  1. 按仓库 cluster Compose 配置数据库、两个 Host、共享密钥和目录。
  2. 先启动 Control Plane,完成初始化并确认就绪:GET /api/health/ready 在未初始化或数据库无法连接时返回 503,就绪后返回 200;GET /api/health/live 只表示进程在运行。这两个地址不需要登录。
  3. 再启动 Data Plane,最后按需要增加副本。
  4. 将 /api/hubs/exec、/a2a/* 和 /.well-known/agents.json 路由到 Data Plane,其余应用路径到 Control Plane。

保留 Host、认证头/Cookie 和 WebSocket Upgrade;执行 Hub 查询字符串不应写入代理访问日志。Control Plane 不提供 A2A。

如何阅读下面的示例

Docker Compose 部署从文末的 cluster Compose 文件开始,并按上一节验证启动顺序和路由。Kubernetes 部署可参考下面的本地 kind 示例;其中 kind 是在容器中运行本地 Kubernetes 集群的工具,Pod 是运行应用的单元,Service 提供访问地址,PV/PVC 用于声明和申请存储。

两种部署都需要统一的客户端入口。Nginx 一节说明哪些请求发送到控制面、哪些发送到数据面。先确保两个服务和数据库可用,再检查入口转发,便于区分服务自身与代理的问题。

Kubernetes YAML 示例

仓库的 deploy/k8s 提供一套本地单节点 kind 示例。它将 Control Plane 和 Data Plane 分成独立 Deployment,使用外部 PostgreSQL,并通过 NodePort 接入后文的 Nginx 配置。以下内容对应这些文件,不会额外创建 PostgreSQL 或 Ingress Controller。

文件用途
kind-agw-cluster.yaml创建本地 kind 集群,映射端口与宿主目录
agw-data-pv-pvc.yaml提供共享给同一节点上各 Pod 的数据卷
agw-control-plane-deployment.yaml1 个 Control Plane 副本和 NodePort Service
agw-data-plane-deployment.yaml2 个 Data Plane 副本和 NodePort Service

集群入口与共享目录

kind-agw-cluster.yaml 将两个 NodePort 映射到宿主机的回环地址,同时将 /opt/agw 挂入 kind 节点:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 30816
        hostPort: 30816
        listenAddress: "127.0.0.1"
        protocol: TCP
      - containerPort: 30820
        hostPort: 30820
        listenAddress: "127.0.0.1"
        protocol: TCP
    extraMounts:
      # Required by agw-data-pv: expose the host directory inside the kind node.
      - hostPath: /opt/agw
        containerPath: /opt/agw

创建集群前,在容器运行时所在主机准备 /opt/agw/agw-data。使用 Docker/Podman 虚拟机时,还需通过文件共享配置使该路径在虚拟机中可用。role: control-plane 指 Kubernetes 节点角色,与 AGW 的 Control Plane 服务不是同一概念。

数据的实际路径为:

宿主机 /opt/agw/agw-data
  → kind 节点 /opt/agw/agw-data
  → PV agw-data-pv → PVC agw-data
  → Control/Data Plane Pod 内 /data

下面是对应的 PV/PVC。Retain 保留回收后的卷数据,但不代替备份;PVC 的 1Gi 是请求容量,PV 声明的容量为 5Gi。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: agw-data-pv
  labels:
    app: agw
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  hostPath:
    # Node path backed by kind-agw-cluster.yaml extraMounts; single-node use only.
    path: /opt/agw/agw-data
    type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: agw-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: manual
  resources:
    requests:
      storage: 1Gi
  volumeName: agw-data-pv

ReadWriteOnce 允许同一节点上的多个 Pod 挂载,因此本例两个角色及 Data Plane 副本可以共用该卷。hostPath 不提供跨节点共享存储;多节点部署需要替换成集群支持的共享存储,并保证密钥、凭据和 Project 工作目录在各执行节点一致。若项目目录位于 /data 之外,还需为这些目录添加相应挂载。

Data Plane Deployment 与 Service

以下是仓库中的完整 Data Plane 示例。它运行两个副本,监听容器端口 8080,读取 agw-database Secret,并将 Service 暴露为 30820。Control Plane 的对应文件使用同样的数据卷和数据库配置,副本数为 1、NodePort 为 30816,并额外读取 agw-admin 的 password 作为 Setup__AdminPassword。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: agw-data-plane
  labels:
    app: agw-data-plane
spec:
  replicas: 2
  selector:
    matchLabels:
      app: agw-data-plane
  template:
    metadata:
      labels:
        app: agw-data-plane
    spec:
      containers:
        - name: agw-data-plane
          image: localhost/agw-data-plane:local
          imagePullPolicy: Never
          env:
            - name: AgwLogDir
              value: /data/logs
            - name: AgwDataDir
              value: /data
            - name: ASPNETCORE_ENVIRONMENT
              value: Production
            - name: ASPNETCORE_URLS
              value: http://0.0.0.0:8080
            - name: DistributedLock__Provider
              value: postgres
            - name: Execution__Provider
              value: Distributed
            - name: Database__Provider
              value: postgres
            - name: Database__ConnectionString
              valueFrom:
                secretKeyRef:
                  name: agw-database
                  key: connection-string
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          securityContext:
            # The local hostPath is owned by the host user with mode 0700; preserve the
            # current local Podman setup's root access so the server can write /data.
            runAsUser: 0
            runAsGroup: 0
            runAsNonRoot: false
          volumeMounts:
            - name: agw-data
              mountPath: /data
      volumes:
        - name: agw-data
          persistentVolumeClaim:
            claimName: agw-data
---
apiVersion: v1
kind: Service
metadata:
  name: agw-data-plane
  labels:
    app: agw-data-plane
spec:
  type: NodePort
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  selector:
    app: agw-data-plane
  ports:
    - name: http
      protocol: TCP
      port: 30820
      targetPort: http
      nodePort: 30820

使用前需要确认以下配置:

  • 镜像:示例使用 localhost/agw-…:local 和 imagePullPolicy: Never,需要先构建镜像并加载到 kind 节点。使用镜像仓库时,替换为可拉取的地址和版本,并调整拉取策略及必要的凭据。
  • 数据库:两个角色的 Secret 必须指向同一个可从 Pod 访问的 PostgreSQL。连接字符串中的 localhost 指 Pod 自身,通常不是宿主机数据库。
  • 权限:示例为了适配本地目录权限使用 root 身份运行。其他环境应按存储权限配置合适的 UID/GID,不应直接照搬这个本地设置。
  • 连接保持:Service 使用 ClientIP 会话亲和性,帮助 SignalR 请求落到同一 Pod。如果 Nginx 位于集群外,多个客户端可能都表现为同一个代理 IP,因此不能据此保证负载均匀。

部署顺序

先准备本地镜像、数据目录和两个 Secret 的内容文件,再从仓库根目录执行。Secret 文件只包含对应值,不要将真实凭据提交到仓库。以下命令使用当前 kubectl 上下文的默认 namespace;若选择其他 namespace,Deployment、Service、PVC 和 Secret 必须保持一致。

# Run from the repository root, after preparing /opt/agw/agw-data.
kind create cluster --name agw --config deploy/k8s/kind-agw-cluster.yaml
kubectl apply -f deploy/k8s/agw-data-pv-pvc.yaml

# These images must already exist in the local container runtime.
kind load docker-image --name agw \
  localhost/agw-control-plane:local localhost/agw-data-plane:local

# Replace these paths with files containing the actual secret values.
kubectl create secret generic agw-database \
  --from-file=connection-string=/secure/agw-database-connection-string
kubectl create secret generic agw-admin \
  --from-file=password=/secure/agw-admin-password

kubectl apply -f deploy/k8s/agw-control-plane-deployment.yaml
kubectl rollout status deployment/agw-control-plane
kubectl logs deployment/agw-control-plane --tail=100

# Continue only after Control Plane initialization has completed.
kubectl apply -f deploy/k8s/agw-data-plane-deployment.yaml
kubectl rollout status deployment/agw-data-plane
kubectl get pods,services,pvc

rollout status 表示 Deployment 已完成滚动更新,不能单独证明 AGW 已完成初始化。本例由 Control Plane 的 Setup__AdminPassword 触发首次初始化;应结合日志和登录页面确认成功后再启动 Data Plane。已有数据库的认证配置不会被该初始密码覆盖。

按上述 kind 端口映射运行时,后文 Nginx 示例中的 upstream 可直接使用 127.0.0.1:30816 和 127.0.0.1:30820。如果 Nginx 在集群内部,则使用相同 namespace 下的 Service 地址 agw-control-plane:30816 和 agw-data-plane:30820。检查 PVC 为 Bound、Pod 正常运行后,再验证登录、执行连接和各节点实际接收的请求。

不要执行 kubectl apply -f deploy/k8s/:目录中的 kind Cluster 文件是 kind 的输入,不是 Kubernetes API 资源。更改 kind 的端口或目录映射需要重建集群,操作前先备份数据。完整步骤见 本地 kind 部署说明。

Nginx 配置示例

下面的配置中,Nginx 提供统一入口,Control Plane 监听 30816,Data Plane 监听 30820,与前面的 kind 示例一致;仓库中的 deploy/nginx.split.conf.example 和 cluster Compose 示例则让 Data Plane 使用 30817。端口只是示例,需要与实际 Host 的监听地址一致;如果服务运行在不同主机或容器中,将 127.0.0.1 换成 Nginx 能访问的地址。

Control Plane 同时提供 Web

将以下内容保存为 Nginx 的站点配置文件,并确保它被 nginx.conf 的 http {} 引入。map、log_format 和 upstream 不能放进 server {}。日志路径相对于 Nginx prefix,使用前创建对应目录或替换为可写的绝对路径。

# Included inside nginx.conf's http {} block.
map $http_upgrade $agw_connection_upgrade {
    default upgrade;
    ''      close;
}

# Do not log query strings, which may contain an execution token.
log_format agw_route '$remote_addr [$time_local] '
                     '"$request_method $uri $server_protocol" $status '
                     'upstream=$upstream_addr upstream_status=$upstream_status';

upstream agw_control_plane {
    server 127.0.0.1:30816;
}

upstream agw_data_plane {
    ip_hash;
    server 127.0.0.1:30820;
    # server 127.0.0.1:30821;
}

server {
    listen 80;
    server_name agw.example.com;

    client_max_body_size 100m;
    access_log logs/agw_access.log agw_route;
    error_log  logs/agw_error.log warn;

    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $agw_connection_upgrade;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;

    # Includes /api/hubs/exec/negotiate and the WebSocket endpoint.
    location ^~ /api/hubs/exec {
        proxy_buffering off;
        proxy_pass http://agw_data_plane;
    }

    location ^~ /a2a/ {
        proxy_buffering off;
        proxy_pass http://agw_data_plane;
    }

    location = /.well-known/agents.json {
        proxy_pass http://agw_data_plane;
    }

    # Setup, management APIs, OpenAPI, and the hosted Web client.
    location / {
        proxy_pass http://agw_control_plane;
    }
}

此示例使用 HTTP 便于本机验证。对外使用时,在该 server 中配置 listen 443 ssl;、ssl_certificate 和 ssl_certificate_key,使用自己的域名与有效证书,并将 HTTP 入口重定向到 HTTPS。

请求转发目标作用
/api/hubs/exec 及其子路径Data PlaneSignalR 协商与执行连接
/a2a/*Data PlaneA2A 请求及流式响应
/.well-known/agents.jsonData PlaneAgent 发现
其余路径Control Plane初始化、管理 API、OpenAPI、Web 页面与静态资源

proxy_pass 不附加 URI,保留原始路径和查询参数。认证头和 Cookie 默认随请求转发;Upgrade、Connection 和 HTTP/1.1 用于 WebSocket。关闭执行与 A2A 路由的响应缓冲,避免流式内容被代理积攒后才返回。3600s 是代理读写超时设置,不保证任意时长的任务都不会断线。

多 Data Plane 实例时,ip_hash 让来自同一 IP 的连接尽量落到同一实例,避免 SignalR 协商和后续连接被分到不同节点;它不能替代共享数据库、执行状态和恢复配置。若 Nginx 前还有代理,需结合实际网络配置可信代理与客户端 IP;不要直接信任来自任意来源的 X-Forwarded-For。

示例访问日志使用 $uri,不记录查询参数;upstream 字段可帮助确认请求实际进入哪个节点。client_max_body_size 只控制 Nginx 请求体限制,不会提高 AGW 对图片等附件的限制。

Web 单独运行

如果 Web 在 3001 单独运行,保留上述 Data Plane 路由和公共代理设置,再添加 agw_web upstream,按下面的方式调整管理路由并替换原来的 location /。3001 是仓库 Web 开发端口,实际部署按 Web 服务端口填写。

# Add inside http {}, alongside the other upstream blocks.
upstream agw_web {
    server 127.0.0.1:3001;
}

# Add these locations inside the existing server {}.
location /api/ {
    proxy_pass http://agw_control_plane;
}
location /openapi/ {
    proxy_pass http://agw_control_plane;
}
location = /setup {
    proxy_pass http://agw_control_plane;
}
location /setup/ {
    proxy_pass http://agw_control_plane;
}

# Replace the existing location /; do not add a second one.
location / {
    proxy_pass http://agw_web;
}

这样 /setup 和 /setup/ 都会进入 Control Plane,普通 /api/ 不会误送到 Web;更长的 /api/hubs/exec 匹配仍进入 Data Plane。独立 Web 服务自身的后端地址也应指向 Control Plane。客户端统一使用 Nginx 的入口地址。

检查并加载配置

保存实际配置后,先检查,再重新加载:

nginx -t
nginx -s reload

重新加载需要在自己的部署环境执行。检查登录和页面资源是否正常;在浏览器网络面板检查执行连接是否成功升级为 WebSocket(101),并查看访问日志中的 upstream 是否为 Data Plane。普通管理 API 则应进入 Control Plane。登录失败先核对 Cookie、转发协议和应用的代理信任设置;执行连接失败先检查 Upgrade、路由及 Data Plane 端口。

验证

依次验证登录、一次 Chat、一次 Job,并观察实际执行节点。所有 Host 从同一数据库读取初始化和认证状态。重启恢复还依赖共享目录、密钥和运行时凭据一致,不能仅验证容器都已启动。

实现与参考

3 - 配置与认证

最近更新:

了解各项设置的用途、默认值,以及如何配置服务和登录认证。

本页说明 AGW Server 的运行设置:服务地址、数据存放位置、任务执行方式、日志和登录认证。模型、Agent、Project 和集成账号则在管理界面中设置。修改本页配置后,请重启相应的 Server 程序。

初次在本机使用时,大多数设置可以保留默认值。需要远程访问时,重点检查服务地址、客户端来源和代理设置;需要分离部署时,再配置 PostgreSQL 和 Distributed 模式。分布式执行的轮询、批量写入等参数,通常可以先保持默认值。

先看与当前任务有关的设置

只在本机开始使用时,可以先保留默认配置并完成初始化。准备长期运行前,确认数据库和数据目录在哪里,按备份指南保存数据。

远程访问遇到问题时,优先检查“服务地址与数据目录”“初始化、来源与反向代理”和“认证与 API Key”。分离部署则先看两端的必需配置;后面的轮询和批量参数是调优参考,无需在首次部署时逐一修改。

配置方法与优先级

同一项设置可以写在配置文件、环境变量或启动命令中。如果多处都设置了它,后面的来源优先:

程序默认值 → appsettings.json → 环境专用 JSON → 开发环境的 Secrets(机密配置)→ 环境变量 → 命令行。

例如,文件中设置为 SQLite,启动命令中指定 PostgreSQL,最终会使用 PostgreSQL;但连接字符串不会随之自动更换,需要一起修改。配置文件位于 Server 程序目录。日志工具 Serilog 的读取方式有所不同,见下方日志配置。

本页用冒号表示配置的分组,例如 Database:Provider 表示 Database 分组中的 Provider。它在环境变量中写成 Database__Provider,在启动命令中写成 --Database:Provider postgres。开关使用 true(开启)或 false(关闭);需要从几个选项中选择时,请使用表格列出的名称。

表格中的“appsettings.json”指 Server 自带的 appsettings.json;“未设置时”指文件、环境变量和命令行中都没有这一项。两种情况下的默认值若有不同,会分别说明。

按部署方式选择配置

先选择部署方式,再查对应的配置组。部署方式决定启动哪些 Server 程序,执行模式决定任务如何运行,两者需要分别选择。

配置组什么时候需要看
通用配置所有部署都需要了解:服务地址、数据目录、数据库、认证和日志等
Standalone 单机部署一个 Server 同时负责管理、调度和执行;默认使用 SQLite 和 InProcess
Control/Data Plane 分离部署控制面负责管理调度,数据面负责执行;必须使用 PostgreSQL 和 Distributed
Distributed 执行调优使用 Distributed 时再看:包括分离部署,也包括主动启用 Distributed 的 Standalone

Standalone 单机部署

本机试用或单台 Server 部署,可以先保留下面的默认组合,通常无需填写这些配置:

完整配置名默认选择含义
Database:Providersqlite使用本地数据库文件
Database:ConnectionStringData Source=agw.dbSQLite 文件位置;相对路径以 <AgwDataDir>/database/ 为起点,默认文件为 <AgwDataDir>/database/agw.db
Execution:ProviderInProcess由当前 Server 直接运行任务
DistributedLock:Provider未设置随 SQLite 自动使用进程内锁
DistributedLock:ConnectionString空进程内锁无需连接数据库

Standalone 也可以使用 PostgreSQL,而继续保留 InProcess。若选择 Distributed,则需要同时满足下一节的 PostgreSQL 数据库和锁要求,并按分布式执行方式配置。单机部署方法见单机与 Docker 部署。

Control/Data Plane 分离部署

两端必须使用同一套业务数据库,并采用下面的组合。先完成 Control Plane 初始化,再启动 Data Plane。

完整配置名必需设置配置在哪一端
Database:Providerpostgres两端
Database:ConnectionString指向同一个 PostgreSQL 数据库两端
Execution:ProviderDistributed两端
DistributedLock:Providerpostgres,或不填以跟随数据库两端
DistributedLock:ConnectionString留空复用数据库连接,或指向相同的锁服务两端保持一致

通用配置也要按各自职责填写:

  • Control Plane:配置初始化密码、管理页面使用的地址,以及集成 OAuth 的公开地址。
  • Data Plane:准备 Agent 所需的 CLI、Shell、工作目录和文件。工作节点的并发数、检查间隔在这一端影响实际执行。
  • 两端共同检查:各自的监听地址、客户端来源和代理、日志与监控;需要解密共享数据的节点应使用一致的加密密钥。所有执行节点都必须能访问任务使用的工作目录。

执行模式

下表配置项的完整名称都以 Execution: 开头。

配置项默认值用途与可选值
ProviderInProcessInProcess:由当前 Server 直接运行任务,适合简单的单机部署。Distributed:将执行状态保存到 PostgreSQL,由负责执行的 Server 领取并运行任务,支持多个执行节点协作。选择 Distributed 时,数据库和分布式锁都必须使用 PostgreSQL。
TurnBroadcastRetentionSeconds300一个回合结束后,本 Server 在内存中保留该回合回放内容的秒数。在这段时间内重新连接,或重试已被接受的同一回合的客户端,可以收到完整回放。

InProcess 模式下,回合只存在于当前 Server 进程中。Server 重启时,上一个进程遗留的运行中回合会被结束为 Interrupted,不会继续执行;需要跨重启恢复时使用 Distributed。

这里的 Host 就是运行中的 Server 程序。启动 Standalone 时,一个程序同时负责管理、调度和执行;分离部署时,Control Plane 负责管理和调度,Data Plane 负责执行。分离部署的两端都需要设置 PostgreSQL 数据库、Distributed 执行模式和 PostgreSQL 锁。请按部署方式启动对应程序,Execution:Provider 只决定任务如何执行。

分布式锁

多台 Server 协作时,需要确认“这项工作现在由谁处理”。锁用来保证同一时刻只有一个执行者取得这项工作的处理权,避免相互冲突。

下表配置项的完整名称都以 DistributedLock: 开头。

配置项默认值用途与可选值
Provider未指定,跟随数据库inmemory:在当前 Server 内记录谁正在执行,适合单节点。postgres:让多个 Server 通过 PostgreSQL 共同确认执行权。未设置或填 null 时会自动选择:SQLite 使用 inmemory,PostgreSQL 使用 postgres。
ConnectionString空postgres 锁使用的连接字符串;空值复用 Database:ConnectionString。inmemory 不使用连接字符串。

Distributed 执行调优

下面的设置用于 Distributed 执行模式。分离部署需要使用它;Standalone 只有启用 Distributed 后才需要关注。先保留默认值,出现明确的性能问题后再逐项调整。

分布式工作节点

工作节点是负责实际运行任务的 Server。下面这些设置决定它多久检查新任务、同时处理多少任务,以及执行租约的时长和续期间隔。

下表配置项的完整名称都以 Execution:Distributed: 开头。

配置项默认值用途与可选值
WorkerPollingMilliseconds250每隔多久检查一次是否有新任务,单位毫秒。默认 250 毫秒,即每秒约检查 4 次。调小后可能更快开始任务,但数据库也会更忙。
MaxConcurrentExecutions4每个执行 Server 最多同时处理多少次任务。默认 4,表示这一台 Server 最多同时运行 4 次任务;部署多台时,每台分别计算。
LeaseSeconds30执行租约的时长,单位秒。领取任务的 Server 持有租约;租约到期而没有续期时,其他 Server 可以接手这次执行。正常运行的长任务会持续续期,不会仅因运行超过 30 秒就被接手。
LeaseRenewSeconds10持有租约的 Server 每隔多久续期一次,单位秒,必须小于 LeaseSeconds。

只有选择 Distributed 模式时才需要关注这些设置。所有数值都必须是大于 0 的整数,且 LeaseSeconds 必须大于 LeaseRenewSeconds,否则 Server 启动时报错;没有明确的性能问题时,建议先使用默认值。

执行事件与回放

Agent 执行时会不断产生回复和状态消息。系统保存这些消息后,客户端重新连接时可以补读执行过程,这就是“回放”。这里设置消息保存在哪里,以及读写的频率。

下表配置项的完整名称都以 Execution:Distributed:EventStream: 开头。

配置项默认值用途与可选值
ProviderPostgres执行过程中的消息总是先保存到 PostgreSQL。Postgres:只从 PostgreSQL 读取,无需额外安装 Redis。Redis:同时把已保存的消息复制一份到 Redis Stream,读取时优先使用 Redis,缺少的部分(包括已过期或 Redis 暂时不可用时)从 PostgreSQL 补齐。任务状态和分布式锁始终需要 PostgreSQL。
ReadPollingMilliseconds250暂时没有新消息时,隔多久再检查一次,单位毫秒,必须大于 0。
ReadBatchSize100一次最多读取多少条执行消息,必须大于 0。
WriteIntervalMilliseconds250收到第一条待保存消息后,最多等待多久把消息一起写入,单位毫秒。默认 250;填 0 表示立即写入,不能为负数。
WriteBatchSize100待保存消息达到多少条时,就触发一次批量写入,必须大于 0。默认积累到 100 条时写入。
Redis:ConnectionString空选择 Redis 时必填,所有相关 Server 使用相同 Redis 服务,例如 redis:6379,password=...。
Redis:StreamTtlMinutes1440执行消息在 Redis 中保留多久,单位分钟。默认 1440 分钟,即 24 小时;选择 Redis 时必须大于 0。过期的部分会改从 PostgreSQL 读取。

通用配置

以下设置适用于两种部署方式。分离部署时,根据每个 Server 承担的职责配置相应项目。

服务地址与数据目录

配置项默认值用途与可选值
ASPNETCORE_URLS / --urls本地默认端口 30816;容器由运行配置指定Server 接收请求的地址。用 --urls http://127.0.0.1:30816 仅供本机访问,或按部署需要绑定其他地址。多个地址用分号分隔。
ASPNETCORE_ENVIRONMENTProduction选择环境专用 JSON,例如 appsettings.Production.json。常用名称为 Development(开发)、Staging(预发布)、Production(生产);这是环境名称,不是只接受三个值的枚举。
AgwDataDir~/agwAGW 数据根目录,包含运行数据、Skills 和加密密钥等。支持 ~;相对路径以启动程序时的工作目录为起点。
AgwLogDir./logs独立的日志目录,不随数据目录移动;支持 ~,相对路径从工作目录解析。
AllowedHosts*允许用哪些域名访问 Server。* 表示不限制;也可以填写 agw.example.com;localhost 这样的域名列表,用分号分隔。客户端从哪个页面连接,则由 AllowedOrigins 设置。

数据库

下表配置项的完整名称都以 Database: 开头。

配置项默认值用途与可选值
Providersqlitesqlite:本地 SQLite 文件,适合单机;postgres:PostgreSQL 服务,支持分离和分布式部署。只有这两种值。
ConnectionStringData Source=agw.db所选数据库的连接字符串。SQLite 使用 Data Source=...,相对路径以 <AgwDataDir>/database/ 为起点;PostgreSQL 使用 Host=...;Port=5432;Database=...;Username=...;Password=...,Host 不能为空。切换 Provider 时必须一起修改。

初始化、来源与反向代理

配置项默认值用途与可选值
Setup:AdminPassword未设置首次初始化的管理员密码,8–256 个字符。通过环境或 Secrets 注入可完成无人值守初始化;已有认证配置时不会覆盖密码。分离部署在 Control Plane 初始化。
Auth:AllowedOriginsagw://app、http://localhost:3000、http://127.0.0.1:3000允许哪些客户端地址连接 Server。Origin 是地址的“协议 + 主机名 + 端口”,例如 http://localhost:3000。请填写实际地址,协议和端口都要一致;完全未设置时列表为空。
ReverseProxy:TrustedProxiesappsettings.json含 127.0.0.1、172.16.0.0/12、10.0.0.0/8如果请求经过反向代理,填写代理的实际 IP,让 Server 能识别原始访问地址和 HTTP/HTTPS 协议。当前只接受单个 IP;10.0.0.0/8 这样的网段写法不会生效。程序读取 X-Forwarded-For、X-Forwarded-Host、X-Forwarded-Proto,最多处理一层转发。

数组通过数字下标配置,例如 Auth__AllowedOrigins__0=agw://app。配置覆盖按下标合并;只覆盖第 0 项不会自动删除appsettings.json中的第 1、2 项,需要完整核对最终列表。

集成 OAuth 地址

下表配置项的完整名称都以 Integrations:OAuth: 开头。

配置项默认值用途与可选值
PublicBaseUrlhttp://localhost:30816用户在 GitHub 等服务中完成授权后,浏览器返回 AGW Server 时使用的地址。程序会在它后面加上 api/integrations/oauth/callback。远程部署时,请填写浏览器实际能访问的 Server 地址。
WebBaseUrlhttp://localhost:3001授权流程结束后,用户返回 AGW 页面时使用的地址。请填写实际打开 Web 界面的地址。

两项都填写以 http:// 或 https:// 开头的完整地址,不要附带用户名、密码、? 后的查询参数或 # 后的内容。未设置或留空时,使用当前请求的基础地址。

对话历史写入

下表配置项的完整名称都以 ConversationHistory: 开头。

配置项默认值用途与可选值
ModeIntervalImmediate:有需要保存的内容就立即写入数据库。Interval:先暂存在内存中,隔一段时间一起保存。TurnEnd:主要等这一回合结束后再保存。暂存内容达到大小上限时,也会提前保存。
FlushIntervalSeconds10 秒Server 自带的 appsettings.json 设为 10 秒;如果所有配置来源都没有设置这一项,程序使用 5 秒。Interval 模式下,隔多少秒把暂存内容保存到数据库。必须大于 0,且不能超过程序计时器支持的范围。
MaxBufferedBytes16777216(16 MiB)允许暂存在内存中的内容大小,单位字节。默认 16 MiB;达到上限会提前保存到数据库,必须大于 0。

这些设置决定聊天记录什么时候保存到数据库,页面仍可实时显示回复。先暂存、后保存可以减少数据库写入,但程序意外退出时,尚未保存的部分可能丢失。如果更看重记录及时保存,可以选择 Immediate。

Shell 工具

下表配置项的完整名称都以 Agents:Shell: 开头。

配置项默认值用途与可选值
Backendlocallocal:直接在运行 Agent 的主机上执行命令,使用项目工作目录。docker:在 Docker 容器中执行命令,需要主机已安装并能使用 Docker。容器中的项目目录为 /workspace,附加目录为 /project-directories/{id};当前容器禁用网络,命令超时为 30 秒。

此项只选择 AGW Shell 工具的执行后端,不改变整个 Server 的部署模式,也不为外部 Agent 安装 CLI。

OpenTelemetry

OpenTelemetry 用于把运行指标和调用追踪等信息发送到监控系统,帮助排查慢请求和错误。已有监控服务时,填写它的接收地址;刚开始使用时,先了解下面的默认行为即可。

下表配置项的完整名称都以 OpenTelemetry: 开头。

配置项默认值用途与可选值
ServiceNameappsettings.json Agw监控系统中显示的服务名称。分离 Host 遇到appsettings.json值 Agw 时会改为 Agw.ControlPlane 或 Agw.DataPlane;未设置时使用 Agw.{Host角色}。
ServiceVersion1.0.0监控系统中显示的版本号,方便区分不同版本的运行情况。
OtlpEndpointappsettings.json空接收运行指标、调用追踪等数据的监控服务地址。留空或不填时,不启用 OpenTelemetry 的追踪、指标和日志导出。

日志配置

日志记录 Server 运行中发生的事情。级别越详细,越有利于排查问题,但产生的日志也越多。下面保留完整配置名,日常调整通常只需关注日志级别和保存目录。

配置项默认值用途与可选值
Logging:LogLevel:DefaultInformationMicrosoft 日志默认记录到什么详细程度;可用 Logging:LogLevel:{类别} 单独调整某个模块。
Logging:LogLevel:Microsoft.AspNetCoreWarningWeb 请求处理相关日志的详细程度。
Logging:LogLevel:Microsoft.EntityFrameworkCoreWarning数据库访问相关日志的详细程度。
Serilog:UsingConsole、File、Async sinks启用控制台、文件和异步输出功能所需的日志组件。一般无需修改。
Serilog:MinimumLevel:DefaultInformation;Development 为 DebugSerilog 默认保留的最低日志级别。低于该级别的日志不会输出。
Serilog:MinimumLevel:Override:Microsoft.AspNetCoreWarning单独设置 Web 请求相关日志的最低级别。
Serilog:MinimumLevel:Override:Microsoft.EntityFrameworkCoreWarning单独设置数据库访问日志的最低级别。
Serilog:MinimumLevel:Override:SystemWarning单独设置 System 系统组件日志的最低级别;其他类别可按同样方式设置。
Serilog:WriteTo:0:NameAsync让日志在后台输出,减少写日志对请求处理的影响。一般保留 Async。
Serilog:WriteTo:0:Args:configure:0:NameConsole将日志输出到运行 Server 的终端或容器日志中。
Serilog:WriteTo:0:Args:configure:0:Args:outputTemplate见下方控制台行格式,包含时间、级别、来源、TraceId、SpanId、线程、消息与异常。
Serilog:EnrichFromLogContext、WithMachineName、WithThreadId、WithOpenTelemetryTraceId、WithOpenTelemetrySpanId给日志补充主机名、线程和请求追踪信息,方便把同一次操作的记录联系起来。

当前 Host 的 Serilog 配置从 appsettings.json 及 appsettings.{ASPNETCORE_ENVIRONMENT}.json 单独读取;不能假设 Serilog__... 环境变量会覆盖这条管线。修改日志输出或级别时编辑相应 JSON 并重启。Logging 与 Serilog 是两套级别配置,当前主要输出使用 Serilog。

Microsoft 日志级别全部为:Trace(最细追踪)、Debug(调试)、Information(正常运行)、Warning(异常征兆)、Error(操作失败)、Critical(严重故障)、None(关闭)。Serilog 对应支持 Verbose、Debug、Information、Warning、Error、Fatal;其中 Verbose 对应最细追踪,Fatal 对应严重故障,Serilog 最小级别没有 None。

WriteTo 的 Name、Using 和 Enrich 是插件名称,不是固定枚举;上表列的是当前appsettings.json。Host 还额外写入 AgwLogDir/application-{角色}-.log,每小时生成一个新日志文件,保留 30 个文件,每秒将日志写入磁盘;这些规则由程序固定,不能通过本页配置修改。

[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{SourceContext}] [TraceId:{TraceId}] [SpanId:{SpanId}] [ThreadId:{ThreadId}] {Message:lj}{NewLine}{Exception}

认证与 API Key

在浏览器中使用远程 Web 时,用管理员密码登录,浏览器会通过 Cookie 记住登录状态。Desktop、Mobile 和自动化程序则使用 API Key(访问密钥),它相当于这些客户端连接 Server 的钥匙。请求格式为:

Authorization: Bearer agw_<your-token>

在 Server 所在主机上直接访问时,满足以下全部条件的请求会自动以管理员 1001 的身份通过认证,不需要密码或 API Key:来源是回环地址,没有任何转发请求头,访问的主机名是 localhost 或回环 IP,且请求不带认证请求头或登录 Cookie。经过反向代理或从其他主机访问的请求不满足这些条件。

创建 API Key 时为它起一个便于识别的名称,并保存当时显示的完整值;之后不会再次显示。自动化程序可以从环境变量或机密配置中读取它。不再使用时撤销该 API Key。Server 会按创建者的身份判断它能访问哪些资源。每个 Server 会把验证通过的 API Key 缓存 30 秒:处理撤销请求的 Server 立即清除缓存;分离部署且没有共享的分布式缓存时,其他副本最多 30 秒后停止接受该 API Key。多账号登录通过下一节的第三方登录配置;当前不提供角色、API Key 权限范围(scopes)或 JWT 的配置。

密码和 API Key 都以用于验证的哈希值保存在数据库中,不保存原文。管理员认证信息位于 setting 表的 auth 分组,API Key 信息位于 api_token 表,由管理功能自动维护,无需在 appsettings 中填写。修改管理员密码后,各 Server 会检查新的登录状态版本;检查每秒进行一次,读取失败时不再沿用缓存的认证信息。忘记密码可先停止 Server,再运行 agw-server auth reset-password;分离部署使用 agw-control-plane auth reset-password。新密码需要 12–256 个字符,重置后已有的 Web 登录会话全部失效。

第三方登录

启用第三方登录后,用户可以用组织账号进入 Web 和 Desktop。每个账号首次登录时创建一个独立的本地用户,编号从 10000 开始,管理员仍是 1001。未配置提供商时,管理员密码和 API Key 继续可用。行为说明见第三方账号登录。

在提供商侧把 AGW 登记为 Web 应用(保密客户端),回调地址按提供商 ID 组成。Desktop 用户同样使用这个地址,提供商把浏览器送回 Server,不直接送回桌面程序:

https://agw.example.com/api/auth/oidc/callback/company

下表配置项的完整名称都以 Auth:Oidc: 开头,{id} 是你为提供商起的编号。

配置项默认值用途与可选值
PublicBaseUrl空浏览器访问 Server 的地址,验证完成后返回这里。程序在它后面加上 api/auth/oidc/callback/{id}。必须是不带路径的完整地址,生产环境使用 HTTPS,开发环境允许本机 HTTP。启用任一提供商后必填。
WebBaseUrl空登录结束后返回的 Web 界面地址。Web 与 Server 同源时留空;源码开发中 Web 在 3001、后端在 30816 时需要填写。
Providers:{id}:Enabledfalse是否启用该提供商。
Providers:{id}:TypeOidcOidc:按 Authority 自动读取端点,请求 openid profile email。OAuth2:逐项填写端点和字段名称。
Providers:{id}:DisplayName提供商 ID登录按钮上显示的名称。
Providers:{id}:ClientId空在提供商侧登记 AGW 得到的客户端编号,必填。
Providers:{id}:ClientSecret空对应的客户端密钥,必填,通过环境变量或机密配置注入。
Providers:{id}:Authority空Type 为 Oidc 时必填,例如 https://sso.example.com/realms/company。
Providers:{id}:AuthorizationEndpoint空Type 为 OAuth2 时必填,用户跳转到提供商的授权地址。
Providers:{id}:TokenEndpoint空Type 为 OAuth2 时必填,Server 换取访问令牌的地址。
Providers:{id}:Issuer空Type 为 OAuth2 时必填,用于标识账号来源,与账号编号一起确定用户身份。
Providers:{id}:IdentitySourceUserInfo仅用于 OAuth2。UserInfo:调用用户信息接口读取账号。AccessToken:从签名的 JWT 访问令牌读取账号。
Providers:{id}:UserInfoEndpoint空IdentitySource 为 UserInfo 时必填。
Providers:{id}:AccessTokenIssuer空IdentitySource 为 AccessToken 时必填,校验令牌的签发者。
Providers:{id}:AccessTokenAudience空IdentitySource 为 AccessToken 时必填,校验令牌的接收方。
Providers:{id}:AccessTokenJwksUri空IdentitySource 为 AccessToken 时必填,读取验签公钥的地址。
Providers:{id}:ClientAuthMethodPostOAuth2 换取令牌时提交客户端凭据的方式:Post 放在请求体,Basic 放在请求头。
Providers:{id}:UsePkcefalseOAuth2 是否启用 S256 校验。Type 为 Oidc 时固定启用。
Providers:{id}:Scopes空OAuth2 申请的权限范围,按数字下标配置,例如 Scopes__0=read:user。
Providers:{id}:SubjectClaimsub仅用于 OAuth2,账号编号对应的字段名称,例如 GitHub 使用 id。Oidc 固定使用 sub。
Providers:{id}:DisplayNameClaimname仅用于 OAuth2,显示名称对应的字段名称,例如 GitHub 使用 login。Oidc 固定使用 name。
Providers:{id}:EmailClaimemail仅用于 OAuth2,邮箱对应的字段名称;Oidc 固定使用 email。缺少显示名称或邮箱不影响登录。

提供商 ID 使用小写字母、数字和连字符,最长 64 个字符,例如 company、entra-id。每个 ID 对应各自的回调地址,登记后不要再修改。

不含密钥的配置示例:

{
  "Auth": {
    "Oidc": {
      "PublicBaseUrl": "https://agw.example.com",
      "Providers": {
        "company": {
          "Enabled": true,
          "Type": "Oidc",
          "DisplayName": "公司账号",
          "Authority": "https://sso.example.com/realms/company",
          "ClientId": "agw"
        }
      }
    }
  }
}

对应的密钥通过 Auth__Oidc__Providers__company__ClientSecret 注入,不要写入 appsettings、前端环境文件或截图。GitHub 这类只提供 OAuth2 的服务改用下面的写法:

{
  "Auth": {
    "Oidc": {
      "Providers": {
        "github": {
          "Enabled": true,
          "Type": "OAuth2",
          "DisplayName": "GitHub",
          "AuthorizationEndpoint": "https://github.com/login/oauth/authorize",
          "TokenEndpoint": "https://github.com/login/oauth/access_token",
          "UserInfoEndpoint": "https://api.github.com/user",
          "Issuer": "https://github.com/login/oauth",
          "IdentitySource": "UserInfo",
          "UsePkce": true,
          "ClientAuthMethod": "Post",
          "Scopes": ["read:user"],
          "SubjectClaim": "id",
          "DisplayNameClaim": "login",
          "EmailClaim": "email",
          "ClientId": "github-client-id"
        }
      }
    }
  }
}

常见提供商的 Authority:

平台Authority
Googlehttps://accounts.google.com
Microsoft Entra IDhttps://login.microsoftonline.com/<租户 ID>/v2.0
Keycloakhttps://sso.example.com/realms/<realm>
Authentikhttps://sso.example.com/application/o/<应用标识>/

修改这些配置后重启相应的 Server 程序。分离部署把登录相关请求指向 Control Plane,数据面使用由此得到的本地凭据;所有副本共用同一套数据库和数据保护密钥,Desktop 的一次性代码可以在不同副本上完成换取。停用某个提供商会阻止新的登录和尚未完成的 Desktop 换取,已经签发的 Cookie 和 API Key 需要单独撤销。

配置示例与验证

下面的示例只展示设置方式,连接字符串应通过环境或 Secrets 注入实际值:

export Database__Provider=postgres
export Database__ConnectionString='Host=db;Port=5432;Database=agw;Username=agw;Password=REPLACE_ME'
export Execution__Provider=Distributed
export DistributedLock__Provider=postgres
agw-server --urls http://127.0.0.1:30816

分离部署将相同数据库和执行配置传给各 Host,先初始化 Control Plane,再启动 Data Plane。上面的 agw-server 是 Standalone 程序,分离部署使用相应 Host 程序。

重启后检查启动日志、访问地址、数据库连接和客户端登录。调整执行参数后,先运行一个小任务,观察开始速度、完成时间和资源占用。启动报错时,先检查选项名称是否拼写正确、数值是否在允许范围内、数据库地址和凭据是否正确,以及 Distributed 所需的数据库和锁是否已配置。排查日志时不要公开密码或 API Key。

实现与参考

4 - 数据目录、备份与升级

最近更新:

备份数据库与密钥,并单独管理项目工作目录。

备份需要同时保留 AGW 的配置和记录、加密密钥,以及项目文件。只复制安装目录,或只保存代码仓库,都可能遗漏恢复服务所需的数据。

开始前,确认实际数据库、数据目录和各 Project 工作目录的位置;下面的默认路径仅用于帮助定位,以运行中的配置为准。

数据范围

AgwDataDir 默认 ~/agw,Docker 使用 /data。路径支持 ~,其他相对路径相对进程工作目录解析。默认的 SQLite 数据库文件是 <AgwDataDir>/database/agw.db,Docker 中为 /data/database/agw.db。

需要一起保存:

  • 数据库,包括 setting、api_token、会话和执行记录。
  • 数据目录中的 keys/ 和 skills/。
  • 部署配置及其秘密引用,实际秘密通过安全存储备份。
  • 各 Project 主目录与附加目录中的业务文件,使用独立文件备份策略。

日志目录独立于数据目录;日志和临时文件不属于认证恢复必需数据。丢失 Data Protection 密钥可能使已保护凭据无法读取。

先列出备份清单

记录数据库位置、AgwDataDir 的实际值、各 Project 的目录及当前程序版本。项目使用文件型 Memory 时,还要包含主目录下的隐藏目录 .agw/memory/;数据库型 Memory 随数据库保存。表单默认的主目录 ~/.agw/<项目文件夹名> 和 Server 默认的 ~/.agw/projects/{projectId:N} 都位于运行 Server 的账号的主目录下,不在 AgwDataDir 中;Docker 中也不在 /data 卷内,需要另外挂载并备份。

备份数据库和文件前,先等待任务结束或中断任务,避免备份期间仍有写入。SQLite 的简单做法是停止 Server 后复制数据库及相关文件;PostgreSQL 使用自己的备份工具。数据目录和项目目录可能在不同位置,应逐项核对,不能假定它们都在 /data 内。

升级顺序

  1. 阅读目标 Release 说明,记录当前镜像或安装包版本。
  2. 停止全部旧版 Standalone、Control Plane 和 Data Plane 进程,取得一致性备份:SQLite 简单部署可在停止后复制文件;PostgreSQL 使用数据库自身的备份机制。
  3. 应用新版本对应数据库的迁移(SQLite 或 PostgreSQL 其中一套)。已初始化的 Server 正常启动时不会自动执行迁移,只有首次 Setup 会执行;源码部署可使用 Development Guide 中按数据库区分的命令。
  4. 更新 Server 与客户端,保留数据、Data Protection 密钥和目录挂载,再启动新版本。新版 Host 在接受请求和启动 Worker 之前,会检查并升级正在进行的执行记录;某条记录无法解密或验证失败时,Host 启动报错,需要修正数据后重新启动。
  5. 验证初始化状态、登录、Project 文件和一个小任务;分离部署还需验证 worker 与 Job。

AGW 在 1.0 前的升级可能包含 schema 变更。回滚时需要使用彼此兼容的程序版本、数据库备份和密钥备份。

恢复验证

优先在隔离环境验证备份能恢复,确认凭据可解密、文件路径可见。更改数据或日志根目录需要重启并自行迁移文件,不会自动搬运已有数据。

实现与参考

5 - 日志与常见问题

最近更新:

沿连接、认证、模型、文件与执行状态排查问题。

排查时先确定故障发生在哪一步:页面连接、登录、模型调用,还是文件和工具操作。用一个简单任务重现问题,通常比反复重启更容易找到原因。

记录出错的 Server、Project、会话和时间,再查看对应服务日志。分离部署的管理问题主要查控制面日志,任务执行问题主要查数据面日志。

排查顺序

现象优先检查
无法打开界面监听地址、端口、容器映射、代理目标
Setup 无法完成数据库连接、目录写权限、远程 Setup Code
登录或 API Key 失败实际 Server、API Key 撤销情况、数据库认证状态
第三方登录失败Auth:Oidc:PublicBaseUrl、提供商侧登记的回调地址、客户端凭据、Server 到提供商的网络
Agent 不回复Model Provider、模型 ID、凭据、待审批/待输入状态
CLI 无法启动执行节点的可执行文件、账号与环境
文件找不到当前目录选择、Server 路径、挂载和访问权限
Job 没运行启用状态、未来时间、UTC Cron、有效目标和日志
断线后状态不对会话选择、WebSocket 代理、执行是否仍在后台运行;InProcess 模式下 Server 重启后,原来运行中的会话会显示为 Interrupted,不会继续执行

示例:页面能打开,但 Agent 不回复

  1. 查看 Chat 是否正在等待审批或补充信息。如果是,先处理请求。
  2. 使用同一个模型连接运行一条纯文字问题。如果仍失败,检查 API 地址、模型 ID 和凭据。
  3. 纯文字正常而工具任务失败时,检查工具绑定、工作目录和执行主机的访问权限。
  4. 分离部署中,若配置页面正常而对话连接失败,检查 /api/hubs/exec 是否转发到数据面,以及代理是否支持 WebSocket。
  5. 根据出错时间查找服务日志中的具体错误,修改后重复同一个小任务验证。

这个顺序可以把模型、工具和连接问题分开,避免同时更改多项设置后无法判断原因。

第三方登录失败

第三方登录失败时,浏览器回到登录页,地址中带有 error=oidc-<类别>,Desktop 在 Server 配置处显示提示。用这个类别配合 Agw.Auth.Oidc 日志定位原因:日志记录提供商、客户端、失败所处的阶段、失败类别和 TraceId。

类别通常的原因
provider-unavailable、provider-timeoutServer 访问不到提供商:网络、出站代理或防火墙
provider-rejected能访问提供商,但 OAuth2 的用户信息接口返回错误状态码:检查 UserInfoEndpoint、Scopes 和令牌权限
protocol-rejectedOIDC 提供商返回协议错误:客户端编号、密钥或已登记的回调地址不符
invalid-state、invalid-nonce回调校验未通过:浏览器访问的地址与 PublicBaseUrl 不一致,或校验用的 Cookie 被拦截
invalid-token令牌校验未通过:Authority、签发者、接收方或验签地址配置不符
protocol-validation-failed其他未归类的协议失败,包括 OAuth2 换取令牌被拒绝:先检查客户端编号、密钥和回调地址
provisioning-failed、grant-creation-failed、session-creation-failedServer 本地处理失败:先检查数据库连接和迁移是否完成
authorization-denied用户在提供商页面取消了授权

排查时不要关闭签发者、接收方、签名、state、nonce 或 PKCE 校验。反向代理需要保留原始协议和主机名,否则回调会落在另一个来源上。报告问题时不要附带认证相关的查询参数和完整的提供商响应。

日志与遥测

AgwLogDir 默认 ./logs,不随 AgwDataDir 自动变化。分离部署要查看对应角色日志。需要集中遥测时配置 OpenTelemetry:OtlpEndpoint;空值或缺失时不启用 OpenTelemetry 的追踪、指标和日志导出。

历史采用 Interval 批量写入。Host 模板的 ConversationHistory:FlushIntervalSeconds 为 10 秒,省略时回退到 5 秒。即时输出与已落库历史存在时间差。

Web 开发代理

Web 开发运行在 3001,后端默认 30816。代理目标依次取 BACKEND_API_BASE_URL、NEXT_PUBLIC_API_BASE_URL、默认本机地址。静态 export 模式不使用 Next.js 代理,应由 Server 或外部入口提供同源路由。

修复后重复原来失败的小任务,确认界面状态和日志都恢复。报告问题时提供版本、部署方式、复现步骤和脱敏错误,不附带真实 API Key 或完整 OAuth 响应。

实现与参考