{"owner":"xerrors","repo":"Yuxi","hasSkills":true,"totalSkillsCount":19,"totalTokensCount":29302,"categories":["root-instruction","claude-rule","anthropic-skill","subagent-persona","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md","backend/AGENTS.md","backend/package/yuxi/agents/skills/buildin/deep-research/SKILL.md","backend/package/yuxi/agents/skills/buildin/html-preview/SKILL.md","backend/package/yuxi/agents/skills/buildin/image-gen/SKILL.md","backend/package/yuxi/agents/skills/buildin/knowledge-base/SKILL.md","backend/package/yuxi/agents/skills/buildin/mysql-reporter/SKILL.md","docs/AGENTS.md","docs/agents/agent-backend-development.md","docs/agents/agent-evaluation.md","docs/agents/agents-config.md","docs/agents/mcp-integration.md","docs/agents/middleware.md","docs/agents/skills-management.md","docs/agents/subagents-management.md","docs/agents/tools-system.md","packages/yuxi-cli/README.md","web/AGENTS.md"],"skills":{"AGENTS.md":"# Yuxi Agent 开发约定\n\nYuxi 是基于 LangGraph、FastAPI、Vue 和多种持久化服务构建的知识库与多智能体平台。Docker Compose 是开发拓扑的事实来源；修改不熟悉的模块前先阅读 [ARCHITECTURE.md](ARCHITECTURE.md)，再用符号搜索确认真实实现。\n\n## 每次任务先加载什么\n\n- [ARCHITECTURE.md](ARCHITECTURE.md)：稳定边界、主链路和架构不变量。\n- [Yuxi Spec Loop](docs/develop-guides/spec-loop.md)：非平凡变更从提案、证据到收敛的流程。\n- [工程信任系统](docs/develop-guides/engineering-trust.md)：语义 Owner、证据、决策记录、派生审计和 gate 规则。\n- [测试规范](docs/develop-guides/testing-guidelines.md)：unit、integration、E2E 的职责与命令。\n- [贡献指南](docs/develop-guides/contributing.md)：分支、独立 Review、commit 和 PR 流程。\n- [并行工作树与隔离运行环境](docs/develop-guides/parallel-worktree-environments.md)：同时运行多个分支、复用长期数据或处理 Schema 不兼容时加载。\n- 用户在当前任务中的明确要求优先于本文件；修改 `backend/`、`web/` 或 `docs/` 时同时遵循该子树的 `AGENTS.md`。子树规则只补充本目录，不复制回根文件。\n\n## 任务与决策\n\n1. 开始实现前，把请求压缩为可验证的目标、非目标和验收主张；多步任务先给出每步带验证方式的简短计划。\n2. 假设显式记录后继续；只有当不同解释会改变验收结果、数据、安全或外部状态时才阻塞询问，并把候选解释一并列出而不是默默选择其一。存在明显更简单的方案时，先说明取舍再动手。\n3. 把\"可以\"\"也可以\"\"类似这样\"\"例如\"当作简单方向，不是设计更大机制、配置项或兼容层的许可。\n4. 在真实语义 Owner 处闭合受影响的工程主张：源码、数据约束或当前契约拥有事实；独立 oracle 与负向案例证明它；实际 workflow 或可问责 Reviewer 产生拒绝后果。不要建立可独立编辑的中央主张清单或 claim ID 体系。\n5. 非平凡变更必须新增或更新一份 tracked [决策记录](docs/develop-guides/decisions/README.md)。记录问题、当前决定、真实替代项、后果和验证，不保存推理流水账或实现过程叙事。\n6. `docs/vibe/` 只用于本地临时计划，被 Git 忽略，不是组织记忆，也不能作为已完成事实的唯一来源。\n7. 只修改验收标准需要的范围；不顺手重构、格式化或添加想象中的配置、兼容层和扩展点。\n\n## 不能破坏的系统事实\n\n- HTTP 路由保持薄；用例流程属于 `yuxi.services`，持久化查询属于 `yuxi.repositories`。\n- 普通请求先在 PostgreSQL 中持久化 Message 和 AgentRunRequest；只有 ready FIFO 队头创建 AgentRun，且每次投递 ARQ 前 owning transaction 都已提交。Redis 负责投递、短期事件、取消和缓存，不拥有最终业务状态。\n- 同一用户、Agent、线程的普通请求按 FIFO 串行派发；Request 和 Run 是不同状态模型。\n- AgentRun 的输出、事件、artifact 和错误必须绑定同一 request/run；禁止从相邻 Run 猜测结果。\n- 非终态 Run 必须有明确执行 Owner、lease/heartbeat 或等价机制，以及崩溃后的可观察结局。\n- `/api/system/health` 只表达进程 liveness；接流量前置条件由 `/api/system/ready` 证明，业务正确性仍由真实链路测试证明。\n- LangGraph checkpoint 只使用 PostgreSQL；API、worker 与 Agent 不提供本地后端选择或静默降级。\n- 权限在后端依赖与 repository 可见性查询处最终执行；前端守卫、prompt、schema omission 和 UI 隐藏不是授权边界。\n- Shipping 启动、路由注册和能力发现始终包含知识库、图谱与评估能力；附件解析入口只在真实解析动作发生时惰性加载 parser。\n- 沙盒虚拟路径、对象 URL 和宿主机路径不可混用；所有用户路径必须在 owning filesystem boundary 校验。\n\n## 证据规则\n\n- Agent 的完成报告、HTTP 200、日志关键词或 mock 调用次数都不是最终事实；重新读取数据库、文件、对象、DOM 或协议结果。\n- 每个新 guard 都要有一个能恢复目标缺陷并使其在正确原因上失败的负向测试。\n- 测试从最小相关集合开始，再按风险扩大；不要用 unit 结果替代真实 PostgreSQL、真实 HTTP、worker 或浏览器语义。\n- Expected output、snapshot 和 fixture 只能显式更新并审阅；CI 不得一边生成 oracle 一边验证 oracle。\n- 实际执行命令、结果和未执行原因写入 PR；未验证不能写成通过。\n\n提交前在仓库根目录至少运行：\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\n```\n\n改动面按下表选择最低证据，并按升级条件扩大；不要用低层级结果替代高风险语义。\n\n| 改动面 | 最低证据 | 何时升级 |\n|---|---|---|\n| 纯 Python / JS 逻辑 | 相关 unit，断言业务结果 | 触及数据库、缓存或文件副作用时补 integration |\n| API / 权限 / 持久化 | 真实 HTTP integration | 跨 worker、队列或用户主链路时补 E2E |\n| Run / FIFO / SSE / 沙盒 / 恢复 | E2E，验证最终状态与产物 | 依赖外部可选服务时记录环境与未验证范围 |\n| 前端交互 | lint + unit（[web/AGENTS.md](web/AGENTS.md)） | 行为关键时补 build 与真实页面验证 |\n| 文档与导航 | 相对链接检查 + docs build | 公开行为变化时与代码验证一起执行 |\n\n完整命令由 [测试规范](docs/develop-guides/testing-guidelines.md) 维护。\n\n## 实现与安全\n\n- 使用满足验收标准的最小、线性实现。抽象、状态机、fallback、兼容路径和依赖都要有当前 consumer 与 Owner；仓库外用户、持久数据和部署承诺也算 consumer。\n- 实现明显长于问题本身，或一半行数可以清楚表达同样行为时，先简化再提交；自检\"高级工程师是否会认为这段代码过度设计、过度防御或过于零碎\"。\n- 不可信输入只在 parser、配置、模型/tool JSON、持久化、worker、process、wire 和用户路径等真实信任边界校验；授权与隔离在产生副作用的 executor/repository fail-closed，直接或替代调用路径都不能绕过。\n- 预设条件不成立时显式失败。可选能力可以降级，但必须结构化、可观察，且不把未就绪伪装为成功。\n- 新增函数/类使用简洁中文 docstring；注释只解释非显然约束、时序、Owner 和安全用法，不复述代码。\n- 不输出或提交 `.env`、账号、Token、用户数据、运行目录和构建产物。\n- 文件以一个换行结尾；提交前运行 `git diff --check`。\n\n## Review 与交付\n\n所有代码变更在 commit 前必须由不继承开发上下文的全新 Reviewer Agent 审查完整需求、diff、测试和规范；修复所有影响功能、边界、证据可信度或认知负担的问题。提交信息使用中文 Conventional Commit，PR 使用仓库模板，以自然语言列出受影响主张、语义 Owner、证据、风险和未验证范围，不引用中央 claim ID。\n","CLAUDE.md":"AGENTS.md","backend/AGENTS.md":"# Backend 约定\n\n本目录承载 FastAPI 适配层、业务服务、repositories、Agent runtime 与持久化实现。先阅读根 [AGENTS.md](../AGENTS.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n## 边界与所有权\n\n- `server/routers` 只处理 HTTP 模型、认证依赖、状态码和响应装配；跨 repository 的用例进入 `package/yuxi/services`。\n- PostgreSQL 是 Request、Run、Message、权限和业务终态的 Owner；Redis/ARQ 是投递与短期事件平面。\n- 写入事实、提交事务、发布队列/事件的顺序必须显式；通知不能早于 owning transaction 的 commit point。\n- 跨 repository 用例只有一个事务 Owner；需要经 HTTP 返回的一次性 secret 必须可由幂等请求安全重放，不能先不可逆消费再祈望响应送达。凭据撤销必须保留足以阻止同一幂等请求复活 secret 的 tombstone。\n- parser、HTTP、模型/tool JSON、持久化、worker、process、wire 和用户路径是运行时校验边界；已由 Python 类型和同进程调用保证的内部值不重复 hostile validation。\n- 核心启动依赖失败要阻止 readiness；可选集成降级要记录组件名、失败类型与当前能力。\n- AgentRun 的状态转换、lease、输出和终态投影由 repository/service 统一维护，调用方不得直接拼装并行真相。\n\n## 实现\n\n- Python 使用 3.12+ 语法；保持主流程线性，优先早返回和清晰主路径，避免细碎 helper、静默 fallback、一次性抽象、不必要的嵌套和多层调用链。\n- 本次新增或实质修改的代码遵循向下规则：同一模块或类内，公开、高层方法在上，专用实现细节在后；旧代码布局不豁免新改动。可调整直接相关定义的位置，不重排整个文件；基类、装饰器、默认值和模块初始化等定义时依赖优先满足 Python 执行顺序。Reviewer 核对局部阅读顺序。\n- 拆函数用于明确复用、隔离副作用或实质降低认知负担；实现细节遮蔽主流程时，即使只调用一次也可提取。仅转发参数或包装表达式且不增加语义的 helper 通常内联。\n- 常量和具名值放在最小合理作用域：跨函数复用、协议标识或配置约束用模块级常量；仅服务单个函数的局部规则留在函数内。\n- 命名表达业务意图；不导入其他模块下划线开头的私有标识，确有共享需求时改为公开命名。\n- 删除本次修改产生的未使用 import、变量、函数和分支；不清理修改前已存在的无关死代码，发现坏味道只说明不擅自处理。\n- 小型状态、进度或摘要需求直接读取来源并返回最小结果，不重建事件流或调试视图。\n- 新增函数/类使用简洁中文 docstring；不同语义的代码段之间留空行保持可读。异常只在当前层能增加稳定语义、执行清理或决定策略时捕获。\n- Schema 演进必须幂等、可在现有数据上执行，并有真实 PostgreSQL 测试；不可逆操作明确数据影响。\n\n## 验证\n\n```bash\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\ndocker compose exec api uv run --group test pytest test/integration\ndocker compose exec api uv run --group test pytest test/e2e -m e2e\ndocker compose exec api uv run ruff check package\ndocker compose exec api uv run ruff format package --check\n```\n\n并发、事务、锁、lease、schema 与 PostgreSQL 专属语义必须在真实 PostgreSQL 上验证；API 行为通过真实 HTTP integration 证明；关键 Run/worker/文件副作用通过 E2E 证明。\n","backend/package/yuxi/agents/skills/buildin/deep-research/SKILL.md":"---\nname: deep-research\ndescription: \"深度研究编排方法论：澄清范围、拆解规划、并行调度子智能体调研、对抗式核验、综合成带引用的结构化报告。当任务需要多来源、可追溯、需事实核查的深度研究时使用此技能。\"\nversion: \"2026.09.19\"\ntool_dependencies: [\"web_search\", \"present_artifacts\"]\n---\n\n# 深度研究技能\n\n当任务目标是产出**多来源、可追溯、经过核验**的深度研究结论（科研综述、行业/竞品调研、技术选型、专题分析等）时，使用此技能组织整个研究过程。本技能的核心是**编排**：你负责整体把控与子智能体调度，把繁重的检索与核验工作派发出去，自己专注规划与综合。\n\n## 可用子智能体\n\n通过 `subagent_start` 工具派发，记录返回的 run_id；需要结果时调用 `subagent_await`。互不依赖的子任务先全部派发，再等待结果：\n\n- `research-explorer`（调研探索员）：围绕一个明确子问题做多轮网页/知识库检索，返回按要点组织、带 `<cite>` 引用的结构化发现。**这是主力，按子问题并行多开。**\n- `fact-verifier`（事实核查员）：对给定的关键论断做对抗式核验，逐条给出 支持 / 存疑 / 反驳 + 依据来源 + 置信度，并标注冲突。\n\n## 编排流程\n\n### 1. 澄清范围\n问题不明确时，先用 `ask_user_question` 补充 2-3 个关键问题（研究目标、受众、范围边界、地域/时效、输出语言与形式），对齐验收标准后再开工。已经清晰的任务不要反复追问。\n\n### 2. 规划拆解\n用 `write_todos` 把研究目标拆成**可独立调研**的子问题，每个子问题写明产出标准（要回答什么、需要哪类证据）。子问题应正交、覆盖完整，避免重叠或遗漏关键角度。\n\n### 3. 并行派发调研\n- 把互不依赖的子问题用**多个 `subagent_start` 调用并行**派发给 `research-explorer`。\n- 每次派发在 `description` 中写清：子问题目标、已知上下文、期望输出格式（要点 + `<cite source=\"$URL\" type=\"url\">$INDEX</cite>` 引用 + 参考来源列表）。\n- 何时派发 vs 自己直检：子问题复杂、需多轮检索、可隔离上下文、可并行时一律派发子智能体；仅在澄清范围、补一两个零散事实、或快速校正方向时才自己少量直接检索。\n- 子问题之间有依赖时，先派发前置子问题，拿到结果后再派发后续。\n\n### 4. 核验关键结论\n对**影响最终结论的关键论断**、数字、以及子智能体之间相互冲突的发现，派发 `fact-verifier` 做对抗式核验。要求其默认倾向「证据不足即标注存疑」。核验未通过的结论不要写进正文，或必须明确降级标注。\n\n### 5. 综合成稿\n证据充分后，由你统一综合为结构化报告，**不要**简单拼接子智能体返回的原文。根据研究主题组织内容，围绕论证而不是资料堆砌。\n\n### 6. 停止准则\n信息饱和、或确认无法获取更多有效信息即停。明确标注证据缺口与不确定性，不臆断、不编造来源。\n\n## 引用\n\n- 子智能体返回中间证据时沿用 `<cite source=\"$URL\" type=\"url\">$INDEX</cite>`；最终报告中的关键结论、数据和观点用指向原始来源的普通 `<a href=\"...\">` 链接就近标注。\n- 引用用户附件或知识库时标明文件名或路径。\n\n## 输出\n\n- 最终交付的是一份可直接使用的报告，而不是「我打算怎么研究」。\n- 不要外泄中间推理过程、原始检索日志，也不要把待办清单原样输出成正文。\n- 报告语言与用户提问语言一致，使用正式、克制、可复核的书面表达。\n- 用户没有指定格式时，默认把完整报告写入当前 Workdir 的 `outputs/` 目录，保存为一个 `.html` 文档；不要把完整报告直接铺在聊天正文里。用户明确要求 Markdown、纯文本、JSON 或其他格式时，以用户要求为准。\n- 从系统提示读取当前 Project Workdir 的**绝对虚拟路径**，用 `write_file` 写入 `<当前 Workdir 绝对路径>/outputs/<文件名>.html`。不要把 `outputs/report.html`、宿主机路径或 `/outputs/report.html` 传给文件工具。\n- 写入成功后，把 `write_file` 返回的同一绝对路径传给 `present_artifacts`。最终聊天回复只需简要说明报告已完成并提示交付物，不要重复报告正文。\n\n默认 HTML 应简洁、优雅并专注阅读，同时给模型保留版式判断空间：\n\n- 页面与正文宽度随视口自适应。宽屏可以使用侧栏目录帮助浏览章节，窄屏隐藏或折叠侧栏，让正文自然占满可用宽度。\n- 可以按内容需要引用外部样式、字体、图片或其他公开资源；图片应服务于研究内容，而不是纯装饰。\n- 不提供固定 HTML 模板；根据研究主题选择合适的章节、图表、表格和视觉层级，整体保持克制、清晰，不做复杂浮夸的展示页。\n","backend/package/yuxi/agents/skills/buildin/html-preview/SKILL.md":"---\nname: html-preview\ndescription: \"使用 Markdown `html:preview` 围栏输出轻量静态 HTML/CSS 可视化。当普通 Markdown 难以清晰表达数值对比、层级关系、流程、时间线、关键指标或布局示意，或用户明确要求可视化 HTML 预览时使用。询问 HTML 源码、教程示例或可复制代码时不要使用。\"\nversion: \"2026.07.23\"\n---\n\n# HTML Preview\n\n将 Markdown 作为回答主体。只有可视化能明显降低理解成本时，才补充一个\n`html:preview` 静态组件；标题、列表、表格或代码块已经足够清楚时，继续使用普通 Markdown。\n\n按以下顺序输出：\n\n1. 先用普通 Markdown 写结论和必要解释。\n2. 顶格写开始围栏 ```` ```html:preview ````。围栏最多只能有 3 个前导空格，否则前端会把它当作源码。\n3. 在围栏内写自包含的静态 `<style>` 和语义化 HTML。\n4. 顶格写结束围栏 ```` ``` ````。\n5. 在围栏后用普通 Markdown 补充背景、风险、完整明细和来源。\n\n## 内容边界\n\n- 只输出静态 HTML/CSS，不编写 JavaScript，不使用表单、iframe、object 或 embed。\n- 可以引用公开、稳定、无需登录的 HTTPS 图片或字体，但核心信息必须在外链失败时仍然可读。\n- 不引用内网、临时地址、鉴权资源或包含用户凭证的链接。\n- 不把组件当成完整网页或正文容器；不要制作导航栏、页脚、登录态、复杂按钮、营销页 Hero 或多屏页面。\n- 不在组件中放长段叙事、完整报告、长表格或长列表。背景、推理、风险、明细和来源放在普通 Markdown 中。\n- 用户询问 HTML 源码、教程示例或需要复制代码时，使用普通 `html` 代码块，不使用 `html:preview`。\n\n## 布局约束\n\n- 面向默认 `800px × 360px` 尺寸设计，并允许容器宽度变化；前端最高显示到 `700px`，超出后在预览内部滚动。\n- 使用 `max-width: 100%`、`box-sizing: border-box`、弹性网格、换行和适度间距保持响应式。\n- 不写死整体画布高度。核心信息应在默认尺寸内无需滚动即可读；放不下时减少内容。\n- 最多展示 1 个短标题、3–5 个关键指标或一组简短对比。数据超过 6 项时，汇总为趋势、区间、异常点或 Top 3。\n- 文字以短标签、数字、单位、状态词和极短备注为主；单条说明尽量不超过 20 个中文字符。\n- 外层已经提供圆角、边框和裁切。不要再给最外层添加卡片壳、页面背景、大圆角、阴影、厚边框或额外外边距。\n- 保持克制和可读，优先使用紧凑指标组、摘要表、对比条、状态标签、时间轴或简单关系图。\n\n## 失败处理\n\n如果内容无法在上述静态、安全和篇幅边界内表达，改用普通 Markdown，并明确说明限制。不要输出残缺围栏、不可访问资源或依赖脚本才能理解的预览。\n","backend/package/yuxi/agents/skills/buildin/image-gen/SKILL.md":"---\nname: image-gen\ndescription: \"在 Agent 沙盒中生成图片并保存到 outputs。当用户要求生成图片、海报、插画、文生图，或指定 Qwen-Image、其它兼容图片生成接口时使用此技能。\"\nversion: \"2026.06.02\"\ntool_dependencies: [\"present_artifacts\"]\n---\n\n# 图片生成技能\n\n当用户要求生成图片、海报、插画、文生图，或明确提到 Qwen-Image 时，使用此技能组织图片生成流程。\n\n## 默认生成接口\n\n默认使用 SiliconFlow 的 Qwen-Image 接口：\n\n- Endpoint: `POST https://api.siliconflow.cn/v1/images/generations`\n- Model: `Qwen/Qwen-Image`\n- 默认参数：\n  - `negative_prompt`: `\"\"`\n  - `num_inference_steps`: `20`\n  - `guidance_scale`: `7.5`\n\n调用外部接口时，必须在 Agent 沙盒执行环境中读取 `SILICONFLOW_API_KEY`。不要依赖后端进程环境变量。\n\n## 操作流程\n\n1. 明确用户要生成的图片内容、风格、尺寸或约束；信息不足但不影响生成时，使用合理默认值，不要反复追问。\n2. 使用可用的执行工具在沙盒中运行脚本，调用图片生成接口，传入用户需求整理后的 `prompt`，并按需传入 `negative_prompt`、`num_inference_steps`、`guidance_scale`。\n3. 从生成接口响应中读取图片地址，默认路径为 `images[0].url`。\n4. 在同一个沙盒脚本中用 `Authorization: Bearer $SILICONFLOW_API_KEY` 下载该图片地址；如果接口直接返回 base64，则直接解码保存。\n5. 将最终图片保存到当前 Workdir 的 `outputs/` 下，例如 `outputs/generated-image.png`。\n6. 调用 `present_artifacts`，传入保存后的 outputs 虚拟路径，让前端展示图片产物。\n7. 最终回复简要说明图片已生成，不要把外部临时 URL 当作最终结果展示。\n\n## 脚本示例\n\n可根据用户需求调整 prompt 和输出文件名：\n\n```python\nimport os\nimport requests\nfrom pathlib import Path\n\napi_key = os.environ[\"SILICONFLOW_API_KEY\"]\nprompt = \"根据用户需求整理后的图片提示词\"\n\nresponse = requests.post(\n    \"https://api.siliconflow.cn/v1/images/generations\",\n    headers={\"Authorization\": f\"Bearer {api_key}\", \"Content-Type\": \"application/json\"},\n    json={\n        \"model\": \"Qwen/Qwen-Image\",\n        \"prompt\": prompt,\n        \"negative_prompt\": \"\",\n        \"num_inference_steps\": 20,\n        \"guidance_scale\": 7.5,\n    },\n    timeout=120,\n)\nresponse.raise_for_status()\nimage_url = response.json()[\"images\"][0][\"url\"]\n\nimage_response = requests.get(\n    image_url,\n    headers={\"Authorization\": f\"Bearer {api_key}\"},\n    timeout=120,\n)\nimage_response.raise_for_status()\n\noutput_path = Path(\"outputs/generated-image.png\")\noutput_path.parent.mkdir(parents=True, exist_ok=True)\noutput_path.write_bytes(image_response.content)\nprint(output_path.as_posix())\n```\n\n## 多模型扩展\n\n如果用户指定其它图片生成模型或兼容接口，可以按该接口的协议先生成图片。只要最终拿到图片 bytes 或 base64，就保存到当前 Workdir 的 `outputs/`，再调用 `present_artifacts` 展示。\n\n## 关键约束\n\n- 不要把外部生成接口返回的临时 URL 当作最终结果直接展示给用户。\n- 不要调用后端 MinIO 上传工具；图片生成和下载都应在沙盒内完成。\n- 如果 `SILICONFLOW_API_KEY` 缺失，应明确提示用户需要在 Agent 沙盒环境变量中配置。\n- 保存到 outputs 后必须调用 `present_artifacts`，否则前端不会自动展示生成图片。\n","backend/package/yuxi/agents/skills/buildin/knowledge-base/SKILL.md":"---\nname: knowledge-base\nslug: knowledge-base\ndescription: \"使用 Yuxi 知识库进行检索、打开文档、文档内定位和查看思维导图。当用户需要基于已配置知识库回答问题、核验资料或引用文档内容时使用此技能。\"\nversion: \"2026.06.24\"\ntool_dependencies: [\"list_kbs\", \"query_kb\", \"find_kb_document\", \"open_kb_document\", \"get_mindmap\", \"search_file\", \"download_kb_file\"]\n---\n\n# 知识库技能\n\n当用户要求基于项目知识库、内部资料、上传入库文档或知识图谱相关内容回答问题时，使用此技能。\n\n## 可用工具\n\n- `list_kbs`：列出当前会话可访问且已启用的知识库。\n- `query_kb`：按 `kb_id` 在指定知识库中检索内容，返回 `file_id` 和相关片段。\n- `open_kb_document`：按 `kb_id` 和 `file_id` 打开文档原文窗口，适合查看更完整上下文。\n- `find_kb_document`：在已知文档内用关键词或正则定位段落。\n- `get_mindmap`：查看知识库思维导图结构。\n- `search_file`：按文件名关键词搜索知识库中的文件，支持指定知识库或跨知识库，返回文件列表与分页信息。\n- `download_kb_file`：按 `kb_id` 和 `file_id` 下载知识库原始二进制（pdf/docx/xlsx 等）到沙盒 `outputs` 目录，返回沙盒内可见的 `virtual_path`。当后续需要用代码读取原始文件结构（如 `openpyxl` 读 xlsx 单元格、`pdfplumber` 重新解析版面）时使用；`query_kb`/`open_kb_document` 只返回文本切片，无法满足需要文件对象的场景。\n\n## 操作流程\n\n1. 需要先确认当前会话有哪些知识库可用；不确定时调用 `list_kbs`。\n2. 针对用户问题选择最相关的知识库，使用 `query_kb` 检索。\n3. 检索结果的 `metadata.chunk_count == 1` 表示文件只有一个分片；除非需要原文行号或额外上下文，否则无需打开。大于 1 且当前片段不足以回答时，使用返回的 `file_id` 调用 `open_kb_document` 查看上下文。字段缺失或为 0 表示未知，不据此判断文档完整。\n4. 如果用户要求定位术语、指标、章节或原文证据，使用 `find_kb_document` 在候选文档内查找。\n5. 当用户关心知识库结构、文件分类或知识框架时，使用 `get_mindmap`。\n\n## 关键约束\n\n- 只能访问当前会话配置和用户权限允许的知识库。\n- 不要编造 `kb_id` 或 `file_id`；优先从 `list_kbs` 和 `query_kb` 的返回结果中获取。\n- 回答需要可追溯时，应说明依据来自哪个知识库、文件或检索片段。\n- Dify 等外部只读知识库可能只支持检索，不一定支持打开全文或文档内查找；遇到工具返回限制说明时，应如实告知用户。\n","backend/package/yuxi/agents/skills/buildin/mysql-reporter/SKILL.md":"---\nname: mysql reporter\nslug: mysql-reporter\ndescription: \"生成 MySQL 查询报表。当用户需要查询 MySQL 数据库并以报表形式展示结果时使用此技能，包括：统计销售数据、分析用户行为、生成业务报表、查询业务指标等。\"\nversion: \"2026.09.17\"\n---\n\n# MySQL 报表技能\n\n根据用户的指令，通过终端脚本访问 MySQL 数据库，根据查询结果构建报表。\n\n## 操作流程\n\n1. 理解用户的指令，明确报表的需求和目标\n2. 通过 terminal 进入技能目录：`cd /home/gem/skills/mysql-reporter`\n3. 使用 `uv run scripts/list_tables.py` 查看可用表；如果脚本提示缺少 MySQL 配置，按“环境变量缺失处理”回复用户\n4. 必要时用 `uv run scripts/describe_table.py --table 表名` 查看表结构\n5. 生成正确且高效的只读 SQL，通过 `uv run scripts/query.py --sql \"SQL语句\" --timeout 60` 执行查询并获取结果\n6. 使用 Markdown 表格呈现查询结果，说明关键指标、变化与结论\n\n## 环境变量缺失处理\n\n脚本只读取 Agent 沙盒中的环境变量，不读取后端 `.env` 或 Docker Compose 变量。必填变量包括：\n\n- `MYSQL_HOST`\n- `MYSQL_USER`\n- `MYSQL_PASSWORD`\n- `MYSQL_DATABASE`\n\n可选变量包括：\n\n- `MYSQL_PORT`：默认 `3306`\n- `MYSQL_DATABASE_DESCRIPTION`：数据库业务说明，用于辅助理解表和指标含义\n\n如果执行脚本时出现 `MySQL configuration missing required key`，不要继续猜测连接信息或编造报表。应明确告诉用户：需要在个人设置中的「沙盒环境变量」里配置缺失的 `MYSQL_*` 变量；保存后仅对新建沙盒生效，需要重新发起任务或新建会话后再执行。\n\n## 关键约束\n\n- 生成的 SQL 查询必须正确且高效，避免全表扫描\n- MySQL 操作必须通过本技能 `scripts/` 下的 CLI 脚本执行，不要调用平台内置 MySQL tools\n- 不要在报表或错误说明中输出 `MYSQL_PASSWORD` 等敏感环境变量的值，只能说明缺少哪些变量名\n- 只返回报表相关的结论，不要返回原始 SQL 查询语句\n\n## 允许的工具\n\n- terminal：执行 `scripts/list_tables.py`、`scripts/describe_table.py`、`scripts/query.py`\n- 网络检索工具：必要时补充背景信息\n","docs/AGENTS.md":"# 文档约定\n\n本目录包含用户教程、配置参考、机制详解、开发规范、决策记录、事故复盘和版本材料。仓库根 `AGENTS.md` 的系统不变量仍然适用；完整信息架构、写作标准与检查清单由[文档编写与维护规范](develop-guides/documentation-guidelines.md)拥有，本文件只保留进入 `docs/` 工作时必须执行的指令。\n\n## 层级与事实 Owner\n\n- 每个事实只有一个负责完整解释的 Owner；其他页面只保留完成当前任务需要的上下文并使用相对链接。先按[层级表](develop-guides/documentation-guidelines.md#信息架构与事实-owner)确定页面位置，再写内容。\n- `intro/` 拥有从零完成结果的教程，`advanced/` 拥有配置和运维参考，`agents/` 拥有 Agent 配置与扩展方法，`mechanisms/` 拥有运行机制、状态、权限、失败和源码定位。实质性混合内容必须拆页。\n- 当前系统边界和主链路属于仓库根 `ARCHITECTURE.md`；测试层级与命令属于[测试规范](develop-guides/testing-guidelines.md)；工程信任闭环属于[工程信任系统](develop-guides/engineering-trust.md)。不要在专题页复制这些完整规则。\n- 非显然取舍属于 `develop-guides/decisions/`，达到门槛的事故因果属于 `develop-guides/postmortems/`，已发布事实属于 changelog，未完成方向属于 roadmap。`docs/vibe/` 只用于被忽略的本地临时计划。\n- 源码、schema、Compose、数据约束和测试拥有可执行事实；外部 Wiki、旧 changelog、历史 PR 和 Agent 自述只能帮助定位，不能覆盖当前 Owner。\n\n## Agent 写作流程\n\n1. 读取根与当前子树指令、`ARCHITECTURE.md`、owning 文档和相关源码；非平凡信息架构或长期约束变化先创建 tracked proposed decision。\n2. 写明预定读者、问题、前置知识、目标、非目标和页面类型。教程必须有顺序结果；参考必须有明确查找范围；实质性混合内容先拆分。\n3. 用符号搜索沿入口 → service/executor → repository/持久化或发布点 → 用户/模型结果重建真实链路，同时核对权限、失败路径、可选能力和相关测试。\n4. 先列 Section 及每节唯一问题，按“概念 → 组件关系 → 状态/Owner → 权限/失败 → 源码定位”渐进；教程按完成任务所需的前置依赖排序。\n5. 一次最多撰写或重写一个 Section。完成后立即核对事实、相对链接、与相邻章节的重复及当前页面职责，再进入下一节。\n6. 先更新事实 Owner，再更新导航、索引和引用页。页面移动或拆分必须在同一变更中修复全部入站链接，不保留两个可独立编辑的副本。\n7. 提交前运行最小相关 gate，并由不继承开发上下文的全新 Reviewer 对照完整需求、源码、diff、测试和未验证范围审查。\n\n## 写作与维护\n\n- 使用直接、具体、可验证的现在时，写明执行者、条件、时序、结果、失败和后果。不要用“应该、一般、可能”掩盖未核实事实；无法核实时明确条件与 `Not run` 范围。\n- 优先用肯定句直接陈述职责、边界和结论。禁止先否定一个弱命题、再用转折强调目标命题的对举句式；删除空泛铺垫、宣传性形容词和替读者下判断的强调语。\n- 普通段落每段只用一个物理行并依靠编辑器软换行；代码块、表格和列表保留结构。一个段落包含多个独立规则时拆段，不用硬换行掩盖段落墙。\n- 不在当前说明中叙述“以前/现在/这次修改”、PR 过程、Review 对话、实现步骤或推理流水账。原因和取舍写入 decision，事故时间线写入 postmortem。\n- 不手写易漂移的完整文件、API、测试或配置清单；保留稳定分类并链接源码、schema、Compose 或生成 Owner。不要按函数逐行复述控制流。\n- 机制页必须覆盖适用范围、真实装配、状态与事实 Owner、权限/隔离、失败/恢复、可观察结果、配置边界和“源码定位与验证”；配置页保留参数、操作与排障，不复制机制全景。\n- 教程的关键步骤给出页面、状态、文件或协议等可观察结果；参考字段写清读取者、默认值、条件、限制和生效时机。实质性的教程与参考内容不能长期共居一页。\n- 图只用于三项以上关系、状态或时序，使用能由 VitePress 渲染的最小 Mermaid；图不能替代正文中的条件、异常和安全语义。尚未实现的方向不进入当前机制图。\n- 站内页面使用相对 Markdown 链接；站点外公开源码使用项目 GitHub `blob/main` 文件链接。禁止本地绝对路径、易漂移行号和只写文件名不说明职责的源码清单。\n- 示例只使用占位凭据、保留域名或假 ID；禁止写入 `.env`、Token、真实账号、用户数据、本地绝对路径或不可公开的内部地址。不要建议把 provisioner/数据库等管理凭据注入 Agent 沙盒。\n- 决策记录遵循[生命周期与格式](develop-guides/decisions/README.md)；implemented 记录使用现在时，不保留提案 checklist。用户可见完成项与 roadmap 不得同时声明同一状态。\n\n## 导航、预算与验证\n\n- 新增正式页面时更新 `.vitepress/config.mts` 的正确父级和阅读顺序；内部 `AGENTS.md`、decision 与 postmortem 不因位于 `docs/` 就自动成为用户导航入口。\n- `scripts/verify_engineering_contracts.py` 的 AGENTS 字符预算是 standing-order guardrail。超限时先下沉示例、背景和专题解释，再压缩重复；确需提高预算时同步更新 decision 和 gate，不能静默删掉必要约束或关闭检查。\n- 修改配置、API、状态、权限或机制说明时检查相关测试；文档构建只证明 Markdown、导航和链接有效，不证明语义正确。Reviewer 必须逐页对照当前源码、配置和风险相称的 oracle。\n- 构建失败要修正文档、锚点或导航，不扩大 `ignoreDeadLinks` 掩盖问题。报告只写实际执行的命令、结果和未验证范围。\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ncd docs && pnpm run build\ngit diff --check\n```\n","docs/agents/agent-backend-development.md":"# 开发智能体后端\n\n本页面向需要在 Yuxi 中新增或维护 Agent 后端的贡献者。它只讲代码装配；配置字段、权限和运行时上下文分别见[配置智能体](./agents-config.md)和[Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n## 新增预置角色\n\n仅改变提示词、模型或能力选择时，在 `backend/package/yuxi/agents/presets/` 新增一个 Python 文件并导出 `PRESET`；子智能体定义放在其 `subagents/` 子目录。发现逻辑递归读取文件，角色类型仍由 `backend_id` 决定：\n\n```python\nfrom yuxi.agents.presets import AgentPreset\n\nPRESET = AgentPreset(\n    slug=\"report-assistant\",\n    name=\"报告助手\",\n    description=\"根据材料整理报告。\",\n    backend_id=\"ChatbotAgent\",\n    context={\n        \"system_prompt\": \"根据用户材料生成结构清晰、来源可核对的报告。\",\n        \"skills\": [\"html-preview\"],\n    },\n)\n```\n\n子智能体使用 `backend_id=\"SubAgentBackend\"`，其类型由后端推导。`context` 复用[智能体配置](./agents-config.md)字段；长提示词可以直接使用 Python 多行字符串。角色模块不得执行外部操作。\n\nAPI 启动时按文件名发现所有非下划线开头的 Python 模块，校验定义类型、重复 slug 和后端存在性，再统一初始化数据库记录。新增角色无需修改注册清单、repository 或启动调用。模块导入失败、缺少 `PRESET`、重复 slug 或不存在的后端会阻止初始化成功。\n\n预置角色仅在 slug 不存在时创建，已有名称、提示词和能力配置保留；修改源码不会覆盖管理员定制。默认智能助手另行维护原有默认与共享约束。重启 API 后，在智能体管理页核对新增角色的名称、后端和能力配置；子智能体还需在主智能体配置中选用。\n\n## 后端放在哪里\n\n随服务发布的 Agent 后端放在：\n\n```text\nbackend/package/yuxi/agents/buildin/<your_agent>/\n├── __init__.py\n├── context.py\n└── graph.py\n```\n\n执行后端由 `buildin/__init__.py` 中的显式字典注册：\n\n```python\nBUILTIN_BACKENDS = {\n    \"ChatbotAgent\": ChatbotAgent,\n    \"SubAgentBackend\": SubAgentBackend,\n}\n```\n\n新增后端时显式导入其类并添加字典条目。字典键是持久配置与接口使用的稳定后端 ID，Python 类重命名不改变该 ID；仅增加目录或导出类不会启用后端。修改注册表时审查执行图、中间件和运行边界，并验证已有配置仍可解析。\n\n`get_agent_backend(backend_id)` 每次创建一个轻量后端对象，`list_agent_backend_info()` 查询已注册后端的基础信息。后端不持有用户、线程或 Graph 缓存；运行状态由本次 Context、Graph 和 middleware 持有。`get_graph(context=...)` 每次构建独立执行图，知识库增删无需重载后端。未知 ID 抛出 `AgentBackendNotFoundError`；接入用例与后端配置接口将该错误转换为 HTTP 404，worker 执行边界按现有失败流程收敛。\n\n后端的 `get_info()` 返回名称、能力与配置描述，不自行生成后端 ID。新增构图逻辑时验证两次运行分别使用各自的 Context 和资源。\n\n## 最小实现\n\n```python\nfrom langchain.agents import create_agent\nfrom yuxi.agents import BaseAgent, BaseContext, load_chat_model\n\n\nclass MyAgent(BaseAgent):\n    name = \"我的智能体\"\n    description = \"用于示例的智能体后端\"\n    context_schema = BaseContext\n\n    async def get_graph(self, *, context, **kwargs):\n        if not getattr(context, \"_runtime_prepared\", False):\n            raise ValueError(\"构图需要已准备的 Context\")\n        return create_agent(\n            model=load_chat_model(fully_specified_name=context.model),\n            system_prompt=context.system_prompt,\n            checkpointer=await self._get_checkpointer(),\n        )\n```\n\n这个示例展示最小的 Context、模型、提示词和 PostgreSQL checkpoint 装配。真实后端还要根据需要接入文件 backend、工具、Skills、审批、Summary、用量和子智能体 middleware。\n\nworker 和主动压缩在执行入口显式调用 `prepare_agent_runtime_context`，为 Context 追加工作区提示词、按当前用户过滤资源，并在模型为空时补齐系统默认模型。`get_graph(context=...)` 只消费准备后的对象。独立调用同样先创建 `context_schema()`，用 `update_config` 装载持久配置、用 `update` 注入已授权身份和运行覆盖，再 await 准备函数；流和 invoke 接口只接受 `context`，不接收配置字典。\n\n## Context 和配置表单\n\n需要让管理员或用户配置 Agent 行为时，在 `context.py` 扩展 `BaseContext`。字段定义、metadata、权限和保存结构的完整规则由[智能体配置](./agents-config.md#自定义-context-字段)拥有，本页只描述扩展时的代码路径：新增 dataclass 字段后，`get_configurable_items()` 自动把它暴露给 Agent 详情接口和 `AgentRuntimeConfigForm`，保存值进入 `config_json.context`，`get_graph(context)` 消费准备后的 Context。\n\n不要只在前端添加一个字段，也不要把运行期 ID、worker 身份和权限快照暴露成可保存配置。新增字段后，沿 context_schema → get_configurable_items() → 详情接口 → 配置表单 → `config_json.context` → `get_graph(context)` 的链路检查每一环的测试和文档。\n\n## 中间件和工具\n\n资源权限和默认资源选择在 Graph 创建前处理；模型提示注入、工具动态开放、文件结果处理、state 更新和观测才适合放入 middleware。内置 Agent 的工具可见性和执行注册分为两层：工具可以先进入 ToolNode，再由 Skill 激活状态决定是否让模型看到。\n\n优先复用：\n\n- [工具系统](./tools-system.md) 的注册和目标校验；\n- [中间件](./middleware.md) 的装配顺序；\n- [Skills 管理](./skills-management.md) 的依赖和激活规则；\n- [沙盒机制](../mechanisms/sandbox.md) 的文件和命令边界。\n\n新 middleware 不要绕过 `prepare_agent_runtime_context`，也不要用 Prompt、前端隐藏或 schema omission 代替后端授权。\n\n## 检查清单\n\n- `BaseAgent` 子类在 `BUILTIN_BACKENDS` 中通过稳定 ID 显式注册；\n- `context_schema` 的默认值、字段权限和选项能被前端正确渲染；\n- Graph 使用 Yuxi 的模型、工具、文件和 checkpoint 装配入口；\n- 工具副作用在执行处验证用户、路径和资源；\n- 新的模型可见输入、状态、文件或协议有正向和负向测试；\n- 相关 API、机制和用户文档已更新。\n\n## 源码和测试\n\n- [BaseAgent](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/base.py)\n- [Context](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/context.py)\n- [Chatbot graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)\n- [执行后端显式注册](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/__init__.py)\n- [Agent unit tests](https://github.com/xerrors/Yuxi/tree/main/backend/test/unit/agents)\n- [Agent integration/E2E](https://github.com/xerrors/Yuxi/tree/main/backend/test/e2e)\n\n改变持久配置、权限、模型可见输入、Run 生命周期或文件边界时，先按 [Yuxi Spec Loop](../develop-guides/spec-loop.md) 建立相应的决策和验证范围。\n","docs/agents/agent-evaluation.md":"# 评估智能体\n\n智能体评估用 Langfuse Dataset 保存一组固定任务，再让 Yuxi 按真实的 AgentRun、worker 和工具链路逐条执行。它适合比较一个智能体在研究、编程、文件处理或多步骤任务上的表现。\n\n本页不介绍知识库的 `recall@K` 和答案指标；那部分见[知识库评估](../intro/evaluation.md)。\n\n## 需要准备什么\n\n1. Yuxi 已配置 Langfuse tracing，API/worker 环境包含：\n\n   ```bash\n   LANGFUSE_PUBLIC_KEY=<your-public-key>\n   LANGFUSE_SECRET_KEY=<your-secret-key>\n   LANGFUSE_BASE_URL=https://cloud.langfuse.com\n   ```\n\n2. 本机运行 CLI 时也能读取同一 Langfuse 项目的变量。CLI 会直接读取 Dataset 并创建 experiment。\n3. 已安装 `yuxi-cli`，并登录目标实例：\n\n   ```bash\n   yuxi remote add local http://localhost:5173\n   yuxi login --browser\n   ```\n\n   CI 或没有浏览器时，可以使用 `yuxi login --api-key \"$YUXI_API_KEY\"`。\n4. 目标智能体已经存在，登录用户有权访问它。命令使用 Agent slug，例如 `default-chatbot`。\n\n## 准备 Dataset\n\n在 Langfuse 中创建 Dataset，并为每个 item 的 `input` 提供任务文本：\n\n```json\n{\"input\":\"请整理这份资料，并列出三个需要核实的事实。\"}\n```\n\n也兼容把任务放在 `query`、`question` 或 `prompt` 字段中。`expected_output` 可以保存参考答案，具体评分规则由 Langfuse evaluator 或人工评审负责。\n\nYuxi 不负责创建或上传 Dataset。先在 Langfuse 中检查任务文本、参考输出和数据集版本，再运行实验。\n\n## 运行实验\n\n```bash\nyuxi agent eval \\\n  --dataset-name demo-dataset \\\n  --agent-slug default-chatbot \\\n  --experiment-name default-chatbot-demo \\\n  --max-concurrency 1 \\\n  --timeout-seconds 900\n```\n\nCLI 对每条 item：\n\n1. 从 Dataset 读取任务文本；\n2. 调用 Yuxi 的 `POST /api/agent-invocation/eval/runs`；\n3. 由 Yuxi 创建临时 Conversation 和 AgentRun；\n4. 通过 worker 执行真实智能体；\n5. 等待 Run 进入终态；\n6. 把最终输出写回 Langfuse experiment item。\n\n`--max-concurrency` 是 Dataset 实验的并发数。先从 `1` 开始，再根据模型服务、worker 和沙盒容量提高。`--timeout-seconds` 是每条样例等待 Yuxi 结果的上限；超时会报告当前运行状态，不应把它当作成功。\n\n## 查看结果\n\n实验完成后，在 Langfuse Dataset 的 experiment 中查看每条 item 的最终输出。Yuxi 会在本地运行上下文和 trace 中保存以下标记，便于筛选：\n\n```text\nsource=agent_evaluation\nevaluation_dataset_name=<dataset-name>\nevaluation_dataset_item_id=<item-id>\nevaluation_experiment_name=<experiment-name>\n```\n\n先比较同一数据集的输出，再按自己的评估规则打分。一次实验的输出只代表当时的模型、Agent 配置、工具、知识库和外部服务状态；改变这些条件后，应创建新的 experiment 名称。\n\n## 排查失败\n\n- 没有 experiment：检查 CLI 的 Langfuse 公钥、密钥、地址和 Dataset 名称。\n- experiment 有 item 但 Yuxi 失败：检查 CLI 登录的 API Key、Agent slug，并用 `docker compose logs api worker` 查看当前槽位日志。\n- Trace 缺失：检查 API/worker 是否读取到 Langfuse 配置；Yuxi 业务结果仍以 PostgreSQL 的 Run 和消息为准。\n- 大量超时：降低 `--max-concurrency`，检查模型响应时间、worker 健康状态和沙盒创建耗时。\n- 实验部分成功：不要只看命令退出前的汇总，回到 Langfuse 检查每条 item 是否都有结果；CLI 会在成功写入数量与 Dataset 总数不一致时报告错误。\n\n实现入口见 [Agent Eval 路由](https://github.com/xerrors/Yuxi/blob/main/backend/server/routers/agent_invocation_eval_router.py)、[CLI 实验](https://github.com/xerrors/Yuxi/blob/main/packages/yuxi-cli/src/yuxi_cli/agent_eval.py) 和 [Langfuse 集成](../advanced/langfuse-integration.md)。\n","docs/agents/agents-config.md":"# 配置智能体\n\n本页是智能体配置参考，说明页面上的字段如何进入一次运行。新增智能体后端的代码结构见[开发智能体后端](./agent-backend-development.md)；只使用现成智能体时，从[快速开始](../intro/quick-start.md)开始。\n\n## 配置模型\n\n| 对象 | 负责什么 |\n| --- | --- |\n| `Agent` | 数据库中的智能体资源，保存名称、slug、共享范围和持久化配置 |\n| `BaseAgent` | 代码中的后端类型，声明 `context_schema` 和 `get_graph()` |\n| `BaseContext` | 配置字段和运行时输入的 Schema |\n| `config_json.context` | 当前 Agent 保存的配置值 |\n| Graph / middleware | 根据 Context 组合模型、工具、文件和扩展能力 |\n\n内置 `ChatbotAgent` 用于普通对话，`SubAgentBackend` 用于被主智能体委派的任务。子智能体的配置入口与普通智能体相同。\n\n## 配置页面从哪里来\n\n`BaseContext` 的 dataclass 字段和 metadata 会生成配置项描述，前端不再维护一份独立字段清单：\n\n```text\nContext 字段\n  → get_configurable_items()\n  → Agent 详情接口\n  → AgentRuntimeConfigForm\n  → config_json.context\n```\n\nmetadata 可以定义展示名称、说明、控件类型、选项和角色权限。运行期 ID、owner 和内部派生值应隐藏，不作为用户配置。\n\n`metadata.auth` 只限制修改权限：`admin` 字段允许管理员和超级管理员修改，`superadmin` 字段只允许超级管理员修改。普通用户读取有权访问的智能体时，可以读到这些已保存值，运行也使用这些值；不可修改的字段不进入其编辑表单，后端会过滤越权提交的字段并保留已保存值。`auth` 不提供字段保密能力，Context 配置不得存放凭据。\n\n例如管理员将 `max_execution_steps` 设置为 50，普通用户运行该智能体时的 `recursion_limit` 也为 50。已经固化运行快照的 Run 保留自己的配置；后续新 Run 读取保存的配置。知识库、Skills 等资源仍按运行用户的访问权限筛选。\n\n## 基础字段\n\n| 字段 | 作用 |\n| --- | --- |\n| `system_prompt` | 智能体角色和行为说明 |\n| `model` | 主模型；留空时使用系统默认模型 |\n| `tool_approval_mode` | `default` 或 `always_trust`；仅管理员可配置 |\n| `tools` | 可使用的内置工具 |\n| `knowledges` | 可检索的知识库范围 |\n| `mcps` | 直接添加到 Agent 的已启用 MCP 服务器 |\n| `skills` | 选择共享和内置 Skill；个人 Skill 始终可用 |\n| `preload_skills` | 从首轮请求加载完整说明和依赖的 Skill 子集 |\n| `summary_threshold`、`summary_keep_messages` | 上下文压缩的唯一压力阈值和摘要后保留消息数 |\n| `summary_prompt`、`summary_tool_result_token_limit` | 摘要提示词和工具结果预览上限 |\n| `max_execution_steps`、`model_retry_times` | 单次运行步数和模型重试次数 |\n\n## 资源选择语义\n\n资源字段 `tools`、`knowledges`、`skills`、`subagents`、`mcps` 和 `preload_skills` 接受 `\"all\"` 或字符串数组，本页是这组契约的唯一 Owner。`\"all\"` 表示每次运行使用当前用户可访问的全部资源，包括之后新增的资源；`[]` 表示不选择；非空数组表示固定范围。新写入拒绝 `null`、未知字符串和非字符串数组成员。\n\n创建时省略字段使用其默认值：工具、知识库和 Skill 为 `\"all\"`，子智能体为 `[\"general-purpose\"]`（通用任务），MCP 和预加载 Skill 为 `[]`。界面的「全部（含新增）」保存 `\"all\"`；逐项勾选保存固定数组，即使勾选了当前所有选项也不会自动转换成全部模式。「清空全部」保存 `[]`，子智能体同样可以全部禁用。\n\n各资源的附加规则：\n\n- MCP 选择只控制直接加载的服务器；已激活 Skill 声明的 MCP 依赖仍按需加载。\n- Skill 选择器只列共享和内置 Skill；运行时自动合并当前用户全部个人 Skill，`skills=[]` 只关闭共享选择。Skill 的激活时机见 [Skills 管理](./skills-management.md)。\n- 预加载候选项限于已选且可访问的共享 Skill；`preload_skills=\"all\"` 在每次运行预加载该范围及其授权依赖的完整说明，并从首轮开放依赖工具。`skills=[]` 时预加载全部仍得到空列表，个人 Skill 继续按需读取正文。\n- 子智能体不能继续调用下一层子智能体。\n\n这些字段只会缩小当前用户已经拥有的权限；运行时如何消费解析结果见 [Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n### 共享智能体与编辑保存\n\n共享智能体保存完整的期望选择，每次运行再与当前操作者的可访问资源取交集，运行不会改写保存的选择。例如创建者选择 10 个 Skill，委托管理员只能访问其中 5 个，委托管理员运行时生效 5 个，保存名称或模型后仍保留原有 10 个选择。\n\n编辑页只提交修改过的配置字段；通过 API 更新 `config_json.context` 时，省略字段保留原值。修改非空资源列表时，后端保留当前管理员不可访问的既有引用，并拒绝新增无权访问的引用；保留的引用维持原相对顺序，新选择追加到末尾。「清空全部」移除所有引用（包括不可访问项），切换到「全部（含新增）」则整体替换固定选择范围。界面会显示当前不在可选范围内的既有选择数量。\n\n## 定时运行\n\n智能体支持用户定时任务（Beta）：为智能体配置 5 段 cron 和 IANA 时区后，worker 到期自动在独立 Conversation 中触发一次运行，也支持手动立即运行；停机错过的多个周期合并为一次，已有未完成执行时跳过并记录。任务的定义、触发与恢复边界见 [Agent 运行时上下文](../mechanisms/agent-runtime.md#用户定时-agent)。\n\n## 自定义 Context 字段\n\n需要让用户配置额外行为时，扩展 Context，让后端和前端沿同一 Schema 工作：\n\n```python\nfrom dataclasses import dataclass, field\nfrom yuxi.agents import BaseContext\n\n\n@dataclass(kw_only=True)\nclass MyAgentContext(BaseContext):\n    response_style: str = field(\n        default=\"concise\",\n        metadata={\n            \"name\": \"回答风格\",\n            \"description\": \"控制回答的详细程度\",\n            \"type\": \"string\",\n            \"options\": [\"concise\", \"detailed\"],\n        },\n    )\n```\n\n新增字段会影响保存结构、配置表单和运行期 Context。字段默认值、权限和选项变化时，同时更新相关测试和文档。\n\n## `capabilities`\n\n`capabilities` 是代码声明的静态能力，用于控制固定的前端入口，例如：\n\n```python\nclass MyAgent(BaseAgent):\n    capabilities = [\"file_upload\", \"files\"]\n```\n\n它不保存待办、文件、产物或子智能体状态。运行态来自 LangGraph state 的 `agent_state`；能力声明只表达后端固定支持哪些 UI 入口。\n\n## 运行时入口\n\n配置如何与用户身份、权限快照、Workdir、Memory 和 PostgreSQL checkpoint 组合，见[Agent 运行时上下文](../mechanisms/agent-runtime.md)。工具可见性和执行授权见[工具系统](./tools-system.md)，上下文压缩见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## 相关页面\n\n- [开发智能体后端](./agent-backend-development.md)\n- [中间件](./middleware.md)\n- [Skills 管理](./skills-management.md)\n- [子智能体](./subagents-management.md)\n- [沙盒机制详解](../mechanisms/sandbox.md)\n","docs/agents/mcp-integration.md":"# 集成 MCP\n\nMCP（Model Context Protocol）让智能体调用外部服务提供的工具。管理员在“扩展 → MCP”中添加远程服务器；Agent 可直接选择服务器，Skill 也可声明激活后加载的依赖。\n\n## 支持的传输方式\n\n| 传输方式 | 适用场景 |\n| --- | --- |\n| `streamable_http` | 新的远程 MCP 服务 |\n| `sse` | 仍提供 SSE 接口的远程服务 |\n\n管理接口只接受 `streamable_http` 和 `sse`。Yuxi 不支持 `stdio`，包括内置 MCP 和直接传入的运行时配置；历史 `stdio` 配置会被禁用，应迁移为远程服务。\n\n## 添加远程 MCP\n\n在“扩展 → MCP”点击“添加 MCP”，填写稳定标识、名称、传输方式和 URL。例如：\n\n```json\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\"\n}\n```\n\n管理接口对应：\n\n```http\nPOST /api/system/mcp-servers\nAuthorization: Bearer <admin-token>\nContent-Type: application/json\n\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\",\n  \"description\": \"提供示例查询工具\"\n}\n```\n\n需要认证的远程服务可以配置 HTTP headers、连接超时和 SSE 读取超时。凭证会随着连接请求发送，请只配置必要的 header，并把管理接口限制在可信的管理员范围。\n\n添加后先点击“测试连接”，确认能发现工具，再把服务器状态设为“已添加”。状态关闭时，服务器记录仍保留，但不会进入运行时。\n\n## 让智能体使用 MCP\n\n在智能体配置的 MCP 字段中显式选择要直接添加的服务器。该字段默认不添加服务器：\n\n- 未显式配置或显式清空时，不直接加载 MCP 服务器；\n- 显式选择后，只使用所选且当前可用的服务器；\n- MCP 工具仍会在执行处使用当前用户身份和服务器配置；\n- 管理员可以在 MCP 详情页单独禁用某个工具。\n\n共享 Skill 的 `mcp_dependencies` 声明所需服务器。Skill 激活后按需加载这些服务器的工具；预加载 Skill 从首轮加载，均无需在 Agent 的 MCP 字段重复选择。服务器必须由管理员启用，禁用的服务器不会提供工具。\n\nMCP 配置从 PostgreSQL 读取，工具对象按配置哈希缓存。修改连接配置或工具禁用列表后，下一次运行会使用新的配置键。\n\n## 内置远程 MCP\n\n内置 DeepWiki 使用 `deepwiki-official` 标识，通过 `https://mcp.deepwiki.com/mcp` 提供 Streamable HTTP 服务，无需认证即可查询公开 GitHub 仓库。详见 [DeepWiki 官方文档](https://docs.devin.ai/work-with-devin/deepwiki-mcp)。\n\n开发者在 [`builtin.py`](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/mcp/builtin.py) 的 `BUILTIN_MCP_SERVERS` 中添加固定远程定义：\n\n```python\nBUILTIN_MCP_SERVERS = {\n    \"deepwiki-official\": {\n        \"transport\": \"streamable_http\",\n        \"url\": \"https://mcp.deepwiki.com/mcp\",\n        \"description\": \"查询公开 GitHub 仓库的文档、架构与代码\",\n        \"icon\": \"📚\",\n        \"tags\": [\"内置\", \"代码\", \"文档\"],\n    },\n}\n```\n\nAPI/worker 启动时同步固定定义到数据库；运行时直接读取代码中的连接字段。新内置 MCP 默认未添加，管理员需要启用；连接配置不可通过页面修改。添加定义无需修改服务逻辑或启动入口。\n\n原内置 `mcp-server-chart` 已退役，启动时删除系统创建的记录。其他历史 stdio 记录保留并禁用，管理员可迁移为远程服务或删除。MySQL 报表技能改用 Markdown 表格，不再依赖图表 MCP；自定义角色或 Skill 中对退役 MCP 的引用需要自行调整。\n\n## 常用管理接口\n\n| 方法 | 路径 | 作用 |\n| --- | --- | --- |\n| `GET` | `/api/system/mcp-servers` | 查看服务器；普通用户只得到脱敏基础信息 |\n| `POST` / `PUT` | `/api/system/mcp-servers`、`/{slug}` | 添加或修改远程 MCP |\n| `PUT` | `/api/system/mcp-servers/{slug}/status` | 启用或停用服务器（对应页面上的添加/移除状态） |\n| `POST` | `/api/system/mcp-servers/{slug}/test` | 测试连接并发现工具 |\n| `GET` | `/api/system/mcp-servers/{slug}/tools` | 查看工具 |\n| `PUT` | `/api/system/mcp-servers/{slug}/tools/{tool_name}/toggle` | 启用或禁用单个工具 |\n\n接口字段和错误响应以实例 Swagger 为准。\n","docs/agents/middleware.md":"# 中间件\n\n中间件把文件、Skills、子智能体、上下文压缩、审批和用量统计接到 LangGraph Agent。它们在模型调用、工具调用或 state 更新的边界运行，让不同 Agent 复用同一套能力。\n\n内置 `ChatbotAgent` 和 `SubAgentBackend` 都在 `get_graph()` 中组装中间件。Graph 创建前，系统先完成用户资源和权限的归一化；中间件不应绕过这一步重新决定授权。\n\n## Graph 创建前的准备\n\n`prepare_agent_runtime_context` 会根据当前用户和 Agent 配置：\n\n- 过滤内置工具、知识库、MCP、Skills 和子智能体；\n- 生成 `_visible_knowledge_bases`；\n- 展开 Skill 依赖，生成 `_effective_skill_slugs` 和 `_runtime_skills`；\n- 使用系统默认模型补齐空的模型配置。\n\n随后，工具解析器准备可执行工具，`build_prompt_with_context` 生成系统提示词，Agent 再创建 Graph。工具执行时仍需检查具体目标，准备阶段的资源快照不是授权替代品。\n\n## 内置中间件顺序\n\n`ChatbotAgent` 的常见顺序如下（[graph.py](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py) 装配）；可选项只在对应能力启用时加入：\n\n| 顺序 | 中间件 | 作用 |\n| --- | --- | --- |\n| 1 | `ToolErrorGuardMiddleware` | 最外层隔离普通工具异常，保留取消与 interrupt 的传播 |\n| 2 | `SteerMiddleware` | 在安全边界发现待接替请求 |\n| 3 | `create_agent_filesystem_middleware` | 提供 Workdir、User Data、Skills 文件后端，并卸载过大的工具结果 |\n| 4 | `SkillsMiddleware` | 注入 Skill 说明，按激活状态开放依赖 |\n| 5 | `YuxiMemoryMiddleware` | Memory 开关开启且 `MEMORY.md` 有内容时，注入用户记忆并提供受限工具 |\n| 6 | `YuxiSubAgentMiddleware` | 主智能体有可见子智能体时提供子智能体生命周期工具 |\n| 7 | `YuxiSummarizationMiddleware` | 先确定性压缩工具结果，仍达到同一阈值时生成摘要 |\n| 8 | `TodoListMiddleware` | 保存待办，供状态面板展示 |\n| 9 | `PatchToolCallsMiddleware` | 修正部分工具调用消息形态 |\n| 10 | `NetworkRetryMiddleware` | 网络错误按预算、其他可重试模型错误按次数重试，耗尽后抛出异常 |\n| 11 | `ImageInputCompatibilityMiddleware` | 桥接工具读取图片与模型输入格式；必要时回退 OCR |\n| 12 | `TokenUsageMiddleware` | 记录近似上下文和主模型实际用量 |\n| 13 | 工具审批 middleware | 默认模式下审批 Project 外的文件写入和命令执行；当前 Project 内的写/编辑自动放行 |\n\n`SubAgentBackend` 复用文件、Skills、Summary、待办、重试和用量等能力，同样以 `ToolErrorGuardMiddleware` 为最外层，但不挂载子智能体 middleware，并过滤不适合子智能体的敏感或交互工具。\n\n模型重试耗尽后，异常进入 Run 失败通道，持久化 `failed` 状态与错误原因；已有部分输出保留错误元数据。\n\n子 Run 的失败通过 `subagent_await` / `subagent_status` 返回给父智能体，由父智能体决定后续处理。最终正常回答仍须满足同 Run 的 model lifecycle 审计关联。\n\n## Skills 和知识库\n\nSkills middleware 将 Skill 说明按模型请求注入：预加载 Skill 从首轮开放工具和 MCP 依赖，普通 Skill 在模型读取对应 `SKILL.md` 后激活，再开放声明的依赖。Agent 显式选择的 MCP 服务器从运行开始提供工具。\n\n知识库能力由内置 `knowledge-base` Skill 提供。它的工具是否注册、模型是否可见、参数是否能访问目标知识库分别由工具组装、Skill 激活和知识库权限检查负责。完整链路见[工具系统](./tools-system.md)和[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n## 文件和附件\n\n附件确认后写入当前 Project Workdir。每次 Run 会把线程历史附件的文件名和实时路径加入本轮用户消息，让模型按需调用 `read_file`；持久化 Message 仍保存原始文本，不会把这段模型专用路径混进用户可见消息。\n\n普通 Agent 和子 Agent 使用根 Conversation 的同一个 `runtime_scope_id` 和 Workdir。子 Agent 的 child thread 只隔离 LangGraph checkpoint，不隔离共享文件。Viewer、附件和 artifact API 直接访问 UserWorkspace 的持久文件，不需要创建 file-bridge Sandbox。\n\n## 子智能体\n\n主智能体配置了可见子智能体时，middleware 提供 `subagent_start`、`subagent_status`、`subagent_cancel` 和 `subagent_await`。`subagent_start` 立即返回子 Run 身份，`subagent_await` 按需等待；查询和取消均按 `run_id` 执行。子智能体使用自己的 Context 和 checkpoint，但继承发起用户的权限、Workdir 和 execution runtime。\n\n详细的调用、busy、结果和文件边界见[子智能体](./subagents-management.md)。\n\n## Summary 上下文压缩\n\nSummary 在文件和 Skills 等中间件之后运行。请求达到唯一压力阈值后，它先为当前模型请求精简工具结果并重新计量；仍达到同一阈值时才写入历史文件、生成摘要并更新 checkpoint。它不会删除 PostgreSQL 聊天消息，也不会把内部摘要模型调用当作用户可见回复。支持主动压缩的 Agent 复用同一摘要器，并通过 service 在空闲线程的 canonical graph state 上更新 checkpoint。\n\n参数和状态细节见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## Token 用量\n\n`TokenUsageMiddleware` 同时记录近似上下文 token（用于摘要阈值和状态面板）和主 Agent 模型返回的 `usage_metadata`（按最近调用、当前 Run、线程和模型分桶保存）。\n\n`siliconflow-cn` 和 `siliconflow` 当前位于 Provider 用量黑名单：这两个供应商仍可提供近似上下文统计，但不会写入最近调用、Run 或线程的 Provider 实际用量聚合。其他能返回兼容 `usage_metadata` 的供应商才会进入这组实际用量统计；Summary 内部摘要模型调用不计入。\n\nRun 终态时，worker 把与当前 `run_id` 匹配的 state 快照写入 `AgentRun.token_usage`；父 Run 和子 Run 分开保存。\n\n## 新增中间件时\n\n先说明它要改变哪一条边界：Prompt、模型调用、工具调用、文件访问、state 或观测。资源筛选和权限收敛放在 Graph 创建前；文件读写和工具结果卸载优先复用现有 filesystem middleware；新增模型可见输入或副作用时补充对应测试和失败案例。\n\n实现入口：[ChatbotAgent graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)、[中间件目录](https://github.com/xerrors/Yuxi/tree/main/backend/package/yuxi/agents/middlewares)。\n","docs/agents/skills-management.md":"# 管理 Skills\n\nSkill 是一个可复用的能力包，通常包含一个 `SKILL.md`、提示词、参考资料和可选脚本。智能体先看到 Skill 的描述，再按需要读取 `SKILL.md`；本地工具和 MCP 依赖随 Skill 激活进入模型请求。\n\n## 什么时候用 Skill\n\n把一类稳定、可复用的工作方式放进 Skill，例如文档处理、研究流程、报表生成或某个外部服务的操作规范。单次任务要求写在对话中；需要跨对话复用的规则和资料再考虑 Skill 或用户工作区文件。\n\n## Skill 保存在哪里\n\n| 来源 | 位置和事实 | 谁可以安装/管理 |\n| --- | --- | --- |\n| 内置 | 随代码发布，启动时同步索引 | 管理员可以启用/停用，不能删除或直接编辑 |\n| 共享上传/远程 | Skill 持久目录 + PostgreSQL 索引 | 管理员按共享范围管理 |\n| 个人 | 当前用户 UserWorkspace 的 `agents/skills/<slug>` | 当前用户；不创建共享 Skill 数据库记录 |\n\n运行时路径是：\n\n- 共享和内置 Skill：`/home/gem/skills/<slug>/`，对 Agent 只读；\n- 个人 Skill：`/home/gem/user-data/agents/skills/<slug>/`，位于当前用户的 UserWorkspace。\n\n同一用户有个人 Skill 和共享 Skill 使用同一个 slug 时，个人版本覆盖该用户看到的共享版本。删除个人版本后，如果共享版本仍可访问，会恢复共享版本。\n\n## 创建 Skill\n\n标准目录至少包含根级 `SKILL.md`：\n\n```text\nmy-skill/\n├── SKILL.md\n├── tools/       # 可选脚本\n└── prompts/     # 可选提示词或参考资料\n```\n\n`SKILL.md` 使用 YAML frontmatter：\n\n```markdown\n---\nname: 文档整理\nslug: document-cleanup\ndescription: 按指定结构整理文档，并保留可核对的来源。\ntool_dependencies: []\nmcp_dependencies: []\nskill_dependencies: []\n---\n\n# 文档整理\n\n## 何时使用\n\n说明这个 Skill 解决什么问题，以及什么时候不该使用。\n\n## 操作步骤\n\n写出 Agent 应遵循的步骤、限制和验收方式。\n```\n\n必填字段是 `name` 和 `description`。名称和 `slug` 最多 128 个字符。`slug` 可省略，省略时直接使用 `name`，因此省略 slug 时 `name` 本身也必须是小写字母、数字和单个短横线组成的值，中文或带空格的展示名称会校验失败。\n\n建议显式填写 slug，把自然语言名称和稳定标识分开。\n\n依赖字段含义：\n\n| 字段 | 作用 |\n| --- | --- |\n| `tool_dependencies` | 需要的内置工具 |\n| `mcp_dependencies` | 需要的 MCP 服务器 |\n| `skill_dependencies` | 需要先提供说明的其他 Skill |\n\n个人 Skill 只作为用户文件读取，不解析这些平台依赖；需要依赖工具、MCP 或其他 Skill 时，安装为共享 Skill。\n\n## 安装方式\n\n进入“扩展 → Skills”，可以选择：\n\n1. **推荐 Skill**：从推荐列表生成安装草稿。\n2. **上传**：上传 ZIP 或单个 `SKILL.md`，解析后生成草稿。\n3. **远程安装**：从 GitHub 仓库、ModelScope Skill 或合集拉取一个或多个 Skill。\n4. **在线编辑**：编辑已有且有管理权限的共享 Skill 文件和依赖。\n5. **Agent 内安装**：主智能体使用 `install_skill` 把 Skill 安装到当前用户的个人来源；子智能体不能使用该工具。\n\n上传和远程安装都先解析为草稿，再选择个人或共享位置并确认。草稿中的可安装条目只包含已解析的临时包；远程拉取或解析失败会单独显示，不能被确认安装。确认前可以检查名称、说明、文件和依赖；取消草稿不会写入正式 Skill。\n\n有管理权限的用户可在共享 Skill 详情页编辑文本文件。保存 `SKILL.md` 时，根文件的名称、描述及依赖声明会同步到数据库索引；“配置”中的依赖选择也会回写根文件。每次保存都使用打开文件时取得的修订值；如果其他人已修改该文件，页面会拒绝覆盖并保留当前草稿，用户需核对最新内容后重试。切换文件或离开页面时，未保存的修改会触发确认。共享范围和启停状态仍在配置页分别保存。新的共享内容在后续 Agent Run 准备时生效；已准备 Run 的预加载内容保留原有快照，按需读取的用户投影可随之后的编辑刷新。\n\n### 远程来源限制\n\n管理员在“设置 → 基本设置 → Skill 配置 → 远程来源白名单”中维护 `remote_skill_source_policy.allowed_hosts`。默认允许 `github.com` 和 `modelscope.cn`；主机名必须精确匹配，保存空列表会关闭远程安装。\n\n示例来源：\n\n```text\nanthropics/skills\nhttps://github.com/anthropics/skills\nhttps://modelscope.cn/skills/@anthropics/pdf\nhttps://modelscope.cn/collections/MiniMax/MiniMax-Office-skills\n```\n\n除了按仓库或合集拉取，也可以在远程安装对话框切换到“全局搜索发现”，输入关键词检索 `skills.sh` 上的开源 Skills，再选择结果批量拉取和安装。搜索在一次性 Sandbox 中执行；选中结果后的实际拉取仍经过来源白名单和安装确认流程。\n\nGitHub 的 `owner/repo` 简写会被转换为 HTTPS 地址。远程来源会在不继承全局或用户环境变量的一次性 Sandbox 中下载和提取，系统会拒绝绝对路径和路径穿越，并限制文件数、目录深度和总大小。来源白名单限制产品允许的地址，不是网络出口防火墙。\n\n### 新增内置 Skill\n\n在 `backend/package/yuxi/agents/skills/buildin/<slug>/` 新增目录，至少包含 `SKILL.md`。启动同步按目录名排序发现直接子目录，忽略下划线或点开头的目录；无需修改 Python 注册清单。\n\n`SKILL.md` frontmatter 唯一拥有名称、描述、版本和依赖。`slug` 必须与目录名一致，省略时使用 `name`；`version` 省略时为 `1.0.0`，建议使用引号包裹版本字符串。工具、MCP、Skill 依赖使用本页定义的字段。缺少根文件或元数据不合法时，启动同步明确失败。\n\nAPI/worker 启动时同步文件、元数据和依赖，保留数据库中的启停状态。重启后在“扩展 → Skills”核对新增项的说明和依赖；新增脚本或资源也必须随发行包携带。源码目录与共享投影分别拥有发布内容和安装文件，编辑应落在源码目录。\n\n安装前仍应审查 Skill 的提示词、脚本、依赖和网络行为。不要把数据库密码、云平台密钥或 `SANDBOX_PROVISIONER_TOKEN` 放进 Skill 或 Agent 环境。\n\n## 依赖和加载时机\n\nAgent 的 `skills` 配置语义（`\"all\"`、固定数组与默认值）由[资源选择契约](./agents-config.md)拥有，本页描述激活时机。每个 Run 准备 Context 时，系统按权限解析共享选择，合并当前用户全部个人 Skill，再展开 `skill_dependencies`；主智能体和子智能体使用同一规则。`skills=[]` 时个人 Skill 仍进入模型可见的描述列表，只是没有共享选择。个人 Skill 安装不修改 Agent 配置，新增 Skill 在下一次 Run 准备时自动加入，已准备的运行保留快照。\n\n### 普通渐进加载\n\n1. 创建 Graph 前，模型得到有效 Skill 的名称、描述和 `SKILL.md` 路径。\n2. 模型读取某个可见 Skill 的 `SKILL.md` 后，该 Skill 进入 `activated_skills`。\n3. 后续模型请求加入它声明的本地工具，并加载已启用的 MCP 依赖服务器提供的工具。\n\n模型没有读取的 Skill 的依赖工具继续隐藏。未激活的 Skill 本地工具即使已注册到 ToolNode，也不能被模型调用。\n\n### 预加载\n\nAgent 配置可以用 `preload_skills` 指定少量需要从首轮就可用的 Skill。预加载选择器只列 `skills` 中当前用户可访问的共享和内置 Skill；系统会展开其依赖闭包，读取根级 `SKILL.md`，并从首轮模型请求开放本地工具和已启用的 MCP 依赖。\n\n预加载的根文件缺失或不可读时，Graph 创建会明确失败，不会静默退回渐进加载。默认值为空列表；选择“全部（含新增）”时，每次运行预加载当前 Agent 已选且可访问的共享 Skill 及其授权依赖。同名个人版本覆盖共享版本时读取个人内容；其他个人 Skill 始终可发现，正文按需读取。固定列表和全部模式均遵循[资源选择契约](./agents-config.md)。\n\n## 权限和选择\n\n共享 Skill 使用 `source_type`、`share_config` 和 `enabled` 表达来源、范围和启用状态。范围使用 version 2 的 `read_scope`、`manage_scope`，可以是全局、部门或指定用户；管理范围必须包含在读取范围内。\n\n| 用户 | 可见和使用 | 可管理 |\n| --- | --- | --- |\n| `superadmin` | 全部允许的共享/内置 Skill | 全部非内置 Skill，及内置 Skill 的启停 |\n| `admin` | 命中读取范围且已启用的 Skill | 命中管理范围的非内置 Skill，及内置 Skill 的启停 |\n| 普通用户 | 命中读取范围且已启用的 Skill | 自己拥有的非内置 Skill；新安装的 Skill 固定进入个人来源 |\n\n普通用户安装的新 Skill 固定进入个人来源，不配置共享范围。管理员安装到共享来源时才会写入 PostgreSQL 索引，并可以配置部门或用户范围。扩展管理页展示当前用户可访问的共享和个人 Skill；Agent 配置选项只展示可访问的共享和内置 Skill。后端在保存时校验共享引用，运行时按当前用户身份加载个人目录。\n\nAgent 配置中的 Skill 选择不能扩大用户的文件、知识库或 MCP 权限。\n\n## 内置 Skill 说明\n\n系统启动时会同步仓库内置 Skills，其中两个内置 Skill 有产品级行为约定。`html-preview` 用于在普通 Markdown 难以清晰表达指标、对比、流程、时间线或层级关系时，指导 Agent 输出静态 `html:preview` 围栏；普通 HTML 源码仍使用 `html` 代码块，前端把该围栏清洗后放入 sandboxed iframe 预览。未显式配置 Skills 的 Agent 按现有资源规则自动获得该 Skill；使用显式 Skills 允许列表的 Agent 需要选择 `html-preview`。\n\n内置 `deep-research` 不依赖 `html-preview`。它默认在当前 Workdir 的 `outputs/` 目录生成独立、响应式的 HTML 阅读文档，并通过交付物入口展示；用户明确指定其他格式时除外。宽屏报告可以提供侧栏目录，窄屏隐藏或折叠侧栏；报告可以按内容需要使用外部图片等公开资源，来源以普通链接呈现。\n\n## 运行时文件行为\n\n共享和内置 Skill 投影对 Agent 只读，但沙盒命令仍可能执行其中的脚本。脚本如需写文件，应写入当前 Project Workdir 或 User Data，而不是 Skill 目录。个人 Skill 直接从当前用户工作区读取，不会复制到共享投影。\n\nSkill 的选择影响 Prompt 和工具激活；共享投影按用户授权集合生成，不会因为某个 Agent 选择了 Skill 就改变 Sandbox 身份。路径穿越、符号链接和跨用户访问由文件系统边界拒绝。\n\n## 维护建议\n\n- slug 使用小写字母、数字和单个短横线，尽量短且能表达用途。\n- `description` 先说明能力和适用场景，再写实现细节。\n- 把“何时使用”“不要做什么”“产物放在哪里”和“如何验收”写进 `SKILL.md`。\n- 依赖链保持短小，避免循环依赖；只有真实需要时才声明工具或 MCP 依赖。\n- 脚本按不可信输入处理，不读取或输出运行环境中的秘密。\n- 修改共享 Skill 的依赖、范围或文件后，用一个真实 Agent Run 验证模型可见工具和最终产物。\n\n实现入口见 [包格式解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/package.py)、[安装草稿生命周期](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/draft.py)、[共享 Skill 索引与安装](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/shared.py)、[共享文件编辑](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/edit.py)、[个人 Skill 安装与文件操作](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/personal.py)、[用户投影](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/projection.py)、[运行时解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/skills/runtime.py) 和 [Skills middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/skills.py)。\n","docs/agents/subagents-management.md":"# 使用子智能体\n\n子智能体是一个特殊的 Agent：它仍然是 `agents` 表中的一级智能体，只是标记为 `is_subagent=true`，并使用 `SubAgentBackend`。因此，子智能体和普通智能体共用创建、权限和配置入口。\n\n## 在页面中配置\n\n进入“智能体”，点击“新增智能体”，在后端类型中选择 `SubAgentBackend`。然后像配置普通智能体一样设置名称、提示词、模型、工具、知识库、MCP 和 Skills。\n\n子智能体有三个限制：\n\n- 不会出现在普通聊天的智能体切换列表中；\n- 不能设置为默认智能体；\n- 不能继续调用其他子智能体，因此不会形成孙级调用链。\n\n主智能体在运行配置的“子智能体”字段中选择允许调用的对象：\n\n- 未配置时默认仅选择通用任务（`[\"general-purpose\"]`），运行时仍按当前用户权限过滤；\n- 显式选择 `\"all\"` 时，每次运行选择当前用户可见的全部子智能体；\n- 保存空列表 `[]` 时不启用子智能体；\n- 显式选择后，只允许调用所选项；\n- 每个子智能体使用自己的 `config_json.context`，不会继承主智能体的模型或工具选择；\n- 用户权限变化后，新运行会重新计算可见范围。\n\n## 调用方式\n\n主智能体通过工具调用子智能体，不要通过 Shell、`curl` 或 HTTP API 间接调用。\n\n### 派发任务：`subagent_start`\n\n所有子任务通过 `subagent_start` 派发，立即返回 `run_id`、`thread_id` 和访问链接。需要结果时调用 `subagent_await(run_id)`；即使短任务也先派发再等待。多个独立任务先全部派发，再按需等待。\n\n工具参数：\n\n```python\n{\n    \"description\": \"整理这份文档的三条要点\",\n    \"subagent_slug\": \"general-purpose\",\n    \"thread_id\": null\n}\n```\n\n首次调用不需要 `thread_id`。如果要继续之前的子任务，把上一次结果中的子智能体线程 ID 传回去。\n\n### 查询、等待与取消\n\n生命周期工具统一按 Run 工作：\n\n| 工具 | 作用 |\n| --- | --- |\n| `subagent_start` | 创建并立即返回 `run_id`、`thread_id` |\n| `subagent_status` | 查询运行状态和最近几条进度摘要 |\n| `subagent_await` | 等待终态并取得最终结果；超时返回当前快照 |\n| `subagent_cancel` | 请求取消子智能体运行 |\n\n同一个子智能体线程同时只能有一个运行中的 Run。忙碌时工具返回 `busy`，不会隐藏地把请求排队。终态后的同一 `thread_id` 可以继续创建新的 Run。\n\n所有生命周期工具按 `run_id` 操作，并验证该 Run 由当前父 Run 创建，不能读取或控制其他任务。\n\n## 运行时边界\n\n一次子智能体调用会创建独立的 child checkpoint thread 和 `agent_runs(run_type=subagent)` 记录，同时继承父运行的用户身份和根执行树：\n\n| 资源 | 主智能体 | 子智能体 |\n| --- | --- | --- |\n| LangGraph checkpoint | 当前 `thread_id` | 独立 `child thread_id` |\n| Sandbox runtime | 根 `runtime_scope_id` | 与根运行相同 |\n| Project Workdir | 当前 Project 的 Workdir | 与根 Conversation 绑定的 Project 相同 |\n| UserWorkspace | 当前用户的工作区 | 同一用户的工作区 |\n| 共享/内置 Skills | 当前用户授权的只读投影 | 同一授权投影 |\n\n父子智能体看到的是同一份 Workdir 文件字节，不会通过 checkpoint 复制或合并文件。child thread 只隔离 LangGraph 上下文；它不是文件系统隔离边界。并发写同一路径仍按真实 POSIX 文件结果处理。\n\n子智能体可以使用自己配置的文件工具和知识库范围，但所有资源访问仍以发起用户的后端权限为最终边界。`present_artifacts`、`ask_user_question`、`install_skill` 等不适合子智能体直接使用的工具会被过滤；禁用状态不只在模型可见性上生效——即使模型显式传回被禁用的工具调用，执行层也会返回绑定原调用的错误结果，不会执行该工具。\n\n## API 和数据模型\n\n子智能体沿用普通 Agent 管理 API：\n\n- `GET /api/agent` 默认返回聊天可用的普通 Agent；\n- `GET /api/agent?include_subagents=true` 返回包含子智能体的列表；\n- 创建或更新 `SubAgentBackend` 时，后端会校验 `is_subagent=true`；\n- 详情、更新和删除复用同一套 Agent 权限检查。\n\n运行时，主智能体的 `subagents` 会先收敛为当前用户可见的允许列表。列表非空时挂载子智能体 middleware；子智能体自身不会挂载这一中间件。\n\n## 查看结果\n\n父 state 的 `subagent_runs` 保存子任务身份，页面加载时通过数据库父子关系补齐尚未进入 checkpoint 的记录。状态面板独立订阅各子 Run 的事件流，并从 Run 接口读取当前状态；父 graph 在 `subagent_await` 中等待时，先完成的子任务立即更新。切换会话会关闭订阅，重新打开时回读状态；完成后从持久化消息读取最终结果。Redis 原始事件只供运行基础设施和前端订阅，不作为主智能体的工具结果。\n\n前端最多同时订阅三个子 Run，其余活跃子 Run 每两秒查询状态，避免 HTTP/1.1 的同源连接被长连接占满；连接无事件时每十五秒核对持久状态，断线或查询失败显示重连提示。\n\n## 从 task 工具迁移\n\n历史 `task` 消息仍可查看，新模型不再获得该工具。升级前完成或取消旧版本的活跃 Run，并在智能体管理中将已保存的深度研究 Agent 及自定义提示词中的 `task` 指令改为先 `subagent_start`、再 `subagent_await`；已有配置不会被新的默认提示词覆盖。历史 checkpoint 中尚未执行的 `task` 调用会明确返回未知工具错误，不会被自动重放为新的子任务。父 Run 终态仍按原有策略取消活跃后代，派发与等待分离不改变此边界。\n\n实现入口见 [子智能体 middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/subagent_task.py)、[SubAgentBackend](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/subagent/graph.py) 和 [AgentRun 服务](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/agent_run_service.py)。\n","docs/agents/tools-system.md":"# 工具系统\n\nYuxi 的工具分成三层：内置工具、知识库工具和 MCP 工具。Graph 创建时准备可执行工具，运行时再根据用户权限、Agent 配置和 Skill 激活状态决定模型能看到什么。\n\n## 注册一个内置工具\n\n普通内置工具使用 `@tool` 注册：\n\n```python\nfrom yuxi.agents.toolkits.registry import tool\n\n\n@tool(category=\"buildin\", tags=[\"示例\"], display_name=\"示例工具\")\ndef example_tool(text: str) -> str:\n    \"\"\"返回处理后的文本。\"\"\"\n    return text\n```\n\n- `category` 用于前端分组，常见值是 `buildin`、`knowledge` 和 `debug`；\n- `tags` 用于展示和筛选；\n- `display_name` 是给用户看的名称，工具 ID 是给代码和模型协议使用的稳定名称；\n- 工具模块需要被 `toolkits` 包导入，装饰器才会执行注册。\n\n工具的注册不等于授权。产生文件、网络或数据库副作用的工具必须在执行边界再次校验当前用户和目标资源。\n\n## 当前内置能力\n\n常用内置工具包括：\n\n| 工具 | 作用 |\n| --- | --- |\n| `ask_user_question` | 等待用户回答交互式问题 |\n| `ocr_parse_file` | 把工作区中受支持的 PDF、Office 或图片转换为 Markdown |\n| `present_artifacts` | 展示当前用户可见的文件产物 |\n| `install_skill` | 从允许的沙盒路径或 Git 来源安装个人 Skill；子智能体不可用 |\n| `web_search` | 使用已配置的豆包或 Tavily 搜索网页 |\n\n文件读写和命令执行由 Agent 的 Sandbox backend 提供。`present_artifacts` 推荐展示当前 Project 的 `outputs/` 文件；`large_tool_results` 和会话摘要等内部文件不会作为交付物展示。\n\n图片生成能力由内置 `image-gen` Skill 提供，不再作为独立的 Python 工具注册。具体依赖和文件位置由该 Skill 说明。\n\n## 知识库工具\n\n知识库工具以 `@tool(category=\"knowledge\")` 注册，但不默认出现在模型工具列表。Agent 激活内置 `knowledge-base` Skill 后，Skills middleware 才会向模型开放：\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_kbs` | 列出当前运行可见的知识库 |\n| `query_kb` | 按 `kb_id` 检索片段，返回 `kb_id`、`file_id` 和内容 |\n| `find_kb_document` | 在指定文件中按关键词或正则定位内容 |\n| `open_kb_document` | 按 `file_id` 分段读取解析后的文档 |\n| `get_mindmap` | 读取知识导图 |\n| `search_file` | 按文件名搜索可见知识库中的文件 |\n| `download_kb_file` | 把有权限的原始文件下载到当前 Project 的 `outputs/` |\n\n工具参数中的 `kb_id`、`file_id` 和文件名都会在工具执行处重新检查，不能用模型提示词或 Agent 配置绕过知识库权限。知识库不会挂载为沙盒目录，读取方式见[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n需要在 Python 中直接取得知识库工具时：\n\n```python\nfrom yuxi.agents.toolkits.kbs import get_common_kb_tools\n\nkb_tools = get_common_kb_tools()\n```\n\n返回的具体顺序由函数实现维护，不要把顺序当作协议。\n\n## 内置工具：网页搜索\n\n`web_search` 让 Agent 从外部网页获取实时信息，当前支持豆包和 Tavily 两个供应商。搜索结果属于外部来源，不能自动替代知识库或人工核验；对重要结论应回到原网页核实。\n\n供应商在 API/worker 环境中选择并提供凭证：\n\n```bash\nWEB_SEARCH_PROVIDER=doubao\nDOUBAO_SEARCH_API_KEY=<your-doubao-key>\n```\n\n或 `WEB_SEARCH_PROVIDER=tavily` 加 `TAVILY_API_KEY=<your-tavily-key>`。`WEB_SEARCH_PROVIDER` 只接受这两个值；留空时按已配置的 API Key 自动选择，两个 Key 都配置时优先豆包，没有有效 Key 时不会注册该工具。密钥只放在受保护的环境变量或密钥管理器中，不要写进 Skill、Agent 用户环境、代码或日志。\n\n工具在 API 和 worker 进程加载时注册，修改环境变量后需要 `docker compose up -d --force-recreate api worker`。验证方式：在智能体详情确认工具列表出现「网页搜索」，用一个需要最新信息的问题发起真实对话，并检查工具调用返回的 URL、标题和摘要。没有该工具时检查 `WEB_SEARCH_PROVIDER` 拼写、对应 Key 是否存在以及容器是否已重建。\n\n实现入口：[网页搜索工具](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/toolkits/buildin/tools.py)。\n\n## 工具组装流程\n\n内置 Agent 创建 Graph 时执行：\n\n1. `prepare_agent_runtime_context` 按当前用户权限过滤工具、知识库、MCP、Skills 和子智能体。\n2. `resolve_configured_runtime_tools(context)` 注册 Agent 配置和可见 Skill 依赖的可执行本地工具，并加载配置的 MCP 工具。\n3. `SkillsMiddleware` 根据当前已预加载或已激活的 Skill，向模型请求开放相应工具 schema。\n4. 工具执行器再次检查具体文件、知识库、MCP 和用户身份。\n\n```text\nAgent 配置 + 用户权限\n        ↓\n运行时资源快照\n        ↓\n可执行工具注册 ──→ Skill 激活门控 ──→ 模型可见工具\n        ↓\n执行处的目标授权\n```\n\n“可执行”与“模型可见”是两件事：工具可以先注册到 ToolNode，等 Skill 激活后才对模型开放；模型看见某个工具也不代表它可以访问任意资源。\n\n## MCP 和 Skills\n\nMCP 工具可由 Agent 直接选择的已启用服务器提供，也可由已激活 Skill 的 `mcp_dependencies` 按需加载；预加载 Skill 从首轮开放本地工具和 MCP 依赖，普通 Skill 在读取 `SKILL.md` 激活后开放这些依赖。这一节只回答“工具从哪里来、谁决定模型可见”；服务器配置和启停归 [MCP 集成](./mcp-integration.md)，Skill 的创建、依赖与激活时机归 [Skills 管理](./skills-management.md)，资源选择的字段语义归[智能体配置](./agents-config.md)。\n\n## 新增工具时检查\n\n- 工具是否属于内置工具、知识库工具、MCP 还是 Skill；\n- 是否需要在 `ToolNode` 注册，是否需要 Skill 激活后才让模型看见；\n- 产生副作用时，执行处是否有用户、路径和资源权限校验；\n- 错误是否以结构化结果返回，并保留可排查信息；\n- 前端展示名称是否来自稳定元数据；\n- 是否补充纯逻辑和真实 HTTP/文件链路的相应测试。\n","packages/yuxi-cli/README.md":"# yuxi-cli\n\nYuxi command line client.\n\nFirst-stage scope:\n\n- remote management through `~/.yuxi/config.toml`\n- browser login\n- API Key import through `--api-key`\n- `whoami`, `status`, and `logout`\n- server discovery and compatibility check for Yuxi `>=0.7.1`\n- `yuxi chat` for a temporary local browser chat with streamed Agent output; `/state` reads thread state and `/approve` resumes a pending tool approval\n- `yuxi agent list` and `yuxi agent show <slug>` for inspecting agents visible to the logged-in user\n- `yuxi kb upload` for knowledge base file uploads\n- `yuxi agent eval` for running existing Langfuse dataset experiments with a logged-in remote\n","web/AGENTS.md":"# Web 约定\n\n本目录是 Vue 3 / Vite 前端。先阅读根 [AGENTS.md](../AGENTS.md)、[设计规范](../docs/develop-guides/design.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n- API 调用统一放在 `src/apis`；组件不直接拼接普通 HTTP 请求。\n- 前端权限与路由守卫只提供体验约束，后端始终执行最终授权。\n- 复用 `src/assets/css/base.css` 变量和 `@lucide/vue`；不为一次性视觉需求引入新依赖。\n- 保持 loading、empty、error、断线恢复和终态投影语义一致；不要用乐观 UI 覆盖 PostgreSQL 返回的最终事实。\n- `pnpm run lint:check` 是只读 gate；`pnpm run lint` 才允许本地自动修复。\n\n提交前运行：\n\n```bash\ndocker compose exec web pnpm run lint:check\ndocker compose exec web pnpm run test:unit\ndocker compose exec web pnpm run build\n```\n\nUI 改动必须在真实页面验证，并提供最终截图或录屏；适用时覆盖浅/深色、响应式、loading、empty 和 error 状态。\n"},"files":{"AGENTS.md":"# Yuxi Agent 开发约定\n\nYuxi 是基于 LangGraph、FastAPI、Vue 和多种持久化服务构建的知识库与多智能体平台。Docker Compose 是开发拓扑的事实来源；修改不熟悉的模块前先阅读 [ARCHITECTURE.md](ARCHITECTURE.md)，再用符号搜索确认真实实现。\n\n## 每次任务先加载什么\n\n- [ARCHITECTURE.md](ARCHITECTURE.md)：稳定边界、主链路和架构不变量。\n- [Yuxi Spec Loop](docs/develop-guides/spec-loop.md)：非平凡变更从提案、证据到收敛的流程。\n- [工程信任系统](docs/develop-guides/engineering-trust.md)：语义 Owner、证据、决策记录、派生审计和 gate 规则。\n- [测试规范](docs/develop-guides/testing-guidelines.md)：unit、integration、E2E 的职责与命令。\n- [贡献指南](docs/develop-guides/contributing.md)：分支、独立 Review、commit 和 PR 流程。\n- [并行工作树与隔离运行环境](docs/develop-guides/parallel-worktree-environments.md)：同时运行多个分支、复用长期数据或处理 Schema 不兼容时加载。\n- 用户在当前任务中的明确要求优先于本文件；修改 `backend/`、`web/` 或 `docs/` 时同时遵循该子树的 `AGENTS.md`。子树规则只补充本目录，不复制回根文件。\n\n## 任务与决策\n\n1. 开始实现前，把请求压缩为可验证的目标、非目标和验收主张；多步任务先给出每步带验证方式的简短计划。\n2. 假设显式记录后继续；只有当不同解释会改变验收结果、数据、安全或外部状态时才阻塞询问，并把候选解释一并列出而不是默默选择其一。存在明显更简单的方案时，先说明取舍再动手。\n3. 把\"可以\"\"也可以\"\"类似这样\"\"例如\"当作简单方向，不是设计更大机制、配置项或兼容层的许可。\n4. 在真实语义 Owner 处闭合受影响的工程主张：源码、数据约束或当前契约拥有事实；独立 oracle 与负向案例证明它；实际 workflow 或可问责 Reviewer 产生拒绝后果。不要建立可独立编辑的中央主张清单或 claim ID 体系。\n5. 非平凡变更必须新增或更新一份 tracked [决策记录](docs/develop-guides/decisions/README.md)。记录问题、当前决定、真实替代项、后果和验证，不保存推理流水账或实现过程叙事。\n6. `docs/vibe/` 只用于本地临时计划，被 Git 忽略，不是组织记忆，也不能作为已完成事实的唯一来源。\n7. 只修改验收标准需要的范围；不顺手重构、格式化或添加想象中的配置、兼容层和扩展点。\n\n## 不能破坏的系统事实\n\n- HTTP 路由保持薄；用例流程属于 `yuxi.services`，持久化查询属于 `yuxi.repositories`。\n- 普通请求先在 PostgreSQL 中持久化 Message 和 AgentRunRequest；只有 ready FIFO 队头创建 AgentRun，且每次投递 ARQ 前 owning transaction 都已提交。Redis 负责投递、短期事件、取消和缓存，不拥有最终业务状态。\n- 同一用户、Agent、线程的普通请求按 FIFO 串行派发；Request 和 Run 是不同状态模型。\n- AgentRun 的输出、事件、artifact 和错误必须绑定同一 request/run；禁止从相邻 Run 猜测结果。\n- 非终态 Run 必须有明确执行 Owner、lease/heartbeat 或等价机制，以及崩溃后的可观察结局。\n- `/api/system/health` 只表达进程 liveness；接流量前置条件由 `/api/system/ready` 证明，业务正确性仍由真实链路测试证明。\n- LangGraph checkpoint 只使用 PostgreSQL；API、worker 与 Agent 不提供本地后端选择或静默降级。\n- 权限在后端依赖与 repository 可见性查询处最终执行；前端守卫、prompt、schema omission 和 UI 隐藏不是授权边界。\n- Shipping 启动、路由注册和能力发现始终包含知识库、图谱与评估能力；附件解析入口只在真实解析动作发生时惰性加载 parser。\n- 沙盒虚拟路径、对象 URL 和宿主机路径不可混用；所有用户路径必须在 owning filesystem boundary 校验。\n\n## 证据规则\n\n- Agent 的完成报告、HTTP 200、日志关键词或 mock 调用次数都不是最终事实；重新读取数据库、文件、对象、DOM 或协议结果。\n- 每个新 guard 都要有一个能恢复目标缺陷并使其在正确原因上失败的负向测试。\n- 测试从最小相关集合开始，再按风险扩大；不要用 unit 结果替代真实 PostgreSQL、真实 HTTP、worker 或浏览器语义。\n- Expected output、snapshot 和 fixture 只能显式更新并审阅；CI 不得一边生成 oracle 一边验证 oracle。\n- 实际执行命令、结果和未执行原因写入 PR；未验证不能写成通过。\n\n提交前在仓库根目录至少运行：\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\n```\n\n改动面按下表选择最低证据，并按升级条件扩大；不要用低层级结果替代高风险语义。\n\n| 改动面 | 最低证据 | 何时升级 |\n|---|---|---|\n| 纯 Python / JS 逻辑 | 相关 unit，断言业务结果 | 触及数据库、缓存或文件副作用时补 integration |\n| API / 权限 / 持久化 | 真实 HTTP integration | 跨 worker、队列或用户主链路时补 E2E |\n| Run / FIFO / SSE / 沙盒 / 恢复 | E2E，验证最终状态与产物 | 依赖外部可选服务时记录环境与未验证范围 |\n| 前端交互 | lint + unit（[web/AGENTS.md](web/AGENTS.md)） | 行为关键时补 build 与真实页面验证 |\n| 文档与导航 | 相对链接检查 + docs build | 公开行为变化时与代码验证一起执行 |\n\n完整命令由 [测试规范](docs/develop-guides/testing-guidelines.md) 维护。\n\n## 实现与安全\n\n- 使用满足验收标准的最小、线性实现。抽象、状态机、fallback、兼容路径和依赖都要有当前 consumer 与 Owner；仓库外用户、持久数据和部署承诺也算 consumer。\n- 实现明显长于问题本身，或一半行数可以清楚表达同样行为时，先简化再提交；自检\"高级工程师是否会认为这段代码过度设计、过度防御或过于零碎\"。\n- 不可信输入只在 parser、配置、模型/tool JSON、持久化、worker、process、wire 和用户路径等真实信任边界校验；授权与隔离在产生副作用的 executor/repository fail-closed，直接或替代调用路径都不能绕过。\n- 预设条件不成立时显式失败。可选能力可以降级，但必须结构化、可观察，且不把未就绪伪装为成功。\n- 新增函数/类使用简洁中文 docstring；注释只解释非显然约束、时序、Owner 和安全用法，不复述代码。\n- 不输出或提交 `.env`、账号、Token、用户数据、运行目录和构建产物。\n- 文件以一个换行结尾；提交前运行 `git diff --check`。\n\n## Review 与交付\n\n所有代码变更在 commit 前必须由不继承开发上下文的全新 Reviewer Agent 审查完整需求、diff、测试和规范；修复所有影响功能、边界、证据可信度或认知负担的问题。提交信息使用中文 Conventional Commit，PR 使用仓库模板，以自然语言列出受影响主张、语义 Owner、证据、风险和未验证范围，不引用中央 claim ID。\n","CLAUDE.md":"AGENTS.md","backend/AGENTS.md":"# Backend 约定\n\n本目录承载 FastAPI 适配层、业务服务、repositories、Agent runtime 与持久化实现。先阅读根 [AGENTS.md](../AGENTS.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n## 边界与所有权\n\n- `server/routers` 只处理 HTTP 模型、认证依赖、状态码和响应装配；跨 repository 的用例进入 `package/yuxi/services`。\n- PostgreSQL 是 Request、Run、Message、权限和业务终态的 Owner；Redis/ARQ 是投递与短期事件平面。\n- 写入事实、提交事务、发布队列/事件的顺序必须显式；通知不能早于 owning transaction 的 commit point。\n- 跨 repository 用例只有一个事务 Owner；需要经 HTTP 返回的一次性 secret 必须可由幂等请求安全重放，不能先不可逆消费再祈望响应送达。凭据撤销必须保留足以阻止同一幂等请求复活 secret 的 tombstone。\n- parser、HTTP、模型/tool JSON、持久化、worker、process、wire 和用户路径是运行时校验边界；已由 Python 类型和同进程调用保证的内部值不重复 hostile validation。\n- 核心启动依赖失败要阻止 readiness；可选集成降级要记录组件名、失败类型与当前能力。\n- AgentRun 的状态转换、lease、输出和终态投影由 repository/service 统一维护，调用方不得直接拼装并行真相。\n\n## 实现\n\n- Python 使用 3.12+ 语法；保持主流程线性，优先早返回和清晰主路径，避免细碎 helper、静默 fallback、一次性抽象、不必要的嵌套和多层调用链。\n- 本次新增或实质修改的代码遵循向下规则：同一模块或类内，公开、高层方法在上，专用实现细节在后；旧代码布局不豁免新改动。可调整直接相关定义的位置，不重排整个文件；基类、装饰器、默认值和模块初始化等定义时依赖优先满足 Python 执行顺序。Reviewer 核对局部阅读顺序。\n- 拆函数用于明确复用、隔离副作用或实质降低认知负担；实现细节遮蔽主流程时，即使只调用一次也可提取。仅转发参数或包装表达式且不增加语义的 helper 通常内联。\n- 常量和具名值放在最小合理作用域：跨函数复用、协议标识或配置约束用模块级常量；仅服务单个函数的局部规则留在函数内。\n- 命名表达业务意图；不导入其他模块下划线开头的私有标识，确有共享需求时改为公开命名。\n- 删除本次修改产生的未使用 import、变量、函数和分支；不清理修改前已存在的无关死代码，发现坏味道只说明不擅自处理。\n- 小型状态、进度或摘要需求直接读取来源并返回最小结果，不重建事件流或调试视图。\n- 新增函数/类使用简洁中文 docstring；不同语义的代码段之间留空行保持可读。异常只在当前层能增加稳定语义、执行清理或决定策略时捕获。\n- Schema 演进必须幂等、可在现有数据上执行，并有真实 PostgreSQL 测试；不可逆操作明确数据影响。\n\n## 验证\n\n```bash\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\ndocker compose exec api uv run --group test pytest test/integration\ndocker compose exec api uv run --group test pytest test/e2e -m e2e\ndocker compose exec api uv run ruff check package\ndocker compose exec api uv run ruff format package --check\n```\n\n并发、事务、锁、lease、schema 与 PostgreSQL 专属语义必须在真实 PostgreSQL 上验证；API 行为通过真实 HTTP integration 证明；关键 Run/worker/文件副作用通过 E2E 证明。\n","backend/package/yuxi/agents/skills/buildin/deep-research/SKILL.md":"---\nname: deep-research\ndescription: \"深度研究编排方法论：澄清范围、拆解规划、并行调度子智能体调研、对抗式核验、综合成带引用的结构化报告。当任务需要多来源、可追溯、需事实核查的深度研究时使用此技能。\"\nversion: \"2026.09.19\"\ntool_dependencies: [\"web_search\", \"present_artifacts\"]\n---\n\n# 深度研究技能\n\n当任务目标是产出**多来源、可追溯、经过核验**的深度研究结论（科研综述、行业/竞品调研、技术选型、专题分析等）时，使用此技能组织整个研究过程。本技能的核心是**编排**：你负责整体把控与子智能体调度，把繁重的检索与核验工作派发出去，自己专注规划与综合。\n\n## 可用子智能体\n\n通过 `subagent_start` 工具派发，记录返回的 run_id；需要结果时调用 `subagent_await`。互不依赖的子任务先全部派发，再等待结果：\n\n- `research-explorer`（调研探索员）：围绕一个明确子问题做多轮网页/知识库检索，返回按要点组织、带 `<cite>` 引用的结构化发现。**这是主力，按子问题并行多开。**\n- `fact-verifier`（事实核查员）：对给定的关键论断做对抗式核验，逐条给出 支持 / 存疑 / 反驳 + 依据来源 + 置信度，并标注冲突。\n\n## 编排流程\n\n### 1. 澄清范围\n问题不明确时，先用 `ask_user_question` 补充 2-3 个关键问题（研究目标、受众、范围边界、地域/时效、输出语言与形式），对齐验收标准后再开工。已经清晰的任务不要反复追问。\n\n### 2. 规划拆解\n用 `write_todos` 把研究目标拆成**可独立调研**的子问题，每个子问题写明产出标准（要回答什么、需要哪类证据）。子问题应正交、覆盖完整，避免重叠或遗漏关键角度。\n\n### 3. 并行派发调研\n- 把互不依赖的子问题用**多个 `subagent_start` 调用并行**派发给 `research-explorer`。\n- 每次派发在 `description` 中写清：子问题目标、已知上下文、期望输出格式（要点 + `<cite source=\"$URL\" type=\"url\">$INDEX</cite>` 引用 + 参考来源列表）。\n- 何时派发 vs 自己直检：子问题复杂、需多轮检索、可隔离上下文、可并行时一律派发子智能体；仅在澄清范围、补一两个零散事实、或快速校正方向时才自己少量直接检索。\n- 子问题之间有依赖时，先派发前置子问题，拿到结果后再派发后续。\n\n### 4. 核验关键结论\n对**影响最终结论的关键论断**、数字、以及子智能体之间相互冲突的发现，派发 `fact-verifier` 做对抗式核验。要求其默认倾向「证据不足即标注存疑」。核验未通过的结论不要写进正文，或必须明确降级标注。\n\n### 5. 综合成稿\n证据充分后，由你统一综合为结构化报告，**不要**简单拼接子智能体返回的原文。根据研究主题组织内容，围绕论证而不是资料堆砌。\n\n### 6. 停止准则\n信息饱和、或确认无法获取更多有效信息即停。明确标注证据缺口与不确定性，不臆断、不编造来源。\n\n## 引用\n\n- 子智能体返回中间证据时沿用 `<cite source=\"$URL\" type=\"url\">$INDEX</cite>`；最终报告中的关键结论、数据和观点用指向原始来源的普通 `<a href=\"...\">` 链接就近标注。\n- 引用用户附件或知识库时标明文件名或路径。\n\n## 输出\n\n- 最终交付的是一份可直接使用的报告，而不是「我打算怎么研究」。\n- 不要外泄中间推理过程、原始检索日志，也不要把待办清单原样输出成正文。\n- 报告语言与用户提问语言一致，使用正式、克制、可复核的书面表达。\n- 用户没有指定格式时，默认把完整报告写入当前 Workdir 的 `outputs/` 目录，保存为一个 `.html` 文档；不要把完整报告直接铺在聊天正文里。用户明确要求 Markdown、纯文本、JSON 或其他格式时，以用户要求为准。\n- 从系统提示读取当前 Project Workdir 的**绝对虚拟路径**，用 `write_file` 写入 `<当前 Workdir 绝对路径>/outputs/<文件名>.html`。不要把 `outputs/report.html`、宿主机路径或 `/outputs/report.html` 传给文件工具。\n- 写入成功后，把 `write_file` 返回的同一绝对路径传给 `present_artifacts`。最终聊天回复只需简要说明报告已完成并提示交付物，不要重复报告正文。\n\n默认 HTML 应简洁、优雅并专注阅读，同时给模型保留版式判断空间：\n\n- 页面与正文宽度随视口自适应。宽屏可以使用侧栏目录帮助浏览章节，窄屏隐藏或折叠侧栏，让正文自然占满可用宽度。\n- 可以按内容需要引用外部样式、字体、图片或其他公开资源；图片应服务于研究内容，而不是纯装饰。\n- 不提供固定 HTML 模板；根据研究主题选择合适的章节、图表、表格和视觉层级，整体保持克制、清晰，不做复杂浮夸的展示页。\n","backend/package/yuxi/agents/skills/buildin/html-preview/SKILL.md":"---\nname: html-preview\ndescription: \"使用 Markdown `html:preview` 围栏输出轻量静态 HTML/CSS 可视化。当普通 Markdown 难以清晰表达数值对比、层级关系、流程、时间线、关键指标或布局示意，或用户明确要求可视化 HTML 预览时使用。询问 HTML 源码、教程示例或可复制代码时不要使用。\"\nversion: \"2026.07.23\"\n---\n\n# HTML Preview\n\n将 Markdown 作为回答主体。只有可视化能明显降低理解成本时，才补充一个\n`html:preview` 静态组件；标题、列表、表格或代码块已经足够清楚时，继续使用普通 Markdown。\n\n按以下顺序输出：\n\n1. 先用普通 Markdown 写结论和必要解释。\n2. 顶格写开始围栏 ```` ```html:preview ````。围栏最多只能有 3 个前导空格，否则前端会把它当作源码。\n3. 在围栏内写自包含的静态 `<style>` 和语义化 HTML。\n4. 顶格写结束围栏 ```` ``` ````。\n5. 在围栏后用普通 Markdown 补充背景、风险、完整明细和来源。\n\n## 内容边界\n\n- 只输出静态 HTML/CSS，不编写 JavaScript，不使用表单、iframe、object 或 embed。\n- 可以引用公开、稳定、无需登录的 HTTPS 图片或字体，但核心信息必须在外链失败时仍然可读。\n- 不引用内网、临时地址、鉴权资源或包含用户凭证的链接。\n- 不把组件当成完整网页或正文容器；不要制作导航栏、页脚、登录态、复杂按钮、营销页 Hero 或多屏页面。\n- 不在组件中放长段叙事、完整报告、长表格或长列表。背景、推理、风险、明细和来源放在普通 Markdown 中。\n- 用户询问 HTML 源码、教程示例或需要复制代码时，使用普通 `html` 代码块，不使用 `html:preview`。\n\n## 布局约束\n\n- 面向默认 `800px × 360px` 尺寸设计，并允许容器宽度变化；前端最高显示到 `700px`，超出后在预览内部滚动。\n- 使用 `max-width: 100%`、`box-sizing: border-box`、弹性网格、换行和适度间距保持响应式。\n- 不写死整体画布高度。核心信息应在默认尺寸内无需滚动即可读；放不下时减少内容。\n- 最多展示 1 个短标题、3–5 个关键指标或一组简短对比。数据超过 6 项时，汇总为趋势、区间、异常点或 Top 3。\n- 文字以短标签、数字、单位、状态词和极短备注为主；单条说明尽量不超过 20 个中文字符。\n- 外层已经提供圆角、边框和裁切。不要再给最外层添加卡片壳、页面背景、大圆角、阴影、厚边框或额外外边距。\n- 保持克制和可读，优先使用紧凑指标组、摘要表、对比条、状态标签、时间轴或简单关系图。\n\n## 失败处理\n\n如果内容无法在上述静态、安全和篇幅边界内表达，改用普通 Markdown，并明确说明限制。不要输出残缺围栏、不可访问资源或依赖脚本才能理解的预览。\n","backend/package/yuxi/agents/skills/buildin/image-gen/SKILL.md":"---\nname: image-gen\ndescription: \"在 Agent 沙盒中生成图片并保存到 outputs。当用户要求生成图片、海报、插画、文生图，或指定 Qwen-Image、其它兼容图片生成接口时使用此技能。\"\nversion: \"2026.06.02\"\ntool_dependencies: [\"present_artifacts\"]\n---\n\n# 图片生成技能\n\n当用户要求生成图片、海报、插画、文生图，或明确提到 Qwen-Image 时，使用此技能组织图片生成流程。\n\n## 默认生成接口\n\n默认使用 SiliconFlow 的 Qwen-Image 接口：\n\n- Endpoint: `POST https://api.siliconflow.cn/v1/images/generations`\n- Model: `Qwen/Qwen-Image`\n- 默认参数：\n  - `negative_prompt`: `\"\"`\n  - `num_inference_steps`: `20`\n  - `guidance_scale`: `7.5`\n\n调用外部接口时，必须在 Agent 沙盒执行环境中读取 `SILICONFLOW_API_KEY`。不要依赖后端进程环境变量。\n\n## 操作流程\n\n1. 明确用户要生成的图片内容、风格、尺寸或约束；信息不足但不影响生成时，使用合理默认值，不要反复追问。\n2. 使用可用的执行工具在沙盒中运行脚本，调用图片生成接口，传入用户需求整理后的 `prompt`，并按需传入 `negative_prompt`、`num_inference_steps`、`guidance_scale`。\n3. 从生成接口响应中读取图片地址，默认路径为 `images[0].url`。\n4. 在同一个沙盒脚本中用 `Authorization: Bearer $SILICONFLOW_API_KEY` 下载该图片地址；如果接口直接返回 base64，则直接解码保存。\n5. 将最终图片保存到当前 Workdir 的 `outputs/` 下，例如 `outputs/generated-image.png`。\n6. 调用 `present_artifacts`，传入保存后的 outputs 虚拟路径，让前端展示图片产物。\n7. 最终回复简要说明图片已生成，不要把外部临时 URL 当作最终结果展示。\n\n## 脚本示例\n\n可根据用户需求调整 prompt 和输出文件名：\n\n```python\nimport os\nimport requests\nfrom pathlib import Path\n\napi_key = os.environ[\"SILICONFLOW_API_KEY\"]\nprompt = \"根据用户需求整理后的图片提示词\"\n\nresponse = requests.post(\n    \"https://api.siliconflow.cn/v1/images/generations\",\n    headers={\"Authorization\": f\"Bearer {api_key}\", \"Content-Type\": \"application/json\"},\n    json={\n        \"model\": \"Qwen/Qwen-Image\",\n        \"prompt\": prompt,\n        \"negative_prompt\": \"\",\n        \"num_inference_steps\": 20,\n        \"guidance_scale\": 7.5,\n    },\n    timeout=120,\n)\nresponse.raise_for_status()\nimage_url = response.json()[\"images\"][0][\"url\"]\n\nimage_response = requests.get(\n    image_url,\n    headers={\"Authorization\": f\"Bearer {api_key}\"},\n    timeout=120,\n)\nimage_response.raise_for_status()\n\noutput_path = Path(\"outputs/generated-image.png\")\noutput_path.parent.mkdir(parents=True, exist_ok=True)\noutput_path.write_bytes(image_response.content)\nprint(output_path.as_posix())\n```\n\n## 多模型扩展\n\n如果用户指定其它图片生成模型或兼容接口，可以按该接口的协议先生成图片。只要最终拿到图片 bytes 或 base64，就保存到当前 Workdir 的 `outputs/`，再调用 `present_artifacts` 展示。\n\n## 关键约束\n\n- 不要把外部生成接口返回的临时 URL 当作最终结果直接展示给用户。\n- 不要调用后端 MinIO 上传工具；图片生成和下载都应在沙盒内完成。\n- 如果 `SILICONFLOW_API_KEY` 缺失，应明确提示用户需要在 Agent 沙盒环境变量中配置。\n- 保存到 outputs 后必须调用 `present_artifacts`，否则前端不会自动展示生成图片。\n","backend/package/yuxi/agents/skills/buildin/knowledge-base/SKILL.md":"---\nname: knowledge-base\nslug: knowledge-base\ndescription: \"使用 Yuxi 知识库进行检索、打开文档、文档内定位和查看思维导图。当用户需要基于已配置知识库回答问题、核验资料或引用文档内容时使用此技能。\"\nversion: \"2026.06.24\"\ntool_dependencies: [\"list_kbs\", \"query_kb\", \"find_kb_document\", \"open_kb_document\", \"get_mindmap\", \"search_file\", \"download_kb_file\"]\n---\n\n# 知识库技能\n\n当用户要求基于项目知识库、内部资料、上传入库文档或知识图谱相关内容回答问题时，使用此技能。\n\n## 可用工具\n\n- `list_kbs`：列出当前会话可访问且已启用的知识库。\n- `query_kb`：按 `kb_id` 在指定知识库中检索内容，返回 `file_id` 和相关片段。\n- `open_kb_document`：按 `kb_id` 和 `file_id` 打开文档原文窗口，适合查看更完整上下文。\n- `find_kb_document`：在已知文档内用关键词或正则定位段落。\n- `get_mindmap`：查看知识库思维导图结构。\n- `search_file`：按文件名关键词搜索知识库中的文件，支持指定知识库或跨知识库，返回文件列表与分页信息。\n- `download_kb_file`：按 `kb_id` 和 `file_id` 下载知识库原始二进制（pdf/docx/xlsx 等）到沙盒 `outputs` 目录，返回沙盒内可见的 `virtual_path`。当后续需要用代码读取原始文件结构（如 `openpyxl` 读 xlsx 单元格、`pdfplumber` 重新解析版面）时使用；`query_kb`/`open_kb_document` 只返回文本切片，无法满足需要文件对象的场景。\n\n## 操作流程\n\n1. 需要先确认当前会话有哪些知识库可用；不确定时调用 `list_kbs`。\n2. 针对用户问题选择最相关的知识库，使用 `query_kb` 检索。\n3. 检索结果的 `metadata.chunk_count == 1` 表示文件只有一个分片；除非需要原文行号或额外上下文，否则无需打开。大于 1 且当前片段不足以回答时，使用返回的 `file_id` 调用 `open_kb_document` 查看上下文。字段缺失或为 0 表示未知，不据此判断文档完整。\n4. 如果用户要求定位术语、指标、章节或原文证据，使用 `find_kb_document` 在候选文档内查找。\n5. 当用户关心知识库结构、文件分类或知识框架时，使用 `get_mindmap`。\n\n## 关键约束\n\n- 只能访问当前会话配置和用户权限允许的知识库。\n- 不要编造 `kb_id` 或 `file_id`；优先从 `list_kbs` 和 `query_kb` 的返回结果中获取。\n- 回答需要可追溯时，应说明依据来自哪个知识库、文件或检索片段。\n- Dify 等外部只读知识库可能只支持检索，不一定支持打开全文或文档内查找；遇到工具返回限制说明时，应如实告知用户。\n","backend/package/yuxi/agents/skills/buildin/mysql-reporter/SKILL.md":"---\nname: mysql reporter\nslug: mysql-reporter\ndescription: \"生成 MySQL 查询报表。当用户需要查询 MySQL 数据库并以报表形式展示结果时使用此技能，包括：统计销售数据、分析用户行为、生成业务报表、查询业务指标等。\"\nversion: \"2026.09.17\"\n---\n\n# MySQL 报表技能\n\n根据用户的指令，通过终端脚本访问 MySQL 数据库，根据查询结果构建报表。\n\n## 操作流程\n\n1. 理解用户的指令，明确报表的需求和目标\n2. 通过 terminal 进入技能目录：`cd /home/gem/skills/mysql-reporter`\n3. 使用 `uv run scripts/list_tables.py` 查看可用表；如果脚本提示缺少 MySQL 配置，按“环境变量缺失处理”回复用户\n4. 必要时用 `uv run scripts/describe_table.py --table 表名` 查看表结构\n5. 生成正确且高效的只读 SQL，通过 `uv run scripts/query.py --sql \"SQL语句\" --timeout 60` 执行查询并获取结果\n6. 使用 Markdown 表格呈现查询结果，说明关键指标、变化与结论\n\n## 环境变量缺失处理\n\n脚本只读取 Agent 沙盒中的环境变量，不读取后端 `.env` 或 Docker Compose 变量。必填变量包括：\n\n- `MYSQL_HOST`\n- `MYSQL_USER`\n- `MYSQL_PASSWORD`\n- `MYSQL_DATABASE`\n\n可选变量包括：\n\n- `MYSQL_PORT`：默认 `3306`\n- `MYSQL_DATABASE_DESCRIPTION`：数据库业务说明，用于辅助理解表和指标含义\n\n如果执行脚本时出现 `MySQL configuration missing required key`，不要继续猜测连接信息或编造报表。应明确告诉用户：需要在个人设置中的「沙盒环境变量」里配置缺失的 `MYSQL_*` 变量；保存后仅对新建沙盒生效，需要重新发起任务或新建会话后再执行。\n\n## 关键约束\n\n- 生成的 SQL 查询必须正确且高效，避免全表扫描\n- MySQL 操作必须通过本技能 `scripts/` 下的 CLI 脚本执行，不要调用平台内置 MySQL tools\n- 不要在报表或错误说明中输出 `MYSQL_PASSWORD` 等敏感环境变量的值，只能说明缺少哪些变量名\n- 只返回报表相关的结论，不要返回原始 SQL 查询语句\n\n## 允许的工具\n\n- terminal：执行 `scripts/list_tables.py`、`scripts/describe_table.py`、`scripts/query.py`\n- 网络检索工具：必要时补充背景信息\n","docs/AGENTS.md":"# 文档约定\n\n本目录包含用户教程、配置参考、机制详解、开发规范、决策记录、事故复盘和版本材料。仓库根 `AGENTS.md` 的系统不变量仍然适用；完整信息架构、写作标准与检查清单由[文档编写与维护规范](develop-guides/documentation-guidelines.md)拥有，本文件只保留进入 `docs/` 工作时必须执行的指令。\n\n## 层级与事实 Owner\n\n- 每个事实只有一个负责完整解释的 Owner；其他页面只保留完成当前任务需要的上下文并使用相对链接。先按[层级表](develop-guides/documentation-guidelines.md#信息架构与事实-owner)确定页面位置，再写内容。\n- `intro/` 拥有从零完成结果的教程，`advanced/` 拥有配置和运维参考，`agents/` 拥有 Agent 配置与扩展方法，`mechanisms/` 拥有运行机制、状态、权限、失败和源码定位。实质性混合内容必须拆页。\n- 当前系统边界和主链路属于仓库根 `ARCHITECTURE.md`；测试层级与命令属于[测试规范](develop-guides/testing-guidelines.md)；工程信任闭环属于[工程信任系统](develop-guides/engineering-trust.md)。不要在专题页复制这些完整规则。\n- 非显然取舍属于 `develop-guides/decisions/`，达到门槛的事故因果属于 `develop-guides/postmortems/`，已发布事实属于 changelog，未完成方向属于 roadmap。`docs/vibe/` 只用于被忽略的本地临时计划。\n- 源码、schema、Compose、数据约束和测试拥有可执行事实；外部 Wiki、旧 changelog、历史 PR 和 Agent 自述只能帮助定位，不能覆盖当前 Owner。\n\n## Agent 写作流程\n\n1. 读取根与当前子树指令、`ARCHITECTURE.md`、owning 文档和相关源码；非平凡信息架构或长期约束变化先创建 tracked proposed decision。\n2. 写明预定读者、问题、前置知识、目标、非目标和页面类型。教程必须有顺序结果；参考必须有明确查找范围；实质性混合内容先拆分。\n3. 用符号搜索沿入口 → service/executor → repository/持久化或发布点 → 用户/模型结果重建真实链路，同时核对权限、失败路径、可选能力和相关测试。\n4. 先列 Section 及每节唯一问题，按“概念 → 组件关系 → 状态/Owner → 权限/失败 → 源码定位”渐进；教程按完成任务所需的前置依赖排序。\n5. 一次最多撰写或重写一个 Section。完成后立即核对事实、相对链接、与相邻章节的重复及当前页面职责，再进入下一节。\n6. 先更新事实 Owner，再更新导航、索引和引用页。页面移动或拆分必须在同一变更中修复全部入站链接，不保留两个可独立编辑的副本。\n7. 提交前运行最小相关 gate，并由不继承开发上下文的全新 Reviewer 对照完整需求、源码、diff、测试和未验证范围审查。\n\n## 写作与维护\n\n- 使用直接、具体、可验证的现在时，写明执行者、条件、时序、结果、失败和后果。不要用“应该、一般、可能”掩盖未核实事实；无法核实时明确条件与 `Not run` 范围。\n- 优先用肯定句直接陈述职责、边界和结论。禁止先否定一个弱命题、再用转折强调目标命题的对举句式；删除空泛铺垫、宣传性形容词和替读者下判断的强调语。\n- 普通段落每段只用一个物理行并依靠编辑器软换行；代码块、表格和列表保留结构。一个段落包含多个独立规则时拆段，不用硬换行掩盖段落墙。\n- 不在当前说明中叙述“以前/现在/这次修改”、PR 过程、Review 对话、实现步骤或推理流水账。原因和取舍写入 decision，事故时间线写入 postmortem。\n- 不手写易漂移的完整文件、API、测试或配置清单；保留稳定分类并链接源码、schema、Compose 或生成 Owner。不要按函数逐行复述控制流。\n- 机制页必须覆盖适用范围、真实装配、状态与事实 Owner、权限/隔离、失败/恢复、可观察结果、配置边界和“源码定位与验证”；配置页保留参数、操作与排障，不复制机制全景。\n- 教程的关键步骤给出页面、状态、文件或协议等可观察结果；参考字段写清读取者、默认值、条件、限制和生效时机。实质性的教程与参考内容不能长期共居一页。\n- 图只用于三项以上关系、状态或时序，使用能由 VitePress 渲染的最小 Mermaid；图不能替代正文中的条件、异常和安全语义。尚未实现的方向不进入当前机制图。\n- 站内页面使用相对 Markdown 链接；站点外公开源码使用项目 GitHub `blob/main` 文件链接。禁止本地绝对路径、易漂移行号和只写文件名不说明职责的源码清单。\n- 示例只使用占位凭据、保留域名或假 ID；禁止写入 `.env`、Token、真实账号、用户数据、本地绝对路径或不可公开的内部地址。不要建议把 provisioner/数据库等管理凭据注入 Agent 沙盒。\n- 决策记录遵循[生命周期与格式](develop-guides/decisions/README.md)；implemented 记录使用现在时，不保留提案 checklist。用户可见完成项与 roadmap 不得同时声明同一状态。\n\n## 导航、预算与验证\n\n- 新增正式页面时更新 `.vitepress/config.mts` 的正确父级和阅读顺序；内部 `AGENTS.md`、decision 与 postmortem 不因位于 `docs/` 就自动成为用户导航入口。\n- `scripts/verify_engineering_contracts.py` 的 AGENTS 字符预算是 standing-order guardrail。超限时先下沉示例、背景和专题解释，再压缩重复；确需提高预算时同步更新 decision 和 gate，不能静默删掉必要约束或关闭检查。\n- 修改配置、API、状态、权限或机制说明时检查相关测试；文档构建只证明 Markdown、导航和链接有效，不证明语义正确。Reviewer 必须逐页对照当前源码、配置和风险相称的 oracle。\n- 构建失败要修正文档、锚点或导航，不扩大 `ignoreDeadLinks` 掩盖问题。报告只写实际执行的命令、结果和未验证范围。\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ncd docs && pnpm run build\ngit diff --check\n```\n","docs/agents/agent-backend-development.md":"# 开发智能体后端\n\n本页面向需要在 Yuxi 中新增或维护 Agent 后端的贡献者。它只讲代码装配；配置字段、权限和运行时上下文分别见[配置智能体](./agents-config.md)和[Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n## 新增预置角色\n\n仅改变提示词、模型或能力选择时，在 `backend/package/yuxi/agents/presets/` 新增一个 Python 文件并导出 `PRESET`；子智能体定义放在其 `subagents/` 子目录。发现逻辑递归读取文件，角色类型仍由 `backend_id` 决定：\n\n```python\nfrom yuxi.agents.presets import AgentPreset\n\nPRESET = AgentPreset(\n    slug=\"report-assistant\",\n    name=\"报告助手\",\n    description=\"根据材料整理报告。\",\n    backend_id=\"ChatbotAgent\",\n    context={\n        \"system_prompt\": \"根据用户材料生成结构清晰、来源可核对的报告。\",\n        \"skills\": [\"html-preview\"],\n    },\n)\n```\n\n子智能体使用 `backend_id=\"SubAgentBackend\"`，其类型由后端推导。`context` 复用[智能体配置](./agents-config.md)字段；长提示词可以直接使用 Python 多行字符串。角色模块不得执行外部操作。\n\nAPI 启动时按文件名发现所有非下划线开头的 Python 模块，校验定义类型、重复 slug 和后端存在性，再统一初始化数据库记录。新增角色无需修改注册清单、repository 或启动调用。模块导入失败、缺少 `PRESET`、重复 slug 或不存在的后端会阻止初始化成功。\n\n预置角色仅在 slug 不存在时创建，已有名称、提示词和能力配置保留；修改源码不会覆盖管理员定制。默认智能助手另行维护原有默认与共享约束。重启 API 后，在智能体管理页核对新增角色的名称、后端和能力配置；子智能体还需在主智能体配置中选用。\n\n## 后端放在哪里\n\n随服务发布的 Agent 后端放在：\n\n```text\nbackend/package/yuxi/agents/buildin/<your_agent>/\n├── __init__.py\n├── context.py\n└── graph.py\n```\n\n执行后端由 `buildin/__init__.py` 中的显式字典注册：\n\n```python\nBUILTIN_BACKENDS = {\n    \"ChatbotAgent\": ChatbotAgent,\n    \"SubAgentBackend\": SubAgentBackend,\n}\n```\n\n新增后端时显式导入其类并添加字典条目。字典键是持久配置与接口使用的稳定后端 ID，Python 类重命名不改变该 ID；仅增加目录或导出类不会启用后端。修改注册表时审查执行图、中间件和运行边界，并验证已有配置仍可解析。\n\n`get_agent_backend(backend_id)` 每次创建一个轻量后端对象，`list_agent_backend_info()` 查询已注册后端的基础信息。后端不持有用户、线程或 Graph 缓存；运行状态由本次 Context、Graph 和 middleware 持有。`get_graph(context=...)` 每次构建独立执行图，知识库增删无需重载后端。未知 ID 抛出 `AgentBackendNotFoundError`；接入用例与后端配置接口将该错误转换为 HTTP 404，worker 执行边界按现有失败流程收敛。\n\n后端的 `get_info()` 返回名称、能力与配置描述，不自行生成后端 ID。新增构图逻辑时验证两次运行分别使用各自的 Context 和资源。\n\n## 最小实现\n\n```python\nfrom langchain.agents import create_agent\nfrom yuxi.agents import BaseAgent, BaseContext, load_chat_model\n\n\nclass MyAgent(BaseAgent):\n    name = \"我的智能体\"\n    description = \"用于示例的智能体后端\"\n    context_schema = BaseContext\n\n    async def get_graph(self, *, context, **kwargs):\n        if not getattr(context, \"_runtime_prepared\", False):\n            raise ValueError(\"构图需要已准备的 Context\")\n        return create_agent(\n            model=load_chat_model(fully_specified_name=context.model),\n            system_prompt=context.system_prompt,\n            checkpointer=await self._get_checkpointer(),\n        )\n```\n\n这个示例展示最小的 Context、模型、提示词和 PostgreSQL checkpoint 装配。真实后端还要根据需要接入文件 backend、工具、Skills、审批、Summary、用量和子智能体 middleware。\n\nworker 和主动压缩在执行入口显式调用 `prepare_agent_runtime_context`，为 Context 追加工作区提示词、按当前用户过滤资源，并在模型为空时补齐系统默认模型。`get_graph(context=...)` 只消费准备后的对象。独立调用同样先创建 `context_schema()`，用 `update_config` 装载持久配置、用 `update` 注入已授权身份和运行覆盖，再 await 准备函数；流和 invoke 接口只接受 `context`，不接收配置字典。\n\n## Context 和配置表单\n\n需要让管理员或用户配置 Agent 行为时，在 `context.py` 扩展 `BaseContext`。字段定义、metadata、权限和保存结构的完整规则由[智能体配置](./agents-config.md#自定义-context-字段)拥有，本页只描述扩展时的代码路径：新增 dataclass 字段后，`get_configurable_items()` 自动把它暴露给 Agent 详情接口和 `AgentRuntimeConfigForm`，保存值进入 `config_json.context`，`get_graph(context)` 消费准备后的 Context。\n\n不要只在前端添加一个字段，也不要把运行期 ID、worker 身份和权限快照暴露成可保存配置。新增字段后，沿 context_schema → get_configurable_items() → 详情接口 → 配置表单 → `config_json.context` → `get_graph(context)` 的链路检查每一环的测试和文档。\n\n## 中间件和工具\n\n资源权限和默认资源选择在 Graph 创建前处理；模型提示注入、工具动态开放、文件结果处理、state 更新和观测才适合放入 middleware。内置 Agent 的工具可见性和执行注册分为两层：工具可以先进入 ToolNode，再由 Skill 激活状态决定是否让模型看到。\n\n优先复用：\n\n- [工具系统](./tools-system.md) 的注册和目标校验；\n- [中间件](./middleware.md) 的装配顺序；\n- [Skills 管理](./skills-management.md) 的依赖和激活规则；\n- [沙盒机制](../mechanisms/sandbox.md) 的文件和命令边界。\n\n新 middleware 不要绕过 `prepare_agent_runtime_context`，也不要用 Prompt、前端隐藏或 schema omission 代替后端授权。\n\n## 检查清单\n\n- `BaseAgent` 子类在 `BUILTIN_BACKENDS` 中通过稳定 ID 显式注册；\n- `context_schema` 的默认值、字段权限和选项能被前端正确渲染；\n- Graph 使用 Yuxi 的模型、工具、文件和 checkpoint 装配入口；\n- 工具副作用在执行处验证用户、路径和资源；\n- 新的模型可见输入、状态、文件或协议有正向和负向测试；\n- 相关 API、机制和用户文档已更新。\n\n## 源码和测试\n\n- [BaseAgent](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/base.py)\n- [Context](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/context.py)\n- [Chatbot graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)\n- [执行后端显式注册](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/__init__.py)\n- [Agent unit tests](https://github.com/xerrors/Yuxi/tree/main/backend/test/unit/agents)\n- [Agent integration/E2E](https://github.com/xerrors/Yuxi/tree/main/backend/test/e2e)\n\n改变持久配置、权限、模型可见输入、Run 生命周期或文件边界时，先按 [Yuxi Spec Loop](../develop-guides/spec-loop.md) 建立相应的决策和验证范围。\n","docs/agents/agent-evaluation.md":"# 评估智能体\n\n智能体评估用 Langfuse Dataset 保存一组固定任务，再让 Yuxi 按真实的 AgentRun、worker 和工具链路逐条执行。它适合比较一个智能体在研究、编程、文件处理或多步骤任务上的表现。\n\n本页不介绍知识库的 `recall@K` 和答案指标；那部分见[知识库评估](../intro/evaluation.md)。\n\n## 需要准备什么\n\n1. Yuxi 已配置 Langfuse tracing，API/worker 环境包含：\n\n   ```bash\n   LANGFUSE_PUBLIC_KEY=<your-public-key>\n   LANGFUSE_SECRET_KEY=<your-secret-key>\n   LANGFUSE_BASE_URL=https://cloud.langfuse.com\n   ```\n\n2. 本机运行 CLI 时也能读取同一 Langfuse 项目的变量。CLI 会直接读取 Dataset 并创建 experiment。\n3. 已安装 `yuxi-cli`，并登录目标实例：\n\n   ```bash\n   yuxi remote add local http://localhost:5173\n   yuxi login --browser\n   ```\n\n   CI 或没有浏览器时，可以使用 `yuxi login --api-key \"$YUXI_API_KEY\"`。\n4. 目标智能体已经存在，登录用户有权访问它。命令使用 Agent slug，例如 `default-chatbot`。\n\n## 准备 Dataset\n\n在 Langfuse 中创建 Dataset，并为每个 item 的 `input` 提供任务文本：\n\n```json\n{\"input\":\"请整理这份资料，并列出三个需要核实的事实。\"}\n```\n\n也兼容把任务放在 `query`、`question` 或 `prompt` 字段中。`expected_output` 可以保存参考答案，具体评分规则由 Langfuse evaluator 或人工评审负责。\n\nYuxi 不负责创建或上传 Dataset。先在 Langfuse 中检查任务文本、参考输出和数据集版本，再运行实验。\n\n## 运行实验\n\n```bash\nyuxi agent eval \\\n  --dataset-name demo-dataset \\\n  --agent-slug default-chatbot \\\n  --experiment-name default-chatbot-demo \\\n  --max-concurrency 1 \\\n  --timeout-seconds 900\n```\n\nCLI 对每条 item：\n\n1. 从 Dataset 读取任务文本；\n2. 调用 Yuxi 的 `POST /api/agent-invocation/eval/runs`；\n3. 由 Yuxi 创建临时 Conversation 和 AgentRun；\n4. 通过 worker 执行真实智能体；\n5. 等待 Run 进入终态；\n6. 把最终输出写回 Langfuse experiment item。\n\n`--max-concurrency` 是 Dataset 实验的并发数。先从 `1` 开始，再根据模型服务、worker 和沙盒容量提高。`--timeout-seconds` 是每条样例等待 Yuxi 结果的上限；超时会报告当前运行状态，不应把它当作成功。\n\n## 查看结果\n\n实验完成后，在 Langfuse Dataset 的 experiment 中查看每条 item 的最终输出。Yuxi 会在本地运行上下文和 trace 中保存以下标记，便于筛选：\n\n```text\nsource=agent_evaluation\nevaluation_dataset_name=<dataset-name>\nevaluation_dataset_item_id=<item-id>\nevaluation_experiment_name=<experiment-name>\n```\n\n先比较同一数据集的输出，再按自己的评估规则打分。一次实验的输出只代表当时的模型、Agent 配置、工具、知识库和外部服务状态；改变这些条件后，应创建新的 experiment 名称。\n\n## 排查失败\n\n- 没有 experiment：检查 CLI 的 Langfuse 公钥、密钥、地址和 Dataset 名称。\n- experiment 有 item 但 Yuxi 失败：检查 CLI 登录的 API Key、Agent slug，并用 `docker compose logs api worker` 查看当前槽位日志。\n- Trace 缺失：检查 API/worker 是否读取到 Langfuse 配置；Yuxi 业务结果仍以 PostgreSQL 的 Run 和消息为准。\n- 大量超时：降低 `--max-concurrency`，检查模型响应时间、worker 健康状态和沙盒创建耗时。\n- 实验部分成功：不要只看命令退出前的汇总，回到 Langfuse 检查每条 item 是否都有结果；CLI 会在成功写入数量与 Dataset 总数不一致时报告错误。\n\n实现入口见 [Agent Eval 路由](https://github.com/xerrors/Yuxi/blob/main/backend/server/routers/agent_invocation_eval_router.py)、[CLI 实验](https://github.com/xerrors/Yuxi/blob/main/packages/yuxi-cli/src/yuxi_cli/agent_eval.py) 和 [Langfuse 集成](../advanced/langfuse-integration.md)。\n","docs/agents/agents-config.md":"# 配置智能体\n\n本页是智能体配置参考，说明页面上的字段如何进入一次运行。新增智能体后端的代码结构见[开发智能体后端](./agent-backend-development.md)；只使用现成智能体时，从[快速开始](../intro/quick-start.md)开始。\n\n## 配置模型\n\n| 对象 | 负责什么 |\n| --- | --- |\n| `Agent` | 数据库中的智能体资源，保存名称、slug、共享范围和持久化配置 |\n| `BaseAgent` | 代码中的后端类型，声明 `context_schema` 和 `get_graph()` |\n| `BaseContext` | 配置字段和运行时输入的 Schema |\n| `config_json.context` | 当前 Agent 保存的配置值 |\n| Graph / middleware | 根据 Context 组合模型、工具、文件和扩展能力 |\n\n内置 `ChatbotAgent` 用于普通对话，`SubAgentBackend` 用于被主智能体委派的任务。子智能体的配置入口与普通智能体相同。\n\n## 配置页面从哪里来\n\n`BaseContext` 的 dataclass 字段和 metadata 会生成配置项描述，前端不再维护一份独立字段清单：\n\n```text\nContext 字段\n  → get_configurable_items()\n  → Agent 详情接口\n  → AgentRuntimeConfigForm\n  → config_json.context\n```\n\nmetadata 可以定义展示名称、说明、控件类型、选项和角色权限。运行期 ID、owner 和内部派生值应隐藏，不作为用户配置。\n\n`metadata.auth` 只限制修改权限：`admin` 字段允许管理员和超级管理员修改，`superadmin` 字段只允许超级管理员修改。普通用户读取有权访问的智能体时，可以读到这些已保存值，运行也使用这些值；不可修改的字段不进入其编辑表单，后端会过滤越权提交的字段并保留已保存值。`auth` 不提供字段保密能力，Context 配置不得存放凭据。\n\n例如管理员将 `max_execution_steps` 设置为 50，普通用户运行该智能体时的 `recursion_limit` 也为 50。已经固化运行快照的 Run 保留自己的配置；后续新 Run 读取保存的配置。知识库、Skills 等资源仍按运行用户的访问权限筛选。\n\n## 基础字段\n\n| 字段 | 作用 |\n| --- | --- |\n| `system_prompt` | 智能体角色和行为说明 |\n| `model` | 主模型；留空时使用系统默认模型 |\n| `tool_approval_mode` | `default` 或 `always_trust`；仅管理员可配置 |\n| `tools` | 可使用的内置工具 |\n| `knowledges` | 可检索的知识库范围 |\n| `mcps` | 直接添加到 Agent 的已启用 MCP 服务器 |\n| `skills` | 选择共享和内置 Skill；个人 Skill 始终可用 |\n| `preload_skills` | 从首轮请求加载完整说明和依赖的 Skill 子集 |\n| `summary_threshold`、`summary_keep_messages` | 上下文压缩的唯一压力阈值和摘要后保留消息数 |\n| `summary_prompt`、`summary_tool_result_token_limit` | 摘要提示词和工具结果预览上限 |\n| `max_execution_steps`、`model_retry_times` | 单次运行步数和模型重试次数 |\n\n## 资源选择语义\n\n资源字段 `tools`、`knowledges`、`skills`、`subagents`、`mcps` 和 `preload_skills` 接受 `\"all\"` 或字符串数组，本页是这组契约的唯一 Owner。`\"all\"` 表示每次运行使用当前用户可访问的全部资源，包括之后新增的资源；`[]` 表示不选择；非空数组表示固定范围。新写入拒绝 `null`、未知字符串和非字符串数组成员。\n\n创建时省略字段使用其默认值：工具、知识库和 Skill 为 `\"all\"`，子智能体为 `[\"general-purpose\"]`（通用任务），MCP 和预加载 Skill 为 `[]`。界面的「全部（含新增）」保存 `\"all\"`；逐项勾选保存固定数组，即使勾选了当前所有选项也不会自动转换成全部模式。「清空全部」保存 `[]`，子智能体同样可以全部禁用。\n\n各资源的附加规则：\n\n- MCP 选择只控制直接加载的服务器；已激活 Skill 声明的 MCP 依赖仍按需加载。\n- Skill 选择器只列共享和内置 Skill；运行时自动合并当前用户全部个人 Skill，`skills=[]` 只关闭共享选择。Skill 的激活时机见 [Skills 管理](./skills-management.md)。\n- 预加载候选项限于已选且可访问的共享 Skill；`preload_skills=\"all\"` 在每次运行预加载该范围及其授权依赖的完整说明，并从首轮开放依赖工具。`skills=[]` 时预加载全部仍得到空列表，个人 Skill 继续按需读取正文。\n- 子智能体不能继续调用下一层子智能体。\n\n这些字段只会缩小当前用户已经拥有的权限；运行时如何消费解析结果见 [Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n### 共享智能体与编辑保存\n\n共享智能体保存完整的期望选择，每次运行再与当前操作者的可访问资源取交集，运行不会改写保存的选择。例如创建者选择 10 个 Skill，委托管理员只能访问其中 5 个，委托管理员运行时生效 5 个，保存名称或模型后仍保留原有 10 个选择。\n\n编辑页只提交修改过的配置字段；通过 API 更新 `config_json.context` 时，省略字段保留原值。修改非空资源列表时，后端保留当前管理员不可访问的既有引用，并拒绝新增无权访问的引用；保留的引用维持原相对顺序，新选择追加到末尾。「清空全部」移除所有引用（包括不可访问项），切换到「全部（含新增）」则整体替换固定选择范围。界面会显示当前不在可选范围内的既有选择数量。\n\n## 定时运行\n\n智能体支持用户定时任务（Beta）：为智能体配置 5 段 cron 和 IANA 时区后，worker 到期自动在独立 Conversation 中触发一次运行，也支持手动立即运行；停机错过的多个周期合并为一次，已有未完成执行时跳过并记录。任务的定义、触发与恢复边界见 [Agent 运行时上下文](../mechanisms/agent-runtime.md#用户定时-agent)。\n\n## 自定义 Context 字段\n\n需要让用户配置额外行为时，扩展 Context，让后端和前端沿同一 Schema 工作：\n\n```python\nfrom dataclasses import dataclass, field\nfrom yuxi.agents import BaseContext\n\n\n@dataclass(kw_only=True)\nclass MyAgentContext(BaseContext):\n    response_style: str = field(\n        default=\"concise\",\n        metadata={\n            \"name\": \"回答风格\",\n            \"description\": \"控制回答的详细程度\",\n            \"type\": \"string\",\n            \"options\": [\"concise\", \"detailed\"],\n        },\n    )\n```\n\n新增字段会影响保存结构、配置表单和运行期 Context。字段默认值、权限和选项变化时，同时更新相关测试和文档。\n\n## `capabilities`\n\n`capabilities` 是代码声明的静态能力，用于控制固定的前端入口，例如：\n\n```python\nclass MyAgent(BaseAgent):\n    capabilities = [\"file_upload\", \"files\"]\n```\n\n它不保存待办、文件、产物或子智能体状态。运行态来自 LangGraph state 的 `agent_state`；能力声明只表达后端固定支持哪些 UI 入口。\n\n## 运行时入口\n\n配置如何与用户身份、权限快照、Workdir、Memory 和 PostgreSQL checkpoint 组合，见[Agent 运行时上下文](../mechanisms/agent-runtime.md)。工具可见性和执行授权见[工具系统](./tools-system.md)，上下文压缩见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## 相关页面\n\n- [开发智能体后端](./agent-backend-development.md)\n- [中间件](./middleware.md)\n- [Skills 管理](./skills-management.md)\n- [子智能体](./subagents-management.md)\n- [沙盒机制详解](../mechanisms/sandbox.md)\n","docs/agents/mcp-integration.md":"# 集成 MCP\n\nMCP（Model Context Protocol）让智能体调用外部服务提供的工具。管理员在“扩展 → MCP”中添加远程服务器；Agent 可直接选择服务器，Skill 也可声明激活后加载的依赖。\n\n## 支持的传输方式\n\n| 传输方式 | 适用场景 |\n| --- | --- |\n| `streamable_http` | 新的远程 MCP 服务 |\n| `sse` | 仍提供 SSE 接口的远程服务 |\n\n管理接口只接受 `streamable_http` 和 `sse`。Yuxi 不支持 `stdio`，包括内置 MCP 和直接传入的运行时配置；历史 `stdio` 配置会被禁用，应迁移为远程服务。\n\n## 添加远程 MCP\n\n在“扩展 → MCP”点击“添加 MCP”，填写稳定标识、名称、传输方式和 URL。例如：\n\n```json\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\"\n}\n```\n\n管理接口对应：\n\n```http\nPOST /api/system/mcp-servers\nAuthorization: Bearer <admin-token>\nContent-Type: application/json\n\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\",\n  \"description\": \"提供示例查询工具\"\n}\n```\n\n需要认证的远程服务可以配置 HTTP headers、连接超时和 SSE 读取超时。凭证会随着连接请求发送，请只配置必要的 header，并把管理接口限制在可信的管理员范围。\n\n添加后先点击“测试连接”，确认能发现工具，再把服务器状态设为“已添加”。状态关闭时，服务器记录仍保留，但不会进入运行时。\n\n## 让智能体使用 MCP\n\n在智能体配置的 MCP 字段中显式选择要直接添加的服务器。该字段默认不添加服务器：\n\n- 未显式配置或显式清空时，不直接加载 MCP 服务器；\n- 显式选择后，只使用所选且当前可用的服务器；\n- MCP 工具仍会在执行处使用当前用户身份和服务器配置；\n- 管理员可以在 MCP 详情页单独禁用某个工具。\n\n共享 Skill 的 `mcp_dependencies` 声明所需服务器。Skill 激活后按需加载这些服务器的工具；预加载 Skill 从首轮加载，均无需在 Agent 的 MCP 字段重复选择。服务器必须由管理员启用，禁用的服务器不会提供工具。\n\nMCP 配置从 PostgreSQL 读取，工具对象按配置哈希缓存。修改连接配置或工具禁用列表后，下一次运行会使用新的配置键。\n\n## 内置远程 MCP\n\n内置 DeepWiki 使用 `deepwiki-official` 标识，通过 `https://mcp.deepwiki.com/mcp` 提供 Streamable HTTP 服务，无需认证即可查询公开 GitHub 仓库。详见 [DeepWiki 官方文档](https://docs.devin.ai/work-with-devin/deepwiki-mcp)。\n\n开发者在 [`builtin.py`](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/mcp/builtin.py) 的 `BUILTIN_MCP_SERVERS` 中添加固定远程定义：\n\n```python\nBUILTIN_MCP_SERVERS = {\n    \"deepwiki-official\": {\n        \"transport\": \"streamable_http\",\n        \"url\": \"https://mcp.deepwiki.com/mcp\",\n        \"description\": \"查询公开 GitHub 仓库的文档、架构与代码\",\n        \"icon\": \"📚\",\n        \"tags\": [\"内置\", \"代码\", \"文档\"],\n    },\n}\n```\n\nAPI/worker 启动时同步固定定义到数据库；运行时直接读取代码中的连接字段。新内置 MCP 默认未添加，管理员需要启用；连接配置不可通过页面修改。添加定义无需修改服务逻辑或启动入口。\n\n原内置 `mcp-server-chart` 已退役，启动时删除系统创建的记录。其他历史 stdio 记录保留并禁用，管理员可迁移为远程服务或删除。MySQL 报表技能改用 Markdown 表格，不再依赖图表 MCP；自定义角色或 Skill 中对退役 MCP 的引用需要自行调整。\n\n## 常用管理接口\n\n| 方法 | 路径 | 作用 |\n| --- | --- | --- |\n| `GET` | `/api/system/mcp-servers` | 查看服务器；普通用户只得到脱敏基础信息 |\n| `POST` / `PUT` | `/api/system/mcp-servers`、`/{slug}` | 添加或修改远程 MCP |\n| `PUT` | `/api/system/mcp-servers/{slug}/status` | 启用或停用服务器（对应页面上的添加/移除状态） |\n| `POST` | `/api/system/mcp-servers/{slug}/test` | 测试连接并发现工具 |\n| `GET` | `/api/system/mcp-servers/{slug}/tools` | 查看工具 |\n| `PUT` | `/api/system/mcp-servers/{slug}/tools/{tool_name}/toggle` | 启用或禁用单个工具 |\n\n接口字段和错误响应以实例 Swagger 为准。\n","docs/agents/middleware.md":"# 中间件\n\n中间件把文件、Skills、子智能体、上下文压缩、审批和用量统计接到 LangGraph Agent。它们在模型调用、工具调用或 state 更新的边界运行，让不同 Agent 复用同一套能力。\n\n内置 `ChatbotAgent` 和 `SubAgentBackend` 都在 `get_graph()` 中组装中间件。Graph 创建前，系统先完成用户资源和权限的归一化；中间件不应绕过这一步重新决定授权。\n\n## Graph 创建前的准备\n\n`prepare_agent_runtime_context` 会根据当前用户和 Agent 配置：\n\n- 过滤内置工具、知识库、MCP、Skills 和子智能体；\n- 生成 `_visible_knowledge_bases`；\n- 展开 Skill 依赖，生成 `_effective_skill_slugs` 和 `_runtime_skills`；\n- 使用系统默认模型补齐空的模型配置。\n\n随后，工具解析器准备可执行工具，`build_prompt_with_context` 生成系统提示词，Agent 再创建 Graph。工具执行时仍需检查具体目标，准备阶段的资源快照不是授权替代品。\n\n## 内置中间件顺序\n\n`ChatbotAgent` 的常见顺序如下（[graph.py](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py) 装配）；可选项只在对应能力启用时加入：\n\n| 顺序 | 中间件 | 作用 |\n| --- | --- | --- |\n| 1 | `ToolErrorGuardMiddleware` | 最外层隔离普通工具异常，保留取消与 interrupt 的传播 |\n| 2 | `SteerMiddleware` | 在安全边界发现待接替请求 |\n| 3 | `create_agent_filesystem_middleware` | 提供 Workdir、User Data、Skills 文件后端，并卸载过大的工具结果 |\n| 4 | `SkillsMiddleware` | 注入 Skill 说明，按激活状态开放依赖 |\n| 5 | `YuxiMemoryMiddleware` | Memory 开关开启且 `MEMORY.md` 有内容时，注入用户记忆并提供受限工具 |\n| 6 | `YuxiSubAgentMiddleware` | 主智能体有可见子智能体时提供子智能体生命周期工具 |\n| 7 | `YuxiSummarizationMiddleware` | 先确定性压缩工具结果，仍达到同一阈值时生成摘要 |\n| 8 | `TodoListMiddleware` | 保存待办，供状态面板展示 |\n| 9 | `PatchToolCallsMiddleware` | 修正部分工具调用消息形态 |\n| 10 | `NetworkRetryMiddleware` | 网络错误按预算、其他可重试模型错误按次数重试，耗尽后抛出异常 |\n| 11 | `ImageInputCompatibilityMiddleware` | 桥接工具读取图片与模型输入格式；必要时回退 OCR |\n| 12 | `TokenUsageMiddleware` | 记录近似上下文和主模型实际用量 |\n| 13 | 工具审批 middleware | 默认模式下审批 Project 外的文件写入和命令执行；当前 Project 内的写/编辑自动放行 |\n\n`SubAgentBackend` 复用文件、Skills、Summary、待办、重试和用量等能力，同样以 `ToolErrorGuardMiddleware` 为最外层，但不挂载子智能体 middleware，并过滤不适合子智能体的敏感或交互工具。\n\n模型重试耗尽后，异常进入 Run 失败通道，持久化 `failed` 状态与错误原因；已有部分输出保留错误元数据。\n\n子 Run 的失败通过 `subagent_await` / `subagent_status` 返回给父智能体，由父智能体决定后续处理。最终正常回答仍须满足同 Run 的 model lifecycle 审计关联。\n\n## Skills 和知识库\n\nSkills middleware 将 Skill 说明按模型请求注入：预加载 Skill 从首轮开放工具和 MCP 依赖，普通 Skill 在模型读取对应 `SKILL.md` 后激活，再开放声明的依赖。Agent 显式选择的 MCP 服务器从运行开始提供工具。\n\n知识库能力由内置 `knowledge-base` Skill 提供。它的工具是否注册、模型是否可见、参数是否能访问目标知识库分别由工具组装、Skill 激活和知识库权限检查负责。完整链路见[工具系统](./tools-system.md)和[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n## 文件和附件\n\n附件确认后写入当前 Project Workdir。每次 Run 会把线程历史附件的文件名和实时路径加入本轮用户消息，让模型按需调用 `read_file`；持久化 Message 仍保存原始文本，不会把这段模型专用路径混进用户可见消息。\n\n普通 Agent 和子 Agent 使用根 Conversation 的同一个 `runtime_scope_id` 和 Workdir。子 Agent 的 child thread 只隔离 LangGraph checkpoint，不隔离共享文件。Viewer、附件和 artifact API 直接访问 UserWorkspace 的持久文件，不需要创建 file-bridge Sandbox。\n\n## 子智能体\n\n主智能体配置了可见子智能体时，middleware 提供 `subagent_start`、`subagent_status`、`subagent_cancel` 和 `subagent_await`。`subagent_start` 立即返回子 Run 身份，`subagent_await` 按需等待；查询和取消均按 `run_id` 执行。子智能体使用自己的 Context 和 checkpoint，但继承发起用户的权限、Workdir 和 execution runtime。\n\n详细的调用、busy、结果和文件边界见[子智能体](./subagents-management.md)。\n\n## Summary 上下文压缩\n\nSummary 在文件和 Skills 等中间件之后运行。请求达到唯一压力阈值后，它先为当前模型请求精简工具结果并重新计量；仍达到同一阈值时才写入历史文件、生成摘要并更新 checkpoint。它不会删除 PostgreSQL 聊天消息，也不会把内部摘要模型调用当作用户可见回复。支持主动压缩的 Agent 复用同一摘要器，并通过 service 在空闲线程的 canonical graph state 上更新 checkpoint。\n\n参数和状态细节见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## Token 用量\n\n`TokenUsageMiddleware` 同时记录近似上下文 token（用于摘要阈值和状态面板）和主 Agent 模型返回的 `usage_metadata`（按最近调用、当前 Run、线程和模型分桶保存）。\n\n`siliconflow-cn` 和 `siliconflow` 当前位于 Provider 用量黑名单：这两个供应商仍可提供近似上下文统计，但不会写入最近调用、Run 或线程的 Provider 实际用量聚合。其他能返回兼容 `usage_metadata` 的供应商才会进入这组实际用量统计；Summary 内部摘要模型调用不计入。\n\nRun 终态时，worker 把与当前 `run_id` 匹配的 state 快照写入 `AgentRun.token_usage`；父 Run 和子 Run 分开保存。\n\n## 新增中间件时\n\n先说明它要改变哪一条边界：Prompt、模型调用、工具调用、文件访问、state 或观测。资源筛选和权限收敛放在 Graph 创建前；文件读写和工具结果卸载优先复用现有 filesystem middleware；新增模型可见输入或副作用时补充对应测试和失败案例。\n\n实现入口：[ChatbotAgent graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)、[中间件目录](https://github.com/xerrors/Yuxi/tree/main/backend/package/yuxi/agents/middlewares)。\n","docs/agents/skills-management.md":"# 管理 Skills\n\nSkill 是一个可复用的能力包，通常包含一个 `SKILL.md`、提示词、参考资料和可选脚本。智能体先看到 Skill 的描述，再按需要读取 `SKILL.md`；本地工具和 MCP 依赖随 Skill 激活进入模型请求。\n\n## 什么时候用 Skill\n\n把一类稳定、可复用的工作方式放进 Skill，例如文档处理、研究流程、报表生成或某个外部服务的操作规范。单次任务要求写在对话中；需要跨对话复用的规则和资料再考虑 Skill 或用户工作区文件。\n\n## Skill 保存在哪里\n\n| 来源 | 位置和事实 | 谁可以安装/管理 |\n| --- | --- | --- |\n| 内置 | 随代码发布，启动时同步索引 | 管理员可以启用/停用，不能删除或直接编辑 |\n| 共享上传/远程 | Skill 持久目录 + PostgreSQL 索引 | 管理员按共享范围管理 |\n| 个人 | 当前用户 UserWorkspace 的 `agents/skills/<slug>` | 当前用户；不创建共享 Skill 数据库记录 |\n\n运行时路径是：\n\n- 共享和内置 Skill：`/home/gem/skills/<slug>/`，对 Agent 只读；\n- 个人 Skill：`/home/gem/user-data/agents/skills/<slug>/`，位于当前用户的 UserWorkspace。\n\n同一用户有个人 Skill 和共享 Skill 使用同一个 slug 时，个人版本覆盖该用户看到的共享版本。删除个人版本后，如果共享版本仍可访问，会恢复共享版本。\n\n## 创建 Skill\n\n标准目录至少包含根级 `SKILL.md`：\n\n```text\nmy-skill/\n├── SKILL.md\n├── tools/       # 可选脚本\n└── prompts/     # 可选提示词或参考资料\n```\n\n`SKILL.md` 使用 YAML frontmatter：\n\n```markdown\n---\nname: 文档整理\nslug: document-cleanup\ndescription: 按指定结构整理文档，并保留可核对的来源。\ntool_dependencies: []\nmcp_dependencies: []\nskill_dependencies: []\n---\n\n# 文档整理\n\n## 何时使用\n\n说明这个 Skill 解决什么问题，以及什么时候不该使用。\n\n## 操作步骤\n\n写出 Agent 应遵循的步骤、限制和验收方式。\n```\n\n必填字段是 `name` 和 `description`。名称和 `slug` 最多 128 个字符。`slug` 可省略，省略时直接使用 `name`，因此省略 slug 时 `name` 本身也必须是小写字母、数字和单个短横线组成的值，中文或带空格的展示名称会校验失败。\n\n建议显式填写 slug，把自然语言名称和稳定标识分开。\n\n依赖字段含义：\n\n| 字段 | 作用 |\n| --- | --- |\n| `tool_dependencies` | 需要的内置工具 |\n| `mcp_dependencies` | 需要的 MCP 服务器 |\n| `skill_dependencies` | 需要先提供说明的其他 Skill |\n\n个人 Skill 只作为用户文件读取，不解析这些平台依赖；需要依赖工具、MCP 或其他 Skill 时，安装为共享 Skill。\n\n## 安装方式\n\n进入“扩展 → Skills”，可以选择：\n\n1. **推荐 Skill**：从推荐列表生成安装草稿。\n2. **上传**：上传 ZIP 或单个 `SKILL.md`，解析后生成草稿。\n3. **远程安装**：从 GitHub 仓库、ModelScope Skill 或合集拉取一个或多个 Skill。\n4. **在线编辑**：编辑已有且有管理权限的共享 Skill 文件和依赖。\n5. **Agent 内安装**：主智能体使用 `install_skill` 把 Skill 安装到当前用户的个人来源；子智能体不能使用该工具。\n\n上传和远程安装都先解析为草稿，再选择个人或共享位置并确认。草稿中的可安装条目只包含已解析的临时包；远程拉取或解析失败会单独显示，不能被确认安装。确认前可以检查名称、说明、文件和依赖；取消草稿不会写入正式 Skill。\n\n有管理权限的用户可在共享 Skill 详情页编辑文本文件。保存 `SKILL.md` 时，根文件的名称、描述及依赖声明会同步到数据库索引；“配置”中的依赖选择也会回写根文件。每次保存都使用打开文件时取得的修订值；如果其他人已修改该文件，页面会拒绝覆盖并保留当前草稿，用户需核对最新内容后重试。切换文件或离开页面时，未保存的修改会触发确认。共享范围和启停状态仍在配置页分别保存。新的共享内容在后续 Agent Run 准备时生效；已准备 Run 的预加载内容保留原有快照，按需读取的用户投影可随之后的编辑刷新。\n\n### 远程来源限制\n\n管理员在“设置 → 基本设置 → Skill 配置 → 远程来源白名单”中维护 `remote_skill_source_policy.allowed_hosts`。默认允许 `github.com` 和 `modelscope.cn`；主机名必须精确匹配，保存空列表会关闭远程安装。\n\n示例来源：\n\n```text\nanthropics/skills\nhttps://github.com/anthropics/skills\nhttps://modelscope.cn/skills/@anthropics/pdf\nhttps://modelscope.cn/collections/MiniMax/MiniMax-Office-skills\n```\n\n除了按仓库或合集拉取，也可以在远程安装对话框切换到“全局搜索发现”，输入关键词检索 `skills.sh` 上的开源 Skills，再选择结果批量拉取和安装。搜索在一次性 Sandbox 中执行；选中结果后的实际拉取仍经过来源白名单和安装确认流程。\n\nGitHub 的 `owner/repo` 简写会被转换为 HTTPS 地址。远程来源会在不继承全局或用户环境变量的一次性 Sandbox 中下载和提取，系统会拒绝绝对路径和路径穿越，并限制文件数、目录深度和总大小。来源白名单限制产品允许的地址，不是网络出口防火墙。\n\n### 新增内置 Skill\n\n在 `backend/package/yuxi/agents/skills/buildin/<slug>/` 新增目录，至少包含 `SKILL.md`。启动同步按目录名排序发现直接子目录，忽略下划线或点开头的目录；无需修改 Python 注册清单。\n\n`SKILL.md` frontmatter 唯一拥有名称、描述、版本和依赖。`slug` 必须与目录名一致，省略时使用 `name`；`version` 省略时为 `1.0.0`，建议使用引号包裹版本字符串。工具、MCP、Skill 依赖使用本页定义的字段。缺少根文件或元数据不合法时，启动同步明确失败。\n\nAPI/worker 启动时同步文件、元数据和依赖，保留数据库中的启停状态。重启后在“扩展 → Skills”核对新增项的说明和依赖；新增脚本或资源也必须随发行包携带。源码目录与共享投影分别拥有发布内容和安装文件，编辑应落在源码目录。\n\n安装前仍应审查 Skill 的提示词、脚本、依赖和网络行为。不要把数据库密码、云平台密钥或 `SANDBOX_PROVISIONER_TOKEN` 放进 Skill 或 Agent 环境。\n\n## 依赖和加载时机\n\nAgent 的 `skills` 配置语义（`\"all\"`、固定数组与默认值）由[资源选择契约](./agents-config.md)拥有，本页描述激活时机。每个 Run 准备 Context 时，系统按权限解析共享选择，合并当前用户全部个人 Skill，再展开 `skill_dependencies`；主智能体和子智能体使用同一规则。`skills=[]` 时个人 Skill 仍进入模型可见的描述列表，只是没有共享选择。个人 Skill 安装不修改 Agent 配置，新增 Skill 在下一次 Run 准备时自动加入，已准备的运行保留快照。\n\n### 普通渐进加载\n\n1. 创建 Graph 前，模型得到有效 Skill 的名称、描述和 `SKILL.md` 路径。\n2. 模型读取某个可见 Skill 的 `SKILL.md` 后，该 Skill 进入 `activated_skills`。\n3. 后续模型请求加入它声明的本地工具，并加载已启用的 MCP 依赖服务器提供的工具。\n\n模型没有读取的 Skill 的依赖工具继续隐藏。未激活的 Skill 本地工具即使已注册到 ToolNode，也不能被模型调用。\n\n### 预加载\n\nAgent 配置可以用 `preload_skills` 指定少量需要从首轮就可用的 Skill。预加载选择器只列 `skills` 中当前用户可访问的共享和内置 Skill；系统会展开其依赖闭包，读取根级 `SKILL.md`，并从首轮模型请求开放本地工具和已启用的 MCP 依赖。\n\n预加载的根文件缺失或不可读时，Graph 创建会明确失败，不会静默退回渐进加载。默认值为空列表；选择“全部（含新增）”时，每次运行预加载当前 Agent 已选且可访问的共享 Skill 及其授权依赖。同名个人版本覆盖共享版本时读取个人内容；其他个人 Skill 始终可发现，正文按需读取。固定列表和全部模式均遵循[资源选择契约](./agents-config.md)。\n\n## 权限和选择\n\n共享 Skill 使用 `source_type`、`share_config` 和 `enabled` 表达来源、范围和启用状态。范围使用 version 2 的 `read_scope`、`manage_scope`，可以是全局、部门或指定用户；管理范围必须包含在读取范围内。\n\n| 用户 | 可见和使用 | 可管理 |\n| --- | --- | --- |\n| `superadmin` | 全部允许的共享/内置 Skill | 全部非内置 Skill，及内置 Skill 的启停 |\n| `admin` | 命中读取范围且已启用的 Skill | 命中管理范围的非内置 Skill，及内置 Skill 的启停 |\n| 普通用户 | 命中读取范围且已启用的 Skill | 自己拥有的非内置 Skill；新安装的 Skill 固定进入个人来源 |\n\n普通用户安装的新 Skill 固定进入个人来源，不配置共享范围。管理员安装到共享来源时才会写入 PostgreSQL 索引，并可以配置部门或用户范围。扩展管理页展示当前用户可访问的共享和个人 Skill；Agent 配置选项只展示可访问的共享和内置 Skill。后端在保存时校验共享引用，运行时按当前用户身份加载个人目录。\n\nAgent 配置中的 Skill 选择不能扩大用户的文件、知识库或 MCP 权限。\n\n## 内置 Skill 说明\n\n系统启动时会同步仓库内置 Skills，其中两个内置 Skill 有产品级行为约定。`html-preview` 用于在普通 Markdown 难以清晰表达指标、对比、流程、时间线或层级关系时，指导 Agent 输出静态 `html:preview` 围栏；普通 HTML 源码仍使用 `html` 代码块，前端把该围栏清洗后放入 sandboxed iframe 预览。未显式配置 Skills 的 Agent 按现有资源规则自动获得该 Skill；使用显式 Skills 允许列表的 Agent 需要选择 `html-preview`。\n\n内置 `deep-research` 不依赖 `html-preview`。它默认在当前 Workdir 的 `outputs/` 目录生成独立、响应式的 HTML 阅读文档，并通过交付物入口展示；用户明确指定其他格式时除外。宽屏报告可以提供侧栏目录，窄屏隐藏或折叠侧栏；报告可以按内容需要使用外部图片等公开资源，来源以普通链接呈现。\n\n## 运行时文件行为\n\n共享和内置 Skill 投影对 Agent 只读，但沙盒命令仍可能执行其中的脚本。脚本如需写文件，应写入当前 Project Workdir 或 User Data，而不是 Skill 目录。个人 Skill 直接从当前用户工作区读取，不会复制到共享投影。\n\nSkill 的选择影响 Prompt 和工具激活；共享投影按用户授权集合生成，不会因为某个 Agent 选择了 Skill 就改变 Sandbox 身份。路径穿越、符号链接和跨用户访问由文件系统边界拒绝。\n\n## 维护建议\n\n- slug 使用小写字母、数字和单个短横线，尽量短且能表达用途。\n- `description` 先说明能力和适用场景，再写实现细节。\n- 把“何时使用”“不要做什么”“产物放在哪里”和“如何验收”写进 `SKILL.md`。\n- 依赖链保持短小，避免循环依赖；只有真实需要时才声明工具或 MCP 依赖。\n- 脚本按不可信输入处理，不读取或输出运行环境中的秘密。\n- 修改共享 Skill 的依赖、范围或文件后，用一个真实 Agent Run 验证模型可见工具和最终产物。\n\n实现入口见 [包格式解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/package.py)、[安装草稿生命周期](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/draft.py)、[共享 Skill 索引与安装](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/shared.py)、[共享文件编辑](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/edit.py)、[个人 Skill 安装与文件操作](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/personal.py)、[用户投影](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/projection.py)、[运行时解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/skills/runtime.py) 和 [Skills middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/skills.py)。\n","docs/agents/subagents-management.md":"# 使用子智能体\n\n子智能体是一个特殊的 Agent：它仍然是 `agents` 表中的一级智能体，只是标记为 `is_subagent=true`，并使用 `SubAgentBackend`。因此，子智能体和普通智能体共用创建、权限和配置入口。\n\n## 在页面中配置\n\n进入“智能体”，点击“新增智能体”，在后端类型中选择 `SubAgentBackend`。然后像配置普通智能体一样设置名称、提示词、模型、工具、知识库、MCP 和 Skills。\n\n子智能体有三个限制：\n\n- 不会出现在普通聊天的智能体切换列表中；\n- 不能设置为默认智能体；\n- 不能继续调用其他子智能体，因此不会形成孙级调用链。\n\n主智能体在运行配置的“子智能体”字段中选择允许调用的对象：\n\n- 未配置时默认仅选择通用任务（`[\"general-purpose\"]`），运行时仍按当前用户权限过滤；\n- 显式选择 `\"all\"` 时，每次运行选择当前用户可见的全部子智能体；\n- 保存空列表 `[]` 时不启用子智能体；\n- 显式选择后，只允许调用所选项；\n- 每个子智能体使用自己的 `config_json.context`，不会继承主智能体的模型或工具选择；\n- 用户权限变化后，新运行会重新计算可见范围。\n\n## 调用方式\n\n主智能体通过工具调用子智能体，不要通过 Shell、`curl` 或 HTTP API 间接调用。\n\n### 派发任务：`subagent_start`\n\n所有子任务通过 `subagent_start` 派发，立即返回 `run_id`、`thread_id` 和访问链接。需要结果时调用 `subagent_await(run_id)`；即使短任务也先派发再等待。多个独立任务先全部派发，再按需等待。\n\n工具参数：\n\n```python\n{\n    \"description\": \"整理这份文档的三条要点\",\n    \"subagent_slug\": \"general-purpose\",\n    \"thread_id\": null\n}\n```\n\n首次调用不需要 `thread_id`。如果要继续之前的子任务，把上一次结果中的子智能体线程 ID 传回去。\n\n### 查询、等待与取消\n\n生命周期工具统一按 Run 工作：\n\n| 工具 | 作用 |\n| --- | --- |\n| `subagent_start` | 创建并立即返回 `run_id`、`thread_id` |\n| `subagent_status` | 查询运行状态和最近几条进度摘要 |\n| `subagent_await` | 等待终态并取得最终结果；超时返回当前快照 |\n| `subagent_cancel` | 请求取消子智能体运行 |\n\n同一个子智能体线程同时只能有一个运行中的 Run。忙碌时工具返回 `busy`，不会隐藏地把请求排队。终态后的同一 `thread_id` 可以继续创建新的 Run。\n\n所有生命周期工具按 `run_id` 操作，并验证该 Run 由当前父 Run 创建，不能读取或控制其他任务。\n\n## 运行时边界\n\n一次子智能体调用会创建独立的 child checkpoint thread 和 `agent_runs(run_type=subagent)` 记录，同时继承父运行的用户身份和根执行树：\n\n| 资源 | 主智能体 | 子智能体 |\n| --- | --- | --- |\n| LangGraph checkpoint | 当前 `thread_id` | 独立 `child thread_id` |\n| Sandbox runtime | 根 `runtime_scope_id` | 与根运行相同 |\n| Project Workdir | 当前 Project 的 Workdir | 与根 Conversation 绑定的 Project 相同 |\n| UserWorkspace | 当前用户的工作区 | 同一用户的工作区 |\n| 共享/内置 Skills | 当前用户授权的只读投影 | 同一授权投影 |\n\n父子智能体看到的是同一份 Workdir 文件字节，不会通过 checkpoint 复制或合并文件。child thread 只隔离 LangGraph 上下文；它不是文件系统隔离边界。并发写同一路径仍按真实 POSIX 文件结果处理。\n\n子智能体可以使用自己配置的文件工具和知识库范围，但所有资源访问仍以发起用户的后端权限为最终边界。`present_artifacts`、`ask_user_question`、`install_skill` 等不适合子智能体直接使用的工具会被过滤；禁用状态不只在模型可见性上生效——即使模型显式传回被禁用的工具调用，执行层也会返回绑定原调用的错误结果，不会执行该工具。\n\n## API 和数据模型\n\n子智能体沿用普通 Agent 管理 API：\n\n- `GET /api/agent` 默认返回聊天可用的普通 Agent；\n- `GET /api/agent?include_subagents=true` 返回包含子智能体的列表；\n- 创建或更新 `SubAgentBackend` 时，后端会校验 `is_subagent=true`；\n- 详情、更新和删除复用同一套 Agent 权限检查。\n\n运行时，主智能体的 `subagents` 会先收敛为当前用户可见的允许列表。列表非空时挂载子智能体 middleware；子智能体自身不会挂载这一中间件。\n\n## 查看结果\n\n父 state 的 `subagent_runs` 保存子任务身份，页面加载时通过数据库父子关系补齐尚未进入 checkpoint 的记录。状态面板独立订阅各子 Run 的事件流，并从 Run 接口读取当前状态；父 graph 在 `subagent_await` 中等待时，先完成的子任务立即更新。切换会话会关闭订阅，重新打开时回读状态；完成后从持久化消息读取最终结果。Redis 原始事件只供运行基础设施和前端订阅，不作为主智能体的工具结果。\n\n前端最多同时订阅三个子 Run，其余活跃子 Run 每两秒查询状态，避免 HTTP/1.1 的同源连接被长连接占满；连接无事件时每十五秒核对持久状态，断线或查询失败显示重连提示。\n\n## 从 task 工具迁移\n\n历史 `task` 消息仍可查看，新模型不再获得该工具。升级前完成或取消旧版本的活跃 Run，并在智能体管理中将已保存的深度研究 Agent 及自定义提示词中的 `task` 指令改为先 `subagent_start`、再 `subagent_await`；已有配置不会被新的默认提示词覆盖。历史 checkpoint 中尚未执行的 `task` 调用会明确返回未知工具错误，不会被自动重放为新的子任务。父 Run 终态仍按原有策略取消活跃后代，派发与等待分离不改变此边界。\n\n实现入口见 [子智能体 middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/subagent_task.py)、[SubAgentBackend](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/subagent/graph.py) 和 [AgentRun 服务](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/agent_run_service.py)。\n","docs/agents/tools-system.md":"# 工具系统\n\nYuxi 的工具分成三层：内置工具、知识库工具和 MCP 工具。Graph 创建时准备可执行工具，运行时再根据用户权限、Agent 配置和 Skill 激活状态决定模型能看到什么。\n\n## 注册一个内置工具\n\n普通内置工具使用 `@tool` 注册：\n\n```python\nfrom yuxi.agents.toolkits.registry import tool\n\n\n@tool(category=\"buildin\", tags=[\"示例\"], display_name=\"示例工具\")\ndef example_tool(text: str) -> str:\n    \"\"\"返回处理后的文本。\"\"\"\n    return text\n```\n\n- `category` 用于前端分组，常见值是 `buildin`、`knowledge` 和 `debug`；\n- `tags` 用于展示和筛选；\n- `display_name` 是给用户看的名称，工具 ID 是给代码和模型协议使用的稳定名称；\n- 工具模块需要被 `toolkits` 包导入，装饰器才会执行注册。\n\n工具的注册不等于授权。产生文件、网络或数据库副作用的工具必须在执行边界再次校验当前用户和目标资源。\n\n## 当前内置能力\n\n常用内置工具包括：\n\n| 工具 | 作用 |\n| --- | --- |\n| `ask_user_question` | 等待用户回答交互式问题 |\n| `ocr_parse_file` | 把工作区中受支持的 PDF、Office 或图片转换为 Markdown |\n| `present_artifacts` | 展示当前用户可见的文件产物 |\n| `install_skill` | 从允许的沙盒路径或 Git 来源安装个人 Skill；子智能体不可用 |\n| `web_search` | 使用已配置的豆包或 Tavily 搜索网页 |\n\n文件读写和命令执行由 Agent 的 Sandbox backend 提供。`present_artifacts` 推荐展示当前 Project 的 `outputs/` 文件；`large_tool_results` 和会话摘要等内部文件不会作为交付物展示。\n\n图片生成能力由内置 `image-gen` Skill 提供，不再作为独立的 Python 工具注册。具体依赖和文件位置由该 Skill 说明。\n\n## 知识库工具\n\n知识库工具以 `@tool(category=\"knowledge\")` 注册，但不默认出现在模型工具列表。Agent 激活内置 `knowledge-base` Skill 后，Skills middleware 才会向模型开放：\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_kbs` | 列出当前运行可见的知识库 |\n| `query_kb` | 按 `kb_id` 检索片段，返回 `kb_id`、`file_id` 和内容 |\n| `find_kb_document` | 在指定文件中按关键词或正则定位内容 |\n| `open_kb_document` | 按 `file_id` 分段读取解析后的文档 |\n| `get_mindmap` | 读取知识导图 |\n| `search_file` | 按文件名搜索可见知识库中的文件 |\n| `download_kb_file` | 把有权限的原始文件下载到当前 Project 的 `outputs/` |\n\n工具参数中的 `kb_id`、`file_id` 和文件名都会在工具执行处重新检查，不能用模型提示词或 Agent 配置绕过知识库权限。知识库不会挂载为沙盒目录，读取方式见[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n需要在 Python 中直接取得知识库工具时：\n\n```python\nfrom yuxi.agents.toolkits.kbs import get_common_kb_tools\n\nkb_tools = get_common_kb_tools()\n```\n\n返回的具体顺序由函数实现维护，不要把顺序当作协议。\n\n## 内置工具：网页搜索\n\n`web_search` 让 Agent 从外部网页获取实时信息，当前支持豆包和 Tavily 两个供应商。搜索结果属于外部来源，不能自动替代知识库或人工核验；对重要结论应回到原网页核实。\n\n供应商在 API/worker 环境中选择并提供凭证：\n\n```bash\nWEB_SEARCH_PROVIDER=doubao\nDOUBAO_SEARCH_API_KEY=<your-doubao-key>\n```\n\n或 `WEB_SEARCH_PROVIDER=tavily` 加 `TAVILY_API_KEY=<your-tavily-key>`。`WEB_SEARCH_PROVIDER` 只接受这两个值；留空时按已配置的 API Key 自动选择，两个 Key 都配置时优先豆包，没有有效 Key 时不会注册该工具。密钥只放在受保护的环境变量或密钥管理器中，不要写进 Skill、Agent 用户环境、代码或日志。\n\n工具在 API 和 worker 进程加载时注册，修改环境变量后需要 `docker compose up -d --force-recreate api worker`。验证方式：在智能体详情确认工具列表出现「网页搜索」，用一个需要最新信息的问题发起真实对话，并检查工具调用返回的 URL、标题和摘要。没有该工具时检查 `WEB_SEARCH_PROVIDER` 拼写、对应 Key 是否存在以及容器是否已重建。\n\n实现入口：[网页搜索工具](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/toolkits/buildin/tools.py)。\n\n## 工具组装流程\n\n内置 Agent 创建 Graph 时执行：\n\n1. `prepare_agent_runtime_context` 按当前用户权限过滤工具、知识库、MCP、Skills 和子智能体。\n2. `resolve_configured_runtime_tools(context)` 注册 Agent 配置和可见 Skill 依赖的可执行本地工具，并加载配置的 MCP 工具。\n3. `SkillsMiddleware` 根据当前已预加载或已激活的 Skill，向模型请求开放相应工具 schema。\n4. 工具执行器再次检查具体文件、知识库、MCP 和用户身份。\n\n```text\nAgent 配置 + 用户权限\n        ↓\n运行时资源快照\n        ↓\n可执行工具注册 ──→ Skill 激活门控 ──→ 模型可见工具\n        ↓\n执行处的目标授权\n```\n\n“可执行”与“模型可见”是两件事：工具可以先注册到 ToolNode，等 Skill 激活后才对模型开放；模型看见某个工具也不代表它可以访问任意资源。\n\n## MCP 和 Skills\n\nMCP 工具可由 Agent 直接选择的已启用服务器提供，也可由已激活 Skill 的 `mcp_dependencies` 按需加载；预加载 Skill 从首轮开放本地工具和 MCP 依赖，普通 Skill 在读取 `SKILL.md` 激活后开放这些依赖。这一节只回答“工具从哪里来、谁决定模型可见”；服务器配置和启停归 [MCP 集成](./mcp-integration.md)，Skill 的创建、依赖与激活时机归 [Skills 管理](./skills-management.md)，资源选择的字段语义归[智能体配置](./agents-config.md)。\n\n## 新增工具时检查\n\n- 工具是否属于内置工具、知识库工具、MCP 还是 Skill；\n- 是否需要在 `ToolNode` 注册，是否需要 Skill 激活后才让模型看见；\n- 产生副作用时，执行处是否有用户、路径和资源权限校验；\n- 错误是否以结构化结果返回，并保留可排查信息；\n- 前端展示名称是否来自稳定元数据；\n- 是否补充纯逻辑和真实 HTTP/文件链路的相应测试。\n","packages/yuxi-cli/README.md":"# yuxi-cli\n\nYuxi command line client.\n\nFirst-stage scope:\n\n- remote management through `~/.yuxi/config.toml`\n- browser login\n- API Key import through `--api-key`\n- `whoami`, `status`, and `logout`\n- server discovery and compatibility check for Yuxi `>=0.7.1`\n- `yuxi chat` for a temporary local browser chat with streamed Agent output; `/state` reads thread state and `/approve` resumes a pending tool approval\n- `yuxi agent list` and `yuxi agent show <slug>` for inspecting agents visible to the logged-in user\n- `yuxi kb upload` for knowledge base file uploads\n- `yuxi agent eval` for running existing Langfuse dataset experiments with a logged-in remote\n","web/AGENTS.md":"# Web 约定\n\n本目录是 Vue 3 / Vite 前端。先阅读根 [AGENTS.md](../AGENTS.md)、[设计规范](../docs/develop-guides/design.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n- API 调用统一放在 `src/apis`；组件不直接拼接普通 HTTP 请求。\n- 前端权限与路由守卫只提供体验约束，后端始终执行最终授权。\n- 复用 `src/assets/css/base.css` 变量和 `@lucide/vue`；不为一次性视觉需求引入新依赖。\n- 保持 loading、empty、error、断线恢复和终态投影语义一致；不要用乐观 UI 覆盖 PostgreSQL 返回的最终事实。\n- `pnpm run lint:check` 是只读 gate；`pnpm run lint` 才允许本地自动修复。\n\n提交前运行：\n\n```bash\ndocker compose exec web pnpm run lint:check\ndocker compose exec web pnpm run test:unit\ndocker compose exec web pnpm run build\n```\n\nUI 改动必须在真实页面验证，并提供最终截图或录屏；适用时覆盖浅/深色、响应式、loading、empty 和 error 状态。\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Yuxi Agent 开发约定\n\nYuxi 是基于 LangGraph、FastAPI、Vue 和多种持久化服务构建的知识库与多智能体平台。Docker Compose 是开发拓扑的事实来源；修改不熟悉的模块前先阅读 [ARCHITECTURE.md](ARCHITECTURE.md)，再用符号搜索确认真实实现。\n\n## 每次任务先加载什么\n\n- [ARCHITECTURE.md](ARCHITECTURE.md)：稳定边界、主链路和架构不变量。\n- [Yuxi Spec Loop](docs/develop-guides/spec-loop.md)：非平凡变更从提案、证据到收敛的流程。\n- [工程信任系统](docs/develop-guides/engineering-trust.md)：语义 Owner、证据、决策记录、派生审计和 gate 规则。\n- [测试规范](docs/develop-guides/testing-guidelines.md)：unit、integration、E2E 的职责与命令。\n- [贡献指南](docs/develop-guides/contributing.md)：分支、独立 Review、commit 和 PR 流程。\n- [并行工作树与隔离运行环境](docs/develop-guides/parallel-worktree-environments.md)：同时运行多个分支、复用长期数据或处理 Schema 不兼容时加载。\n- 用户在当前任务中的明确要求优先于本文件；修改 `backend/`、`web/` 或 `docs/` 时同时遵循该子树的 `AGENTS.md`。子树规则只补充本目录，不复制回根文件。\n\n## 任务与决策\n\n1. 开始实现前，把请求压缩为可验证的目标、非目标和验收主张；多步任务先给出每步带验证方式的简短计划。\n2. 假设显式记录后继续；只有当不同解释会改变验收结果、数据、安全或外部状态时才阻塞询问，并把候选解释一并列出而不是默默选择其一。存在明显更简单的方案时，先说明取舍再动手。\n3. 把\"可以\"\"也可以\"\"类似这样\"\"例如\"当作简单方向，不是设计更大机制、配置项或兼容层的许可。\n4. 在真实语义 Owner 处闭合受影响的工程主张：源码、数据约束或当前契约拥有事实；独立 oracle 与负向案例证明它；实际 workflow 或可问责 Reviewer 产生拒绝后果。不要建立可独立编辑的中央主张清单或 claim ID 体系。\n5. 非平凡变更必须新增或更新一份 tracked [决策记录](docs/develop-guides/decisions/README.md)。记录问题、当前决定、真实替代项、后果和验证，不保存推理流水账或实现过程叙事。\n6. `docs/vibe/` 只用于本地临时计划，被 Git 忽略，不是组织记忆，也不能作为已完成事实的唯一来源。\n7. 只修改验收标准需要的范围；不顺手重构、格式化或添加想象中的配置、兼容层和扩展点。\n\n## 不能破坏的系统事实\n\n- HTTP 路由保持薄；用例流程属于 `yuxi.services`，持久化查询属于 `yuxi.repositories`。\n- 普通请求先在 PostgreSQL 中持久化 Message 和 AgentRunRequest；只有 ready FIFO 队头创建 AgentRun，且每次投递 ARQ 前 owning transaction 都已提交。Redis 负责投递、短期事件、取消和缓存，不拥有最终业务状态。\n- 同一用户、Agent、线程的普通请求按 FIFO 串行派发；Request 和 Run 是不同状态模型。\n- AgentRun 的输出、事件、artifact 和错误必须绑定同一 request/run；禁止从相邻 Run 猜测结果。\n- 非终态 Run 必须有明确执行 Owner、lease/heartbeat 或等价机制，以及崩溃后的可观察结局。\n- `/api/system/health` 只表达进程 liveness；接流量前置条件由 `/api/system/ready` 证明，业务正确性仍由真实链路测试证明。\n- LangGraph checkpoint 只使用 PostgreSQL；API、worker 与 Agent 不提供本地后端选择或静默降级。\n- 权限在后端依赖与 repository 可见性查询处最终执行；前端守卫、prompt、schema omission 和 UI 隐藏不是授权边界。\n- Shipping 启动、路由注册和能力发现始终包含知识库、图谱与评估能力；附件解析入口只在真实解析动作发生时惰性加载 parser。\n- 沙盒虚拟路径、对象 URL 和宿主机路径不可混用；所有用户路径必须在 owning filesystem boundary 校验。\n\n## 证据规则\n\n- Agent 的完成报告、HTTP 200、日志关键词或 mock 调用次数都不是最终事实；重新读取数据库、文件、对象、DOM 或协议结果。\n- 每个新 guard 都要有一个能恢复目标缺陷并使其在正确原因上失败的负向测试。\n- 测试从最小相关集合开始，再按风险扩大；不要用 unit 结果替代真实 PostgreSQL、真实 HTTP、worker 或浏览器语义。\n- Expected output、snapshot 和 fixture 只能显式更新并审阅；CI 不得一边生成 oracle 一边验证 oracle。\n- 实际执行命令、结果和未执行原因写入 PR；未验证不能写成通过。\n\n提交前在仓库根目录至少运行：\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\n```\n\n改动面按下表选择最低证据，并按升级条件扩大；不要用低层级结果替代高风险语义。\n\n| 改动面 | 最低证据 | 何时升级 |\n|---|---|---|\n| 纯 Python / JS 逻辑 | 相关 unit，断言业务结果 | 触及数据库、缓存或文件副作用时补 integration |\n| API / 权限 / 持久化 | 真实 HTTP integration | 跨 worker、队列或用户主链路时补 E2E |\n| Run / FIFO / SSE / 沙盒 / 恢复 | E2E，验证最终状态与产物 | 依赖外部可选服务时记录环境与未验证范围 |\n| 前端交互 | lint + unit（[web/AGENTS.md](web/AGENTS.md)） | 行为关键时补 build 与真实页面验证 |\n| 文档与导航 | 相对链接检查 + docs build | 公开行为变化时与代码验证一起执行 |\n\n完整命令由 [测试规范](docs/develop-guides/testing-guidelines.md) 维护。\n\n## 实现与安全\n\n- 使用满足验收标准的最小、线性实现。抽象、状态机、fallback、兼容路径和依赖都要有当前 consumer 与 Owner；仓库外用户、持久数据和部署承诺也算 consumer。\n- 实现明显长于问题本身，或一半行数可以清楚表达同样行为时，先简化再提交；自检\"高级工程师是否会认为这段代码过度设计、过度防御或过于零碎\"。\n- 不可信输入只在 parser、配置、模型/tool JSON、持久化、worker、process、wire 和用户路径等真实信任边界校验；授权与隔离在产生副作用的 executor/repository fail-closed，直接或替代调用路径都不能绕过。\n- 预设条件不成立时显式失败。可选能力可以降级，但必须结构化、可观察，且不把未就绪伪装为成功。\n- 新增函数/类使用简洁中文 docstring；注释只解释非显然约束、时序、Owner 和安全用法，不复述代码。\n- 不输出或提交 `.env`、账号、Token、用户数据、运行目录和构建产物。\n- 文件以一个换行结尾；提交前运行 `git diff --check`。\n\n## Review 与交付\n\n所有代码变更在 commit 前必须由不继承开发上下文的全新 Reviewer Agent 审查完整需求、diff、测试和规范；修复所有影响功能、边界、证据可信度或认知负担的问题。提交信息使用中文 Conventional Commit，PR 使用仓库模板，以自然语言列出受影响主张、语义 Owner、证据、风险和未验证范围，不引用中央 claim ID。\n","isInternal":false,"tokens":2458,"sizeBytes":7324},{"name":"AGENTS.md","path":"backend/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/backend/AGENTS.md","title":"backend - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Backend 约定\n\n本目录承载 FastAPI 适配层、业务服务、repositories、Agent runtime 与持久化实现。先阅读根 [AGENTS.md](../AGENTS.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n## 边界与所有权\n\n- `server/routers` 只处理 HTTP 模型、认证依赖、状态码和响应装配；跨 repository 的用例进入 `package/yuxi/services`。\n- PostgreSQL 是 Request、Run、Message、权限和业务终态的 Owner；Redis/ARQ 是投递与短期事件平面。\n- 写入事实、提交事务、发布队列/事件的顺序必须显式；通知不能早于 owning transaction 的 commit point。\n- 跨 repository 用例只有一个事务 Owner；需要经 HTTP 返回的一次性 secret 必须可由幂等请求安全重放，不能先不可逆消费再祈望响应送达。凭据撤销必须保留足以阻止同一幂等请求复活 secret 的 tombstone。\n- parser、HTTP、模型/tool JSON、持久化、worker、process、wire 和用户路径是运行时校验边界；已由 Python 类型和同进程调用保证的内部值不重复 hostile validation。\n- 核心启动依赖失败要阻止 readiness；可选集成降级要记录组件名、失败类型与当前能力。\n- AgentRun 的状态转换、lease、输出和终态投影由 repository/service 统一维护，调用方不得直接拼装并行真相。\n\n## 实现\n\n- Python 使用 3.12+ 语法；保持主流程线性，优先早返回和清晰主路径，避免细碎 helper、静默 fallback、一次性抽象、不必要的嵌套和多层调用链。\n- 本次新增或实质修改的代码遵循向下规则：同一模块或类内，公开、高层方法在上，专用实现细节在后；旧代码布局不豁免新改动。可调整直接相关定义的位置，不重排整个文件；基类、装饰器、默认值和模块初始化等定义时依赖优先满足 Python 执行顺序。Reviewer 核对局部阅读顺序。\n- 拆函数用于明确复用、隔离副作用或实质降低认知负担；实现细节遮蔽主流程时，即使只调用一次也可提取。仅转发参数或包装表达式且不增加语义的 helper 通常内联。\n- 常量和具名值放在最小合理作用域：跨函数复用、协议标识或配置约束用模块级常量；仅服务单个函数的局部规则留在函数内。\n- 命名表达业务意图；不导入其他模块下划线开头的私有标识，确有共享需求时改为公开命名。\n- 删除本次修改产生的未使用 import、变量、函数和分支；不清理修改前已存在的无关死代码，发现坏味道只说明不擅自处理。\n- 小型状态、进度或摘要需求直接读取来源并返回最小结果，不重建事件流或调试视图。\n- 新增函数/类使用简洁中文 docstring；不同语义的代码段之间留空行保持可读。异常只在当前层能增加稳定语义、执行清理或决定策略时捕获。\n- Schema 演进必须幂等、可在现有数据上执行，并有真实 PostgreSQL 测试；不可逆操作明确数据影响。\n\n## 验证\n\n```bash\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\ndocker compose exec api uv run --group test pytest test/integration\ndocker compose exec api uv run --group test pytest test/e2e -m e2e\ndocker compose exec api uv run ruff check package\ndocker compose exec api uv run ruff format package --check\n```\n\n并发、事务、锁、lease、schema 与 PostgreSQL 专属语义必须在真实 PostgreSQL 上验证；API 行为通过真实 HTTP integration 证明；关键 Run/worker/文件副作用通过 E2E 证明。\n","isInternal":false,"tokens":1166,"sizeBytes":3598},{"name":"AGENTS.md","path":"docs/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/AGENTS.md","title":"docs - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# 文档约定\n\n本目录包含用户教程、配置参考、机制详解、开发规范、决策记录、事故复盘和版本材料。仓库根 `AGENTS.md` 的系统不变量仍然适用；完整信息架构、写作标准与检查清单由[文档编写与维护规范](develop-guides/documentation-guidelines.md)拥有，本文件只保留进入 `docs/` 工作时必须执行的指令。\n\n## 层级与事实 Owner\n\n- 每个事实只有一个负责完整解释的 Owner；其他页面只保留完成当前任务需要的上下文并使用相对链接。先按[层级表](develop-guides/documentation-guidelines.md#信息架构与事实-owner)确定页面位置，再写内容。\n- `intro/` 拥有从零完成结果的教程，`advanced/` 拥有配置和运维参考，`agents/` 拥有 Agent 配置与扩展方法，`mechanisms/` 拥有运行机制、状态、权限、失败和源码定位。实质性混合内容必须拆页。\n- 当前系统边界和主链路属于仓库根 `ARCHITECTURE.md`；测试层级与命令属于[测试规范](develop-guides/testing-guidelines.md)；工程信任闭环属于[工程信任系统](develop-guides/engineering-trust.md)。不要在专题页复制这些完整规则。\n- 非显然取舍属于 `develop-guides/decisions/`，达到门槛的事故因果属于 `develop-guides/postmortems/`，已发布事实属于 changelog，未完成方向属于 roadmap。`docs/vibe/` 只用于被忽略的本地临时计划。\n- 源码、schema、Compose、数据约束和测试拥有可执行事实；外部 Wiki、旧 changelog、历史 PR 和 Agent 自述只能帮助定位，不能覆盖当前 Owner。\n\n## Agent 写作流程\n\n1. 读取根与当前子树指令、`ARCHITECTURE.md`、owning 文档和相关源码；非平凡信息架构或长期约束变化先创建 tracked proposed decision。\n2. 写明预定读者、问题、前置知识、目标、非目标和页面类型。教程必须有顺序结果；参考必须有明确查找范围；实质性混合内容先拆分。\n3. 用符号搜索沿入口 → service/executor → repository/持久化或发布点 → 用户/模型结果重建真实链路，同时核对权限、失败路径、可选能力和相关测试。\n4. 先列 Section 及每节唯一问题，按“概念 → 组件关系 → 状态/Owner → 权限/失败 → 源码定位”渐进；教程按完成任务所需的前置依赖排序。\n5. 一次最多撰写或重写一个 Section。完成后立即核对事实、相对链接、与相邻章节的重复及当前页面职责，再进入下一节。\n6. 先更新事实 Owner，再更新导航、索引和引用页。页面移动或拆分必须在同一变更中修复全部入站链接，不保留两个可独立编辑的副本。\n7. 提交前运行最小相关 gate，并由不继承开发上下文的全新 Reviewer 对照完整需求、源码、diff、测试和未验证范围审查。\n\n## 写作与维护\n\n- 使用直接、具体、可验证的现在时，写明执行者、条件、时序、结果、失败和后果。不要用“应该、一般、可能”掩盖未核实事实；无法核实时明确条件与 `Not run` 范围。\n- 优先用肯定句直接陈述职责、边界和结论。禁止先否定一个弱命题、再用转折强调目标命题的对举句式；删除空泛铺垫、宣传性形容词和替读者下判断的强调语。\n- 普通段落每段只用一个物理行并依靠编辑器软换行；代码块、表格和列表保留结构。一个段落包含多个独立规则时拆段，不用硬换行掩盖段落墙。\n- 不在当前说明中叙述“以前/现在/这次修改”、PR 过程、Review 对话、实现步骤或推理流水账。原因和取舍写入 decision，事故时间线写入 postmortem。\n- 不手写易漂移的完整文件、API、测试或配置清单；保留稳定分类并链接源码、schema、Compose 或生成 Owner。不要按函数逐行复述控制流。\n- 机制页必须覆盖适用范围、真实装配、状态与事实 Owner、权限/隔离、失败/恢复、可观察结果、配置边界和“源码定位与验证”；配置页保留参数、操作与排障，不复制机制全景。\n- 教程的关键步骤给出页面、状态、文件或协议等可观察结果；参考字段写清读取者、默认值、条件、限制和生效时机。实质性的教程与参考内容不能长期共居一页。\n- 图只用于三项以上关系、状态或时序，使用能由 VitePress 渲染的最小 Mermaid；图不能替代正文中的条件、异常和安全语义。尚未实现的方向不进入当前机制图。\n- 站内页面使用相对 Markdown 链接；站点外公开源码使用项目 GitHub `blob/main` 文件链接。禁止本地绝对路径、易漂移行号和只写文件名不说明职责的源码清单。\n- 示例只使用占位凭据、保留域名或假 ID；禁止写入 `.env`、Token、真实账号、用户数据、本地绝对路径或不可公开的内部地址。不要建议把 provisioner/数据库等管理凭据注入 Agent 沙盒。\n- 决策记录遵循[生命周期与格式](develop-guides/decisions/README.md)；implemented 记录使用现在时，不保留提案 checklist。用户可见完成项与 roadmap 不得同时声明同一状态。\n\n## 导航、预算与验证\n\n- 新增正式页面时更新 `.vitepress/config.mts` 的正确父级和阅读顺序；内部 `AGENTS.md`、decision 与 postmortem 不因位于 `docs/` 就自动成为用户导航入口。\n- `scripts/verify_engineering_contracts.py` 的 AGENTS 字符预算是 standing-order guardrail。超限时先下沉示例、背景和专题解释，再压缩重复；确需提高预算时同步更新 decision 和 gate，不能静默删掉必要约束或关闭检查。\n- 修改配置、API、状态、权限或机制说明时检查相关测试；文档构建只证明 Markdown、导航和链接有效，不证明语义正确。Reviewer 必须逐页对照当前源码、配置和风险相称的 oracle。\n- 构建失败要修正文档、锚点或导航，不扩大 `ignoreDeadLinks` 掩盖问题。报告只写实际执行的命令、结果和未验证范围。\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ncd docs && pnpm run build\ngit diff --check\n```\n","isInternal":false,"tokens":2101,"sizeBytes":6305},{"name":"AGENTS.md","path":"web/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/web/AGENTS.md","title":"web - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Web 约定\n\n本目录是 Vue 3 / Vite 前端。先阅读根 [AGENTS.md](../AGENTS.md)、[设计规范](../docs/develop-guides/design.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n- API 调用统一放在 `src/apis`；组件不直接拼接普通 HTTP 请求。\n- 前端权限与路由守卫只提供体验约束，后端始终执行最终授权。\n- 复用 `src/assets/css/base.css` 变量和 `@lucide/vue`；不为一次性视觉需求引入新依赖。\n- 保持 loading、empty、error、断线恢复和终态投影语义一致；不要用乐观 UI 覆盖 PostgreSQL 返回的最终事实。\n- `pnpm run lint:check` 是只读 gate；`pnpm run lint` 才允许本地自动修复。\n\n提交前运行：\n\n```bash\ndocker compose exec web pnpm run lint:check\ndocker compose exec web pnpm run test:unit\ndocker compose exec web pnpm run build\n```\n\nUI 改动必须在真实页面验证，并提供最终截图或录屏；适用时覆盖浅/深色、响应式、loading、empty 和 error 状态。\n","isInternal":false,"tokens":350,"sizeBytes":995},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"AGENTS.md","isInternal":false,"tokens":3,"sizeBytes":9},{"name":"SKILL.md","path":"backend/package/yuxi/agents/skills/buildin/deep-research/SKILL.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/backend/package/yuxi/agents/skills/buildin/deep-research/SKILL.md","title":"deep-research","category":"anthropic-skill","format":"markdown","content":"---\nname: deep-research\ndescription: \"深度研究编排方法论：澄清范围、拆解规划、并行调度子智能体调研、对抗式核验、综合成带引用的结构化报告。当任务需要多来源、可追溯、需事实核查的深度研究时使用此技能。\"\nversion: \"2026.09.19\"\ntool_dependencies: [\"web_search\", \"present_artifacts\"]\n---\n\n# 深度研究技能\n\n当任务目标是产出**多来源、可追溯、经过核验**的深度研究结论（科研综述、行业/竞品调研、技术选型、专题分析等）时，使用此技能组织整个研究过程。本技能的核心是**编排**：你负责整体把控与子智能体调度，把繁重的检索与核验工作派发出去，自己专注规划与综合。\n\n## 可用子智能体\n\n通过 `subagent_start` 工具派发，记录返回的 run_id；需要结果时调用 `subagent_await`。互不依赖的子任务先全部派发，再等待结果：\n\n- `research-explorer`（调研探索员）：围绕一个明确子问题做多轮网页/知识库检索，返回按要点组织、带 `<cite>` 引用的结构化发现。**这是主力，按子问题并行多开。**\n- `fact-verifier`（事实核查员）：对给定的关键论断做对抗式核验，逐条给出 支持 / 存疑 / 反驳 + 依据来源 + 置信度，并标注冲突。\n\n## 编排流程\n\n### 1. 澄清范围\n问题不明确时，先用 `ask_user_question` 补充 2-3 个关键问题（研究目标、受众、范围边界、地域/时效、输出语言与形式），对齐验收标准后再开工。已经清晰的任务不要反复追问。\n\n### 2. 规划拆解\n用 `write_todos` 把研究目标拆成**可独立调研**的子问题，每个子问题写明产出标准（要回答什么、需要哪类证据）。子问题应正交、覆盖完整，避免重叠或遗漏关键角度。\n\n### 3. 并行派发调研\n- 把互不依赖的子问题用**多个 `subagent_start` 调用并行**派发给 `research-explorer`。\n- 每次派发在 `description` 中写清：子问题目标、已知上下文、期望输出格式（要点 + `<cite source=\"$URL\" type=\"url\">$INDEX</cite>` 引用 + 参考来源列表）。\n- 何时派发 vs 自己直检：子问题复杂、需多轮检索、可隔离上下文、可并行时一律派发子智能体；仅在澄清范围、补一两个零散事实、或快速校正方向时才自己少量直接检索。\n- 子问题之间有依赖时，先派发前置子问题，拿到结果后再派发后续。\n\n### 4. 核验关键结论\n对**影响最终结论的关键论断**、数字、以及子智能体之间相互冲突的发现，派发 `fact-verifier` 做对抗式核验。要求其默认倾向「证据不足即标注存疑」。核验未通过的结论不要写进正文，或必须明确降级标注。\n\n### 5. 综合成稿\n证据充分后，由你统一综合为结构化报告，**不要**简单拼接子智能体返回的原文。根据研究主题组织内容，围绕论证而不是资料堆砌。\n\n### 6. 停止准则\n信息饱和、或确认无法获取更多有效信息即停。明确标注证据缺口与不确定性，不臆断、不编造来源。\n\n## 引用\n\n- 子智能体返回中间证据时沿用 `<cite source=\"$URL\" type=\"url\">$INDEX</cite>`；最终报告中的关键结论、数据和观点用指向原始来源的普通 `<a href=\"...\">` 链接就近标注。\n- 引用用户附件或知识库时标明文件名或路径。\n\n## 输出\n\n- 最终交付的是一份可直接使用的报告，而不是「我打算怎么研究」。\n- 不要外泄中间推理过程、原始检索日志，也不要把待办清单原样输出成正文。\n- 报告语言与用户提问语言一致，使用正式、克制、可复核的书面表达。\n- 用户没有指定格式时，默认把完整报告写入当前 Workdir 的 `outputs/` 目录，保存为一个 `.html` 文档；不要把完整报告直接铺在聊天正文里。用户明确要求 Markdown、纯文本、JSON 或其他格式时，以用户要求为准。\n- 从系统提示读取当前 Project Workdir 的**绝对虚拟路径**，用 `write_file` 写入 `<当前 Workdir 绝对路径>/outputs/<文件名>.html`。不要把 `outputs/report.html`、宿主机路径或 `/outputs/report.html` 传给文件工具。\n- 写入成功后，把 `write_file` 返回的同一绝对路径传给 `present_artifacts`。最终聊天回复只需简要说明报告已完成并提示交付物，不要重复报告正文。\n\n默认 HTML 应简洁、优雅并专注阅读，同时给模型保留版式判断空间：\n\n- 页面与正文宽度随视口自适应。宽屏可以使用侧栏目录帮助浏览章节，窄屏隐藏或折叠侧栏，让正文自然占满可用宽度。\n- 可以按内容需要引用外部样式、字体、图片或其他公开资源；图片应服务于研究内容，而不是纯装饰。\n- 不提供固定 HTML 模板；根据研究主题选择合适的章节、图表、表格和视觉层级，整体保持克制、清晰，不做复杂浮夸的展示页。\n","frontmatter":{"name":"deep-research","description":"深度研究编排方法论：澄清范围、拆解规划、并行调度子智能体调研、对抗式核验、综合成带引用的结构化报告。当任务需要多来源、可追溯、需事实核查的深度研究时使用此技能。","version":"2026.09.19","tool_dependencies":["web_search","present_artifacts"]},"isInternal":false,"tokens":1862,"sizeBytes":5032},{"name":"SKILL.md","path":"backend/package/yuxi/agents/skills/buildin/html-preview/SKILL.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/backend/package/yuxi/agents/skills/buildin/html-preview/SKILL.md","title":"html-preview","category":"anthropic-skill","format":"markdown","content":"---\nname: html-preview\ndescription: \"使用 Markdown `html:preview` 围栏输出轻量静态 HTML/CSS 可视化。当普通 Markdown 难以清晰表达数值对比、层级关系、流程、时间线、关键指标或布局示意，或用户明确要求可视化 HTML 预览时使用。询问 HTML 源码、教程示例或可复制代码时不要使用。\"\nversion: \"2026.07.23\"\n---\n\n# HTML Preview\n\n将 Markdown 作为回答主体。只有可视化能明显降低理解成本时，才补充一个\n`html:preview` 静态组件；标题、列表、表格或代码块已经足够清楚时，继续使用普通 Markdown。\n\n按以下顺序输出：\n\n1. 先用普通 Markdown 写结论和必要解释。\n2. 顶格写开始围栏 ```` ```html:preview ````。围栏最多只能有 3 个前导空格，否则前端会把它当作源码。\n3. 在围栏内写自包含的静态 `<style>` 和语义化 HTML。\n4. 顶格写结束围栏 ```` ``` ````。\n5. 在围栏后用普通 Markdown 补充背景、风险、完整明细和来源。\n\n## 内容边界\n\n- 只输出静态 HTML/CSS，不编写 JavaScript，不使用表单、iframe、object 或 embed。\n- 可以引用公开、稳定、无需登录的 HTTPS 图片或字体，但核心信息必须在外链失败时仍然可读。\n- 不引用内网、临时地址、鉴权资源或包含用户凭证的链接。\n- 不把组件当成完整网页或正文容器；不要制作导航栏、页脚、登录态、复杂按钮、营销页 Hero 或多屏页面。\n- 不在组件中放长段叙事、完整报告、长表格或长列表。背景、推理、风险、明细和来源放在普通 Markdown 中。\n- 用户询问 HTML 源码、教程示例或需要复制代码时，使用普通 `html` 代码块，不使用 `html:preview`。\n\n## 布局约束\n\n- 面向默认 `800px × 360px` 尺寸设计，并允许容器宽度变化；前端最高显示到 `700px`，超出后在预览内部滚动。\n- 使用 `max-width: 100%`、`box-sizing: border-box`、弹性网格、换行和适度间距保持响应式。\n- 不写死整体画布高度。核心信息应在默认尺寸内无需滚动即可读；放不下时减少内容。\n- 最多展示 1 个短标题、3–5 个关键指标或一组简短对比。数据超过 6 项时，汇总为趋势、区间、异常点或 Top 3。\n- 文字以短标签、数字、单位、状态词和极短备注为主；单条说明尽量不超过 20 个中文字符。\n- 外层已经提供圆角、边框和裁切。不要再给最外层添加卡片壳、页面背景、大圆角、阴影、厚边框或额外外边距。\n- 保持克制和可读，优先使用紧凑指标组、摘要表、对比条、状态标签、时间轴或简单关系图。\n\n## 失败处理\n\n如果内容无法在上述静态、安全和篇幅边界内表达，改用普通 Markdown，并明确说明限制。不要输出残缺围栏、不可访问资源或依赖脚本才能理解的预览。\n","frontmatter":{"name":"html-preview","description":"使用 Markdown `html:preview` 围栏输出轻量静态 HTML/CSS 可视化。当普通 Markdown 难以清晰表达数值对比、层级关系、流程、时间线、关键指标或布局示意，或用户明确要求可视化 HTML 预览时使用。询问 HTML 源码、教程示例或可复制代码时不要使用。","version":"2026.07.23"},"isInternal":false,"tokens":1062,"sizeBytes":2928},{"name":"SKILL.md","path":"backend/package/yuxi/agents/skills/buildin/image-gen/SKILL.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/backend/package/yuxi/agents/skills/buildin/image-gen/SKILL.md","title":"image-gen","category":"anthropic-skill","format":"markdown","content":"---\nname: image-gen\ndescription: \"在 Agent 沙盒中生成图片并保存到 outputs。当用户要求生成图片、海报、插画、文生图，或指定 Qwen-Image、其它兼容图片生成接口时使用此技能。\"\nversion: \"2026.06.02\"\ntool_dependencies: [\"present_artifacts\"]\n---\n\n# 图片生成技能\n\n当用户要求生成图片、海报、插画、文生图，或明确提到 Qwen-Image 时，使用此技能组织图片生成流程。\n\n## 默认生成接口\n\n默认使用 SiliconFlow 的 Qwen-Image 接口：\n\n- Endpoint: `POST https://api.siliconflow.cn/v1/images/generations`\n- Model: `Qwen/Qwen-Image`\n- 默认参数：\n  - `negative_prompt`: `\"\"`\n  - `num_inference_steps`: `20`\n  - `guidance_scale`: `7.5`\n\n调用外部接口时，必须在 Agent 沙盒执行环境中读取 `SILICONFLOW_API_KEY`。不要依赖后端进程环境变量。\n\n## 操作流程\n\n1. 明确用户要生成的图片内容、风格、尺寸或约束；信息不足但不影响生成时，使用合理默认值，不要反复追问。\n2. 使用可用的执行工具在沙盒中运行脚本，调用图片生成接口，传入用户需求整理后的 `prompt`，并按需传入 `negative_prompt`、`num_inference_steps`、`guidance_scale`。\n3. 从生成接口响应中读取图片地址，默认路径为 `images[0].url`。\n4. 在同一个沙盒脚本中用 `Authorization: Bearer $SILICONFLOW_API_KEY` 下载该图片地址；如果接口直接返回 base64，则直接解码保存。\n5. 将最终图片保存到当前 Workdir 的 `outputs/` 下，例如 `outputs/generated-image.png`。\n6. 调用 `present_artifacts`，传入保存后的 outputs 虚拟路径，让前端展示图片产物。\n7. 最终回复简要说明图片已生成，不要把外部临时 URL 当作最终结果展示。\n\n## 脚本示例\n\n可根据用户需求调整 prompt 和输出文件名：\n\n```python\nimport os\nimport requests\nfrom pathlib import Path\n\napi_key = os.environ[\"SILICONFLOW_API_KEY\"]\nprompt = \"根据用户需求整理后的图片提示词\"\n\nresponse = requests.post(\n    \"https://api.siliconflow.cn/v1/images/generations\",\n    headers={\"Authorization\": f\"Bearer {api_key}\", \"Content-Type\": \"application/json\"},\n    json={\n        \"model\": \"Qwen/Qwen-Image\",\n        \"prompt\": prompt,\n        \"negative_prompt\": \"\",\n        \"num_inference_steps\": 20,\n        \"guidance_scale\": 7.5,\n    },\n    timeout=120,\n)\nresponse.raise_for_status()\nimage_url = response.json()[\"images\"][0][\"url\"]\n\nimage_response = requests.get(\n    image_url,\n    headers={\"Authorization\": f\"Bearer {api_key}\"},\n    timeout=120,\n)\nimage_response.raise_for_status()\n\noutput_path = Path(\"outputs/generated-image.png\")\noutput_path.parent.mkdir(parents=True, exist_ok=True)\noutput_path.write_bytes(image_response.content)\nprint(output_path.as_posix())\n```\n\n## 多模型扩展\n\n如果用户指定其它图片生成模型或兼容接口，可以按该接口的协议先生成图片。只要最终拿到图片 bytes 或 base64，就保存到当前 Workdir 的 `outputs/`，再调用 `present_artifacts` 展示。\n\n## 关键约束\n\n- 不要把外部生成接口返回的临时 URL 当作最终结果直接展示给用户。\n- 不要调用后端 MinIO 上传工具；图片生成和下载都应在沙盒内完成。\n- 如果 `SILICONFLOW_API_KEY` 缺失，应明确提示用户需要在 Agent 沙盒环境变量中配置。\n- 保存到 outputs 后必须调用 `present_artifacts`，否则前端不会自动展示生成图片。\n","frontmatter":{"name":"image-gen","description":"在 Agent 沙盒中生成图片并保存到 outputs。当用户要求生成图片、海报、插画、文生图，或指定 Qwen-Image、其它兼容图片生成接口时使用此技能。","version":"2026.06.02","tool_dependencies":["present_artifacts"]},"isInternal":false,"tokens":1027,"sizeBytes":3474},{"name":"SKILL.md","path":"backend/package/yuxi/agents/skills/buildin/knowledge-base/SKILL.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/backend/package/yuxi/agents/skills/buildin/knowledge-base/SKILL.md","title":"knowledge-base","category":"anthropic-skill","format":"markdown","content":"---\nname: knowledge-base\nslug: knowledge-base\ndescription: \"使用 Yuxi 知识库进行检索、打开文档、文档内定位和查看思维导图。当用户需要基于已配置知识库回答问题、核验资料或引用文档内容时使用此技能。\"\nversion: \"2026.06.24\"\ntool_dependencies: [\"list_kbs\", \"query_kb\", \"find_kb_document\", \"open_kb_document\", \"get_mindmap\", \"search_file\", \"download_kb_file\"]\n---\n\n# 知识库技能\n\n当用户要求基于项目知识库、内部资料、上传入库文档或知识图谱相关内容回答问题时，使用此技能。\n\n## 可用工具\n\n- `list_kbs`：列出当前会话可访问且已启用的知识库。\n- `query_kb`：按 `kb_id` 在指定知识库中检索内容，返回 `file_id` 和相关片段。\n- `open_kb_document`：按 `kb_id` 和 `file_id` 打开文档原文窗口，适合查看更完整上下文。\n- `find_kb_document`：在已知文档内用关键词或正则定位段落。\n- `get_mindmap`：查看知识库思维导图结构。\n- `search_file`：按文件名关键词搜索知识库中的文件，支持指定知识库或跨知识库，返回文件列表与分页信息。\n- `download_kb_file`：按 `kb_id` 和 `file_id` 下载知识库原始二进制（pdf/docx/xlsx 等）到沙盒 `outputs` 目录，返回沙盒内可见的 `virtual_path`。当后续需要用代码读取原始文件结构（如 `openpyxl` 读 xlsx 单元格、`pdfplumber` 重新解析版面）时使用；`query_kb`/`open_kb_document` 只返回文本切片，无法满足需要文件对象的场景。\n\n## 操作流程\n\n1. 需要先确认当前会话有哪些知识库可用；不确定时调用 `list_kbs`。\n2. 针对用户问题选择最相关的知识库，使用 `query_kb` 检索。\n3. 检索结果的 `metadata.chunk_count == 1` 表示文件只有一个分片；除非需要原文行号或额外上下文，否则无需打开。大于 1 且当前片段不足以回答时，使用返回的 `file_id` 调用 `open_kb_document` 查看上下文。字段缺失或为 0 表示未知，不据此判断文档完整。\n4. 如果用户要求定位术语、指标、章节或原文证据，使用 `find_kb_document` 在候选文档内查找。\n5. 当用户关心知识库结构、文件分类或知识框架时，使用 `get_mindmap`。\n\n## 关键约束\n\n- 只能访问当前会话配置和用户权限允许的知识库。\n- 不要编造 `kb_id` 或 `file_id`；优先从 `list_kbs` 和 `query_kb` 的返回结果中获取。\n- 回答需要可追溯时，应说明依据来自哪个知识库、文件或检索片段。\n- Dify 等外部只读知识库可能只支持检索，不一定支持打开全文或文档内查找；遇到工具返回限制说明时，应如实告知用户。\n","frontmatter":{"name":"knowledge-base","slug":"knowledge-base","description":"使用 Yuxi 知识库进行检索、打开文档、文档内定位和查看思维导图。当用户需要基于已配置知识库回答问题、核验资料或引用文档内容时使用此技能。","version":"2026.06.24","tool_dependencies":["list_kbs","query_kb","find_kb_document","open_kb_document","get_mindmap","search_file","download_kb_file"]},"isInternal":false,"tokens":934,"sizeBytes":2732},{"name":"SKILL.md","path":"backend/package/yuxi/agents/skills/buildin/mysql-reporter/SKILL.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/backend/package/yuxi/agents/skills/buildin/mysql-reporter/SKILL.md","title":"mysql reporter","category":"anthropic-skill","format":"markdown","content":"---\nname: mysql reporter\nslug: mysql-reporter\ndescription: \"生成 MySQL 查询报表。当用户需要查询 MySQL 数据库并以报表形式展示结果时使用此技能，包括：统计销售数据、分析用户行为、生成业务报表、查询业务指标等。\"\nversion: \"2026.09.17\"\n---\n\n# MySQL 报表技能\n\n根据用户的指令，通过终端脚本访问 MySQL 数据库，根据查询结果构建报表。\n\n## 操作流程\n\n1. 理解用户的指令，明确报表的需求和目标\n2. 通过 terminal 进入技能目录：`cd /home/gem/skills/mysql-reporter`\n3. 使用 `uv run scripts/list_tables.py` 查看可用表；如果脚本提示缺少 MySQL 配置，按“环境变量缺失处理”回复用户\n4. 必要时用 `uv run scripts/describe_table.py --table 表名` 查看表结构\n5. 生成正确且高效的只读 SQL，通过 `uv run scripts/query.py --sql \"SQL语句\" --timeout 60` 执行查询并获取结果\n6. 使用 Markdown 表格呈现查询结果，说明关键指标、变化与结论\n\n## 环境变量缺失处理\n\n脚本只读取 Agent 沙盒中的环境变量，不读取后端 `.env` 或 Docker Compose 变量。必填变量包括：\n\n- `MYSQL_HOST`\n- `MYSQL_USER`\n- `MYSQL_PASSWORD`\n- `MYSQL_DATABASE`\n\n可选变量包括：\n\n- `MYSQL_PORT`：默认 `3306`\n- `MYSQL_DATABASE_DESCRIPTION`：数据库业务说明，用于辅助理解表和指标含义\n\n如果执行脚本时出现 `MySQL configuration missing required key`，不要继续猜测连接信息或编造报表。应明确告诉用户：需要在个人设置中的「沙盒环境变量」里配置缺失的 `MYSQL_*` 变量；保存后仅对新建沙盒生效，需要重新发起任务或新建会话后再执行。\n\n## 关键约束\n\n- 生成的 SQL 查询必须正确且高效，避免全表扫描\n- MySQL 操作必须通过本技能 `scripts/` 下的 CLI 脚本执行，不要调用平台内置 MySQL tools\n- 不要在报表或错误说明中输出 `MYSQL_PASSWORD` 等敏感环境变量的值，只能说明缺少哪些变量名\n- 只返回报表相关的结论，不要返回原始 SQL 查询语句\n\n## 允许的工具\n\n- terminal：执行 `scripts/list_tables.py`、`scripts/describe_table.py`、`scripts/query.py`\n- 网络检索工具：必要时补充背景信息\n","frontmatter":{"name":"mysql reporter","slug":"mysql-reporter","description":"生成 MySQL 查询报表。当用户需要查询 MySQL 数据库并以报表形式展示结果时使用此技能，包括：统计销售数据、分析用户行为、生成业务报表、查询业务指标等。","version":"2026.09.17"},"isInternal":false,"tokens":737,"sizeBytes":2271},{"name":"agent-backend-development.md","path":"docs/agents/agent-backend-development.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/agent-backend-development.md","title":"Subagent: agent-backend-development","category":"subagent-persona","format":"markdown","content":"# 开发智能体后端\n\n本页面向需要在 Yuxi 中新增或维护 Agent 后端的贡献者。它只讲代码装配；配置字段、权限和运行时上下文分别见[配置智能体](./agents-config.md)和[Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n## 新增预置角色\n\n仅改变提示词、模型或能力选择时，在 `backend/package/yuxi/agents/presets/` 新增一个 Python 文件并导出 `PRESET`；子智能体定义放在其 `subagents/` 子目录。发现逻辑递归读取文件，角色类型仍由 `backend_id` 决定：\n\n```python\nfrom yuxi.agents.presets import AgentPreset\n\nPRESET = AgentPreset(\n    slug=\"report-assistant\",\n    name=\"报告助手\",\n    description=\"根据材料整理报告。\",\n    backend_id=\"ChatbotAgent\",\n    context={\n        \"system_prompt\": \"根据用户材料生成结构清晰、来源可核对的报告。\",\n        \"skills\": [\"html-preview\"],\n    },\n)\n```\n\n子智能体使用 `backend_id=\"SubAgentBackend\"`，其类型由后端推导。`context` 复用[智能体配置](./agents-config.md)字段；长提示词可以直接使用 Python 多行字符串。角色模块不得执行外部操作。\n\nAPI 启动时按文件名发现所有非下划线开头的 Python 模块，校验定义类型、重复 slug 和后端存在性，再统一初始化数据库记录。新增角色无需修改注册清单、repository 或启动调用。模块导入失败、缺少 `PRESET`、重复 slug 或不存在的后端会阻止初始化成功。\n\n预置角色仅在 slug 不存在时创建，已有名称、提示词和能力配置保留；修改源码不会覆盖管理员定制。默认智能助手另行维护原有默认与共享约束。重启 API 后，在智能体管理页核对新增角色的名称、后端和能力配置；子智能体还需在主智能体配置中选用。\n\n## 后端放在哪里\n\n随服务发布的 Agent 后端放在：\n\n```text\nbackend/package/yuxi/agents/buildin/<your_agent>/\n├── __init__.py\n├── context.py\n└── graph.py\n```\n\n执行后端由 `buildin/__init__.py` 中的显式字典注册：\n\n```python\nBUILTIN_BACKENDS = {\n    \"ChatbotAgent\": ChatbotAgent,\n    \"SubAgentBackend\": SubAgentBackend,\n}\n```\n\n新增后端时显式导入其类并添加字典条目。字典键是持久配置与接口使用的稳定后端 ID，Python 类重命名不改变该 ID；仅增加目录或导出类不会启用后端。修改注册表时审查执行图、中间件和运行边界，并验证已有配置仍可解析。\n\n`get_agent_backend(backend_id)` 每次创建一个轻量后端对象，`list_agent_backend_info()` 查询已注册后端的基础信息。后端不持有用户、线程或 Graph 缓存；运行状态由本次 Context、Graph 和 middleware 持有。`get_graph(context=...)` 每次构建独立执行图，知识库增删无需重载后端。未知 ID 抛出 `AgentBackendNotFoundError`；接入用例与后端配置接口将该错误转换为 HTTP 404，worker 执行边界按现有失败流程收敛。\n\n后端的 `get_info()` 返回名称、能力与配置描述，不自行生成后端 ID。新增构图逻辑时验证两次运行分别使用各自的 Context 和资源。\n\n## 最小实现\n\n```python\nfrom langchain.agents import create_agent\nfrom yuxi.agents import BaseAgent, BaseContext, load_chat_model\n\n\nclass MyAgent(BaseAgent):\n    name = \"我的智能体\"\n    description = \"用于示例的智能体后端\"\n    context_schema = BaseContext\n\n    async def get_graph(self, *, context, **kwargs):\n        if not getattr(context, \"_runtime_prepared\", False):\n            raise ValueError(\"构图需要已准备的 Context\")\n        return create_agent(\n            model=load_chat_model(fully_specified_name=context.model),\n            system_prompt=context.system_prompt,\n            checkpointer=await self._get_checkpointer(),\n        )\n```\n\n这个示例展示最小的 Context、模型、提示词和 PostgreSQL checkpoint 装配。真实后端还要根据需要接入文件 backend、工具、Skills、审批、Summary、用量和子智能体 middleware。\n\nworker 和主动压缩在执行入口显式调用 `prepare_agent_runtime_context`，为 Context 追加工作区提示词、按当前用户过滤资源，并在模型为空时补齐系统默认模型。`get_graph(context=...)` 只消费准备后的对象。独立调用同样先创建 `context_schema()`，用 `update_config` 装载持久配置、用 `update` 注入已授权身份和运行覆盖，再 await 准备函数；流和 invoke 接口只接受 `context`，不接收配置字典。\n\n## Context 和配置表单\n\n需要让管理员或用户配置 Agent 行为时，在 `context.py` 扩展 `BaseContext`。字段定义、metadata、权限和保存结构的完整规则由[智能体配置](./agents-config.md#自定义-context-字段)拥有，本页只描述扩展时的代码路径：新增 dataclass 字段后，`get_configurable_items()` 自动把它暴露给 Agent 详情接口和 `AgentRuntimeConfigForm`，保存值进入 `config_json.context`，`get_graph(context)` 消费准备后的 Context。\n\n不要只在前端添加一个字段，也不要把运行期 ID、worker 身份和权限快照暴露成可保存配置。新增字段后，沿 context_schema → get_configurable_items() → 详情接口 → 配置表单 → `config_json.context` → `get_graph(context)` 的链路检查每一环的测试和文档。\n\n## 中间件和工具\n\n资源权限和默认资源选择在 Graph 创建前处理；模型提示注入、工具动态开放、文件结果处理、state 更新和观测才适合放入 middleware。内置 Agent 的工具可见性和执行注册分为两层：工具可以先进入 ToolNode，再由 Skill 激活状态决定是否让模型看到。\n\n优先复用：\n\n- [工具系统](./tools-system.md) 的注册和目标校验；\n- [中间件](./middleware.md) 的装配顺序；\n- [Skills 管理](./skills-management.md) 的依赖和激活规则；\n- [沙盒机制](../mechanisms/sandbox.md) 的文件和命令边界。\n\n新 middleware 不要绕过 `prepare_agent_runtime_context`，也不要用 Prompt、前端隐藏或 schema omission 代替后端授权。\n\n## 检查清单\n\n- `BaseAgent` 子类在 `BUILTIN_BACKENDS` 中通过稳定 ID 显式注册；\n- `context_schema` 的默认值、字段权限和选项能被前端正确渲染；\n- Graph 使用 Yuxi 的模型、工具、文件和 checkpoint 装配入口；\n- 工具副作用在执行处验证用户、路径和资源；\n- 新的模型可见输入、状态、文件或协议有正向和负向测试；\n- 相关 API、机制和用户文档已更新。\n\n## 源码和测试\n\n- [BaseAgent](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/base.py)\n- [Context](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/context.py)\n- [Chatbot graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)\n- [执行后端显式注册](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/__init__.py)\n- [Agent unit tests](https://github.com/xerrors/Yuxi/tree/main/backend/test/unit/agents)\n- [Agent integration/E2E](https://github.com/xerrors/Yuxi/tree/main/backend/test/e2e)\n\n改变持久配置、权限、模型可见输入、Run 生命周期或文件边界时，先按 [Yuxi Spec Loop](../develop-guides/spec-loop.md) 建立相应的决策和验证范围。\n","isInternal":false,"tokens":2182,"sizeBytes":7425},{"name":"agent-evaluation.md","path":"docs/agents/agent-evaluation.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/agent-evaluation.md","title":"Subagent: agent-evaluation","category":"subagent-persona","format":"markdown","content":"# 评估智能体\n\n智能体评估用 Langfuse Dataset 保存一组固定任务，再让 Yuxi 按真实的 AgentRun、worker 和工具链路逐条执行。它适合比较一个智能体在研究、编程、文件处理或多步骤任务上的表现。\n\n本页不介绍知识库的 `recall@K` 和答案指标；那部分见[知识库评估](../intro/evaluation.md)。\n\n## 需要准备什么\n\n1. Yuxi 已配置 Langfuse tracing，API/worker 环境包含：\n\n   ```bash\n   LANGFUSE_PUBLIC_KEY=<your-public-key>\n   LANGFUSE_SECRET_KEY=<your-secret-key>\n   LANGFUSE_BASE_URL=https://cloud.langfuse.com\n   ```\n\n2. 本机运行 CLI 时也能读取同一 Langfuse 项目的变量。CLI 会直接读取 Dataset 并创建 experiment。\n3. 已安装 `yuxi-cli`，并登录目标实例：\n\n   ```bash\n   yuxi remote add local http://localhost:5173\n   yuxi login --browser\n   ```\n\n   CI 或没有浏览器时，可以使用 `yuxi login --api-key \"$YUXI_API_KEY\"`。\n4. 目标智能体已经存在，登录用户有权访问它。命令使用 Agent slug，例如 `default-chatbot`。\n\n## 准备 Dataset\n\n在 Langfuse 中创建 Dataset，并为每个 item 的 `input` 提供任务文本：\n\n```json\n{\"input\":\"请整理这份资料，并列出三个需要核实的事实。\"}\n```\n\n也兼容把任务放在 `query`、`question` 或 `prompt` 字段中。`expected_output` 可以保存参考答案，具体评分规则由 Langfuse evaluator 或人工评审负责。\n\nYuxi 不负责创建或上传 Dataset。先在 Langfuse 中检查任务文本、参考输出和数据集版本，再运行实验。\n\n## 运行实验\n\n```bash\nyuxi agent eval \\\n  --dataset-name demo-dataset \\\n  --agent-slug default-chatbot \\\n  --experiment-name default-chatbot-demo \\\n  --max-concurrency 1 \\\n  --timeout-seconds 900\n```\n\nCLI 对每条 item：\n\n1. 从 Dataset 读取任务文本；\n2. 调用 Yuxi 的 `POST /api/agent-invocation/eval/runs`；\n3. 由 Yuxi 创建临时 Conversation 和 AgentRun；\n4. 通过 worker 执行真实智能体；\n5. 等待 Run 进入终态；\n6. 把最终输出写回 Langfuse experiment item。\n\n`--max-concurrency` 是 Dataset 实验的并发数。先从 `1` 开始，再根据模型服务、worker 和沙盒容量提高。`--timeout-seconds` 是每条样例等待 Yuxi 结果的上限；超时会报告当前运行状态，不应把它当作成功。\n\n## 查看结果\n\n实验完成后，在 Langfuse Dataset 的 experiment 中查看每条 item 的最终输出。Yuxi 会在本地运行上下文和 trace 中保存以下标记，便于筛选：\n\n```text\nsource=agent_evaluation\nevaluation_dataset_name=<dataset-name>\nevaluation_dataset_item_id=<item-id>\nevaluation_experiment_name=<experiment-name>\n```\n\n先比较同一数据集的输出，再按自己的评估规则打分。一次实验的输出只代表当时的模型、Agent 配置、工具、知识库和外部服务状态；改变这些条件后，应创建新的 experiment 名称。\n\n## 排查失败\n\n- 没有 experiment：检查 CLI 的 Langfuse 公钥、密钥、地址和 Dataset 名称。\n- experiment 有 item 但 Yuxi 失败：检查 CLI 登录的 API Key、Agent slug，并用 `docker compose logs api worker` 查看当前槽位日志。\n- Trace 缺失：检查 API/worker 是否读取到 Langfuse 配置；Yuxi 业务结果仍以 PostgreSQL 的 Run 和消息为准。\n- 大量超时：降低 `--max-concurrency`，检查模型响应时间、worker 健康状态和沙盒创建耗时。\n- 实验部分成功：不要只看命令退出前的汇总，回到 Langfuse 检查每条 item 是否都有结果；CLI 会在成功写入数量与 Dataset 总数不一致时报告错误。\n\n实现入口见 [Agent Eval 路由](https://github.com/xerrors/Yuxi/blob/main/backend/server/routers/agent_invocation_eval_router.py)、[CLI 实验](https://github.com/xerrors/Yuxi/blob/main/packages/yuxi-cli/src/yuxi_cli/agent_eval.py) 和 [Langfuse 集成](../advanced/langfuse-integration.md)。\n","isInternal":false,"tokens":1227,"sizeBytes":3934},{"name":"agents-config.md","path":"docs/agents/agents-config.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/agents-config.md","title":"Subagent: agents-config","category":"subagent-persona","format":"markdown","content":"# 配置智能体\n\n本页是智能体配置参考，说明页面上的字段如何进入一次运行。新增智能体后端的代码结构见[开发智能体后端](./agent-backend-development.md)；只使用现成智能体时，从[快速开始](../intro/quick-start.md)开始。\n\n## 配置模型\n\n| 对象 | 负责什么 |\n| --- | --- |\n| `Agent` | 数据库中的智能体资源，保存名称、slug、共享范围和持久化配置 |\n| `BaseAgent` | 代码中的后端类型，声明 `context_schema` 和 `get_graph()` |\n| `BaseContext` | 配置字段和运行时输入的 Schema |\n| `config_json.context` | 当前 Agent 保存的配置值 |\n| Graph / middleware | 根据 Context 组合模型、工具、文件和扩展能力 |\n\n内置 `ChatbotAgent` 用于普通对话，`SubAgentBackend` 用于被主智能体委派的任务。子智能体的配置入口与普通智能体相同。\n\n## 配置页面从哪里来\n\n`BaseContext` 的 dataclass 字段和 metadata 会生成配置项描述，前端不再维护一份独立字段清单：\n\n```text\nContext 字段\n  → get_configurable_items()\n  → Agent 详情接口\n  → AgentRuntimeConfigForm\n  → config_json.context\n```\n\nmetadata 可以定义展示名称、说明、控件类型、选项和角色权限。运行期 ID、owner 和内部派生值应隐藏，不作为用户配置。\n\n`metadata.auth` 只限制修改权限：`admin` 字段允许管理员和超级管理员修改，`superadmin` 字段只允许超级管理员修改。普通用户读取有权访问的智能体时，可以读到这些已保存值，运行也使用这些值；不可修改的字段不进入其编辑表单，后端会过滤越权提交的字段并保留已保存值。`auth` 不提供字段保密能力，Context 配置不得存放凭据。\n\n例如管理员将 `max_execution_steps` 设置为 50，普通用户运行该智能体时的 `recursion_limit` 也为 50。已经固化运行快照的 Run 保留自己的配置；后续新 Run 读取保存的配置。知识库、Skills 等资源仍按运行用户的访问权限筛选。\n\n## 基础字段\n\n| 字段 | 作用 |\n| --- | --- |\n| `system_prompt` | 智能体角色和行为说明 |\n| `model` | 主模型；留空时使用系统默认模型 |\n| `tool_approval_mode` | `default` 或 `always_trust`；仅管理员可配置 |\n| `tools` | 可使用的内置工具 |\n| `knowledges` | 可检索的知识库范围 |\n| `mcps` | 直接添加到 Agent 的已启用 MCP 服务器 |\n| `skills` | 选择共享和内置 Skill；个人 Skill 始终可用 |\n| `preload_skills` | 从首轮请求加载完整说明和依赖的 Skill 子集 |\n| `summary_threshold`、`summary_keep_messages` | 上下文压缩的唯一压力阈值和摘要后保留消息数 |\n| `summary_prompt`、`summary_tool_result_token_limit` | 摘要提示词和工具结果预览上限 |\n| `max_execution_steps`、`model_retry_times` | 单次运行步数和模型重试次数 |\n\n## 资源选择语义\n\n资源字段 `tools`、`knowledges`、`skills`、`subagents`、`mcps` 和 `preload_skills` 接受 `\"all\"` 或字符串数组，本页是这组契约的唯一 Owner。`\"all\"` 表示每次运行使用当前用户可访问的全部资源，包括之后新增的资源；`[]` 表示不选择；非空数组表示固定范围。新写入拒绝 `null`、未知字符串和非字符串数组成员。\n\n创建时省略字段使用其默认值：工具、知识库和 Skill 为 `\"all\"`，子智能体为 `[\"general-purpose\"]`（通用任务），MCP 和预加载 Skill 为 `[]`。界面的「全部（含新增）」保存 `\"all\"`；逐项勾选保存固定数组，即使勾选了当前所有选项也不会自动转换成全部模式。「清空全部」保存 `[]`，子智能体同样可以全部禁用。\n\n各资源的附加规则：\n\n- MCP 选择只控制直接加载的服务器；已激活 Skill 声明的 MCP 依赖仍按需加载。\n- Skill 选择器只列共享和内置 Skill；运行时自动合并当前用户全部个人 Skill，`skills=[]` 只关闭共享选择。Skill 的激活时机见 [Skills 管理](./skills-management.md)。\n- 预加载候选项限于已选且可访问的共享 Skill；`preload_skills=\"all\"` 在每次运行预加载该范围及其授权依赖的完整说明，并从首轮开放依赖工具。`skills=[]` 时预加载全部仍得到空列表，个人 Skill 继续按需读取正文。\n- 子智能体不能继续调用下一层子智能体。\n\n这些字段只会缩小当前用户已经拥有的权限；运行时如何消费解析结果见 [Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n### 共享智能体与编辑保存\n\n共享智能体保存完整的期望选择，每次运行再与当前操作者的可访问资源取交集，运行不会改写保存的选择。例如创建者选择 10 个 Skill，委托管理员只能访问其中 5 个，委托管理员运行时生效 5 个，保存名称或模型后仍保留原有 10 个选择。\n\n编辑页只提交修改过的配置字段；通过 API 更新 `config_json.context` 时，省略字段保留原值。修改非空资源列表时，后端保留当前管理员不可访问的既有引用，并拒绝新增无权访问的引用；保留的引用维持原相对顺序，新选择追加到末尾。「清空全部」移除所有引用（包括不可访问项），切换到「全部（含新增）」则整体替换固定选择范围。界面会显示当前不在可选范围内的既有选择数量。\n\n## 定时运行\n\n智能体支持用户定时任务（Beta）：为智能体配置 5 段 cron 和 IANA 时区后，worker 到期自动在独立 Conversation 中触发一次运行，也支持手动立即运行；停机错过的多个周期合并为一次，已有未完成执行时跳过并记录。任务的定义、触发与恢复边界见 [Agent 运行时上下文](../mechanisms/agent-runtime.md#用户定时-agent)。\n\n## 自定义 Context 字段\n\n需要让用户配置额外行为时，扩展 Context，让后端和前端沿同一 Schema 工作：\n\n```python\nfrom dataclasses import dataclass, field\nfrom yuxi.agents import BaseContext\n\n\n@dataclass(kw_only=True)\nclass MyAgentContext(BaseContext):\n    response_style: str = field(\n        default=\"concise\",\n        metadata={\n            \"name\": \"回答风格\",\n            \"description\": \"控制回答的详细程度\",\n            \"type\": \"string\",\n            \"options\": [\"concise\", \"detailed\"],\n        },\n    )\n```\n\n新增字段会影响保存结构、配置表单和运行期 Context。字段默认值、权限和选项变化时，同时更新相关测试和文档。\n\n## `capabilities`\n\n`capabilities` 是代码声明的静态能力，用于控制固定的前端入口，例如：\n\n```python\nclass MyAgent(BaseAgent):\n    capabilities = [\"file_upload\", \"files\"]\n```\n\n它不保存待办、文件、产物或子智能体状态。运行态来自 LangGraph state 的 `agent_state`；能力声明只表达后端固定支持哪些 UI 入口。\n\n## 运行时入口\n\n配置如何与用户身份、权限快照、Workdir、Memory 和 PostgreSQL checkpoint 组合，见[Agent 运行时上下文](../mechanisms/agent-runtime.md)。工具可见性和执行授权见[工具系统](./tools-system.md)，上下文压缩见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## 相关页面\n\n- [开发智能体后端](./agent-backend-development.md)\n- [中间件](./middleware.md)\n- [Skills 管理](./skills-management.md)\n- [子智能体](./subagents-management.md)\n- [沙盒机制详解](../mechanisms/sandbox.md)\n","isInternal":false,"tokens":2375,"sizeBytes":7527},{"name":"mcp-integration.md","path":"docs/agents/mcp-integration.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/mcp-integration.md","title":"Subagent: mcp-integration","category":"subagent-persona","format":"markdown","content":"# 集成 MCP\n\nMCP（Model Context Protocol）让智能体调用外部服务提供的工具。管理员在“扩展 → MCP”中添加远程服务器；Agent 可直接选择服务器，Skill 也可声明激活后加载的依赖。\n\n## 支持的传输方式\n\n| 传输方式 | 适用场景 |\n| --- | --- |\n| `streamable_http` | 新的远程 MCP 服务 |\n| `sse` | 仍提供 SSE 接口的远程服务 |\n\n管理接口只接受 `streamable_http` 和 `sse`。Yuxi 不支持 `stdio`，包括内置 MCP 和直接传入的运行时配置；历史 `stdio` 配置会被禁用，应迁移为远程服务。\n\n## 添加远程 MCP\n\n在“扩展 → MCP”点击“添加 MCP”，填写稳定标识、名称、传输方式和 URL。例如：\n\n```json\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\"\n}\n```\n\n管理接口对应：\n\n```http\nPOST /api/system/mcp-servers\nAuthorization: Bearer <admin-token>\nContent-Type: application/json\n\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\",\n  \"description\": \"提供示例查询工具\"\n}\n```\n\n需要认证的远程服务可以配置 HTTP headers、连接超时和 SSE 读取超时。凭证会随着连接请求发送，请只配置必要的 header，并把管理接口限制在可信的管理员范围。\n\n添加后先点击“测试连接”，确认能发现工具，再把服务器状态设为“已添加”。状态关闭时，服务器记录仍保留，但不会进入运行时。\n\n## 让智能体使用 MCP\n\n在智能体配置的 MCP 字段中显式选择要直接添加的服务器。该字段默认不添加服务器：\n\n- 未显式配置或显式清空时，不直接加载 MCP 服务器；\n- 显式选择后，只使用所选且当前可用的服务器；\n- MCP 工具仍会在执行处使用当前用户身份和服务器配置；\n- 管理员可以在 MCP 详情页单独禁用某个工具。\n\n共享 Skill 的 `mcp_dependencies` 声明所需服务器。Skill 激活后按需加载这些服务器的工具；预加载 Skill 从首轮加载，均无需在 Agent 的 MCP 字段重复选择。服务器必须由管理员启用，禁用的服务器不会提供工具。\n\nMCP 配置从 PostgreSQL 读取，工具对象按配置哈希缓存。修改连接配置或工具禁用列表后，下一次运行会使用新的配置键。\n\n## 内置远程 MCP\n\n内置 DeepWiki 使用 `deepwiki-official` 标识，通过 `https://mcp.deepwiki.com/mcp` 提供 Streamable HTTP 服务，无需认证即可查询公开 GitHub 仓库。详见 [DeepWiki 官方文档](https://docs.devin.ai/work-with-devin/deepwiki-mcp)。\n\n开发者在 [`builtin.py`](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/mcp/builtin.py) 的 `BUILTIN_MCP_SERVERS` 中添加固定远程定义：\n\n```python\nBUILTIN_MCP_SERVERS = {\n    \"deepwiki-official\": {\n        \"transport\": \"streamable_http\",\n        \"url\": \"https://mcp.deepwiki.com/mcp\",\n        \"description\": \"查询公开 GitHub 仓库的文档、架构与代码\",\n        \"icon\": \"📚\",\n        \"tags\": [\"内置\", \"代码\", \"文档\"],\n    },\n}\n```\n\nAPI/worker 启动时同步固定定义到数据库；运行时直接读取代码中的连接字段。新内置 MCP 默认未添加，管理员需要启用；连接配置不可通过页面修改。添加定义无需修改服务逻辑或启动入口。\n\n原内置 `mcp-server-chart` 已退役，启动时删除系统创建的记录。其他历史 stdio 记录保留并禁用，管理员可迁移为远程服务或删除。MySQL 报表技能改用 Markdown 表格，不再依赖图表 MCP；自定义角色或 Skill 中对退役 MCP 的引用需要自行调整。\n\n## 常用管理接口\n\n| 方法 | 路径 | 作用 |\n| --- | --- | --- |\n| `GET` | `/api/system/mcp-servers` | 查看服务器；普通用户只得到脱敏基础信息 |\n| `POST` / `PUT` | `/api/system/mcp-servers`、`/{slug}` | 添加或修改远程 MCP |\n| `PUT` | `/api/system/mcp-servers/{slug}/status` | 启用或停用服务器（对应页面上的添加/移除状态） |\n| `POST` | `/api/system/mcp-servers/{slug}/test` | 测试连接并发现工具 |\n| `GET` | `/api/system/mcp-servers/{slug}/tools` | 查看工具 |\n| `PUT` | `/api/system/mcp-servers/{slug}/tools/{tool_name}/toggle` | 启用或禁用单个工具 |\n\n接口字段和错误响应以实例 Swagger 为准。\n","isInternal":false,"tokens":1372,"sizeBytes":4429},{"name":"middleware.md","path":"docs/agents/middleware.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/middleware.md","title":"Subagent: middleware","category":"subagent-persona","format":"markdown","content":"# 中间件\n\n中间件把文件、Skills、子智能体、上下文压缩、审批和用量统计接到 LangGraph Agent。它们在模型调用、工具调用或 state 更新的边界运行，让不同 Agent 复用同一套能力。\n\n内置 `ChatbotAgent` 和 `SubAgentBackend` 都在 `get_graph()` 中组装中间件。Graph 创建前，系统先完成用户资源和权限的归一化；中间件不应绕过这一步重新决定授权。\n\n## Graph 创建前的准备\n\n`prepare_agent_runtime_context` 会根据当前用户和 Agent 配置：\n\n- 过滤内置工具、知识库、MCP、Skills 和子智能体；\n- 生成 `_visible_knowledge_bases`；\n- 展开 Skill 依赖，生成 `_effective_skill_slugs` 和 `_runtime_skills`；\n- 使用系统默认模型补齐空的模型配置。\n\n随后，工具解析器准备可执行工具，`build_prompt_with_context` 生成系统提示词，Agent 再创建 Graph。工具执行时仍需检查具体目标，准备阶段的资源快照不是授权替代品。\n\n## 内置中间件顺序\n\n`ChatbotAgent` 的常见顺序如下（[graph.py](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py) 装配）；可选项只在对应能力启用时加入：\n\n| 顺序 | 中间件 | 作用 |\n| --- | --- | --- |\n| 1 | `ToolErrorGuardMiddleware` | 最外层隔离普通工具异常，保留取消与 interrupt 的传播 |\n| 2 | `SteerMiddleware` | 在安全边界发现待接替请求 |\n| 3 | `create_agent_filesystem_middleware` | 提供 Workdir、User Data、Skills 文件后端，并卸载过大的工具结果 |\n| 4 | `SkillsMiddleware` | 注入 Skill 说明，按激活状态开放依赖 |\n| 5 | `YuxiMemoryMiddleware` | Memory 开关开启且 `MEMORY.md` 有内容时，注入用户记忆并提供受限工具 |\n| 6 | `YuxiSubAgentMiddleware` | 主智能体有可见子智能体时提供子智能体生命周期工具 |\n| 7 | `YuxiSummarizationMiddleware` | 先确定性压缩工具结果，仍达到同一阈值时生成摘要 |\n| 8 | `TodoListMiddleware` | 保存待办，供状态面板展示 |\n| 9 | `PatchToolCallsMiddleware` | 修正部分工具调用消息形态 |\n| 10 | `NetworkRetryMiddleware` | 网络错误按预算、其他可重试模型错误按次数重试，耗尽后抛出异常 |\n| 11 | `ImageInputCompatibilityMiddleware` | 桥接工具读取图片与模型输入格式；必要时回退 OCR |\n| 12 | `TokenUsageMiddleware` | 记录近似上下文和主模型实际用量 |\n| 13 | 工具审批 middleware | 默认模式下审批 Project 外的文件写入和命令执行；当前 Project 内的写/编辑自动放行 |\n\n`SubAgentBackend` 复用文件、Skills、Summary、待办、重试和用量等能力，同样以 `ToolErrorGuardMiddleware` 为最外层，但不挂载子智能体 middleware，并过滤不适合子智能体的敏感或交互工具。\n\n模型重试耗尽后，异常进入 Run 失败通道，持久化 `failed` 状态与错误原因；已有部分输出保留错误元数据。\n\n子 Run 的失败通过 `subagent_await` / `subagent_status` 返回给父智能体，由父智能体决定后续处理。最终正常回答仍须满足同 Run 的 model lifecycle 审计关联。\n\n## Skills 和知识库\n\nSkills middleware 将 Skill 说明按模型请求注入：预加载 Skill 从首轮开放工具和 MCP 依赖，普通 Skill 在模型读取对应 `SKILL.md` 后激活，再开放声明的依赖。Agent 显式选择的 MCP 服务器从运行开始提供工具。\n\n知识库能力由内置 `knowledge-base` Skill 提供。它的工具是否注册、模型是否可见、参数是否能访问目标知识库分别由工具组装、Skill 激活和知识库权限检查负责。完整链路见[工具系统](./tools-system.md)和[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n## 文件和附件\n\n附件确认后写入当前 Project Workdir。每次 Run 会把线程历史附件的文件名和实时路径加入本轮用户消息，让模型按需调用 `read_file`；持久化 Message 仍保存原始文本，不会把这段模型专用路径混进用户可见消息。\n\n普通 Agent 和子 Agent 使用根 Conversation 的同一个 `runtime_scope_id` 和 Workdir。子 Agent 的 child thread 只隔离 LangGraph checkpoint，不隔离共享文件。Viewer、附件和 artifact API 直接访问 UserWorkspace 的持久文件，不需要创建 file-bridge Sandbox。\n\n## 子智能体\n\n主智能体配置了可见子智能体时，middleware 提供 `subagent_start`、`subagent_status`、`subagent_cancel` 和 `subagent_await`。`subagent_start` 立即返回子 Run 身份，`subagent_await` 按需等待；查询和取消均按 `run_id` 执行。子智能体使用自己的 Context 和 checkpoint，但继承发起用户的权限、Workdir 和 execution runtime。\n\n详细的调用、busy、结果和文件边界见[子智能体](./subagents-management.md)。\n\n## Summary 上下文压缩\n\nSummary 在文件和 Skills 等中间件之后运行。请求达到唯一压力阈值后，它先为当前模型请求精简工具结果并重新计量；仍达到同一阈值时才写入历史文件、生成摘要并更新 checkpoint。它不会删除 PostgreSQL 聊天消息，也不会把内部摘要模型调用当作用户可见回复。支持主动压缩的 Agent 复用同一摘要器，并通过 service 在空闲线程的 canonical graph state 上更新 checkpoint。\n\n参数和状态细节见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## Token 用量\n\n`TokenUsageMiddleware` 同时记录近似上下文 token（用于摘要阈值和状态面板）和主 Agent 模型返回的 `usage_metadata`（按最近调用、当前 Run、线程和模型分桶保存）。\n\n`siliconflow-cn` 和 `siliconflow` 当前位于 Provider 用量黑名单：这两个供应商仍可提供近似上下文统计，但不会写入最近调用、Run 或线程的 Provider 实际用量聚合。其他能返回兼容 `usage_metadata` 的供应商才会进入这组实际用量统计；Summary 内部摘要模型调用不计入。\n\nRun 终态时，worker 把与当前 `run_id` 匹配的 state 快照写入 `AgentRun.token_usage`；父 Run 和子 Run 分开保存。\n\n## 新增中间件时\n\n先说明它要改变哪一条边界：Prompt、模型调用、工具调用、文件访问、state 或观测。资源筛选和权限收敛放在 Graph 创建前；文件读写和工具结果卸载优先复用现有 filesystem middleware；新增模型可见输入或副作用时补充对应测试和失败案例。\n\n实现入口：[ChatbotAgent graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)、[中间件目录](https://github.com/xerrors/Yuxi/tree/main/backend/package/yuxi/agents/middlewares)。\n","isInternal":false,"tokens":2150,"sizeBytes":6768},{"name":"skills-management.md","path":"docs/agents/skills-management.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/skills-management.md","title":"Subagent: skills-management","category":"subagent-persona","format":"markdown","content":"# 管理 Skills\n\nSkill 是一个可复用的能力包，通常包含一个 `SKILL.md`、提示词、参考资料和可选脚本。智能体先看到 Skill 的描述，再按需要读取 `SKILL.md`；本地工具和 MCP 依赖随 Skill 激活进入模型请求。\n\n## 什么时候用 Skill\n\n把一类稳定、可复用的工作方式放进 Skill，例如文档处理、研究流程、报表生成或某个外部服务的操作规范。单次任务要求写在对话中；需要跨对话复用的规则和资料再考虑 Skill 或用户工作区文件。\n\n## Skill 保存在哪里\n\n| 来源 | 位置和事实 | 谁可以安装/管理 |\n| --- | --- | --- |\n| 内置 | 随代码发布，启动时同步索引 | 管理员可以启用/停用，不能删除或直接编辑 |\n| 共享上传/远程 | Skill 持久目录 + PostgreSQL 索引 | 管理员按共享范围管理 |\n| 个人 | 当前用户 UserWorkspace 的 `agents/skills/<slug>` | 当前用户；不创建共享 Skill 数据库记录 |\n\n运行时路径是：\n\n- 共享和内置 Skill：`/home/gem/skills/<slug>/`，对 Agent 只读；\n- 个人 Skill：`/home/gem/user-data/agents/skills/<slug>/`，位于当前用户的 UserWorkspace。\n\n同一用户有个人 Skill 和共享 Skill 使用同一个 slug 时，个人版本覆盖该用户看到的共享版本。删除个人版本后，如果共享版本仍可访问，会恢复共享版本。\n\n## 创建 Skill\n\n标准目录至少包含根级 `SKILL.md`：\n\n```text\nmy-skill/\n├── SKILL.md\n├── tools/       # 可选脚本\n└── prompts/     # 可选提示词或参考资料\n```\n\n`SKILL.md` 使用 YAML frontmatter：\n\n```markdown\n---\nname: 文档整理\nslug: document-cleanup\ndescription: 按指定结构整理文档，并保留可核对的来源。\ntool_dependencies: []\nmcp_dependencies: []\nskill_dependencies: []\n---\n\n# 文档整理\n\n## 何时使用\n\n说明这个 Skill 解决什么问题，以及什么时候不该使用。\n\n## 操作步骤\n\n写出 Agent 应遵循的步骤、限制和验收方式。\n```\n\n必填字段是 `name` 和 `description`。名称和 `slug` 最多 128 个字符。`slug` 可省略，省略时直接使用 `name`，因此省略 slug 时 `name` 本身也必须是小写字母、数字和单个短横线组成的值，中文或带空格的展示名称会校验失败。\n\n建议显式填写 slug，把自然语言名称和稳定标识分开。\n\n依赖字段含义：\n\n| 字段 | 作用 |\n| --- | --- |\n| `tool_dependencies` | 需要的内置工具 |\n| `mcp_dependencies` | 需要的 MCP 服务器 |\n| `skill_dependencies` | 需要先提供说明的其他 Skill |\n\n个人 Skill 只作为用户文件读取，不解析这些平台依赖；需要依赖工具、MCP 或其他 Skill 时，安装为共享 Skill。\n\n## 安装方式\n\n进入“扩展 → Skills”，可以选择：\n\n1. **推荐 Skill**：从推荐列表生成安装草稿。\n2. **上传**：上传 ZIP 或单个 `SKILL.md`，解析后生成草稿。\n3. **远程安装**：从 GitHub 仓库、ModelScope Skill 或合集拉取一个或多个 Skill。\n4. **在线编辑**：编辑已有且有管理权限的共享 Skill 文件和依赖。\n5. **Agent 内安装**：主智能体使用 `install_skill` 把 Skill 安装到当前用户的个人来源；子智能体不能使用该工具。\n\n上传和远程安装都先解析为草稿，再选择个人或共享位置并确认。草稿中的可安装条目只包含已解析的临时包；远程拉取或解析失败会单独显示，不能被确认安装。确认前可以检查名称、说明、文件和依赖；取消草稿不会写入正式 Skill。\n\n有管理权限的用户可在共享 Skill 详情页编辑文本文件。保存 `SKILL.md` 时，根文件的名称、描述及依赖声明会同步到数据库索引；“配置”中的依赖选择也会回写根文件。每次保存都使用打开文件时取得的修订值；如果其他人已修改该文件，页面会拒绝覆盖并保留当前草稿，用户需核对最新内容后重试。切换文件或离开页面时，未保存的修改会触发确认。共享范围和启停状态仍在配置页分别保存。新的共享内容在后续 Agent Run 准备时生效；已准备 Run 的预加载内容保留原有快照，按需读取的用户投影可随之后的编辑刷新。\n\n### 远程来源限制\n\n管理员在“设置 → 基本设置 → Skill 配置 → 远程来源白名单”中维护 `remote_skill_source_policy.allowed_hosts`。默认允许 `github.com` 和 `modelscope.cn`；主机名必须精确匹配，保存空列表会关闭远程安装。\n\n示例来源：\n\n```text\nanthropics/skills\nhttps://github.com/anthropics/skills\nhttps://modelscope.cn/skills/@anthropics/pdf\nhttps://modelscope.cn/collections/MiniMax/MiniMax-Office-skills\n```\n\n除了按仓库或合集拉取，也可以在远程安装对话框切换到“全局搜索发现”，输入关键词检索 `skills.sh` 上的开源 Skills，再选择结果批量拉取和安装。搜索在一次性 Sandbox 中执行；选中结果后的实际拉取仍经过来源白名单和安装确认流程。\n\nGitHub 的 `owner/repo` 简写会被转换为 HTTPS 地址。远程来源会在不继承全局或用户环境变量的一次性 Sandbox 中下载和提取，系统会拒绝绝对路径和路径穿越，并限制文件数、目录深度和总大小。来源白名单限制产品允许的地址，不是网络出口防火墙。\n\n### 新增内置 Skill\n\n在 `backend/package/yuxi/agents/skills/buildin/<slug>/` 新增目录，至少包含 `SKILL.md`。启动同步按目录名排序发现直接子目录，忽略下划线或点开头的目录；无需修改 Python 注册清单。\n\n`SKILL.md` frontmatter 唯一拥有名称、描述、版本和依赖。`slug` 必须与目录名一致，省略时使用 `name`；`version` 省略时为 `1.0.0`，建议使用引号包裹版本字符串。工具、MCP、Skill 依赖使用本页定义的字段。缺少根文件或元数据不合法时，启动同步明确失败。\n\nAPI/worker 启动时同步文件、元数据和依赖，保留数据库中的启停状态。重启后在“扩展 → Skills”核对新增项的说明和依赖；新增脚本或资源也必须随发行包携带。源码目录与共享投影分别拥有发布内容和安装文件，编辑应落在源码目录。\n\n安装前仍应审查 Skill 的提示词、脚本、依赖和网络行为。不要把数据库密码、云平台密钥或 `SANDBOX_PROVISIONER_TOKEN` 放进 Skill 或 Agent 环境。\n\n## 依赖和加载时机\n\nAgent 的 `skills` 配置语义（`\"all\"`、固定数组与默认值）由[资源选择契约](./agents-config.md)拥有，本页描述激活时机。每个 Run 准备 Context 时，系统按权限解析共享选择，合并当前用户全部个人 Skill，再展开 `skill_dependencies`；主智能体和子智能体使用同一规则。`skills=[]` 时个人 Skill 仍进入模型可见的描述列表，只是没有共享选择。个人 Skill 安装不修改 Agent 配置，新增 Skill 在下一次 Run 准备时自动加入，已准备的运行保留快照。\n\n### 普通渐进加载\n\n1. 创建 Graph 前，模型得到有效 Skill 的名称、描述和 `SKILL.md` 路径。\n2. 模型读取某个可见 Skill 的 `SKILL.md` 后，该 Skill 进入 `activated_skills`。\n3. 后续模型请求加入它声明的本地工具，并加载已启用的 MCP 依赖服务器提供的工具。\n\n模型没有读取的 Skill 的依赖工具继续隐藏。未激活的 Skill 本地工具即使已注册到 ToolNode，也不能被模型调用。\n\n### 预加载\n\nAgent 配置可以用 `preload_skills` 指定少量需要从首轮就可用的 Skill。预加载选择器只列 `skills` 中当前用户可访问的共享和内置 Skill；系统会展开其依赖闭包，读取根级 `SKILL.md`，并从首轮模型请求开放本地工具和已启用的 MCP 依赖。\n\n预加载的根文件缺失或不可读时，Graph 创建会明确失败，不会静默退回渐进加载。默认值为空列表；选择“全部（含新增）”时，每次运行预加载当前 Agent 已选且可访问的共享 Skill 及其授权依赖。同名个人版本覆盖共享版本时读取个人内容；其他个人 Skill 始终可发现，正文按需读取。固定列表和全部模式均遵循[资源选择契约](./agents-config.md)。\n\n## 权限和选择\n\n共享 Skill 使用 `source_type`、`share_config` 和 `enabled` 表达来源、范围和启用状态。范围使用 version 2 的 `read_scope`、`manage_scope`，可以是全局、部门或指定用户；管理范围必须包含在读取范围内。\n\n| 用户 | 可见和使用 | 可管理 |\n| --- | --- | --- |\n| `superadmin` | 全部允许的共享/内置 Skill | 全部非内置 Skill，及内置 Skill 的启停 |\n| `admin` | 命中读取范围且已启用的 Skill | 命中管理范围的非内置 Skill，及内置 Skill 的启停 |\n| 普通用户 | 命中读取范围且已启用的 Skill | 自己拥有的非内置 Skill；新安装的 Skill 固定进入个人来源 |\n\n普通用户安装的新 Skill 固定进入个人来源，不配置共享范围。管理员安装到共享来源时才会写入 PostgreSQL 索引，并可以配置部门或用户范围。扩展管理页展示当前用户可访问的共享和个人 Skill；Agent 配置选项只展示可访问的共享和内置 Skill。后端在保存时校验共享引用，运行时按当前用户身份加载个人目录。\n\nAgent 配置中的 Skill 选择不能扩大用户的文件、知识库或 MCP 权限。\n\n## 内置 Skill 说明\n\n系统启动时会同步仓库内置 Skills，其中两个内置 Skill 有产品级行为约定。`html-preview` 用于在普通 Markdown 难以清晰表达指标、对比、流程、时间线或层级关系时，指导 Agent 输出静态 `html:preview` 围栏；普通 HTML 源码仍使用 `html` 代码块，前端把该围栏清洗后放入 sandboxed iframe 预览。未显式配置 Skills 的 Agent 按现有资源规则自动获得该 Skill；使用显式 Skills 允许列表的 Agent 需要选择 `html-preview`。\n\n内置 `deep-research` 不依赖 `html-preview`。它默认在当前 Workdir 的 `outputs/` 目录生成独立、响应式的 HTML 阅读文档，并通过交付物入口展示；用户明确指定其他格式时除外。宽屏报告可以提供侧栏目录，窄屏隐藏或折叠侧栏；报告可以按内容需要使用外部图片等公开资源，来源以普通链接呈现。\n\n## 运行时文件行为\n\n共享和内置 Skill 投影对 Agent 只读，但沙盒命令仍可能执行其中的脚本。脚本如需写文件，应写入当前 Project Workdir 或 User Data，而不是 Skill 目录。个人 Skill 直接从当前用户工作区读取，不会复制到共享投影。\n\nSkill 的选择影响 Prompt 和工具激活；共享投影按用户授权集合生成，不会因为某个 Agent 选择了 Skill 就改变 Sandbox 身份。路径穿越、符号链接和跨用户访问由文件系统边界拒绝。\n\n## 维护建议\n\n- slug 使用小写字母、数字和单个短横线，尽量短且能表达用途。\n- `description` 先说明能力和适用场景，再写实现细节。\n- 把“何时使用”“不要做什么”“产物放在哪里”和“如何验收”写进 `SKILL.md`。\n- 依赖链保持短小，避免循环依赖；只有真实需要时才声明工具或 MCP 依赖。\n- 脚本按不可信输入处理，不读取或输出运行环境中的秘密。\n- 修改共享 Skill 的依赖、范围或文件后，用一个真实 Agent Run 验证模型可见工具和最终产物。\n\n实现入口见 [包格式解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/package.py)、[安装草稿生命周期](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/draft.py)、[共享 Skill 索引与安装](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/shared.py)、[共享文件编辑](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/edit.py)、[个人 Skill 安装与文件操作](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/personal.py)、[用户投影](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/projection.py)、[运行时解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/skills/runtime.py) 和 [Skills middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/skills.py)。\n","isInternal":false,"tokens":4067,"sizeBytes":12538},{"name":"subagents-management.md","path":"docs/agents/subagents-management.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/subagents-management.md","title":"Subagent: subagents-management","category":"subagent-persona","format":"markdown","content":"# 使用子智能体\n\n子智能体是一个特殊的 Agent：它仍然是 `agents` 表中的一级智能体，只是标记为 `is_subagent=true`，并使用 `SubAgentBackend`。因此，子智能体和普通智能体共用创建、权限和配置入口。\n\n## 在页面中配置\n\n进入“智能体”，点击“新增智能体”，在后端类型中选择 `SubAgentBackend`。然后像配置普通智能体一样设置名称、提示词、模型、工具、知识库、MCP 和 Skills。\n\n子智能体有三个限制：\n\n- 不会出现在普通聊天的智能体切换列表中；\n- 不能设置为默认智能体；\n- 不能继续调用其他子智能体，因此不会形成孙级调用链。\n\n主智能体在运行配置的“子智能体”字段中选择允许调用的对象：\n\n- 未配置时默认仅选择通用任务（`[\"general-purpose\"]`），运行时仍按当前用户权限过滤；\n- 显式选择 `\"all\"` 时，每次运行选择当前用户可见的全部子智能体；\n- 保存空列表 `[]` 时不启用子智能体；\n- 显式选择后，只允许调用所选项；\n- 每个子智能体使用自己的 `config_json.context`，不会继承主智能体的模型或工具选择；\n- 用户权限变化后，新运行会重新计算可见范围。\n\n## 调用方式\n\n主智能体通过工具调用子智能体，不要通过 Shell、`curl` 或 HTTP API 间接调用。\n\n### 派发任务：`subagent_start`\n\n所有子任务通过 `subagent_start` 派发，立即返回 `run_id`、`thread_id` 和访问链接。需要结果时调用 `subagent_await(run_id)`；即使短任务也先派发再等待。多个独立任务先全部派发，再按需等待。\n\n工具参数：\n\n```python\n{\n    \"description\": \"整理这份文档的三条要点\",\n    \"subagent_slug\": \"general-purpose\",\n    \"thread_id\": null\n}\n```\n\n首次调用不需要 `thread_id`。如果要继续之前的子任务，把上一次结果中的子智能体线程 ID 传回去。\n\n### 查询、等待与取消\n\n生命周期工具统一按 Run 工作：\n\n| 工具 | 作用 |\n| --- | --- |\n| `subagent_start` | 创建并立即返回 `run_id`、`thread_id` |\n| `subagent_status` | 查询运行状态和最近几条进度摘要 |\n| `subagent_await` | 等待终态并取得最终结果；超时返回当前快照 |\n| `subagent_cancel` | 请求取消子智能体运行 |\n\n同一个子智能体线程同时只能有一个运行中的 Run。忙碌时工具返回 `busy`，不会隐藏地把请求排队。终态后的同一 `thread_id` 可以继续创建新的 Run。\n\n所有生命周期工具按 `run_id` 操作，并验证该 Run 由当前父 Run 创建，不能读取或控制其他任务。\n\n## 运行时边界\n\n一次子智能体调用会创建独立的 child checkpoint thread 和 `agent_runs(run_type=subagent)` 记录，同时继承父运行的用户身份和根执行树：\n\n| 资源 | 主智能体 | 子智能体 |\n| --- | --- | --- |\n| LangGraph checkpoint | 当前 `thread_id` | 独立 `child thread_id` |\n| Sandbox runtime | 根 `runtime_scope_id` | 与根运行相同 |\n| Project Workdir | 当前 Project 的 Workdir | 与根 Conversation 绑定的 Project 相同 |\n| UserWorkspace | 当前用户的工作区 | 同一用户的工作区 |\n| 共享/内置 Skills | 当前用户授权的只读投影 | 同一授权投影 |\n\n父子智能体看到的是同一份 Workdir 文件字节，不会通过 checkpoint 复制或合并文件。child thread 只隔离 LangGraph 上下文；它不是文件系统隔离边界。并发写同一路径仍按真实 POSIX 文件结果处理。\n\n子智能体可以使用自己配置的文件工具和知识库范围，但所有资源访问仍以发起用户的后端权限为最终边界。`present_artifacts`、`ask_user_question`、`install_skill` 等不适合子智能体直接使用的工具会被过滤；禁用状态不只在模型可见性上生效——即使模型显式传回被禁用的工具调用，执行层也会返回绑定原调用的错误结果，不会执行该工具。\n\n## API 和数据模型\n\n子智能体沿用普通 Agent 管理 API：\n\n- `GET /api/agent` 默认返回聊天可用的普通 Agent；\n- `GET /api/agent?include_subagents=true` 返回包含子智能体的列表；\n- 创建或更新 `SubAgentBackend` 时，后端会校验 `is_subagent=true`；\n- 详情、更新和删除复用同一套 Agent 权限检查。\n\n运行时，主智能体的 `subagents` 会先收敛为当前用户可见的允许列表。列表非空时挂载子智能体 middleware；子智能体自身不会挂载这一中间件。\n\n## 查看结果\n\n父 state 的 `subagent_runs` 保存子任务身份，页面加载时通过数据库父子关系补齐尚未进入 checkpoint 的记录。状态面板独立订阅各子 Run 的事件流，并从 Run 接口读取当前状态；父 graph 在 `subagent_await` 中等待时，先完成的子任务立即更新。切换会话会关闭订阅，重新打开时回读状态；完成后从持久化消息读取最终结果。Redis 原始事件只供运行基础设施和前端订阅，不作为主智能体的工具结果。\n\n前端最多同时订阅三个子 Run，其余活跃子 Run 每两秒查询状态，避免 HTTP/1.1 的同源连接被长连接占满；连接无事件时每十五秒核对持久状态，断线或查询失败显示重连提示。\n\n## 从 task 工具迁移\n\n历史 `task` 消息仍可查看，新模型不再获得该工具。升级前完成或取消旧版本的活跃 Run，并在智能体管理中将已保存的深度研究 Agent 及自定义提示词中的 `task` 指令改为先 `subagent_start`、再 `subagent_await`；已有配置不会被新的默认提示词覆盖。历史 checkpoint 中尚未执行的 `task` 调用会明确返回未知工具错误，不会被自动重放为新的子任务。父 Run 终态仍按原有策略取消活跃后代，派发与等待分离不改变此边界。\n\n实现入口见 [子智能体 middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/subagent_task.py)、[SubAgentBackend](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/subagent/graph.py) 和 [AgentRun 服务](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/agent_run_service.py)。\n","isInternal":false,"tokens":2030,"sizeBytes":6259},{"name":"tools-system.md","path":"docs/agents/tools-system.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/docs/agents/tools-system.md","title":"Subagent: tools-system","category":"subagent-persona","format":"markdown","content":"# 工具系统\n\nYuxi 的工具分成三层：内置工具、知识库工具和 MCP 工具。Graph 创建时准备可执行工具，运行时再根据用户权限、Agent 配置和 Skill 激活状态决定模型能看到什么。\n\n## 注册一个内置工具\n\n普通内置工具使用 `@tool` 注册：\n\n```python\nfrom yuxi.agents.toolkits.registry import tool\n\n\n@tool(category=\"buildin\", tags=[\"示例\"], display_name=\"示例工具\")\ndef example_tool(text: str) -> str:\n    \"\"\"返回处理后的文本。\"\"\"\n    return text\n```\n\n- `category` 用于前端分组，常见值是 `buildin`、`knowledge` 和 `debug`；\n- `tags` 用于展示和筛选；\n- `display_name` 是给用户看的名称，工具 ID 是给代码和模型协议使用的稳定名称；\n- 工具模块需要被 `toolkits` 包导入，装饰器才会执行注册。\n\n工具的注册不等于授权。产生文件、网络或数据库副作用的工具必须在执行边界再次校验当前用户和目标资源。\n\n## 当前内置能力\n\n常用内置工具包括：\n\n| 工具 | 作用 |\n| --- | --- |\n| `ask_user_question` | 等待用户回答交互式问题 |\n| `ocr_parse_file` | 把工作区中受支持的 PDF、Office 或图片转换为 Markdown |\n| `present_artifacts` | 展示当前用户可见的文件产物 |\n| `install_skill` | 从允许的沙盒路径或 Git 来源安装个人 Skill；子智能体不可用 |\n| `web_search` | 使用已配置的豆包或 Tavily 搜索网页 |\n\n文件读写和命令执行由 Agent 的 Sandbox backend 提供。`present_artifacts` 推荐展示当前 Project 的 `outputs/` 文件；`large_tool_results` 和会话摘要等内部文件不会作为交付物展示。\n\n图片生成能力由内置 `image-gen` Skill 提供，不再作为独立的 Python 工具注册。具体依赖和文件位置由该 Skill 说明。\n\n## 知识库工具\n\n知识库工具以 `@tool(category=\"knowledge\")` 注册，但不默认出现在模型工具列表。Agent 激活内置 `knowledge-base` Skill 后，Skills middleware 才会向模型开放：\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_kbs` | 列出当前运行可见的知识库 |\n| `query_kb` | 按 `kb_id` 检索片段，返回 `kb_id`、`file_id` 和内容 |\n| `find_kb_document` | 在指定文件中按关键词或正则定位内容 |\n| `open_kb_document` | 按 `file_id` 分段读取解析后的文档 |\n| `get_mindmap` | 读取知识导图 |\n| `search_file` | 按文件名搜索可见知识库中的文件 |\n| `download_kb_file` | 把有权限的原始文件下载到当前 Project 的 `outputs/` |\n\n工具参数中的 `kb_id`、`file_id` 和文件名都会在工具执行处重新检查，不能用模型提示词或 Agent 配置绕过知识库权限。知识库不会挂载为沙盒目录，读取方式见[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n需要在 Python 中直接取得知识库工具时：\n\n```python\nfrom yuxi.agents.toolkits.kbs import get_common_kb_tools\n\nkb_tools = get_common_kb_tools()\n```\n\n返回的具体顺序由函数实现维护，不要把顺序当作协议。\n\n## 内置工具：网页搜索\n\n`web_search` 让 Agent 从外部网页获取实时信息，当前支持豆包和 Tavily 两个供应商。搜索结果属于外部来源，不能自动替代知识库或人工核验；对重要结论应回到原网页核实。\n\n供应商在 API/worker 环境中选择并提供凭证：\n\n```bash\nWEB_SEARCH_PROVIDER=doubao\nDOUBAO_SEARCH_API_KEY=<your-doubao-key>\n```\n\n或 `WEB_SEARCH_PROVIDER=tavily` 加 `TAVILY_API_KEY=<your-tavily-key>`。`WEB_SEARCH_PROVIDER` 只接受这两个值；留空时按已配置的 API Key 自动选择，两个 Key 都配置时优先豆包，没有有效 Key 时不会注册该工具。密钥只放在受保护的环境变量或密钥管理器中，不要写进 Skill、Agent 用户环境、代码或日志。\n\n工具在 API 和 worker 进程加载时注册，修改环境变量后需要 `docker compose up -d --force-recreate api worker`。验证方式：在智能体详情确认工具列表出现「网页搜索」，用一个需要最新信息的问题发起真实对话，并检查工具调用返回的 URL、标题和摘要。没有该工具时检查 `WEB_SEARCH_PROVIDER` 拼写、对应 Key 是否存在以及容器是否已重建。\n\n实现入口：[网页搜索工具](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/toolkits/buildin/tools.py)。\n\n## 工具组装流程\n\n内置 Agent 创建 Graph 时执行：\n\n1. `prepare_agent_runtime_context` 按当前用户权限过滤工具、知识库、MCP、Skills 和子智能体。\n2. `resolve_configured_runtime_tools(context)` 注册 Agent 配置和可见 Skill 依赖的可执行本地工具，并加载配置的 MCP 工具。\n3. `SkillsMiddleware` 根据当前已预加载或已激活的 Skill，向模型请求开放相应工具 schema。\n4. 工具执行器再次检查具体文件、知识库、MCP 和用户身份。\n\n```text\nAgent 配置 + 用户权限\n        ↓\n运行时资源快照\n        ↓\n可执行工具注册 ──→ Skill 激活门控 ──→ 模型可见工具\n        ↓\n执行处的目标授权\n```\n\n“可执行”与“模型可见”是两件事：工具可以先注册到 ToolNode，等 Skill 激活后才对模型开放；模型看见某个工具也不代表它可以访问任意资源。\n\n## MCP 和 Skills\n\nMCP 工具可由 Agent 直接选择的已启用服务器提供，也可由已激活 Skill 的 `mcp_dependencies` 按需加载；预加载 Skill 从首轮开放本地工具和 MCP 依赖，普通 Skill 在读取 `SKILL.md` 激活后开放这些依赖。这一节只回答“工具从哪里来、谁决定模型可见”；服务器配置和启停归 [MCP 集成](./mcp-integration.md)，Skill 的创建、依赖与激活时机归 [Skills 管理](./skills-management.md)，资源选择的字段语义归[智能体配置](./agents-config.md)。\n\n## 新增工具时检查\n\n- 工具是否属于内置工具、知识库工具、MCP 还是 Skill；\n- 是否需要在 `ToolNode` 注册，是否需要 Skill 激活后才让模型看见；\n- 产生副作用时，执行处是否有用户、路径和资源权限校验；\n- 错误是否以结构化结果返回，并保留可排查信息；\n- 前端展示名称是否来自稳定元数据；\n- 是否补充纯逻辑和真实 HTTP/文件链路的相应测试。\n","isInternal":false,"tokens":2026,"sizeBytes":6341},{"name":"README.md","path":"packages/yuxi-cli/README.md","rawUrl":"https://raw.githubusercontent.com/xerrors/Yuxi/HEAD/packages/yuxi-cli/README.md","title":"yuxi-cli Documentation","category":"plugin-manifest","format":"markdown","content":"# yuxi-cli\n\nYuxi command line client.\n\nFirst-stage scope:\n\n- remote management through `~/.yuxi/config.toml`\n- browser login\n- API Key import through `--api-key`\n- `whoami`, `status`, and `logout`\n- server discovery and compatibility check for Yuxi `>=0.7.1`\n- `yuxi chat` for a temporary local browser chat with streamed Agent output; `/state` reads thread state and `/approve` resumes a pending tool approval\n- `yuxi agent list` and `yuxi agent show <slug>` for inspecting agents visible to the logged-in user\n- `yuxi kb upload` for knowledge base file uploads\n- `yuxi agent eval` for running existing Langfuse dataset experiments with a logged-in remote\n","isInternal":false,"tokens":173,"sizeBytes":657}],"systemPromptSnippet":"<yakaai_skills repo=\"xerrors/Yuxi\">\n<!-- File: AGENTS.md (Tokens: ~2458 | Category: root-instruction) -->\n# Yuxi Agent 开发约定\n\nYuxi 是基于 LangGraph、FastAPI、Vue 和多种持久化服务构建的知识库与多智能体平台。Docker Compose 是开发拓扑的事实来源；修改不熟悉的模块前先阅读 [ARCHITECTURE.md](ARCHITECTURE.md)，再用符号搜索确认真实实现。\n\n## 每次任务先加载什么\n\n- [ARCHITECTURE.md](ARCHITECTURE.md)：稳定边界、主链路和架构不变量。\n- [Yuxi Spec Loop](docs/develop-guides/spec-loop.md)：非平凡变更从提案、证据到收敛的流程。\n- [工程信任系统](docs/develop-guides/engineering-trust.md)：语义 Owner、证据、决策记录、派生审计和 gate 规则。\n- [测试规范](docs/develop-guides/testing-guidelines.md)：unit、integration、E2E 的职责与命令。\n- [贡献指南](docs/develop-guides/contributing.md)：分支、独立 Review、commit 和 PR 流程。\n- [并行工作树与隔离运行环境](docs/develop-guides/parallel-worktree-environments.md)：同时运行多个分支、复用长期数据或处理 Schema 不兼容时加载。\n- 用户在当前任务中的明确要求优先于本文件；修改 `backend/`、`web/` 或 `docs/` 时同时遵循该子树的 `AGENTS.md`。子树规则只补充本目录，不复制回根文件。\n\n## 任务与决策\n\n1. 开始实现前，把请求压缩为可验证的目标、非目标和验收主张；多步任务先给出每步带验证方式的简短计划。\n2. 假设显式记录后继续；只有当不同解释会改变验收结果、数据、安全或外部状态时才阻塞询问，并把候选解释一并列出而不是默默选择其一。存在明显更简单的方案时，先说明取舍再动手。\n3. 把\"可以\"\"也可以\"\"类似这样\"\"例如\"当作简单方向，不是设计更大机制、配置项或兼容层的许可。\n4. 在真实语义 Owner 处闭合受影响的工程主张：源码、数据约束或当前契约拥有事实；独立 oracle 与负向案例证明它；实际 workflow 或可问责 Reviewer 产生拒绝后果。不要建立可独立编辑的中央主张清单或 claim ID 体系。\n5. 非平凡变更必须新增或更新一份 tracked [决策记录](docs/develop-guides/decisions/README.md)。记录问题、当前决定、真实替代项、后果和验证，不保存推理流水账或实现过程叙事。\n6. `docs/vibe/` 只用于本地临时计划，被 Git 忽略，不是组织记忆，也不能作为已完成事实的唯一来源。\n7. 只修改验收标准需要的范围；不顺手重构、格式化或添加想象中的配置、兼容层和扩展点。\n\n## 不能破坏的系统事实\n\n- HTTP 路由保持薄；用例流程属于 `yuxi.services`，持久化查询属于 `yuxi.repositories`。\n- 普通请求先在 PostgreSQL 中持久化 Message 和 AgentRunRequest；只有 ready FIFO 队头创建 AgentRun，且每次投递 ARQ 前 owning transaction 都已提交。Redis 负责投递、短期事件、取消和缓存，不拥有最终业务状态。\n- 同一用户、Agent、线程的普通请求按 FIFO 串行派发；Request 和 Run 是不同状态模型。\n- AgentRun 的输出、事件、artifact 和错误必须绑定同一 request/run；禁止从相邻 Run 猜测结果。\n- 非终态 Run 必须有明确执行 Owner、lease/heartbeat 或等价机制，以及崩溃后的可观察结局。\n- `/api/system/health` 只表达进程 liveness；接流量前置条件由 `/api/system/ready` 证明，业务正确性仍由真实链路测试证明。\n- LangGraph checkpoint 只使用 PostgreSQL；API、worker 与 Agent 不提供本地后端选择或静默降级。\n- 权限在后端依赖与 repository 可见性查询处最终执行；前端守卫、prompt、schema omission 和 UI 隐藏不是授权边界。\n- Shipping 启动、路由注册和能力发现始终包含知识库、图谱与评估能力；附件解析入口只在真实解析动作发生时惰性加载 parser。\n- 沙盒虚拟路径、对象 URL 和宿主机路径不可混用；所有用户路径必须在 owning filesystem boundary 校验。\n\n## 证据规则\n\n- Agent 的完成报告、HTTP 200、日志关键词或 mock 调用次数都不是最终事实；重新读取数据库、文件、对象、DOM 或协议结果。\n- 每个新 guard 都要有一个能恢复目标缺陷并使其在正确原因上失败的负向测试。\n- 测试从最小相关集合开始，再按风险扩大；不要用 unit 结果替代真实 PostgreSQL、真实 HTTP、worker 或浏览器语义。\n- Expected output、snapshot 和 fixture 只能显式更新并审阅；CI 不得一边生成 oracle 一边验证 oracle。\n- 实际执行命令、结果和未执行原因写入 PR；未验证不能写成通过。\n\n提交前在仓库根目录至少运行：\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\n```\n\n改动面按下表选择最低证据，并按升级条件扩大；不要用低层级结果替代高风险语义。\n\n| 改动面 | 最低证据 | 何时升级 |\n|---|---|---|\n| 纯 Python / JS 逻辑 | 相关 unit，断言业务结果 | 触及数据库、缓存或文件副作用时补 integration |\n| API / 权限 / 持久化 | 真实 HTTP integration | 跨 worker、队列或用户主链路时补 E2E |\n| Run / FIFO / SSE / 沙盒 / 恢复 | E2E，验证最终状态与产物 | 依赖外部可选服务时记录环境与未验证范围 |\n| 前端交互 | lint + unit（[web/AGENTS.md](web/AGENTS.md)） | 行为关键时补 build 与真实页面验证 |\n| 文档与导航 | 相对链接检查 + docs build | 公开行为变化时与代码验证一起执行 |\n\n完整命令由 [测试规范](docs/develop-guides/testing-guidelines.md) 维护。\n\n## 实现与安全\n\n- 使用满足验收标准的最小、线性实现。抽象、状态机、fallback、兼容路径和依赖都要有当前 consumer 与 Owner；仓库外用户、持久数据和部署承诺也算 consumer。\n- 实现明显长于问题本身，或一半行数可以清楚表达同样行为时，先简化再提交；自检\"高级工程师是否会认为这段代码过度设计、过度防御或过于零碎\"。\n- 不可信输入只在 parser、配置、模型/tool JSON、持久化、worker、process、wire 和用户路径等真实信任边界校验；授权与隔离在产生副作用的 executor/repository fail-closed，直接或替代调用路径都不能绕过。\n- 预设条件不成立时显式失败。可选能力可以降级，但必须结构化、可观察，且不把未就绪伪装为成功。\n- 新增函数/类使用简洁中文 docstring；注释只解释非显然约束、时序、Owner 和安全用法，不复述代码。\n- 不输出或提交 `.env`、账号、Token、用户数据、运行目录和构建产物。\n- 文件以一个换行结尾；提交前运行 `git diff --check`。\n\n## Review 与交付\n\n所有代码变更在 commit 前必须由不继承开发上下文的全新 Reviewer Agent 审查完整需求、diff、测试和规范；修复所有影响功能、边界、证据可信度或认知负担的问题。提交信息使用中文 Conventional Commit，PR 使用仓库模板，以自然语言列出受影响主张、语义 Owner、证据、风险和未验证范围，不引用中央 claim ID。\n\n<!-- File: backend/AGENTS.md (Tokens: ~1166 | Category: root-instruction) -->\n# Backend 约定\n\n本目录承载 FastAPI 适配层、业务服务、repositories、Agent runtime 与持久化实现。先阅读根 [AGENTS.md](../AGENTS.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n## 边界与所有权\n\n- `server/routers` 只处理 HTTP 模型、认证依赖、状态码和响应装配；跨 repository 的用例进入 `package/yuxi/services`。\n- PostgreSQL 是 Request、Run、Message、权限和业务终态的 Owner；Redis/ARQ 是投递与短期事件平面。\n- 写入事实、提交事务、发布队列/事件的顺序必须显式；通知不能早于 owning transaction 的 commit point。\n- 跨 repository 用例只有一个事务 Owner；需要经 HTTP 返回的一次性 secret 必须可由幂等请求安全重放，不能先不可逆消费再祈望响应送达。凭据撤销必须保留足以阻止同一幂等请求复活 secret 的 tombstone。\n- parser、HTTP、模型/tool JSON、持久化、worker、process、wire 和用户路径是运行时校验边界；已由 Python 类型和同进程调用保证的内部值不重复 hostile validation。\n- 核心启动依赖失败要阻止 readiness；可选集成降级要记录组件名、失败类型与当前能力。\n- AgentRun 的状态转换、lease、输出和终态投影由 repository/service 统一维护，调用方不得直接拼装并行真相。\n\n## 实现\n\n- Python 使用 3.12+ 语法；保持主流程线性，优先早返回和清晰主路径，避免细碎 helper、静默 fallback、一次性抽象、不必要的嵌套和多层调用链。\n- 本次新增或实质修改的代码遵循向下规则：同一模块或类内，公开、高层方法在上，专用实现细节在后；旧代码布局不豁免新改动。可调整直接相关定义的位置，不重排整个文件；基类、装饰器、默认值和模块初始化等定义时依赖优先满足 Python 执行顺序。Reviewer 核对局部阅读顺序。\n- 拆函数用于明确复用、隔离副作用或实质降低认知负担；实现细节遮蔽主流程时，即使只调用一次也可提取。仅转发参数或包装表达式且不增加语义的 helper 通常内联。\n- 常量和具名值放在最小合理作用域：跨函数复用、协议标识或配置约束用模块级常量；仅服务单个函数的局部规则留在函数内。\n- 命名表达业务意图；不导入其他模块下划线开头的私有标识，确有共享需求时改为公开命名。\n- 删除本次修改产生的未使用 import、变量、函数和分支；不清理修改前已存在的无关死代码，发现坏味道只说明不擅自处理。\n- 小型状态、进度或摘要需求直接读取来源并返回最小结果，不重建事件流或调试视图。\n- 新增函数/类使用简洁中文 docstring；不同语义的代码段之间留空行保持可读。异常只在当前层能增加稳定语义、执行清理或决定策略时捕获。\n- Schema 演进必须幂等、可在现有数据上执行，并有真实 PostgreSQL 测试；不可逆操作明确数据影响。\n\n## 验证\n\n```bash\ndocker compose exec api uv run --group test pytest test/unit -m \"not slow\"\ndocker compose exec api uv run --group test pytest test/integration\ndocker compose exec api uv run --group test pytest test/e2e -m e2e\ndocker compose exec api uv run ruff check package\ndocker compose exec api uv run ruff format package --check\n```\n\n并发、事务、锁、lease、schema 与 PostgreSQL 专属语义必须在真实 PostgreSQL 上验证；API 行为通过真实 HTTP integration 证明；关键 Run/worker/文件副作用通过 E2E 证明。\n\n<!-- File: docs/AGENTS.md (Tokens: ~2101 | Category: root-instruction) -->\n# 文档约定\n\n本目录包含用户教程、配置参考、机制详解、开发规范、决策记录、事故复盘和版本材料。仓库根 `AGENTS.md` 的系统不变量仍然适用；完整信息架构、写作标准与检查清单由[文档编写与维护规范](develop-guides/documentation-guidelines.md)拥有，本文件只保留进入 `docs/` 工作时必须执行的指令。\n\n## 层级与事实 Owner\n\n- 每个事实只有一个负责完整解释的 Owner；其他页面只保留完成当前任务需要的上下文并使用相对链接。先按[层级表](develop-guides/documentation-guidelines.md#信息架构与事实-owner)确定页面位置，再写内容。\n- `intro/` 拥有从零完成结果的教程，`advanced/` 拥有配置和运维参考，`agents/` 拥有 Agent 配置与扩展方法，`mechanisms/` 拥有运行机制、状态、权限、失败和源码定位。实质性混合内容必须拆页。\n- 当前系统边界和主链路属于仓库根 `ARCHITECTURE.md`；测试层级与命令属于[测试规范](develop-guides/testing-guidelines.md)；工程信任闭环属于[工程信任系统](develop-guides/engineering-trust.md)。不要在专题页复制这些完整规则。\n- 非显然取舍属于 `develop-guides/decisions/`，达到门槛的事故因果属于 `develop-guides/postmortems/`，已发布事实属于 changelog，未完成方向属于 roadmap。`docs/vibe/` 只用于被忽略的本地临时计划。\n- 源码、schema、Compose、数据约束和测试拥有可执行事实；外部 Wiki、旧 changelog、历史 PR 和 Agent 自述只能帮助定位，不能覆盖当前 Owner。\n\n## Agent 写作流程\n\n1. 读取根与当前子树指令、`ARCHITECTURE.md`、owning 文档和相关源码；非平凡信息架构或长期约束变化先创建 tracked proposed decision。\n2. 写明预定读者、问题、前置知识、目标、非目标和页面类型。教程必须有顺序结果；参考必须有明确查找范围；实质性混合内容先拆分。\n3. 用符号搜索沿入口 → service/executor → repository/持久化或发布点 → 用户/模型结果重建真实链路，同时核对权限、失败路径、可选能力和相关测试。\n4. 先列 Section 及每节唯一问题，按“概念 → 组件关系 → 状态/Owner → 权限/失败 → 源码定位”渐进；教程按完成任务所需的前置依赖排序。\n5. 一次最多撰写或重写一个 Section。完成后立即核对事实、相对链接、与相邻章节的重复及当前页面职责，再进入下一节。\n6. 先更新事实 Owner，再更新导航、索引和引用页。页面移动或拆分必须在同一变更中修复全部入站链接，不保留两个可独立编辑的副本。\n7. 提交前运行最小相关 gate，并由不继承开发上下文的全新 Reviewer 对照完整需求、源码、diff、测试和未验证范围审查。\n\n## 写作与维护\n\n- 使用直接、具体、可验证的现在时，写明执行者、条件、时序、结果、失败和后果。不要用“应该、一般、可能”掩盖未核实事实；无法核实时明确条件与 `Not run` 范围。\n- 优先用肯定句直接陈述职责、边界和结论。禁止先否定一个弱命题、再用转折强调目标命题的对举句式；删除空泛铺垫、宣传性形容词和替读者下判断的强调语。\n- 普通段落每段只用一个物理行并依靠编辑器软换行；代码块、表格和列表保留结构。一个段落包含多个独立规则时拆段，不用硬换行掩盖段落墙。\n- 不在当前说明中叙述“以前/现在/这次修改”、PR 过程、Review 对话、实现步骤或推理流水账。原因和取舍写入 decision，事故时间线写入 postmortem。\n- 不手写易漂移的完整文件、API、测试或配置清单；保留稳定分类并链接源码、schema、Compose 或生成 Owner。不要按函数逐行复述控制流。\n- 机制页必须覆盖适用范围、真实装配、状态与事实 Owner、权限/隔离、失败/恢复、可观察结果、配置边界和“源码定位与验证”；配置页保留参数、操作与排障，不复制机制全景。\n- 教程的关键步骤给出页面、状态、文件或协议等可观察结果；参考字段写清读取者、默认值、条件、限制和生效时机。实质性的教程与参考内容不能长期共居一页。\n- 图只用于三项以上关系、状态或时序，使用能由 VitePress 渲染的最小 Mermaid；图不能替代正文中的条件、异常和安全语义。尚未实现的方向不进入当前机制图。\n- 站内页面使用相对 Markdown 链接；站点外公开源码使用项目 GitHub `blob/main` 文件链接。禁止本地绝对路径、易漂移行号和只写文件名不说明职责的源码清单。\n- 示例只使用占位凭据、保留域名或假 ID；禁止写入 `.env`、Token、真实账号、用户数据、本地绝对路径或不可公开的内部地址。不要建议把 provisioner/数据库等管理凭据注入 Agent 沙盒。\n- 决策记录遵循[生命周期与格式](develop-guides/decisions/README.md)；implemented 记录使用现在时，不保留提案 checklist。用户可见完成项与 roadmap 不得同时声明同一状态。\n\n## 导航、预算与验证\n\n- 新增正式页面时更新 `.vitepress/config.mts` 的正确父级和阅读顺序；内部 `AGENTS.md`、decision 与 postmortem 不因位于 `docs/` 就自动成为用户导航入口。\n- `scripts/verify_engineering_contracts.py` 的 AGENTS 字符预算是 standing-order guardrail。超限时先下沉示例、背景和专题解释，再压缩重复；确需提高预算时同步更新 decision 和 gate，不能静默删掉必要约束或关闭检查。\n- 修改配置、API、状态、权限或机制说明时检查相关测试；文档构建只证明 Markdown、导航和链接有效，不证明语义正确。Reviewer 必须逐页对照当前源码、配置和风险相称的 oracle。\n- 构建失败要修正文档、锚点或导航，不扩大 `ignoreDeadLinks` 掩盖问题。报告只写实际执行的命令、结果和未验证范围。\n\n```bash\npython3 scripts/verify_engineering_contracts.py\npython3 -m unittest scripts.test_verify_engineering_contracts\ncd docs && pnpm run build\ngit diff --check\n```\n\n<!-- File: web/AGENTS.md (Tokens: ~350 | Category: root-instruction) -->\n# Web 约定\n\n本目录是 Vue 3 / Vite 前端。先阅读根 [AGENTS.md](../AGENTS.md)、[设计规范](../docs/develop-guides/design.md) 和 [ARCHITECTURE.md](../ARCHITECTURE.md)。\n\n- API 调用统一放在 `src/apis`；组件不直接拼接普通 HTTP 请求。\n- 前端权限与路由守卫只提供体验约束，后端始终执行最终授权。\n- 复用 `src/assets/css/base.css` 变量和 `@lucide/vue`；不为一次性视觉需求引入新依赖。\n- 保持 loading、empty、error、断线恢复和终态投影语义一致；不要用乐观 UI 覆盖 PostgreSQL 返回的最终事实。\n- `pnpm run lint:check` 是只读 gate；`pnpm run lint` 才允许本地自动修复。\n\n提交前运行：\n\n```bash\ndocker compose exec web pnpm run lint:check\ndocker compose exec web pnpm run test:unit\ndocker compose exec web pnpm run build\n```\n\nUI 改动必须在真实页面验证，并提供最终截图或录屏；适用时覆盖浅/深色、响应式、loading、empty 和 error 状态。\n\n<!-- File: CLAUDE.md (Tokens: ~3 | Category: claude-rule) -->\nAGENTS.md\n\n<!-- File: backend/package/yuxi/agents/skills/buildin/deep-research/SKILL.md (Tokens: ~1862 | Category: anthropic-skill) -->\n---\nname: deep-research\ndescription: \"深度研究编排方法论：澄清范围、拆解规划、并行调度子智能体调研、对抗式核验、综合成带引用的结构化报告。当任务需要多来源、可追溯、需事实核查的深度研究时使用此技能。\"\nversion: \"2026.09.19\"\ntool_dependencies: [\"web_search\", \"present_artifacts\"]\n---\n\n# 深度研究技能\n\n当任务目标是产出**多来源、可追溯、经过核验**的深度研究结论（科研综述、行业/竞品调研、技术选型、专题分析等）时，使用此技能组织整个研究过程。本技能的核心是**编排**：你负责整体把控与子智能体调度，把繁重的检索与核验工作派发出去，自己专注规划与综合。\n\n## 可用子智能体\n\n通过 `subagent_start` 工具派发，记录返回的 run_id；需要结果时调用 `subagent_await`。互不依赖的子任务先全部派发，再等待结果：\n\n- `research-explorer`（调研探索员）：围绕一个明确子问题做多轮网页/知识库检索，返回按要点组织、带 `<cite>` 引用的结构化发现。**这是主力，按子问题并行多开。**\n- `fact-verifier`（事实核查员）：对给定的关键论断做对抗式核验，逐条给出 支持 / 存疑 / 反驳 + 依据来源 + 置信度，并标注冲突。\n\n## 编排流程\n\n### 1. 澄清范围\n问题不明确时，先用 `ask_user_question` 补充 2-3 个关键问题（研究目标、受众、范围边界、地域/时效、输出语言与形式），对齐验收标准后再开工。已经清晰的任务不要反复追问。\n\n### 2. 规划拆解\n用 `write_todos` 把研究目标拆成**可独立调研**的子问题，每个子问题写明产出标准（要回答什么、需要哪类证据）。子问题应正交、覆盖完整，避免重叠或遗漏关键角度。\n\n### 3. 并行派发调研\n- 把互不依赖的子问题用**多个 `subagent_start` 调用并行**派发给 `research-explorer`。\n- 每次派发在 `description` 中写清：子问题目标、已知上下文、期望输出格式（要点 + `<cite source=\"$URL\" type=\"url\">$INDEX</cite>` 引用 + 参考来源列表）。\n- 何时派发 vs 自己直检：子问题复杂、需多轮检索、可隔离上下文、可并行时一律派发子智能体；仅在澄清范围、补一两个零散事实、或快速校正方向时才自己少量直接检索。\n- 子问题之间有依赖时，先派发前置子问题，拿到结果后再派发后续。\n\n### 4. 核验关键结论\n对**影响最终结论的关键论断**、数字、以及子智能体之间相互冲突的发现，派发 `fact-verifier` 做对抗式核验。要求其默认倾向「证据不足即标注存疑」。核验未通过的结论不要写进正文，或必须明确降级标注。\n\n### 5. 综合成稿\n证据充分后，由你统一综合为结构化报告，**不要**简单拼接子智能体返回的原文。根据研究主题组织内容，围绕论证而不是资料堆砌。\n\n### 6. 停止准则\n信息饱和、或确认无法获取更多有效信息即停。明确标注证据缺口与不确定性，不臆断、不编造来源。\n\n## 引用\n\n- 子智能体返回中间证据时沿用 `<cite source=\"$URL\" type=\"url\">$INDEX</cite>`；最终报告中的关键结论、数据和观点用指向原始来源的普通 `<a href=\"...\">` 链接就近标注。\n- 引用用户附件或知识库时标明文件名或路径。\n\n## 输出\n\n- 最终交付的是一份可直接使用的报告，而不是「我打算怎么研究」。\n- 不要外泄中间推理过程、原始检索日志，也不要把待办清单原样输出成正文。\n- 报告语言与用户提问语言一致，使用正式、克制、可复核的书面表达。\n- 用户没有指定格式时，默认把完整报告写入当前 Workdir 的 `outputs/` 目录，保存为一个 `.html` 文档；不要把完整报告直接铺在聊天正文里。用户明确要求 Markdown、纯文本、JSON 或其他格式时，以用户要求为准。\n- 从系统提示读取当前 Project Workdir 的**绝对虚拟路径**，用 `write_file` 写入 `<当前 Workdir 绝对路径>/outputs/<文件名>.html`。不要把 `outputs/report.html`、宿主机路径或 `/outputs/report.html` 传给文件工具。\n- 写入成功后，把 `write_file` 返回的同一绝对路径传给 `present_artifacts`。最终聊天回复只需简要说明报告已完成并提示交付物，不要重复报告正文。\n\n默认 HTML 应简洁、优雅并专注阅读，同时给模型保留版式判断空间：\n\n- 页面与正文宽度随视口自适应。宽屏可以使用侧栏目录帮助浏览章节，窄屏隐藏或折叠侧栏，让正文自然占满可用宽度。\n- 可以按内容需要引用外部样式、字体、图片或其他公开资源；图片应服务于研究内容，而不是纯装饰。\n- 不提供固定 HTML 模板；根据研究主题选择合适的章节、图表、表格和视觉层级，整体保持克制、清晰，不做复杂浮夸的展示页。\n\n<!-- File: backend/package/yuxi/agents/skills/buildin/html-preview/SKILL.md (Tokens: ~1062 | Category: anthropic-skill) -->\n---\nname: html-preview\ndescription: \"使用 Markdown `html:preview` 围栏输出轻量静态 HTML/CSS 可视化。当普通 Markdown 难以清晰表达数值对比、层级关系、流程、时间线、关键指标或布局示意，或用户明确要求可视化 HTML 预览时使用。询问 HTML 源码、教程示例或可复制代码时不要使用。\"\nversion: \"2026.07.23\"\n---\n\n# HTML Preview\n\n将 Markdown 作为回答主体。只有可视化能明显降低理解成本时，才补充一个\n`html:preview` 静态组件；标题、列表、表格或代码块已经足够清楚时，继续使用普通 Markdown。\n\n按以下顺序输出：\n\n1. 先用普通 Markdown 写结论和必要解释。\n2. 顶格写开始围栏 ```` ```html:preview ````。围栏最多只能有 3 个前导空格，否则前端会把它当作源码。\n3. 在围栏内写自包含的静态 `<style>` 和语义化 HTML。\n4. 顶格写结束围栏 ```` ``` ````。\n5. 在围栏后用普通 Markdown 补充背景、风险、完整明细和来源。\n\n## 内容边界\n\n- 只输出静态 HTML/CSS，不编写 JavaScript，不使用表单、iframe、object 或 embed。\n- 可以引用公开、稳定、无需登录的 HTTPS 图片或字体，但核心信息必须在外链失败时仍然可读。\n- 不引用内网、临时地址、鉴权资源或包含用户凭证的链接。\n- 不把组件当成完整网页或正文容器；不要制作导航栏、页脚、登录态、复杂按钮、营销页 Hero 或多屏页面。\n- 不在组件中放长段叙事、完整报告、长表格或长列表。背景、推理、风险、明细和来源放在普通 Markdown 中。\n- 用户询问 HTML 源码、教程示例或需要复制代码时，使用普通 `html` 代码块，不使用 `html:preview`。\n\n## 布局约束\n\n- 面向默认 `800px × 360px` 尺寸设计，并允许容器宽度变化；前端最高显示到 `700px`，超出后在预览内部滚动。\n- 使用 `max-width: 100%`、`box-sizing: border-box`、弹性网格、换行和适度间距保持响应式。\n- 不写死整体画布高度。核心信息应在默认尺寸内无需滚动即可读；放不下时减少内容。\n- 最多展示 1 个短标题、3–5 个关键指标或一组简短对比。数据超过 6 项时，汇总为趋势、区间、异常点或 Top 3。\n- 文字以短标签、数字、单位、状态词和极短备注为主；单条说明尽量不超过 20 个中文字符。\n- 外层已经提供圆角、边框和裁切。不要再给最外层添加卡片壳、页面背景、大圆角、阴影、厚边框或额外外边距。\n- 保持克制和可读，优先使用紧凑指标组、摘要表、对比条、状态标签、时间轴或简单关系图。\n\n## 失败处理\n\n如果内容无法在上述静态、安全和篇幅边界内表达，改用普通 Markdown，并明确说明限制。不要输出残缺围栏、不可访问资源或依赖脚本才能理解的预览。\n\n<!-- File: backend/package/yuxi/agents/skills/buildin/image-gen/SKILL.md (Tokens: ~1027 | Category: anthropic-skill) -->\n---\nname: image-gen\ndescription: \"在 Agent 沙盒中生成图片并保存到 outputs。当用户要求生成图片、海报、插画、文生图，或指定 Qwen-Image、其它兼容图片生成接口时使用此技能。\"\nversion: \"2026.06.02\"\ntool_dependencies: [\"present_artifacts\"]\n---\n\n# 图片生成技能\n\n当用户要求生成图片、海报、插画、文生图，或明确提到 Qwen-Image 时，使用此技能组织图片生成流程。\n\n## 默认生成接口\n\n默认使用 SiliconFlow 的 Qwen-Image 接口：\n\n- Endpoint: `POST https://api.siliconflow.cn/v1/images/generations`\n- Model: `Qwen/Qwen-Image`\n- 默认参数：\n  - `negative_prompt`: `\"\"`\n  - `num_inference_steps`: `20`\n  - `guidance_scale`: `7.5`\n\n调用外部接口时，必须在 Agent 沙盒执行环境中读取 `SILICONFLOW_API_KEY`。不要依赖后端进程环境变量。\n\n## 操作流程\n\n1. 明确用户要生成的图片内容、风格、尺寸或约束；信息不足但不影响生成时，使用合理默认值，不要反复追问。\n2. 使用可用的执行工具在沙盒中运行脚本，调用图片生成接口，传入用户需求整理后的 `prompt`，并按需传入 `negative_prompt`、`num_inference_steps`、`guidance_scale`。\n3. 从生成接口响应中读取图片地址，默认路径为 `images[0].url`。\n4. 在同一个沙盒脚本中用 `Authorization: Bearer $SILICONFLOW_API_KEY` 下载该图片地址；如果接口直接返回 base64，则直接解码保存。\n5. 将最终图片保存到当前 Workdir 的 `outputs/` 下，例如 `outputs/generated-image.png`。\n6. 调用 `present_artifacts`，传入保存后的 outputs 虚拟路径，让前端展示图片产物。\n7. 最终回复简要说明图片已生成，不要把外部临时 URL 当作最终结果展示。\n\n## 脚本示例\n\n可根据用户需求调整 prompt 和输出文件名：\n\n```python\nimport os\nimport requests\nfrom pathlib import Path\n\napi_key = os.environ[\"SILICONFLOW_API_KEY\"]\nprompt = \"根据用户需求整理后的图片提示词\"\n\nresponse = requests.post(\n    \"https://api.siliconflow.cn/v1/images/generations\",\n    headers={\"Authorization\": f\"Bearer {api_key}\", \"Content-Type\": \"application/json\"},\n    json={\n        \"model\": \"Qwen/Qwen-Image\",\n        \"prompt\": prompt,\n        \"negative_prompt\": \"\",\n        \"num_inference_steps\": 20,\n        \"guidance_scale\": 7.5,\n    },\n    timeout=120,\n)\nresponse.raise_for_status()\nimage_url = response.json()[\"images\"][0][\"url\"]\n\nimage_response = requests.get(\n    image_url,\n    headers={\"Authorization\": f\"Bearer {api_key}\"},\n    timeout=120,\n)\nimage_response.raise_for_status()\n\noutput_path = Path(\"outputs/generated-image.png\")\noutput_path.parent.mkdir(parents=True, exist_ok=True)\noutput_path.write_bytes(image_response.content)\nprint(output_path.as_posix())\n```\n\n## 多模型扩展\n\n如果用户指定其它图片生成模型或兼容接口，可以按该接口的协议先生成图片。只要最终拿到图片 bytes 或 base64，就保存到当前 Workdir 的 `outputs/`，再调用 `present_artifacts` 展示。\n\n## 关键约束\n\n- 不要把外部生成接口返回的临时 URL 当作最终结果直接展示给用户。\n- 不要调用后端 MinIO 上传工具；图片生成和下载都应在沙盒内完成。\n- 如果 `SILICONFLOW_API_KEY` 缺失，应明确提示用户需要在 Agent 沙盒环境变量中配置。\n- 保存到 outputs 后必须调用 `present_artifacts`，否则前端不会自动展示生成图片。\n\n<!-- File: backend/package/yuxi/agents/skills/buildin/knowledge-base/SKILL.md (Tokens: ~934 | Category: anthropic-skill) -->\n---\nname: knowledge-base\nslug: knowledge-base\ndescription: \"使用 Yuxi 知识库进行检索、打开文档、文档内定位和查看思维导图。当用户需要基于已配置知识库回答问题、核验资料或引用文档内容时使用此技能。\"\nversion: \"2026.06.24\"\ntool_dependencies: [\"list_kbs\", \"query_kb\", \"find_kb_document\", \"open_kb_document\", \"get_mindmap\", \"search_file\", \"download_kb_file\"]\n---\n\n# 知识库技能\n\n当用户要求基于项目知识库、内部资料、上传入库文档或知识图谱相关内容回答问题时，使用此技能。\n\n## 可用工具\n\n- `list_kbs`：列出当前会话可访问且已启用的知识库。\n- `query_kb`：按 `kb_id` 在指定知识库中检索内容，返回 `file_id` 和相关片段。\n- `open_kb_document`：按 `kb_id` 和 `file_id` 打开文档原文窗口，适合查看更完整上下文。\n- `find_kb_document`：在已知文档内用关键词或正则定位段落。\n- `get_mindmap`：查看知识库思维导图结构。\n- `search_file`：按文件名关键词搜索知识库中的文件，支持指定知识库或跨知识库，返回文件列表与分页信息。\n- `download_kb_file`：按 `kb_id` 和 `file_id` 下载知识库原始二进制（pdf/docx/xlsx 等）到沙盒 `outputs` 目录，返回沙盒内可见的 `virtual_path`。当后续需要用代码读取原始文件结构（如 `openpyxl` 读 xlsx 单元格、`pdfplumber` 重新解析版面）时使用；`query_kb`/`open_kb_document` 只返回文本切片，无法满足需要文件对象的场景。\n\n## 操作流程\n\n1. 需要先确认当前会话有哪些知识库可用；不确定时调用 `list_kbs`。\n2. 针对用户问题选择最相关的知识库，使用 `query_kb` 检索。\n3. 检索结果的 `metadata.chunk_count == 1` 表示文件只有一个分片；除非需要原文行号或额外上下文，否则无需打开。大于 1 且当前片段不足以回答时，使用返回的 `file_id` 调用 `open_kb_document` 查看上下文。字段缺失或为 0 表示未知，不据此判断文档完整。\n4. 如果用户要求定位术语、指标、章节或原文证据，使用 `find_kb_document` 在候选文档内查找。\n5. 当用户关心知识库结构、文件分类或知识框架时，使用 `get_mindmap`。\n\n## 关键约束\n\n- 只能访问当前会话配置和用户权限允许的知识库。\n- 不要编造 `kb_id` 或 `file_id`；优先从 `list_kbs` 和 `query_kb` 的返回结果中获取。\n- 回答需要可追溯时，应说明依据来自哪个知识库、文件或检索片段。\n- Dify 等外部只读知识库可能只支持检索，不一定支持打开全文或文档内查找；遇到工具返回限制说明时，应如实告知用户。\n\n<!-- File: backend/package/yuxi/agents/skills/buildin/mysql-reporter/SKILL.md (Tokens: ~737 | Category: anthropic-skill) -->\n---\nname: mysql reporter\nslug: mysql-reporter\ndescription: \"生成 MySQL 查询报表。当用户需要查询 MySQL 数据库并以报表形式展示结果时使用此技能，包括：统计销售数据、分析用户行为、生成业务报表、查询业务指标等。\"\nversion: \"2026.09.17\"\n---\n\n# MySQL 报表技能\n\n根据用户的指令，通过终端脚本访问 MySQL 数据库，根据查询结果构建报表。\n\n## 操作流程\n\n1. 理解用户的指令，明确报表的需求和目标\n2. 通过 terminal 进入技能目录：`cd /home/gem/skills/mysql-reporter`\n3. 使用 `uv run scripts/list_tables.py` 查看可用表；如果脚本提示缺少 MySQL 配置，按“环境变量缺失处理”回复用户\n4. 必要时用 `uv run scripts/describe_table.py --table 表名` 查看表结构\n5. 生成正确且高效的只读 SQL，通过 `uv run scripts/query.py --sql \"SQL语句\" --timeout 60` 执行查询并获取结果\n6. 使用 Markdown 表格呈现查询结果，说明关键指标、变化与结论\n\n## 环境变量缺失处理\n\n脚本只读取 Agent 沙盒中的环境变量，不读取后端 `.env` 或 Docker Compose 变量。必填变量包括：\n\n- `MYSQL_HOST`\n- `MYSQL_USER`\n- `MYSQL_PASSWORD`\n- `MYSQL_DATABASE`\n\n可选变量包括：\n\n- `MYSQL_PORT`：默认 `3306`\n- `MYSQL_DATABASE_DESCRIPTION`：数据库业务说明，用于辅助理解表和指标含义\n\n如果执行脚本时出现 `MySQL configuration missing required key`，不要继续猜测连接信息或编造报表。应明确告诉用户：需要在个人设置中的「沙盒环境变量」里配置缺失的 `MYSQL_*` 变量；保存后仅对新建沙盒生效，需要重新发起任务或新建会话后再执行。\n\n## 关键约束\n\n- 生成的 SQL 查询必须正确且高效，避免全表扫描\n- MySQL 操作必须通过本技能 `scripts/` 下的 CLI 脚本执行，不要调用平台内置 MySQL tools\n- 不要在报表或错误说明中输出 `MYSQL_PASSWORD` 等敏感环境变量的值，只能说明缺少哪些变量名\n- 只返回报表相关的结论，不要返回原始 SQL 查询语句\n\n## 允许的工具\n\n- terminal：执行 `scripts/list_tables.py`、`scripts/describe_table.py`、`scripts/query.py`\n- 网络检索工具：必要时补充背景信息\n\n<!-- File: docs/agents/agent-backend-development.md (Tokens: ~2182 | Category: subagent-persona) -->\n# 开发智能体后端\n\n本页面向需要在 Yuxi 中新增或维护 Agent 后端的贡献者。它只讲代码装配；配置字段、权限和运行时上下文分别见[配置智能体](./agents-config.md)和[Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n## 新增预置角色\n\n仅改变提示词、模型或能力选择时，在 `backend/package/yuxi/agents/presets/` 新增一个 Python 文件并导出 `PRESET`；子智能体定义放在其 `subagents/` 子目录。发现逻辑递归读取文件，角色类型仍由 `backend_id` 决定：\n\n```python\nfrom yuxi.agents.presets import AgentPreset\n\nPRESET = AgentPreset(\n    slug=\"report-assistant\",\n    name=\"报告助手\",\n    description=\"根据材料整理报告。\",\n    backend_id=\"ChatbotAgent\",\n    context={\n        \"system_prompt\": \"根据用户材料生成结构清晰、来源可核对的报告。\",\n        \"skills\": [\"html-preview\"],\n    },\n)\n```\n\n子智能体使用 `backend_id=\"SubAgentBackend\"`，其类型由后端推导。`context` 复用[智能体配置](./agents-config.md)字段；长提示词可以直接使用 Python 多行字符串。角色模块不得执行外部操作。\n\nAPI 启动时按文件名发现所有非下划线开头的 Python 模块，校验定义类型、重复 slug 和后端存在性，再统一初始化数据库记录。新增角色无需修改注册清单、repository 或启动调用。模块导入失败、缺少 `PRESET`、重复 slug 或不存在的后端会阻止初始化成功。\n\n预置角色仅在 slug 不存在时创建，已有名称、提示词和能力配置保留；修改源码不会覆盖管理员定制。默认智能助手另行维护原有默认与共享约束。重启 API 后，在智能体管理页核对新增角色的名称、后端和能力配置；子智能体还需在主智能体配置中选用。\n\n## 后端放在哪里\n\n随服务发布的 Agent 后端放在：\n\n```text\nbackend/package/yuxi/agents/buildin/<your_agent>/\n├── __init__.py\n├── context.py\n└── graph.py\n```\n\n执行后端由 `buildin/__init__.py` 中的显式字典注册：\n\n```python\nBUILTIN_BACKENDS = {\n    \"ChatbotAgent\": ChatbotAgent,\n    \"SubAgentBackend\": SubAgentBackend,\n}\n```\n\n新增后端时显式导入其类并添加字典条目。字典键是持久配置与接口使用的稳定后端 ID，Python 类重命名不改变该 ID；仅增加目录或导出类不会启用后端。修改注册表时审查执行图、中间件和运行边界，并验证已有配置仍可解析。\n\n`get_agent_backend(backend_id)` 每次创建一个轻量后端对象，`list_agent_backend_info()` 查询已注册后端的基础信息。后端不持有用户、线程或 Graph 缓存；运行状态由本次 Context、Graph 和 middleware 持有。`get_graph(context=...)` 每次构建独立执行图，知识库增删无需重载后端。未知 ID 抛出 `AgentBackendNotFoundError`；接入用例与后端配置接口将该错误转换为 HTTP 404，worker 执行边界按现有失败流程收敛。\n\n后端的 `get_info()` 返回名称、能力与配置描述，不自行生成后端 ID。新增构图逻辑时验证两次运行分别使用各自的 Context 和资源。\n\n## 最小实现\n\n```python\nfrom langchain.agents import create_agent\nfrom yuxi.agents import BaseAgent, BaseContext, load_chat_model\n\n\nclass MyAgent(BaseAgent):\n    name = \"我的智能体\"\n    description = \"用于示例的智能体后端\"\n    context_schema = BaseContext\n\n    async def get_graph(self, *, context, **kwargs):\n        if not getattr(context, \"_runtime_prepared\", False):\n            raise ValueError(\"构图需要已准备的 Context\")\n        return create_agent(\n            model=load_chat_model(fully_specified_name=context.model),\n            system_prompt=context.system_prompt,\n            checkpointer=await self._get_checkpointer(),\n        )\n```\n\n这个示例展示最小的 Context、模型、提示词和 PostgreSQL checkpoint 装配。真实后端还要根据需要接入文件 backend、工具、Skills、审批、Summary、用量和子智能体 middleware。\n\nworker 和主动压缩在执行入口显式调用 `prepare_agent_runtime_context`，为 Context 追加工作区提示词、按当前用户过滤资源，并在模型为空时补齐系统默认模型。`get_graph(context=...)` 只消费准备后的对象。独立调用同样先创建 `context_schema()`，用 `update_config` 装载持久配置、用 `update` 注入已授权身份和运行覆盖，再 await 准备函数；流和 invoke 接口只接受 `context`，不接收配置字典。\n\n## Context 和配置表单\n\n需要让管理员或用户配置 Agent 行为时，在 `context.py` 扩展 `BaseContext`。字段定义、metadata、权限和保存结构的完整规则由[智能体配置](./agents-config.md#自定义-context-字段)拥有，本页只描述扩展时的代码路径：新增 dataclass 字段后，`get_configurable_items()` 自动把它暴露给 Agent 详情接口和 `AgentRuntimeConfigForm`，保存值进入 `config_json.context`，`get_graph(context)` 消费准备后的 Context。\n\n不要只在前端添加一个字段，也不要把运行期 ID、worker 身份和权限快照暴露成可保存配置。新增字段后，沿 context_schema → get_configurable_items() → 详情接口 → 配置表单 → `config_json.context` → `get_graph(context)` 的链路检查每一环的测试和文档。\n\n## 中间件和工具\n\n资源权限和默认资源选择在 Graph 创建前处理；模型提示注入、工具动态开放、文件结果处理、state 更新和观测才适合放入 middleware。内置 Agent 的工具可见性和执行注册分为两层：工具可以先进入 ToolNode，再由 Skill 激活状态决定是否让模型看到。\n\n优先复用：\n\n- [工具系统](./tools-system.md) 的注册和目标校验；\n- [中间件](./middleware.md) 的装配顺序；\n- [Skills 管理](./skills-management.md) 的依赖和激活规则；\n- [沙盒机制](../mechanisms/sandbox.md) 的文件和命令边界。\n\n新 middleware 不要绕过 `prepare_agent_runtime_context`，也不要用 Prompt、前端隐藏或 schema omission 代替后端授权。\n\n## 检查清单\n\n- `BaseAgent` 子类在 `BUILTIN_BACKENDS` 中通过稳定 ID 显式注册；\n- `context_schema` 的默认值、字段权限和选项能被前端正确渲染；\n- Graph 使用 Yuxi 的模型、工具、文件和 checkpoint 装配入口；\n- 工具副作用在执行处验证用户、路径和资源；\n- 新的模型可见输入、状态、文件或协议有正向和负向测试；\n- 相关 API、机制和用户文档已更新。\n\n## 源码和测试\n\n- [BaseAgent](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/base.py)\n- [Context](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/context.py)\n- [Chatbot graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)\n- [执行后端显式注册](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/__init__.py)\n- [Agent unit tests](https://github.com/xerrors/Yuxi/tree/main/backend/test/unit/agents)\n- [Agent integration/E2E](https://github.com/xerrors/Yuxi/tree/main/backend/test/e2e)\n\n改变持久配置、权限、模型可见输入、Run 生命周期或文件边界时，先按 [Yuxi Spec Loop](../develop-guides/spec-loop.md) 建立相应的决策和验证范围。\n\n<!-- File: docs/agents/agent-evaluation.md (Tokens: ~1227 | Category: subagent-persona) -->\n# 评估智能体\n\n智能体评估用 Langfuse Dataset 保存一组固定任务，再让 Yuxi 按真实的 AgentRun、worker 和工具链路逐条执行。它适合比较一个智能体在研究、编程、文件处理或多步骤任务上的表现。\n\n本页不介绍知识库的 `recall@K` 和答案指标；那部分见[知识库评估](../intro/evaluation.md)。\n\n## 需要准备什么\n\n1. Yuxi 已配置 Langfuse tracing，API/worker 环境包含：\n\n   ```bash\n   LANGFUSE_PUBLIC_KEY=<your-public-key>\n   LANGFUSE_SECRET_KEY=<your-secret-key>\n   LANGFUSE_BASE_URL=https://cloud.langfuse.com\n   ```\n\n2. 本机运行 CLI 时也能读取同一 Langfuse 项目的变量。CLI 会直接读取 Dataset 并创建 experiment。\n3. 已安装 `yuxi-cli`，并登录目标实例：\n\n   ```bash\n   yuxi remote add local http://localhost:5173\n   yuxi login --browser\n   ```\n\n   CI 或没有浏览器时，可以使用 `yuxi login --api-key \"$YUXI_API_KEY\"`。\n4. 目标智能体已经存在，登录用户有权访问它。命令使用 Agent slug，例如 `default-chatbot`。\n\n## 准备 Dataset\n\n在 Langfuse 中创建 Dataset，并为每个 item 的 `input` 提供任务文本：\n\n```json\n{\"input\":\"请整理这份资料，并列出三个需要核实的事实。\"}\n```\n\n也兼容把任务放在 `query`、`question` 或 `prompt` 字段中。`expected_output` 可以保存参考答案，具体评分规则由 Langfuse evaluator 或人工评审负责。\n\nYuxi 不负责创建或上传 Dataset。先在 Langfuse 中检查任务文本、参考输出和数据集版本，再运行实验。\n\n## 运行实验\n\n```bash\nyuxi agent eval \\\n  --dataset-name demo-dataset \\\n  --agent-slug default-chatbot \\\n  --experiment-name default-chatbot-demo \\\n  --max-concurrency 1 \\\n  --timeout-seconds 900\n```\n\nCLI 对每条 item：\n\n1. 从 Dataset 读取任务文本；\n2. 调用 Yuxi 的 `POST /api/agent-invocation/eval/runs`；\n3. 由 Yuxi 创建临时 Conversation 和 AgentRun；\n4. 通过 worker 执行真实智能体；\n5. 等待 Run 进入终态；\n6. 把最终输出写回 Langfuse experiment item。\n\n`--max-concurrency` 是 Dataset 实验的并发数。先从 `1` 开始，再根据模型服务、worker 和沙盒容量提高。`--timeout-seconds` 是每条样例等待 Yuxi 结果的上限；超时会报告当前运行状态，不应把它当作成功。\n\n## 查看结果\n\n实验完成后，在 Langfuse Dataset 的 experiment 中查看每条 item 的最终输出。Yuxi 会在本地运行上下文和 trace 中保存以下标记，便于筛选：\n\n```text\nsource=agent_evaluation\nevaluation_dataset_name=<dataset-name>\nevaluation_dataset_item_id=<item-id>\nevaluation_experiment_name=<experiment-name>\n```\n\n先比较同一数据集的输出，再按自己的评估规则打分。一次实验的输出只代表当时的模型、Agent 配置、工具、知识库和外部服务状态；改变这些条件后，应创建新的 experiment 名称。\n\n## 排查失败\n\n- 没有 experiment：检查 CLI 的 Langfuse 公钥、密钥、地址和 Dataset 名称。\n- experiment 有 item 但 Yuxi 失败：检查 CLI 登录的 API Key、Agent slug，并用 `docker compose logs api worker` 查看当前槽位日志。\n- Trace 缺失：检查 API/worker 是否读取到 Langfuse 配置；Yuxi 业务结果仍以 PostgreSQL 的 Run 和消息为准。\n- 大量超时：降低 `--max-concurrency`，检查模型响应时间、worker 健康状态和沙盒创建耗时。\n- 实验部分成功：不要只看命令退出前的汇总，回到 Langfuse 检查每条 item 是否都有结果；CLI 会在成功写入数量与 Dataset 总数不一致时报告错误。\n\n实现入口见 [Agent Eval 路由](https://github.com/xerrors/Yuxi/blob/main/backend/server/routers/agent_invocation_eval_router.py)、[CLI 实验](https://github.com/xerrors/Yuxi/blob/main/packages/yuxi-cli/src/yuxi_cli/agent_eval.py) 和 [Langfuse 集成](../advanced/langfuse-integration.md)。\n\n<!-- File: docs/agents/agents-config.md (Tokens: ~2375 | Category: subagent-persona) -->\n# 配置智能体\n\n本页是智能体配置参考，说明页面上的字段如何进入一次运行。新增智能体后端的代码结构见[开发智能体后端](./agent-backend-development.md)；只使用现成智能体时，从[快速开始](../intro/quick-start.md)开始。\n\n## 配置模型\n\n| 对象 | 负责什么 |\n| --- | --- |\n| `Agent` | 数据库中的智能体资源，保存名称、slug、共享范围和持久化配置 |\n| `BaseAgent` | 代码中的后端类型，声明 `context_schema` 和 `get_graph()` |\n| `BaseContext` | 配置字段和运行时输入的 Schema |\n| `config_json.context` | 当前 Agent 保存的配置值 |\n| Graph / middleware | 根据 Context 组合模型、工具、文件和扩展能力 |\n\n内置 `ChatbotAgent` 用于普通对话，`SubAgentBackend` 用于被主智能体委派的任务。子智能体的配置入口与普通智能体相同。\n\n## 配置页面从哪里来\n\n`BaseContext` 的 dataclass 字段和 metadata 会生成配置项描述，前端不再维护一份独立字段清单：\n\n```text\nContext 字段\n  → get_configurable_items()\n  → Agent 详情接口\n  → AgentRuntimeConfigForm\n  → config_json.context\n```\n\nmetadata 可以定义展示名称、说明、控件类型、选项和角色权限。运行期 ID、owner 和内部派生值应隐藏，不作为用户配置。\n\n`metadata.auth` 只限制修改权限：`admin` 字段允许管理员和超级管理员修改，`superadmin` 字段只允许超级管理员修改。普通用户读取有权访问的智能体时，可以读到这些已保存值，运行也使用这些值；不可修改的字段不进入其编辑表单，后端会过滤越权提交的字段并保留已保存值。`auth` 不提供字段保密能力，Context 配置不得存放凭据。\n\n例如管理员将 `max_execution_steps` 设置为 50，普通用户运行该智能体时的 `recursion_limit` 也为 50。已经固化运行快照的 Run 保留自己的配置；后续新 Run 读取保存的配置。知识库、Skills 等资源仍按运行用户的访问权限筛选。\n\n## 基础字段\n\n| 字段 | 作用 |\n| --- | --- |\n| `system_prompt` | 智能体角色和行为说明 |\n| `model` | 主模型；留空时使用系统默认模型 |\n| `tool_approval_mode` | `default` 或 `always_trust`；仅管理员可配置 |\n| `tools` | 可使用的内置工具 |\n| `knowledges` | 可检索的知识库范围 |\n| `mcps` | 直接添加到 Agent 的已启用 MCP 服务器 |\n| `skills` | 选择共享和内置 Skill；个人 Skill 始终可用 |\n| `preload_skills` | 从首轮请求加载完整说明和依赖的 Skill 子集 |\n| `summary_threshold`、`summary_keep_messages` | 上下文压缩的唯一压力阈值和摘要后保留消息数 |\n| `summary_prompt`、`summary_tool_result_token_limit` | 摘要提示词和工具结果预览上限 |\n| `max_execution_steps`、`model_retry_times` | 单次运行步数和模型重试次数 |\n\n## 资源选择语义\n\n资源字段 `tools`、`knowledges`、`skills`、`subagents`、`mcps` 和 `preload_skills` 接受 `\"all\"` 或字符串数组，本页是这组契约的唯一 Owner。`\"all\"` 表示每次运行使用当前用户可访问的全部资源，包括之后新增的资源；`[]` 表示不选择；非空数组表示固定范围。新写入拒绝 `null`、未知字符串和非字符串数组成员。\n\n创建时省略字段使用其默认值：工具、知识库和 Skill 为 `\"all\"`，子智能体为 `[\"general-purpose\"]`（通用任务），MCP 和预加载 Skill 为 `[]`。界面的「全部（含新增）」保存 `\"all\"`；逐项勾选保存固定数组，即使勾选了当前所有选项也不会自动转换成全部模式。「清空全部」保存 `[]`，子智能体同样可以全部禁用。\n\n各资源的附加规则：\n\n- MCP 选择只控制直接加载的服务器；已激活 Skill 声明的 MCP 依赖仍按需加载。\n- Skill 选择器只列共享和内置 Skill；运行时自动合并当前用户全部个人 Skill，`skills=[]` 只关闭共享选择。Skill 的激活时机见 [Skills 管理](./skills-management.md)。\n- 预加载候选项限于已选且可访问的共享 Skill；`preload_skills=\"all\"` 在每次运行预加载该范围及其授权依赖的完整说明，并从首轮开放依赖工具。`skills=[]` 时预加载全部仍得到空列表，个人 Skill 继续按需读取正文。\n- 子智能体不能继续调用下一层子智能体。\n\n这些字段只会缩小当前用户已经拥有的权限；运行时如何消费解析结果见 [Agent 运行时上下文](../mechanisms/agent-runtime.md)。\n\n### 共享智能体与编辑保存\n\n共享智能体保存完整的期望选择，每次运行再与当前操作者的可访问资源取交集，运行不会改写保存的选择。例如创建者选择 10 个 Skill，委托管理员只能访问其中 5 个，委托管理员运行时生效 5 个，保存名称或模型后仍保留原有 10 个选择。\n\n编辑页只提交修改过的配置字段；通过 API 更新 `config_json.context` 时，省略字段保留原值。修改非空资源列表时，后端保留当前管理员不可访问的既有引用，并拒绝新增无权访问的引用；保留的引用维持原相对顺序，新选择追加到末尾。「清空全部」移除所有引用（包括不可访问项），切换到「全部（含新增）」则整体替换固定选择范围。界面会显示当前不在可选范围内的既有选择数量。\n\n## 定时运行\n\n智能体支持用户定时任务（Beta）：为智能体配置 5 段 cron 和 IANA 时区后，worker 到期自动在独立 Conversation 中触发一次运行，也支持手动立即运行；停机错过的多个周期合并为一次，已有未完成执行时跳过并记录。任务的定义、触发与恢复边界见 [Agent 运行时上下文](../mechanisms/agent-runtime.md#用户定时-agent)。\n\n## 自定义 Context 字段\n\n需要让用户配置额外行为时，扩展 Context，让后端和前端沿同一 Schema 工作：\n\n```python\nfrom dataclasses import dataclass, field\nfrom yuxi.agents import BaseContext\n\n\n@dataclass(kw_only=True)\nclass MyAgentContext(BaseContext):\n    response_style: str = field(\n        default=\"concise\",\n        metadata={\n            \"name\": \"回答风格\",\n            \"description\": \"控制回答的详细程度\",\n            \"type\": \"string\",\n            \"options\": [\"concise\", \"detailed\"],\n        },\n    )\n```\n\n新增字段会影响保存结构、配置表单和运行期 Context。字段默认值、权限和选项变化时，同时更新相关测试和文档。\n\n## `capabilities`\n\n`capabilities` 是代码声明的静态能力，用于控制固定的前端入口，例如：\n\n```python\nclass MyAgent(BaseAgent):\n    capabilities = [\"file_upload\", \"files\"]\n```\n\n它不保存待办、文件、产物或子智能体状态。运行态来自 LangGraph state 的 `agent_state`；能力声明只表达后端固定支持哪些 UI 入口。\n\n## 运行时入口\n\n配置如何与用户身份、权限快照、Workdir、Memory 和 PostgreSQL checkpoint 组合，见[Agent 运行时上下文](../mechanisms/agent-runtime.md)。工具可见性和执行授权见[工具系统](./tools-system.md)，上下文压缩见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## 相关页面\n\n- [开发智能体后端](./agent-backend-development.md)\n- [中间件](./middleware.md)\n- [Skills 管理](./skills-management.md)\n- [子智能体](./subagents-management.md)\n- [沙盒机制详解](../mechanisms/sandbox.md)\n\n<!-- File: docs/agents/mcp-integration.md (Tokens: ~1372 | Category: subagent-persona) -->\n# 集成 MCP\n\nMCP（Model Context Protocol）让智能体调用外部服务提供的工具。管理员在“扩展 → MCP”中添加远程服务器；Agent 可直接选择服务器，Skill 也可声明激活后加载的依赖。\n\n## 支持的传输方式\n\n| 传输方式 | 适用场景 |\n| --- | --- |\n| `streamable_http` | 新的远程 MCP 服务 |\n| `sse` | 仍提供 SSE 接口的远程服务 |\n\n管理接口只接受 `streamable_http` 和 `sse`。Yuxi 不支持 `stdio`，包括内置 MCP 和直接传入的运行时配置；历史 `stdio` 配置会被禁用，应迁移为远程服务。\n\n## 添加远程 MCP\n\n在“扩展 → MCP”点击“添加 MCP”，填写稳定标识、名称、传输方式和 URL。例如：\n\n```json\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\"\n}\n```\n\n管理接口对应：\n\n```http\nPOST /api/system/mcp-servers\nAuthorization: Bearer <admin-token>\nContent-Type: application/json\n\n{\n  \"slug\": \"custom-remote-mcp\",\n  \"name\": \"Example MCP\",\n  \"transport\": \"streamable_http\",\n  \"url\": \"https://example.com/mcp\",\n  \"description\": \"提供示例查询工具\"\n}\n```\n\n需要认证的远程服务可以配置 HTTP headers、连接超时和 SSE 读取超时。凭证会随着连接请求发送，请只配置必要的 header，并把管理接口限制在可信的管理员范围。\n\n添加后先点击“测试连接”，确认能发现工具，再把服务器状态设为“已添加”。状态关闭时，服务器记录仍保留，但不会进入运行时。\n\n## 让智能体使用 MCP\n\n在智能体配置的 MCP 字段中显式选择要直接添加的服务器。该字段默认不添加服务器：\n\n- 未显式配置或显式清空时，不直接加载 MCP 服务器；\n- 显式选择后，只使用所选且当前可用的服务器；\n- MCP 工具仍会在执行处使用当前用户身份和服务器配置；\n- 管理员可以在 MCP 详情页单独禁用某个工具。\n\n共享 Skill 的 `mcp_dependencies` 声明所需服务器。Skill 激活后按需加载这些服务器的工具；预加载 Skill 从首轮加载，均无需在 Agent 的 MCP 字段重复选择。服务器必须由管理员启用，禁用的服务器不会提供工具。\n\nMCP 配置从 PostgreSQL 读取，工具对象按配置哈希缓存。修改连接配置或工具禁用列表后，下一次运行会使用新的配置键。\n\n## 内置远程 MCP\n\n内置 DeepWiki 使用 `deepwiki-official` 标识，通过 `https://mcp.deepwiki.com/mcp` 提供 Streamable HTTP 服务，无需认证即可查询公开 GitHub 仓库。详见 [DeepWiki 官方文档](https://docs.devin.ai/work-with-devin/deepwiki-mcp)。\n\n开发者在 [`builtin.py`](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/mcp/builtin.py) 的 `BUILTIN_MCP_SERVERS` 中添加固定远程定义：\n\n```python\nBUILTIN_MCP_SERVERS = {\n    \"deepwiki-official\": {\n        \"transport\": \"streamable_http\",\n        \"url\": \"https://mcp.deepwiki.com/mcp\",\n        \"description\": \"查询公开 GitHub 仓库的文档、架构与代码\",\n        \"icon\": \"📚\",\n        \"tags\": [\"内置\", \"代码\", \"文档\"],\n    },\n}\n```\n\nAPI/worker 启动时同步固定定义到数据库；运行时直接读取代码中的连接字段。新内置 MCP 默认未添加，管理员需要启用；连接配置不可通过页面修改。添加定义无需修改服务逻辑或启动入口。\n\n原内置 `mcp-server-chart` 已退役，启动时删除系统创建的记录。其他历史 stdio 记录保留并禁用，管理员可迁移为远程服务或删除。MySQL 报表技能改用 Markdown 表格，不再依赖图表 MCP；自定义角色或 Skill 中对退役 MCP 的引用需要自行调整。\n\n## 常用管理接口\n\n| 方法 | 路径 | 作用 |\n| --- | --- | --- |\n| `GET` | `/api/system/mcp-servers` | 查看服务器；普通用户只得到脱敏基础信息 |\n| `POST` / `PUT` | `/api/system/mcp-servers`、`/{slug}` | 添加或修改远程 MCP |\n| `PUT` | `/api/system/mcp-servers/{slug}/status` | 启用或停用服务器（对应页面上的添加/移除状态） |\n| `POST` | `/api/system/mcp-servers/{slug}/test` | 测试连接并发现工具 |\n| `GET` | `/api/system/mcp-servers/{slug}/tools` | 查看工具 |\n| `PUT` | `/api/system/mcp-servers/{slug}/tools/{tool_name}/toggle` | 启用或禁用单个工具 |\n\n接口字段和错误响应以实例 Swagger 为准。\n\n<!-- File: docs/agents/middleware.md (Tokens: ~2150 | Category: subagent-persona) -->\n# 中间件\n\n中间件把文件、Skills、子智能体、上下文压缩、审批和用量统计接到 LangGraph Agent。它们在模型调用、工具调用或 state 更新的边界运行，让不同 Agent 复用同一套能力。\n\n内置 `ChatbotAgent` 和 `SubAgentBackend` 都在 `get_graph()` 中组装中间件。Graph 创建前，系统先完成用户资源和权限的归一化；中间件不应绕过这一步重新决定授权。\n\n## Graph 创建前的准备\n\n`prepare_agent_runtime_context` 会根据当前用户和 Agent 配置：\n\n- 过滤内置工具、知识库、MCP、Skills 和子智能体；\n- 生成 `_visible_knowledge_bases`；\n- 展开 Skill 依赖，生成 `_effective_skill_slugs` 和 `_runtime_skills`；\n- 使用系统默认模型补齐空的模型配置。\n\n随后，工具解析器准备可执行工具，`build_prompt_with_context` 生成系统提示词，Agent 再创建 Graph。工具执行时仍需检查具体目标，准备阶段的资源快照不是授权替代品。\n\n## 内置中间件顺序\n\n`ChatbotAgent` 的常见顺序如下（[graph.py](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py) 装配）；可选项只在对应能力启用时加入：\n\n| 顺序 | 中间件 | 作用 |\n| --- | --- | --- |\n| 1 | `ToolErrorGuardMiddleware` | 最外层隔离普通工具异常，保留取消与 interrupt 的传播 |\n| 2 | `SteerMiddleware` | 在安全边界发现待接替请求 |\n| 3 | `create_agent_filesystem_middleware` | 提供 Workdir、User Data、Skills 文件后端，并卸载过大的工具结果 |\n| 4 | `SkillsMiddleware` | 注入 Skill 说明，按激活状态开放依赖 |\n| 5 | `YuxiMemoryMiddleware` | Memory 开关开启且 `MEMORY.md` 有内容时，注入用户记忆并提供受限工具 |\n| 6 | `YuxiSubAgentMiddleware` | 主智能体有可见子智能体时提供子智能体生命周期工具 |\n| 7 | `YuxiSummarizationMiddleware` | 先确定性压缩工具结果，仍达到同一阈值时生成摘要 |\n| 8 | `TodoListMiddleware` | 保存待办，供状态面板展示 |\n| 9 | `PatchToolCallsMiddleware` | 修正部分工具调用消息形态 |\n| 10 | `NetworkRetryMiddleware` | 网络错误按预算、其他可重试模型错误按次数重试，耗尽后抛出异常 |\n| 11 | `ImageInputCompatibilityMiddleware` | 桥接工具读取图片与模型输入格式；必要时回退 OCR |\n| 12 | `TokenUsageMiddleware` | 记录近似上下文和主模型实际用量 |\n| 13 | 工具审批 middleware | 默认模式下审批 Project 外的文件写入和命令执行；当前 Project 内的写/编辑自动放行 |\n\n`SubAgentBackend` 复用文件、Skills、Summary、待办、重试和用量等能力，同样以 `ToolErrorGuardMiddleware` 为最外层，但不挂载子智能体 middleware，并过滤不适合子智能体的敏感或交互工具。\n\n模型重试耗尽后，异常进入 Run 失败通道，持久化 `failed` 状态与错误原因；已有部分输出保留错误元数据。\n\n子 Run 的失败通过 `subagent_await` / `subagent_status` 返回给父智能体，由父智能体决定后续处理。最终正常回答仍须满足同 Run 的 model lifecycle 审计关联。\n\n## Skills 和知识库\n\nSkills middleware 将 Skill 说明按模型请求注入：预加载 Skill 从首轮开放工具和 MCP 依赖，普通 Skill 在模型读取对应 `SKILL.md` 后激活，再开放声明的依赖。Agent 显式选择的 MCP 服务器从运行开始提供工具。\n\n知识库能力由内置 `knowledge-base` Skill 提供。它的工具是否注册、模型是否可见、参数是否能访问目标知识库分别由工具组装、Skill 激活和知识库权限检查负责。完整链路见[工具系统](./tools-system.md)和[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n## 文件和附件\n\n附件确认后写入当前 Project Workdir。每次 Run 会把线程历史附件的文件名和实时路径加入本轮用户消息，让模型按需调用 `read_file`；持久化 Message 仍保存原始文本，不会把这段模型专用路径混进用户可见消息。\n\n普通 Agent 和子 Agent 使用根 Conversation 的同一个 `runtime_scope_id` 和 Workdir。子 Agent 的 child thread 只隔离 LangGraph checkpoint，不隔离共享文件。Viewer、附件和 artifact API 直接访问 UserWorkspace 的持久文件，不需要创建 file-bridge Sandbox。\n\n## 子智能体\n\n主智能体配置了可见子智能体时，middleware 提供 `subagent_start`、`subagent_status`、`subagent_cancel` 和 `subagent_await`。`subagent_start` 立即返回子 Run 身份，`subagent_await` 按需等待；查询和取消均按 `run_id` 执行。子智能体使用自己的 Context 和 checkpoint，但继承发起用户的权限、Workdir 和 execution runtime。\n\n详细的调用、busy、结果和文件边界见[子智能体](./subagents-management.md)。\n\n## Summary 上下文压缩\n\nSummary 在文件和 Skills 等中间件之后运行。请求达到唯一压力阈值后，它先为当前模型请求精简工具结果并重新计量；仍达到同一阈值时才写入历史文件、生成摘要并更新 checkpoint。它不会删除 PostgreSQL 聊天消息，也不会把内部摘要模型调用当作用户可见回复。支持主动压缩的 Agent 复用同一摘要器，并通过 service 在空闲线程的 canonical graph state 上更新 checkpoint。\n\n参数和状态细节见[上下文压缩机制](../mechanisms/context-compression.md)。\n\n## Token 用量\n\n`TokenUsageMiddleware` 同时记录近似上下文 token（用于摘要阈值和状态面板）和主 Agent 模型返回的 `usage_metadata`（按最近调用、当前 Run、线程和模型分桶保存）。\n\n`siliconflow-cn` 和 `siliconflow` 当前位于 Provider 用量黑名单：这两个供应商仍可提供近似上下文统计，但不会写入最近调用、Run 或线程的 Provider 实际用量聚合。其他能返回兼容 `usage_metadata` 的供应商才会进入这组实际用量统计；Summary 内部摘要模型调用不计入。\n\nRun 终态时，worker 把与当前 `run_id` 匹配的 state 快照写入 `AgentRun.token_usage`；父 Run 和子 Run 分开保存。\n\n## 新增中间件时\n\n先说明它要改变哪一条边界：Prompt、模型调用、工具调用、文件访问、state 或观测。资源筛选和权限收敛放在 Graph 创建前；文件读写和工具结果卸载优先复用现有 filesystem middleware；新增模型可见输入或副作用时补充对应测试和失败案例。\n\n实现入口：[ChatbotAgent graph](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/chatbot/graph.py)、[中间件目录](https://github.com/xerrors/Yuxi/tree/main/backend/package/yuxi/agents/middlewares)。\n\n<!-- File: docs/agents/skills-management.md (Tokens: ~4067 | Category: subagent-persona) -->\n# 管理 Skills\n\nSkill 是一个可复用的能力包，通常包含一个 `SKILL.md`、提示词、参考资料和可选脚本。智能体先看到 Skill 的描述，再按需要读取 `SKILL.md`；本地工具和 MCP 依赖随 Skill 激活进入模型请求。\n\n## 什么时候用 Skill\n\n把一类稳定、可复用的工作方式放进 Skill，例如文档处理、研究流程、报表生成或某个外部服务的操作规范。单次任务要求写在对话中；需要跨对话复用的规则和资料再考虑 Skill 或用户工作区文件。\n\n## Skill 保存在哪里\n\n| 来源 | 位置和事实 | 谁可以安装/管理 |\n| --- | --- | --- |\n| 内置 | 随代码发布，启动时同步索引 | 管理员可以启用/停用，不能删除或直接编辑 |\n| 共享上传/远程 | Skill 持久目录 + PostgreSQL 索引 | 管理员按共享范围管理 |\n| 个人 | 当前用户 UserWorkspace 的 `agents/skills/<slug>` | 当前用户；不创建共享 Skill 数据库记录 |\n\n运行时路径是：\n\n- 共享和内置 Skill：`/home/gem/skills/<slug>/`，对 Agent 只读；\n- 个人 Skill：`/home/gem/user-data/agents/skills/<slug>/`，位于当前用户的 UserWorkspace。\n\n同一用户有个人 Skill 和共享 Skill 使用同一个 slug 时，个人版本覆盖该用户看到的共享版本。删除个人版本后，如果共享版本仍可访问，会恢复共享版本。\n\n## 创建 Skill\n\n标准目录至少包含根级 `SKILL.md`：\n\n```text\nmy-skill/\n├── SKILL.md\n├── tools/       # 可选脚本\n└── prompts/     # 可选提示词或参考资料\n```\n\n`SKILL.md` 使用 YAML frontmatter：\n\n```markdown\n---\nname: 文档整理\nslug: document-cleanup\ndescription: 按指定结构整理文档，并保留可核对的来源。\ntool_dependencies: []\nmcp_dependencies: []\nskill_dependencies: []\n---\n\n# 文档整理\n\n## 何时使用\n\n说明这个 Skill 解决什么问题，以及什么时候不该使用。\n\n## 操作步骤\n\n写出 Agent 应遵循的步骤、限制和验收方式。\n```\n\n必填字段是 `name` 和 `description`。名称和 `slug` 最多 128 个字符。`slug` 可省略，省略时直接使用 `name`，因此省略 slug 时 `name` 本身也必须是小写字母、数字和单个短横线组成的值，中文或带空格的展示名称会校验失败。\n\n建议显式填写 slug，把自然语言名称和稳定标识分开。\n\n依赖字段含义：\n\n| 字段 | 作用 |\n| --- | --- |\n| `tool_dependencies` | 需要的内置工具 |\n| `mcp_dependencies` | 需要的 MCP 服务器 |\n| `skill_dependencies` | 需要先提供说明的其他 Skill |\n\n个人 Skill 只作为用户文件读取，不解析这些平台依赖；需要依赖工具、MCP 或其他 Skill 时，安装为共享 Skill。\n\n## 安装方式\n\n进入“扩展 → Skills”，可以选择：\n\n1. **推荐 Skill**：从推荐列表生成安装草稿。\n2. **上传**：上传 ZIP 或单个 `SKILL.md`，解析后生成草稿。\n3. **远程安装**：从 GitHub 仓库、ModelScope Skill 或合集拉取一个或多个 Skill。\n4. **在线编辑**：编辑已有且有管理权限的共享 Skill 文件和依赖。\n5. **Agent 内安装**：主智能体使用 `install_skill` 把 Skill 安装到当前用户的个人来源；子智能体不能使用该工具。\n\n上传和远程安装都先解析为草稿，再选择个人或共享位置并确认。草稿中的可安装条目只包含已解析的临时包；远程拉取或解析失败会单独显示，不能被确认安装。确认前可以检查名称、说明、文件和依赖；取消草稿不会写入正式 Skill。\n\n有管理权限的用户可在共享 Skill 详情页编辑文本文件。保存 `SKILL.md` 时，根文件的名称、描述及依赖声明会同步到数据库索引；“配置”中的依赖选择也会回写根文件。每次保存都使用打开文件时取得的修订值；如果其他人已修改该文件，页面会拒绝覆盖并保留当前草稿，用户需核对最新内容后重试。切换文件或离开页面时，未保存的修改会触发确认。共享范围和启停状态仍在配置页分别保存。新的共享内容在后续 Agent Run 准备时生效；已准备 Run 的预加载内容保留原有快照，按需读取的用户投影可随之后的编辑刷新。\n\n### 远程来源限制\n\n管理员在“设置 → 基本设置 → Skill 配置 → 远程来源白名单”中维护 `remote_skill_source_policy.allowed_hosts`。默认允许 `github.com` 和 `modelscope.cn`；主机名必须精确匹配，保存空列表会关闭远程安装。\n\n示例来源：\n\n```text\nanthropics/skills\nhttps://github.com/anthropics/skills\nhttps://modelscope.cn/skills/@anthropics/pdf\nhttps://modelscope.cn/collections/MiniMax/MiniMax-Office-skills\n```\n\n除了按仓库或合集拉取，也可以在远程安装对话框切换到“全局搜索发现”，输入关键词检索 `skills.sh` 上的开源 Skills，再选择结果批量拉取和安装。搜索在一次性 Sandbox 中执行；选中结果后的实际拉取仍经过来源白名单和安装确认流程。\n\nGitHub 的 `owner/repo` 简写会被转换为 HTTPS 地址。远程来源会在不继承全局或用户环境变量的一次性 Sandbox 中下载和提取，系统会拒绝绝对路径和路径穿越，并限制文件数、目录深度和总大小。来源白名单限制产品允许的地址，不是网络出口防火墙。\n\n### 新增内置 Skill\n\n在 `backend/package/yuxi/agents/skills/buildin/<slug>/` 新增目录，至少包含 `SKILL.md`。启动同步按目录名排序发现直接子目录，忽略下划线或点开头的目录；无需修改 Python 注册清单。\n\n`SKILL.md` frontmatter 唯一拥有名称、描述、版本和依赖。`slug` 必须与目录名一致，省略时使用 `name`；`version` 省略时为 `1.0.0`，建议使用引号包裹版本字符串。工具、MCP、Skill 依赖使用本页定义的字段。缺少根文件或元数据不合法时，启动同步明确失败。\n\nAPI/worker 启动时同步文件、元数据和依赖，保留数据库中的启停状态。重启后在“扩展 → Skills”核对新增项的说明和依赖；新增脚本或资源也必须随发行包携带。源码目录与共享投影分别拥有发布内容和安装文件，编辑应落在源码目录。\n\n安装前仍应审查 Skill 的提示词、脚本、依赖和网络行为。不要把数据库密码、云平台密钥或 `SANDBOX_PROVISIONER_TOKEN` 放进 Skill 或 Agent 环境。\n\n## 依赖和加载时机\n\nAgent 的 `skills` 配置语义（`\"all\"`、固定数组与默认值）由[资源选择契约](./agents-config.md)拥有，本页描述激活时机。每个 Run 准备 Context 时，系统按权限解析共享选择，合并当前用户全部个人 Skill，再展开 `skill_dependencies`；主智能体和子智能体使用同一规则。`skills=[]` 时个人 Skill 仍进入模型可见的描述列表，只是没有共享选择。个人 Skill 安装不修改 Agent 配置，新增 Skill 在下一次 Run 准备时自动加入，已准备的运行保留快照。\n\n### 普通渐进加载\n\n1. 创建 Graph 前，模型得到有效 Skill 的名称、描述和 `SKILL.md` 路径。\n2. 模型读取某个可见 Skill 的 `SKILL.md` 后，该 Skill 进入 `activated_skills`。\n3. 后续模型请求加入它声明的本地工具，并加载已启用的 MCP 依赖服务器提供的工具。\n\n模型没有读取的 Skill 的依赖工具继续隐藏。未激活的 Skill 本地工具即使已注册到 ToolNode，也不能被模型调用。\n\n### 预加载\n\nAgent 配置可以用 `preload_skills` 指定少量需要从首轮就可用的 Skill。预加载选择器只列 `skills` 中当前用户可访问的共享和内置 Skill；系统会展开其依赖闭包，读取根级 `SKILL.md`，并从首轮模型请求开放本地工具和已启用的 MCP 依赖。\n\n预加载的根文件缺失或不可读时，Graph 创建会明确失败，不会静默退回渐进加载。默认值为空列表；选择“全部（含新增）”时，每次运行预加载当前 Agent 已选且可访问的共享 Skill 及其授权依赖。同名个人版本覆盖共享版本时读取个人内容；其他个人 Skill 始终可发现，正文按需读取。固定列表和全部模式均遵循[资源选择契约](./agents-config.md)。\n\n## 权限和选择\n\n共享 Skill 使用 `source_type`、`share_config` 和 `enabled` 表达来源、范围和启用状态。范围使用 version 2 的 `read_scope`、`manage_scope`，可以是全局、部门或指定用户；管理范围必须包含在读取范围内。\n\n| 用户 | 可见和使用 | 可管理 |\n| --- | --- | --- |\n| `superadmin` | 全部允许的共享/内置 Skill | 全部非内置 Skill，及内置 Skill 的启停 |\n| `admin` | 命中读取范围且已启用的 Skill | 命中管理范围的非内置 Skill，及内置 Skill 的启停 |\n| 普通用户 | 命中读取范围且已启用的 Skill | 自己拥有的非内置 Skill；新安装的 Skill 固定进入个人来源 |\n\n普通用户安装的新 Skill 固定进入个人来源，不配置共享范围。管理员安装到共享来源时才会写入 PostgreSQL 索引，并可以配置部门或用户范围。扩展管理页展示当前用户可访问的共享和个人 Skill；Agent 配置选项只展示可访问的共享和内置 Skill。后端在保存时校验共享引用，运行时按当前用户身份加载个人目录。\n\nAgent 配置中的 Skill 选择不能扩大用户的文件、知识库或 MCP 权限。\n\n## 内置 Skill 说明\n\n系统启动时会同步仓库内置 Skills，其中两个内置 Skill 有产品级行为约定。`html-preview` 用于在普通 Markdown 难以清晰表达指标、对比、流程、时间线或层级关系时，指导 Agent 输出静态 `html:preview` 围栏；普通 HTML 源码仍使用 `html` 代码块，前端把该围栏清洗后放入 sandboxed iframe 预览。未显式配置 Skills 的 Agent 按现有资源规则自动获得该 Skill；使用显式 Skills 允许列表的 Agent 需要选择 `html-preview`。\n\n内置 `deep-research` 不依赖 `html-preview`。它默认在当前 Workdir 的 `outputs/` 目录生成独立、响应式的 HTML 阅读文档，并通过交付物入口展示；用户明确指定其他格式时除外。宽屏报告可以提供侧栏目录，窄屏隐藏或折叠侧栏；报告可以按内容需要使用外部图片等公开资源，来源以普通链接呈现。\n\n## 运行时文件行为\n\n共享和内置 Skill 投影对 Agent 只读，但沙盒命令仍可能执行其中的脚本。脚本如需写文件，应写入当前 Project Workdir 或 User Data，而不是 Skill 目录。个人 Skill 直接从当前用户工作区读取，不会复制到共享投影。\n\nSkill 的选择影响 Prompt 和工具激活；共享投影按用户授权集合生成，不会因为某个 Agent 选择了 Skill 就改变 Sandbox 身份。路径穿越、符号链接和跨用户访问由文件系统边界拒绝。\n\n## 维护建议\n\n- slug 使用小写字母、数字和单个短横线，尽量短且能表达用途。\n- `description` 先说明能力和适用场景，再写实现细节。\n- 把“何时使用”“不要做什么”“产物放在哪里”和“如何验收”写进 `SKILL.md`。\n- 依赖链保持短小，避免循环依赖；只有真实需要时才声明工具或 MCP 依赖。\n- 脚本按不可信输入处理，不读取或输出运行环境中的秘密。\n- 修改共享 Skill 的依赖、范围或文件后，用一个真实 Agent Run 验证模型可见工具和最终产物。\n\n实现入口见 [包格式解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/package.py)、[安装草稿生命周期](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/draft.py)、[共享 Skill 索引与安装](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/shared.py)、[共享文件编辑](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/edit.py)、[个人 Skill 安装与文件操作](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/personal.py)、[用户投影](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/skills/projection.py)、[运行时解析](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/skills/runtime.py) 和 [Skills middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/skills.py)。\n\n<!-- File: docs/agents/subagents-management.md (Tokens: ~2030 | Category: subagent-persona) -->\n# 使用子智能体\n\n子智能体是一个特殊的 Agent：它仍然是 `agents` 表中的一级智能体，只是标记为 `is_subagent=true`，并使用 `SubAgentBackend`。因此，子智能体和普通智能体共用创建、权限和配置入口。\n\n## 在页面中配置\n\n进入“智能体”，点击“新增智能体”，在后端类型中选择 `SubAgentBackend`。然后像配置普通智能体一样设置名称、提示词、模型、工具、知识库、MCP 和 Skills。\n\n子智能体有三个限制：\n\n- 不会出现在普通聊天的智能体切换列表中；\n- 不能设置为默认智能体；\n- 不能继续调用其他子智能体，因此不会形成孙级调用链。\n\n主智能体在运行配置的“子智能体”字段中选择允许调用的对象：\n\n- 未配置时默认仅选择通用任务（`[\"general-purpose\"]`），运行时仍按当前用户权限过滤；\n- 显式选择 `\"all\"` 时，每次运行选择当前用户可见的全部子智能体；\n- 保存空列表 `[]` 时不启用子智能体；\n- 显式选择后，只允许调用所选项；\n- 每个子智能体使用自己的 `config_json.context`，不会继承主智能体的模型或工具选择；\n- 用户权限变化后，新运行会重新计算可见范围。\n\n## 调用方式\n\n主智能体通过工具调用子智能体，不要通过 Shell、`curl` 或 HTTP API 间接调用。\n\n### 派发任务：`subagent_start`\n\n所有子任务通过 `subagent_start` 派发，立即返回 `run_id`、`thread_id` 和访问链接。需要结果时调用 `subagent_await(run_id)`；即使短任务也先派发再等待。多个独立任务先全部派发，再按需等待。\n\n工具参数：\n\n```python\n{\n    \"description\": \"整理这份文档的三条要点\",\n    \"subagent_slug\": \"general-purpose\",\n    \"thread_id\": null\n}\n```\n\n首次调用不需要 `thread_id`。如果要继续之前的子任务，把上一次结果中的子智能体线程 ID 传回去。\n\n### 查询、等待与取消\n\n生命周期工具统一按 Run 工作：\n\n| 工具 | 作用 |\n| --- | --- |\n| `subagent_start` | 创建并立即返回 `run_id`、`thread_id` |\n| `subagent_status` | 查询运行状态和最近几条进度摘要 |\n| `subagent_await` | 等待终态并取得最终结果；超时返回当前快照 |\n| `subagent_cancel` | 请求取消子智能体运行 |\n\n同一个子智能体线程同时只能有一个运行中的 Run。忙碌时工具返回 `busy`，不会隐藏地把请求排队。终态后的同一 `thread_id` 可以继续创建新的 Run。\n\n所有生命周期工具按 `run_id` 操作，并验证该 Run 由当前父 Run 创建，不能读取或控制其他任务。\n\n## 运行时边界\n\n一次子智能体调用会创建独立的 child checkpoint thread 和 `agent_runs(run_type=subagent)` 记录，同时继承父运行的用户身份和根执行树：\n\n| 资源 | 主智能体 | 子智能体 |\n| --- | --- | --- |\n| LangGraph checkpoint | 当前 `thread_id` | 独立 `child thread_id` |\n| Sandbox runtime | 根 `runtime_scope_id` | 与根运行相同 |\n| Project Workdir | 当前 Project 的 Workdir | 与根 Conversation 绑定的 Project 相同 |\n| UserWorkspace | 当前用户的工作区 | 同一用户的工作区 |\n| 共享/内置 Skills | 当前用户授权的只读投影 | 同一授权投影 |\n\n父子智能体看到的是同一份 Workdir 文件字节，不会通过 checkpoint 复制或合并文件。child thread 只隔离 LangGraph 上下文；它不是文件系统隔离边界。并发写同一路径仍按真实 POSIX 文件结果处理。\n\n子智能体可以使用自己配置的文件工具和知识库范围，但所有资源访问仍以发起用户的后端权限为最终边界。`present_artifacts`、`ask_user_question`、`install_skill` 等不适合子智能体直接使用的工具会被过滤；禁用状态不只在模型可见性上生效——即使模型显式传回被禁用的工具调用，执行层也会返回绑定原调用的错误结果，不会执行该工具。\n\n## API 和数据模型\n\n子智能体沿用普通 Agent 管理 API：\n\n- `GET /api/agent` 默认返回聊天可用的普通 Agent；\n- `GET /api/agent?include_subagents=true` 返回包含子智能体的列表；\n- 创建或更新 `SubAgentBackend` 时，后端会校验 `is_subagent=true`；\n- 详情、更新和删除复用同一套 Agent 权限检查。\n\n运行时，主智能体的 `subagents` 会先收敛为当前用户可见的允许列表。列表非空时挂载子智能体 middleware；子智能体自身不会挂载这一中间件。\n\n## 查看结果\n\n父 state 的 `subagent_runs` 保存子任务身份，页面加载时通过数据库父子关系补齐尚未进入 checkpoint 的记录。状态面板独立订阅各子 Run 的事件流，并从 Run 接口读取当前状态；父 graph 在 `subagent_await` 中等待时，先完成的子任务立即更新。切换会话会关闭订阅，重新打开时回读状态；完成后从持久化消息读取最终结果。Redis 原始事件只供运行基础设施和前端订阅，不作为主智能体的工具结果。\n\n前端最多同时订阅三个子 Run，其余活跃子 Run 每两秒查询状态，避免 HTTP/1.1 的同源连接被长连接占满；连接无事件时每十五秒核对持久状态，断线或查询失败显示重连提示。\n\n## 从 task 工具迁移\n\n历史 `task` 消息仍可查看，新模型不再获得该工具。升级前完成或取消旧版本的活跃 Run，并在智能体管理中将已保存的深度研究 Agent 及自定义提示词中的 `task` 指令改为先 `subagent_start`、再 `subagent_await`；已有配置不会被新的默认提示词覆盖。历史 checkpoint 中尚未执行的 `task` 调用会明确返回未知工具错误，不会被自动重放为新的子任务。父 Run 终态仍按原有策略取消活跃后代，派发与等待分离不改变此边界。\n\n实现入口见 [子智能体 middleware](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/middlewares/subagent_task.py)、[SubAgentBackend](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/buildin/subagent/graph.py) 和 [AgentRun 服务](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/services/agent_run_service.py)。\n\n<!-- File: docs/agents/tools-system.md (Tokens: ~2026 | Category: subagent-persona) -->\n# 工具系统\n\nYuxi 的工具分成三层：内置工具、知识库工具和 MCP 工具。Graph 创建时准备可执行工具，运行时再根据用户权限、Agent 配置和 Skill 激活状态决定模型能看到什么。\n\n## 注册一个内置工具\n\n普通内置工具使用 `@tool` 注册：\n\n```python\nfrom yuxi.agents.toolkits.registry import tool\n\n\n@tool(category=\"buildin\", tags=[\"示例\"], display_name=\"示例工具\")\ndef example_tool(text: str) -> str:\n    \"\"\"返回处理后的文本。\"\"\"\n    return text\n```\n\n- `category` 用于前端分组，常见值是 `buildin`、`knowledge` 和 `debug`；\n- `tags` 用于展示和筛选；\n- `display_name` 是给用户看的名称，工具 ID 是给代码和模型协议使用的稳定名称；\n- 工具模块需要被 `toolkits` 包导入，装饰器才会执行注册。\n\n工具的注册不等于授权。产生文件、网络或数据库副作用的工具必须在执行边界再次校验当前用户和目标资源。\n\n## 当前内置能力\n\n常用内置工具包括：\n\n| 工具 | 作用 |\n| --- | --- |\n| `ask_user_question` | 等待用户回答交互式问题 |\n| `ocr_parse_file` | 把工作区中受支持的 PDF、Office 或图片转换为 Markdown |\n| `present_artifacts` | 展示当前用户可见的文件产物 |\n| `install_skill` | 从允许的沙盒路径或 Git 来源安装个人 Skill；子智能体不可用 |\n| `web_search` | 使用已配置的豆包或 Tavily 搜索网页 |\n\n文件读写和命令执行由 Agent 的 Sandbox backend 提供。`present_artifacts` 推荐展示当前 Project 的 `outputs/` 文件；`large_tool_results` 和会话摘要等内部文件不会作为交付物展示。\n\n图片生成能力由内置 `image-gen` Skill 提供，不再作为独立的 Python 工具注册。具体依赖和文件位置由该 Skill 说明。\n\n## 知识库工具\n\n知识库工具以 `@tool(category=\"knowledge\")` 注册，但不默认出现在模型工具列表。Agent 激活内置 `knowledge-base` Skill 后，Skills middleware 才会向模型开放：\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_kbs` | 列出当前运行可见的知识库 |\n| `query_kb` | 按 `kb_id` 检索片段，返回 `kb_id`、`file_id` 和内容 |\n| `find_kb_document` | 在指定文件中按关键词或正则定位内容 |\n| `open_kb_document` | 按 `file_id` 分段读取解析后的文档 |\n| `get_mindmap` | 读取知识导图 |\n| `search_file` | 按文件名搜索可见知识库中的文件 |\n| `download_kb_file` | 把有权限的原始文件下载到当前 Project 的 `outputs/` |\n\n工具参数中的 `kb_id`、`file_id` 和文件名都会在工具执行处重新检查，不能用模型提示词或 Agent 配置绕过知识库权限。知识库不会挂载为沙盒目录，读取方式见[知识库机制详解](../mechanisms/knowledge-base.md)。\n\n需要在 Python 中直接取得知识库工具时：\n\n```python\nfrom yuxi.agents.toolkits.kbs import get_common_kb_tools\n\nkb_tools = get_common_kb_tools()\n```\n\n返回的具体顺序由函数实现维护，不要把顺序当作协议。\n\n## 内置工具：网页搜索\n\n`web_search` 让 Agent 从外部网页获取实时信息，当前支持豆包和 Tavily 两个供应商。搜索结果属于外部来源，不能自动替代知识库或人工核验；对重要结论应回到原网页核实。\n\n供应商在 API/worker 环境中选择并提供凭证：\n\n```bash\nWEB_SEARCH_PROVIDER=doubao\nDOUBAO_SEARCH_API_KEY=<your-doubao-key>\n```\n\n或 `WEB_SEARCH_PROVIDER=tavily` 加 `TAVILY_API_KEY=<your-tavily-key>`。`WEB_SEARCH_PROVIDER` 只接受这两个值；留空时按已配置的 API Key 自动选择，两个 Key 都配置时优先豆包，没有有效 Key 时不会注册该工具。密钥只放在受保护的环境变量或密钥管理器中，不要写进 Skill、Agent 用户环境、代码或日志。\n\n工具在 API 和 worker 进程加载时注册，修改环境变量后需要 `docker compose up -d --force-recreate api worker`。验证方式：在智能体详情确认工具列表出现「网页搜索」，用一个需要最新信息的问题发起真实对话，并检查工具调用返回的 URL、标题和摘要。没有该工具时检查 `WEB_SEARCH_PROVIDER` 拼写、对应 Key 是否存在以及容器是否已重建。\n\n实现入口：[网页搜索工具](https://github.com/xerrors/Yuxi/blob/main/backend/package/yuxi/agents/toolkits/buildin/tools.py)。\n\n## 工具组装流程\n\n内置 Agent 创建 Graph 时执行：\n\n1. `prepare_agent_runtime_context` 按当前用户权限过滤工具、知识库、MCP、Skills 和子智能体。\n2. `resolve_configured_runtime_tools(context)` 注册 Agent 配置和可见 Skill 依赖的可执行本地工具，并加载配置的 MCP 工具。\n3. `SkillsMiddleware` 根据当前已预加载或已激活的 Skill，向模型请求开放相应工具 schema。\n4. 工具执行器再次检查具体文件、知识库、MCP 和用户身份。\n\n```text\nAgent 配置 + 用户权限\n        ↓\n运行时资源快照\n        ↓\n可执行工具注册 ──→ Skill 激活门控 ──→ 模型可见工具\n        ↓\n执行处的目标授权\n```\n\n“可执行”与“模型可见”是两件事：工具可以先注册到 ToolNode，等 Skill 激活后才对模型开放；模型看见某个工具也不代表它可以访问任意资源。\n\n## MCP 和 Skills\n\nMCP 工具可由 Agent 直接选择的已启用服务器提供，也可由已激活 Skill 的 `mcp_dependencies` 按需加载；预加载 Skill 从首轮开放本地工具和 MCP 依赖，普通 Skill 在读取 `SKILL.md` 激活后开放这些依赖。这一节只回答“工具从哪里来、谁决定模型可见”；服务器配置和启停归 [MCP 集成](./mcp-integration.md)，Skill 的创建、依赖与激活时机归 [Skills 管理](./skills-management.md)，资源选择的字段语义归[智能体配置](./agents-config.md)。\n\n## 新增工具时检查\n\n- 工具是否属于内置工具、知识库工具、MCP 还是 Skill；\n- 是否需要在 `ToolNode` 注册，是否需要 Skill 激活后才让模型看见；\n- 产生副作用时，执行处是否有用户、路径和资源权限校验；\n- 错误是否以结构化结果返回，并保留可排查信息；\n- 前端展示名称是否来自稳定元数据；\n- 是否补充纯逻辑和真实 HTTP/文件链路的相应测试。\n\n<!-- File: packages/yuxi-cli/README.md (Tokens: ~173 | Category: plugin-manifest) -->\n# yuxi-cli\n\nYuxi command line client.\n\nFirst-stage scope:\n\n- remote management through `~/.yuxi/config.toml`\n- browser login\n- API Key import through `--api-key`\n- `whoami`, `status`, and `logout`\n- server discovery and compatibility check for Yuxi `>=0.7.1`\n- `yuxi chat` for a temporary local browser chat with streamed Agent output; `/state` reads thread state and `/approve` resumes a pending tool approval\n- `yuxi agent list` and `yuxi agent show <slug>` for inspecting agents visible to the logged-in user\n- `yuxi kb upload` for knowledge base file uploads\n- `yuxi agent eval` for running existing Langfuse dataset experiments with a logged-in remote\n</yakaai_skills>"}