فهرست منبع

docs: recreate CLAUDE.md with pi-agent tool design rules

Key additions:
- schema 即文档: TypeBox description directly informs the LLM
- Union+Literal for enums: model sees const constraints in JSON Schema
- No hardcoded pipeline params
- Return values must be actionable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fanghanjun 6 ماه پیش
والد
کامیت
c21e2995e2
3فایلهای تغییر یافته به همراه81 افزوده شده و 9 حذف شده
  1. 52 0
      CLAUDE.md
  2. 22 4
      docs/plans/2026-04-15-sub-agent-params-alignment-plan.md
  3. 7 5
      docs/plans/2026-04-15-sub-agent-params-alignment.md

+ 52 - 0
CLAUDE.md

@@ -0,0 +1,52 @@
+# InkOS
+
+AI 长篇小说写作平台。monorepo: `packages/core`(运行时)、`packages/cli`(TUI)、`packages/studio`(Web UI / Hono + Vite)。
+
+## 常用命令
+
+```bash
+pnpm dev              # 从根目录启动!core tsc --watch + studio Vite HMR + API server
+pnpm build            # 全量构建
+pnpm test             # vitest 全仓测试
+pnpm --filter @actalk/inkos-core build     # 改了 core 后必须重建(或用 pnpm dev 自动 watch)
+```
+
+## 设计文档
+
+| 文档 | 范围 | 何时读 |
+|------|------|--------|
+| `docs/infra/studio-state-management.md` | Studio Zustand store 架构、slice 约定、目录结构 | 修改 `packages/studio/src/store/` 时 |
+| `docs/infra/studio-routing-and-session.md` | URL hash 路由 + 消息隔离 + pi-ai/pi-agent 集成 | 修改路由、session、LLM provider 时 |
+| `docs/infra/studio-service-management.md` | 服务商管理页设计 | 修改 ConfigView / 服务商配置时 |
+| `docs/plans/2026-04-15-sub-agent-params-alignment.md` | sub_agent 工具参数对齐设计 | 修改 agent-tools.ts / sub_agent 工具时 |
+
+## 工作规范
+
+- **不要推测错误** — 遇到报错先读完整错误信息,定位根因后再修,不凭猜测改代码
+- **原子化提交** — 每个 commit 只做一件事,功能和修复分开提交;不要把不相关的改动塞进同一个 commit
+- **每步都跑测试** — 每个 task 完成后必须 `pnpm test` 全量通过,不能攒到最后才跑
+- **防止 maxTokens 回归** — 替换 LLM provider 时必须确认 maxTokens 参数正确传递,不能丢失或硬编码;写测试验证
+
+## pi-agent 工具设计规范
+
+修改 `packages/core/src/agent/agent-tools.ts` 时必须遵守:
+
+- **schema 即文档** — TypeBox schema 的 `description` 直接传给 LLM,模型靠它理解参数含义;每个字段的 description 必须标注适用的 agent(如 `"reviser only: revision mode"`)
+- **枚举用 Union+Literal** — 有限合法值用 `Type.Union([Type.Literal("a"), Type.Literal("b")])` 而非 `Type.String()`,模型能在 JSON Schema 里看到 `const` 约束,不会传错
+- **开放文本用 String** — 书名、题材等无法穷举的值用 `Type.String({ description: "..." })`
+- **Optional 包裹可选字段** — `Type.Optional(...)` 标记非必填参数
+- **不硬编码 pipeline 参数** — sub_agent 的参数必须与 pipeline runner 方法签名对齐,不能在 execute 里写死值(如 `genre: "general"`)
+- **返回值要可操作** — agent 收到的返回文本必须包含足够信息让它决定下一步(如 auditor 返回 issue 列表而非只返回数量)
+- **错误抛异常** — execute 里不要 catch-and-encode 错误到返回值,throw 由 agent loop 统一处理
+
+## 关键路径
+
+- **Agent 工具**: `core/src/agent/agent-tools.ts` → `SubAgentParams` schema → `createSubAgentTool` → 调用 `PipelineRunner` 方法
+- **建书流程**: `core/interaction/project-tools.ts` → `CREATE_BOOK_TOOL` + `chatWithTools` → Studio `ChatPage` → `BookFormCard`
+- **Studio 入口**: `studio/src/App.tsx` → route 驱动页面切换
+- **状态管理**: Zustand store,LobeHub slice 约定
+  - `studio/src/store/chat/` — 对话消息、创建流程、侧边栏状态
+  - `studio/src/store/service/` — 服务商连接状态、模型列表缓存、model picker 三态(loading/no-models/ready)
+  - **原则**: Zustand selector 只返回 store 中已有的原始值。如果需要 `.filter()` / `.map()` 等产生新数组的派生,用组件内 `useMemo` 而不是 store selector — 否则会造成无限渲染循环
+- **SSE 事件**: `studio/src/hooks/use-sse.ts`(共享 buffer)+ 组件内直连 EventSource(流式渲染)
+- **服务商 preset**: `core/src/llm/service-presets.ts` → baseUrl/api/knownModels/modelsBaseUrl;Anthropic 兼容端点用于 MiniMax 和百炼

+ 22 - 4
docs/plans/2026-04-15-sub-agent-params-alignment-plan.md

@@ -95,14 +95,32 @@ const SubAgentParams = Type.Object({
   // -- architect params --
   title: Type.Optional(Type.String({ description: "architect only: book title" })),
   genre: Type.Optional(Type.String({ description: "architect only: genre (xuanhuan, urban, mystery, romance, scifi, fantasy, wuxia, general, etc.)" })),
-  platform: Type.Optional(Type.String({ description: "architect only: target platform (tomato, qidian, feilu, other). Default: other" })),
-  language: Type.Optional(Type.String({ description: "architect only: writing language (zh/en). Default: zh" })),
+  platform: Type.Optional(Type.Union([
+    Type.Literal("tomato"),
+    Type.Literal("qidian"),
+    Type.Literal("feilu"),
+    Type.Literal("other"),
+  ], { description: "architect only: target platform. Default: other" })),
+  language: Type.Optional(Type.Union([
+    Type.Literal("zh"),
+    Type.Literal("en"),
+  ], { description: "architect only: writing language. Default: zh" })),
   targetChapters: Type.Optional(Type.Number({ description: "architect only: total chapter count. Default: 200" })),
   chapterWordCount: Type.Optional(Type.Number({ description: "architect/writer: words per chapter. Default: 3000" })),
   // -- reviser params --
-  mode: Type.Optional(Type.String({ description: "reviser only: revision mode (spot-fix, polish, rewrite, rework, anti-detect). Default: spot-fix" })),
+  mode: Type.Optional(Type.Union([
+    Type.Literal("spot-fix"),
+    Type.Literal("polish"),
+    Type.Literal("rewrite"),
+    Type.Literal("rework"),
+    Type.Literal("anti-detect"),
+  ], { description: "reviser only: revision mode. Default: spot-fix" })),
   // -- exporter params --
-  format: Type.Optional(Type.String({ description: "exporter only: export format (txt, md, epub). Default: txt" })),
+  format: Type.Optional(Type.Union([
+    Type.Literal("txt"),
+    Type.Literal("md"),
+    Type.Literal("epub"),
+  ], { description: "exporter only: export format. Default: txt" })),
   approvedOnly: Type.Optional(Type.Boolean({ description: "exporter only: export only approved chapters. Default: false" })),
 });
 ```

+ 7 - 5
docs/plans/2026-04-15-sub-agent-params-alignment.md

@@ -25,15 +25,17 @@
 | 字段 | 类型 | description | 适用 agent |
 |------|------|-------------|-----------|
 | `title` | `String` | architect only: 书名 | architect |
-| `genre` | `String` | architect only: 题材 (xuanhuan, urban, mystery, romance, scifi, fantasy, wuxia, general 等) | architect |
-| `platform` | `String` | architect only: 目标平台 (tomato, qidian, feilu, other) | architect |
-| `language` | `String` | architect only: 写作语言 (zh/en),默认 zh | architect |
+| `genre` | `String` | architect only: 题材 (xuanhuan, urban, mystery 等) | architect |
+| `platform` | `Union(Literal)` | architect only: 目标平台 (tomato/qidian/feilu/other) | architect |
+| `language` | `Union(Literal)` | architect only: 写作语言 (zh/en) | architect |
 | `targetChapters` | `Number` | architect only: 目标总章数,默认 200 | architect |
 | `chapterWordCount` | `Number` | architect/writer: 每章目标字数,默认 3000 | architect, writer |
-| `mode` | `String` | reviser only: 修订模式 (spot-fix/polish/rewrite/rework/anti-detect),默认 spot-fix | reviser |
-| `format` | `String` | exporter only: 导出格式 (txt/md/epub),默认 txt | exporter |
+| `mode` | `Union(Literal)` | reviser only: 修订模式 (spot-fix/polish/rewrite/rework/anti-detect) | reviser |
+| `format` | `Union(Literal)` | exporter only: 导出格式 (txt/md/epub) | exporter |
 | `approvedOnly` | `Boolean` | exporter only: 仅导出已审核通过的章节 | exporter |
 
+> **设计原则**:有限枚举值用 `Type.Union([Type.Literal(...)])` 而非 `Type.String`,模型能在 JSON Schema 里看到合法值列表(`const` 约束),不会传错。`genre` 和 `title` 是开放文本保持 `String`。
+
 ## 各 agent 行为变更
 
 ### 1. architect