chore: 取消追踪 .astrion/memory 本地记忆文件
This commit is contained in:
parent
3ea8d9ad00
commit
b0fdd47ad9
@ -1,153 +0,0 @@
|
|||||||
---
|
|
||||||
name: agent_hooks_design
|
|
||||||
description: 当需要继续讨论或实现本项目的 hooks / 生命周期钩子功能时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hooks / 生命周期钩子功能设计讨论(进行中)
|
|
||||||
|
|
||||||
## 调研结论
|
|
||||||
|
|
||||||
### 主流实现
|
|
||||||
- **Claude Code**:最完整,20+ 事件、5 种 handler(command/http/mcp_tool/prompt/agent)、多层配置(全局/项目/本地/组织/插件/SDK)。
|
|
||||||
- **OpenAI Codex**:11 个事件、command 回调为主、有信任审查机制和企业托管 hooks。
|
|
||||||
- **Cursor**:`.cursor/hooks.json`,Beta,command 回调,项目/用户/企业三级。
|
|
||||||
- **GitHub Copilot**:`.github/hooks/*.json`,command/http/prompt 三种回调,Cloud Agent 也支持。
|
|
||||||
- **Windsurf/Cascade**:`.windsurf/hooks.json`,exit 2 阻断,12 个事件。
|
|
||||||
- **Aider / Continue.dev / Kimi Code / Devin**:无原生 hooks 或资料有限。
|
|
||||||
|
|
||||||
### 共性模式
|
|
||||||
```
|
|
||||||
Agent 事件发生
|
|
||||||
↓
|
|
||||||
stdin 发送 JSON 事件载荷 → Hook 脚本 / HTTP 端点
|
|
||||||
↓
|
|
||||||
返回决策(allow/deny/ask/modify/injectMessage)
|
|
||||||
↓
|
|
||||||
Agent 根据决策继续执行
|
|
||||||
```
|
|
||||||
|
|
||||||
## 用户(项目 owner)对 hooks 的理解
|
|
||||||
|
|
||||||
> 钩子 = 发生一件事件之前/中/后 → 自动触发另一个事件。用 JSON 格式写条件和行为,分为全局/项目级,放在数据存储目录下/工作区内。
|
|
||||||
|
|
||||||
用户认可的核心:**事件驱动 + JSON 配置 + 全局/项目两级 + before/after 拦截**。
|
|
||||||
|
|
||||||
## 已达成的设计共识
|
|
||||||
|
|
||||||
### 1. 配置位置
|
|
||||||
- 全局:`~/.astrion/astrion/config/hooks.json`
|
|
||||||
- 项目级:`<project>/.astrion/hooks.json`
|
|
||||||
- 未来可扩展:`.astrion/hooks.local.json`、企业策略级。
|
|
||||||
- 多层配置**合并执行**,不是覆盖(更安全)。
|
|
||||||
|
|
||||||
### 2. 优先事件(MVP 范围建议)
|
|
||||||
- `PreToolUse`:工具执行前拦截/修改
|
|
||||||
- `PostToolUse`:工具执行后触发后续动作
|
|
||||||
- `Stop`:模型/turn 结束时注入反馈
|
|
||||||
- `PreCompact`:上下文压缩前备份
|
|
||||||
|
|
||||||
### 3. 优先回调类型
|
|
||||||
- MVP 只做 `command`(shell/python/任意可执行文件)。
|
|
||||||
- 后续可扩展 `http`、`prompt`、`agent`、`mcp_tool`。
|
|
||||||
|
|
||||||
### 4. 通信协议(对齐 Claude Code/Codex)
|
|
||||||
- stdin 输入 JSON 事件载荷
|
|
||||||
- stdout 返回 JSON 决策
|
|
||||||
- exit code:0=通过,1=非阻断错误,2=阻断
|
|
||||||
|
|
||||||
### 5. JSON 结构草案
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"disabled": false,
|
|
||||||
"hooks": {
|
|
||||||
"PreToolUse": [
|
|
||||||
{
|
|
||||||
"name": "block-dangerous-rm",
|
|
||||||
"matcher": "run_command",
|
|
||||||
"if": "command contains 'rm -rf /'",
|
|
||||||
"enabled": true,
|
|
||||||
"max_repeats": null,
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "python3 ~/.astrion/astrion/hooks/block_dangerous.py",
|
|
||||||
"timeout": 5,
|
|
||||||
"statusMessage": "检查危险命令...",
|
|
||||||
"fail_policy": "continue",
|
|
||||||
"on_error": "log"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Stop": [
|
|
||||||
{
|
|
||||||
"name": "remind-check-completion",
|
|
||||||
"max_repeats": 3,
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "python3 ~/.astrion/astrion/hooks/remind_check.py",
|
|
||||||
"timeout": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 已讨论的具体例子
|
|
||||||
|
|
||||||
### 例子 1:拦截危险命令
|
|
||||||
- 事件:`PreToolUse`
|
|
||||||
- 条件:`matcher=run_command` + `if=command contains 'rm -rf /'`
|
|
||||||
- 行为:调用脚本,返回 `permissionDecision: deny`
|
|
||||||
|
|
||||||
### 例子 2:edit_file 单行全替换触发审核
|
|
||||||
- 事件:`PreToolUse`
|
|
||||||
- 条件:`matcher=edit_file` + `old_string` 只有一行 + `replace_all=true`
|
|
||||||
- 行为:返回 `permissionDecision: ask`
|
|
||||||
|
|
||||||
### 例子 3:Stop 时自动插入 user 消息,最多 3 次
|
|
||||||
- 事件:`Stop`
|
|
||||||
- 行为:返回 `injectMessage: {role: "user", content: "请仔细检查是否真的完成了所有工作"}`
|
|
||||||
- "最多 3 次"可用 `max_repeats` 或由脚本自己维护状态文件。
|
|
||||||
|
|
||||||
## 尚未决定的决策点
|
|
||||||
|
|
||||||
1. **`if` 条件表达式语法**:
|
|
||||||
- 选项 A:简单规则 DSL(如 `command contains '...'`)
|
|
||||||
- 选项 B:结构化 JSON(如 `{"field": "...", "op": "contains", "value": "..."}`)
|
|
||||||
- 选项 C:交给 hook 脚本自己判断,`if` 只保留工具名匹配
|
|
||||||
|
|
||||||
2. **"最多 N 次"由谁负责**:
|
|
||||||
- 框架负责 `max_repeats`(简单,但状态语义固定)
|
|
||||||
- 脚本自己维护状态文件(灵活,框架更轻量)
|
|
||||||
|
|
||||||
3. **注入消息语义**:
|
|
||||||
- `injectMessage.user`:会触发模型再回复一轮
|
|
||||||
- `addContext.system`:只是给下一轮加提示,不立刻触发回复
|
|
||||||
|
|
||||||
4. **hook 失败时的主流程策略**:
|
|
||||||
- 默认 `continue`(用户配置错误不瘫痪智能体)
|
|
||||||
- 或提供 `fail_policy: block` 让管理员强制策略
|
|
||||||
|
|
||||||
5. **工具名匹配**:
|
|
||||||
- 是否同时支持内部工具名(`run_command`/`edit_file`)和 Claude Code 风格别名(`Bash`/`Edit|Write`)
|
|
||||||
|
|
||||||
## 推荐后续路线
|
|
||||||
|
|
||||||
1. 先定 JSON schema 和 `if` 语法。
|
|
||||||
2. MVP 实现 `command` 回调 + `PreToolUse`/`PostToolUse`/`Stop`。
|
|
||||||
3. 新增 `modules/hook_manager.py`。
|
|
||||||
4. 在 `core/main_terminal_parts/tools_execution.py` 和 `core/web_terminal.py` 插入 hook 调用。
|
|
||||||
5. 确保 API task 模式也走 hooks。
|
|
||||||
6. 补文档 `docs/hooks.md`。
|
|
||||||
|
|
||||||
## 参考资料位置
|
|
||||||
|
|
||||||
- 调研报告:`research/claudecode_hooks/claudecode_hooks.md`
|
|
||||||
- 调研报告:`research/codex_hooks/codex_hooks.md`
|
|
||||||
- 调研报告:`research/other_agents_hooks/other_agents_hooks.md`
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
---
|
|
||||||
name: ai_avatar_demo
|
|
||||||
description: 当需要了解或继续开发/修改 agents 项目的 AI 形象头像 demo 时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
---
|
|
||||||
name: ai_avatar_demo
|
|
||||||
description: 当需要了解或继续开发/修改 agents 项目的 AI 形象头像 demo 时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# AI 形象头像 Demo
|
|
||||||
|
|
||||||
## 文件位置
|
|
||||||
`/Users/jojo/Desktop/agents/正在修复中/agents/demo/ai-avatar/index.html`
|
|
||||||
|
|
||||||
## 设计目标
|
|
||||||
为项目设计一个独特、动态的 AI 形象,替代之前从网上找的通用小机器人 SVG。
|
|
||||||
|
|
||||||
## 当前设计
|
|
||||||
|
|
||||||
### 视觉构成
|
|
||||||
- **外框**:flat-top 圆角正六边形(上下边水平,底部是一条横线),中心 `(100,100)`,外接圆半径 80,圆角半径 14。
|
|
||||||
- **内部符号**:由两条等粗白色线段组成:
|
|
||||||
- 空闲态:两条竖线作为眼睛,长度 22,间距 32,位于 `y ≈ 84`(面部上半区域略偏上)。
|
|
||||||
- 工作态:`>` 终端提示符(无下横线),尖端朝右,开角 60°,单条线长度 34。
|
|
||||||
|
|
||||||
### 状态与动画
|
|
||||||
- **空闲**:六边形停到最近的 60° 正位,显示眼睛,眼睛跟随鼠标移动。
|
|
||||||
- **工作**:六边形开始持续旋转,显示 `>` 符号。
|
|
||||||
- 状态切换时内部符号立即变形,不等待外框停稳;外框则从当前角度平滑过渡到目标角度或开始旋转。
|
|
||||||
- 内部符号在空闲/工作之间通过**同两条 `<line>`** 进行 morph:
|
|
||||||
- `transform`(位置 + 旋转)使用 CSS `transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1)`。
|
|
||||||
- `x2`(线段长度)使用同节奏(0.55s,相同 cubic-bezier)的 `requestAnimationFrame` 插值,避免长度跳变。
|
|
||||||
- 提供一个独立的"停止追踪 / 开始追踪"按钮:停止追踪时眼睛平滑回到默认居中位置。
|
|
||||||
|
|
||||||
### 技术实现
|
|
||||||
- **纯 SVG**:外框用 `<path>`,眼睛 / `>` 用 `<line>`,整体用 `<svg>` + `<g>`。
|
|
||||||
- **动画方式**:
|
|
||||||
- 六边形旋转:CSS `@keyframes spin` + CSS `transition`。
|
|
||||||
- 符号变形:CSS `transition` 改变 `transform`;JS `requestAnimationFrame` 插值改变 `x2`。
|
|
||||||
- 眼睛追踪:JS `requestAnimationFrame` 实时更新 SVG `<g>` 的 `transform`。
|
|
||||||
- **无 Canvas / WebGL / GIF / Lottie / 图片序列帧**。
|
|
||||||
- 整体缩放不会导致比例或长度失调(矢量 SVG)。
|
|
||||||
|
|
||||||
## 设计约束
|
|
||||||
|
|
||||||
### 不可改动的核心动画
|
|
||||||
`demo/ai-avatar/index.html` 中 **空闲(idle)** 与 **工作(work)** 两个核心状态的切换动画不可改动:
|
|
||||||
|
|
||||||
- 六边形外框:空闲时从当前角度滑到最近的 60° 正位后停住;工作时从当前角度开始持续旋转。
|
|
||||||
- 内部图标:空闲为眼睛,工作为 `>` 符号,二者通过同两条 `<line>` 元素进行 morphing 变形。
|
|
||||||
- 变形节奏统一为 0.55s cubic-bezier(0.22, 1, 0.36, 1):
|
|
||||||
- `transform` 由 CSS transition 处理。
|
|
||||||
- `x2` 长度由 JS `requestAnimationFrame` 插值处理(CSS transition 不直接支持 SVG `x2` 属性)。
|
|
||||||
|
|
||||||
### 设计原则
|
|
||||||
- 只允许 SVG 的**微动 / 线条 / 点**之间的**优雅细微动化**。
|
|
||||||
- 禁止花哨特效:脉冲波纹、光标闪烁、发光、强烈缩放、大幅度弹跳等。
|
|
||||||
- 新增工作状态时,应保持通用切换容器,避免为每个 SVG 单独适配位置。
|
|
||||||
- 内部图标线条应比外框稍细,保持视觉层次感。
|
|
||||||
|
|
||||||
## 关键调整历史
|
|
||||||
- 外框从 pointy-top 改为 flat-top(横线在底部)。
|
|
||||||
- 边角从锐利直线改为 Q 贝塞尔圆角。
|
|
||||||
- 状态含义反转:空闲 = 静止 + 眼睛 + 追踪;工作 = 旋转 + `>`。
|
|
||||||
- 眼睛长度缩短,位置上移至 `y=84`。
|
|
||||||
- 鼠标追踪范围水平/垂直均为 ±10px。
|
|
||||||
- 2026-07-01:为 `x2` 长度变化增加与 `transform` 同节奏的 JS 插值动画,解决工作/休息切换时长度跳变问题。
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
name: app_initialization_flow
|
|
||||||
description: 当需要适配桌面 App 初始化流程或理解项目首次启动配置时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
## 项目初始化流程(首次启动 / 桌面 App 适配必读)
|
|
||||||
|
|
||||||
### 关键文件
|
|
||||||
- `_bootstrap.sh` — 共享引导(定位项目根、准备 Python venv + Node 依赖)
|
|
||||||
- `setup.sh` — 首次初始化入口:调 `_bootstrap.sh` 后运行 `python -m scripts.setup`
|
|
||||||
- `scripts/setup.py` — **交互式配置向导**(终端),5 步生成 `.env` + `custom_models.json`
|
|
||||||
|
|
||||||
### setup.py 向导步骤
|
|
||||||
1. **运行模式** — `host`(单机本地沙箱)/ `web`(多用户 Docker),决定 `TERMINAL_SANDBOX_MODE`
|
|
||||||
2. **Web 监听** — 端口 (`WEB_SERVER_PORT`) + 地址 (`WEB_SERVER_HOST`),host 模式默认 127.0.0.1
|
|
||||||
3. **数据目录** — `~/.astrion/astrion/<mode>/`,可自定义(设 `ASTRION_DATA_ROOT`)
|
|
||||||
4. **管理员账户** — `AGENT_ADMIN_USERNAME` + `AGENT_ADMIN_PASSWORD_HASH`(werkzeug hash)
|
|
||||||
5. **模型配置** — 写入部署级 `custom_models.json`(~/.astrion/astrion/config/):model_name / url / apikey / model_id / thinking 支持
|
|
||||||
6. **自动生成** — `WEB_SECRET_KEY` + `API_TOKEN_SECRET`(secrets.token_hex(32))
|
|
||||||
|
|
||||||
### 设计要点
|
|
||||||
- 向导**刻意不 import config**,以免在 `.env` 写之前过早锁定路径
|
|
||||||
- 以 `.env.example` 为模板,仅替换被接管键,其余原样保留(含注释)
|
|
||||||
- `.env` 写完后 chmod 600
|
|
||||||
|
|
||||||
### 桌面 App 适配要点
|
|
||||||
- 桌面 App 应固定 `TERMINAL_SANDBOX_MODE=host`
|
|
||||||
- 端口应自动找可用端口
|
|
||||||
- 模型配置可在 Web UI 中后续修改,但首次启动至少需要一个模型
|
|
||||||
- `_deploy_config_dir()` 推导逻辑必须与 `config/paths.py` 保持一致
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
name: astrion_naming
|
|
||||||
description: 当需要了解或引用本项目的品牌名称、产品名 Astrion 的含义与来源时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 项目名称 / 产品名:Astrion
|
|
||||||
|
|
||||||
## 决策记录
|
|
||||||
- **暂定名称**:Astrion
|
|
||||||
- **决策时间**:2026-07-07
|
|
||||||
- **命名方向**:人格化 + 宇宙现象/天体 + 科技意象
|
|
||||||
- **当前状态**:暂定,后续如需变更请同步更新本记忆、AGENTS.md 与 claude.md
|
|
||||||
|
|
||||||
## 名称含义
|
|
||||||
|
|
||||||
**构词**
|
|
||||||
- **Astr-**:来自希腊语 *astron*,意为“星星 / 天体 / 宇宙”
|
|
||||||
- **-ion**:科技命名中常见的粒子/状态后缀(如 photon、electron、neutron、ion)
|
|
||||||
|
|
||||||
**字面意象**
|
|
||||||
> 星之子、星际粒子、来自宇宙尘埃的微小发光体。
|
|
||||||
|
|
||||||
## 表达特点
|
|
||||||
|
|
||||||
| 特点 | 解读 |
|
|
||||||
|------|------|
|
|
||||||
| 科技感 | `-ion` 后缀自带物理/科技联想 |
|
|
||||||
| 宇宙感 | `Astr-` 词根直接指向星空 |
|
|
||||||
| 轻盈感 | 粒子暗示微小、快速、自由穿行 |
|
|
||||||
| 能量感 | ion(离子)带电,暗示活跃、驱动、连接 |
|
|
||||||
| 可人格化 | 三音节 `/ˈæstriən/`,发音接近 Orion、Damion,适合作为“他/她” |
|
|
||||||
| 中性 | 不偏男性或女性,适用面广 |
|
|
||||||
|
|
||||||
## 品牌叙事参考
|
|
||||||
|
|
||||||
1. **连接者**:像星际间传递信号的粒子,把人类意图传递到工具、文件、终端、子智能体之间。
|
|
||||||
2. **从混沌中组织秩序**:宇宙尘埃在引力下聚集成恒星;Astrion 把零散任务聚合成可执行结果。
|
|
||||||
3. **渺小但明亮**:单个粒子微不足道,但携带能量、能引发连锁反应——对应 Agent 虽小,却能撬动复杂系统。
|
|
||||||
|
|
||||||
## 中文参考
|
|
||||||
|
|
||||||
- 全称音译:**阿斯特里恩**
|
|
||||||
- 简称候选:**星子**(对应 astron + particle,更顺口)
|
|
||||||
|
|
||||||
## 同步位置
|
|
||||||
|
|
||||||
- 项目规范:`AGENTS.md`
|
|
||||||
- Claude Code 指南:`claude.md`
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
---
|
|
||||||
name: background_notification_pool
|
|
||||||
description: 当需要讨论或修复后台子智能体/后台 run_command 完成通知机制时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 后台子智能体 / 后台 run_command 通知机制问题(待修复)
|
|
||||||
|
|
||||||
## 问题描述
|
|
||||||
|
|
||||||
后台子智能体和后台 `run_command` 在“主智能体停止输出后完成”的场景下,通知机制存在串行化问题:
|
|
||||||
|
|
||||||
- 若同时有多个后台任务在运行,它们不会随完成逐个通知。
|
|
||||||
- 而是在所有后台任务全部完成后,按时间顺序一条一条地发送 user 消息。
|
|
||||||
- 且每发送一条 user 消息就要触发一次“工作 → 停止 → 再工作”的循环,导致通知被拆成多次停止输出。
|
|
||||||
|
|
||||||
## 当前机制
|
|
||||||
|
|
||||||
运行期间(inline)和停止输出后(polling)是两条链路:
|
|
||||||
|
|
||||||
### 1. 运行期间(inline,当前正常)
|
|
||||||
|
|
||||||
- 位置:`server/chat_flow_tool_loop.py` 中 `execute_tool_calls` 末尾。
|
|
||||||
- 调用:
|
|
||||||
- `process_sub_agent_updates(..., inline=True)`
|
|
||||||
- `process_background_command_updates(..., inline=True)`
|
|
||||||
- 行为:每次工具循环结束时,把**当前所有已完成且未通知**的任务一次性插入 `messages`,不触发新任务。
|
|
||||||
- 结论:运行期间的批量通知已经是“池化”的,暂无问题。
|
|
||||||
|
|
||||||
### 2. 停止输出后(polling,问题所在)
|
|
||||||
|
|
||||||
- 位置:`server/chat_flow_task_main.py` 中 `handle_task_with_sender` 结尾。
|
|
||||||
- 调用:
|
|
||||||
- `poll_sub_agent_completion(...)`
|
|
||||||
- `poll_background_command_completion(...)`
|
|
||||||
- 行为:
|
|
||||||
- 两个轮询器各自由 `socketio.start_background_task(...)` 启动。
|
|
||||||
- 每轮循环只处理**一个**完成的通知。
|
|
||||||
- 处理完一个通知后,创建一个新的 chat task,然后 `return`。
|
|
||||||
- 下一个通知必须等待这个新 chat task 跑完,并再次走到 `handle_task_with_sender` 结尾重新 spawn 轮询器时才会发出。
|
|
||||||
|
|
||||||
## 根因
|
|
||||||
|
|
||||||
后台任务管理器本身已经扮演了“后台池 + 通知池”的角色(任务完成但 `notified=False` 就是待通知状态)。
|
|
||||||
|
|
||||||
但轮询器是 **one-shot** 的:每次只消费一条通知就退出,导致通知被串行化到多次“工作 → 停止 → 再工作”循环中。
|
|
||||||
|
|
||||||
## 讨论中的设计方案
|
|
||||||
|
|
||||||
统一为“通知池轮询器”:
|
|
||||||
|
|
||||||
1. 合并 `poll_sub_agent_completion` 和 `poll_background_command_completion` 为一个统一的 `poll_completion_notifications`。
|
|
||||||
2. 每次轮询时,同时从以下两处取出**所有**待通知项:
|
|
||||||
- `sub_agent_manager.poll_updates()`
|
|
||||||
- `background_command_manager.poll_updates(conversation_id=...)`
|
|
||||||
3. 把所有待通知项作为 user 消息插入对话历史(倾向保留为多条消息,而非合并成一条)。
|
|
||||||
4. 只创建 **一个**后续 chat task 来触发继续工作。
|
|
||||||
5. 轮询器返回,由这个新任务在结束时继续下一轮轮询。
|
|
||||||
|
|
||||||
### 期望行为示例
|
|
||||||
|
|
||||||
- 3 个后台子智能体运行中,主智能体停止输出。
|
|
||||||
- 第 1 个完成 → 通知池有 1 条 → 插入 1 条 user 消息 → 触发 1 个新任务。
|
|
||||||
- 新任务处理期间,第 2、3 个完成 → 进入通知池。
|
|
||||||
- 新任务停止输出 → 轮询器发现通知池还有 2 条 → 一次性插入 2 条 user 消息 → 再触发 1 个新任务。
|
|
||||||
- 不再有后台任务时,轮询器直接退出。
|
|
||||||
|
|
||||||
## 涉及文件
|
|
||||||
|
|
||||||
| 文件 | 角色 |
|
|
||||||
|---|---|
|
|
||||||
| `server/chat_flow_task_support.py` | 新增统一轮询器 `poll_completion_notifications`;可废弃旧的两个轮询函数。 |
|
|
||||||
| `server/chat_flow_task_main.py` | 在 `handle_task_with_sender` 结尾,把分别 spawn 两个轮询器的逻辑替换为 spawn 一个统一轮询器。 |
|
|
||||||
| `modules/sub_agent/manager.py` | 提供 `poll_updates()`,基于 `notified` 标志表示待通知状态。 |
|
|
||||||
| `modules/background_command_manager.py` | 提供 `poll_updates()` 和 `has_pending_for_conversation()`。 |
|
|
||||||
|
|
||||||
## 待决策点
|
|
||||||
|
|
||||||
- 是否坚持把通知拆成多条 user 消息插入历史(推荐),还是合并为一条。
|
|
||||||
- 统一轮询器是否做成“持久单例”还是保持 one-shot batch(当前讨论倾向 one-shot batch,由新任务继续轮询)。
|
|
||||||
|
|
||||||
## 状态
|
|
||||||
|
|
||||||
已实施(2026-06-24,方案 A + one-shot batch)。
|
|
||||||
|
|
||||||
### 落地实现
|
|
||||||
|
|
||||||
- `server/chat_flow_task_support.py` 未改;新增逻辑集中在 `server/chat_flow_task_main.py`。
|
|
||||||
- 新增统一轮询器 `poll_completion_notifications`,替换并删除旧的 `poll_sub_agent_completion` / `poll_background_command_completion`。
|
|
||||||
- 新增 `_collect_pending_completion_notices`:每轮一次性从子智能体 + 后台命令两路取出所有待通知项(取出即就地标记 `notified` / `_announced_sub_agent_tasks`),按 `updated_at` 跨两路排序。
|
|
||||||
- 新增 `_has_pending_completion_work`:判断是否还有运行中/待通知的后台工作,决定轮询是否继续。
|
|
||||||
- 新增 `_build_sub_agent_notice_text`:抽出子智能体通知文本构建。
|
|
||||||
- `handle_task_with_sender` 结尾:保留 `sub_agent_waiting` 事件探测,但把「分别 spawn 两个轮询器」改为只 spawn **一个** `run_completion_poll`。**这也顺带消除了两个轮询器同时 `create_chat_task` 撞 `create_chat_task` 单工作区互斥(`models.py` RuntimeError)的隐患。**
|
|
||||||
|
|
||||||
### 方案 A 的「多条 + 单 task」如何落地
|
|
||||||
|
|
||||||
`create_chat_task` 只收单条 message,`handle_task_with_sender` 开头无条件 `add_conversation`。因此:
|
|
||||||
|
|
||||||
- 一批取出 N 条:前 N-1 条为「前置通知」,最后 1 条作为后续 chat task 的触发消息。
|
|
||||||
- `_dispatch_completion_user_notice` 新增 `preceding_notices` 参数:
|
|
||||||
- 通过 `_persist_and_echo_preceding_notice` 把前 N-1 条**写入对话历史**(`add_conversation`,落盘)+ **socketio 回显**(在线客户端即时可见),`starts_work=False`(渲染为完成态气泡,不起独立计时器)。
|
|
||||||
- 把 `preceding_user_notices` 塞进 `session_data`,传给后续任务。
|
|
||||||
- `server/tasks/models.py` 的 `_run_chat_task`:在补发触发 user_message 事件**之前**,先按顺序把 `preceding_user_notices` 逐条 `_append_event("user_message", ...)` 注入任务事件流——保证 REST 轮询客户端按时间顺序看到所有完成通知。
|
|
||||||
- 触发消息(最后 1 条)走原有 `auto_user_message_event` 链路,正常 `starts_work=True` 启动一轮工作。
|
|
||||||
|
|
||||||
### 验证
|
|
||||||
|
|
||||||
- `python3 -m py_compile server/chat_flow_task_main.py server/tasks/models.py` 通过(注意本机 pycache 目录无写权限,需 `PYTHONPYCACHEPREFIX=$TMPDIR/pycache`)。
|
|
||||||
- `python3 -m unittest test.test_server_refactor_smoke` 6 用例通过。
|
|
||||||
- 前端未改:复用既有 `user_message` / `sub_agent_notice` 渲染链路(`messaging.ts` handleUserMessage + socketio `user_message`),dedup 与单通知场景一致。
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
name: chat_block_scroll_anchor
|
|
||||||
description: 当需要优化聊天区域块展开/折叠滚动行为,或排查向上滚动后瞬间跳回问题时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 聊天区域块展开/折叠滚动行为优化记录
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
聊天区域默认使用 `vue-stick-to-bottom` 保持底部锁定。块(thinking/tool)展开/折叠时,`stick-to-bottom` 的弹簧追底动画和 CSS 高度过渡不同步,会产生「向下展开一点、向上滚动一点」的顿挫感。
|
|
||||||
|
|
||||||
## 已尝试方案
|
|
||||||
|
|
||||||
### 1. 块展开锚定(useBlockExpansionAnchor)
|
|
||||||
- 在 `static/src/composables/useBlockExpansionAnchor.ts` 中实现
|
|
||||||
- 块展开/折叠的 CSS 过渡期间(约 260–300ms),用 `requestAnimationFrame` 持续覆盖 `scrollTop`
|
|
||||||
- 目标:让被操作块的底边或顶边保持在原来视口位置
|
|
||||||
- 方向判断:
|
|
||||||
- 用**底边**判断比顶边更准确。高块即使顶边位于上半,底边仍可能在下半;用顶边判断会把高块误判为 `down` 模式
|
|
||||||
- `up` 模式:底边不动,头部向上展开
|
|
||||||
- `down` 模式:顶边不动,内容向下展开
|
|
||||||
- 方向在单次展开-收起周期内记忆(`preferredModes`),保证收起是展开的倒放;收起动画结束后清除记录
|
|
||||||
|
|
||||||
### 2. 运行期间策略
|
|
||||||
- 运行中的 thinking/tool 块(带 `.processing`/`.running`/`.thinking-animation`)保持旧逻辑,由 `stick-to-bottom` 追底,避免被输入栏遮挡
|
|
||||||
- 非运行中的块(包括运行期间的历史块)走锚定逻辑
|
|
||||||
- 已实现 commit:`f43b5ea7 fix(chat): 运行期间恢复旧滚动逻辑,堆叠块更多按钮也接入锚定`
|
|
||||||
|
|
||||||
### 3. 堆叠块「更多」按钮
|
|
||||||
- `StackedBlocks.vue` 在 toggleMore 时 emit `more-toggle`,`ChatArea.vue` 监听后对 more-block 元素启动锚定
|
|
||||||
- 注意:事件 payload 里的展开状态应使用 `payload.expanded`,避免 `ReferenceError`
|
|
||||||
|
|
||||||
## 未解决的向上滚动跳回问题
|
|
||||||
|
|
||||||
### 现象
|
|
||||||
- 在块较多的对话中,**向上滚动**后页面会瞬间跳回滚动前的位置
|
|
||||||
- 向下滚动不会出现
|
|
||||||
- 禁用 `anchorBlockElement` 调用、禁用 wheel-up 时的 `stopScroll()` 后问题依然存在
|
|
||||||
|
|
||||||
### 调试结论
|
|
||||||
- 不是 `anchorBlockElement` 直接导致
|
|
||||||
- 不是 `stopScroll()` 直接导致
|
|
||||||
- `heightDelta` 可能为负(内容高度减小),但 large-growth 检测未触发
|
|
||||||
- `escapedFromLock` 已为 `true`,`stick-state-change` 未出现异常变化
|
|
||||||
- 原因未最终定位,可能与 `vue-stick-to-bottom` 内部状态、浏览器原生滚动行为或内容高度变化后的布局抖动有关
|
|
||||||
|
|
||||||
## 当前代码状态
|
|
||||||
- 当前 HEAD:`f43b5ea7 fix(chat): 运行期间恢复旧滚动逻辑,堆叠块更多按钮也接入锚定`
|
|
||||||
- 工作区干净,所有调试和临时修改已撤回,stash 已清空
|
|
||||||
- 后续若继续优化,建议从 `f43b5ea7` 开始,并优先定位向上滚动跳回的根本原因
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
---
|
|
||||||
name: cloud_server_disk_usage
|
|
||||||
description: 当需要了解云服务器磁盘使用现状、规划Docker镜像构建空间、或决定清理哪些文件释放空间时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 云服务器磁盘使用现状
|
|
||||||
|
|
||||||
> 记录时间:2026-06-25
|
|
||||||
> 服务器:59.110.19.30(Ubuntu 24.04.2 LTS, x86_64)
|
|
||||||
|
|
||||||
## 总体情况
|
|
||||||
|
|
||||||
```text
|
|
||||||
Filesystem Size Used Avail Use%
|
|
||||||
/dev/vda3 49G 43G 4.0G 92%
|
|
||||||
```
|
|
||||||
|
|
||||||
- 总磁盘:约 49 GB
|
|
||||||
- 已用:约 43 GB(92%)
|
|
||||||
- 可用:约 4 GB
|
|
||||||
|
|
||||||
**结论:磁盘非常紧张,不足以直接本地构建方案 B 的 2.9GB Docker 镜像。**
|
|
||||||
|
|
||||||
## 占用大户(按大小排序)
|
|
||||||
|
|
||||||
| 路径 | 大小 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| `/var/lib/docker` | **18 GB** | Docker 镜像、容器、volume |
|
|
||||||
| `/var/log/journal` | **4.1 GB** | systemd journal 日志 |
|
|
||||||
| `/opt/agent` | **5.0 GB** | agents 项目源码、运行时、备份、镜像 |
|
|
||||||
| `/opt/openwebui` | **3.6 GB** | OpenWebUI 部署目录 |
|
|
||||||
| `/var/lib/filebrowser` | **3.0 GB** | filebrowser 数据 |
|
|
||||||
| `/swapfile` | **2.1 GB** | swap 文件 |
|
|
||||||
| `/home/git/data` | **856 MB** | gitea 数据 |
|
|
||||||
| `/home/admin/go` | **770 MB** | Go 相关文件/缓存 |
|
|
||||||
| `/var/www` | **1.4 GB** | nginx web 目录 |
|
|
||||||
| `/var/lib/clamav` | **1.2 GB** | 病毒库 |
|
|
||||||
| `/usr` | **4.2 GB** | 系统软件 |
|
|
||||||
|
|
||||||
## Docker 详细占用
|
|
||||||
|
|
||||||
```text
|
|
||||||
Images: 4 5.284 GB
|
|
||||||
- ghcr.io/open-webui/open-webui <none> 4.32 GB
|
|
||||||
- my-agent-shell amd64 913 MB
|
|
||||||
- my-agent-shell latest 913 MB(与 amd64 同 image id)
|
|
||||||
- python 3.11-slim 124 MB
|
|
||||||
- ghcr.io/corentinth/it-tools latest 56.2 MB
|
|
||||||
|
|
||||||
Volumes: 1 7.966 GB
|
|
||||||
- open-webui: 7.966 GB
|
|
||||||
|
|
||||||
Containers: 3
|
|
||||||
- agent-term-jojo-default my-agent-shell:latest Up
|
|
||||||
- openwebui-new open-webui image Up (healthy)
|
|
||||||
- it-tools it-tools image Exited (7 months ago)
|
|
||||||
|
|
||||||
Build Cache: 10 / 2.182 kB(可忽略)
|
|
||||||
```
|
|
||||||
|
|
||||||
## `/opt/agent` 详细占用
|
|
||||||
|
|
||||||
| 路径 | 大小 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| `/opt/agent/backup` | **1.5 GB** | 6 个压缩备份包(runtime_backup_* ×3, users_data_backup_* ×3) |
|
|
||||||
| `/opt/agent/dockerimage` | **894 MB** | `my-agent-shell_amd64.tar` 旧镜像 tar |
|
|
||||||
| `/opt/agent/agents` | **883 MB** | 当前源码目录 |
|
|
||||||
| `/opt/agent/agents_old_20260614` | **540 MB** | 旧版本源码备份 |
|
|
||||||
| `/opt/agent/runtime` | **520 MB** | 运行时数据(对话、用户工作区等) |
|
|
||||||
| `/opt/agent/voice_models` | **230 MB** | 语音模型文件 |
|
|
||||||
| `/opt/agent/agents_old_20260612_012026` | **190 MB** | 旧版本源码备份 |
|
|
||||||
| `/opt/agent/agents_old_20260614_190527` | **190 MB** | 旧版本源码备份 |
|
|
||||||
| `/opt/agent/venv` | **96 MB** | Python 虚拟环境 |
|
|
||||||
| `/opt/agent/agents.zip` | **42 MB** | 源码 zip 包 |
|
|
||||||
|
|
||||||
## 建议清理清单
|
|
||||||
|
|
||||||
### 低风险(约可释放 6.5 GB)
|
|
||||||
|
|
||||||
| 操作 | 可释放 | 命令/路径 |
|
|
||||||
|------|--------|-----------|
|
|
||||||
| 清理 systemd journal 旧日志 | ~4 GB | `journalctl --vacuum-time=7d` 或 `--vacuum-size=500M` |
|
|
||||||
| 删除 `/opt/agent/backup` 旧备份,只留最新各 1 个 | ~1 GB | 手动删除旧的 `runtime_backup_*` 和 `users_data_backup_*` |
|
|
||||||
| 删除 `/opt/agent/dockerimage/my-agent-shell_amd64.tar` | 894 MB | `rm /opt/agent/dockerimage/my-agent-shell_amd64.tar` |
|
|
||||||
| 删除 `/opt/agent/agents_old_*` 旧版本目录 | ~920 MB | `rm -rf /opt/agent/agents_old_*` |
|
|
||||||
| 删除 `/opt/agent/agents.zip` | 42 MB | `rm /opt/agent/agents.zip` |
|
|
||||||
| 删除已退出容器 `it-tools` | ~514 kB | `docker rm it-tools` |
|
|
||||||
|
|
||||||
### 中风险(需先确认是否仍在使用)
|
|
||||||
|
|
||||||
| 操作 | 可释放 | 说明 |
|
|
||||||
|------|--------|------|
|
|
||||||
| 清理 `/var/lib/filebrowser` | 3 GB | 需确认 filebrowser 中是否还有需要保留的文件 |
|
|
||||||
| 清理 `/home/admin/go` 缓存 | ~770 MB | `go clean -cache` |
|
|
||||||
| 删除 `/var/lib/clamav` | 1.2 GB | 如果系统不使用 clamav 扫描 |
|
|
||||||
|
|
||||||
### 不建议动
|
|
||||||
|
|
||||||
- `/opt/agent/runtime`:用户运行时数据(对话、项目文件等)
|
|
||||||
- `/opt/agent/agents`:当前项目源码
|
|
||||||
- `/opt/openwebui`:如果 OpenWebUI 服务仍在使用
|
|
||||||
- `/home/git/data`:gitea 数据
|
|
||||||
- Docker volume `open-webui`:OpenWebUI 用户数据
|
|
||||||
|
|
||||||
## 关键命令(下次快速查看)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 总体磁盘
|
|
||||||
ssh 59.110.19.30
|
|
||||||
df -h
|
|
||||||
|
|
||||||
# 根目录一级占用
|
|
||||||
sudo du -sh /* 2>/dev/null | sort -h | tail -20
|
|
||||||
|
|
||||||
# /var 明细
|
|
||||||
sudo du -sh /var/lib/* /var/log/* 2>/dev/null | sort -h | tail -20
|
|
||||||
|
|
||||||
# /opt/agent 明细
|
|
||||||
sudo du -sh /opt/agent/* 2>/dev/null | sort -h | tail -20
|
|
||||||
|
|
||||||
# Docker 占用
|
|
||||||
docker system df
|
|
||||||
docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'
|
|
||||||
docker volume ls
|
|
||||||
|
|
||||||
# 清理 journal
|
|
||||||
sudo journalctl --vacuum-time=7d
|
|
||||||
|
|
||||||
# 清理 Docker
|
|
||||||
# docker image prune -a # 谨慎:会删除所有未使用镜像
|
|
||||||
# docker volume prune # 谨慎:会删除未使用 volume
|
|
||||||
```
|
|
||||||
|
|
||||||
## 与本次 Docker 改造的关系
|
|
||||||
|
|
||||||
- 当前 `my-agent-shell` 镜像:913 MB
|
|
||||||
- 方案 B 目标镜像:约 2.9 GB(本地 arm64 实测)
|
|
||||||
- 构建过程需下载 586 MB apt 包,解压后约 2.2 GB
|
|
||||||
- 当前可用空间仅 4 GB,**不足以安全构建方案 B**
|
|
||||||
- 建议先执行低风险清理释放 6-8 GB,再考虑构建
|
|
||||||
- 云端构建建议分阶段:先只加 LibreOffice + 字体 + Node + Python 必要包(约 1.5 GB 镜像),验证稳定后再决定是否加 chromium/ffmpeg
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 服务器架构为 x86_64,本地为 arm64,**禁止将本地构建的镜像上传到云端**
|
|
||||||
- 云端网络环境较差,大文件下载可能超时
|
|
||||||
- 云端内存 2 GB,构建/运行大镜像需警惕 OOM
|
|
||||||
- 清理前务必确认文件是否仍在使用,尤其是 `/var/lib/filebrowser` 和 `/opt/openwebui`
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
---
|
|
||||||
name: cloud_server_ssh_and_directories
|
|
||||||
description: 当需要SSH登录云服务器排查部署问题、查看日志、或操作服务器文件时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 云服务器 SSH 与目录指南
|
|
||||||
|
|
||||||
## SSH 登录
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh 59.110.19.30
|
|
||||||
# 使用与本地相同的 id_ed25519 密钥
|
|
||||||
```
|
|
||||||
|
|
||||||
## 关键目录
|
|
||||||
|
|
||||||
| 用途 | 路径 |
|
|
||||||
|------|------|
|
|
||||||
| **源码根目录** | `/opt/agent/agents/` |
|
|
||||||
| **运行时数据根** | `/opt/agent/runtime/`(`ASTRION_DATA_ROOT`) |
|
|
||||||
| **部署级配置** | `/opt/agent/runtime/config/` |
|
|
||||||
| **子智能体模型配置** | `/opt/agent/runtime/config/sub_agent_models.json` |
|
|
||||||
| **子智能体代码** | `/opt/agent/agents/modules/sub_agent/`(Python 主进程内协程) |
|
|
||||||
| **虚拟环境** | `/opt/agent/venv/` |
|
|
||||||
|
|
||||||
## 用户数据
|
|
||||||
|
|
||||||
```
|
|
||||||
/opt/agent/runtime/web/users/<用户名>/projects/<项目名>/
|
|
||||||
├── project/ # 用户工作区文件
|
|
||||||
└── data/
|
|
||||||
└── conversations/
|
|
||||||
└── default/
|
|
||||||
└── conv_xxx.json # 对话记录
|
|
||||||
```
|
|
||||||
|
|
||||||
## 子智能体相关
|
|
||||||
|
|
||||||
| 用途 | 路径 |
|
|
||||||
|------|------|
|
|
||||||
| **子智能体任务目录** | `/opt/agent/runtime/web/data/sub_agent_tasks/<task_id>/` |
|
|
||||||
| **任务文件** | `task.txt`、`system_prompt.txt` |
|
|
||||||
| **子智能体交付目录** | 在用户 project 下的 `sub_agent_results/` |
|
|
||||||
|
|
||||||
## 服务管理
|
|
||||||
|
|
||||||
| 操作 | 命令/路径 |
|
|
||||||
|------|----------|
|
|
||||||
| **启动服务** | `bash /opt/agent/start_webapp_new.sh`(端口 8091) |
|
|
||||||
| **更新服务** | `bash /opt/agent/refresh_agents.sh`(git pull + 重启 + nginx reload) |
|
|
||||||
| **数据迁移脚本** | `python3 /opt/agent/agents/scripts/migrate_runtime_data.py`(执行前务必 `--dry-run`) |
|
|
||||||
| **Nginx 配置** | `/etc/nginx/sites-enabled/agent.cyjai.com` |
|
|
||||||
| **本地部署脚本** | `deploy_agents.sh`(git push → npm build → scp dist/ → 触发远程 refresh) |
|
|
||||||
|
|
||||||
## 日志
|
|
||||||
|
|
||||||
| 用途 | 路径 |
|
|
||||||
|------|------|
|
|
||||||
| **Web 日志** | `/opt/agent/runtime/web/logs/` |
|
|
||||||
| **主机调试日志** | `/opt/agent/runtime/web/logs/host_workspace_debug.log` |
|
|
||||||
|
|
||||||
## 常见排查流程
|
|
||||||
|
|
||||||
1. SSH 登录服务器
|
|
||||||
2. 查看对话 JSON:`cat /opt/agent/runtime/web/users/<user>/projects/<project>/data/conversations/default/conv_xxx.json`
|
|
||||||
3. 查看子智能体任务:`ls /opt/agent/runtime/web/data/sub_agent_tasks/`
|
|
||||||
4. 查看日志:`tail -100 /opt/agent/runtime/web/logs/host_workspace_debug.log`
|
|
||||||
5. 查看子智能体任务文件:`ls /opt/agent/runtime/web/data/sub_agent_tasks/<task_id>/`
|
|
||||||
|
|
||||||
## 已知坑
|
|
||||||
|
|
||||||
- **前端构建不能服务器执行**:2G 内存会 OOM,必须本地 build 后 scp 上传 `static/dist/`
|
|
||||||
- **venv 符号链接问题**:从 macOS 复制来的 venv,启动脚本 `fix_venv()` 会自动检测重建
|
|
||||||
- **子智能体现为 Python 协程**:`easyagent/` 目录已废弃,不再作为子智能体入口
|
|
||||||
- **数据迁移后路径变更**:运行态数据根的环境变量由 `AGENTS_DATA_ROOT` 改为 `ASTRION_DATA_ROOT`,工作区内部目录由 `.agents/` 改为 `.astrion/`
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
name: config_system_and_deployment
|
|
||||||
description: 当修改配置加载逻辑、服务器部署流程、skill 归档路径或理解 settings.json 结构时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
## Skill 归档路径约定(2026-07-09 更新)
|
|
||||||
|
|
||||||
- **内置 skill 库**:`AGENT_SKILLS_DIR`(源码树 `agentskills/`),只放通用、自带、适用于项目的 skill。
|
|
||||||
- **用户自定义 skill 归档目录**:`CUSTOM_SKILLS_DIR = <RUNTIME_ROOT>/<mode>/agentskills`
|
|
||||||
- host 模式:`~/.astrion/astrion/host/agentskills/`(全局,不按用户拆分)
|
|
||||||
- web/docker 模式:`~/.astrion/astrion/web/agentskills/`(可与 per-user `personal/agentskills` 并存)
|
|
||||||
- **create_skill 行为**:统一通过 `infer_private_skills_dir(data_dir)` 解析目标目录,host 模式不再特殊处理为源码树 `agentskills/`。
|
|
||||||
- **实验/测试 skill**:如 `test` 应从源码树 `agentskills/` 移出,归档到 `CUSTOM_SKILLS_DIR`,不作为内置 skill 发布。
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
name: conversation_model_persistence
|
|
||||||
description: 当加载/恢复对话时发现模型未恢复、新建对话后旧对话模型被覆盖、或修改 /api/model、_apply_workspace_personalization_preferences 时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
---
|
|
||||||
name: conversation_model_persistence
|
|
||||||
description: 当加载/恢复对话时发现模型未恢复、新建对话后旧对话模型被覆盖、或修改 /api/model、_apply_workspace_personalization_preferences 时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 对话模型持久化约定(对话级隔离架构版)
|
|
||||||
|
|
||||||
## 核心规则
|
|
||||||
|
|
||||||
- 每个对话的 `model_key` 保存在对话文件的 `metadata.model_key`。
|
|
||||||
- **对话级 terminal(`_bound_conversation_id` 非空)的模型由绑定加载权威恢复**(`WebTerminal._ensure_conversation` 用 `restore_model=True`),其它任何逻辑不得覆盖它。
|
|
||||||
- **工作区级 terminal(未绑定)刻意 `restore_model=False`** 自动加载最近对话:避免 /new 页面显示旧对话模型。
|
|
||||||
|
|
||||||
## 四条关键防线(2026-07-20 修复,commit 1289c31d)
|
|
||||||
|
|
||||||
1. **`core/web_terminal.py::_ensure_conversation`**:绑定路径 `load_conversation(bound_id, restore_model=True)`。工作区自动加载路径保持 `restore_model=False`。
|
|
||||||
2. **`server/chat/settings.py::/api/model`**:仅当请求显式携带 `conversation_id` 且与 terminal `current_conversation_id` 一致时才 `save_conversation`;无 cid(/new 页面)不持久化——工作区级 terminal 的 current 可能是陈旧对话,盲存会把模型写错对话或用陈旧 history 覆盖新消息。
|
|
||||||
3. **前端 `static/src/app/methods/ui/model.ts::handleModelSelect`**:POST /api/model 必须带 `conversation_id: this.currentConversationId`。
|
|
||||||
4. **`server/context.py::_apply_workspace_personalization_preferences`**:对 `_bound_conversation_id` 非空的 terminal 跳过 session 模型恢复与 default_model 应用——否则 session 里的全局模型会把对话级 terminal 恢复好的模型再盖掉(重启后回变默认的直接元凶)。
|
|
||||||
|
|
||||||
## 会话级语义
|
|
||||||
|
|
||||||
- `session["model_key"]` 是全局"用户最后选择的模型":用于工作区级 terminal 恢复与 /new 继承,**不代表任何具体对话的模型**。
|
|
||||||
- 任务创建时前端随任务传 `model_key`(`send.ts`),`server/tasks/models.py` 在任务起点 `terminal.set_model(rec.model_key)`——前端是任务模型的权威来源。
|
|
||||||
- `set_model()` 可能因图片/视频不兼容或模型被禁用抛异常,加载场景应捕获降级。
|
|
||||||
- 管理员强制禁用模型的切换(context.py 406/549 行附近)对对话级 terminal 仍然生效,属正当覆盖。
|
|
||||||
|
|
||||||
## 验证脚本
|
|
||||||
|
|
||||||
- `_experiments/verify_model_fix.py`:p1 全周期(带cid切换→落盘、不带cid不污染)、p2 重启后保持;另需验证 `GET /api/status?conversation_id=X` 返回对话保存模型。
|
|
||||||
@ -1,210 +0,0 @@
|
|||||||
---
|
|
||||||
name: docker_toolbox_enhancement_plan
|
|
||||||
description: 当需要继续推进 my-agent-shell 容器镜像增强改造、查看本地实验结果、或在云端构建新版镜像时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# my-agent-shell 容器镜像增强改造计划与本地实验进度
|
|
||||||
|
|
||||||
> 记录时间:2026-06-25
|
|
||||||
> 相关记忆:`cloud_server_disk_usage`(服务器磁盘现状)
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
当前运行在云端的 `my-agent-shell` 容器(镜像 `my-agent-shell:latest`,基于 `python:3.11-slim`)依赖不足:
|
|
||||||
|
|
||||||
- docx/pptx skill 缺少 `defusedxml`
|
|
||||||
- pptx skill 推荐的 `markitdown` 未安装
|
|
||||||
- 完全缺少 LibreOffice / soffice
|
|
||||||
- 完全缺少 pandoc、pdftoppm(poppler-utils)
|
|
||||||
- 完全缺少 Node.js / npm
|
|
||||||
- 完全缺少中文字体(`fc-list` 为 0)
|
|
||||||
|
|
||||||
导致用户常见的 Office 转换、图片处理、OCR、网页截图等需求无法跑通。
|
|
||||||
|
|
||||||
## 改造方案
|
|
||||||
|
|
||||||
### 方案 B(已选定)
|
|
||||||
|
|
||||||
在最小方案基础上扩展常见业务能力:
|
|
||||||
|
|
||||||
**系统 apt 包:**
|
|
||||||
- Office:libreoffice、pandoc、poppler-utils
|
|
||||||
- 图片/视频:imagemagick、ffmpeg
|
|
||||||
- OCR:tesseract-ocr、tesseract-ocr-chi-sim、tesseract-ocr-chi-tra
|
|
||||||
- 网页:chromium
|
|
||||||
- 字体:fonts-noto-cjk、fonts-noto-color-emoji、fonts-liberation
|
|
||||||
- 开发工具:jq、ripgrep、fd-find、tree
|
|
||||||
|
|
||||||
**Python pip 包(加入 `docker/toolbox-requirements.txt`):**
|
|
||||||
- defusedxml
|
|
||||||
- markitdown[docx]
|
|
||||||
- pytesseract
|
|
||||||
- pdf2image
|
|
||||||
|
|
||||||
**Node 全局包:**
|
|
||||||
- docx
|
|
||||||
- pptxgenjs
|
|
||||||
|
|
||||||
**特殊处理:**
|
|
||||||
- 用 NodeSource 安装 Node 20 LTS,避免 Debian 源版本过老
|
|
||||||
- 修改 ImageMagick policy.xml 允许读写 PDF
|
|
||||||
- 设置 `NODE_PATH=/usr/lib/node_modules`
|
|
||||||
|
|
||||||
## 已修改文件
|
|
||||||
|
|
||||||
### 1. `docker/terminal.Dockerfile`
|
|
||||||
|
|
||||||
主要变更:
|
|
||||||
- apt 安装段加入 libreoffice、pandoc、poppler-utils、imagemagick、ffmpeg、tesseract、chromium、中文字体、开发工具
|
|
||||||
- 增加 ImageMagick PDF 策略调整 RUN 层
|
|
||||||
- 增加 NodeSource Node 20 安装
|
|
||||||
- 增加 `npm install -g docx pptxgenjs`
|
|
||||||
- 末尾添加 `ENV NODE_PATH="/usr/lib/node_modules"`
|
|
||||||
|
|
||||||
### 2. `docker/toolbox-requirements.txt`
|
|
||||||
|
|
||||||
新增:
|
|
||||||
- `defusedxml`
|
|
||||||
- `markitdown[docx]`
|
|
||||||
- `pytesseract`
|
|
||||||
- `pdf2image`
|
|
||||||
|
|
||||||
## 本地实验进度
|
|
||||||
|
|
||||||
### 实验环境
|
|
||||||
|
|
||||||
- 本地 macOS 15.6.1, arm64
|
|
||||||
- Docker Desktop
|
|
||||||
- 构建命令:
|
|
||||||
```bash
|
|
||||||
cd /Users/jojo/Desktop/agents/正在修复中/agents
|
|
||||||
docker build -f docker/terminal.Dockerfile -t my-agent-shell:local .
|
|
||||||
```
|
|
||||||
|
|
||||||
### 实验结果
|
|
||||||
|
|
||||||
| 指标 | 结果 |
|
|
||||||
|------|------|
|
|
||||||
| 构建成功 | ✅ 是 |
|
|
||||||
| 新镜像大小 | **2.9 GB**(原镜像 913 MB) |
|
|
||||||
| 镜像增加 | 约 **2 GB** |
|
|
||||||
| 空闲内存 | ~10 MB |
|
|
||||||
| 单任务峰值内存 | ~40 MB |
|
|
||||||
| 并发 5×soffice + 1×chromium 峰值内存 | **~237 MB** |
|
|
||||||
|
|
||||||
### 功能验证(全部通过)
|
|
||||||
|
|
||||||
| 测试项 | 结果 |
|
|
||||||
|--------|------|
|
|
||||||
| `soffice --headless --convert-to pdf` docx→pdf | ✅ |
|
|
||||||
| `pandoc docx -o md` | ✅ |
|
|
||||||
| `pdftoppm` pdf→png | ✅ |
|
|
||||||
| `markitdown` 读取 docx/pptx | ✅ |
|
|
||||||
| `python-docx` / `python-pptx` | ✅ |
|
|
||||||
| Node `docx-js` 生成 docx | ✅ |
|
|
||||||
| Node `pptxgenjs` 生成 pptx | ✅ |
|
|
||||||
| ImageMagick 图片裁剪 | ✅ |
|
|
||||||
| matplotlib 生成中文字体表格图片 | ✅ |
|
|
||||||
| tesseract OCR 中英文 | ✅(可运行,中文识别准确度一般) |
|
|
||||||
| chromium 网页截图 | ✅ |
|
|
||||||
| `pdf2image` PDF 转图片 | ✅ |
|
|
||||||
| docx/pptx skill 脚本 import | ✅ |
|
|
||||||
|
|
||||||
### 遇到的问题与解决
|
|
||||||
|
|
||||||
1. **markitdown 读 docx 报 MissingDependencyException**
|
|
||||||
- 原因:默认 `markitdown` 不包含 docx converter 的 `mammoth` 依赖
|
|
||||||
- 解决:改成 `markitdown[docx]`
|
|
||||||
|
|
||||||
2. **Node 全局安装的 `docx`/`pptxgenjs` require 不到**
|
|
||||||
- 原因:node 默认不搜索 `/usr/lib/node_modules`
|
|
||||||
- 解决:Dockerfile 添加 `ENV NODE_PATH="/usr/lib/node_modules"`
|
|
||||||
|
|
||||||
3. **chromium 运行时有 dbus 报错**
|
|
||||||
- 不影响截图结果,属于 headless 环境下的常见警告
|
|
||||||
|
|
||||||
4. **ImageMagick 默认禁止 PDF 处理**
|
|
||||||
- 解决:修改 `/etc/ImageMagick-6/policy.xml` 中 PDF 的 rights 为 `read|write`
|
|
||||||
|
|
||||||
5. **onnxruntime 启动警告**
|
|
||||||
- 警告:`Unknown CPU vendor. cpuinfo_vendor value: 0`
|
|
||||||
- 不影响功能,arm64 容器中的常见提示
|
|
||||||
|
|
||||||
## 尚未完成的工作
|
|
||||||
|
|
||||||
1. **云端构建验证**
|
|
||||||
- 当前只在本地 arm64 构建验证
|
|
||||||
- 需要在云端 x86_64 上实际构建
|
|
||||||
- 云端磁盘紧张(只剩 4GB),需先清理或分阶段构建
|
|
||||||
|
|
||||||
2. **精简镜像体积(可选)**
|
|
||||||
- 2.9GB 较大,可考虑去掉 chromium/ffmpeg 做成"最小方案 B"(约 1.5GB)
|
|
||||||
- 或保留完整方案 B,但需确保云端有足够空间
|
|
||||||
|
|
||||||
3. **react-icons / react / react-dom / sharp**
|
|
||||||
- pptxgenjs skill 文档中提到图标生成需要这几个包
|
|
||||||
- 当前未预装,需要时可在运行时安装或后续加入镜像
|
|
||||||
- `sharp` 涉及原生编译,构建时间和失败风险较高
|
|
||||||
|
|
||||||
4. **更详细的内存压力测试**
|
|
||||||
- 当前只做了简单并发测试
|
|
||||||
- 未测试大文件转换、长时间运行场景
|
|
||||||
|
|
||||||
5. **ImageMagick 安全策略复核**
|
|
||||||
- 当前为支持 PDF 放宽了限制,需确认是否符合安全要求
|
|
||||||
|
|
||||||
## 下一步建议
|
|
||||||
|
|
||||||
### 推荐路径:分阶段上云端
|
|
||||||
|
|
||||||
1. **阶段一:精简必要包**
|
|
||||||
- 去掉 chromium、ffmpeg、imagemagick、tesseract
|
|
||||||
- 保留:libreoffice、pandoc、poppler-utils、node、npm、docx、pptxgenjs、字体、defusedxml、markitdown[docx]
|
|
||||||
- 目标镜像约 1.3-1.5GB
|
|
||||||
- 先验证 Office skill 能跑通
|
|
||||||
|
|
||||||
2. **阶段二:按需加回媒体能力**
|
|
||||||
- 根据实际使用频率,决定是否加回 imagemagick、ffmpeg、chromium、tesseract
|
|
||||||
|
|
||||||
3. **阶段三:优化构建缓存**
|
|
||||||
- 考虑把 apt 层拆成多个 RUN,便于失败后重试
|
|
||||||
- 云端构建时加 `--progress=plain` 便于排查
|
|
||||||
|
|
||||||
## 关键命令
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 本地构建
|
|
||||||
cd /Users/jojo/Desktop/agents/正在修复中/agents
|
|
||||||
docker build -f docker/terminal.Dockerfile -t my-agent-shell:local .
|
|
||||||
|
|
||||||
# 本地测试容器
|
|
||||||
docker run -d --name agent-test-local my-agent-shell:local tail -f /dev/null
|
|
||||||
|
|
||||||
# 验证基础依赖
|
|
||||||
docker exec agent-test-local bash -c "which soffice pandoc pdftoppm node npm convert ffmpeg chromium tesseract jq rg fdfind tree"
|
|
||||||
|
|
||||||
# 验证 Python 包
|
|
||||||
docker exec agent-test-local bash -c "/opt/agent-venv/bin/python -c 'import defusedxml, markitdown, pytesseract, pdf2image'"
|
|
||||||
|
|
||||||
# 验证 Node 包
|
|
||||||
docker exec agent-test-local bash -c "node -e 'require(\"docx\"); require(\"pptxgenjs\"); console.log(\"OK\")'"
|
|
||||||
|
|
||||||
# 查看镜像大小
|
|
||||||
docker images my-agent-shell:local
|
|
||||||
|
|
||||||
# 监控运行时内存
|
|
||||||
docker stats agent-test-local --no-stream
|
|
||||||
```
|
|
||||||
|
|
||||||
## 重要约束
|
|
||||||
|
|
||||||
- **本地 arm64,云端 x86_64,禁止把本地构建的镜像上传到云端**
|
|
||||||
- 云端磁盘只剩 4GB,必须清理或分阶段构建
|
|
||||||
- 云端内存 2GB,构建/运行需警惕 OOM
|
|
||||||
- 云端网络差,大文件下载可能超时
|
|
||||||
|
|
||||||
## 相关文件
|
|
||||||
|
|
||||||
- `docker/terminal.Dockerfile`
|
|
||||||
- `docker/toolbox-requirements.txt`
|
|
||||||
- 项目记忆:`cloud_server_disk_usage`(服务器磁盘现状)
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
---
|
|
||||||
name: frontend_debug_log_to_file
|
|
||||||
description: 当需要把前端日志输出到后端文件(如调试目标模式、轮询状态等)时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 前端日志输出到文件
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
项目已内置一套轻量级的前端 → 后端日志上报机制,用于把浏览器端的状态变化/调试信息持久化到服务器日志文件,便于复现和排查问题。
|
|
||||||
|
|
||||||
## 基础设施
|
|
||||||
|
|
||||||
### 后端端点
|
|
||||||
|
|
||||||
- 文件:`server/app_legacy.py`
|
|
||||||
- 端点:`POST /api/client_debug_log`
|
|
||||||
- 写入函数:`log_goal_mode_debug_entry(data: Dict[str, Any])`
|
|
||||||
- 日志文件:`{LOGS_DIR}/goal_mode_debug.log`
|
|
||||||
- 宿主机模式示例:`/Users/jojo/.astrion/astrion/host/logs/goal_mode_debug.log`
|
|
||||||
|
|
||||||
### 前端函数
|
|
||||||
|
|
||||||
- 文件:`static/src/app/methods/common.ts`
|
|
||||||
- 函数:`goalModeDebugLog(event: string, payload: Record<string, any> = {})`
|
|
||||||
- 默认关闭,避免日常刷屏
|
|
||||||
|
|
||||||
## 启用方式
|
|
||||||
|
|
||||||
### 方式一:浏览器控制台临时开启(无需改代码)
|
|
||||||
|
|
||||||
```js
|
|
||||||
localStorage.setItem('goalModeDebug', '1')
|
|
||||||
// 刷新页面后生效
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方式二:代码里强制开启
|
|
||||||
|
|
||||||
在 `common.ts` 中修改 `isGoalModeDebugEnabled()`,让它默认返回 `true`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
function isGoalModeDebugEnabled() {
|
|
||||||
// ...
|
|
||||||
return true; // 强制开启
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方式三:运行时开关
|
|
||||||
|
|
||||||
```js
|
|
||||||
window.__GOAL_MODE_DEBUG__ = true
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用方式
|
|
||||||
|
|
||||||
在前端需要记录状态的位置调用:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { goalModeDebugLog } from './common';
|
|
||||||
|
|
||||||
goalModeDebugLog('event_name', {
|
|
||||||
conversationId: this.currentConversationId,
|
|
||||||
someState: this.someState,
|
|
||||||
// 其他需要记录的字段
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
调用后会:
|
|
||||||
1. 在浏览器控制台输出 `[GOAL_MODE_DEBUG]` 前缀日志
|
|
||||||
2. 通过 `fetch('/api/client_debug_log', ...)` 上报到后端
|
|
||||||
3. 后端写入 `goal_mode_debug.log`
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 日志默认关闭,只有显式开启后才会输出
|
|
||||||
- `fetch` 调用使用 `void` 前缀,不会阻塞业务流程
|
|
||||||
- 上报失败会被静默捕获,不影响主流程
|
|
||||||
- 如需长期保留某类日志,应把调用代码保留在目标文件中;临时调试后建议清理调用点,只保留 `common.ts` 和后端端点基础设施
|
|
||||||
|
|
||||||
## 历史应用
|
|
||||||
|
|
||||||
- 曾用于排查"第一个对话开启目标模式后停止,目标状态被错误继承到下一个对话"的问题
|
|
||||||
- 修复代码位于 `server/tasks/api.py`:
|
|
||||||
- `/api/tasks/<task_id>/cancel` 取消任务时同步停止工作区目标状态
|
|
||||||
- `POST /api/tasks` 创建非目标模式任务时清理残留的活动目标状态
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
name: ocr_client_initialization
|
|
||||||
description: 当 OCRClient/OpenAI 初始化报错、WebTerminal 创建失败、或升级 openai 包时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# OCRClient 初始化与 OpenAI 包兼容性
|
|
||||||
|
|
||||||
## 问题
|
|
||||||
|
|
||||||
`modules/ocr_client.py` 在 `__init__` 中直接创建 `OpenAI(api_key=OCR_API_KEY)`。
|
|
||||||
|
|
||||||
- `openai` 包较新版本(>= 2.40)在 `api_key` 为空时会抛出 `openai.OpenAIError: Missing credentials`
|
|
||||||
- 服务器 settings.json 通常未配置 `OCR_API_KEY`
|
|
||||||
- 这会导致 `WebTerminal`/`MainTerminal` 初始化失败,所有依赖 terminal 的 API 都返回 500
|
|
||||||
|
|
||||||
## 修复
|
|
||||||
|
|
||||||
在 `OCRClient.__init__` 中,仅在 `OCR_API_KEY` 存在时才创建 `OpenAI` 客户端:
|
|
||||||
|
|
||||||
```python
|
|
||||||
self.client = None
|
|
||||||
if OCR_API_KEY:
|
|
||||||
self.client = OpenAI(
|
|
||||||
api_key=OCR_API_KEY,
|
|
||||||
base_url=base_url or None,
|
|
||||||
http_client=self.http_client,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
在 `vlm_analyze` 中同时检查 `self.client`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
if not self.client or not OCR_API_KEY or not OCR_API_BASE_URL or not self.model:
|
|
||||||
return {"success": False, "error": "VLM 配置缺失,请设置 OCR_API_BASE_URL / OCR_API_KEY / OCR_MODEL_ID", "warnings": warnings}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 教训
|
|
||||||
|
|
||||||
- 升级依赖包后要检查破坏性变更
|
|
||||||
- 可选功能(OCR/VLM)的客户端初始化不应阻塞主终端启动
|
|
||||||
- 服务器环境缺少某个 API key 时,应优雅降级而不是直接崩溃
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
---
|
|
||||||
name: opencode_key_replacement_script
|
|
||||||
description: 当需要批量替换 ~/.astrion/astrion、~/.astrion/astrion-clone 和服务器 /opt/agent/runtime 中的 OpenCode API key 时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
---
|
|
||||||
name: opencode_key_replacement_script
|
|
||||||
description: 当需要批量替换 ~/.astrion/astrion、~/.astrion/astrion-clone 和服务器 /opt/agent/runtime 中的 OpenCode API key 时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# OpenCode API key 批量替换脚本
|
|
||||||
|
|
||||||
## 脚本位置
|
|
||||||
|
|
||||||
项目根目录下的 `replace_opencode_keys.py`。
|
|
||||||
|
|
||||||
## 作用
|
|
||||||
|
|
||||||
一次性批量替换以下目录中所有 OpenCode 相关的 API key:
|
|
||||||
|
|
||||||
- 本地:`/Users/jojo/.astrion/astrion`
|
|
||||||
- 本地:`/Users/jojo/.astrion/astrion-clone`
|
|
||||||
- 服务器:`/opt/agent/runtime/`(或服务器上 `ASTRION_DATA_ROOT` 指向的目录)
|
|
||||||
|
|
||||||
## 扫描范围
|
|
||||||
|
|
||||||
仅扫描配置文件,自动跳过运行时数据:
|
|
||||||
|
|
||||||
- `settings.json`
|
|
||||||
- `config/*.json`
|
|
||||||
- 跳过 `data/`、`logs/`、对话历史等目录
|
|
||||||
- 跳过已有的 `.bak_` / `.bak` 备份文件
|
|
||||||
|
|
||||||
## 替换规则
|
|
||||||
|
|
||||||
脚本会查找已知的旧 OpenCode key 值(被多个 OpenCode Go 相关环境变量共用的同一串 key),并将其替换为新的 OpenCode key。
|
|
||||||
|
|
||||||
实际会改到的键名通常包括:
|
|
||||||
|
|
||||||
- `settings.json` 中 `env_vars` 下的:
|
|
||||||
- `API_KEY_OPENCODE`
|
|
||||||
- `AGENT_API_KEY`
|
|
||||||
- `AGENT_THINKING_API_KEY`
|
|
||||||
- `AGENT_TITLE_API_KEY`
|
|
||||||
- 指向 `opencode.ai` 的 `API_KEY_KIMI`、`API_KEY_DEEPSEEK`、`API_KEY_QWEN`、`API_KEY_MINIMAX` 等
|
|
||||||
- `config/sub_agent_models.json` 中 `url` 为 `https://opencode.ai/zen/go/v1` 的模型条目的 `apikey`
|
|
||||||
|
|
||||||
`config/custom_models.json` 中一般使用 `${API_KEY_OPENCODE}` 变量引用,key 本身存在 `settings.json`,脚本不会直接修改它。
|
|
||||||
|
|
||||||
## 用法
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 只预览,不写入(循环轮换模式)
|
|
||||||
python3 replace_opencode_keys.py --dry-run
|
|
||||||
|
|
||||||
# 正式执行循环轮换(默认:key1→key2, key2→key3, key3→key1)
|
|
||||||
python3 replace_opencode_keys.py
|
|
||||||
|
|
||||||
# 单次指定替换(覆盖默认循环模式)
|
|
||||||
python3 replace_opencode_keys.py --old-key <旧key> --new-key <新key>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 服务器替换流程
|
|
||||||
|
|
||||||
1. 确保脚本已上传至 `/opt/agent/agents/replace_opencode_keys.py`
|
|
||||||
2. 在服务器上修改脚本,把 `Path("/opt/agent/runtime")` 加入 `TARGET_DIRS`
|
|
||||||
3. 先执行 `--dry-run` 预览影响范围
|
|
||||||
4. 去掉 `--dry-run` 正式执行
|
|
||||||
5. 执行 `bash /opt/agent/refresh_agents.sh` 重启 webapp 服务使配置生效
|
|
||||||
6. 删除生成的 `.bak_时间戳` 备份文件
|
|
||||||
|
|
||||||
## 服务器执行方式建议
|
|
||||||
|
|
||||||
- **推荐**:通过 `terminal_session` 打开一个 SSH 会话,在会话里直接运行 `python3 replace_opencode_keys.py ...`
|
|
||||||
- **次选**:使用服务器虚拟环境的完整路径,例如 `/opt/agent/venv/bin/python3 replace_opencode_keys.py ...`
|
|
||||||
- **注意**:在旧版本或未重启前,通过 `run_command` 直接执行 `ssh host "python3 ..."` 时,`run_command` 可能把命令中的 `python3` 重写为本地 macOS 的 Python 绝对路径,导致远程服务器找不到解释器。该问题已在 `modules/terminal_ops/run.py` 中修复:当命令通过 `ssh` 在远程执行时,跳过 `python3`/`pip` 重写。
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 执行前会自动在同级目录生成 `.bak_时间戳` 备份。
|
|
||||||
- 如果在沙箱/受限执行环境下运行,原目录不可写时脚本会把备份和修改后的文件写到项目工作区的 `opencode_key_replacement_backup/` 目录下,需要手动复制覆盖。
|
|
||||||
- 建议执行后删除生成的 `.bak_时间戳` 备份文件。
|
|
||||||
- 服务器上配置文件修改后,需要重启 webapp 服务才能生效。
|
|
||||||
|
|
||||||
## 历史记录
|
|
||||||
|
|
||||||
- 2026-06-19:使用脚本将本地两个目录的配置从主 key 替换为备用 key,并删除备份。
|
|
||||||
- 2026-06-19:将服务器 `/opt/agent/runtime/` 下的 `settings.json` 和 `config/sub_agent_models.json` 也从主 key 替换为备用 key,并删除备份。
|
|
||||||
- 2026-06-24:将服务器 `/opt/agent/runtime/` 下的 `settings.json`(9 处)和 `config/sub_agent_models.json`(1 处)从备用 key 替换回主 key,随后执行 `bash /opt/agent/refresh_agents.sh` 重启 webapp 服务,并删除备份。期间发现 `run_command` 执行 `ssh host "python3 ..."` 会错误重写 `python3` 为本地 macOS 路径,已修复 `modules/terminal_ops/run.py`。
|
|
||||||
- 2026-06-25:重构脚本为三 key 循环轮换模式(key1/key2/key3,去掉主/备用命名)。将本地 `~/.astrion/astrion` 和 `~/.astrion/astrion-clone` 的配置从 key2 替换为 key3(4 文件 20 处)。服务器尚未操作,当前仍为 key1。
|
|
||||||
- 2026-06-25:将本地 `~/.astrion/astrion` 和 `~/.astrion/astrion-clone` 的配置从 key2 替换为 key3(4 文件 20 处),删除备份。服务器仍为 key1。
|
|
||||||
|
|
||||||
## 相关 key 记录
|
|
||||||
|
|
||||||
| 说明 | Key |
|
|
||||||
|---|---|
|
|
||||||
| key1 | `sk-zNU867TGGXNKxoM0H6Pg6Jq4PYUPUOPDgMlzrOl3SS6jECRMWK5nNi6YMSsHzoZ` |
|
|
||||||
| key2 | `sk-sgt4ERgERPx2axU18PSfmenHv9zNDD3y8M5yr0p4ryhcNSm04CZsCxDHftJ9AkFD` |
|
|
||||||
| key3 | `sk-Jhvi76vJdCEQzvEdl8cAS5MIwBxBS4iTrJYSru2zCAXVnDVQpiZIvrcn9hBwkdZ4` |
|
|
||||||
| legacy(已停用,等同 key1 轮换到 key2) | `sk-j0NdTvEDoqqkKCHEAFXXYneEsuXEiOXNCgLeZARJmfYUBf2Riym7KPFWq1amkB6z` |
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
---
|
|
||||||
name: pywebview_macos_packaging
|
|
||||||
description: 当打包 PyWebview macOS App、调试 "The string did not match" 错误、修改 _packaging/ 目录时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# PyWebview macOS 打包项目记忆
|
|
||||||
|
|
||||||
## 目标
|
|
||||||
将 Vue 3 + Python Flask 项目打包为 macOS 桌面 App(PyWebview),不使用 Electron/Tauri 或原生 macOS 对话框。
|
|
||||||
|
|
||||||
## 核心文件
|
|
||||||
| 文件 | 作用 |
|
|
||||||
|------|------|
|
|
||||||
| `_packaging/app.py` | PyWebview 主入口:检测 `.env` → 直接启动 / setup页面 |
|
|
||||||
| `_packaging/setup.html` | 首次设置页面(4步:工作区→模型→管理员→确认),classic暖奶油主题 |
|
|
||||||
| `_packaging/app.spec` | PyInstaller 打包配置 |
|
|
||||||
| `_packaging/dist/AI-Agent-System.app` | 打包产物 |
|
|
||||||
| `_packaging/AI-Agent-System-v8.dmg` | 最后一个可用DMG(527MB) |
|
|
||||||
|
|
||||||
## 关键修复历史
|
|
||||||
|
|
||||||
### 1. DMG 禁止标志
|
|
||||||
- **原因**:DMG 内同时存在 `AI-Agent-System/`(onedir目录)和 `AI-Agent-System.app`
|
|
||||||
- **修复**:DMG 只放 `.app` + `Applications` 符号链接
|
|
||||||
- **hdiutil 创建需完全访问权限**:当前受限制,需切换到完全访问权限模式
|
|
||||||
|
|
||||||
### 2. OpenSSL 符号冲突
|
|
||||||
- **原因**:`_ssl.cpython-311-darwin.so` 加载 psycopg2 自带的旧版 `libcrypto.3.dylib`
|
|
||||||
- **修复**:用 Homebrew OpenSSL 3.6.2 的 dylib 替换 bundle 中 `__dot__dylibs` 目录
|
|
||||||
|
|
||||||
### 3. `.env` 加载路径
|
|
||||||
- **修复**:`config/__init__.py` 的 `_load_dotenv()` 改为 frozen 时读 `~/.astrion/astrion/host/.env`
|
|
||||||
|
|
||||||
### 4. engineio Invalid async_mode
|
|
||||||
- **原因**:PyInstaller frozen 环境缺少 `engineio.async_drivers.threading`
|
|
||||||
- **修复**:`app.spec` hiddenimports 添加 `'engineio.async_drivers.threading'`
|
|
||||||
|
|
||||||
### 5. PyWebview 6.x private_mode 🚨 **关键问题**
|
|
||||||
- **现象**:Playwright 浏览器中正常(0 errors),PyWebview App 中所有 API 返回 HTML 导致 "The string did not match the expected pattern"
|
|
||||||
- **根因**:`webview.start()` 默认 `private_mode=True`,WKWebView 隐私模式下 cookie 不工作,session 丢失
|
|
||||||
- **修复**:`webview.start(private_mode=False)`
|
|
||||||
|
|
||||||
### 6. App Transport Security
|
|
||||||
- **修复**:`app.spec` Info.plist 添加 `NSAppTransportSecurity: { NSAllowsLocalNetworking: True }`
|
|
||||||
|
|
||||||
### 7. 环境变量污染
|
|
||||||
- **问题**:旧环境变量 `AGENTS_HOST_HOME=~/.agents/host-clone` 导致数据目录错误(现已被 `ASTRION_DATA_ROOT` 替代)
|
|
||||||
- **注意**:`~/.astrion/astrion-clone` 是本对话副本数据,不要动
|
|
||||||
- **修复**:`_start_flask` 导入前清除 `ASTRION_DATA_ROOT`/`DATA_DIR`/`LOGS_DIR` 等旧环境变量污染
|
|
||||||
|
|
||||||
### 8. JSON 错误处理器
|
|
||||||
- **修复**:`_start_flask` 中注册全局 error handler,确保任何异常都返回 JSON 而非 HTML
|
|
||||||
|
|
||||||
## 当前状态(2026-06-12)
|
|
||||||
|
|
||||||
### ✅ 已确认正常
|
|
||||||
- Flask 后端在浏览器中完全正常(curl/Playwright 测试 0 errors)
|
|
||||||
- 登录、对话列表、数据加载均正常
|
|
||||||
- API 返回纯 JSON
|
|
||||||
|
|
||||||
### ❌ 仍存在问题
|
|
||||||
- **PyWebview App 中仍然无数据**,前端报 "The string did not match the expected pattern"
|
|
||||||
- 已排除:backend 崩溃、engineio 错误、private_mode、ATS、环境变量污染
|
|
||||||
- **问题定位**:在 PyWebview 的 WKWebView 内部,fetch API 请求被某种机制拦截/修改,导致返回非 JSON 响应
|
|
||||||
|
|
||||||
### 🔍 下一步排查方向
|
|
||||||
1. 在 PyWebview 中启用远程调试(`webview.start(debug=True)`)查看 WKWebView console
|
|
||||||
2. 检查 PyWebview 6.x 的 `request_sent`/`response_received` 事件是否拦截了请求
|
|
||||||
3. 检查 WKWebView 对 `127.0.0.1` 的 same-origin 策略是否有特殊处理
|
|
||||||
4. 尝试用 `localhost` 替代 `127.0.0.1`
|
|
||||||
5. 考虑降级 PyWebview 到 5.x 或尝试其他打包方案(Tauri/Electron)
|
|
||||||
|
|
||||||
## 打包命令
|
|
||||||
```bash
|
|
||||||
# Python 必须是 Homebrew Python 3.11(Xcode Python 3.9 会 SIGKILL)
|
|
||||||
cd _packaging
|
|
||||||
pyinstaller --noconfirm --distpath ./dist --workpath ./build app.spec
|
|
||||||
|
|
||||||
# 安装到 /Applications
|
|
||||||
cp -R dist/AI-Agent-System.app /Applications/
|
|
||||||
codesign --force --deep -s - /Applications/AI-Agent-System.app
|
|
||||||
xattr -cr /Applications/AI-Agent-System.app
|
|
||||||
|
|
||||||
# 创建 DMG(需完全访问权限)
|
|
||||||
hdiutil create -srcfolder dist/AI-Agent-System.app -volname "AI_Agent_System" -ov -format UDZO AI-Agent-System.dmg
|
|
||||||
```
|
|
||||||
|
|
||||||
## 用户要求
|
|
||||||
- 禁止原生 macOS/浏览器组件
|
|
||||||
- setup 页面需与项目设计风格统一
|
|
||||||
- 只读一个 `.env`(数据存储目录 ~/.astrion/astrion/host/.env)
|
|
||||||
- 等待时间不要设置太长
|
|
||||||
- `~/.astrion/astrion-clone` 是本对话副本数据,不要乱动
|
|
||||||
- `~/.astrion/astrion/host` 是正常运行时数据
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
---
|
|
||||||
name: remaining_long_files_to_split
|
|
||||||
description: 当需要继续拆分项目中的过长单个文件时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 仍需拆分的长文件清单
|
|
||||||
|
|
||||||
> 更新于 2026-06-21。
|
|
||||||
> 已完成的拆分:`server/chat/`、`server/status/`、`server/tasks/`、`utils/context_manager/`、`utils/conversation_manager/`、`utils/api_client/`、`utils/tool_result_formatter/`、`modules/file_manager/`、`modules/persistent_terminal/`、`modules/terminal_ops/`、`modules/mcp_client_manager/`、`core/main_terminal_parts/context/`、`core/main_terminal_parts/tools_definition/`、前端 `static/src/app/methods/{ui,message,upload,taskPolling,conversation}/`。
|
|
||||||
> `static/src/components/chat/monitor/MonitorDirector.ts` 按用户要求不参与拆分。
|
|
||||||
|
|
||||||
下列文件仍是单个文件且超过约 500 行,**但 500 行只是参考线,不是硬性指标**。是否拆分取决于可维护性——优先拆**业务逻辑重、复用价值高、边界清晰**的文件;边界模糊、耦合紧密或改动频率低的文件,保持现状更划算。Vue SFC 文件因模板-样式-脚本耦合较深,拆分风险较高,建议先抽离 composable / 子组件,而非直接切分 `.vue`。
|
|
||||||
|
|
||||||
## 后端 Python(高优先级)
|
|
||||||
|
|
||||||
| 文件 | 行数 | 建议拆分方向 |
|
|
||||||
|------|------|--------------|
|
|
||||||
| `core/main_terminal_parts/tools_execution.py` | ~1911 | 按执行阶段拆:准备 / 工具调度 / 结果处理 / 错误恢复 / 流式输出 |
|
|
||||||
| `server/chat_flow_task_main.py` | ~1739 | 拆为消息处理、guidance 队列、流式响应、任务生命周期、历史保存等 mixin |
|
|
||||||
| `server/conversation.py` | ~1981 | 拆为对话 CRUD、回顾(review)、压缩(compression)、待办联动等模块 |
|
|
||||||
| `server/chat_flow_tool_loop.py` | ~1391 | 按循环阶段拆:参数检查、工具调用、结果合并、循环终止 |
|
|
||||||
| `server/app_legacy.py` | ~1431 | 它是 legacy 入口,改动频繁则优先拆为注册/中间件/路由聚合 |
|
|
||||||
|
|
||||||
## 后端 Python(中优先级)
|
|
||||||
|
|
||||||
| 文件 | 行数 | 建议拆分方向 |
|
|
||||||
|------|------|--------------|
|
|
||||||
| `core/main_terminal.py` | ~661 | 拆 init / 状态 / 主循环 / 事件处理 mixin |
|
|
||||||
| `core/web_terminal.py` | ~710 | 拆轮询任务 / Socket.IO / REST 接口 / 前端状态同步 |
|
|
||||||
| `core/main_terminal_parts/commands.py` | ~877 | 拆命令解析、分发、内置命令处理 |
|
|
||||||
| `server/context.py` | ~725 | 拆 terminal/workspace 资源获取、会话装饰器、错误处理 |
|
|
||||||
| `server/admin.py` | ~728 | 按管理领域拆:用户/模型/策略/日志/系统配置 |
|
|
||||||
| `server/api_v1.py` | ~704 | 按资源拆子 blueprint |
|
|
||||||
| `modules/background_command_manager.py` | ~727 | 拆生命周期、输出流、持久化 |
|
|
||||||
| `modules/personalization_manager.py` | ~805 | 拆读取/合并/保存/校验 |
|
|
||||||
| `modules/terminal_manager.py` | ~756 | 拆会话池、命令执行、输出缓存 |
|
|
||||||
| `modules/user_manager.py` | ~779 | 拆认证、工作区管理、host/web 双路径 |
|
|
||||||
| `modules/versioning_manager.py` | ~842 | 拆版本记录、快照、回滚、清理 |
|
|
||||||
|
|
||||||
## 前端 TypeScript / Vue(高优先级)
|
|
||||||
|
|
||||||
| 文件 | 行数 | 建议拆分方向 |
|
|
||||||
|------|------|--------------|
|
|
||||||
| `static/src/components/input/InputComposer.vue` | ~2824 | **高风险**:先抽离 composable(输入状态、快捷键、粘贴处理)、子组件(附件列表、工具按钮),保留 `.vue` 外壳 |
|
|
||||||
| `static/src/components/personalization/PersonalizationDrawer.vue` | ~3547 | **高风险**:按设置分组拆成子组件 / tab panels,抽离表单校验 composable |
|
|
||||||
| `static/src/components/chat/ChatArea.vue` | ~1728 | 抽离消息列表、滚动行为、空状态、错误状态为子组件或 composable |
|
|
||||||
| `static/src/composables/useLegacySocket.ts` | ~1885 | 按事件类型拆为多个 handler 模块 |
|
|
||||||
| `static/src/admin/AdminDashboardApp.vue` | ~1959 | 按 admin 子页面拆为独立组件 |
|
|
||||||
| `static/src/admin/PolicyApp.vue` | ~1601 | 按策略维度拆为子表单 / 子组件 |
|
|
||||||
| `static/src/app/bootstrap.ts` | ~1339 | 按初始化阶段拆:配置加载、store 初始化、路由恢复、终端重建 |
|
|
||||||
|
|
||||||
## 前端 TypeScript / Vue(中优先级)
|
|
||||||
|
|
||||||
| 文件 | 行数 | 建议拆分方向 |
|
|
||||||
|------|------|--------------|
|
|
||||||
| `static/src/components/chat/actions/ToolAction.vue` | ~1532 | 拆工具状态展示、参数/结果渲染、审批交互 |
|
|
||||||
| `static/src/components/chat/actions/toolRenderers.ts` | ~1044 | 按工具类型拆 renderer |
|
|
||||||
| `static/src/App.vue` | ~882 | 抽离布局壳、侧边栏、全局弹窗 |
|
|
||||||
| `static/src/stores/personalization.ts` | ~924 | 按功能域拆 actions |
|
|
||||||
| `static/src/stores/task.ts` | ~632 | 若继续增长可拆任务/审批/队列 |
|
|
||||||
| `static/src/stores/tutorial.ts` | ~749 | 拆 tutorial 步骤、状态、触发逻辑 |
|
|
||||||
| `static/src/stores/monitor.ts` | ~677 | 目前可接受,继续增长再拆 |
|
|
||||||
|
|
||||||
## CLI
|
|
||||||
|
|
||||||
| 文件 | 行数 | 建议拆分方向 |
|
|
||||||
|------|------|--------------|
|
|
||||||
| `cli/src/App.tsx` | ~704 | 拆 views / screens / hooks |
|
|
||||||
|
|
||||||
## 拆分原则(与 AGENTS.md 一致)
|
|
||||||
|
|
||||||
- 优先用 **mixin / 子包** 模式,保持 `from xxx import Xxx` 兼容入口。
|
|
||||||
- 拆 Flask blueprint 时,`__init__.py` 创建唯一 blueprint,子模块从包导入并保留装饰器。
|
|
||||||
- 拆分后检查**跨模块共享函数/常量/类属性**,必要时抽到 `common.py` 或显式导入。
|
|
||||||
- 前端 methods 拆完后,入口 `index.ts` 必须用 `...methodGroup` 平铺,避免嵌套对象导致 `this.xxx` 不存在。
|
|
||||||
- 每次拆分后必须:
|
|
||||||
```bash
|
|
||||||
python3 -m py_compile <对应包>/*.py
|
|
||||||
python -m unittest test.test_server_refactor_smoke
|
|
||||||
npm run build --silent 2>&1 | tail -n 5
|
|
||||||
```
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
name: runtime_state_loading_concurrency_redesign
|
|
||||||
description: 当继续讨论/实现 conversation 级 WebTerminal 隔离(单对话单 terminal、同工作区多对话并行)、24h TTL 回收、子智能体按需加载、git worktree 时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 运行状态对账 + Conversation 级 WebTerminal 隔离(全部已完成)
|
|
||||||
|
|
||||||
> 2026-07-20 更新。完整实现记录见 `docs/conversation_level_terminal_design.md` §7(含文件级改动清单与验证结果)。
|
|
||||||
|
|
||||||
## 状态
|
|
||||||
- 议题 1(运行状态 REST 对账):✅ 完成并验证。事件为主 + 2.5s 对账纠偏(`GET /api/conversations/<id>/running-status`,前端 `probe.ts` 单循环)。
|
|
||||||
- 议题 4(conversation 级 WebTerminal 隔离):✅ 2026-07-20 完成并端到端验证。议题 2(子智能体按需加载)、议题 3(worktree)暂缓。
|
|
||||||
|
|
||||||
## 议题 4 核心架构(勿重新讨论,用户已拍板)
|
|
||||||
- terminal key = `username::workspace_id::conversation_id`(`server/context.py _make_terminal_key`);**container key 保持工作区级**(docker 一工作区一容器)。
|
|
||||||
- `get_user_resources(..., conversation_id=None)` 可选参数;无对话上下文的 API(对话列表/api_v1/skills/conversation.py/socket connect)保持工作区级服务实例(shell 惰性)。
|
|
||||||
- WebTerminal `_bound_conversation_id`,`_ensure_conversation` 加载绑定对话不 fallback;`last_activity_at` + 24h TTL 回收器(`reap_idle_conversation_terminals`,env `CONVERSATION_TERMINAL_TTL_SECONDS`)。
|
|
||||||
- chat 互斥:同工作区 → 同对话(`tasks/models.py create_chat_task`,`_norm_cid` 比较)。
|
|
||||||
- TerminalManager per 对话(每对话 max 3 终端、对话间分离);广播回调包装注入 conversation_id(`_wrap_callback_with_conversation_id`),前端 TerminalPanel `acceptTerminalBroadcast` 过滤 7 事件。
|
|
||||||
- 前端 `currentWorkspaceHasRunningTask` 已改当前对话判定(名字未改,遗留更名)。
|
|
||||||
- **行为变化**:SubAgentManager 按 `owner_conversation_id` 过滤恢复,重启后多智能体任务延迟到对话激活时恢复(工作区级 manager 不再恢复)。
|
|
||||||
|
|
||||||
## 验证资产
|
|
||||||
- `_experiments/verify_conversation_level_terminal.py`(9 断言)、`_experiments/verify_running_status_api.py`。
|
|
||||||
- 实测铁证:双对话并行 running、同对话 409、A 运行中 B 页面零锁定、终端 A/B 列表隔离、/api/terminals max_allowed=3。
|
|
||||||
|
|
||||||
## 边界修复轮次(commit 序列)
|
|
||||||
- `a4eb6888` 议题1+4 主体 → `1289c31d` 模型持久化四防线 → `6b5b1732` 二轮边界(压缩路由走对话级/回收器竞态防护/空cid补建对话)→ `e8bbaccf` idle 多智能体不算运行(对齐 socket 语义,防 REST 幽灵运行+回收器永久阻塞;idle 实例靠 restore_running_tasks 磁盘恢复兜底)。
|
|
||||||
- 详见 `docs/conversation_level_terminal_design.md` §8/§9。
|
|
||||||
|
|
||||||
## 环境教训(2026-07-20 实测踩坑)
|
|
||||||
- **8092 端口是用户另一检出的服务**(cwd=`/Users/jojo/Desktop/外置/agents`),不是本仓库;打接口前必须 `lsof -nP -p <pid> | grep cwd` 确认进程归属。
|
|
||||||
- 沙箱会拦 `kill`/`ps`(Operation not permitted),新服务在 socketio.run 绑端口时被杀;测服务相关改动优先进程内直调(如 `_experiments/test_reaper_unit.py` 19 断言)。
|
|
||||||
- host 模式多实例共写同一 debug_stream.log 且启动即截断;import server.app 的测试脚本必须先设 `ASTRION_DATA_ROOT` 到 /tmp 隔离。
|
|
||||||
- 短 TTL 回收实测(CONVERSATION_TERMINAL_TTL_SECONDS=120)尚未在真实服务验证,待用户下次自然重启服务后补测(单元测试已覆盖逻辑)。
|
|
||||||
|
|
||||||
## 遗留
|
|
||||||
- 测试对话 conv_20260720_001402_595/857(语音工作区,A 内有 test-shell-a);张雪峰对话 conv_20260718_005231_752 有测试消息;conv_20260720_032131_953/032132_014(TTL 测试误建,在共享数据里,可删)。
|
|
||||||
- 既有 bug 未修:assistant reasoning_content 前端渲染两遍(历史加载路径)。
|
|
||||||
- 服务验证用 host-login 后必须重新 GET /api/csrf-token(login 会 force_new token);启动服务不要用 `| head` 管道(Broken pipe 杀任务)。
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
---
|
|
||||||
name: server_deployment_venv
|
|
||||||
description: 当服务器部署时遇到 venv/pip 损坏、Python 路径错误,或需要更新部署脚本时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 服务器 venv 部署注意事项
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
服务器 `/opt/agent/venv` 最初是从 macOS 复制到 Linux(Ubuntu 24.04)的,导致:
|
|
||||||
|
|
||||||
- `python3` 等符号链接指向 macOS 路径(`/Library/Developer/...`)
|
|
||||||
- `pip`、`flask` 等脚本的 shebang 指向 macOS 路径或错误路径
|
|
||||||
- 结果:`python3` 可能能用,但 `pip` 无法执行
|
|
||||||
|
|
||||||
## 修复方法
|
|
||||||
|
|
||||||
### 1. 重建 venv(推荐)
|
|
||||||
|
|
||||||
在服务器上用 Linux 系统 Python 重建:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
/usr/bin/python3.12 -m venv --clear /opt/agent/venv
|
|
||||||
/opt/agent/venv/bin/pip install -r /opt/agent/agents/requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 修改后的部署脚本
|
|
||||||
|
|
||||||
#### `/opt/agent/start_webapp_new.sh`
|
|
||||||
|
|
||||||
`fix_venv()` 同时检查 `python3` 和 `pip` 是否可用:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
fix_venv() {
|
|
||||||
if [ -x "$VENV_DIR/bin/python3" ] && "$VENV_DIR/bin/python3" --version &>/dev/null && \
|
|
||||||
[ -x "$VENV_DIR/bin/pip" ] && "$VENV_DIR/bin/pip" --version &>/dev/null; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
# ... 重建 venv ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `/opt/agent/refresh_agents.sh`
|
|
||||||
|
|
||||||
安装依赖前验证 `pip` 是否可执行,不可执行则自动重建 venv:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
VENV_DIR="/opt/agent/venv"
|
|
||||||
if [ -x "$VENV_DIR/bin/pip" ] && "$VENV_DIR/bin/pip" --version &>/dev/null; then
|
|
||||||
"$VENV_DIR/bin/pip" install -r "$AGENTS_DIR/requirements.txt" -q 2>&1 | tail -3
|
|
||||||
else
|
|
||||||
log "venv pip 不可用,重建虚拟环境..."
|
|
||||||
sys_python=$(command -v python3.12 2>/dev/null || command -v python3 2>/dev/null || command -v python 2>/dev/null || true)
|
|
||||||
if [ -z "$sys_python" ]; then
|
|
||||||
log "错误: 找不到系统 Python"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
"$sys_python" -m venv --clear "$VENV_DIR"
|
|
||||||
"$VENV_DIR/bin/pip" install -r "$AGENTS_DIR/requirements.txt" -q 2>&1 | tail -3
|
|
||||||
log "虚拟环境重建完成"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 服务器内存只有 2G,前端构建不在服务器执行,由本地 `deploy_agents.sh` scp 上传 `static/dist/`
|
|
||||||
- 以后部署不要再从 macOS 复制 venv 到服务器
|
|
||||||
- 如果 pip 包版本升级引入破坏性变更(如 `openai>=2.40` 强制要求 `api_key`),需要同步修复代码
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
name: server_frontend_build_oom
|
|
||||||
description: 当服务器执行前端构建失败、OOM、或需要部署前端时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 服务器前端构建 OOM
|
|
||||||
|
|
||||||
## 问题
|
|
||||||
服务器(2G 内存 Linux)执行 `npm run build` 时因内存不足导致 OOM,进程被 kill,服务器可能 SSH 超时无响应。
|
|
||||||
|
|
||||||
## 解决方案
|
|
||||||
**不在服务器上构建前端。** 在本地(macOS,充足内存)构建,然后 scp 上传 `static/dist/`。
|
|
||||||
|
|
||||||
## 操作步骤
|
|
||||||
```bash
|
|
||||||
# 本地构建
|
|
||||||
cd agents && npm run build
|
|
||||||
|
|
||||||
# 打包上传
|
|
||||||
tar czf /tmp/static_dist.tar.gz static/dist/
|
|
||||||
scp /tmp/static_dist.tar.gz root@59.110.19.30:/tmp/
|
|
||||||
ssh root@59.110.19.30 "cd /opt/agent/agents && tar xzf /tmp/static_dist.tar.gz"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 已落实到脚本
|
|
||||||
- `deploy_agents.sh`(本地):自动本地 build + 上传
|
|
||||||
- `refresh_agents.sh`(服务器):已移除 npm build 步骤,注释说明原因
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
name: slash_menu_highlight_animation
|
|
||||||
description: 当修改/菜单选中高亮动画时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
## / 菜单选中高亮动画 — 设计教训
|
|
||||||
|
|
||||||
### 最终方案
|
|
||||||
- 提取 `skill-slash-menu-wrapper` 外壳层,高亮条(`.skill-slash-menu__highlight`)放在 wrapper 内但**滚动容器外**,用 `position: absolute` 相对于 wrapper 定位
|
|
||||||
- 用一个 `requestAnimationFrame` 循环**同时驱动 scrollTop 和 slashHighlightTop**,相同 easing(easeOutCubic)、相同时间(180ms),帧帧同步
|
|
||||||
- 不使用 CSS transition、scroll-behavior: smooth、scroll 事件
|
|
||||||
|
|
||||||
### 踩过的坑
|
|
||||||
1. **高亮条必须在滚动容器外**:放在内部会随内容滚动,钉死在中间的效果无法实现
|
|
||||||
2. **CSS transition + scroll-behavior: smooth 无法精确同步**:easing 曲线和时间窗口不完全匹配,导致运动快慢不一致
|
|
||||||
3. **scroll 事件有盲区**:当 scrollTop 不变(边界、所有项可见)时不触发,导致高亮条卡死
|
|
||||||
4. **底部 5px 偏差**:`maxScroll` 不是 step(43px) 的整数倍,用 `visibleRows` 推算永远无法精确对齐。正确做法:`highlightTop = gap + index * step - targetScroll`,公式本身就保证了精确对齐
|
|
||||||
|
|
||||||
### 中间区域"固定"原理
|
|
||||||
高亮条和列表以相同距离(step)、相同时间(180ms)、相同 easing 反向运动,视觉上高亮条钉在屏幕中间不动,只有列表在滚。到达滚动极限时,高亮条才开始实际移动。
|
|
||||||
|
|
||||||
### 已选中项 hover 不叠加
|
|
||||||
`.skill-slash-item--active:hover` 设为 `background: transparent`,避免高亮条 + hover 两层背景叠加。
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
name: sub_agent_network_permission
|
|
||||||
description: 当需要理解或调整子智能体在受限网络下的行为时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 子智能体进程网络权限设计
|
|
||||||
|
|
||||||
## 问题
|
|
||||||
当用户把「网络权限」设置为受限(`HOST_SANDBOX_NETWORK_PERMISSION=restricted`)时,创建子智能体会失败,子智能体进程内报 `fetch failed (ENOTFOUND)`。原因是子智能体进程本身也被加上了与工具命令相同的宿主机沙箱,导致它无法解析模型 API 域名、无法调用模型接口。
|
|
||||||
|
|
||||||
## 解决方案
|
|
||||||
子智能体进程使用独立的网络权限配置 `SUB_AGENT_HOST_NETWORK_PERMISSION`(默认 `full`),与工具级 `HOST_SANDBOX_NETWORK_PERMISSION` 解耦。
|
|
||||||
|
|
||||||
- 子智能体本身需要调用模型 API,因此默认拥有完整网络,避免在受限网络下直接无法启动。
|
|
||||||
- 主进程对普通 `run_command` / `terminal_input` 等命令单独施加 `HOST_SANDBOX_NETWORK_PERMISSION` 沙箱,这部分限制不受影响。
|
|
||||||
- 子智能体内部执行的 `run_command` 目前不额外加沙箱(macOS 的 `sandbox-exec` 不支持嵌套沙箱,无法在同一个子智能体进程内再套一层)。
|
|
||||||
|
|
||||||
## 配置项
|
|
||||||
- `config/sub_agent.py` 新增 `SUB_AGENT_HOST_NETWORK_PERMISSION`,可通过环境变量覆盖,默认 `full`。
|
|
||||||
- `modules/sub_agent_manager.py` 在构建宿主机沙箱启动计划时使用该配置,而不是直接使用 `HOST_SANDBOX_NETWORK_PERMISSION`。
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
- 若未来需要在子智能体内部也对命令做网络限制,必须采用非嵌套方案(例如由主进程提供本地代理/执行服务,或让子智能体通过 localhost 调用主进程代理后再外出)。不能简单在子智能体里再次调用 `sandbox-exec`。
|
|
||||||
- 在 Docker/容器模式下,网络由容器自身策略控制,上述配置不影响容器行为。
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
---
|
|
||||||
name: sub_agent_python_rewrite
|
|
||||||
description: 当需要了解子智能体当前实现、修改子智能体代码、或排查子智能体相关问题时,应该索引本记忆
|
|
||||||
---
|
|
||||||
|
|
||||||
# 子智能体现状(Python 主进程内协程)
|
|
||||||
|
|
||||||
## 核心变更
|
|
||||||
|
|
||||||
子智能体已经从 **Node.js 子进程**(`easyagent/src/batch/index.js`)彻底改为 **Python 主进程内协程**。
|
|
||||||
|
|
||||||
## 代码位置
|
|
||||||
|
|
||||||
所有子智能体专用代码集中在 `modules/sub_agent/` 包内:
|
|
||||||
|
|
||||||
```
|
|
||||||
modules/sub_agent/
|
|
||||||
├── __init__.py # 导出 SubAgentManager
|
|
||||||
├── manager.py # 任务调度、创建、终止、状态查询
|
|
||||||
├── task.py # LLM 循环、模型调用、工具执行驱动
|
|
||||||
├── toolkit.py # 8 个工具定义、工具结果格式化、模型配置解析
|
|
||||||
├── prompts.py # 用户消息和系统提示词构建
|
|
||||||
├── tools.py # search_workspace、read_mediafile 本地实现
|
|
||||||
├── state.py # 状态加载、保存、刷新、超时、僵尸清理 Mixin
|
|
||||||
├── stats.py # 统计摘要与结果消息组装 Mixin
|
|
||||||
└── creation.py # 创建参数校验、任务 ID 生成、交付目录解析 Mixin
|
|
||||||
```
|
|
||||||
|
|
||||||
## 执行机制
|
|
||||||
|
|
||||||
- 子智能体作为 `asyncio.Task` 在独立的后台事件循环线程中运行(避免与 Flask-SocketIO threading 模式冲突)。
|
|
||||||
- 所有实际工具调用都通过 `WebTerminal.handle_tool_call()` 执行,因此**自然复用主进程的宿主机沙箱 / Docker 容器链路**。
|
|
||||||
- 子智能体自身不再被整体沙箱限制网络(因为它需要调用模型 API),网络调用走 `utils.api_client.DeepSeekClient`。
|
|
||||||
- 权限固定为 `unrestricted`,但工具执行本身受主进程沙箱/容器/审批链路约束。
|
|
||||||
|
|
||||||
## 模型配置
|
|
||||||
|
|
||||||
- 子智能体模型独立配置,不跟主模型合并。
|
|
||||||
- 配置文件:`~/.astrion/astrion/config/sub_agent_models.json`(由 `SUB_AGENT_MODELS_CONFIG_FILE` 指定)。
|
|
||||||
- 默认读取该文件中的 `default_model` 和 `models` 列表。
|
|
||||||
|
|
||||||
## 可用工具(8 个)
|
|
||||||
|
|
||||||
1. `read_file` - 读取/搜索/抽取文件
|
|
||||||
2. `write_file` - 创建或覆盖文件(支持 append)
|
|
||||||
3. `edit_file` - 精确字符串替换,支持 `replacements` 数组和 `replace_all`
|
|
||||||
4. `run_command` - 执行命令(走主进程沙箱/容器)
|
|
||||||
5. `web_search` - 网络搜索
|
|
||||||
6. `extract_webpage` - 网页内容提取
|
|
||||||
7. `search_workspace` - 工作区搜索
|
|
||||||
8. `read_mediafile` - 读取图片/视频文件(base64 返回)
|
|
||||||
|
|
||||||
## easyagent/ 目录状态
|
|
||||||
|
|
||||||
- `easyagent/` 是旧的 Node.js 子智能体实现,目前**暂时保留但已不再使用**。
|
|
||||||
- 主项目 Python 代码没有任何地方引用 `easyagent/`。
|
|
||||||
- 是否删除待后续决策。
|
|
||||||
|
|
||||||
## 入口点
|
|
||||||
|
|
||||||
- 主项目创建子智能体:`modules.sub_agent.manager.SubAgentManager.create_sub_agent()`
|
|
||||||
- `core/main_terminal.py` 中通过 `SubAgentManager.set_terminal(self)` 注入 WebTerminal 引用。
|
|
||||||
|
|
||||||
## 回归测试
|
|
||||||
|
|
||||||
- 测试脚本:`test/test_sub_agent_regression.py`
|
|
||||||
- 覆盖:write_file、edit_file(多处替换 + replace_all)、search_workspace、read_mediafile、terminate
|
|
||||||
|
|
||||||
## 已删除/清理的内容
|
|
||||||
|
|
||||||
- `config/sub_agent.py` 中已删除 `SUB_AGENT_HOST_NETWORK_PERMISSION` 临时配置。
|
|
||||||
- `easyagent/src/batch/index.js` 已删除(旧子进程入口)。
|
|
||||||
- `AGENTS.md` 和 `CLAUDE.md` 中关于子智能体的描述已更新为当前实现。
|
|
||||||
Loading…
Reference in New Issue
Block a user