fix(portal-auth): 修复 dev mode JWT 验证误激活 + README 文档同步
## Portal 401 根因修复 问题:AGENTKIT_JWT_SECRET 未设置时,jwt_utils 生成 ephemeral 非空 secret, 该 secret 被传给 AuthMiddleware 后 _is_dev_mode() 返回 False(not "" = False), 导致无 JWT/API key 的请求被拒为 401(17 个 portal 测试失败)。 修复:分离 explicit_jwt_secret 与 jwt_secret — - explicit_jwt_secret = get_jwt_secret() # None when env unset - jwt_secret = explicit_jwt_secret or get_or_create_jwt_secret() # for signing - AuthMiddleware(jwt_secret=explicit_jwt_secret or "") # only explicit activates JWT verify ephemeral secret 仅供 token 签名 routes,不激活 middleware 的 JWT 验证。 生产环境(AGENTKIT_JWT_SECRET 已设置)行为不变。 验证: - _is_dev_mode(): False → True - GET /api/v1/portal/conversations: 401 → 200 - 27 个 portal 测试全部通过(之前 17 失败) - 232 个测试通过 (portal + auth + calendar),0 失败 ## README 文档同步 代码中 CostAwareRouter / RegexRules / HeuristicClassifier / SemanticRouter / LLMClassifier 类已完全删除,仅 RequestPreprocessor 存在。README.md 6 处过时引用同步: - 第 4 节"意图路由"改为引用 RequestPreprocessor(详见第 7 节) - 第 7 节重写为"请求预处理(RequestPreprocessor)",按 AGENTS.md 架构描述 - 第 8 节"语义路由"删除(合并入第 7 节历史说明) - 架构图 CostAwareRouter → RequestPreprocessor,22→28 路由模块 - 模块详解 chat/skill_routing + chat/semantic_router 合并为 chat/request_preprocessor - 模块详解 router/intent 描述更新为"未接入 chat 流程" - 目录注释 CostAwareRouter → RequestPreprocessor - 章节重新编号 1-16 连续(原 1-17 跳过 9)
This commit is contained in:
parent
c9ce15fa4b
commit
d27681a93c
89
README.md
89
README.md
|
|
@ -42,7 +42,7 @@ Skill = SkillConfig + 绑定 Tools。一个 Skill 代表一个可执行技能,
|
|||
|
||||
### 4. 意图路由
|
||||
|
||||
两级路由:Level 1 关键词匹配(零成本,~0ms),Level 2 LLM 分类(回退方案,~200 tokens)。自动将用户输入路由到最佳匹配的 Skill。
|
||||
请求预处理(`RequestPreprocessor`,详见第 7 节)按前缀分流:`@skill:xxx` 显式选技能、琐碎输入走 `DIRECT_CHAT`、其余走 `REACT`。旧的 3 层 `CostAwareRouter`(含 `RegexRules` / `HeuristicClassifier` / `SemanticRouter` / `Vickrey Auction`)已被 `RequestPreprocessor` 替换;`IntentRouter`(`router/intent.py`)存在但未接入 chat 流程。
|
||||
|
||||
### 5. 记忆系统
|
||||
|
||||
|
|
@ -70,28 +70,21 @@ Skill = SkillConfig + 绑定 Tools。一个 Skill 代表一个可执行技能,
|
|||
- **Prompt 优化** -- 遗传算法 + A/B 测试自动优化 Prompt
|
||||
- **路径优化** -- 分析工具调用路径,推荐更优执行策略
|
||||
|
||||
### 7. 三层意图路由
|
||||
### 7. 请求预处理(RequestPreprocessor)
|
||||
|
||||
CostAwareRouter 三层路由,从零成本到高成本逐层升级:
|
||||
`RequestPreprocessor`(`chat/request_preprocessor.py`)按前缀分流,零成本路由:
|
||||
|
||||
| Layer | 方法 | 延迟 | Token 消耗 | 说明 |
|
||||
|-------|------|------|-----------|------|
|
||||
| 0 | 正则规则 | ~0ms | 0 | 问候/简单对话/@team/@skill 前缀直接回复 |
|
||||
| 1 | 启发式分类 | ~0ms | 0 | 关键词 + 模式匹配 + 复杂度评估 |
|
||||
| 1.5 | 语义路由 | ~0ms | 0 | 向量相似度匹配(可选) |
|
||||
| 2 | LLM 分类 | ~500ms | ~200 | 回退方案,LLM 判断意图 |
|
||||
| Layer | 触发条件 | 延迟 | Token | 路由结果 |
|
||||
|-------|---------|------|-------|---------|
|
||||
| 0 | `@skill:xxx` 前缀 | ~0ms | 0 | 显式技能选择(`SKILL_REACT` 或技能配置的模式) |
|
||||
| 1 | 琐碎输入正则(问候/身份/事实/数学/翻译) | ~0ms | 0 | `DIRECT_CHAT`(由 `_TOOL_CONTEXT_RE` 守护) |
|
||||
| 默认 | 其他输入 | ~0ms | 0 | `REACT`(LLM 在 agent 循环中自主决定工具使用) |
|
||||
|
||||
路由结果携带 `ExecutionMode` 枚举(`DIRECT_CHAT` / `REACT` / `SKILL_REACT` / `TEAM_COLLAB`),作为路由层与执行层的架构契约,杜绝硬编码。
|
||||
`@board` 前缀走 `BoardRouter`(多轮讨论),`@team` 前缀走 `ExpertTeamRouter`(流水线协作),均在 `RequestPreprocessor` 之前分流。
|
||||
|
||||
### 8. 语义路由
|
||||
路由结果携带 `ExecutionMode` 枚举(`DIRECT_CHAT` / `REACT` / `SKILL_REACT` / `REWOO` / `REFLEXION` / `PLAN_EXEC` / `TEAM_COLLAB`),作为路由层与执行层的架构契约,杜绝硬编码。
|
||||
|
||||
基于向量相似度的意图路由,作为关键词匹配的补充:
|
||||
|
||||
- **SemanticRouter** -- 将用户输入和 Skill 描述向量化,通过余弦相似度匹配
|
||||
- **缓存友好** -- 向量缓存避免重复计算
|
||||
- **平滑降级** -- 语义路由失败时自动回退到启发式/LLM 分类
|
||||
|
||||
### 9. LLM 响应缓存
|
||||
### 8. LLM 响应缓存
|
||||
|
||||
语义相似度缓存,减少重复 LLM 调用:
|
||||
|
||||
|
|
@ -99,7 +92,7 @@ CostAwareRouter 三层路由,从零成本到高成本逐层升级:
|
|||
- **语义匹配** -- 相似 prompt 命中缓存,避免重复调用
|
||||
- **TTL 管理** -- 缓存条目自动过期,支持手动失效
|
||||
|
||||
### 10. 级联检测与状态持久化
|
||||
### 9. 级联检测与状态持久化
|
||||
|
||||
生产级故障防护:
|
||||
|
||||
|
|
@ -108,15 +101,15 @@ CostAwareRouter 三层路由,从零成本到高成本逐层升级:
|
|||
- **session_ttl** -- 可配置的会话 TTL,自动清理过期状态
|
||||
- **优雅降级** -- Redis 不可用时自动降级到 InMemory,保持服务可用
|
||||
|
||||
### 11. 产出质量管理
|
||||
### 10. 产出质量管理
|
||||
|
||||
四维质量检查:必填字段、最低字数、JSON Schema 校验、自定义验证器。检查不通过时自动重试(可配置 max_retries),重试时携带质量反馈信息。
|
||||
|
||||
### 12. 标准化输出
|
||||
### 11. 标准化输出
|
||||
|
||||
Schema 验证 + 字段类型归一化(str -> int/float/bool)+ 元数据附加(version、produced_at、quality_score)。所有 Skill 产出统一为 StandardOutput 格式。
|
||||
|
||||
### 13. 内置工具集
|
||||
### 12. 内置工具集
|
||||
|
||||
开箱即用的工具插件,覆盖常见 Agent 需求:
|
||||
|
||||
|
|
@ -136,7 +129,7 @@ Schema 验证 + 字段类型归一化(str -> int/float/bool)+ 元数据附
|
|||
|
||||
工具组合:`SequentialChain`(顺序链)、`ParallelFanOut`(并行扇出)、`DynamicSelector`(动态选择)。
|
||||
|
||||
### 14. Pipeline 编排
|
||||
### 13. Pipeline 编排
|
||||
|
||||
多 Agent 协同编排,支持复杂工作流:
|
||||
|
||||
|
|
@ -146,7 +139,7 @@ Schema 验证 + 字段类型归一化(str -> int/float/bool)+ 元数据附
|
|||
- **PipelineReflector** -- 执行反思与重规划
|
||||
- **HandoffManager** -- Agent 间任务移交
|
||||
|
||||
### 15. Expert Team Mode
|
||||
### 14. Expert Team Mode
|
||||
|
||||
多专家协作执行复杂任务,B+C 混合模式(结构化协作计划 + 去中心化执行),前端以多角色对话流呈现:
|
||||
|
||||
|
|
@ -217,7 +210,7 @@ result = await orchestrator.execute_plan(plan)
|
|||
|
||||
用户也可在聊天中通过 `@team:researcher,writer,reviewer 任务描述` 前缀触发团队模式。
|
||||
|
||||
### 16. 企业级客户端-服务端架构
|
||||
### 15. 企业级客户端-服务端架构
|
||||
|
||||
将 AgentKit 从纯本地运行架构演进为企业级客户端+服务端架构。客户端(Tauri 桌面端)作为 AI 工作台本地执行 Agent/终端/文件操作,服务端作为企业平台提供 LLM 网关(统一 Key 管理)、用户权限、审计日志、知识库共享能力。
|
||||
|
||||
|
|
@ -298,7 +291,7 @@ provider = RemoteLLMProvider(
|
|||
response = await provider.chat(request)
|
||||
```
|
||||
|
||||
### 17. 文档处理能力
|
||||
### 16. 文档处理能力
|
||||
|
||||
Agent 内置文档生成与读取能力,Agent 通过 `DocumentTool` 自主创建 Word/Excel/PDF 文档、填充 Word 模板、读取多格式文档,无需用户手动操作 Office 软件。
|
||||
|
||||
|
|
@ -402,15 +395,16 @@ result = await tool.execute(
|
|||
│ portal.py · chat.py · evolution.py · workflows.py │
|
||||
│ auth.py · terminal_server.py · terminal_whitelist.py │
|
||||
│ llm_gateway.py · config_sync.py · system.py · ... │
|
||||
│ 22个路由模块 · Agent Pool · Expert Team · Memory Store │
|
||||
│ 28个路由模块 · Agent Pool · Expert Team · Memory Store │
|
||||
└──────────────────────────┼───────────────────────────────────┘
|
||||
│
|
||||
┌──────────────┼──────────────┐
|
||||
│ CostAwareRouter │
|
||||
│ Layer 0: 正则规则 (0ms) │
|
||||
│ Layer 1: 启发式分类 (0ms) │
|
||||
│ Layer 1.5: 语义路由 (可选) │
|
||||
│ Layer 2: LLM分类 (~500ms) │
|
||||
│ RequestPreprocessor │
|
||||
│ @board → BoardRouter │
|
||||
│ @team → ExpertTeamRouter │
|
||||
│ Layer 0: @skill:xxx 前缀 │
|
||||
│ Layer 1: 琐碎输入正则 (0ms) │
|
||||
│ 默认: REACT (LLM 自主决策) │
|
||||
│ → ExecutionMode 枚举契约 │
|
||||
└──────┬───────────────┬───────┘
|
||||
│ │
|
||||
|
|
@ -1220,26 +1214,19 @@ ReActEngine 实现 Think -> Act -> Observe 循环:
|
|||
|
||||
危险工具确认流:非白名单命令触发 `needs_confirmation`,用户确认后以 `_skip_dangerous_check=True` 重新执行,避免无限循环。
|
||||
|
||||
### chat/skill_routing -- CostAwareRouter 三层路由
|
||||
### chat/request_preprocessor -- 请求预处理
|
||||
|
||||
三层路由从零成本到高成本逐层升级:
|
||||
按前缀分流的零成本路由:
|
||||
|
||||
| Layer | 组件 | 延迟 | Token |
|
||||
|-------|------|------|-------|
|
||||
| 0 | `RegexRules` | ~0ms | 0 |
|
||||
| 1 | `HeuristicClassifier` | ~0ms | 0 |
|
||||
| 1.5 | `SemanticRouter` | ~0ms | 0 |
|
||||
| 2 | `LLMClassifier` | ~500ms | ~200 |
|
||||
| Layer | 触发条件 | 延迟 | Token | 路由结果 |
|
||||
|-------|---------|------|-------|---------|
|
||||
| 0 | `@skill:xxx` 前缀 | ~0ms | 0 | 显式技能选择(`SKILL_REACT` 或技能配置的模式) |
|
||||
| 1 | 琐碎输入正则 | ~0ms | 0 | `DIRECT_CHAT`(由 `_TOOL_CONTEXT_RE` 守护) |
|
||||
| 默认 | 其他输入 | ~0ms | 0 | `REACT`(LLM 自主决策) |
|
||||
|
||||
路由结果包含 `ExecutionMode` 枚举(`DIRECT_CHAT` / `REACT` / `SKILL_REACT` / `TEAM_COLLAB`),作为路由层与执行层的架构契约。`complexity` 评分使用 `if is not None` 判断,避免 `0.0 or default` 误覆盖。`@team:expert1,expert2` 前缀直接路由到 `TEAM_COLLAB` 模式。
|
||||
路由结果包含 `ExecutionMode` 枚举(`DIRECT_CHAT` / `REACT` / `SKILL_REACT` / `REWOO` / `REFLEXION` / `PLAN_EXEC` / `TEAM_COLLAB`),作为路由层与执行层的架构契约。`@team:expert1,expert2` 前缀直接路由到 `TEAM_COLLAB` 模式(由 `ExpertTeamRouter` 处理),`@board` 前缀走 `BoardRouter`(多轮讨论)。
|
||||
|
||||
### chat/semantic_router -- 语义路由
|
||||
|
||||
基于向量相似度的意图路由,作为关键词匹配的补充:
|
||||
|
||||
- **SemanticRouter** -- 将用户输入和 Skill 描述向量化,通过余弦相似度匹配
|
||||
- **缓存友好** -- 向量缓存避免重复计算
|
||||
- **平滑降级** -- 语义路由失败时自动回退到启发式/LLM 分类
|
||||
旧的 3 层 `CostAwareRouter`(含 `RegexRules` / `HeuristicClassifier` / `SemanticRouter` / `Vickrey Auction`)已被 `RequestPreprocessor` 替换;`IntentRouter`(`router/intent.py`)存在但未接入 chat 流程;`AuctionHouse`(Vickrey 拍卖)位于 `marketplace/auction.py`(属于 marketplace 子系统,非路由)。
|
||||
|
||||
### llm/gateway -- LLM Gateway
|
||||
|
||||
|
|
@ -1280,9 +1267,9 @@ SkillRegistry 管理 Skill 的注册、发现、更新。
|
|||
|
||||
团队生命周期:FORMING -> PLANNING -> EXECUTING -> SYNTHESIZING -> COMPLETED。失败时自动回退到单 Agent 模式(lead 或首个活跃专家)。
|
||||
|
||||
### router/intent -- 意图路由(已升级为 chat/skill_routing)
|
||||
### router/intent -- 意图路由(未接入 chat 流程)
|
||||
|
||||
原两级路由已升级为 CostAwareRouter 三层路由(详见 chat/skill_routing 模块详解)。
|
||||
`IntentRouter` 存在但未接入 chat 流程;当前 chat 请求由 `RequestPreprocessor`(详见 `chat/request_preprocessor` 模块详解)处理。本模块保留供未来扩展。
|
||||
|
||||
### quality/gate -- 产出质量管理
|
||||
|
||||
|
|
@ -1613,7 +1600,7 @@ async def generate_content(keyword: str, brand: str) -> dict:
|
|||
fischer-agentkit/
|
||||
├── src/agentkit/ # Python 后端
|
||||
│ ├── bus/ # 消息总线(MemoryBus + RedisBus)
|
||||
│ ├── chat/ # 聊天路由(CostAwareRouter + ExecutionMode)
|
||||
│ ├── chat/ # 聊天路由(RequestPreprocessor + ExecutionMode)
|
||||
│ ├── cli/ # CLI 命令(Typer)
|
||||
│ ├── client/ # 客户端 SDK(ConfigSync + RemoteLLMProvider 集成)
|
||||
│ ├── core/ # 核心引擎(ReAct/Reflexion/ReWOO/ConfigDriven + HandoffTransport)
|
||||
|
|
|
|||
|
|
@ -800,7 +800,17 @@ def create_app(
|
|||
# that login (signing), whoami (verifying), and the middleware all use
|
||||
# the same secret. Without this, get_or_create_jwt_secret() would mint
|
||||
# a different random secret on every call and tokens could never verify.
|
||||
jwt_secret = get_jwt_secret() or get_or_create_jwt_secret()
|
||||
#
|
||||
# ponytail: ephemeral secret is for token-signing only — passing it to
|
||||
# AuthMiddleware would activate JWT verification in dev mode, breaking
|
||||
# _is_dev_mode() and rejecting unauthenticated requests with 401 (root
|
||||
# cause of test_portal_routes 17 failures). Only explicit AGENTKIT_JWT_SECRET
|
||||
# activates middleware JWT verification; ephemeral stays on app.state for
|
||||
# signing routes (auth.py). Ceiling: dev tokens signed with ephemeral
|
||||
# secret still verify if a client somehow obtains them, but ephemeral
|
||||
# secret is non-persistent and invalidated on restart by design.
|
||||
explicit_jwt_secret = get_jwt_secret() # None when AGENTKIT_JWT_SECRET unset
|
||||
jwt_secret = explicit_jwt_secret or get_or_create_jwt_secret() # for signing
|
||||
client_keys: dict[str, str] = {}
|
||||
try:
|
||||
from agentkit.server.middleware import _load_client_keys
|
||||
|
|
@ -811,7 +821,7 @@ def create_app(
|
|||
|
||||
app.add_middleware(
|
||||
AuthMiddleware,
|
||||
jwt_secret=jwt_secret or "",
|
||||
jwt_secret=explicit_jwt_secret or "", # only explicit secret activates JWT verify
|
||||
api_key=effective_api_key,
|
||||
client_keys=client_keys,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue