Przeglądaj źródła

fix: type error in knowhub frontend

Talegorithm 3 dni temu
rodzic
commit
0bd786e9d7

+ 0 - 234
.refactor-knowledge-complete.md

@@ -1,234 +0,0 @@
-# 知识管理系统重构完成报告
-
-## 重构日期
-2026-03-05
-
-## 核心改动
-
-### 1. 数据结构调整
-
-按照 `agent/docs/knowledge.md` 定义,完成以下字段调整:
-
-**旧结构 → 新结构:**
-- `scenario` → `task`(任务描述)
-- `tags_type` → `types`(支持多选数组)
-- 新增 `tags`(JSON 对象,业务标签)
-- 新增 `scopes`(数组,可见范围)
-- 新增 `owner`(所有者)
-- `source_*` 字段 → `source`(嵌套对象)
-- `eval_*` 和 `metrics_*` 字段 → `eval`(嵌套对象)
-- 保留顶层 `message_id`
-
-**新数据结构示例:**
-```json
-{
-  "id": "knowledge-xxx",
-  "message_id": "msg-xxx",
-  "types": ["strategy", "tool"],
-  "task": "任务描述",
-  "tags": {"category": "preference"},
-  "scopes": ["org:cybertogether"],
-  "owner": "agent:research_agent",
-  "content": "知识内容",
-  "source": {
-    "name": "资源名称",
-    "category": "exp",
-    "urls": ["https://example.com"],
-    "agent_id": "research_agent",
-    "submitted_by": "user@example.com",
-    "timestamp": "2026-03-05T12:00:00Z"
-  },
-  "eval": {
-    "score": 4,
-    "helpful": 5,
-    "harmful": 0,
-    "confidence": 0.9,
-    "helpful_history": [],
-    "harmful_history": []
-  },
-  "created_at": "2026-03-05T12:00:00Z",
-  "updated_at": "2026-03-05T12:00:00Z"
-}
-```
-
-### 2. 数据库迁移
-
-**文件:** `knowhub/server.py`
-
-- 重建 knowledge 表结构
-- 使用 JSON 字段存储 `types`, `tags`, `scopes`, `source`, `eval`
-- 删除旧的扁平化字段(`tags_type`, `scenario`, `source_*`, `eval_*`, `metrics_*`)
-- 备份旧数据库到 `knowhub.db.backup-20260305`
-
-### 3. API 更新
-
-**文件:** `knowhub/server.py`
-
-所有 knowledge API 已更新:
-
-- `POST /api/knowledge` - 保存知识(使用新结构)
-- `GET /api/knowledge/search` - 搜索知识(参数 `tags_type` → `types`)
-- `GET /api/knowledge` - 列出知识(参数 `tags_type` → `types`,新增 `scopes`)
-- `GET /api/knowledge/{id}` - 获取知识(返回新结构)
-- `PUT /api/knowledge/{id}` - 更新知识(使用嵌套 eval 结构)
-- `POST /api/knowledge/batch_update` - 批量更新(使用嵌套 eval 结构)
-- `POST /api/knowledge/slim` - 知识瘦身(使用新结构)
-
-### 4. CLI 工具更新
-
-**文件:** `knowhub/skill/cli.py`
-
-完全重写 CLI 工具以匹配新的数据结构:
-
-```bash
-# 搜索知识
-python -m knowhub.skill.cli search "查询内容" --types strategy
-
-# 保存知识
-python -m knowhub.skill.cli save \
-  --task "任务描述" \
-  --content "知识内容" \
-  --types strategy,tool \
-  --tags '{"category":"preference"}' \
-  --scopes org:cybertogether
-
-# 列出知识
-python -m knowhub.skill.cli list --limit 10 --types strategy
-
-# 更新知识
-python -m knowhub.skill.cli update knowledge-xxx \
-  --score 5 \
-  --helpful-case "有效案例"
-
-# 批量更新
-python -m knowhub.skill.cli batch-update --file feedback.json
-
-# 知识瘦身
-python -m knowhub.skill.cli slim --model google/gemini-2.0-flash-001
-```
-
-### 5. Agent 工具更新
-
-**文件:** `agent/tools/builtin/knowledge.py`
-
-更新所有工具函数:
-
-**knowledge_search:**
-- 参数 `tags_type` → `types`
-- 输出显示 `task` 而不是 `scenario`
-
-**knowledge_save:**
-- 参数 `scenario` → `task`
-- 参数 `tags_type` → `types`
-- 新增参数:`tags`, `scopes`, `owner`, `source_name`, `source_category`, `submitted_by`
-- **重要:** 默认值在 agent 代码中设置(不是服务器端):
-  - `scopes` 默认 `["org:cybertogether"]`
-  - `owner` 默认 `f"agent:{agent_id}"`
-
-**knowledge_list:**
-- 参数 `tags_type` → `types`
-- 新增参数:`scopes`
-
-**knowledge_slim:**
-- 默认模型改为 `google/gemini-2.0-flash-001`
-
-### 6. 清理旧代码
-
-**已删除/备份:**
-- `agent/tools/builtin/experience.py` → `experience.py.old`(旧的经验系统)
-- `agent/tools/builtin/__init__.py` - 删除 `get_experience` 导入和导出
-- `agent/core/runner.py` - 删除 `experiences_path` 参数和 `_load_experiences()` 方法
-- `agent/core/runner.py` - 从 BUILTIN_TOOLS 列表中删除 `get_experience`
-
-### 7. 备份文件
-
-所有修改前的文件都已备份:
-- `knowhub/server.py.old`
-- `knowhub/skill/cli.py.old`
-- `agent/tools/builtin/knowledge.py.old`
-- `agent/tools/builtin/experience.py.old`
-- `knowhub.db.backup-20260305`
-
-## 测试建议
-
-### 1. 启动 KnowHub Server
-
-```bash
-cd knowhub
-python server.py
-```
-
-### 2. 测试 CLI 工具
-
-```bash
-# 保存知识
-python -m knowhub.skill.cli save \
-  --task "测试任务" \
-  --content "测试内容" \
-  --types strategy
-
-# 搜索知识
-python -m knowhub.skill.cli search "测试"
-
-# 列出知识
-python -m knowhub.skill.cli list
-```
-
-### 3. 测试 Agent 工具
-
-在 agent 代码中调用:
-
-```python
-from agent.tools.builtin.knowledge import knowledge_save, knowledge_search
-
-# 保存知识
-await knowledge_save(
-    task="测试任务",
-    content="测试内容",
-    types=["strategy"],
-    agent_id="test_agent"
-)
-
-# 搜索知识
-await knowledge_search(
-    query="测试",
-    types=["strategy"]
-)
-```
-
-## 注意事项
-
-1. **默认值设置位置:** 按照用户要求,默认 org (`scopes`) 和 owner 在 agent 代码中设置,不在服务器端设置。
-
-2. **数据库重建:** 旧数据库已备份,新数据库为空。如需迁移旧数据,需要编写迁移脚本。
-
-3. **完全移除旧系统:** 已删除所有旧的经验系统代码(experience.py, get_experience 等),不保留兼容接口。
-
-4. **环境变量:** 确保设置 `OPEN_ROUTER_API_KEY` 和 `KNOWHUB_API`。
-
-## 下一步
-
-1. 测试所有 API 端点
-2. 如需要,编写数据迁移脚本
-3. 更新相关文档
-
-## 文件清单
-
-**已修改:**
-- `knowhub/server.py` - KnowHub Server(数据库 + API)
-- `knowhub/skill/cli.py` - CLI 工具
-- `agent/tools/builtin/knowledge.py` - Agent 工具集成
-- `agent/tools/builtin/__init__.py` - 删除旧的 experience 导入
-- `agent/core/runner.py` - 删除 experiences_path 和相关代码
-
-**已删除/备份:**
-- `agent/tools/builtin/experience.py` → `experience.py.old`
-
-**已备份:**
-- `knowhub/server.py.old`
-- `knowhub/skill/cli.py.old`
-- `agent/tools/builtin/knowledge.py.old`
-- `knowhub.db.backup-20260305`
-
-**新增:**
-- `.refactor-knowledge-complete.md` - 本文档

+ 0 - 166
.refactor-knowledge.md

@@ -1,166 +0,0 @@
-# 知识管理系统重构追踪
-
-## 目标
-
-将知识管理系统重构为统一的 KnowHub Server 架构。
-
-## 主要变更
-
-### 1. 工具命名规范
-- [ ] `search_knowledge` → `knowledge_search`
-- [ ] `save_knowledge` → `knowledge_save`
-- [ ] `update_knowledge` → `knowledge_update`
-- [ ] `batch_update_knowledge` → `knowledge_batch_update`
-- [ ] `list_knowledge` → `knowledge_list`
-- [ ] `slim_knowledge` → `knowledge_slim`
-- [ ] `get_experience` → 保持不变(兼容接口)
-
-### 2. 数据结构调整
-- [ ] `trace_id` → `message_id`
-- [ ] `trace` 对象 → `source` 对象
-- [ ] 更新所有相关的数据结构
-
-### 3. 架构调整
-- [ ] 将核心逻辑从 `agent/tools/builtin/knowledge.py` 迁移到 `knowhub/server.py`
-- [ ] Agent 工具改为 API 调用封装
-- [ ] 实现 KnowHub Server 的 API 端点
-
-### 4. 知识注入位置调整
-- [ ] 从 `runner.py` 移除知识注入逻辑
-- [ ] 在 `goal_tool.py` 的 `focus_goal` 中实现自动注入
-- [ ] 移除 runner 中的研究流程相关代码
-
-## 实施步骤
-
-### Phase 1: 准备工作
-- [ ] 查看当前实现状态
-- [ ] 备份关键文件
-- [ ] 确定迁移策略
-
-### Phase 2: KnowHub Server 实现
-- [ ] 实现知识管理 API 端点
-- [ ] 实现两阶段检索逻辑
-- [ ] 实现知识进化逻辑
-- [ ] 实现知识瘦身逻辑
-
-### Phase 3: Agent 工具重构
-- [ ] 重命名工具
-- [ ] 改为 API 调用封装
-- [ ] 更新数据结构
-
-### Phase 4: 知识注入重构
-- [ ] 在 goal_tool.py 中实现自动注入
-- [ ] 从 runner.py 移除相关逻辑
-- [ ] 更新 system prompt
-
-### Phase 5: 测试和验证
-- [ ] 测试工具调用
-- [ ] 测试知识注入
-- [ ] 测试 API 端点
-
-## 当前状态
-
-### 现有实现
-- `agent/tools/builtin/knowledge.py`: 1183 行,完整的本地实现
-  - 包含两阶段检索逻辑
-  - 包含知识进化逻辑
-  - 包含知识瘦身逻辑
-  - 直接操作 `.cache/knowledge_atoms/` 目录
-
-- `knowhub/server.py`: 359 行,只有 experiences API
-  - 缺少 knowledge 相关的 API 端点
-  - 缺少 knowledge 表结构
-
-### 实施策略
-
-采用渐进式迁移:
-1. 先在 KnowHub Server 添加 knowledge API 端点
-2. 将核心逻辑从 knowledge.py 迁移到 server.py
-3. 重构 Agent 工具为 API 调用
-4. 调整知识注入位置
-5. 清理 runner 中的旧逻辑
-
-## 详细步骤
-
-### Step 1: KnowHub Server - 添加 knowledge 表和基础 API
-- [x] 添加 knowledge 表结构
-- [x] 实现 POST /api/knowledge (保存知识)
-- [x] 实现 GET /api/knowledge (列出知识)
-- [x] 实现 GET /api/knowledge/{id} (获取单条知识)
-
-### Step 2: KnowHub Server - 实现检索逻辑
-- [x] 实现两阶段检索逻辑
-  - [x] 语义路由(LLM 筛选)
-  - [x] 质量精排(评分过滤)
-- [x] 实现 GET /api/knowledge/search
-
-### Step 3: KnowHub Server - 实现更新和进化
-- [x] 实现 PUT /api/knowledge/{id} (更新知识)
-- [x] 实现知识进化逻辑(LLM 重写)
-- [x] 实现 POST /api/knowledge/batch_update
-
-### Step 4: KnowHub Server - 实现瘦身
-- [x] 实现 POST /api/knowledge/slim
-
-### Step 5: Agent 工具重构
-- [x] 重命名工具(xxx_knowledge → knowledge_xxx)
-- [x] 改为 HTTP API 调用
-- [x] 更新数据结构(trace_id → message_id)
-- [x] 备份旧文件到 knowledge.py.backup
-
-### Step 6: 知识注入重构
-- [x] 在 goal_tool.py 实现自动注入(focus 时自动调用 knowledge_search)
-- [x] 从 runner.py 移除知识注入逻辑
-  - [x] 更新导入:knowledge_save, knowledge_batch_update
-  - [x] 更新 BUILTIN_TOOLS 中的工具名称
-  - [x] 更新所有工具调用为新名称
-  - [x] 修复 agent/tools/builtin/__init__.py 的导入
-  - [x] 验证导入成功
-  - [ ] 移除 _research_states 相关代码(保留,用于研究流程)
-  - [ ] 移除 _init_research_flow 函数(保留,用于研究流程)
-  - [ ] 移除 _get_research_state 函数(保留,用于研究流程)
-  - [ ] 移除 _update_research_stage 函数(保留,用于研究流程)
-  - [ ] 移除 _build_research_guide 函数(保留,用于研究流程)
-  - [ ] 移除 _build_research_decision_guide 函数(保留,用于研究流程)
-  - [ ] 移除 _handle_research_flow_transition 函数(保留,用于研究流程)
-  - [ ] 移除 enable_research_flow 配置项(保留,用于研究流程)
-  - [x] 移除经验检索注入逻辑(已注释,1064-1105行)
-
-### Step 7: 测试和清理
-- [x] 启动 KnowHub Server
-- [x] 测试 knowledge_save 工具
-- [x] 测试 knowledge_search 工具
-- [x] 测试 knowledge_update 工具
-- [x] 测试 knowledge_batch_update 工具
-- [x] 修复环境变量加载问题(添加 load_dotenv)
-- [x] 调整 LLM 模型为 gemini-2.0-flash-001
-- [x] 实现 CLI 工具(knowhub/cli.py)
-- [x] 更新 skill 文档(knowhub/skill/knowhub.md)
-- [x] 添加 CLI 使用文档(knowhub/CLI.md)
-- [ ] 测试 goal focus 自动注入
-- [ ] 测试完整流程(保存→检索→注入)
-- [ ] 清理注释代码(可选)
-- [ ] 更新 .gitignore(排除 .cache/knowledge_atoms/)
-
-## 完成状态
-
-### 已完成
-- ✅ KnowHub Server 完整实现(知识表、API 端点)
-- ✅ 两阶段检索逻辑(语义路由 + 质量精排)
-- ✅ 知识进化和瘦身功能
-- ✅ Agent 工具重构为 API 封装(1183 → 398 行)
-- ✅ 工具重命名(knowledge_xxx 前缀)
-- ✅ 数据结构调整(trace_id → message_id)
-- ✅ goal_tool.py 自动知识注入
-- ✅ runner.py 导入和工具名称更新
-- ✅ 模块导入验证通过
-
-### 待测试
-- 端到端知识管理流程
-- KnowHub Server API 调用
-- 自动知识注入效果
-
-### 备注
-- 研究流程相关代码保留(_research_states, _init_research_flow 等),因为它们用于显式的调研决策流程
-- 知识注入已从研究流程中分离,现在是 goal focus 时的自动行为
-- 旧的经验检索逻辑已注释(1064-1105 行)

+ 0 - 63
QUICK_MIGRATION.md

@@ -1,63 +0,0 @@
-# 快速迁移参考
-
-## 一键迁移命令
-
-```bash
-# 1. 连接服务器
-ssh agent_server
-cd ~/main_agent
-
-# 2. 备份数据库
-cp knowhub.db knowhub.db.backup.$(date +%Y%m%d_%H%M%S)
-
-# 3. 拉取迁移脚本
-git fetch origin
-git checkout origin/main -- migrate_to_resource.py
-
-# 4. 执行迁移
-python migrate_to_resource.py
-
-# 5. 拉取新代码
-git pull origin main
-
-# 6. 重启服务
-# 根据你的服务管理方式选择:
-sudo systemctl restart knowhub
-# 或
-pkill -f "uvicorn knowhub.server" && uvicorn knowhub.server:app --host 0.0.0.0 --port 8000 &
-```
-
-## 迁移内容
-
-1. **resources表**:contents → resources + 新字段
-2. **knowledge表**:添加resource_ids字段
-
-## 验证
-
-```bash
-# 检查表
-sqlite3 knowhub.db "SELECT name FROM sqlite_master WHERE type='table';"
-
-# 检查字段
-sqlite3 knowhub.db "PRAGMA table_info(resources);" | grep -E "secure_body|content_type|metadata|updated_at"
-sqlite3 knowhub.db "PRAGMA table_info(knowledge);" | grep resource_ids
-
-# 测试API
-curl http://localhost:8000/api/resource
-```
-
-## 回滚
-
-```bash
-# 停止服务
-pkill -f "uvicorn knowhub.server"
-
-# 恢复备份
-cp knowhub.db.backup.YYYYMMDD_HHMMSS knowhub.db
-
-# 回退代码
-git reset --hard HEAD~1
-
-# 重启服务
-uvicorn knowhub.server:app --host 0.0.0.0 --port 8000 &
-```

+ 0 - 62
RENAME_CONTENT_TO_RESOURCE.md

@@ -1,62 +0,0 @@
-# Content → Resource 重命名总结
-
-## 重命名原因
-
-避免与Knowledge表的`content`字段混淆,提高命名清晰度。
-
-## 已完成的修改
-
-### 1. 数据库
-- ✅ 表名:`contents` → `resources`
-
-### 2. 代码 (knowhub/server.py)
-- ✅ 模型:`ContentIn` → `ResourceIn`
-- ✅ 模型:`ContentOut` → `ResourceOut`
-- ✅ 模型:`ContentPatchIn` → `ResourcePatchIn`
-- ✅ 模型:`ContentNode` → `ResourceNode`
-- ✅ 函数:`submit_content` → `submit_resource`
-- ✅ 函数:`get_content` → `get_resource`
-- ✅ 函数:`patch_content` → `patch_resource`
-- ✅ 函数:`list_contents` → `list_resources`
-- ✅ 函数:`encrypt_content` → `encrypt_resource`
-- ✅ 函数:`decrypt_content` → `decrypt_resource`
-- ✅ 参数:`content_id` → `resource_id`
-- ✅ API路径:`/api/content` → `/api/resource`
-
-### 3. 文档
-- ✅ `knowhub/docs/content-storage.md` → `resource-storage.md`
-- ✅ `knowhub/docs/content-storage-examples.md` → `resource-storage-examples.md`
-- ✅ 更新 `knowhub/docs/knowledge-management.md`
-- ✅ 更新 `knowhub/docs/decisions.md`
-
-### 4. 测试和工具
-- ✅ 测试脚本:`test_resource_storage.py`
-- ✅ 实现总结:`RESOURCE_STORAGE_IMPLEMENTATION.md`
-
-## API端点变更
-
-| 旧端点 | 新端点 |
-|--------|--------|
-| POST /api/content | POST /api/resource |
-| GET /api/content/{content_id} | GET /api/resource/{resource_id} |
-| PATCH /api/content/{content_id} | PATCH /api/resource/{resource_id} |
-| GET /api/content | GET /api/resource |
-
-## 验证
-
-```bash
-# 1. 导入测试
-python -c "from knowhub.server import app; print('✅ OK')"
-
-# 2. 启动服务
-uvicorn knowhub.server:app --reload
-
-# 3. 运行测试
-python test_resource_storage.py
-```
-
-## 注意事项
-
-- 数据库表已重命名,旧的API端点不再可用
-- 如果有外部系统调用旧API,需要更新
-- 文档中所有引用已更新

+ 0 - 95
RESOURCE_STORAGE_IMPLEMENTATION.md

@@ -1,95 +0,0 @@
-# Resource存储系统实现总结
-
-## 实现内容
-
-### 1. 数据库扩展
-- 扩展`contents`表,添加以下字段:
-  - `secure_body`: 敏感内容(加密存储)
-  - `content_type`: 内容类型(text|code|credential|cookie)
-  - `metadata`: JSON元数据
-  - `updated_at`: 更新时间
-
-### 2. 加密机制
-- 使用AES-256-GCM加密算法
-- 密钥从环境变量`ORG_KEYS`读取
-- 格式:`encrypted:AES256-GCM:{base64_data}`
-- 实现函数:
-  - `encrypt_resource()`: 加密
-  - `decrypt_resource()`: 解密
-  - `get_org_key()`: 获取组织密钥
-
-### 3. API端点
-- `POST /api/resource`: 提交资源(自动加密secure_body)
-- `GET /api/resource/{id}`: 获取资源(支持X-Org-Key头解密)
-- `PATCH /api/resource/{id}`: 更新资源
-- `GET /api/resource`: 列出资源(支持content_type过滤)
-
-### 4. 数据模型
-- `ResourceIn`: 提交请求模型
-- `ResourcePatchIn`: 更新请求模型
-- `ResourceOut`: 响应模型
-
-### 5. 文档
-- `knowhub/docs/resource-storage.md`: 设计文档
-- `knowhub/docs/resource-storage-examples.md`: 使用示例
-- `knowhub/docs/decisions.md`: 决策记录(新增第0条)
-- `knowhub/docs/knowledge-management.md`: 更新架构说明
-
-### 6. 工具
-- `migrate_resources.py`: 数据库迁移脚本
-- `test_resource_storage.py`: 测试脚本
-
-## 使用方法
-
-### 1. 配置密钥
-
-```bash
-# 生成密钥
-python -c "import os, base64; print(base64.b64encode(os.urandom(32)).decode())"
-
-# 添加到.env
-echo "ORG_KEYS=test:你的密钥base64" >> .env
-```
-
-### 2. 迁移数据库
-
-```bash
-python migrate_resources.py
-```
-
-### 3. 启动服务
-
-```bash
-uvicorn knowhub.server:app --reload
-```
-
-### 4. 测试功能
-
-```bash
-python test_resource_storage.py
-```
-
-## 设计特点
-
-1. **分离公开/敏感内容**:body明文可搜索,secure_body加密保护
-2. **灵活的密钥管理**:支持多组织密钥,通过resource_id前缀区分
-3. **访问控制**:需要提供正确的X-Org-Key才能解密
-4. **元数据支持**:记录获取时间、过期时间、语言等信息
-5. **层级结构**:通过ID路径实现树形组织(如tools/selenium/login)
-
-## 安全考虑
-
-- ✅ 敏感内容加密存储
-- ✅ 密钥不入库,存储在环境变量
-- ✅ 访问需要验证密钥
-- ✅ 记录提交者和时间戳
-- ⚠️ 密钥在HTTP头中传输(建议使用HTTPS)
-- ⚠️ 数据库文件泄露仍有风险(建议文件系统加密)
-
-## 后续改进
-
-1. 支持密钥轮换
-2. 添加访问日志
-3. 支持密钥过期时间
-4. 前端管理界面
-5. 批量导入/导出功能

+ 0 - 214
SERVER_MIGRATION_GUIDE.md

@@ -1,214 +0,0 @@
-# 服务器迁移指南
-
-## ⚠️ 重要:不要直接拉取代码!
-
-由于我们进行了以下数据库变更,必须先执行迁移,否则会导致数据丢失或功能异常:
-
-1. **resources表**:contents → resources(表重命名)
-2. **resources表**:添加新字段(secure_body, content_type, metadata, updated_at)
-3. **knowledge表**:添加resource_ids字段(用于关联资源)
-
-## 迁移步骤
-
-### 1. 连接到服务器
-
-```bash
-ssh agent_server
-cd ~/main_agent
-```
-
-### 2. 备份数据库(重要!)
-
-```bash
-# 备份数据库文件
-cp knowhub.db knowhub.db.backup.$(date +%Y%m%d_%H%M%S)
-
-# 验证备份
-ls -lh knowhub.db*
-```
-
-### 3. 拉取迁移脚本(不拉取全部代码)
-
-```bash
-# 只拉取迁移脚本
-git fetch origin
-git checkout origin/main -- migrate_to_resource.py
-
-# 或者手动创建脚本(如果git checkout不工作)
-# 将migrate_to_resource.py的内容复制到服务器
-```
-
-### 4. 执行迁移
-
-```bash
-# 确保在包含knowhub.db的目录中
-python migrate_to_resource.py
-```
-
-**预期输出**:
-```
-============================================================
-KnowHub 数据库迁移
-变更内容:
-  1. contents表 → resources表
-  2. resources表添加新字段
-  3. knowledge表添加resource_ids字段
-============================================================
-数据库路径: /home/user/main_agent/knowhub.db
-
-当前表: {'contents', 'knowledge', 'experiences'}
-
-============================================================
-步骤1: 迁移 resources 表
-============================================================
-contents表中有 X 条记录
-现有字段: {...}
-
-添加 N 个新字段...
-  ALTER TABLE contents ADD COLUMN secure_body TEXT DEFAULT ''
-  ALTER TABLE contents ADD COLUMN content_type TEXT DEFAULT 'text'
-  ALTER TABLE contents ADD COLUMN metadata TEXT DEFAULT '{}'
-  ALTER TABLE contents ADD COLUMN updated_at TEXT DEFAULT ''
-✅ 字段添加完成
-
-重命名表: contents → resources
-✅ 表重命名完成
-resources表中有 X 条记录
-✅ 数据完整,X 条记录全部保留
-
-============================================================
-步骤2: 更新 knowledge 表
-============================================================
-现有字段: {...}
-
-添加 resource_ids 字段...
-✅ resource_ids字段添加完成
-
-============================================================
-迁移总结
-============================================================
-迁移后的表: {'resources', 'knowledge', 'experiences'}
-✅ resources表: X 条记录
-✅ knowledge表: Y 条记录, resource_ids字段: 存在
-
-✅ 迁移完成!现在可以拉取新代码并重启服务
-```
-
-### 5. 验证迁移
-
-```bash
-# 检查表是否存在
-sqlite3 knowhub.db "SELECT name FROM sqlite_master WHERE type='table';"
-
-# 检查resources表数据
-sqlite3 knowhub.db "SELECT COUNT(*) FROM resources;"
-
-# 检查knowledge表是否有resource_ids字段
-sqlite3 knowhub.db "PRAGMA table_info(knowledge);" | grep resource_ids
-```
-
-### 6. 拉取新代码
-
-```bash
-git pull origin main
-```
-
-### 7. 重启服务
-
-```bash
-# 根据你的服务管理方式
-# 如果使用systemd
-sudo systemctl restart knowhub
-
-# 如果使用screen/tmux
-# 先停止旧进程,然后启动新进程
-pkill -f "uvicorn knowhub.server"
-uvicorn knowhub.server:app --host 0.0.0.0 --port 8000 &
-
-# 或者使用你的启动脚本
-./start_knowhub.sh
-```
-
-### 8. 验证服务
-
-```bash
-# 测试API
-curl http://localhost:8000/api/resource
-
-# 检查日志
-tail -f logs/knowhub.log  # 根据实际日志路径
-```
-
-## 如果迁移失败
-
-### 恢复备份
-
-```bash
-# 停止服务
-pkill -f "uvicorn knowhub.server"
-
-# 恢复备份
-cp knowhub.db.backup.YYYYMMDD_HHMMSS knowhub.db
-
-# 回退代码
-git reset --hard HEAD~1
-
-# 重启服务
-uvicorn knowhub.server:app --host 0.0.0.0 --port 8000 &
-```
-
-### 检查问题
-
-```bash
-# 查看迁移脚本输出
-# 查看数据库状态
-sqlite3 knowhub.db ".tables"
-sqlite3 knowhub.db ".schema contents"
-sqlite3 knowhub.db ".schema resources"
-```
-
-## 数据库变更详情
-
-### 1. resources表(原contents表)
-
-**变更**:
-- 表名:`contents` → `resources`
-- 新增字段:
-  - `secure_body TEXT DEFAULT ''` - 敏感内容(加密存储)
-  - `content_type TEXT DEFAULT 'text'` - 内容类型(text|code|credential|cookie)
-  - `metadata TEXT DEFAULT '{}'` - JSON元数据
-  - `updated_at TEXT DEFAULT ''` - 更新时间
-
-**影响**:
-- 旧的`/api/content`端点不再可用
-- 需要使用新的`/api/resource`端点
-
-### 2. knowledge表
-
-**变更**:
-- 新增字段:`resource_ids TEXT DEFAULT '[]'` - 关联的资源ID列表(JSON数组)
-
-**用途**:
-- 知识条目可以引用多个资源
-- 示例:`["code/selenium/login", "credentials/website_a"]`
-- 通过`GET /api/resource/{id}`获取关联的资源详情
-
-## 注意事项
-
-1. **必须先备份**:迁移前务必备份数据库
-2. **按顺序执行**:不要跳过步骤
-3. **验证完整性**:迁移后检查数据条数是否一致
-4. **测试API**:重启后测试新的/api/resource端点
-
-## API端点变更
-
-迁移后,API端点已更改:
-
-| 旧端点 | 新端点 | 状态 |
-|--------|--------|------|
-| POST /api/content | POST /api/resource | ✅ 已更新 |
-| GET /api/content/{id} | GET /api/resource/{id} | ✅ 已更新 |
-| PATCH /api/content/{id} | PATCH /api/resource/{id} | ✅ 已更新 |
-| GET /api/content | GET /api/resource | ✅ 已更新 |
-
-如果有外部系统调用旧API,需要同步更新。

+ 28 - 16
knowhub/server.py

@@ -779,15 +779,13 @@ def list_knowledge(
         params = []
         conditions = []
 
-        # types 支持多个,用 OR 连接
+        # types 支持多个,用 AND 连接(交集:必须同时包含所有选中的type)
         if types:
             type_list = [t.strip() for t in types.split(',') if t.strip()]
             if type_list:
-                type_conditions = []
                 for t in type_list:
-                    type_conditions.append("types LIKE ?")
+                    conditions.append("types LIKE ?")
                     params.append(f"%{t}%")
-                conditions.append(f"({' OR '.join(type_conditions)})")
 
         if scopes:
             conditions.append("scopes LIKE ?")
@@ -797,15 +795,13 @@ def list_knowledge(
             conditions.append("owner LIKE ?")
             params.append(f"%{owner}%")
 
-        # tags 支持多个,用 OR 连接
+        # tags 支持多个,用 AND 连接(交集:必须同时包含所有选中的tag)
         if tags:
             tag_list = [t.strip() for t in tags.split(',') if t.strip()]
             if tag_list:
-                tag_conditions = []
                 for t in tag_list:
-                    tag_conditions.append("tags LIKE ?")
+                    conditions.append("tags LIKE ?")
                     params.append(f"%{t}%")
-                conditions.append(f"({' OR '.join(tag_conditions)})")
 
         if conditions:
             query += " WHERE " + " AND ".join(conditions)
@@ -1559,10 +1555,20 @@ def frontend():
                 params.append('scopes', scopesFilter);
             }
 
-            const res = await fetch(`/api/knowledge?${params.toString()}`);
-            const data = await res.json();
-            allKnowledge = data.results;
-            renderKnowledge(allKnowledge);
+            try {
+                const res = await fetch(`/api/knowledge?${params.toString()}`);
+                if (!res.ok) {
+                    console.error('加载失败:', res.status, res.statusText);
+                    document.getElementById('knowledgeList').innerHTML = '<p class="text-red-500 text-center py-8">加载失败,请刷新页面重试</p>';
+                    return;
+                }
+                const data = await res.json();
+                allKnowledge = data.results || [];
+                renderKnowledge(allKnowledge);
+            } catch (error) {
+                console.error('加载错误:', error);
+                document.getElementById('knowledgeList').innerHTML = '<p class="text-red-500 text-center py-8">加载错误: ' + error.message + '</p>';
+            }
         }
 
         function applyFilters() {
@@ -1576,13 +1582,18 @@ def frontend():
                 return;
             }
 
-            container.innerHTML = list.map(k => `
+            container.innerHTML = list.map(k => {
+                // 确保types是数组
+                const types = Array.isArray(k.types) ? k.types : (typeof k.types === 'string' ? JSON.parse(k.types) : []);
+                const eval_data = k.eval || {};
+
+                return `
                 <div class="bg-white rounded-lg shadow p-6 hover:shadow-lg transition cursor-pointer" onclick="openEditModal('${k.id}')">
                     <div class="flex justify-between items-start mb-2">
                         <div class="flex gap-2 flex-wrap">
-                            ${k.types.map(t => `<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">${t}</span>`).join('')}
+                            ${types.map(t => `<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">${t}</span>`).join('')}
                         </div>
-                        <span class="text-sm text-gray-500">${k.eval.score || 3}/5</span>
+                        <span class="text-sm text-gray-500">${eval_data.score || 3}/5</span>
                     </div>
                     <h3 class="text-lg font-semibold text-gray-800 mb-2">${escapeHtml(k.task)}</h3>
                     <p class="text-sm text-gray-600 mb-2">${escapeHtml(k.content.substring(0, 150))}${k.content.length > 150 ? '...' : ''}</p>
@@ -1591,7 +1602,8 @@ def frontend():
                         <span>${new Date(k.created_at).toLocaleDateString()}</span>
                     </div>
                 </div>
-            `).join('');
+            `;
+            }).join('');
         }
 
         function openAddModal() {

+ 0 - 204
migrate_to_resource.py

@@ -1,204 +0,0 @@
-#!/usr/bin/env python3
-"""
-安全迁移脚本:contents → resources + knowledge.resource_ids
-在服务器上执行此脚本,然后再拉取新代码
-
-变更内容:
-1. contents表 → resources表
-2. 为resources表添加新字段(secure_body, content_type, metadata, updated_at)
-3. 为knowledge表添加resource_ids字段
-"""
-
-import sqlite3
-import sys
-from pathlib import Path
-
-def migrate_resources_table(conn, cursor):
-    """迁移resources表(原contents表)"""
-    print("\n" + "="*60)
-    print("步骤1: 迁移 resources 表")
-    print("="*60)
-
-    # 检查contents表是否存在
-    cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='contents'")
-    if not cursor.fetchone():
-        cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='resources'")
-        if cursor.fetchone():
-            print("✅ resources表已存在,跳过此步骤")
-            return True
-        else:
-            print("⚠️  既没有contents表也没有resources表,将在首次启动时创建")
-            return False
-
-    # 检查contents表中的数据
-    cursor.execute("SELECT COUNT(*) FROM contents")
-    count = cursor.fetchone()[0]
-    print(f"contents表中有 {count} 条记录")
-
-    # 检查是否已有新字段
-    cursor.execute("PRAGMA table_info(contents)")
-    columns = {row[1] for row in cursor.fetchall()}
-    print(f"现有字段: {columns}")
-
-    # 添加新字段(如果不存在)
-    migrations = []
-    if "secure_body" not in columns:
-        migrations.append("ALTER TABLE contents ADD COLUMN secure_body TEXT DEFAULT ''")
-    if "content_type" not in columns:
-        migrations.append("ALTER TABLE contents ADD COLUMN content_type TEXT DEFAULT 'text'")
-    if "metadata" not in columns:
-        migrations.append("ALTER TABLE contents ADD COLUMN metadata TEXT DEFAULT '{}'")
-    if "updated_at" not in columns:
-        migrations.append("ALTER TABLE contents ADD COLUMN updated_at TEXT DEFAULT ''")
-
-    if migrations:
-        print(f"\n添加 {len(migrations)} 个新字段...")
-        for sql in migrations:
-            print(f"  {sql}")
-            cursor.execute(sql)
-        conn.commit()
-        print("✅ 字段添加完成")
-
-    # 重命名表
-    print("\n重命名表: contents → resources")
-    try:
-        cursor.execute("ALTER TABLE contents RENAME TO resources")
-        conn.commit()
-        print("✅ 表重命名完成")
-    except Exception as e:
-        print(f"❌ 重命名失败: {e}")
-        return False
-
-    # 验证
-    cursor.execute("SELECT COUNT(*) FROM resources")
-    new_count = cursor.fetchone()[0]
-    print(f"resources表中有 {new_count} 条记录")
-
-    if new_count == count:
-        print(f"✅ 数据完整,{count} 条记录全部保留")
-        return True
-    else:
-        print(f"⚠️  数据不一致: 原 {count} 条 → 现 {new_count} 条")
-        return False
-
-
-def migrate_knowledge_table(conn, cursor):
-    """为knowledge表添加resource_ids字段"""
-    print("\n" + "="*60)
-    print("步骤2: 更新 knowledge 表")
-    print("="*60)
-
-    # 检查knowledge表是否存在
-    cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='knowledge'")
-    if not cursor.fetchone():
-        print("⚠️  knowledge表不存在,跳过此步骤")
-        return True
-
-    # 检查是否已有resource_ids字段
-    cursor.execute("PRAGMA table_info(knowledge)")
-    columns = {row[1] for row in cursor.fetchall()}
-    print(f"现有字段: {columns}")
-
-    if "resource_ids" in columns:
-        print("✅ resource_ids字段已存在,跳过此步骤")
-        return True
-
-    # 添加resource_ids字段
-    print("\n添加 resource_ids 字段...")
-    try:
-        cursor.execute("ALTER TABLE knowledge ADD COLUMN resource_ids TEXT DEFAULT '[]'")
-        conn.commit()
-        print("✅ resource_ids字段添加完成")
-        return True
-    except Exception as e:
-        print(f"❌ 添加字段失败: {e}")
-        return False
-
-
-def migrate():
-    """主迁移函数"""
-def migrate():
-    """主迁移函数"""
-    # 查找数据库文件
-    db_path = Path("knowhub.db")
-    if not db_path.exists():
-        print("❌ 找不到 knowhub.db")
-        print("请在包含数据库的目录中运行此脚本")
-        sys.exit(1)
-
-    print(f"数据库路径: {db_path.absolute()}")
-
-    conn = sqlite3.connect(str(db_path))
-    cursor = conn.cursor()
-
-    # 显示当前表状态
-    cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
-    tables = {row[0] for row in cursor.fetchall()}
-    print(f"\n当前表: {tables}")
-
-    # 执行迁移
-    success = True
-
-    # 步骤1: 迁移resources表
-    if not migrate_resources_table(conn, cursor):
-        success = False
-
-    # 步骤2: 更新knowledge表
-    if not migrate_knowledge_table(conn, cursor):
-        success = False
-
-    # 最终验证
-    print("\n" + "="*60)
-    print("迁移总结")
-    print("="*60)
-
-    cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
-    tables = {row[0] for row in cursor.fetchall()}
-    print(f"迁移后的表: {tables}")
-
-    # 检查resources表
-    if "resources" in tables:
-        cursor.execute("SELECT COUNT(*) FROM resources")
-        count = cursor.fetchone()[0]
-        print(f"✅ resources表: {count} 条记录")
-    else:
-        print("⚠️  resources表不存在")
-
-    # 检查knowledge表
-    if "knowledge" in tables:
-        cursor.execute("SELECT COUNT(*) FROM knowledge")
-        count = cursor.fetchone()[0]
-        cursor.execute("PRAGMA table_info(knowledge)")
-        columns = {row[1] for row in cursor.fetchall()}
-        has_resource_ids = "resource_ids" in columns
-        print(f"✅ knowledge表: {count} 条记录, resource_ids字段: {'存在' if has_resource_ids else '不存在'}")
-    else:
-        print("⚠️  knowledge表不存在")
-
-    conn.close()
-
-    if success:
-        print("\n✅ 迁移完成!现在可以拉取新代码并重启服务")
-    else:
-        print("\n⚠️  迁移过程中有警告,请检查上述输出")
-
-    return success
-
-
-if __name__ == "__main__":
-    print("=" * 60)
-    print("KnowHub 数据库迁移")
-    print("变更内容:")
-    print("  1. contents表 → resources表")
-    print("  2. resources表添加新字段")
-    print("  3. knowledge表添加resource_ids字段")
-    print("=" * 60)
-
-    try:
-        success = migrate()
-        sys.exit(0 if success else 1)
-    except Exception as e:
-        print(f"\n❌ 迁移失败: {e}")
-        import traceback
-        traceback.print_exc()
-        sys.exit(1)

+ 0 - 35
rename_to_resource.py

@@ -1,35 +0,0 @@
-#!/usr/bin/env python3
-"""
-重命名:contents → resources
-"""
-
-import sqlite3
-from pathlib import Path
-
-DB_PATH = Path(__file__).parent / "knowhub.db"
-
-def rename_table():
-    if not DB_PATH.exists():
-        print("数据库不存在")
-        return
-
-    conn = sqlite3.connect(str(DB_PATH))
-    cursor = conn.cursor()
-
-    # 检查表是否存在
-    cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='contents'")
-    if not cursor.fetchone():
-        print("contents表不存在,可能已经重命名")
-        conn.close()
-        return
-
-    print("重命名表:contents → resources")
-    cursor.execute("ALTER TABLE contents RENAME TO resources")
-
-    conn.commit()
-    conn.close()
-    print("✅ 重命名完成")
-
-
-if __name__ == "__main__":
-    rename_table()

+ 0 - 143
test_resource_storage.py

@@ -1,143 +0,0 @@
-#!/usr/bin/env python3
-"""测试Resource存储系统"""
-
-import requests
-import base64
-import os
-
-BASE_URL = "http://localhost:8000"
-
-# 生成测试密钥
-test_key = base64.b64encode(os.urandom(32)).decode('ascii')
-print(f"测试密钥: {test_key}")
-print(f"请在.env中设置: ORG_KEYS=test:{test_key}")
-print()
-
-def test_submit_resource():
-    """测试提交resource"""
-    print("=== 测试1: 提交普通代码 ===")
-    response = requests.post(f"{BASE_URL}/api/resource", json={
-        "id": "test/code/example",
-        "title": "示例代码",
-        "body": "print('Hello World')",
-        "content_type": "code",
-        "metadata": {"language": "python"},
-        "submitted_by": "test@example.com"
-    })
-    print(f"状态码: {response.status_code}")
-    print(f"响应: {response.json()}")
-    print()
-
-    print("=== 测试2: 提交敏感凭证 ===")
-    response = requests.post(f"{BASE_URL}/api/resource", json={
-        "id": "test/credentials/website",
-        "title": "网站登录凭证",
-        "body": "使用方法:直接登录",
-        "secure_body": "账号:user@example.com\n密码:SecurePass123",
-        "content_type": "credential",
-        "metadata": {"acquired_at": "2026-03-06T10:00:00Z"},
-        "submitted_by": "test@example.com"
-    })
-    print(f"状态码: {response.status_code}")
-    print(f"响应: {response.json()}")
-    print()
-
-    print("=== 测试3: 提交Cookie ===")
-    response = requests.post(f"{BASE_URL}/api/resource", json={
-        "id": "test/cookies/website",
-        "title": "网站Cookie",
-        "body": "适用于:已登录状态",
-        "secure_body": "session_id=abc123; auth_token=xyz789",
-        "content_type": "cookie",
-        "metadata": {
-            "acquired_at": "2026-03-06T10:00:00Z",
-            "expires_at": "2026-03-07T10:00:00Z"
-        },
-        "submitted_by": "test@example.com"
-    })
-    print(f"状态码: {response.status_code}")
-    print(f"响应: {response.json()}")
-    print()
-
-
-def test_get_resource():
-    """测试获取resource"""
-    print("=== 测试4: 获取普通代码(无需密钥)===")
-    response = requests.get(f"{BASE_URL}/api/resource/test/code/example")
-    print(f"状态码: {response.status_code}")
-    data = response.json()
-    print(f"标题: {data['title']}")
-    print(f"内容: {data['body']}")
-    print(f"敏感内容: {data['secure_body']}")
-    print()
-
-    print("=== 测试5: 获取凭证(无密钥)===")
-    response = requests.get(f"{BASE_URL}/api/resource/test/credentials/website")
-    print(f"状态码: {response.status_code}")
-    data = response.json()
-    print(f"标题: {data['title']}")
-    print(f"公开内容: {data['body']}")
-    print(f"敏感内容: {data['secure_body']}")  # 应该是 [ENCRYPTED]
-    print()
-
-    print("=== 测试6: 获取凭证(有密钥)===")
-    response = requests.get(
-        f"{BASE_URL}/api/resource/test/credentials/website",
-        headers={"X-Org-Key": test_key}
-    )
-    print(f"状态码: {response.status_code}")
-    data = response.json()
-    print(f"标题: {data['title']}")
-    print(f"公开内容: {data['body']}")
-    print(f"敏感内容: {data['secure_body']}")  # 应该解密
-    print()
-
-
-def test_patch_resource():
-    """测试更新resource"""
-    print("=== 测试7: 更新resource ===")
-    response = requests.patch(
-        f"{BASE_URL}/api/resource/test/credentials/website",
-        json={
-            "title": "更新后的标题",
-            "metadata": {"acquired_at": "2026-03-06T11:00:00Z"}
-        }
-    )
-    print(f"状态码: {response.status_code}")
-    print(f"响应: {response.json()}")
-    print()
-
-
-def test_list_resources():
-    """测试列出resource"""
-    print("=== 测试8: 列出所有resource ===")
-    response = requests.get(f"{BASE_URL}/api/resource")
-    print(f"状态码: {response.status_code}")
-    data = response.json()
-    print(f"总数: {data['count']}")
-    for item in data['results']:
-        print(f"  - {item['id']}: {item['title']} ({item['content_type']})")
-    print()
-
-    print("=== 测试9: 按类型过滤 ===")
-    response = requests.get(f"{BASE_URL}/api/resource?content_type=credential")
-    print(f"状态码: {response.status_code}")
-    data = response.json()
-    print(f"凭证数量: {data['count']}")
-    for item in data['results']:
-        print(f"  - {item['id']}: {item['title']}")
-    print()
-
-
-if __name__ == "__main__":
-    print("请确保服务器正在运行: uvicorn knowhub.server:app --reload")
-    print()
-
-    try:
-        test_submit_resource()
-        test_get_resource()
-        test_patch_resource()
-        test_list_resources()
-        print("✅ 所有测试完成")
-    except Exception as e:
-        print(f"❌ 测试失败: {e}")